async def on_raw_reaction_add(self, payload): if payload.user_id == self.bot.user.id: return if payload.emoji.name == '🚮': is_tracked = False sender_uid = None with sql_cur(self.db) as cur: cur.execute("SELECT messid, sender_uid FROM tracked_messages WHERE messid=?", (payload.message_id,)) row = cur.fetchone() if row: is_tracked = True sender_uid = row[1] if is_tracked: reacting_member = self.bot.get_guild(payload.guild_id).get_member(payload.user_id) can_delete = self.bot.get_channel(payload.channel_id).permissions_for(reacting_member).manage_messages if payload.user_id == sender_uid or can_delete: relevant_message = await self.bot.get_channel(payload.channel_id).get_message(payload.message_id) await relevant_message.delete() elif payload.emoji.name == '\u2733': row = None with sql_cur(self.db) as cur: cur.execute('SELECT command_name, error_name, error_text, full_command_string, full_backtrace FROM error_messages WHERE message_id=? AND channel_id=?;',(payload.message_id, payload.channel_id)) row = cur.fetchone() if not row: return to_edit = await self.bot.get_channel(payload.channel_id).get_message(payload.message_id) new_embed = self._construct_error_embed(row[0],row[1],row[2],row[3],row[4]) await to_edit.edit(content='⚠ Command error ⚠',embed=new_embed)
def _perms_set(self, guild_id, role_id, permissions, mode=None): if not mode: mode = self._GRANT current_perms = 0 with sql_cur(self.db) as cur: cur.execute( 'SELECT permissions FROM permissions WHERE guild_id=? AND role_id=?;', (guild_id, role_id)) res = cur.fetchone() if res: current_perms = res[0] new_perms = current_perms for perm in permissions: if mode == self._DENY: if _has(new_perms, perm) or not _denied(new_perms, perm): new_perms = _deny_perm(new_perms, perm) elif mode == self._GRANT: if _denied(new_perms, perm) or not _has(new_perms, perm): new_perms = _grant_perm(new_perms, perm) elif mode == self._CLEAR: if _has(new_perms, perm) or _denied(new_perms, perm): new_perms = _clear_perm(new_perms, perm) else: raise commands.CommandError( 'Unknown value {0} for permission mode enum.'.format(mode)) self._perms_write(guild_id, role_id, new_perms)
async def on_raw_reaction_remove(self, payload): if payload.user_id == self.bot.user.id: return if payload.emoji.name == '\u2733': row = None with sql_cur(self.db) as cur: cur.execute('SELECT command_name, error_name, error_text, full_command_string, full_backtrace FROM error_messages WHERE message_id=? AND channel_id=?;',(payload.message_id, payload.channel_id)) row = cur.fetchone() if not row: return to_edit = await self.bot.get_channel(payload.channel_id).get_message(payload.message_id) new_embed = self._construct_error_embed(row[0],row[1],row[2],row[3]) await to_edit.edit(content='⚠ Command error ⚠',embed=new_embed)
async def track(message, author=None): ''' ' Marks a message in the database so that it will be automatically ' deleted if the sender or an admin reacts with the 'trash' emoji ''' await message.add_reaction('🚮') sql_db = sql_con() aid = 0 if author: aid = author.id with sql_cur(sql_db) as cur: cur.execute( "INSERT INTO tracked_messages (messid, sender_uid, track_time) VALUES (?, ?, ?);", (message.id, aid, message.created_at))
async def _refresh_region_meta(self, region_meta): region = self.bot.get_channel(region_meta['channel_id']) if not region: with sql_cur(self.db) as cur: cur.execute('DELETE FROM regions WHERE channel_id=?', (region_meta['channel_id'], )) raise commands.CheckFailure( 'Channel for region {0} is missing! Removed associated region data.' ) channel_category_id = None if region_meta['status'] != 1: channel_category_id = region_meta['active_category'] else: with sql_cur(self.db) as cur: cur.execute( 'SELECT inactive_category FROM guild_settings WHERE guild_id=?', (region.guild.id, )) row = cur.fetchone() if not row: raise commands.BadArgument( 'Please use the {0}rpset inactive command to set up a channel category for inactive channels. {0}help rpset inactive for more information.' .format(self.bot.command_prefix)) channel_category_id = row[0] channel_category = None for category in region.guild.categories: if category.id == channel_category_id: channel_category = category await region.edit(name=self._sanitize_channel_name( region_meta['name']), position=0, nsfw=region.is_nsfw(), topic=await self._generate_topic(region_meta), sync_permissions=True, category=channel_category, reason='Updating RP region metadata.') await self._edit_region(region_meta)
async def unmake_region(self, ctx): ''' Removes Hector region status from the channel ' (Note: this does not delete the channel itself) ''' region = await self._get_region(ctx.guild.id, ctx.channel.id) if not region: raise commands.CheckFailure('Channel #{0} is not a region!'.format( ctx.channel.name)) with sql_cur(self.db) as cur: cur.execute('DELETE FROM regions WHERE channel_id=?;', (ctx.channel.id, )) await ctx.message.add_reaction('✅')
def _perms_write(self, guild_id, role_id, perms): with sql_cur(self.db) as cur: cur.execute( 'SELECT permissions FROM permissions WHERE guild_id=? AND role_id=?;', (guild_id, role_id)) res = cur.fetchone() if res: cur.execute( 'UPDATE permissions SET permissions=? WHERE guild_id=? AND role_id=?', (perms, guild_id, role_id)) else: cur.execute( 'INSERT INTO permissions (guild_id, role_id, permissions) VALUES (?,?,?);', (guild_id, role_id, perms))
async def set_inactive(self, ctx): ''' Set the category where inactive region channels are stored ''' with sql_cur(self.db) as cur: cur.execute( 'SELECT inactive_category FROM guild_settings WHERE guild_id=?;', (ctx.guild.id, )) if cur.fetchone(): cur.execute( 'UPDATE guild_settings SET inactive_category=? WHERE guild_id=?;', (ctx.channel.category_id, ctx.guild.id)) else: cur.execute( 'INSERT INTO guild_settings (guild_id, inactive_category) VALUES (?,?);', (ctx.guild.id, ctx.channel.category_id)) await ctx.message.add_reaction('✅')
async def _edit_region(self, region): with sql_cur(self.db) as cur: cur.execute( 'SELECT name FROM regions WHERE channel_id=? AND guild_id=?;', (region['channel_id'], region['guild_id'])) if len(cur.fetchall()) == 0: cur.execute( 'INSERT INTO regions (channel_id, guild_id, name, description, status, active_category) VALUES (?,?,?,?,?,?);', (region['channel_id'], region['guild_id'], region['name'], region['description'], region['status'], region['active_category'])) else: cur.execute( 'UPDATE regions SET name=?,description=?,status=?,active_category=? WHERE channel_id=? AND guild_id=?;', (region['name'], region['description'], region['status'], region['active_category'], region['channel_id'], region['guild_id']))
async def get_permissions(member, guild): tracked_roles = {} with sql_cur(sql_con()) as cur: cur.execute( 'SELECT role_id,permissions FROM permissions WHERE guild_id=?', (guild.id, )) for role in cur.fetchall(): tracked_roles[role[0]] = role[1] if len(tracked_roles) == 0: return 0 permissions = 0 for role in member.roles: if role.id in tracked_roles.keys(): role_perms = tracked_roles[role.id] permissions = _perms_combine(role_perms, permissions) return permissions
async def on_command_error(self, ctx, error): if type(error) == discord.ext.commands.MissingPermissions: await ctx.message.add_reaction('⛔') embed = discord.Embed( title='⛔ Insufficient Permissions', colour=discord.Colour(0x913232), description="You are not permitted to run the command ``{0}``". format(ctx.message.content)) embed.add_field(name="Reason:", value=str(error)) msg = await ctx.send(content='', embed=embed) await track(msg, ctx.author) else: embed = None if not ctx.command: embed = self._construct_unknown_command_embed( str(error), ctx.message.content) else: embed = self._construct_error_embed(ctx.command.name, str(type(error)), str(error), ctx.message.content) await ctx.message.add_reaction('⚠') msg = await ctx.send(content='', embed=embed) await track(msg, ctx.author) if not ctx.command: return await msg.add_reaction('\u2733') with sql_cur(self.db) as cur: bt_string = ''.join( traceback.format_exception(type(error), error, error.__traceback__)) print('Hector encountered an error:\n{0}'.format(bt_string)) cur.execute( 'INSERT INTO error_messages (message_id, channel_id, command_name, error_name, error_text, full_backtrace, full_command_string) VALUES (?,?,?,?,?,?,?);', (msg.id, msg.channel.id, ctx.command.name, str( type(error)), str(error), bt_string, ctx.message.content))
async def _list_regions(self, guild_id=None): regions = [] query = 'SELECT channel_id, guild_id, name, description, status, active_category FROM regions' if guild_id: query = query + ' WHERE guild_id=?;' else: query = query + ';' with sql_cur(self.db) as cur: if guild_id: cur.execute(query, (guild_id, )) else: cur.execute(query) for region in cur.fetchall(): regions.append({ 'channel_id': region[0], 'guild_id': region[1], 'name': region[2], 'description': region[3], 'status': region[4], 'active_category': region[5] }) return regions
async def setup_preset(self, ctx, preset_name): ''' (Admin-only) Sets up permissions based on a built-in preset. ' WARNING: This will OVERWRITE any existing permissions. ''' if not preset_name: raise commands.BadArgument('Please provide a preset name to use.') presets = None with open('data/default_presets.json') as preset_file: presets = json.load(preset_file) preset = None for possible_preset in presets: if possible_preset['name'].lower() == preset_name.lower(): preset = possible_preset if not preset: raise commands.BadArgument( 'Preset {0} not found. Try `{1}perms list` for a list of presets.' .format(preset_name, self.bot.command_prefix)) existing_perms = False with sql_cur(self.db) as cur: cur.execute('SELECT * FROM permissions WHERE guild_id=?;', (ctx.guild.id, )) if cur.fetchone(): existing_perms = True if existing_perms: embed = discord.Embed( title='\U0001f6a8 WARNING!', colour=discord.Colour(0xc7b61a), description= 'Hector already has permission records for this server! Continuing will erase these records and replace them with preset values!' ) embed.add_field(name='If you are sure,', value='Press the \u26a0 button.', inline=False) embed.set_footer( text= 'This command will time out in 60 seconds. To cancel, wait until time-out.' ) warn_msg = await ctx.send(content='', embed=embed) await warn_msg.add_reaction('\u26a0') def check(reaction, user): return user == ctx.message.author and str( reaction.emoji ) == '\u26a0' and reaction.message.id == warn_msg.id try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check) except TimeoutError: await warn_msg.clear_reactions() await warn_msg.edit(content='Confirmation timed out.') await track(warn_msg, ctx.author) return else: await warn_msg.clear_reactions() await warn_msg.edit( content='Confirmed. Setting up permissions from preset {0}.' .format(preset['name'])) with sql_cur(self.db) as cur: cur.execute('DELETE FROM permissions WHERE guild_id=?;', (ctx.guild.id, )) for role in preset['roles']: perm_val = _construct_from_preset_string(role['permissions']) if role['name'] == '*': # Handle @everyone separately with sql_cur(self.db) as cur: cur.execute( 'INSERT INTO permissions (guild_id, role_id, permissions) VALUES (?,?,?);', (ctx.guild.id, ctx.guild.default_role.id, perm_val)) else: if not 'color' in role.keys(): role['color'] = 0x419492 new_role = await ctx.guild.create_role( name=role['name'], colour=discord.Colour(role['color']), mentionable=True, reason= 'Setting up permissions from preset (requesting user: {0})' .format(ctx.message.author)) with sql_cur(self.db) as cur: cur.execute( 'INSERT INTO permissions (guild_id, role_id, permissions) VALUES (?,?,?);', (ctx.guild.id, new_role.id, perm_val)) await ctx.message.add_reaction('✅')