Ejemplo n.º 1
0
def debug(message):
    guild = '-'
    if message.guild:
        guild = message.guild.name
    log.debug(
        f'[{guild}][{message.channel}][{message.author.display_name}] {message.content}'
    )
Ejemplo n.º 2
0
async def task_check_for_data_updates(discord_client):
    filenames = LANG_FILES + [
        'World.json', 'User.json', 'Campaign.json', 'Soulforge.json'
    ]
    now = datetime.datetime.now()
    modified_files = []
    for filename in filenames:
        file_path = GameAssets.path(filename)
        try:
            modification_time = datetime.datetime.fromtimestamp(
                os.path.getmtime(file_path))
        except FileNotFoundError:
            continue
        modified = now - modification_time <= datetime.timedelta(
            seconds=CONFIG.get('file_update_check_seconds'))
        if modified:
            modified_files.append(filename)
    if modified_files:
        log.debug(
            f'Game file modification detected, reloading {", ".join(modified_files)}.'
        )
        await asyncio.sleep(5)
        lock = asyncio.Lock()
        async with lock:
            try:
                old_expander = discord_client.expander
                del discord_client.expander
                discord_client.expander = TeamExpander()
                update_translations()
            except Exception as e:
                log.error('Could not update game file. Stacktrace follows.')
                log.exception(e)
                discord_client.expander = old_expander
Ejemplo n.º 3
0
 async def soulforge_preview(self,
                             message,
                             lang,
                             search_term,
                             release_date=None,
                             switch=None,
                             **kwargs):
     if switch is None:
         switch = CONFIG.get('default_news_platform') == 'switch'
     async with message.channel.typing():
         if message.interaction_id:
             await self.send_slash_command_result(
                 message,
                 content=None,
                 embed=None,
                 response_type=InteractionResponseType.PONG.value)
         start = time.time()
         weapon_data = self.expander.get_soulforge_weapon_image_data(
             search_term, release_date, switch, lang)
         if not weapon_data:
             e = discord.Embed(
                 title=
                 f'Weapon search for `{search_term}` did not yield any result',
                 description=':(',
                 color=self.BLACK)
             return await self.answer(message, e)
         image_data = graphic_soulforge_preview.render_all(weapon_data)
         result = discord.File(image_data, f'soulforge_{release_date}.png')
         duration = time.time() - start
         log.debug(f'Soulforge generation took {duration:0.2f} seconds.')
         await message.channel.send(file=result)
Ejemplo n.º 4
0
 async def campaign_preview(self,
                            message,
                            lang,
                            switch=None,
                            team_code=None,
                            **kwargs):
     switch = switch or CONFIG.get('default_news_platform') == 'switch'
     async with message.channel.typing():
         if hasattr(message, 'interaction_id') and message.interaction_id:
             await self.send_slash_command_result(
                 message,
                 response_type=InteractionResponseType.MESSAGE.value,
                 content='Please stand by ...',
                 embed=None)
         start = time.time()
         campaign_data = self.expander.get_campaign_tasks(lang)
         campaign_data['switch'] = switch
         campaign_data['team'] = None
         if team_code:
             campaign_data['team'] = self.expander.get_team_from_message(
                 team_code, lang)
         image_data = graphic_campaign_preview.render_all(campaign_data)
         result = discord.File(
             image_data, f'campaign_{campaign_data["start_date"]}.png')
         duration = time.time() - start
         log.debug(f'Soulforge generation took {duration:0.2f} seconds.')
         await message.channel.send(file=result)
Ejemplo n.º 5
0
 async def register_slash_commands(self):
     guild_id = CONFIG.get('slash_command_guild_id')
     existing_commands = await get_all_commands(self.user.id,
                                                TOKEN,
                                                guild_id=guild_id)
     new_command_names = [c['function'] for c in COMMAND_REGISTRY]
     for command in existing_commands:
         if command['name'] not in new_command_names or CONFIG.get(
                 'deregister_slash_commands'):
             log.debug(f'Deregistering slash command {command["name"]}...')
             await remove_slash_command(self.user.id, TOKEN, guild_id,
                                        command['id'])
     if not CONFIG.get('register_slash_commands'):
         return
     for command in COMMAND_REGISTRY:
         if 'description' not in command:
             continue
         if command['function'] in [c['name'] for c in existing_commands]:
             continue
         log.debug(f'Registering slash command {command["function"]}...')
         await add_slash_command(self.user.id,
                                 bot_token=TOKEN,
                                 guild_id=guild_id,
                                 cmd_name=command['function'],
                                 description=command['description'],
                                 options=command.get('options', []))
