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)
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
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
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
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
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
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)
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
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
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