Repository: hikari-py/hikari (python)

Issue:
Uncast Raw Command Interaction Option Values
### Steps to reproduce
1. Have a slash command with any option with type user, channel, role, or mentionable. 
2. Invoke it.

### Expected Result
Expected values of options with type 6—9 to be of type Snowflake.

### Actual Result
They're str.

### System info
hikari (2.0.0.dev102) [HEAD]
CPython 3.9.6 GCC 11.1.0
Linux arch 5.13.13-zen1-1-zen #​1 ZEN SMP PREEMPT Thu, 26 Aug 2021 19:14:35 +0000 x86_64

### Further info
Seems like it's passed in as-is. https://github.com/hikari-py/hikari/blob/0d38beef0b883f39e6dd6dd9ae60c2abf2541cca/hikari/impl/entity_factory.py#L1743

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have made sure to remove ANY sensitive information (bot token, passwords,
      credentials, personal details, etc).
- [x] I have searched the issue tracker and have made sure it's not a duplicate.
      If it is a follow up of another issue, I have specified it.


Pull request description:
Cast `CommandOption.value` to a `Snowflake` for types 6-9
### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have run `nox` and all the pipelines have passed.
- [x] I have made unittests according to the code I have added/modified/deleted.

### Related issues
Closes #777


Patch:
diff --git a/changes/785.bugfix.md b/changes/785.bugfix.md
new file mode 100644
index 00000000..7883fc18
--- /dev/null
+++ b/changes/785.bugfix.md
@@ -0,0 +1,1 @@
+`CommandOption.value` will now be cast to a `Snowflake` for types 6-9
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index 610a1408..66a5e2e6 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -66,6 +66,13 @@
 _ValueT = typing.TypeVar("_ValueT")
 _LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.entity_factory")
 
+_interaction_option_type_mapping: typing.Dict[int, typing.Callable[[typing.Any], typing.Any]] = {
+    commands.OptionType.USER: snowflakes.Snowflake,
+    commands.OptionType.CHANNEL: snowflakes.Snowflake,
+    commands.OptionType.ROLE: snowflakes.Snowflake,
+    commands.OptionType.MENTIONABLE: snowflakes.Snowflake,
+}
+
 
 def _with_int_cast(cast: typing.Callable[[int], _ValueT]) -> typing.Callable[[typing.Any], _ValueT]:
     """Wrap a cast to ensure the value passed to it will first be cast to int."""
@@ -1737,10 +1744,15 @@ def _deserialize_interaction_command_option(
         if raw_suboptions := payload.get("options"):
             suboptions = [self._deserialize_interaction_command_option(suboption) for suboption in raw_suboptions]
 
+        option_type = commands.OptionType(payload["type"])
+        value = payload.get("value")
+        if modifier := _interaction_option_type_mapping.get(option_type):
+            value = modifier(value)
+
         return command_interactions.CommandInteractionOption(
             name=payload["name"],
-            type=commands.OptionType(payload["type"]),
-            value=payload.get("value"),
+            type=option_type,
+            value=value,
             options=suboptions,
         )
 
diff --git a/hikari/interactions/command_interactions.py b/hikari/interactions/command_interactions.py
index ec2443b5..ca5c6beb 100644
--- a/hikari/interactions/command_interactions.py
+++ b/hikari/interactions/command_interactions.py
@@ -115,7 +115,7 @@ class CommandInteractionOption:
     type: typing.Union[commands.OptionType, int] = attr.field(repr=True)
     """Type of this option."""
 
-    value: typing.Union[str, int, bool, None] = attr.field(repr=True)
+    value: typing.Union[snowflakes.Snowflake, str, int, bool, None] = attr.field(repr=True)
     """Value provided for this option.
 
     Either `CommandInteractionOption.value` or `CommandInteractionOption.options`


Test patch:
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index 5bbd7bd9..47daeccb 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -2908,7 +2908,14 @@ def command_interaction_payload(self, member_payload, user_payload, guild_role_p
                 "id": "43123123",
                 "name": "okokokok",
                 "options": [
-                    {"name": "an option", "type": 1, "options": [{"name": "go ice", "type": 4, "value": "42"}]},
+                    {
+                        "name": "an option",
+                        "type": 1,
+                        "options": [
+                            {"name": "go ice", "type": 4, "value": "42"},
+                            {"name": "go fire", "type": 6, "value": 123123123},
+                        ],
+                    },
                 ],
                 "resolved": {
                     "channels": {
@@ -2969,13 +2976,20 @@ def test_deserialize_command_interaction(
         assert option.name == "an option"
         assert option.value is None
         assert option.type is commands.OptionType.SUB_COMMAND
-        assert len(option.options) == 1
-        sub_option = option.options[0]
-        assert sub_option.name == "go ice"
-        assert sub_option.value == "42"
-        assert sub_option.type is commands.OptionType.INTEGER
-        assert sub_option.options is None
-        assert isinstance(sub_option, command_interactions.CommandInteractionOption)
+        assert len(option.options) == 2
+        sub_option1 = option.options[0]
+        assert sub_option1.name == "go ice"
+        assert sub_option1.value == "42"
+        assert sub_option1.type is commands.OptionType.INTEGER
+        assert sub_option1.options is None
+        assert isinstance(sub_option1, command_interactions.CommandInteractionOption)
+        sub_option2 = option.options[1]
+        assert sub_option2.name == "go fire"
+        assert sub_option2.value == 123123123
+        assert isinstance(sub_option2.value, snowflakes.Snowflake)
+        assert sub_option2.type is commands.OptionType.USER
+        assert sub_option2.options is None
+        assert isinstance(sub_option2, command_interactions.CommandInteractionOption)
         assert isinstance(option, command_interactions.CommandInteractionOption)
 
         # ResolvedOptionData