hikari-arc 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
arc/client.py CHANGED
@@ -146,7 +146,7 @@ class GatewayClientBase(Client[GatewayBotT]):
146
146
  The event type to subscribe to.
147
147
 
148
148
  `EventT` must be a subclass of `hikari.events.base_events.Event`.
149
- callback : t.Callable[t.Concatenate[EventT, ...], t.Awaitable[None]]
149
+ callback : t.Callable[EventT], t.Awaitable[None]]
150
150
  The callback to call when the event is dispatched.
151
151
  """
152
152
  self.app.event_manager.subscribe(event_type, callback) # pyright: ignore reportGeneralTypeIssues
@@ -158,7 +158,7 @@ class GatewayClientBase(Client[GatewayBotT]):
158
158
  ----------
159
159
  event_type : type[EventT]
160
160
  The event type to unsubscribe from.
161
- callback : t.Callable[t.Concatenate[EventT, ...], t.Awaitable[None]]
161
+ callback : t.Callable[[EventT], t.Awaitable[None]]
162
162
  The callback to unsubscribe.
163
163
  """
164
164
  self.app.event_manager.unsubscribe(event_type, callback) # pyright: ignore reportGeneralTypeIssues
@@ -178,7 +178,7 @@ class GatewayClientBase(Client[GatewayBotT]):
178
178
 
179
179
  Returns
180
180
  -------
181
- t.Callable[t.Callable[t.Concatenate[EventT, ...], t.Awaitable[None]]], t.Callable[t.Concatenate[EventT, ...], t.Awaitable[None]]]
181
+ t.Callable[t.Callable[[EventT], t.Awaitable[None]]], t.Callable[[EventT], t.Awaitable[None]]]
182
182
  A decorator for a coroutine function that passes it to
183
183
  `EventManager.subscribe` before returning the function
184
184
  reference.
arc/command/message.py CHANGED
@@ -8,12 +8,12 @@ import hikari
8
8
  from arc.abc.command import CallableCommandBase
9
9
  from arc.context import AutodeferMode, Context
10
10
  from arc.errors import CommandInvokeError
11
- from arc.internal.types import ClientT, MessageContextCallbackT, ResponseBuilderT
11
+ from arc.internal.types import ClientT, MessageCommandCallbackT, ResponseBuilderT
12
12
 
13
13
  if t.TYPE_CHECKING:
14
14
  import asyncio
15
15
 
16
- from ..abc import CallableCommandProto
16
+ from arc.abc import CallableCommandProto
17
17
 
18
18
  __all__ = ("MessageCommand", "message_command")
19
19
 
@@ -69,7 +69,7 @@ def message_command(
69
69
  autodefer: bool | AutodeferMode | hikari.UndefinedType = hikari.UNDEFINED,
70
70
  default_permissions: hikari.Permissions | hikari.UndefinedType = hikari.UNDEFINED,
71
71
  name_localizations: t.Mapping[hikari.Locale, str] | None = None,
72
- ) -> t.Callable[[MessageContextCallbackT[ClientT]], MessageCommand[ClientT]]:
72
+ ) -> t.Callable[[MessageCommandCallbackT[ClientT]], MessageCommand[ClientT]]:
73
73
  """A decorator that creates a context-menu command on a message.
74
74
 
75
75
  Parameters
@@ -105,7 +105,7 @@ def message_command(
105
105
  ```
106
106
  """
107
107
 
108
- def decorator(callback: MessageContextCallbackT[ClientT]) -> MessageCommand[ClientT]:
108
+ def decorator(callback: MessageCommandCallbackT[ClientT]) -> MessageCommand[ClientT]:
109
109
  guild_ids = tuple(hikari.Snowflake(i) for i in guilds) if guilds is not hikari.UNDEFINED else hikari.UNDEFINED
110
110
 
