Пример #1
0
async def get_channel_obj(bot: Red, channel_id: int,
                          data: dict) -> Optional[discord.TextChannel]:
    """
    Requires a bot object to access config, channel_id, and channel config data
    Returns the channel object and sets the guild ID if it's missing from config

    This is used in Game objects and Goal objects so it's here to be shared
    between the two rather than duplicating the code
    """
    if not data["guild_id"]:
        channel = bot.get_channel(id=channel_id)
        if not channel:
            await bot.get_cog("Hockey").config.channel_from_id(channel_id
                                                               ).clear()
            log.info(
                f"{channel_id} channel was removed because it no longer exists"
            )
            return None
        guild = channel.guild
        await bot.get_cog("Hockey").config.channel(channel).guild_id.set(
            guild.id)
        return channel
    guild = bot.get_guild(data["guild_id"])
    if not guild:
        await bot.get_cog("Hockey").config.channel_from_id(channel_id).clear()
        log.info(
            f"{channel_id} channel was removed because it no longer exists")
        return None
    channel = guild.get_channel(channel_id)
    if channel is None:
        await bot.get_cog("Hockey").config.channel_from_id(channel_id).clear()
        log.info(
            f"{channel_id} channel was removed because it no longer exists")
        return None
    return channel
Пример #2
0
    async def edit_team_goal(self, bot: Red, game_data: Game,
                             og_msg: Tuple[int, int, int]) -> None:
        """
        When a goal scorer has changed we want to edit the original post
        """
        # scorer = self.headshots.format(goal["players"][0]["player"]["id"])
        # post_state = ["all", game_data.home_team, game_data.away_team]
        em = await self.goal_post_embed(game_data)
        async for guild_id, channel_id, message_id in AsyncIter(og_msg,
                                                                steps=100):
            guild = bot.get_guild(guild_id)
            if not guild:
                continue
            channel = guild.get_channel(int(channel_id))
            if channel is None:
                continue
            bot.loop.create_task(self.edit_goal(bot, channel, message_id, em))
            # This is to prevent endlessly waiting incase someone
            # decided to publish one of our messages we want to edit
            # if we did bounded_gather here the gather would wait until
            # rate limits are up for editing that one message
            # in this case we can send off the task to do it's thing
            # and forget about it. If one never finishes I don't care

        return
Пример #3
0
    def check_channel(self, bot: Red, channel: discord.TextChannel) -> bool:
        """
        Checks if the channel is allowed to track starboard
        messages

        Parameters
        ----------
            bot: Red
                The bot object
            channel: discord.TextChannel
                The channel we want to verify we're allowed to post in

        Returns
        -------
            bool
                Whether or not the channel we got a "star" in we're allowed
                to repost.
        """
        guild = bot.get_guild(self.guild)
        if channel.is_nsfw() and not guild.get_channel(self.channel).is_nsfw():
            return False
        if self.whitelist:
            if channel.id in self.whitelist:
                return True
            if channel.category_id and channel.category_id in self.whitelist:
                return True
            return False
        else:
            if channel.id in self.blacklist:
                return False
            if channel.category_id and channel.category_id in self.blacklist:
                return False
            return True