Ejemplo n.º 6
0
 async def on_slash_command(self, function, options, message):
     try:
         if 'lang' not in options:
             options['lang'] = self.language.get(message.guild)
         debug(message)
         await function(message=message, **options)
     except discord.HTTPException as e:
         log.debug(f'Could not answer to slash command: {e}')
Ejemplo n.º 7
0
 def is_banner(source):
     request = requests.get(source)
     image = Image.open(BytesIO(request.content))
     size = image.size
     ratio = size[0] / size[1]
     arbitrary_ratio_limit_for_banners = 10
     log.debug(f'[NEWS] Found a ration of {ratio} in {source}.')
     return ratio >= arbitrary_ratio_limit_for_banners
Ejemplo n.º 8
0
async def task_update_dbl_stats(client):
    if client.topgg_client is None:
        return
    try:
        await client.topgg_client.post_guild_count()
        log.debug('Posted server count ({})'.format(client.topgg_client.guild_count))
    except Exception as e:
        log.exception('Failed to post server count\n{}: {}'.format(type(e).__name__, e))
Ejemplo n.º 9
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        log.debug(
            f'--------------------------- Starting {self.BOT_NAME} v{self.VERSION} --------------------------'
        )

        self.expander = TeamExpander()
        self.tower_data = TowerOfDoomData(self.my_emojis)
        self.prefix = models.Prefix(CONFIG.get('default_prefix'))
        self.language = models.Language(CONFIG.get('default_language'))
        self.subscriptions = models.Subscriptions()
        self.views = Views(emojis={})
Ejemplo n.º 10
0
    async def load_rescues(cls, client):
        db = DB()
        db_result = db.cursor.execute('SELECT * FROM PetRescue;').fetchall()
        rescues = []
        broken_rescues = []
        for i, entry in enumerate(db_result, start=1):
            log.debug(f'Loading pet rescue {i} of {len(db_result)}')
            pet = client.expander.pets[entry['pet_id']].copy()
            client.expander.translate_pet(pet, entry['lang'])

            try:
                channel = await client.fetch_channel(entry['channel_id'])
                guild = None
                if not isinstance(channel, discord.DMChannel):
                    guild = channel.guild
                message = FakeMessage('author', guild, channel, 'content')
                if entry['message_id']:
                    message = await channel.fetch_message(entry['message_id'])
            except discord.errors.DiscordException:
                broken_rescues.append(entry['id'])
                continue
            rescue = PetRescue(
                pet=pet,
                time_left=0,
                message=message,
                mention=entry['mention'],
                lang=entry['lang'],
                answer_method=client.answer,
                config=client.pet_rescue_config,
            )
            try:
                if entry['alert_message_id']:
                    rescue.alert_message = await channel.fetch_message(
                        entry['alert_message_id'])
                rescue.pet_message = await channel.fetch_message(
                    entry['pet_message_id'])
            except discord.errors.DiscordException:
                broken_rescues.append(entry['id'])
                continue
            rescue.start_time = entry['start_time']
            rescues.append(rescue)
        db.close()

        if broken_rescues:
            log.debug(
                f'Pruning {len(broken_rescues)} broken pet rescues from the database: {broken_rescues}.'
            )
        for rescue_id in broken_rescues:
            await cls.delete_by_id(rescue_id=rescue_id)

        return rescues
Ejemplo n.º 11
0
 async def handle_team_code(self,
                            message,
                            lang,
                            team_code,
                            shortened='',
                            **kwargs):
     team = self.expander.get_team_from_message(team_code, lang)
     if not team or not team['troops']:
         log.debug(f'nothing found in message {team_code}.')
         return
     author = message.author.display_name
     author = await pluralize_author(author)
     e = self.views.render_team(team, author, shortened)
     await self.answer(message, e)
Ejemplo n.º 12
0
 async def change_prefix(self, message, new_prefix, **kwargs):
     my_prefix = self.prefix.get(message.guild)
     if len(new_prefix) != 1:
         e = self.generate_response(
             'Prefix change', self.RED, 'Error',
             f'Your new prefix has to be 1 characters long,'
             f' `{new_prefix}` has {len(new_prefix)}.')
         await self.answer(message, e)
         return
     await self.prefix.set(message.guild, new_prefix)
     e = self.generate_response(
         'Administrative action', self.RED, 'Prefix change',
         f'Prefix was changed from `{my_prefix}` to `{new_prefix}`')
     await self.answer(message, e)
     log.debug(
         f'[{message.guild.name}] Changed prefix from {my_prefix} to {new_prefix}'
     )
