Example #1
0
class BotPermissionsCheck(PermissionCheck):
    __slots__: typing.Sequence[str] = ("_lock", "_me")

    def __init__(self, permissions: typing.Union[permissions_.Permissions,
                                                 int], /) -> None:
        super().__init__(permissions)
        self._lock = asyncio.Lock()
        self._me: typing.Optional[users.User] = None

    async def get_permissions(self, ctx: tanjun_traits.Context,
                              /) -> permissions_.Permissions:
        if ctx.message.guild_id is None:
            return utilities.ALL_PERMISSIONS

        member = await self._get_member(ctx, ctx.message.guild_id)
        return await utilities.fetch_permissions(
            ctx.client, member, channel=ctx.message.channel_id)

    async def _get_member(self, ctx: tanjun_traits.Context,
                          guild_id: snowflakes.Snowflake, /) -> guilds.Member:
        user = await self._get_user(ctx.client.cache_service,
                                    ctx.client.rest_service)

        if ctx.client.cache_service and (
                member := ctx.client.cache_service.cache.get_member(
                    guild_id, user.id)):
            return member

        retry = backoff.Backoff(maximum=5, max_retries=4)
        return await utilities.fetch_resource(
            retry, ctx.client.rest_service.rest.fetch_member, guild_id,
            user.id)
Example #2
0
async def nsfw_check(ctx: tanjun_traits.Context, /) -> bool:
    channel: typing.Optional[channels.PartialChannel] = None
    if ctx.client.cache_service:
        channel = ctx.client.cache_service.cache.get_guild_channel(
            ctx.message.channel_id)

    if not channel:
        retry = backoff.Backoff(maximum=5, max_retries=4)
        channel = await utilities.fetch_resource(retry,
                                                 ctx.message.fetch_channel)

    return channel.is_nsfw or False if isinstance(
        channel, channels.GuildChannel) else True
Example #3
0
    async def _try_fetch(
            self, rest: hikari_traits.RESTAware,
            /) -> applications.Application:  # type: ignore[return]
        retry = backoff.Backoff()
        async for _ in retry:
            try:
                self._application = await rest.rest.fetch_application()
                return self._application

            except (hikari_errors.RateLimitedError,
                    hikari_errors.RateLimitTooLongError) as exc:
                retry.set_next_backoff(exc.retry_after)

            except hikari_errors.InternalServerError:
                continue
Example #4
0
    async def _get_user(self, cache_service: typing.Optional[
        hikari_traits.CacheAware], rest_service: hikari_traits.RESTAware,
                        /) -> users.User:
        if not self._me:
            async with self._lock:
                if self._me:
                    return self._me

                if cache_service and (user := cache_service.cache.get_me()):
                    self._me = user

                else:
                    retry = backoff.Backoff(maximum=5, max_retries=4)
                    raw_user = await utilities.fetch_resource(
                        retry, rest_service.rest.fetch_my_user)
                    self._me = raw_user
Example #5
0
    async def _delete_message(self, message_id: snowflakes.Snowflake, /) -> None:
        retry = backoff.Backoff()

        async for _ in retry:
            try:
                await self._rest.rest.delete_message(self._channel_id, message_id)

            except (errors.NotFoundError, errors.ForbiddenError):  # TODO: attempt to check permissions first
                return

            except errors.InternalServerError:
                continue

            except errors.RateLimitedError as exc:
                retry.set_next_backoff(exc.retry_after)

            else:
                break
Example #6
0
    async def on_reaction_event(self, emoji: emojis.Emoji, user_id: snowflakes.Snowflake) -> typing.Optional[str]:
        # <<inherited docstring from AbstractPaginator>>.
        if self.expired:
            asyncio.create_task(self.close(remove_reactions=True))
            return END

        if self._message_id is None or self._authors and user_id not in self._authors or self._locked:
            return None

        method = self._emoji_mapping.get(emoji)
        if emoji not in self._triggers or not method:
            return None

        result = await method()
        if isinstance(result, str) or result is None:
            return END

        self._last_triggered = datetime.datetime.now(tz=datetime.timezone.utc)
        retry = backoff.Backoff()

        async for _ in retry:
            # Mypy makes the false assumption that this value will stay as None while this function yields.
            if self._message_id is None:
                break  # type: ignore[unreachable]

            try:
                await self._rest.rest.edit_message(
                    self._channel_id, self._message_id, content=result[0], embed=result[1]
                )

            except errors.InternalServerError:
                continue

            except errors.RateLimitedError as exc:
                retry.set_next_backoff(exc.retry_after)

            except (errors.NotFoundError, errors.ForbiddenError):
                return END

            else:
                break

        return None