111
111
  return MessageCommand(
@@ -5,7 +5,7 @@ import typing as t
5
5
  import attr
6
6
  import hikari
7
7
 
8
- from arc.abc.option import CommandOptionBase, OptionParams
8
+ from arc.abc.option import CommandOptionBase, OptionParams, OptionType
9
9
  from arc.internal.types import ClientT
10
10
 
11
11
  if t.TYPE_CHECKING:
@@ -46,8 +46,8 @@ class AttachmentOption(CommandOptionBase[hikari.Attachment, ClientT, AttachmentP
46
46
  """
47
47
 
48
48
  @property
49
- def option_type(self) -> hikari.OptionType:
50
- return hikari.OptionType.ATTACHMENT
49
+ def option_type(self) -> OptionType:
50
+ return OptionType.ATTACHMENT
51
51
 
52
52
  @classmethod
53
53
  def _from_params(cls, *, name: str, is_required: bool, params: AttachmentParams, **kwargs: t.Any) -> te.Self:
@@ -3,9 +3,8 @@ from __future__ import annotations
3
3
  import typing as t
4
4
 
5
5
  import attr
6
- import hikari
7
6
 
8
- from arc.abc.option import CommandOptionBase, OptionParams
7
+ from arc.abc.option import CommandOptionBase, OptionParams, OptionType
9
8
  from arc.internal.types import ClientT
10
9
 
11
10
  if t.TYPE_CHECKING:
@@ -46,8 +45,8 @@ class BoolOption(CommandOptionBase[bool, ClientT, BoolParams]):
46
45
  """
47
46
 
48
47
  @property
49
- def option_type(self) -> hikari.OptionType:
50
- return hikari.OptionType.BOOLEAN
48
+ def option_type(self) -> OptionType:
49
+ return OptionType.BOOLEAN
51
50
 
52
51
  @classmethod
53
52
  def _from_params(cls, *, name: str, is_required: bool, params: BoolParams, **kwargs: t.Any) -> te.Self:
@@ -5,7 +5,7 @@ import typing as t
5
5
  import attr
6
6
  import hikari
7
7
 
8
- from arc.abc.option import CommandOptionBase, OptionParams
8
+ from arc.abc.option import CommandOptionBase, OptionParams, OptionType
9
9
  from arc.internal.types import ClientT
10
10
 
11
11
  if t.TYPE_CHECKING:
@@ -57,8 +57,8 @@ class ChannelOption(CommandOptionBase[hikari.PartialChannel, ClientT, ChannelPar
57
57
  """The channel types that the option can be."""
58
58
 
59
59
  @property
60
- def option_type(self) -> hikari.OptionType:
61
- return hikari.OptionType.CHANNEL
60
+ def option_type(self) -> OptionType:
61
+ return OptionType.CHANNEL
62
62
 
63
63
  @classmethod
64
64
  def _from_params(cls, *, name: str, is_required: bool, params: ChannelParams, **kwargs: t.Any) -> te.Self:
@@ -3,12 +3,12 @@ from __future__ import annotations
3
3
  import typing as t
4
4
 
5
5
  import attr
6
- import hikari
7
6
 
8
- from arc.abc.option import OptionWithChoices, OptionWithChoicesParams
7
+ from arc.abc.option import OptionType, OptionWithChoices, OptionWithChoicesParams
9
8
  from arc.internal.types import ClientT
10
9
 
11
10
  if t.TYPE_CHECKING:
11
+ import hikari
12
12
  import typing_extensions as te
13
13
 
14
14
  from ...internal.types import AutocompleteCallbackT
@@ -97,8 +97,8 @@ class FloatOption(OptionWithChoices[float, ClientT, FloatParams[ClientT]]):
97
97
  """The maximum value of the option."""
98
98
 
99
99
  @property
100
- def option_type(self) -> hikari.OptionType:
101
- return hikari.OptionType.FLOAT
100
+ def option_type(self) -> OptionType:
101
+ return OptionType.FLOAT
102
102
 
103
103
  @classmethod
104
104
  def _from_params(cls, *, name: str, is_required: bool, params: FloatParams[ClientT], **kwargs: t.Any) -> te.Self:
arc/command/option/int.py CHANGED
@@ -3,12 +3,12 @@ from __future__ import annotations
3
3
  import typing as t
4
4
 
5
5
  import attr
6
- import hikari
7
6
 
8
- from arc.abc.option import OptionWithChoices, OptionWithChoicesParams
7
+ from arc.abc.option import OptionType, OptionWithChoices, OptionWithChoicesParams
9
8
  from arc.internal.types import ClientT
10
9
 
11
10
  if t.TYPE_CHECKING:
11
+ import hikari
12
12
  import typing_extensions as te
13
13
 
14
14
  from ...internal.types import AutocompleteCallbackT
@@ -97,8 +97,8 @@ class IntOption(OptionWithChoices[int, ClientT, IntParams[ClientT]]):
97
97
  """The maximum value of the option."""
98
98
 
99
99
  @property
100
- def option_type(self) -> hikari.OptionType:
101
- return hikari.OptionType.INTEGER
100
+ def option_type(self) -> OptionType:
101
+ return OptionType.INTEGER
102
102
 
103
103
  @classmethod
104
104
  def _from_params(cls, *, name: str, is_required: bool, params: IntParams[ClientT], **kwargs: t.Any) -> te.Self:
@@ -5,7 +5,7 @@ import typing as t
5
5
  import attr
6
6
  import hikari
7
7
 
8
- from arc.abc.option import CommandOptionBase, OptionParams
8
+ from arc.abc.option import CommandOptionBase, OptionParams, OptionType
9
9
  from arc.internal.types import ClientT
10
10
 
11
11
  if t.TYPE_CHECKING:
@@ -47,8 +47,8 @@ class MentionableOption(CommandOptionBase[hikari.Role | hikari.User, ClientT, Me
47
47
  """
48
48
 
49
49
  @property
50
- def option_type(self) -> hikari.OptionType:
51
- return hikari.OptionType.MENTIONABLE
50
+ def option_type(self) -> OptionType:
51
+ return OptionType.MENTIONABLE
52
52
 
53
53
  @classmethod
54
54
  def _from_params(cls, *, name: str, is_required: bool, params: MentionableParams, **kwargs: t.Any) -> te.Self:
@@ -5,7 +5,7 @@ import typing as t
5
5
  import attr
6
6
  import hikari
7
7
 
8
- from arc.abc.option import CommandOptionBase, OptionParams
8
+ from arc.abc.option import CommandOptionBase, OptionParams, OptionType
9
9
  from arc.internal.types import ClientT
10
10
 
11
11
  if t.TYPE_CHECKING:
@@ -46,8 +46,8 @@ class RoleOption(CommandOptionBase[hikari.Role, ClientT, RoleParams]):
46
46
  """
47
47
 
48
48
  @property
49
- def option_type(self) -> hikari.OptionType:
50
- return hikari.OptionType.ROLE
49
+ def option_type(self) -> OptionType:
50
+ return OptionType.ROLE
51
51
 
52
52
  @classmethod
53
53
  def _from_params(cls, *, name: str, is_required: bool, params: RoleParams, **kwargs: t.Any) -> te.Self:
arc/command/option/str.py CHANGED
@@ -3,12 +3,12 @@ from __future__ import annotations
3
3
  import typing as t
4
4
 
5
5
  import attr
6
- import hikari
7
6
 
8
- from arc.abc.option import OptionWithChoices, OptionWithChoicesParams
7
+ from arc.abc.option import OptionType, OptionWithChoices, OptionWithChoicesParams
9
8
  from arc.internal.types import ClientT
10
9
 
11
10
  if t.TYPE_CHECKING:
11
+ import hikari
12
12
  import typing_extensions as te
13
13
 
14
14
  from arc.internal.types import AutocompleteCallbackT
@@ -98,8 +98,8 @@ class StrOption(OptionWithChoices[str, ClientT, StrParams[ClientT]]):
98
98
  """The maximum length of the option."""
99
99
 
100
100
  @property
101
- def option_type(self) -> hikari.OptionType:
102
- return hikari.OptionType.STRING
101
+ def option_type(self) -> OptionType:
102
+ return OptionType.STRING
103
103
 
104
104
  @classmethod
105
105
  def _from_params(cls, *, name: str, is_required: bool, params: StrParams[ClientT], **kwargs: t.Any) -> te.Self:
@@ -5,7 +5,7 @@ import typing as t
5
5
  import attr
6
6
  import hikari
7
7
 
8
- from arc.abc.option import CommandOptionBase, OptionParams
8
+ from arc.abc.option import CommandOptionBase, OptionParams, OptionType
9
9
  from arc.internal.types import ClientT
10
10
 
11
11
  if t.TYPE_CHECKING:
@@ -47,8 +47,8 @@ class UserOption(CommandOptionBase[hikari.User, ClientT, UserParams]):
47
47
  """
48
48
 
49
49
  @property
50
- def option_type(self) -> hikari.OptionType:
51
- return hikari.OptionType.USER
50
+ def option_type(self) -> OptionType:
51
+ return OptionType.USER
52
52
 
53
53
  @classmethod
54
54
  def _from_params(cls, *, name: str, is_required: bool, params: UserParams, **kwargs: t.Any) -> te.Self:
arc/command/slash.py CHANGED
@@ -7,9 +7,10 @@ import attr
7
7
  import hikari
8
8
 
9
9
  from arc.abc.command import CallableCommandBase, CallableCommandProto, CommandBase, SubCommandBase, _CommandSettings
10
- from arc.abc.option import OptionWithChoices
10
+ from arc.abc.option import OptionType, OptionWithChoices
11
11
  from arc.context import AutocompleteData, AutodeferMode, Context
12
12
  from arc.errors import AutocompleteError, CommandInvokeError
13
+ from arc.internal.options import resolve_options
13
14
  from arc.internal.sigparse import parse_command_signature
14
15
  from arc.internal.types import ClientT, CommandCallbackT, HookT, PostHookT, ResponseBuilderT, SlashCommandLike
15
16
  from arc.locale import CommandLocaleRequest, LocaleResponse
@@ -33,64 +34,6 @@ __all__ = (
33
34
  )
34
35
 
35
36
 
36
- def _resolve_options(
37
- local_options: t.MutableMapping[str, CommandOptionBase[ClientT, t.Any, t.Any]],
38
- incoming_options: t.Sequence[hikari.CommandInteractionOption],
39
- resolved: hikari.ResolvedOptionData | None,
40
- ) -> dict[str, t.Any]:
41
- """Resolve the options into kwargs for the callback.
42
-
43
- Parameters
44
- ----------
45
- local_options : t.MutableMapping[str, Option[t.Any, t.Any]]
46
- The options of the locally stored command.
47
- incoming_options : t.Sequence[hikari.CommandInteractionOption]
48
- The options of the interaction.
49
- resolved : hikari.ResolvedOptionData
50
- The resolved option data of the interaction.
51
-
52
- Returns
53
- -------
54
- dict[str, Any]
55
- The resolved options as kwargs, ready to be passed to the callback.
56
- """
57
- option_kwargs: dict[str, t.Any] = {}
58
-
59
- for arg_name, opt in local_options.items():
60
- inter_opt = next((o for o in incoming_options if o.name == opt.name), None)
61
-
62
- if inter_opt is None:
63
- continue
64
-
65
- if isinstance(inter_opt.value, hikari.Snowflake) and resolved:
66
- match inter_opt.type:
67
- case hikari.OptionType.USER:
68
- value = resolved.members.get(inter_opt.value) or resolved.users[inter_opt.value]
69
- case hikari.OptionType.ATTACHMENT:
70
- value = resolved.attachments[inter_opt.value]
71
- case hikari.OptionType.CHANNEL:
72
- value = resolved.channels[inter_opt.value]
73
- case hikari.OptionType.ROLE:
74
- value = resolved.roles[inter_opt.value]
75
- case hikari.OptionType.MENTIONABLE:
76
- value = (
77
- resolved.members.get(inter_opt.value)
78
- or resolved.users.get(inter_opt.value)
79
- or resolved.roles[inter_opt.value]
80
- )
81
- case _:
82
- raise ValueError(f"Unexpected option type '{inter_opt.type}.'")
83
-
84
- option_kwargs[arg_name] = value
85
-
86
- elif isinstance(inter_opt.value, hikari.Snowflake):
87
- raise ValueError(f"Missing resolved option data for '{inter_opt.name}'.")
88
- else:
89
- option_kwargs[arg_name] = inter_opt.value
90
-
91
- return option_kwargs
92
-
93
-
94
37
  def _choices_to_builders(
95
38
  choices: t.Sequence[hikari.api.AutocompleteChoiceBuilder] | t.Sequence[t.Any],
96
39
  ) -> t.Sequence[hikari.api.AutocompleteChoiceBuilder]:
@@ -126,6 +69,11 @@ class SlashCommand(CallableCommandBase[ClientT, hikari.api.SlashCommandBuilder])
126
69
  def qualified_name(self) -> t.Sequence[str]:
127
70
  return (self.name,)
128
71
 
72
+ @property
73
+ def display_name(self) -> str:
74
+ """The display name of this command."""
75
+ return f"/{self.name}"
76
+
129
77
  def _get_context(
130
78
  self, interaction: hikari.CommandInteraction, command: CallableCommandProto[ClientT]
131
79
  ) -> Context[ClientT]:
@@ -134,7 +82,9 @@ class SlashCommand(CallableCommandBase[ClientT, hikari.api.SlashCommandBuilder])
134
82
  if interaction.command_type is not hikari.CommandType.SLASH:
135
83
  raise ValueError(f"Expected slash command, got {interaction.command_type}")
136
84
 
137
- return Context(self.client, command, interaction)
85
+ ctx = Context(self.client, command, interaction)
86
+ ctx._options = interaction.options
87
+ return ctx
138
88
 
139
89
  def _to_dict(self) -> dict[str, t.Any]:
140
90
  sorted_options = sorted(self.options.values(), key=lambda option: option.is_required, reverse=True)
@@ -164,11 +114,36 @@ class SlashCommand(CallableCommandBase[ClientT, hikari.api.SlashCommandBuilder])
164
114
  return await super().invoke(
165
115
  interaction,
166
116
  *args,
167
- **{**kwargs, **_resolve_options(self.options, interaction.options, interaction.resolved)},
117
+ **{**kwargs, **resolve_options(self.options, interaction.options, interaction.resolved)},
168
118
  )
169
119
  else:
170
120
  return await super().invoke(interaction, *args, **kwargs)
171
121
 
122
+ def make_mention(self, *, guild: hikari.Snowflakeish | hikari.PartialGuild | None = None) -> str:
123
+ """Make a slash mention for this command.
124
+
125
+ Parameters
126
+ ----------
127
+ guild : hikari.SnowflakeishOr[hikari.PartialGuild] | None
128
+ The guild the command is registered in. If None, the global command's ID is used.
129
+
130
+ Returns
131
+ -------
132
+ str
133
+ The slash command mention.
134
+
135
+ Raises
136
+ ------
137
+ KeyError
138
+ If the command has not been published in the given guild or globally.
139
+ """
140
+ instance = self._instances.get(hikari.Snowflake(guild) if guild else None)
141
+
142
+ if instance is None:
143
+ raise KeyError(f"Command '{self.display_name}' has not been published in the given scope.")
144
+
145
+ return f"</{self.name}:{instance.id}>"
146
+
172
147
  async def _on_autocomplete(
173
148
  self, interaction: hikari.AutocompleteInteraction
174
149
  ) -> hikari.api.InteractionAutocompleteBuilder | None:
@@ -249,6 +224,11 @@ class SlashGroup(CommandBase[ClientT, hikari.api.SlashCommandBuilder]):
249
224
  def qualified_name(self) -> t.Sequence[str]:
250
225
  return (self.name,)
251
226
 
227
+ @property
228
+ def display_name(self) -> str:
229
+ """The display name of this command."""
230
+ return f"/{self.name}"
231
+
252
232
  def _to_dict(self) -> dict[str, t.Any]:
253
233
  return {
254
234
  **super()._to_dict(),
@@ -283,11 +263,13 @@ class SlashGroup(CommandBase[ClientT, hikari.api.SlashCommandBuilder]):
283
263
  self,
284
264
  subcommand: SlashSubCommand[ClientT],
285
265
  interaction: hikari.CommandInteraction,
266
+ options: t.Sequence[hikari.CommandInteractionOption] | None = None,
286
267
  *args: t.Any,
287
268
  **kwargs: t.Any,
288
269
  ) -> asyncio.Future[ResponseBuilderT] | None:
289
270
  """Invoke a subcommand."""
290
271
  ctx = self._get_context(interaction, subcommand)
272
+ ctx._options = options
291
273
 
292
274
  if (autodefer := subcommand.autodefer) and autodefer.should_autodefer:
293
275
  ctx._start_autodefer(autodefer)
@@ -314,12 +296,12 @@ class SlashGroup(CommandBase[ClientT, hikari.api.SlashCommandBuilder]):
314
296
  if sub.options is None:
315
297
  if not isinstance(subcmd, SlashSubCommand):
316
298
  raise CommandInvokeError(f"Slash group got subgroup without options: '{subcmd.name}'.")
317
- return await self._invoke_subcmd(subcmd, interaction, *args, **kwargs)
299
+ return await self._invoke_subcmd(subcmd, interaction, None, *args, **kwargs)
318
300
 
319
301
  # Resolve options and invoke if it does
320
302
  if isinstance(subcmd, SlashSubCommand):
321
- res = _resolve_options(subcmd.options, sub.options, interaction.resolved)
322
- return await self._invoke_subcmd(subcmd, interaction, *args, **{**kwargs, **res})
303
+ res = resolve_options(subcmd.options, sub.options, interaction.resolved)
304
+ return await self._invoke_subcmd(subcmd, interaction, sub.options, *args, **{**kwargs, **res})
323
305
 
324
306
  # Get second-order subcommand
325
307
  subsub = next((o for o in sub.options if o.name in subcmd.children), None)
@@ -331,11 +313,11 @@ class SlashGroup(CommandBase[ClientT, hikari.api.SlashCommandBuilder]):
331
313
 
332
314
  # Invoke it if it has no options
333
315
  if subsub.options is None:
334
- return await self._invoke_subcmd(subsubcmd, interaction, *args, **kwargs)
316
+ return await self._invoke_subcmd(subsubcmd, interaction, None, *args, **kwargs)
335
317
 
336
318
  # Resolve options and invoke if it does
337
- res = _resolve_options(subsubcmd.options, subsub.options, interaction.resolved)
338
- return await self._invoke_subcmd(subsubcmd, interaction, *args, **{**kwargs, **res})
319
+ res = resolve_options(subsubcmd.options, subsub.options, interaction.resolved)
320
+ return await self._invoke_subcmd(subsubcmd, interaction, subsub.options, *args, **{**kwargs, **res})
339
321
 
340
322
  async def _on_autocomplete(
341
323
  self, interaction: hikari.AutocompleteInteraction
@@ -486,8 +468,8 @@ class SlashSubGroup(SubCommandBase[ClientT, SlashGroup[ClientT]]):
486
468
  """
487
469
 
488
470
  @property
489
- def option_type(self) -> hikari.OptionType:
490
- return hikari.OptionType.SUB_COMMAND_GROUP
471
+ def option_type(self) -> OptionType:
472
+ return OptionType.SUB_COMMAND_GROUP
491
473
 
492
474
  @property
493
475
  def command_type(self) -> hikari.CommandType:
@@ -497,6 +479,10 @@ class SlashSubGroup(SubCommandBase[ClientT, SlashGroup[ClientT]]):
497
479
  def qualified_name(self) -> t.Sequence[str]:
498
480
  return (self.parent.name, self.name)
499
481
 
482
+ @property
483
+ def display_name(self) -> str:
484
+ return "/" + " ".join(self.qualified_name)
485
+
500
486
  @property
501
487
  def client(self) -> ClientT:
502
488
  """The client that includes this subgroup."""
@@ -599,49 +585,6 @@ class SlashSubCommand(
599
585
 
600
586
  _invoke_task: asyncio.Task[t.Any] | None = attr.field(default=None, init=False)
601
587
 
602
- def _resolve_settings(self) -> _CommandSettings:
603
- settings = self._parent._resolve_settings() if self._parent else _CommandSettings.default()
604
-
605
- return settings.apply(
606
- _CommandSettings(
607
- autodefer=self._autodefer,
608
- default_permissions=hikari.UNDEFINED,
609
- is_nsfw=hikari.UNDEFINED,
610
- is_dm_enabled=hikari.UNDEFINED,
611
- )
612
- )
613
-
614
- def _resolve_hooks(self) -> list[HookT[ClientT]]:
615
- assert self._parent is not None
616
- return self._parent._resolve_hooks() + self._hooks
617
-
618
- def _resolve_post_hooks(self) -> list[PostHookT[ClientT]]:
619
- assert self._parent is not None
620
- return self._parent._resolve_post_hooks() + self._post_hooks
621
-
622
- async def _handle_exception(self, ctx: Context[ClientT], exc: Exception) -> None:
623
- try:
624
- if self.error_handler:
625
- await self.error_handler(ctx, exc)
626
- else:
627
- raise exc
628
- except Exception as e:
629
- assert self._parent is not None
630
- await self._parent._handle_exception(ctx, e)
631
-
632
- @property
633
- def qualified_name(self) -> t.Sequence[str]:
634
- if self._parent is None:
635
- raise ValueError("Cannot get qualified name of subcommand without parent.")
636
-
637
- if isinstance(self._parent, SlashSubGroup):
638
- if self._parent._parent is None:
639
- raise ValueError("Cannot get qualified name of subcommand without parent.")
640
-
641
- return (self._parent._parent.name, self._parent.name, self.name)
642
-
643
- return (self._parent.name, self.name)
644
-
645
588
  @property
646
589
  def root(self) -> SlashGroup[ClientT]:
647
590
  """The root group of this subcommand."""
@@ -656,18 +599,27 @@ class SlashSubCommand(
656
599
 
657
600
  return self._parent
658
601
 
602
+ @property
603
+ def qualified_name(self) -> t.Sequence[str]:
604
+ if isinstance(self._parent, SlashSubGroup):
605
+ return (self.root.name, self.parent.name, self.name)
606
+
607
+ return (self.parent.name, self.name)
608
+
659
609
  @property
660
610
  def parent(self) -> SlashGroup[ClientT] | SlashSubGroup[ClientT]:
661
611
  """The parent of this subcommand."""
662
- return self.parent
612
+ if self._parent is None:
613
+ raise ValueError("Cannot get parent of subcommand without parent.")
614
+ return self._parent
663
615
 
664
616
  @property
665
617
  def command_type(self) -> hikari.CommandType:
666
618
  return hikari.CommandType.SLASH
667
619
 
668
620
  @property
669
- def option_type(self) -> hikari.OptionType:
670
- return hikari.OptionType.SUB_COMMAND
621
+ def option_type(self) -> OptionType:
622
+ return OptionType.SUB_COMMAND
671
623
 
672
624
  @property
673
625
  def client(self) -> ClientT:
@@ -686,6 +638,55 @@ class SlashSubCommand(
686
638
  assert autodefer is not hikari.UNDEFINED
687
639
  return autodefer
688
640
 
641
+ @property
642
+ def display_name(self) -> str:
643
+ return "/" + " ".join(self.qualified_name)
644
+
645
+ def make_mention(self, guild: hikari.Snowflakeish | hikari.PartialGuild | None = None) -> str:
646
+ """Make a slash mention for this command.
647
+
648
+ Returns
649
+ -------
650
+ str
651
+ The slash command mention.
652
+ """
653
+ instance = self.root._instances.get(hikari.Snowflake(guild) if guild else None)
654
+
655
+ if instance is None:
656
+ raise KeyError(f"Command '{self.display_name}' has not been published in the given scope.")
657
+
658
+ return f"</{' '.join(self.qualified_name)}:{instance.id}>"
659
+
660
+ def _resolve_settings(self) -> _CommandSettings:
661
+ settings = self._parent._resolve_settings() if self._parent else _CommandSettings.default()
662
+
663
+ return settings.apply(
664
+ _CommandSettings(
665
+ autodefer=self._autodefer,
666
+ default_permissions=hikari.UNDEFINED,
667
+ is_nsfw=hikari.UNDEFINED,
668
+ is_dm_enabled=hikari.UNDEFINED,
669
+ )
670
+ )
671
+
672
+ def _resolve_hooks(self) -> list[HookT[ClientT]]:
673
+ assert self._parent is not None
674
+ return self._parent._resolve_hooks() + self._hooks
675
+
676
+ def _resolve_post_hooks(self) -> list[PostHookT[ClientT]]:
677
+ assert self._parent is not None
678
+ return self._parent._resolve_post_hooks() + self._post_hooks
679
+
680
+ async def _handle_exception(self, ctx: Context[ClientT], exc: Exception) -> None:
681
+ try:
682
+ if self.error_handler:
683
+ await self.error_handler(ctx, exc)
684
+ else:
685
+ raise exc
686
+ except Exception as e:
687
+ assert self._parent is not None
688
+ await self._parent._handle_exception(ctx, e)
689
+
689
690
  def _request_option_locale(self, client: Client[t.Any], command: CommandProto) -> None:
690
691
  super()._request_option_locale(client, command)
691
692
 
arc/command/user.py CHANGED
@@ -14,7 +14,7 @@ if t.TYPE_CHECKING:
14
14
  import asyncio
15
15
 
16
16
  from arc.abc.command import CallableCommandProto
17
- from arc.internal.types import UserContextCallbackT
17
+ from arc.internal.types import UserCommandCallbackT
18
18
 
19
19
  __all__ = ("UserCommand", "user_command")
20
20
 
@@ -72,7 +72,7 @@ def user_command(
72
72
  autodefer: bool | AutodeferMode | hikari.UndefinedType = hikari.UNDEFINED,
73
73
  default_permissions: hikari.Permissions | hikari.UndefinedType = hikari.UNDEFINED,
74
74
  name_localizations: t.Mapping[hikari.Locale, str] | None = None,
75
- ) -> t.Callable[[UserContextCallbackT[ClientT]], UserCommand[ClientT]]:
75
+ ) -> t.Callable[[UserCommandCallbackT[ClientT]], UserCommand[ClientT]]:
76
76
  """A decorator that creates a context-menu command on a user.
77
77
 
78
78
  Parameters
@@ -106,7 +106,7 @@ def user_command(
106
106
  ```
107
107
  """
108
108
 
109
- def decorator(callback: UserContextCallbackT[ClientT]) -> UserCommand[ClientT]:
109
+ def decorator(callback: UserCommandCallbackT[ClientT]) -> UserCommand[ClientT]:
110
110
  guild_ids = tuple(hikari.Snowflake(i) for i in guilds) if guilds is not hikari.UNDEFINED else hikari.UNDEFINED
111
111
 
112
112
  return UserCommand(