Ejemplo n.º 13
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        log.debug(
            f'--------------------------- Starting {self.BOT_NAME} v{self.VERSION} --------------------------'
        )

        self.expander = TeamExpander()
        self.tower_data = TowerOfDoomData(self.my_emojis)
        self.prefix = models.Prefix(CONFIG.get('default_prefix'))
        self.language = models.Language(CONFIG.get('default_language'))
        self.subscriptions = models.Subscriptions()
        self.views = Views(emojis={})
        self.pet_rescues = []
        self.pet_rescue_config: PetRescueConfig = None
        token = CONFIG.get('dbl_token')
        self.dbl_client = None
        self.server_status_cache = {'last_updated': datetime.datetime.min}
        if token:
            self.dbl_client = dbl.DBLClient(self, token)
Ejemplo n.º 14
0
    async def on_guild_join(self, guild):
        await super().on_guild_join(guild)
        first_writable_channel = self.first_writable_channel(guild)

        ban = Ban.get(guild.id)
        if ban:
            log.debug(
                f'Guild {guild} ({guild.id}) was banned by {ban["author_name"]} because: {ban["reason"]}'
            )
            if first_writable_channel:
                try:
                    ban_message = self.views.render_ban_message(ban)
                    await first_writable_channel.send(embed=ban_message)
                finally:
                    return await guild.leave()

        welcome_message = self.views.render_welcome_message(
            self.prefix.get(guild))
        if first_writable_channel:
            await first_writable_channel.send(embed=welcome_message)
Ejemplo n.º 15
0
 async def register_slash_commands(self):
     log.debug('Deregistering all slash commands...')
     guild_id = CONFIG.get('slash_command_guild_id')
     for command in await get_all_commands(self.user.id,
                                           TOKEN,
                                           guild_id=guild_id):
         await remove_slash_command(self.user.id, TOKEN, guild_id,
                                    command['id'])
     if not CONFIG.get('register_slash_commands'):
         return
     log.debug(f'Registering slash commands...')
     for command in COMMAND_REGISTRY:
         if 'description' not in command:
             continue
         await add_slash_command(self.user.id,
                                 bot_token=TOKEN,
                                 guild_id=guild_id,
                                 cmd_name=command['function'],
                                 description=command['description'],
                                 options=command.get('options', []))
Ejemplo n.º 16
0
    async def on_ready(self):
        if not self.bot_connect:
            self.bot_connect = datetime.datetime.now()
            log.debug(f'Connected at {self.bot_connect}.')
        else:
            await self.on_resumed()
        self.invite_url = f'https://discordapp.com/api/oauth2/authorize' \
                          f'?client_id={self.user.id}' \
                          f'&scope=bot' \
                          f'&permissions={self.permissions.value}'
        log.info(f'Logged in as {self.user.name}')

        subscriptions = sum([s.get('pc', True) for s in self.subscriptions])
        log.info(f'{subscriptions} channels subscribed to news.')
        log.info(f'Active in {len(self.guilds)} guilds.')

        game = discord.Game("Gems of War")
        await self.change_presence(status=discord.Status.online, activity=game)
        await self.update_base_emojis()
        self.views.my_emojis = self.my_emojis
Ejemplo n.º 17
0
    async def change_language(self, message, new_language, **kwargs):
        my_language = self.language.get(message.guild)
        if new_language not in LANGUAGES:
            e = discord.Embed(title='Default Language', color=self.BLACK)
            e.add_field(
                name='Error',
                value=f'`{new_language}` is not a valid language code.')
            self.add_available_languages(e)
            await self.answer(message, e)
            return

        await self.language.set(message.guild, new_language)
        e = self.generate_response(
            'Default Language', self.WHITE,
            f'Default language for {message.guild}',
            f'Default language was changed from `{my_language}` to `{new_language}`.'
        )
        await self.answer(message, e)
        log.debug(
            f'[{message.guild.name}] Changed language from {my_language} to {new_language}.'
        )
Ejemplo n.º 18
0
 async def handle_team_code(self,
                            message,
                            lang,
                            team_code,
                            shortened='',
                            **kwargs):
     team = self.expander.get_team_from_message(team_code, lang)
     if not team or not team['troops']:
         log.debug(f'nothing found in message {team_code}.')
         return
     author = message.author.display_name
     author = await pluralize_author(author)
     if kwargs.get('title') is None:
         team_code = None
     e = self.views.render_team(team,
                                author,
                                shortened,
                                team_code=team_code,
                                title=kwargs.get('title'))
     await self.answer(message, e)
     if team_code:
         await message.channel.send(content=f'[{team_code}]')
