Repository: Pycord-Development/pycord

Issue:
[ext.bridge] Allow for local error handling w/ bridge commands.
### Summary

Allowing for errors raised inside of bridge commands to be handled locally.

### What is the feature request for?

discord.ext.bridge

### The Problem

When writing bridge commands, no error handler can be directly attached to the command. 
An error handler of such should look like this: `@bridge_command_name.error`
Currently, however, doing so raises the following error: `AttributeError: 'BridgeCommand' object has no attribute 'error'`

### The Ideal Solution

The ideal solution is being able to have the same syntax as other types of commands where error handlers can be attached directly to the BridgeCommand. This would look the same way as the previous: `@bridge_command_name.error`

### The Current Solution

The current solution is to handle all errors within the respective `on_command_error` and `on_application_command_error` events globally.

### Additional Context

_No response_
[ext.bridge] No Before and After Hooks
### Summary

The same functionality of hooks like in application or ext commandss

### What is the feature request for?

The core library

### The Problem

When creating bridge commands, no `before` or `after` hooks can be added

### The Ideal Solution

Same syntax of attaching hooks with passing `BridgeExtContext` or `BridgeApplicationContext`

### The Current Solution

_No response_

### Additional Context

![image](https://user-images.githubusercontent.com/76729234/172029875-71f01e40-f46a-4ae5-ab3f-15f965e95f34.png)
No discord.ext.bridge selection


Discussion:

Resolving patch:
diff --git a/discord/ext/bridge/core.py b/discord/ext/bridge/core.py
--- a/discord/ext/bridge/core.py
+++ b/discord/ext/bridge/core.py
@@ -23,6 +23,7 @@
 DEALINGS IN THE SOFTWARE.
 """
 from typing import Any, List, Union
+import asyncio
 
 import discord.commands.options
 from discord.commands import Option, SlashCommand
@@ -78,6 +79,9 @@ def __init__(self, callback, **kwargs):
         self.callback = callback
         self.kwargs = kwargs
 
+        self.ext_command = BridgeExtCommand(self.callback, **self.kwargs)
+        self.application_command = BridgeSlashCommand(self.callback, **self.kwargs)
+
     def get_ext_command(self):
         """A method to get the ext.commands version of this command.
 
@@ -86,8 +90,7 @@ def get_ext_command(self):
         :class:`BridgeExtCommand`
             The respective traditional (prefix-based) version of the command.
         """
-        command = BridgeExtCommand(self.callback, **self.kwargs)
-        return command
+        return self.ext_command
 
     def get_application_command(self):
         """A method to get the discord.commands version of this command.
@@ -97,8 +100,7 @@ def get_application_command(self):
         :class:`BridgeSlashCommand`
             The respective slash command version of the command.
         """
-        command = BridgeSlashCommand(self.callback, **self.kwargs)
-        return command
+        return self.application_command
 
     def add_to(self, bot: Union[ExtBot, ExtAutoShardedBot]) -> None:
         """Adds the command to a bot.
@@ -108,8 +110,95 @@ def add_to(self, bot: Union[ExtBot, ExtAutoShardedBot]) -> None:
         bot: Union[:class:`ExtBot`, :class:`ExtAutoShardedBot`]
             The bot to add the command to.
         """
-        bot.add_command(self.get_ext_command())
-        bot.add_application_command(self.get_application_command())
+
+        bot.add_command(self.ext_command)
+        bot.add_application_command(self.application_command)
+
+    def error(self, coro):
+        """A decorator that registers a coroutine as a local error handler.
+
+        This error handler is limited to the command it is defined to.
+        However, higher scope handlers (per-cog and global) are still
+        invoked afterwards as a catch-all. This handler also functions as
+        the handler for both the prefixed and slash versions of the command.
+
+        This error handler takes two parameters, a :class:`.BridgeContext` and
+        a :class:`~discord.DiscordException`.
+
+        Parameters
+        -----------
+        coro: :ref:`coroutine <coroutine>`
+            The coroutine to register as the local error handler.
+
+        Raises
+        -------
+        TypeError
+            The coroutine passed is not actually a coroutine.
+        """
+
+        if not asyncio.iscoroutinefunction(coro):
+            raise TypeError("The error handler must be a coroutine.")
+
+        self.ext_command.on_error = coro
+        self.application_command.on_error = coro
+
+        return coro
+
+    def before_invoke(self, coro):
+        """A decorator that registers a coroutine as a pre-invoke hook.
+
+        This hook is called directly before the command is called, making
+        it useful for any sort of set up required. This hook is called
+        for both the prefixed and slash versions of the command.
+
+        This pre-invoke hook takes a sole parameter, a :class:`.BridgeContext`.
+
+        Parameters
+        -----------
+        coro: :ref:`coroutine <coroutine>`
+            The coroutine to register as the pre-invoke hook.
+
+        Raises
+        -------
+        TypeError
+            The coroutine passed is not actually a coroutine.
+        """
+
+        if not asyncio.iscoroutinefunction(coro):
+            raise TypeError("The pre-invoke hook must be a coroutine.")
+
+        self.ext_command.before_invoke = coro
+        self.application_command.before_invoke = coro
+
+        return coro
+
+    def after_invoke(self, coro):
+        """A decorator that registers a coroutine as a post-invoke hook.
+
+        This hook is called directly after the command is called, making it
+        useful for any sort of clean up required. This hook is called for
+        both the prefixed and slash versions of the command.
+
+        This post-invoke hook takes a sole parameter, a :class:`.BridgeContext`.
+
+        Parameters
+        -----------
+        coro: :ref:`coroutine <coroutine>`
+            The coroutine to register as the post-invoke hook.
+
+        Raises
+        -------
+        TypeError
+            The coroutine passed is not actually a coroutine.
+        """
+
+        if not asyncio.iscoroutinefunction(coro):
+            raise TypeError("The post-invoke hook must be a coroutine.")
+
+        self.ext_command.after_invoke = coro
+        self.application_command.after_invoke = coro
+
+        return coro
 
 
 def bridge_command(**kwargs):


Test patch: