Skip to content

feat: Bridge Command Events #1916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ _No changes yet_
([#1880](https://github.com/Pycord-Development/pycord/pull/1880))
- Improved support for setting channel types & added new channel types for
`discord.Option`. ([#1883](https://github.com/Pycord-Development/pycord/pull/1883))
- Added new events `on_bride_command`, `on_bridge_command_completion`, and
`on_bridge_command_error`.
([#1916](https://github.com/Pycord-Development/pycord/pull/1916))

### Changed

Expand Down
63 changes: 62 additions & 1 deletion discord/ext/bridge/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,24 @@

from abc import ABC

from discord.commands import ApplicationContext
from discord.errors import CheckFailure, DiscordException
from discord.interactions import Interaction
from discord.message import Message

from ..commands import AutoShardedBot as ExtAutoShardedBot
from ..commands import Bot as ExtBot
from ..commands import Context as ExtContext
from ..commands import errors
from .context import BridgeApplicationContext, BridgeExtContext
from .core import BridgeCommand, BridgeCommandGroup, bridge_command, bridge_group
from .core import (
BridgeCommand,
BridgeCommandGroup,
BridgeExtCommand,
BridgeSlashCommand,
bridge_command,
bridge_group,
)

__all__ = ("Bot", "AutoShardedBot")

Expand Down Expand Up @@ -106,6 +117,56 @@ def decorator(func) -> BridgeCommandGroup:

return decorator

async def invoke(self, ctx: ExtContext | BridgeExtContext):
if ctx.command is not None:
self.dispatch("command", ctx)
if br_cmd := isinstance(ctx.command, BridgeExtCommand):
self.dispatch("bridge_command", ctx)
try:
if await self.can_run(ctx, call_once=True):
await ctx.command.invoke(ctx)
else:
raise errors.CheckFailure("The global check once functions failed.")
except errors.CommandError as exc:
await ctx.command.dispatch_error(ctx, exc)
else:
self.dispatch("command_completion", ctx)
if br_cmd:
self.dispatch("bridge_command_completion", ctx)
elif ctx.invoked_with:
exc = errors.CommandNotFound(f'Command "{ctx.invoked_with}" is not found')
self.dispatch("command_error", ctx, exc)
if br_cmd:
self.dispatch("bridge_command_error", ctx, exc)

async def invoke_application_command(
self, ctx: ApplicationContext | BridgeApplicationContext
) -> None:
"""|coro|

Invokes the application command given under the invocation
context and handles all the internal event dispatch mechanisms.

Parameters
----------
ctx: :class:`.ApplicationCommand`
The invocation context to invoke.
"""
self._bot.dispatch("application_command", ctx)
if br_cmd := isinstance(ctx.command, BridgeSlashCommand):
self._bot.dispatch("bridge_command", ctx)
try:
if await self._bot.can_run(ctx, call_once=True):
await ctx.command.invoke(ctx)
else:
raise CheckFailure("The global check once functions failed.")
except DiscordException as exc:
await ctx.command.dispatch_error(ctx, exc)
else:
self._bot.dispatch("application_command_completion", ctx)
if br_cmd:
self._bot.dispatch("bridge_command_completion", ctx)


class Bot(BotBase, ExtBot):
"""Represents a discord bot, with support for cross-compatibility between command types.
Expand Down
10 changes: 10 additions & 0 deletions discord/ext/bridge/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,23 @@ def __init__(self, func, **kwargs):
self.brief = kwargs.pop("brief", None)
super().__init__(func, **kwargs)

async def dispatch_error(
self, ctx: BridgeApplicationContext, error: Exception
) -> None:
await super().dispatch_error(ctx, error)
ctx.bot.dispatch("bridge_command_error", ctx, error)


class BridgeExtCommand(Command):
"""A subclass of :class:`.ext.commands.Command` that is used for bridge commands."""

def __init__(self, func, **kwargs):
super().__init__(func, **kwargs)

async def dispatch_error(self, ctx: BridgeExtContext, error: Exception) -> None:
await super().dispatch_error(ctx, error)
ctx.bot.dispatch("bridge_command_error", ctx, error)

async def transform(self, ctx: Context, param: inspect.Parameter) -> Any:
if param.annotation is Attachment:
# skip the parameter checks for bridge attachments
Expand Down
37 changes: 37 additions & 0 deletions docs/ext/bridge/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,43 @@ AutoShardedBot
.. autoclass:: discord.ext.bridge.AutoShardedBot
:members:

Event Reference
---------------

These events function similar to :ref:`the regular events <discord-api-events>`, except they
are custom to the bridge extension module.

.. function:: discord.ext.bridge.on_bridge_command_error(ctx, error)

An error handler that is called when an error is raised
inside a command either through user input error, check
failure, or an error in your own code.

:param ctx: The invocation context.
:type ctx: :class:`.Context`
:param error: The error that was raised.
:type error: :class:`.CommandError` derived

.. function:: discord.ext.bridge.on_bridge_command(ctx)

An event that is called when a command is found and is about to be invoked.

This event is called regardless of whether the command itself succeeds via
error or completes.

:param ctx: The invocation context.
:type ctx: :class:`.Context`

.. function:: discord.ext.bridge.on_bridge_command_completion(ctx)

An event that is called when a command has completed its invocation.

This event is called only if the command succeeded, i.e. all checks have
passed and users input them correctly.

:param ctx: The invocation context.
:type ctx: :class:`.Context`

Commands
--------

Expand Down