users module: add user classification and stats commands.
This commit is contained in:
parent
9b5ac99a40
commit
87aa703512
19
README.md
19
README.md
|
@ -624,12 +624,27 @@ by default, but you can set any single instance to search on.
|
||||||
|
|
||||||
### User management
|
### User management
|
||||||
|
|
||||||
Admin commands to manage users.
|
Admin commands to manage users and some utilities.
|
||||||
|
|
||||||
|
You can classify users based on MXID to get stats on where users come from.
|
||||||
|
|
||||||
#### Usage
|
#### Usage
|
||||||
|
|
||||||
* !users list [pattern] - List users matching wildcard pattern (must be owner)
|
* !users list [pattern] - List users matching wildcard pattern in this room (must be owner)
|
||||||
* !users kick [pattern] - Kick users matching wildcard pattern from room (must be admin in room)
|
* !users kick [pattern] - Kick users matching wildcard pattern from room (must be admin in room)
|
||||||
|
* !users classify add [name] [pattern] - Add a classification pattern (must be owner)
|
||||||
|
* !users classify list - List classifications
|
||||||
|
* !users classify del [name] - Delete classification
|
||||||
|
* !users roomstats - List how many users are in each class in this room
|
||||||
|
* !users stats - List how many users are in each class globally as seen by bot
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
* !users classify add matrix.org @*:matrix.org
|
||||||
|
* !users classify add libera.chat @*:libera.chat
|
||||||
|
* !users classify add discord @*discordpuppet*:*
|
||||||
|
* !users stats
|
||||||
|
* !users roomstats
|
||||||
|
|
||||||
### RASP (Gliding Weather forecast)
|
### RASP (Gliding Weather forecast)
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,43 @@ from modules.common.module import BotModule
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
|
||||||
class MatrixModule(BotModule):
|
class MatrixModule(BotModule):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.classes = dict() # classname <-> pattern
|
||||||
|
|
||||||
async def matrix_message(self, bot, room, event):
|
async def matrix_message(self, bot, room, event):
|
||||||
args = event.body.split()
|
args = event.body.split()
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
|
|
||||||
|
if len(args) == 1:
|
||||||
|
if args[0] == 'stats' or args[0] == 'roomstats':
|
||||||
|
stats = dict()
|
||||||
|
for name, pattern in self.classes.items():
|
||||||
|
stats[name] = 0
|
||||||
|
if args[0] == 'stats':
|
||||||
|
allusers = self.get_users(bot)
|
||||||
|
else:
|
||||||
|
allusers = self.get_users(bot, room.room_id)
|
||||||
|
total = len(allusers)
|
||||||
|
matched = 0
|
||||||
|
for user in allusers:
|
||||||
|
for name, pattern in self.classes.items():
|
||||||
|
match = fnmatch.fnmatch(user, pattern)
|
||||||
|
if match:
|
||||||
|
stats[name] = stats[name] + 1
|
||||||
|
matched = matched + 1
|
||||||
|
|
||||||
|
stats['Matrix'] = total - matched
|
||||||
|
stats = dict(sorted(stats.items(), key=lambda item: item[1], reverse=True))
|
||||||
|
|
||||||
|
if args[0] == 'stats':
|
||||||
|
reply = f'I am seeing total {len(allusers)} users:\n'
|
||||||
|
else:
|
||||||
|
reply = f'I am seeing {len(allusers)} users in this room:\n'
|
||||||
|
for name in stats:
|
||||||
|
reply = reply + f' - {name}: {stats[name]} ({stats[name] / total * 100}%)\n'
|
||||||
|
await bot.send_text(room, reply)
|
||||||
|
return
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
if args[0] == 'list':
|
if args[0] == 'list':
|
||||||
bot.must_be_owner(event)
|
bot.must_be_owner(event)
|
||||||
|
@ -25,20 +58,64 @@ class MatrixModule(BotModule):
|
||||||
else:
|
else:
|
||||||
await bot.send_text(room, 'No matching users found!')
|
await bot.send_text(room, 'No matching users found!')
|
||||||
return
|
return
|
||||||
|
if args[0] == 'classify':
|
||||||
|
if args[1] == 'list':
|
||||||
|
await bot.send_text(room, f'Classes in use: {self.classes}.')
|
||||||
|
return
|
||||||
|
elif len(args) == 4:
|
||||||
|
if args[0] == 'classify':
|
||||||
|
if args[1] == 'add':
|
||||||
|
bot.must_be_owner(event)
|
||||||
|
name = args[2]
|
||||||
|
pattern = args[3]
|
||||||
|
self.classes[name] = pattern
|
||||||
|
await bot.send_text(room, f'Added class {name} pattern {pattern}.')
|
||||||
|
bot.save_settings()
|
||||||
|
return
|
||||||
|
elif len(args) == 3:
|
||||||
|
if args[0] == 'classify':
|
||||||
|
if args[1] == 'del':
|
||||||
|
bot.must_be_owner(event)
|
||||||
|
name = args[2]
|
||||||
|
del self.classes[name]
|
||||||
|
await bot.send_text(room, f'Deleted class {name}.')
|
||||||
|
bot.save_settings()
|
||||||
|
return
|
||||||
|
|
||||||
await bot.send_text(room, 'Unknown command - please see readme')
|
await bot.send_text(room, 'Unknown command - please see readme')
|
||||||
|
|
||||||
def search_users(self, bot, pattern):
|
def get_users(self, bot, roomid=None):
|
||||||
allusers = []
|
allusers = []
|
||||||
for croomid in bot.client.rooms:
|
for croomid in self.bot.client.rooms:
|
||||||
|
if roomid and (roomid != croomid):
|
||||||
|
break
|
||||||
try:
|
try:
|
||||||
users = bot.client.rooms[croomid].users
|
users = self.bot.client.rooms[croomid].users
|
||||||
except (KeyError, ValueError) as e:
|
except (KeyError, ValueError) as e:
|
||||||
self.logger.warning(f"Couldn't get user list in room with id {croomid}, skipping: {repr(e)}")
|
self.logger.warning(f"Couldn't get user list in room with id {croomid}, skipping: {repr(e)}")
|
||||||
continue
|
continue
|
||||||
for user in users:
|
for user in users:
|
||||||
allusers.append(user)
|
allusers.append(user)
|
||||||
allusers = list(dict.fromkeys(allusers)) # Deduplicate
|
allusers = list(dict.fromkeys(allusers)) # Deduplicate
|
||||||
|
return allusers
|
||||||
|
|
||||||
|
def search_users(self, bot, pattern):
|
||||||
|
allusers = self.get_users(self, bot)
|
||||||
return fnmatch.filter(allusers, pattern)
|
return fnmatch.filter(allusers, pattern)
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
return 'User management tools'
|
return 'User management tools'
|
||||||
|
|
||||||
|
def get_settings(self):
|
||||||
|
data = super().get_settings()
|
||||||
|
data["classes"] = self.classes
|
||||||
|
return data
|
||||||
|
|
||||||
|
def set_settings(self, data):
|
||||||
|
super().set_settings(data)
|
||||||
|
if data.get("classes"):
|
||||||
|
self.classes = data["classes"]
|
||||||
|
|
||||||
|
def matrix_start(self, bot):
|
||||||
|
super().matrix_start(bot)
|
||||||
|
self.bot = bot
|
Loading…
Reference in New Issue