Skip to content

Commit

Permalink
users module: add user classification and stats commands.
Browse files Browse the repository at this point in the history
  • Loading branch information
vranki committed Sep 11, 2021
1 parent 9b5ac99 commit 87aa703
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 5 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -624,12 +624,27 @@ by default, but you can set any single instance to search on.

### 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

* !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 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)

Expand Down
83 changes: 80 additions & 3 deletions modules/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,43 @@
import fnmatch

class MatrixModule(BotModule):
def __init__(self, name):
super().__init__(name)
self.classes = dict() # classname <-> pattern

async def matrix_message(self, bot, room, event):
args = event.body.split()
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 args[0] == 'list':
bot.must_be_owner(event)
Expand All @@ -25,20 +58,64 @@ async def matrix_message(self, bot, room, event):
else:
await bot.send_text(room, 'No matching users found!')
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')

def search_users(self, bot, pattern):
def get_users(self, bot, roomid=None):
allusers = []
for croomid in bot.client.rooms:
for croomid in self.bot.client.rooms:
if roomid and (roomid != croomid):
break
try:
users = bot.client.rooms[croomid].users
users = self.bot.client.rooms[croomid].users
except (KeyError, ValueError) as e:
self.logger.warning(f"Couldn't get user list in room with id {croomid}, skipping: {repr(e)}")
continue
for user in users:
allusers.append(user)
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)

def help(self):
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

0 comments on commit 87aa703

Please sign in to comment.