Source code for lightbulb.context.slash

# -*- coding: utf-8 -*-
# Copyright © tandemdude 2020-present
#
# This file is part of Lightbulb.
#
# Lightbulb is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Lightbulb is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Lightbulb. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ["SlashContext"]

import asyncio
import inspect
import typing as t

import hikari

from lightbulb import commands
from lightbulb.context import base
from lightbulb.converters import CONVERTER_TYPE_MAPPING
from lightbulb.converters import BaseConverter

if t.TYPE_CHECKING:
    from lightbulb import app as app_


[docs] class SlashContext(base.ApplicationContext): """ An implementation of :obj:`~.context.base.Context` for slash commands. Args: app (:obj:`~.app.BotApp`): The ``BotApp`` instance that the context is linked to. event (:obj:`~hikari.events.interaction_events.InteractionCreateEvent`): The event to create the context from. command (:obj:`~.commands.slash.SlashCommand`): The command that the context is for. """ __slots__ = ("_options", "_raw_options", "_to_convert") def __init__( self, app: app_.BotApp, event: hikari.InteractionCreateEvent, command: commands.slash.SlashCommand ) -> None: super().__init__(app, event, command) self._options: t.Dict[str, t.Any] = {} self._raw_options: t.Sequence[hikari.CommandInteractionOption] = self.interaction.options or [] self._to_convert: t.List[t.Coroutine[t.Any, t.Any, None]] = [] self._parse_options(self.interaction.options) async def _convert_option(self, name: str, value: str) -> None: cmd = self.invoked or self.command opt_type = cmd.options[name].arg_type if opt_type in CONVERTER_TYPE_MAPPING: self._options[name] = await CONVERTER_TYPE_MAPPING[cmd.options[name].arg_type](self).convert(value) elif inspect.isclass(opt_type) and issubclass(cmd.options[name].arg_type, BaseConverter): self._options[name] = await opt_type(self).convert(value) elif callable(opt_type): temp: t.Any = opt_type(value) if inspect.iscoroutine(temp): temp = await temp self._options[name] = temp else: self._options[name] = value def _parse_options(self, options: t.Optional[t.Sequence[hikari.CommandInteractionOption]]) -> None: # We need to clear the options here to ensure the subcommand name does not exist in the mapping self._options.clear() for opt in options or []: # Why is mypy so annoying about this ?? if opt.type is hikari.OptionType.USER and self.resolved is not None: val = t.cast(hikari.Snowflake, opt.value) self._options[opt.name] = self.resolved.members.get(val, self.resolved.users.get(val, opt.value)) elif opt.type is hikari.OptionType.CHANNEL and self.resolved is not None: val = t.cast(hikari.Snowflake, opt.value) self._options[opt.name] = self.resolved.channels.get(val, opt.value) elif opt.type is hikari.OptionType.ROLE and self.resolved is not None: val = t.cast(hikari.Snowflake, opt.value) self._options[opt.name] = self.resolved.roles.get(val, opt.value) elif opt.type is hikari.OptionType.ATTACHMENT and self.resolved is not None: val = t.cast(hikari.Snowflake, int(opt.value) if isinstance(opt.value, str) else opt.value) self._options[opt.name] = self.resolved.attachments.get(val, opt.value) else: if isinstance(opt.value, str): self._to_convert.append(self._convert_option(opt.name, opt.value)) continue self._options[opt.name] = opt.value cmd = self.invoked or self.command for opt in cmd.options.values(): self._options.setdefault(opt.name, opt.default if opt.default is not hikari.UNDEFINED else None) async def _maybe_defer(self) -> None: await super()._maybe_defer() # Ensure that running converters don't block the automatic deferral await asyncio.gather(*self._to_convert) @property def raw_options(self) -> t.Dict[str, t.Any]: return self._options @property def prefix(self) -> str: return "/" @property def command(self) -> commands.slash.SlashCommand: assert isinstance(self._command, commands.slash.SlashCommand) return self._command