def on_commands_info(self, event, mid): cmd = Command.select(Command, Message, Channel).join( Message, on=(Command.message_id == Message.id).alias('message')).join( Channel, on=(Channel.channel_id == Message.channel_id).alias('channel') ).join( User, on=(User.user_id == Message.author_id).alias('author')).where( Command.message_id == mid).order_by( Message.timestamp.desc(), ).get() embed = MessageEmbed() embed.title = '{}.{} ({})'.format(cmd.plugin, cmd.command, cmd.message.id) embed.set_author(name=str(cmd.message.author), icon_url=cmd.message.author.get_avatar_url()) embed.color = 0x77dd77 if cmd.success else 0xff6961 if not cmd.success: embed.description = '```{}```'.format(cmd.traceback) embed.add_field(name='Message', value=cmd.message.content) embed.add_field(name='Channel', value='{} `{}`'.format(cmd.message.channel.name, cmd.message.channel.channel_id)) embed.add_field(name='Guild', value=str(cmd.message.guild_id)) event.msg.reply(embed=embed)
def on_commands_stats(self, event, name): if '.' in name: plugin, command = name.split('.', 1) q = ( (Command.plugin == plugin) & (Command.command == command) ) else: q = (Command.command == name) result = list(Command.select( fn.COUNT('*'), Command.success, ).where(q).group_by(Command.success).order_by(fn.COUNT('*').desc()).tuples()) success, error = 0, 0 for count, check in result: if check: success = count else: error = count event.msg.reply('Command `{}` was used a total of {} times, {} of those had errors'.format( name, success + error, error ))
def on_commands_errors(self, event): q = Command.select().join(Message, on=(Command.message_id == Message.id)).where( Command.success == 0).order_by( Message.timestamp.desc()).limit(10) tbl = MessageTable() tbl.set_header('ID', 'Command', 'Error') for err in q: tbl.add(err.message_id, '{}.{}'.format(err.plugin, err.command), err.traceback.split('\n')[-2]) event.msg.reply(tbl.compile())
def on_commands_usage(self, event): q = Command.select( fn.COUNT('*'), Command.plugin, Command.command, ).group_by(Command.plugin, Command.command).order_by(fn.COUNT('*').desc()).limit(25) tbl = MessageTable() tbl.set_header('Plugin', 'Command', 'Usage') for count, plugin, command in q.tuples(): tbl.add(plugin, command, count) event.msg.reply(tbl.compile())
def on_message_create(self, event): """ This monstrosity of a function handles the parsing and dispatching of commands. """ # Ignore messages sent by bots if event.message.author.bot: return # If this is message for a guild, grab the guild object if hasattr(event, 'guild') and event.guild: guild_id = event.guild.id elif hasattr(event, 'guild_id') and event.guild_id: guild_id = event.guild_id else: guild_id = None guild = self.guilds.get(event.guild.id) if guild_id else None config = guild and guild.get_config() # If the guild has configuration, use that (otherwise use defaults) if config and config.commands: commands = list( self.bot.get_commands_for_message(config.commands.mention, {}, config.commands.prefix, event.message)) elif guild_id: # Otherwise, default to requiring mentions commands = list( self.bot.get_commands_for_message(True, {}, '', event.message)) else: if ENV != 'prod': if not event.message.content.startswith(ENV + '!'): return event.message.content = event.message.content[len(ENV) + 1:] # DM's just use the commands (no prefix/mention) commands = list( self.bot.get_commands_for_message(False, {}, '', event.message)) # If we didn't find any matching commands, return if not len(commands): return event.user_level = self.get_level(event.guild, event.author) if event.guild else 0 # Grab whether this user is a global admin # TODO: cache this global_admin = rdb.sismember('global_admins', event.author.id) # Iterate over commands and find a match for command, match in commands: if command.level == -1 and not global_admin: continue level = command.level if guild and not config and command.triggers[0] != 'setup': continue elif config and config.commands and command.plugin != self: overrides = {} for obj in config.commands.get_command_override(command): overrides.update(obj) if overrides.get('disabled'): continue level = overrides.get('level', level) if not global_admin and event.user_level < level: continue with timed('rowboat.command.duration', tags={ 'plugin': command.plugin.name, 'command': command.name }): try: command_event = CommandEvent(command, event.message, match) command_event.user_level = event.user_level command.plugin.execute(command_event) except CommandResponse as e: event.reply(e.response) except: tracked = Command.track(event, command, exception=True) self.log.exception('Command error:') with self.send_control_message() as embed: embed.title = u'Command Error: {}'.format(command.name) embed.color = 0xff6961 embed.add_field(name='Author', value='({}) `{}`'.format( event.author, event.author.id), inline=True) embed.add_field(name='Channel', value='({}) `{}`'.format( event.channel.name, event.channel.id), inline=True) embed.description = '```{}```'.format(u'\n'.join( tracked.traceback.split('\n')[-8:])) return event.reply( '<:{}> something went wrong, perhaps try again later'. format(RED_TICK_EMOJI)) Command.track(event, command) # Dispatch the command used modlog event if config: modlog_config = getattr(config.plugins, 'modlog', None) if not modlog_config: return self._attach_local_event_data(event, 'modlog', event.guild.id) plugin = self.bot.plugins.get('ModLogPlugin') if plugin: plugin.log_action(Actions.COMMAND_USED, event) return
def guild_stats_self(guild): def serialize_user(gcc): for i in gcc: user_raw = ''' SELECT username, discriminator FROM users WHERE user_id=%s AND bot=false; ''' user = list(User.raw(user_raw, i[1]).tuples()) if user: return { 'user': { 'username': user[0][0], 'discrim': str(user[0][1]), 'id': i[1] }, 'user_count': int(i[0]), } return { 'user': '******', 'user_count': 0, } def serialize_emoji(gcc): for i in gcc: emoji_raw = ''' SELECT emoji_id FROM guild_emojis WHERE emoji_id=%s AND guild_id=%s; ''' emoji = list( GuildEmoji.raw(emoji_raw, i[0], guild.guild_id).tuples()) if emoji: return str(emoji[0][0]) return '230870076126003200' data = json.loads( rdb.get('web:guild:{}:stats'.format(guild.guild_id)) or '{}') if not data: # Totals totals_messages = Message.select(Message.id).where( (Message.guild_id == guild.guild_id)).count() totals_infractions = Infraction.select(Infraction.id).where( (Infraction.guild_id == guild.guild_id)).count() # Peaks ## Messages peaks_messages_raw = ''' SELECT count(id), author_id FROM messages WHERE guild_id=%s GROUP BY author_id ORDER BY count DESC LIMIT 5; ''' peaks_messages = list( Message.raw(peaks_messages_raw, guild.guild_id).tuples()) ## Infractions peaks_infractions_raw = ''' SELECT count(id), user_id FROM infractions WHERE guild_id=%s GROUP BY user_id ORDER BY count DESC LIMIT 5; ''' peaks_infractions = list( Infraction.raw(peaks_infractions_raw, guild.guild_id).tuples()) ## Emoji peaks_emoji_raw = ''' SELECT id, count(*) FROM ( SELECT unnest(emojis) as id FROM messages WHERE guild_id=%s and cardinality(emojis) > 0 ) q GROUP BY 1 ORDER BY 2 DESC LIMIT 5 ''' peaks_emoji = list( Message.raw(peaks_emoji_raw, guild.guild_id).tuples()) ## Command peaks_command_raw = ''' SELECT count(c.command), c.command FROM commands c INNER JOIN messages m ON (c.message_id = m.id) WHERE m.guild_id=%s GROUP BY 2 ORDER BY 1 DESC LIMIT 1; ''' peaks_command = list( Command.raw(peaks_command_raw, guild.guild_id).tuples()) if totals_messages: totals_messages = totals_messages else: totals_messages = 0 if totals_infractions: totals_infractions = totals_infractions else: totals_infractions = 0 if peaks_messages: pm = serialize_user(peaks_messages) else: pm = { 'user': '******', 'user_count': 0, } if peaks_infractions: pi = serialize_user(peaks_infractions) else: pi = { 'user': '******', 'user_count': 0, } if peaks_emoji: anim = False peaks_emoji_id = serialize_emoji(peaks_emoji) url = 'https://discordapp.com/api/emojis/{}.gif'.format( peaks_emoji_id) r = requests.get(url) try: r.raise_for_status() anim = True except requests.HTTPError: pass if anim: peaks_emoji_ext = 'gif' else: peaks_emoji_ext = 'png' else: peaks_emoji_id = '230870076126003200' peaks_emoji_ext = 'png' if peaks_command: peaks_command = '{1}'.format(*peaks_command[0]) else: peaks_command = 'N/A' data = { 'totals': { 'messages': totals_messages, 'infractions': totals_infractions, }, 'peaks': { 'messages': pm, 'infractions': pi, 'emoji': { 'id': peaks_emoji_id, 'ext': peaks_emoji_ext, }, 'command': peaks_command, }, } rdb.setex('web:guild:{}:stats'.format(guild.guild_id), json.dumps(data), 600) return jsonify(data)
def on_message_create(self, event, is_tag=False): """ This monstrosity of a function handles the parsing and dispatching of commands. """ # Ignore messages sent by bots if event.message.author.bot or rdb.sismember('ignored_channels', event.message.channel_id): return # If this is message for a guild, grab the guild object if hasattr(event, 'guild') and event.guild: guild_id = event.guild.id elif hasattr(event, 'guild_id') and event.guild_id: guild_id = event.guild_id else: guild_id = None guild = self.guilds.get(event.guild.id) if guild_id else None config = guild and guild.get_config() cc = config.commands if config else None # If the guild has configuration, use that (otherwise use defaults) if config and config.commands: commands = list( self.bot.get_commands_for_message(config.commands.mention, {}, config.commands.prefix, event.message)) elif guild_id: # Otherwise, default to requiring mentions commands = list( self.bot.get_commands_for_message(True, {}, '', event.message)) else: # if ENV != 'prod': if ENV not in 'prod': if not event.message.content.startswith(ENV + '!'): return event.message.content = event.message.content[len(ENV) + 1:] # DM's just use the commands (no prefix/mention) commands = list( self.bot.get_commands_for_message(False, {}, '', event.message)) # if no command, attempt to run as a tag if not commands: if is_tag: return if not event.guild or not self.bot.plugins.get('TagsPlugin'): return if not config or not config.plugins or not config.plugins.tags: return prefixes = [] if cc.prefix: prefixes.append(cc.prefix) if cc.mention: prefixes.append('{} '.format(self.bot.client.state.me.mention)) if not prefixes: return tag_re = re.compile('^({})(.+)'.format( re.escape('|'.join(prefixes)))) m = tag_re.match(event.message.content) if not m: return sqlplugin = self.bot.plugins.get('SQLPlugin') if sqlplugin: sqlplugin.tag_messages.append(event.message.id) event.message.content = '{}tags show {}'.format( m.group(1), m.group(2)) return self.on_message_create(event, True) event.user_level = self.get_level(event.guild, event.author) if event.guild else 0 # Grab whether this user is a global admin # TODO: cache this ## this is technically already cached and runs fastest this way global_admin = rdb.sismember('global_admins', event.author.id) # Iterate over commands and find a match for command, match in commands: if command.level == -1 and not global_admin: continue level = command.level if guild and not config and command.triggers[0] != 'setup': continue elif config and config.commands and command.plugin != self: overrides = {} for obj in config.commands.get_command_override(command): overrides.update(obj) if overrides.get('disabled'): continue level = overrides.get('level', level) if not global_admin and event.user_level < level: continue try: command_event = CommandEvent(command, event.message, match) command_event.user_level = event.user_level command.plugin.execute(command_event) except CommandResponse as e: if is_tag: return event.reply(e.response) except: tracked = Command.track(event, command, exception=True) self.log.exception('Command error:') with self.send_control_message() as embed: embed.title = 'Command Error: {}'.format(command.name) embed.color = 0xff6961 embed.add_field(name='Author', value='({}) `{}`'.format( event.author, event.author.id), inline=True) embed.add_field(name='Channel', value='({}) `{}`'.format( event.channel.name, event.channel.id), inline=True) embed.description = '```{}```'.format('\n'.join( tracked.traceback.split('\n')[-8:])) return event.reply( '<:{}> something went wrong, perhaps try again later'. format(RED_TICK_EMOJI)) Command.track(event, command) # Dispatch the command used modlog event if config: modlog_config = getattr(config.plugins, 'modlog', None) if not modlog_config: return if is_tag: # Yes, I know, this is ugly but I don't have better event.content = event.content.replace('tags show ', '', 1) self._attach_local_event_data(event, 'modlog', event.guild.id) plugin = self.bot.plugins.get('ModLogPlugin') if plugin: plugin.log_action(Actions.COMMAND_USED, event) return