# Emote Collector collects emotes from other servers for use by people without Nitro # Copyright © 2018–2019 lambda#0987 # # Emote Collector is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # Emote Collector is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Emote Collector. If not, see <https://www.gnu.org/licenses/>. import asyncio import contextlib import inspect import itertools import json import logging import traceback from pathlib import Path import asyncpg import discord import jinja2 from bot_bin.bot import Bot from braceexpand import braceexpand from discord.ext import commands try: import uvloop except ImportError: pass # Windows else: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) # set BASE_DIR before importing utils because utils.i18n depends on it BASE_DIR = Path(__file__).parent from . import utils from . import extensions logging.basicConfig(level=logging.INFO) logger = logging.getLogger('bot') class EmoteCollector(Bot): def __init__(self, **kwargs): super().__init__(setup_db=True, **kwargs) self.jinja_env = jinja2.Environment( loader=jinja2.FileSystemLoader(str(BASE_DIR / 'sql')), line_statement_prefix='-- :') def process_config(self): super().process_config() self.config['backend_user_accounts'] = set(self.config['backend_user_accounts']) with contextlib.suppress(KeyError): self.config['copyright_license_file'] = BASE_DIR / self.config['copyright_license_file'] utils.SUCCESS_EMOJIS = self.config.get('success_or_failure_emojis', ('❌', '✅')) ### Events async def on_message(self, message): if self.should_reply(message): await self.set_locale(message) await self.process_commands(message) async def set_locale(self, message): locale = await self.cogs['Locales'].locale(message) utils.i18n.current_locale.set(locale) # https://github.com/Rapptz/RoboDanny/blob/ca75fae7de132e55270e53d89bc19dd2958c2ae0/bot.py#L77-L85 async def on_command_error(self, context, error): if isinstance(error, commands.NoPrivateMessage): await context.author.send(_('This command cannot be used in private messages.')) elif isinstance(error, commands.DisabledCommand): message = _('Sorry. This command is disabled and cannot be used.') try: await context.author.send(message) except discord.Forbidden: await context.send(message) elif isinstance(error, commands.NotOwner): logger.error('%s tried to run %s but is not the owner', context.author, context.command.name) with contextlib.suppress(discord.HTTPException): await context.try_add_reaction(utils.SUCCESS_EMOJIS[False]) elif isinstance(error, (commands.UserInputError, commands.CheckFailure)): await context.send(error) elif ( isinstance(error, commands.CommandInvokeError) # abort if it's overridden and getattr( type(context.cog), 'cog_command_error', # treat ones with no cog (e.g. eval'd ones) as being in a cog that did not override commands.Cog.cog_command_error) is commands.Cog.cog_command_error ): if not isinstance(error.original, discord.HTTPException): logger.error('"%s" caused an exception', context.message.content) logger.error(''.join(traceback.format_tb(error.original.__traceback__))) # pylint: disable=logging-format-interpolation logger.error('{0.__class__.__name__}: {0}'.format(error.original)) await context.send(_('An internal error occurred while trying to run that command.')) elif isinstance(error.original, discord.Forbidden): await context.send(_("I'm missing permissions to perform that action.")) ### Utility functions async def get_context(self, message, cls=None): return await super().get_context(message, cls=cls or utils.context.CustomContext) # https://github.com/Rapptz/discord.py/blob/814b03f5a8a6faa33d80495691f1e1cbdce40ce2/discord/ext/commands/core.py#L1338-L1346 def has_permissions(self, message, **perms): guild = message.guild me = guild.me if guild is not None else self.user permissions = message.channel.permissions_for(me) for perm, value in perms.items(): if getattr(permissions, perm, None) != value: return False return True def queries(self, template_name): return self.jinja_env.get_template(str(template_name)).module ### Init / Shutdown startup_extensions = list(braceexpand("""{ emote_collector.extensions.{ locale, file_upload_hook, logging, db, emote, api, gimme, meta, stats, meme, bingo.{ db, commands}}, jishaku, bot_bin.{ misc, debug, sql}} """.replace('\t', '').replace('\n', ''))) def load_extensions(self): utils.i18n.set_default_locale() super().load_extensions()