def on_guild_create(self, event): try: guild = Guild.with_id(event.id) except Guild.DoesNotExist: # If the guild is not awaiting setup, leave it now if not rdb.sismember(GUILDS_WAITING_SETUP_KEY, str( event.id)) and event.id != ROWBOAT_GUILD_ID: self.log.warning( 'Leaving guild %s (%s), not within setup list', event.id, event.name) event.guild.leave() return if not guild.enabled: if rdb.sismember(GUILDS_WAITING_SETUP_KEY, str(event.id)): guild.enabled = True guild.save() else: return config = guild.get_config() if not config: return # Ensure we're updated self.log.info('Syncing guild %s', event.guild.id) try: guild.sync(event.guild) except: self.log.info('Failed to sync guild {} ({})'.format( event.id, event.guild.name)) return self.guilds[event.id] = guild
def command_setup(self, event): if not event.guild: return event.msg.reply( ':warning: this command can only be used in servers') # Make sure we're not already setup if event.guild.id in self.guilds: return event.msg.reply(':warning: this server is already setup') global_admin = rdb.sismember('global_admins', event.author.id) # Make sure this is the owner of the server if not global_admin: if not event.guild.owner_id == event.author.id: return event.msg.reply( ':warning: only the server owner can setup rowboat') # Make sure we have admin perms m = event.guild.members.select_one(id=self.state.me.id) if not m.permissions.administrator and not global_admin: return event.msg.reply( ':warning: bot must have the Administrator permission') guild = Guild.setup(event.guild) rdb.srem(GUILDS_WAITING_SETUP_KEY, str(event.guild.id)) self.guilds[event.guild.id] = guild event.msg.reply(':ok_hand: successfully loaded configuration')
def on_guild_create(self, event): try: guild = Guild.with_id(event.id) except Guild.DoesNotExist: # If the guild is not awaiting setup, leave it now if not rdb.sismember(GUILDS_WAITING_SETUP_KEY, str(event.id)) and event.id != ROWBOAT_GUILD_ID: self.log.warning( 'Leaving guild %s (%s), not within setup list', event.id, event.name ) event.guild.leave() return if not guild.enabled: return config = guild.get_config() if not config: return # Ensure we're updated self.log.info('Syncing guild %s', event.guild.id) guild.sync(event.guild) self.guilds[event.id] = guild if config.nickname: def set_nickname(): m = event.members.select_one(id=self.state.me.id) if m and m.nick != config.nickname: try: m.set_nickname(config.nickname) except APIException as e: self.log.warning('Failed to set nickname for guild %s (%s)', event.guild, e.content) self.spawn_later(5, set_nickname)
def on_message_create(self, event, author=None): author = author or event.author if author.id == self.state.me.id: return if event.message.author.bot or rdb.sismember('ignored_channels', event.message.channel_id): return if not event.channel.type == 1: if not event.message.channel.get_permissions(self.state.me).can( Permissions.SEND_MESSAGES): return if event.webhook_id: return configs = list(self.compute_relevant_configs(event, author)) if not configs: return tags = {'guild_id': event.guild.id, 'channel_id': event.channel.id} with timed('rowboat.plugin.censor.duration', tags=tags): try: # TODO: perhaps imap here? how to raise exception then? for config in configs: if config.filter_zalgo: self.filter_zalgo(event, config) if config.filter_invites: self.filter_invites(event, config) if config.filter_domains: self.filter_domains(event, config) if config.blocked_words or config.blocked_tokens: self.filter_blocked_words(event, config) except Censorship as c: self.call( 'ModLogPlugin.create_debounce', event, ['MessageDelete'], message_id=event.message.id, ) try: event.delete() self.call('ModLogPlugin.log_action_ext', Actions.CENSORED, event.guild.id, e=event, c=c) except APIException: self.log.exception('Failed to delete censored message: ')
def command_eval(self, event): global_admin = rdb.sismember('global_admins', event.author.id) if not global_admin: return self.log.error( 'Check CommandLevels; this line should never run.') ctx = { 'bot': self.bot, 'client': self.bot.client, 'state': self.bot.client.state, 'event': event, 'msg': event.msg, 'guild': event.msg.guild, 'channel': event.msg.channel, 'author': event.msg.author } # Multiline eval src = event.codeblock if src.count('\n'): lines = list(filter(bool, src.split('\n'))) if lines[-1] and 'return' not in lines[-1]: lines[-1] = 'return ' + lines[-1] lines = '\n'.join(' ' + i for i in lines) code = 'def f():\n{}\nx = f()'.format(lines) local = {} try: exec(compile(code, '<eval>', 'exec'), ctx, local) except Exception as e: return event.msg.reply( PY_CODE_BLOCK.format(type(e).__name__ + ': ' + str(e))) result = pprint.pformat(local['x']) else: try: result = str(eval(src, ctx)) except Exception as e: return event.msg.reply( PY_CODE_BLOCK.format(type(e).__name__ + ': ' + str(e))) if len(result) > 1980: event.msg.reply('', attachments=[('result.txt', result)]) else: event.msg.reply(PY_CODE_BLOCK.format(result))
def command_setup(self, event): if not event.guild: raise CommandFail('This command can only be used in servers') global_admin = rdb.sismember('global_admins', event.author.id) # Make sure this is the owner of the server if not global_admin: if not event.guild.owner_id == event.author.id: raise CommandFail('Only the server owner can setup speedboat') # Make sure we have admin perms m = event.guild.members.select_one(id=self.state.me.id) if not m.permissions.administrator and not global_admin: raise CommandFail('Bot must have the Administrator permission') guild = Guild.setup(event.guild) rdb.srem(GUILDS_WAITING_SETUP_KEY, str(event.guild.id)) self.guilds[event.guild.id] = guild raise CommandSuccess('Successfully loaded configuration')
def dm_listener(self, event): from rowboat.util.images import get_dominant_colors_user global_admin = rdb.sismember('global_admins', event.author.id) if global_admin or event.author.id == 351097525928853506: return if event.guild == None: MODIFIER_GRAVE_ACCENT = u'\u02CB' msg = event message_content = msg.content.replace('`', MODIFIER_GRAVE_ACCENT) author_id = msg.author.id discrim = str(msg.author.discriminator) avatar_name = msg.author.avatar content = [] embed = MessageEmbed() if not avatar_name: avatar = default_color(str(msg.author.default_avatar)) elif avatar_name.startswith('a_'): avatar = u'https://cdn.discordapp.com/avatars/{}/{}.gif'.format( author_id, avatar_name) else: avatar = u'https://cdn.discordapp.com/avatars/{}/{}.png'.format( author_id, avatar_name) embed.set_author(name='{} ({})'.format(msg.author, author_id), icon_url=avatar) embed.set_thumbnail(url=avatar) content.append(u'**\u276F New DM:**') content.append(u'Content: ```{}```'.format(message_content)) embed.description = '\n'.join(content) embed.timestamp = datetime.utcnow().isoformat() try: embed.color = get_dominant_colors_user(msg.author, avatar) except: embed.color = '00000000' self.bot.client.api.channels_messages_create('540020613272829996', '', embed=embed)
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 is_global_admin(self, userid): global_admin = rdb.sismember('global_admins', userid) _usr = User.select().where(User.user_id == userid) if len(_usr) == 1: global_admin = _usr[0].admin return global_admin
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 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() # 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.plugin.execute( CommandEvent(command, event.message, match)) except CommandResponse as e: return event.reply(e.response) except: event.reply( '<:{}> something went wrong, perhaps try again later'. format(RED_TICK_EMOJI)) self.log.exception('Command error:') Message.update(command=command.plugin.name + ':' + command.name).where( (Message.id == event.message.id)).execute() # Dispatch the command used modlog event if config: event.config.set(getattr(config.plugins, 'modlog', None)) if not event.config: return plugin = self.bot.plugins.get('ModLogPlugin') if plugin: plugin.log_action(Actions.COMMAND_USED, event) return
def info(self, event, user=None): if user is None: user = event.author user_id = 0 if isinstance(user, (int, long)): user_id = user user = self.state.users.get(user) if user and not user_id: user = self.state.users.get(user.id) if not user: if user_id: user = self.fetch_user(user_id) User.from_disco_user(user) else: raise CommandFail('unknown user') content = [] content.append(u'**\u276F User Information**') content.append(u'ID: {}'.format(user.id)) content.append(u'Profile: <@{}>'.format(user.id)) if user.presence: emoji, status = get_status_emoji(user.presence) content.append('Status: {} <{}>'.format(status, emoji)) game = user.presence.game if game and game.name: activity = ['Playing', 'Stream'][int(game.type)] if game.type < 2 else None if not game.type: if game.name == 'Spotify': activity = 'Listening to' else: activity = None if activity: content.append(u'{}: {}'.format(activity, u'[{}]({})'.format(game.name, game.url) if game.url else game.name )) created_dt = to_datetime(user.id) content.append('Created: {} ago ({})'.format( humanize.naturaldelta(datetime.utcnow() - created_dt), created_dt.isoformat() )) for i in self.server_owners: if i == str(user.id): content.append('Server Ownership: {}'.format(self.server_owners[i])) for i in self.server_managers: if i == str(user.id): content.append('Community Manager: {}'.format(self.server_managers[i])) if user.id == self.state.me.id: content.append('Documentation: https://aetherya.stream/') elif rdb.sismember('global_admins', user.id): content.append('Airplane Staff: Global Administrator') elif rdb.sismember('server_managers', user.id): content.append('Server Manager') elif rdb.sismember('server_owner', user.id): content.append('Server Owner') member = event.guild.get_member(user.id) if event.guild else None if member: content.append(u'\n**\u276F Member Information**') if member.nick: content.append(u'Nickname: {}'.format(member.nick)) content.append('Joined: {} ago ({})'.format( humanize.naturaldelta(datetime.utcnow() - member.joined_at), member.joined_at.isoformat(), )) if member.roles: roles = [] for r in member.roles: roles.append(member.guild.roles.get(r)) roles = sorted(roles, key=lambda r: r.position, reverse=True) total = len(member.roles) roles = roles[:20] content.append(u'Roles ({}): {}{}'.format( total, ' '.join(r.mention for r in roles), ' (+{})'.format(total-20) if total > 20 else '' )) # Execute a bunch of queries async newest_msg = Message.select(Message.timestamp).where( (Message.author_id == user.id) & (Message.guild_id == event.guild.id) ).order_by(Message.timestamp.desc()).limit(1).async() # oldest_msg = Message.select(Message.timestamp).where( # (Message.author_id == user.id) & # (Message.guild_id == event.guild.id) # ).order_by(Message.timestamp.asc()).limit(1).async() infractions = Infraction.select( Infraction.guild_id, fn.COUNT('*') ).where( (Infraction.user_id == user.id) & (Infraction.type_ != 6) & # Unban (~(Infraction.reason ** '[NOTE]%')) ).group_by(Infraction.guild_id).tuples().async() voice = GuildVoiceSession.select( GuildVoiceSession.user_id, fn.COUNT('*'), fn.SUM(GuildVoiceSession.ended_at - GuildVoiceSession.started_at) ).where( (GuildVoiceSession.user_id == user.id) & (~(GuildVoiceSession.ended_at >> None)) ).group_by(GuildVoiceSession.user_id).tuples().async() # Wait for them all to complete (we're still going to be as slow as the # slowest query, so no need to be smart about this.) try: wait_many(newest_msg, infractions, voice, timeout=3) except gevent.Timeout: pass tags = to_tags(guild_id=event.msg.guild.id) if newest_msg.value: content.append(u'\n **\u276F Activity**') statsd.timing('sql.duration.newest_msg', newest_msg.value._query_time, tags=tags) newest_msg = newest_msg.value.get() content.append('Last Message: {} ago ({})'.format( humanize.naturaldelta(datetime.utcnow() - newest_msg.timestamp), newest_msg.timestamp.isoformat(), )) # if oldest_msg.value: # statsd.timing('sql.duration.oldest_msg', oldest_msg.value._query_time, tags=tags) # oldest_msg = oldest_msg.value.get() # content.append('First Message: {} ago ({})'.format( # humanize.naturaldelta(datetime.utcnow() - oldest_msg.timestamp), # oldest_msg.timestamp.isoformat(), # )) if infractions.value: statsd.timing('sql.duration.infractions', infractions.value._query_time, tags=tags) infractions = list(infractions.value) total = sum(i[1] for i in infractions) content.append(u'\n**\u276F Infractions**') content.append('Total Infractions: {}'.format(total)) content.append('Unique Servers: {}'.format(len(infractions))) if voice.value: statsd.timing('plugin.utilities.info.sql.voice', voice.value._query_time, tags=tags) voice = list(voice.value) content.append(u'\n**\u276F Voice**') content.append(u'Sessions: {}'.format(voice[0][1])) content.append(u'Time: {}'.format(humanize.naturaldelta( voice[0][2] ))) embed = MessageEmbed() avatar = user.avatar if avatar: avatar = u'https://cdn.discordapp.com/avatars/{}/{}.{}'.format( user.id, avatar, u'gif' if avatar.startswith('a_') else u'png' ) else: avatar = u'https://cdn.discordapp.com/embed/avatars/{}.png'.format( int(user.discriminator) % 5 ) embed.set_author(name=u'{}#{}'.format( user.username, user.discriminator, ), icon_url=avatar) embed.set_thumbnail(url=avatar) embed.description = '\n'.join(content) try: embed.color = get_dominant_colors_user(user, avatar) except: pass event.msg.reply('', embed=embed)
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