Ejemplo n.º 19
0
    async def show_latest_news(self):
        if not self.is_ready():
            return

        with open(NewsDownloader.NEWS_FILENAME) as f:
            articles = json.load(f)
            articles.reverse()
        if articles:
            log.debug(
                f'Distributing {len(articles)} news articles to {len(self.subscriptions)} channels.'
            )
        for article in articles:
            e = discord.Embed(title='Gems of War news',
                              color=self.WHITE,
                              url=article['url'])
            content = self.views.trim_news_to_length(article['content'],
                                                     article['url'])
            e.add_field(name=article['title'], value=content)
            embeds = [e]
            for image_url in article['images']:
                e = discord.Embed(type='image', color=self.WHITE)
                e.set_image(url=image_url)
                embeds.append(e)

            for subscription in self.subscriptions:
                relevant_news = subscription.get(article['platform'])
                if not relevant_news:
                    continue
                channel = self.get_channel(subscription['channel_id'])
                if not channel:
                    log.debug(
                        f'Subscription {subscription} is broken, skipping.')
                    continue
                log.debug(
                    f'Sending [{article["platform"]}] {article["title"]} to {channel.guild.name}/{channel.name}.'
                )
                if not await self.is_writable(channel):
                    message = 'is not writable' if channel else 'does not exist'
                    log.debug(f'Channel {message}.')
                    continue
                try:
                    for e in embeds:
                        await channel.send(embed=e)
                except Exception as e:
                    log.error('Could not send out news, exception follows')
                    log.exception(e)
        with open(NewsDownloader.NEWS_FILENAME, 'w') as f:
            f.write('[]')
Ejemplo n.º 20
0
 async def soulforge_preview(self,
                             message,
                             lang,
                             search_term,
                             release_date=None,
                             switch=False,
                             **kwargs):
     async with message.channel.typing():
         start = time.time()
         weapon_data = self.expander.get_soulforge_weapon_image_data(
             search_term, release_date, switch, lang)
         if not weapon_data:
             e = discord.Embed(
                 title=
                 f'Weapon search for `{search_term}` did not yield any result',
                 description=':(',
                 color=self.BLACK)
             return await self.answer(message, e)
         image_data = soulforge_preview.render_all(weapon_data)
         result = discord.File(image_data, f'soulforge_{release_date}.png')
         duration = time.time() - start
         log.debug(f'Soulforge generation took {duration:0.2f} seconds.')
         await message.channel.send(file=result)
Ejemplo n.º 21
0
    async def on_ready(self):
        if not self.bot_connect:
            self.bot_connect = datetime.datetime.now()
            log.debug(f'Connected at {self.bot_connect}.')
        else:
            await self.on_resumed()
        self.invite_url = discord.utils.oauth_url(client_id=self.user.id,
                                                  permissions=self.permissions)
        subscriptions = sum([s.get('pc', True) for s in self.subscriptions])
        log.info(f'{subscriptions} channels subscribed to PC news.')

        game = discord.Game("Gems of War")
        await self.change_presence(status=discord.Status.online, activity=game)

        await self.update_base_emojis()
        self.views.my_emojis = self.my_emojis
        log.info(f'Logged in as {self.user.name}')
        log.info(f'Active in {len(self.guilds)} guilds.')

        self.pet_rescue_config = PetRescueConfig()
        await self.pet_rescue_config.load()
        self.pet_rescues = await PetRescue.load_rescues(self)
        log.debug(f'Loaded {len(self.pet_rescues)} pet rescues after restart.')
        await self.register_slash_commands()
Ejemplo n.º 22
0
    async def show_latest_news(self):
        if not self.is_ready():
            return

        with open(NewsDownloader.NEWS_FILENAME) as f:
            articles = json.load(f)
            articles.reverse()
        if articles:
            log.debug(
                f'Distributing {len(articles)} news articles to {len(self.subscriptions)} channels.'
            )
        for article in articles:
            embeds = self.views.render_news(article)
            for subscription in self.subscriptions:
                relevant_news = subscription.get(article['platform'])
                if not relevant_news:
                    continue
                channel = self.get_channel(subscription['channel_id'])
                if not channel:
                    log.debug(
                        f'Subscription {subscription} is broken, skipping.')
                    continue
                log.debug(
                    f'Sending [{article["platform"]}] {article["title"]} to {channel.guild.name}/{channel.name}.'
                )
                if not await self.is_writable(channel):
                    message = 'is not writable' if channel else 'does not exist'
                    log.debug(f'Channel {message}.')
                    continue
                try:
                    for e in embeds:
                        await channel.send(embed=e)
                except Exception as ex:
                    log.error('Could not send out news, exception follows')
                    log.error(repr(e.fields))
                    log.exception(ex)
        with open(NewsDownloader.NEWS_FILENAME, 'w') as f:
            f.write('[]')