Repository: interactions-py/interactions.py

Issue:
[BUG] command dispatching in the gateway is not functioning properly
Command arguments are not dispatched correctly, sub_command(_group)s are not dispatched correctly


Discussion:

Resolving patch:
diff --git a/interactions/api/dispatch.py b/interactions/api/dispatch.py
--- a/interactions/api/dispatch.py
+++ b/interactions/api/dispatch.py
@@ -33,6 +33,7 @@ def dispatch(self, __name: str, *args, **kwargs) -> None:
         :type \**kwargs: dict
         """
         for event in self.events.get(__name, []):
+
             self.loop.create_task(event(*args, **kwargs))
             log.debug(f"DISPATCH: {event}")
 
diff --git a/interactions/api/gateway.py b/interactions/api/gateway.py
--- a/interactions/api/gateway.py
+++ b/interactions/api/gateway.py
@@ -310,17 +310,23 @@ def _dispatch_event(self, event: str, data: dict) -> None:
 
                         if _context.data._json.get("options"):
                             for option in _context.data.options:
-                                __kwargs.update(self.__sub_command_context(option))
-                                __kwargs.update(
-                                    self.__option_type_context(
-                                        _context,
-                                        (
-                                            option["type"]
-                                            if isinstance(option, dict)
-                                            else option.type.value
-                                        ),
-                                    )
+                                _type = self.__option_type_context(
+                                    _context,
+                                    (
+                                        option["type"]
+                                        if isinstance(option, dict)
+                                        else option.type.value
+                                    ),
                                 )
+                                if _type:
+                                    if isinstance(option, dict):
+                                        _type[option["value"]]._client = self._http
+                                        option.update({"value": _type[option["value"]]})
+                                    else:
+                                        _type[option.value]._client = self._http
+                                        option._json.update({"value": _type[option.value]})
+                                _option = self.__sub_command_context(option, _context)
+                                __kwargs.update(_option)
 
                         self._dispatch.dispatch("on_command", _context)
                     elif data["type"] == InteractionType.MESSAGE_COMPONENT:
@@ -390,7 +396,9 @@ def __contextualize(self, data: dict) -> object:
             context: object = getattr(__import__("interactions.context"), _context)
             return context(**data)
 
-    def __sub_command_context(self, data: Union[dict, Option]) -> Union[Tuple[str], dict]:
+    def __sub_command_context(
+        self, data: Union[dict, Option], _context: Optional[object] = MISSING
+    ) -> Union[Tuple[str], dict]:
         """
         Checks if an application command schema has sub commands
         needed for argument collection.
@@ -404,23 +412,62 @@ def __sub_command_context(self, data: Union[dict, Option]) -> Union[Tuple[str],
         _data: dict = data._json if isinstance(data, Option) else data
 
         def _check_auto(option: dict) -> Optional[Tuple[str]]:
-            if option.get("focused"):
-                return (option["name"], option["value"])
+            try:
+                if option.get("focused"):
+                    return (option["name"], option["value"])
+            except AttributeError:
+                return False
 
         x = _check_auto(_data)
         if x:
             return x
         if _data.get("options"):
-            for option in _data["options"]:
-                if option["type"] == OptionType.SUB_COMMAND:
-                    for sub_option in _data["options"]:
+            if _data["type"] == OptionType.SUB_COMMAND:
+                __kwargs["sub_command"] = _data["name"]
+                for sub_option in _data["options"]:
+                    _check_auto(sub_option)
+                    _option_context = self.__option_type_context(
+                        _context,
+                        (
+                            sub_option["type"]
+                            if isinstance(sub_option, dict)
+                            else sub_option.type.value
+                        ),
+                    )
+                    if _option_context:
+                        if isinstance(sub_option, dict):
+                            _option_context[sub_option["value"]]._client = self._http
+                            sub_option.update({"value": _option_context[sub_option["value"]]})
+                        else:
+                            _option_context[sub_option.value]._client = self._http
+                            sub_option._json.update({"value": _option_context[sub_option.value]})
+                    __kwargs[sub_option["name"]] = sub_option["value"]
+            elif _data["type"] == OptionType.SUB_COMMAND_GROUP:
+                __kwargs["sub_command_group"] = _data["name"]
+                for _group_option in _data["options"]:
+                    _check_auto(_group_option)
+                    __kwargs["sub_command"] = _group_option["name"]
+                    for sub_option in _group_option["options"]:
                         _check_auto(sub_option)
+                        _option_context = self.__option_type_context(
+                            _context,
+                            (
+                                sub_option["type"]
+                                if isinstance(sub_option, dict)
+                                else sub_option.type.value
+                            ),
+                        )
+                        if _option_context:
+                            if isinstance(sub_option, dict):
+                                _option_context[sub_option["value"]]._client = self._http
+                                sub_option.update({"value": _option_context[sub_option["value"]]})
+                            else:
+                                _option_context[sub_option.value]._client = self._http
+                                sub_option._json.update(
+                                    {"value": _option_context[sub_option.value]}
+                                )
                         __kwargs[sub_option["name"]] = sub_option["value"]
-                else:
-                    for group in _data["options"]:
-                        for _group_option in group:
-                            _check_auto(_group_option)
-                            __kwargs[_group_option["name"]] = _group_option["value"]
+
         elif _data.get("value") and _data.get("name"):
             __kwargs[_data["name"]] = _data["value"]
 
diff --git a/interactions/api/models/gw.py b/interactions/api/models/gw.py
--- a/interactions/api/models/gw.py
+++ b/interactions/api/models/gw.py
@@ -389,7 +389,16 @@ class MessageReaction(DictSerializerMixin):
     :ivar Optional[Emoji] emoji?: The emoji of the event.
     """
 
-    __slots__ = ("_json", "user_id", "channel_id", "message_id", "guild_id", "member", "emoji")
+    __slots__ = (
+        "_json",
+        "_client",
+        "user_id",
+        "channel_id",
+        "message_id",
+        "guild_id",
+        "member",
+        "emoji",
+    )
 
     def __init__(self, **kwargs):
         super().__init__(**kwargs)
@@ -416,7 +425,7 @@ class ReactionRemove(MessageReaction):
     :ivar Optional[Emoji] emoji?: The emoji of the event.
     """
 
-    __slots__ = ("_json", "user_id", "channel_id", "message_id", "guild_id", "emoji")
+    __slots__ = ("_json", "_client", "user_id", "channel_id", "message_id", "guild_id", "emoji")
 
     def __init__(self, **kwargs):
         super().__init__(**kwargs)


Test patch: