From: CMDR furrycat Date: Sun, 9 Dec 2018 11:43:05 +0000 (+0000) Subject: Plugin to prune old members. X-Git-Url: http://git.furryclan.net/?a=commitdiff_plain;h=36c38385ee7d6796e3cd055d8eb87165c1aef069;p=furrycat%2Fcatbot.git Plugin to prune old members. --- diff --git a/plugin/prune/__init__.py b/plugin/prune/__init__.py new file mode 100644 index 0000000..f7bed65 --- /dev/null +++ b/plugin/prune/__init__.py @@ -0,0 +1,195 @@ +import asyncio +import datetime +import shlex + +from plugins import PluginCommand +import bot +import cat + +class Prune(object): + def description(self): + return 'Prunes inactive members.' + + def valid_commands(self): + return ['prune'] + + async def handle_command(self, message, command, raw): + if command not in self.valid_commands(): + return PluginCommand.ignored + await self.run_command(message, shlex.split(raw)) + return PluginCommand.exclusive + + async def handle_help(self, message, command, *args): + await self.help_command(message, command, *args) + + async def run_command(self, message, args): + command = args[0] + + if command == 'prune': + await self.handle_prune(message, args[1:]) + + async def help_command(self, message, command): + if command == 'prune': + lines = [ + 'Identify and optionally kick inactive members.', + '```', + 'prune server SERVER [only] role ROLE days DAYS', + 'prune server SERVER [only] role ROLE days DAYS kick', + '```', + '', + 'Here are the parameters:', + '```server SERVER```', + 'Specify the server on which members will be searched.', + '', + '```role ROLE```', + 'Only find members who have the given role.', + '```only role ROLE```', + 'Omit members who have other roles as well as the given role. Include them if they have the role and no others.', + '', + '```days DAYS```', + 'Omit members who have been on the server for less than `DAYS` days.', + '', + '```kick```', + 'Kick matching members. Without `kick` they will just be listed' + ] + else: + lines = ['*shrugs*'] + await bot.say_many(message.channel, lines) + + async def handle_prune(self, message, args): + if args[0].lower() == 'help': + await self.help_command(message, 'prune') + return + + if not bot.is_admin(message.author): + await cat.hiss(message.channel) + return + + parsed = { + 'kick': False, + 'only': False + } + ok = False + i = 0 + while i < len(args): + arg = args[i].lower() + if i > len(args) - 1: + break + try: + param = args[i + 1] + except IndexError: + param = None + log.info('{}: {}={}'.format(i, arg, param)) + ok = False + + if arg == 'days': + if param is not None: + try: + parsed['days'] = int(param) + if parsed['days'] < 1: + await bot.say(message.channel, 'days must be positive!') + return + ok = True + except: + await bot.say(message.channel, '{}?'.format(param)) + return + elif arg == 'kick': + parsed['kick'] = True + i -= 1 + elif arg == 'only': + ok = True + parsed['only'] = True + i -= 1 + elif arg == 'role': + if param is not None: + if param in ['everyone', 'here']: + await bot.say(message.channel, 'role must not be *everyone* or *here*!') + return None + ok = True + parsed['role'] = await bot.parse_role(param, author = message.author) + elif arg == 'server': + if param is not None: + ok = True + parsed['server'] = bot.parse_server(param, author = message.author) + + if ok: + i += 2 + else: + break + + if not ok: + log.info('Failed to parse prune. Got: {}'.format(parsed)) + await bot.say(message.channel, '{}?'.format(arg)) + return None + + if 'days' not in parsed: + await bot.say(message.channel, 'How long?') + return None + + if 'role' in parsed: + if parsed['role'] is None: + await bot.say(message.channel, 'Role?') + return None + if 'server' not in parsed: + if message.channel.is_private: + await bot.say(message.channel, 'Which server?') + return None + parsed['server'] = message.channel.server + elif parsed['server'] is None: + await bot.say(message.channel, 'Server?') + return None + + if 'role' in parsed: + # Prune by role. + members = await self.find_members(**parsed) + if not len(members): + await cat.shrug(message.channel) + else: + await bot.say_many(message.channel, [member.name for member in members]) + if parsed['kick']: + for member in members: + try: + await client.kick(member) + except: + log.exception('Failed to kick {}'.format(member.name)) + await cat.yelp(message.channel) + break + else: + await bot.say(message.channel, 'Use `kick` option to kick the above members!') + else: + # Use server prune_members API call. + pruned = None + if parsed['kick']: + try: + log.info('Pruning members older than {} day{} from server {}.'.format(parsed['days'], 's' if parsed['days'] != 1 else '', parsed['server'].name)) + pruned = await client.prune_members(parsed['server'], days = parsed['days']) + except: + log.exception('Failed to prune members from {}'.format(parsed['server'].name)) + await cat.yelp(message.channel) + else: + try: + log.info('Estimating pruned members older than {} day{} from server {}.'.format(parsed['days'], 's' if parsed['days'] != 1 else '', parsed['server'].name)) + pruned = await client.estimate_pruned_members(parsed['server'], days = parsed['days']) + except: + log.exception('Failed to estimate pruned members from {}'.format(parsed['server'].name)) + await cat.yelp(message.channel) + if pruned is not None: + await bot.say(message.channel, str(pruned)) + async def find_members(self, days = None, kick = False, only = False, role = None, server = None): + cutoff = datetime.datetime.utcnow() - datetime.timedelta(days) + + members = [] + for member in server.members: + if member.joined_at > cutoff: + log.debug('Member {} on server {} joined more recently than {}'.format(member.name, server.name, cutoff)) + continue + if role is not None: + if role not in member.roles: + log.debug('Member {} on server {} does not have required role {}'.format(member.name, server.name, role.name)) + continue + if only: + if len([other_role for other_role in member.roles if other_role not in [role, server.default_role]]): + log.debug('Member {} on server {} has roles other than {}'.format(member.name, server.name, role.name)) + continue + members.append(member) + return members