Пример #4
0
    async def update_count(self, bot: Red, starboard: StarboardEntry,
                           remove: Optional[int]) -> None:
        """
        This function can pull the most accurate reaction info from a starboarded message
        However it takes at least 2 API calls which can be expensive. I am leaving
        This here for future potential needs but we should instead rely on our
        listener to keep track of reactions added/removed.

        Parameters
        ----------
            bot: Red
                The bot object used for bot.get_guild
            starbaord: StarboardEntry
                The starboard object which contains this message entry
            remove: Optional[int]
                This was used to represent a user who removed their reaction.

        Returns
        -------
            MessageEntry
                Returns itself although since this is handled in memory is not required.
        """
        guild = bot.get_guild(self.guild)
        # log.debug(f"{guild=} {self.guild=}")
        orig_channel = guild.get_channel(self.original_channel)
        new_channel = guild.get_channel(self.new_channel)
        orig_reaction = []
        if orig_channel:
            try:
                orig_msg = await orig_channel.fetch_message(
                    self.original_message)
                orig_reaction = [
                    r for r in orig_msg.reactions
                    if str(r.emoji) == str(starboard.emoji)
                ]
            except discord.HTTPException:
                pass
        new_reaction = []
        if new_channel:
            try:
                new_msg = await new_channel.fetch_message(self.new_message)
                new_reaction = [
                    r for r in new_msg.reactions
                    if str(r.emoji) == str(starboard.emoji)
                ]
            except discord.HTTPException:
                pass
        reactions = orig_reaction + new_reaction
        for reaction in reactions:
            async for user in reaction.users():
                if not starboard.check_roles(user):
                    continue
                if not starboard.selfstar and user.id == orig_msg.author.id:
                    continue
                if user.id not in self.reactions and not user.bot:
                    self.reactions.append(user.id)
        if remove and remove in self.reactions:
            self.reactions.remove(remove)
        self.reactions = list(set(self.reactions))
        return self
Пример #5
0
 async def remove_goal_post(bot: Red, goal: str, team: str,
                            data: Game) -> None:
     """
     Attempt to delete a goal if it was pulled back
     """
     config = bot.get_cog("Hockey").config
     team_list = await config.teams()
     team_data = await get_team(bot, team)
     if goal not in [goal.goal_id for goal in data.goals]:
         try:
             old_msgs = team_data["goal_id"][goal]["messages"]
         except KeyError:
             return
         except Exception:
             log.exception("Error iterating saved goals")
             return
         for guild_id, channel_id, message_id in old_msgs:
             guild = bot.get_guild(guild_id)
             if not guild:
                 continue
             channel = guild.get_channel(int(channel_id))
             if channel and channel.permissions_for(
                     channel.guild.me).read_message_history:
                 try:
                     if version_info >= VersionInfo.from_str("3.4.6"):
                         message = channel.get_partial_message(message_id)
                     else:
                         message = await channel.fetch_message(message_id)
                 except (discord.errors.NotFound, discord.errors.Forbidden):
                     continue
                 except Exception:
                     log.exception(
                         f"Error getting old goal for {str(team)} {str(goal)} in "
                         f"{guild_id=} {channel_id=}")
                     pass
                 if message is not None:
                     try:
                         await message.delete()
                     except (discord.errors.NotFound,
                             discord.errors.Forbidden):
                         pass
                     except Exception:
                         log.exception(
                             f"Error getting old goal for {str(team)} {str(goal)} in "
                             f"{guild_id=} {channel_id=}")
             else:
                 log.debug(
                     "Channel does not have permission to read history")
         try:
             team_list.remove(team_data)
             del team_data["goal_id"][goal]
             team_list.append(team_data)
             await config.teams.set(team_list)
         except Exception:
             log.exception("Error removing teams goals")
             return
     return
Пример #6
0
    async def from_json(cls, mod_channel: discord.TextChannel, bot: Red,
                        data: dict):
        """Get a Case object from the provided information

        Parameters
        ----------
        mod_channel: discord.TextChannel
            The mod log channel for the guild
        bot: Red
            The bot's instance. Needed to get the target user
        data: dict
            The JSON representation of the case to be gotten

        Returns
        -------
        Case
            The case object for the requested case

        Raises
        ------
        `discord.NotFound`
            The user the case is for no longer exists
        `discord.Forbidden`
            Cannot read message history to fetch the original message.
        `discord.HTTPException`
            A generic API issue
        """
        guild = mod_channel.guild
        if data["message"]:
            try:
                message = await mod_channel.get_message(data["message"])
            except discord.NotFound:
                message = None
        user = await bot.get_user_info(data["user"])
        moderator = guild.get_member(data["moderator"])
        channel = guild.get_channel(data["channel"])
        amended_by = guild.get_member(data["amended_by"])
        case_guild = bot.get_guild(data["guild"])
        return cls(
            bot=bot,
            guild=case_guild,
            created_at=data["created_at"],
            action_type=data["action_type"],
            user=user,
            moderator=moderator,
            case_number=data["case_number"],
            reason=data["reason"],
            until=data["until"],
            channel=channel,
            amended_by=amended_by,
            modified_at=data["modified_at"],
            message=message,
        )