Example #7
0
    async def open(self, *, register_listener: bool = True) -> None:
        if self._grab_mention_prefix:
            user: typing.Optional[users.User] = None
            if self._cache:
                user = self._cache.cache.get_me()

            if not user:
                retry = backoff.Backoff(max_retries=4, maximum=30)

                async for _ in retry:
                    try:
                        user = await self._rest.rest.fetch_my_user()
                        break

                    except (hikari_errors.RateLimitedError,
                            hikari_errors.RateLimitTooLongError) as exc:
                        if exc.retry_after > 30:
                            raise

                        retry.set_next_backoff(exc.retry_after)

                    except hikari_errors.InternalServerError:
                        continue

                else:
                    user = await self._rest.rest.fetch_my_user()

            self._prefixes.add(f"<@{user.id}>")
            self._prefixes.add(f"<@!{user.id}>")
            self._grab_mention_prefix = False

        await asyncio.gather(*(component.open()
                               for component in self._components))

        event_type = self._accepts.get_event_type()
        if register_listener and event_type:
            self._events.event_manager.subscribe(event_type,
                                                 self.on_message_create)
Example #8
0
    async def close(self, remove_reactions: bool = False) -> None:
        # <<inherited docstring from AbstractPaginator>>.
        if message_id := self._message_id:
            self._message_id = None
            retry = backoff.Backoff()
            # TODO: check if we can just clear the reactions before doing this using the cache.
            for emoji in self._triggers:
                retry.reset()
                async for _ in retry:
                    try:
                        await self._rest.rest.delete_my_reaction(self._channel_id, message_id, emoji)

                    except (errors.NotFoundError, errors.ForbiddenError):
                        return

                    except errors.RateLimitedError as exc:
                        retry.set_next_backoff(exc.retry_after)

                    except errors.InternalServerError:
                        continue

                    else:
                        break
Example #9
0
        The object of ID of the channel to get their permissions in.
        If left as `builtins.None` then this will return their base guild
        permissions.

    !!! note
        This function will fallback to REST requests if cache lookups fail or
        are not possible.

    Returns
    -------
    hikari.permissions.Permissions
        The calculated permissions.
    """
    # The ordering of how this adds and removes permissions does matter.
    # For more information see https://discord.com/developers/docs/topics/permissions#permission-hierarchy.
    retry = backoff.Backoff(maximum=5, max_retries=4)
    guild: typing.Optional[guilds.Guild]
    roles: typing.Optional[typing.Mapping[snowflakes.Snowflake,
                                          guilds.Role]] = None
    guild = client.cache_service.cache.get_guild(
        member.guild_id) if client.cache_service else None
    if not guild:
        guild = await fetch_resource(retry,
                                     client.rest_service.rest.fetch_guild,
                                     member.guild_id)
        assert guild is not None
        roles = guild.roles

    # Guild owners are implicitly admins.
    if guild.owner_id == member.user.id:
        return ALL_PERMISSIONS
Example #10
0
    async def open(
        self,
        *,
        message: typing.Optional[messages.Message] = None,
        add_reactions: bool = True,
        max_retries: int = 5,
        max_backoff: float = 2.0,
    ) -> messages.Message:
        # <<inherited docstring from AbstractPaginator>>.
        if self._message_id is not None:
            raise RuntimeError("Paginator is already running")

        retry = backoff.Backoff(max_retries=max_retries - 1, maximum=max_backoff)
        if message is None:
            entry = await self._on_next()

            if entry is None:
                raise ValueError("Paginator iterator yielded no pages.")

            async for _ in retry:
                try:
                    message = await self._rest.rest.create_message(self._channel_id, content=entry[0], embed=entry[1])

                except errors.RateLimitedError as exc:
                    if exc.retry_after > max_backoff:
                        raise

                    retry.set_next_backoff(exc.retry_after)

                except errors.InternalServerError:
                    continue

                else:
                    break

            else:
                message = await self._rest.rest.create_message(self._channel_id, content=entry[0], embed=entry[1])

        self._message_id = message.id
        for emoji in self._triggers:
            retry.reset()
            async for _ in retry:
                try:
                    await self._rest.rest.add_reaction(self._channel_id, message, emoji)

                except errors.NotFoundError:
                    self._message_id = None
                    raise

                except errors.ForbiddenError:  # TODO: attempt to check permissions first
                    # If this is reached then we just don't have reaction permissions in the channel.
                    return message

                except errors.RateLimitedError as exc:
                    if exc.retry_after > max_backoff:
                        raise

                    retry.set_next_backoff(exc.retry_after)

                except errors.InternalServerError:
                    continue

                else:
                    break

            else:
                await self._rest.rest.add_reaction(self._channel_id, message, emoji)

        return message