From 4f8717e9b0ff92e2573e5b8f7cbecf989240e54f Mon Sep 17 00:00:00 2001 From: CMDR furrycat Date: Sat, 20 Mar 2021 17:37:23 +0100 Subject: [PATCH] Added role vending machine. --- plugins/role/__init__.py | 122 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 12 deletions(-) diff --git a/plugins/role/__init__.py b/plugins/role/__init__.py index 3ecbe53..74a7079 100644 --- a/plugins/role/__init__.py +++ b/plugins/role/__init__.py @@ -1,5 +1,6 @@ import asyncio import discord +import re import shlex from db import DBConnection @@ -20,6 +21,9 @@ class ManagedRole(object): self.below_role_id = None class Role(DBConnection): + YES = r'✅' + NO = r'❎' + def __init__(self, filename = None): super(Role, self).__init__(filename) self.create_tables() @@ -59,6 +63,7 @@ class Role(DBConnection): '```', 'assign', 'unassign', + 'vending' '```', 'Send `role help COMMAND` for help on a specific command.' ] @@ -94,6 +99,11 @@ class Role(DBConnection): '```role unassign ROLE```', 'Only roles managed (with `role manage`) can be assigned or unassigned.' ] + elif command == 'vending': + lines = [ + 'List managed roles and watch for reactions from users, assigning or unassigning the roles as they react:', + '```role vending```' + ] elif command == 'help': lines = ['grr!'] else: @@ -112,19 +122,21 @@ class Role(DBConnection): await self.assign_role(message, args[1:]) elif command == 'unassign': await self.unassign_role(message, args[1:]) + elif command == 'vending': + await self.role_vending_machine(message, args[1:]) elif command == 'help': await self.help_roles(message, args[1] if len(args) > 1 else None) else: await cat.yelp(message.channel) async def can_manage_roles(self, author, channel, command, server = None, hiss = True): - if command == 'list': + if command in ['list', 'vending']: log.debug('Anyone can list managed roles.') if command in ['manage', 'unmanage']: if bot.is_admin(author, server): return True else: - if hiss: + if hiss and channel is not None: await cat.hiss(channel) return False log.debug('Anyone can attempt to assign or unassign roles.') @@ -154,29 +166,33 @@ class Role(DBConnection): return None return managed_roles[0] - async def find_role(self, message, role_name, command = False): - role = await bot.parse_role(role_name, author = message.author) + async def find_role(self, role_name, message = None, member = None, server = None, command = False): + author = message.author if message is not None else member + role = await bot.parse_role(role_name, author = author) if not isinstance(role, discord.role.Role): log.info('Failed to find server for role {}'.format(role_name)) - await bot.say(message.channel, '?') + if message is not None: + await bot.say(message.channel, '?') return (None, None, None) if bot.is_everyone(role): log.info("Can't make &everyone assignable!") - await bot.say(message.channel, 'everyone?') + if message is not None: + await bot.say(message.channel, 'everyone?') return (None, None, None) - if not bot.shared_with(message.author, role): + if not bot.shared_with(author, role): return (None, None, cat.hiss) managed_role = self.get_managed_role(role) if managed_role is None: return (None, None, cat.yelp) - member = role.guild.get_member(message.author.id) + if not member: + member = role.guild.get_member(message.author.id) can_manage = True if command is not None: - can_manage = await self.can_manage_roles(message.author, message.channel, command, role.guild) + can_manage = await self.can_manage_roles(author, message.channel if message is not None else None, command, role.guild) if managed_role.above_role_id: above_role = self.get_role_on_server(managed_role.above_role_id, role.guild) @@ -347,7 +363,7 @@ class Role(DBConnection): await bot.say(message.channel, 'role?') return - role, managed_role, method = await self.find_role(message, args[0], 'unmanage') + role, managed_role, method = await self.find_role(args[0], 'unmanage', message = message) if method is not None: await method(message.channel) if not role: @@ -366,7 +382,7 @@ class Role(DBConnection): await bot.say(message.channel, 'role?') return - role, managed_role, method = await self.find_role(message, ' '.join(args)) + role, managed_role, method = await self.find_role(' '.join(args), message = message) if method is not None: await method(message.channel) if not role: @@ -394,7 +410,7 @@ class Role(DBConnection): await bot.say(message.channel, 'role?') return - role, managed_role, method = await self.find_role(message, ' '.join(args)) + role, managed_role, method = await self.find_role(' '.join(args), message = message) if method is not None: await method(message.channel) if not role: @@ -417,3 +433,85 @@ class Role(DBConnection): log.error("Couldn't remove role {} on {} from {}".format(role.name, role.guild.name, member.name)) await cat.yelp(message.channel) + async def role_vending_machine(self, message, args): + roles = [] + hiss = False + for managed_role in self.get_managed_roles(): + server = bot.get_server(managed_role.server_id) + result = await self.can_manage_roles(message.author, message.channel, 'vending', server, False) + if not result: + hiss = True + continue + if not bot.shared_with(message.author, server): + log.debug('Not vending managed role {} on server {} to user {} not on that server'.format(managed_role.role_id, server.name, message.author.name)) + hiss = True + continue + if not bot.is_private(message.channel): + if message.channel.guild.id != managed_role.server_id: + log.debug('Not vending managed role {} on server {} to authorised user {} in public channel {} on {}'.format(managed_role.role_id, server_name, message.author.name, message.channel.name, message.channel.guild.name)) + continue + role = self.get_role_on_server(managed_role.role_id, server) + if not role: + log.warning("Can't find role {} on server {}".format(managed_role.role_id, server.name)) + continue + roles.append(role) + + if len(roles): + async with message.channel.typing() as typing: + await bot.say_many(message.channel, ['React with {} to any role listed below and I will attempt to assign the role.'.format(self.YES), 'React with {} if you want me to unassign the role.'.format(self.NO)], typing = typing) + for role in roles: + m = await bot.say(message.channel, '"{}" on {}'.format(role.name, role.guild.name) if bot.is_private(message.channel) else role.name, typing = typing) + if m: + await m.add_reaction(self.YES) + await m.add_reaction(self.NO) + elif hiss: + await cat.hiss(message.channel) + else: + await cat.shrug(message.channel) + + async def handle_reaction(self, reaction, message, member, channel, server, event_type, emoji): + if event_type != 'REACTION_ADD': + return + if message is None: + log.error('Handling reaction on null message!') + return + if server is not None: + role_name = message.content + server_name = server.name + else: + m = re.match(r'^"([^"]+)" on (.+)', message.content) + if not m: + log.error('Failed to parse role vending message: {}'.format(message.content)) + return + role_name = m[1] + server_name = m[2] + user = client.get_user(reaction.user_id) + if not user: + log.error('Failed to find user with ID {}!'.format(reaction.user_id)) + return + server = bot.parse_server(server_name, author = user) + if not server: + log.error('Failed to find server "{}"'.format(server_name)) + return + member = server.get_member(reaction.user_id) + role, managed_role, hiss = await self.find_role(role_name, member = member, server = server) + if not role: + log.error('Failed to find role "{}" on server {}!'.format(role_name, server_name)) + return + if not managed_role: + log.error('Role "{}" on server {} is not managed!'.format(role_name, server_name)) + return + if hiss: + return + if emoji.name == self.YES: + try: + await member.add_roles(role, reason = 'Self-assignable role') + log.info('Added role {} to {} on {}'.format(role.name, role.guild.name, member.name)) + except: + log.error("Couldn't add role {} on {} to {}".format(role.name, role.guild.name, member.name)) + elif emoji.name == self.NO: + try: + await member.remove_roles(role, reason = 'Self-assignable role') + log.info('Removed role {} from {} on {}'.format(role.name, role.guild.name, member.name)) + except: + log.error("Couldn't remove role {} on {} from {}".format(role.name, role.guild.name, member.name)) \ No newline at end of file -- 2.7.4