Пример #7
0
    async def get_ctx(self, bot: Red) -> Optional[commands.Context]:
        """
        Returns the context object for the events message

        This can't be used to invoke another command but
        it is useful to get a basis for an events final posted message.
        """
        guild = bot.get_guild(self.guild)
        if not guild:
            return None
        chan = guild.get_channel(self.channel)
        if not chan:
            return None
        try:
            msg = await chan.fetch_message(self.message)
        except (discord.errors.NotFound, discord.errors.Forbidden):
            return None
        return await bot.get_context(msg)
Пример #8
0
    async def from_json(cls, mod_channel: discord.TextChannel, bot: Red,
                        data: dict):
        """Get a Case object from the provided information

        Parameters
        ----------
        mod_channel: discord.TextChannel
            The mod log channel for the guild
        bot: Red
            The bot's instance. Needed to get the target user
        data: dict
            The JSON representation of the case to be gotten

        Returns
        -------
        Case
            The case object for the requested case

        """
        guild = mod_channel.guild
        message = await mod_channel.get_message(data["message"])
        user = await bot.get_user_info(data["user"])
        moderator = guild.get_member(data["moderator"])
        channel = guild.get_channel(data["channel"])
        amended_by = guild.get_member(data["amended_by"])
        case_guild = bot.get_guild(data["guild"])
        return cls(
            bot=bot,
            guild=case_guild,
            created_at=data["created_at"],
            action_type=data["action_type"],
            user=user,
            moderator=moderator,
            case_number=data["case_number"],
            reason=data["reason"],
            until=data["until"],
            channel=channel,
            amended_by=amended_by,
            modified_at=data["modified_at"],
            message=message,
        )
Пример #9
0
    async def from_json(
        cls, mod_channel: discord.TextChannel, bot: Red, case_number: int, data: dict, **kwargs
    ):
        """Get a Case object from the provided information

        Parameters
        ----------
        mod_channel: discord.TextChannel
            The mod log channel for the guild
        bot: Red
            The bot's instance. Needed to get the target user
        case_number: int
            The case's number.
        data: dict
            The JSON representation of the case to be gotten
        **kwargs
            Extra attributes for the Case instance which override values
            in the data dict. These should be complete objects and not
            IDs, where possible.

        Returns
        -------
        Case
            The case object for the requested case

        Raises
        ------
        `discord.NotFound`
            The user the case is for no longer exists
        `discord.Forbidden`
            Cannot read message history to fetch the original message.
        `discord.HTTPException`
            A generic API issue
        """
        guild = kwargs.get("guild") or mod_channel.guild

        message = kwargs.get("message")
        if message is None:
            message_id = data.get("message")
            if message_id is not None:
                message = discord.utils.get(bot.cached_messages, id=message_id)
                if message is None:
                    try:
                        message = await mod_channel.fetch_message(message_id)
                    except discord.HTTPException:
                        message = None
            else:
                message = None

        user_objects = {"user": None, "moderator": None, "amended_by": None}
        for user_key in tuple(user_objects):
            user_object = kwargs.get(user_key)
            if user_object is None:
                user_id = data.get(user_key)
                if user_id is None:
                    user_object = None
                else:
                    user_object = bot.get_user(user_id) or user_id
            user_objects[user_key] = user_object

        channel = kwargs.get("channel") or guild.get_channel(data["channel"]) or data["channel"]
        case_guild = kwargs.get("guild") or bot.get_guild(data["guild"])
        return cls(
            bot=bot,
            guild=case_guild,
            created_at=data["created_at"],
            action_type=data["action_type"],
            case_number=case_number,
            reason=data["reason"],
            until=data["until"],
            channel=channel,
            modified_at=data["modified_at"],
            message=message,
            last_known_username=data.get("last_known_username"),
            **user_objects,
        )