yor-discord-bot / cogs / genshin.py
genshin.py
Raw
"""This file contains the Genshin Discord Cog extension.
"""
import discord
from discord.ext import commands
from discord import NotFound
from time import time
from asyncio import exceptions

from py_files.src.gaming_libraries import genshin_kqm as kqm


class Genshin(commands.Cog):
    """
    This class provides functions related to Genshin Impact.
    """
    def __init__(self, bot):
        self.bot = bot
        self._infographs = kqm.kqm_infographics()
        self._guides = kqm.kqm_guides()
        self._kqm_characters = self._infographs.keys()
        self._embed_colors = (255, 226, 136) # r, g, b color values
        self._sent_ids = set() # for reaction remove
        self._kqm_access_time = time() # prevents scraping kqm too frequently.

    @commands.command(name='kqm')
    async def kqm_guide(self, ctx, *argv):
        """
        Sends the requested kqm infographic for the given character.
        """
        # process user input
        user_input = ''
        for arg in argv:
            user_input += arg + ' '
        user_input = user_input.strip()

        # get character name
        character = user_input
        for avail_name in self._kqm_characters:
            if user_input.lower() in avail_name.lower():
                character = avail_name
        
        # get character details
        info_details = self._infographs.get(character)
        guide_details = self._guides.get(character)
        curr_time = time()
        half_day = 43200
        time_check = (curr_time - self._kqm_access_time) > half_day

        # check if kqm got updated if no character found
        if info_details == None and time_check:
            self._infographs = kqm.kqm_infographics()
            self._kqm_characters = self._infographs.keys()
            # try searching for character again
            info_details = self._infographs.get(character)

        # check if kqm got updated if no character found
        if guide_details == None and time_check:
            self._guides = kqm.kqm_guides()
            # try searching for character again
            guide_details = self._guides.get(character)

        # character not found
        if info_details == None and guide_details == None:
            await ctx.send(f'There are no guides or infographics for {character} from KeqingMains.')
            return

        # character found, stores character info as Discord embed "pages"
        embed_pages = []
        guide_url = None
        card_img = None
        info_img = None
        role = None
        r, g, b = self._embed_colors

        # retrive guide details
        if guide_details != None:
            guide_url = guide_details[0]
            card_img = guide_details[1]
            # first page of embed if possible
            embed_desc = 'Guide from KeqingMains'
            disc_embed = discord.Embed.from_dict({'title':character, 'url':guide_url, 'description':embed_desc})
            disc_embed.set_image(url=card_img)
            embed_pages.append(disc_embed)

        # retrieve infographic details
        if info_details != None:
            for char in info_details:
                role = char[0]
                info_img = char[1]
                # add pages to list
                disc_embed = discord.Embed.from_dict({'title':character, 'url':guide_url, 'description':role})
                disc_embed.set_image(url=info_img)
                embed_pages.append(disc_embed)
        
        # add footer and color
        for page, embed in enumerate(embed_pages):
            footer_note = str(page + 1) + '/' + str(len(embed_pages))
            embed.set_footer(text=footer_note)
            embed.color = discord.Color.from_rgb(r, g, b)

        # send embeds
        await self._manage_pages(ctx, embed_pages)

    async def _manage_pages(self, ctx, embed_pages):
        """
        Adds left and right arrow emojis to the given msg.
        """
        # send first page of embed
        page = 0
        msg = await ctx.send(embed=embed_pages[page])
        self._sent_ids.add(msg.id)

        # add reactions
        reactions = ['⬅️', '➡️']
        for emoji in reactions:
            await msg.add_reaction(emoji)

        # start time
        t0 = time()

        # checks if reacted
        def check(reaction, user):
            return str(reaction.emoji) in reactions \
                   and user != self.bot.user \
                   and reaction.message.id == msg.id

        # waits for reaction
        while True:
            # stop function after 60 sec of inactivity
            try:
                msg1 = await self.bot.wait_for('reaction_add', check=check, timeout=60.0)
            except exceptions.TimeoutError:
                return 

            reaction_emoji = str(msg1[0])

            # turn page back
            if reaction_emoji == reactions[0]:
                if (page > 0):
                    page -= 1
                    await msg.edit(embed=embed_pages[page])
            # turn page forward
            else:
                if (page < len(embed_pages) - 1):
                    page += 1
                    await msg.edit(embed=embed_pages[page])
            
            if time() - t0 >= 60:
                self._sent_ids.pop(msg.id)
                return   

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction, user):
        """
        Performs operations based on reactions in the text channel
        """
        message = reaction.message
        # check if author is a bot
        if user == self.bot.user:
            return

        if message.id in self._sent_ids:
            try:
                await reaction.remove(user)
            except NotFound:
                pass
    
async def setup(bot):
    await bot.add_cog(Genshin(bot))