Repository: hikari-py/hikari
Pull request #1564 (2023-03-28 15:23:23)

Fix forum applied tags
### Summary
Fixed applied forum tags not being a sequence of snowflakes.

### 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.




Patch:
diff --git a/changes/1528.feature.md b/changes/1528.feature.md
new file mode 100644
index 00000000..d0eb6116
--- /dev/null
+++ b/changes/1528.feature.md
@@ -0,0 +1,1 @@
+`Role.mention` now returns `"@everyone"` for the `@everyone` role.
diff --git a/changes/1533.breaking.md b/changes/1533.breaking.md
new file mode 100644
index 00000000..31504d74
--- /dev/null
+++ b/changes/1533.breaking.md
@@ -0,0 +1,16 @@
+Refactors to the component builder interfaces which make them flatter:
+
+* Removed `add_to_container` from `ButtonBuilder`, `LinkButtonBuilder`, `InteractiveButtonBuilder`, `SelectMenuBuilder`, `ChannelSelectMenuBuilder`, and `TextInputBuilder`; these classes are no-longer generic and no-longer take `container` in their inits.
+* Replaced `TextSelectMenuBuilder.add_to_container` with the `TextSelectMenuBuilder.parent` property.
+    This new property doesn't "finalise" the addition but rather just returns the parent object, or raises if the select menu is an orphan. This change also involves replacing the `container` parameter in `TextSelectMenuBuilder.__init__` with an optional `parent` parameter.
+* Removed `SelectOptionBuilder.add_to_menu`; this class isn't generic anymore.
+* `TextSelectMenuBuilder.add_option` now takes all the option's configuration as parameters and returns `Self`.
+* Split `MessageActionRowBuilder.add_button` into `.add_interactive_button` and `.add_link_button`.
+    These both now take all the button's configuration as parameters and return `Self`.
+* `MessageActionRowBuilder.add_select_menu` now takes all the menu's configuration as parameters and returns `Self`.
+    The new `.add_channel_menu` and `.add_text_menu` methods should be used for adding text and channel menus. Where `.add_channel_menu` returns `Self` and `.add_text_menu` returns a text menu builder with a `parent` property for getting back to the action row builder.
+* `ModalActionRowBuilder.add_text_input` now takes all the text input's configuration as parameters and returns `Self`.
+* `min_length` and `max_length` can no-longer be `hikari.undefined.UNDEFINED` for the text input builder, and default to `0` and `4000` respectively. This change effects both the types accepted by `ModalActionRowBuilder.__init__` and the return types of the relevant properties.
+* Removed the `emoji_id` and `emoji_name` parameters from `LinkButtonBuilder.__init__`, and `InteractiveButtonBuilder.__init__`.
+* Removed the `style` and `custom_id` parameters from `LinkButtonBuilder.__init__`.
+* Removed the `url` parameter from `InteractiveButtonBuilder.__init__`.
diff --git a/changes/1533.bugfix.md b/changes/1533.bugfix.md
new file mode 100644
index 00000000..414a6238
--- /dev/null
+++ b/changes/1533.bugfix.md
@@ -0,0 +1,1 @@
+`emoji=` can now be passed to `LinkButtonBuilder.__init__` and `InteractiveButtonBuilder.__init__` alone without causing serialization issues (and Pyright will now let you pass it).
diff --git a/changes/1533.deprecation.md b/changes/1533.deprecation.md
new file mode 100644
index 00000000..a272d284
--- /dev/null
+++ b/changes/1533.deprecation.md
@@ -0,0 +1,1 @@
+Renamed `TextInputBuilder.required` property to `TextInputBuilder.is_required`.
diff --git a/changes/1533.feature.md b/changes/1533.feature.md
new file mode 100644
index 00000000..329fa4fe
--- /dev/null
+++ b/changes/1533.feature.md
@@ -0,0 +1,4 @@
+Refactors to the component builder interfaces which make them flatter:
+
+* `hikari.undefined.UNDEFINED` can now be passed to `TextInputBuilder.set_placeholder` and `TextInputBuilder.set_value`.
+* The standard implementation of a select option builder is now exposed at `hikari.impl.special_endpoints.SelectOptionBuilder`.
diff --git a/changes/1564.bugfix.md b/changes/1564.bugfix.md
new file mode 100644
index 00000000..fbbf499b
--- /dev/null
+++ b/changes/1564.bugfix.md
@@ -0,0 +1,1 @@
+Fixed forum channel applied tags not being a sequence of snowflakes.
diff --git a/dev-requirements/formatting.txt b/dev-requirements/formatting.txt
index 369de29f..72fde7f1 100644
--- a/dev-requirements/formatting.txt
+++ b/dev-requirements/formatting.txt
@@ -1,2 +1,2 @@
-black==23.1.0
+black==23.3.0
 isort==5.12.0
diff --git a/dev-requirements/pyright.txt b/dev-requirements/pyright.txt
index de82c85d..bff2e88a 100644
--- a/dev-requirements/pyright.txt
+++ b/dev-requirements/pyright.txt
@@ -1,1 +1,1 @@
-pyright==1.1.300
+pyright==1.1.301
diff --git a/dev-requirements/sphinx.txt b/dev-requirements/sphinx.txt
index d1c9ff2e..6b35b0ee 100644
--- a/dev-requirements/sphinx.txt
+++ b/dev-requirements/sphinx.txt
@@ -4,7 +4,7 @@ sphinx==6.1.3
 furo==2023.3.27
 
 # Autodocumentation
-sphinx-autoapi==2.0.1
+sphinx-autoapi==2.1.0
 numpydoc==1.5.0
 
 # Static files
diff --git a/docs/conf.py b/docs/conf.py
index b61730b5..73844e5c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -82,9 +82,6 @@
 
 html_theme = "furo"
 html_favicon = "https://www.hikari-py.dev/logo.png"
-# html_theme_options = {
-#     "top_of_page_button": None,
-# }
 html_static_path = ["_static"]
 html_css_files = ["extra.css"]
 
@@ -107,7 +104,6 @@
 autoapi_template_dir = "_templates"
 
 autoapi_add_toctree_entry = False
-autoapi_add_objects_to_toctree = False
 autoapi_keep_files = True
 autoapi_member_order = "groupwise"
 
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index e638d8f6..a308d6fb 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -219,7 +219,7 @@ async def edit_channel(
         locked: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         invitable: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         auto_archive_duration: undefined.UndefinedOr[time.Intervalish] = undefined.UNDEFINED,
-        applied_tags: undefined.UndefinedOr[typing.Sequence[channels_.ForumTag]] = undefined.UNDEFINED,
+        applied_tags: undefined.UndefinedOr[snowflakes.SnowflakeishSequence[channels_.ForumTag]] = undefined.UNDEFINED,
         reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
     ) -> channels_.PartialChannel:
         """Edit a channel.
@@ -302,7 +302,7 @@ async def edit_channel(
 
             This should be either 60, 1440, 4320 or 10080 minutes, as of
             writing.
-        applied_tags : hikari.undefined.UndefinedOr[typing.Sequence[hikari.channels.ForumTag]]
+        applied_tags : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishSequence[hikari.channels.ForumTag]]
             If provided, the new tags to apply to the thread. This only applies
             to threads in a forum channel.
         reason : hikari.undefined.UndefinedOr[str]
diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py
index ef85ce82..aa4ec4b2 100644
--- a/hikari/api/special_endpoints.py
+++ b/hikari/api/special_endpoints.py
@@ -50,6 +50,7 @@
 import abc
 import typing
 
+from hikari import components as components_
 from hikari import undefined
 
 if typing.TYPE_CHECKING:
@@ -60,7 +61,6 @@
     from hikari import channels
     from hikari import colors
     from hikari import commands
-    from hikari import components as components_
     from hikari import embeds as embeds_
     from hikari import emojis
     from hikari import files
@@ -76,8 +76,7 @@
     from hikari.interactions import base_interactions
     from hikari.internal import time
 
-_SelectMenuBuilderT = typing.TypeVar("_SelectMenuBuilderT", bound="SelectMenuBuilder[typing.Any]")
-_ContainerT = typing.TypeVar("_ContainerT")
+_ParentT = typing.TypeVar("_ParentT")
 
 
 class TypingIndicator(abc.ABC):
@@ -1264,7 +1263,7 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
         """
 
 
-class ButtonBuilder(ComponentBuilder, abc.ABC, typing.Generic[_ContainerT]):
+class ButtonBuilder(ComponentBuilder, abc.ABC):
     """Builder class for a message button component."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1349,20 +1348,8 @@ def set_is_disabled(self, state: bool, /) -> Self:
             The builder object to enable chained calls.
         """
 
-    @abc.abstractmethod
-    def add_to_container(self) -> _ContainerT:
-        """Add this button to the container component it belongs to.
-
-        This is used as the finalising call during chained calls.
 
-        Returns
-        -------
-        _ContainerT
-            The container component that owns this button.
-        """
-
-
-class LinkButtonBuilder(ButtonBuilder[_ContainerT], abc.ABC):
+class LinkButtonBuilder(ButtonBuilder, abc.ABC):
     """Builder interface for link buttons."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1373,7 +1360,7 @@ def url(self) -> str:
         """URL this button should link to when pressed."""
 
 
-class InteractiveButtonBuilder(ButtonBuilder[_ContainerT], abc.ABC):
+class InteractiveButtonBuilder(ButtonBuilder, abc.ABC):
     """Builder interface for interactive buttons."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1384,7 +1371,7 @@ def custom_id(self) -> str:
         """Developer set custom ID used for identifying interactions with this button."""
 
 
-class SelectOptionBuilder(abc.ABC, typing.Generic[_SelectMenuBuilderT]):
+class SelectOptionBuilder(abc.ABC):
     """Builder class for select menu options."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1463,18 +1450,6 @@ def set_is_default(self, state: bool, /) -> Self:
             The builder object to enable chained calls.
         """
 
-    @abc.abstractmethod
-    def add_to_menu(self) -> _SelectMenuBuilderT:
-        """Add this option to the menu component it belongs to.
-
-        This is used as the finalising call during chained calls.
-
-        Returns
-        -------
-        _SelectMenuBuilderT
-            The menu component that owns this button.
-        """
-
     @abc.abstractmethod
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         """Build a JSON object from this builder.
@@ -1486,7 +1461,7 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
         """
 
 
-class SelectMenuBuilder(ComponentBuilder, abc.ABC, typing.Generic[_ContainerT]):
+class SelectMenuBuilder(ComponentBuilder, abc.ABC):
     """Builder class for a select menu."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1597,50 +1572,58 @@ def set_max_values(self, value: int, /) -> Self:
             The builder object to enable chained calls.
         """
 
-    @abc.abstractmethod
-    def add_to_container(self) -> _ContainerT:
-        """Finalise this builder by adding it to its parent container component.
 
-        Returns
-        -------
-        _ContainerT
-            The parent container component builder.
-        """
-
-
-class TextSelectMenuBuilder(SelectMenuBuilder[_ContainerT], abc.ABC, typing.Generic[_ContainerT]):
+class TextSelectMenuBuilder(SelectMenuBuilder, abc.ABC, typing.Generic[_ParentT]):
     """Builder class for a text select menu."""
 
     __slots__: typing.Sequence[str] = ()
 
     @property
     @abc.abstractmethod
-    def options(self) -> typing.Sequence[SelectOptionBuilder[Self]]:
+    def parent(self) -> _ParentT:
+        """Parent object which initialised this builder."""
+
+    @property
+    @abc.abstractmethod
+    def options(self) -> typing.Sequence[SelectOptionBuilder]:
         """Sequence of the options set for this select menu."""
 
     @abc.abstractmethod
-    def add_option(self, label: str, value: str, /) -> SelectOptionBuilder[Self]:
+    def add_option(
+        self,
+        label: str,
+        value: str,
+        /,
+        *,
+        description: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED,
+        is_default: bool = False,
+    ) -> Self:
         """Add an option to this menu.
 
-        .. note::
-            Setup should be finalised by calling `add_to_menu` in the builder
-            returned.
-
         Parameters
         ----------
         label : str
             The user-facing name of this option, max 100 characters.
         value : str
             The developer defined value of this option, max 100 characters.
+        description : hikari.undefined.UndefinedOr[str]
+            The option's description.
+
+            This can be up to 100 characters long.
+        emoji : typing.Union[hikari.snowflakes.Snowflakeish, hikari.emojis.Emoji, str, hikari.undefined.UndefinedType]
+            The option's display emoji.
+        is_default : bool
+            Whether this option should be selected by default.
 
         Returns
         -------
-        SelectOptionBuilder[SelectMenuBuilder]
-            Option builder object.
+        TextSelectMenuBuilder
+            The select menu builder to enable call chaining.
         """
 
 
-class ChannelSelectMenuBuilder(SelectMenuBuilder[_ContainerT], abc.ABC, typing.Generic[_ContainerT]):
+class ChannelSelectMenuBuilder(SelectMenuBuilder, abc.ABC):
     """Builder class for a channel select menu."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1666,7 +1649,7 @@ def set_channel_types(self, value: typing.Sequence[channels.ChannelType], /) ->
         """
 
 
-class TextInputBuilder(ComponentBuilder, abc.ABC, typing.Generic[_ContainerT]):
+class TextInputBuilder(ComponentBuilder, abc.ABC):
     """Builder class for text inputs components."""
 
     __slots__: typing.Sequence[str] = ()
@@ -1708,17 +1691,22 @@ def value(self) -> undefined.UndefinedOr[str]:
 
     @property
     @abc.abstractmethod
-    def required(self) -> undefined.UndefinedOr[bool]:
+    def required(self) -> bool:
+        """Deprecated alias for `hikari.api.special_endpoints.TextInputBuilder`."""
+
+    @property
+    @abc.abstractmethod
+    def is_required(self) -> bool:
         """Whether this text input is required to be filled-in."""
 
     @property
     @abc.abstractmethod
-    def min_length(self) -> undefined.UndefinedOr[int]:
+    def min_length(self) -> int:
         """Minimum length the text should have."""
 
     @property
     @abc.abstractmethod
-    def max_length(self) -> undefined.UndefinedOr[int]:
+    def max_length(self) -> int:
         """Maximum length the text should have."""
 
     @abc.abstractmethod
@@ -1767,12 +1755,12 @@ def set_label(self, label: str, /) -> Self:
         """
 
     @abc.abstractmethod
-    def set_placeholder(self, placeholder: str, /) -> Self:
+    def set_placeholder(self, placeholder: undefined.UndefinedOr[str], /) -> Self:
         """Set the placeholder text for when the text input is empty.
 
         Parameters
         ----------
-        placeholder : str:
+        placeholder : hikari.undefined.UndefinedOr[str]
             Placeholder text that will disappear when the user types anything.
 
         Returns
@@ -1782,12 +1770,12 @@ def set_placeholder(self, placeholder: str, /) -> Self:
         """
 
     @abc.abstractmethod
-    def set_value(self, value: str, /) -> Self:
+    def set_value(self, value: undefined.UndefinedOr[str], /) -> Self:
         """Pre-filled text that will be sent if the user does not write anything.
 
         Parameters
         ----------
-        value : str
+        value : hikari.undefined.UndefinedOr[str]
             Pre-filled text that will be sent if the user does not write anything.
 
         Returns
@@ -1841,16 +1829,6 @@ def set_max_length(self, max_length: int, /) -> Self:
             The builder object to enable chained calls.
         """
 
-    @abc.abstractmethod
-    def add_to_container(self) -> _ContainerT:
-        """Finalise this builder by adding it to its parent container component.
-
-        Returns
-        -------
-        _ContainerT
-            The parent container component builder.
-        """
-
 
 class MessageActionRowBuilder(ComponentBuilder, abc.ABC):
     """Builder class for action row components."""
@@ -1887,95 +1865,199 @@ def add_component(self, component: ComponentBuilder, /) -> Self:
             The builder object to enable chained calls.
         """
 
-    @typing.overload
     @abc.abstractmethod
-    def add_button(
-        self, style: components_.InteractiveButtonTypesT, custom_id: str, /
-    ) -> InteractiveButtonBuilder[Self]:
-        ...
+    def add_interactive_button(
+        self,
+        style: components_.InteractiveButtonTypesT,
+        custom_id: str,
+        /,
+        *,
+        emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED,
+        label: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        is_disabled: bool = False,
+    ) -> Self:
+        """Add an interactive button component to this action row builder.
 
-    @typing.overload
-    @abc.abstractmethod
-    def add_button(
-        self, style: typing.Literal[components_.ButtonStyle.LINK, 5], url: str, /
-    ) -> LinkButtonBuilder[Self]:
-        ...
+        Either `emoji` or `label` (exclusively) must be provided to be the button's
+        displayed label.
 
-    @typing.overload
-    @abc.abstractmethod
-    def add_button(
-        self, style: typing.Union[int, components_.ButtonStyle], url_or_custom_id: str, /
-    ) -> typing.Union[LinkButtonBuilder[Self], InteractiveButtonBuilder[Self]]:
-        ...
+        Parameters
+        ----------
+        style : hikari.messages.InteractiveButtonTypesT
+            The button's style.
+        custom_id : str
+            The developer-defined custom identifier used to identify which button
+            triggered component interactions.
+        emoji : typing.Union[hikari.snowflakes.Snowflakeish, hikari.emojis.Emoji, str, hikari.undefined.UndefinedType]
+            The button's display emoji.
+        label : hikari.undefined.UndefinedOr[str]
+            The button's display label.
+        is_disabled : bool
+            Whether the button should be marked as disabled.
+
+        Returns
+        -------
+        ActionRowBuilder
+            The action row builder to enable chained calls.
+        """
 
     @abc.abstractmethod
-    def add_button(
-        self, style: typing.Union[int, components_.ButtonStyle], url_or_custom_id: str, /
-    ) -> typing.Union[LinkButtonBuilder[Self], InteractiveButtonBuilder[Self]]:
-        """Add a button component to this action row builder.
+    def add_link_button(
+        self,
+        url: str,
+        /,
+        *,
+        emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED,
+        label: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        is_disabled: bool = False,
+    ) -> Self:
+        """Add a link button component to this action row builder.
+
+        Either `emoji` or `label` (exclusively) must be provided to be the button's
+        displayed label.
 
         Parameters
         ----------
-        style : typing.Union[int, hikari.messages.ButtonStyle]
-            The button's style.
-        url_or_custom_id : str
-            For interactive button styles this is a developer-defined custom
-            identifier used to identify which button triggered component interactions.
-
-            For Link button styles this is the URL the link button should redirect
-            to.
+        url : str
+            The URL the link button should redirect to.
+        emoji : typing.Union[hikari.snowflakes.Snowflakeish, hikari.emojis.Emoji, str, hikari.undefined.UndefinedType]
+            The button's display emoji.
+        label : hikari.undefined.UndefinedOr[str]
+            The button's display label.
+        is_disabled : bool
+            Whether the button should be marked as disabled.
 
         Returns
         -------
-        typing.Union[LinkButtonBuilder[Self], InteractiveButtonBuilder[Self]]
-            Button builder object.
-            `ButtonBuilder.add_to_container` should be called to finalise the
-            component.
+        ActionRowBuilder
+            The action row builder to enable chained calls.
         """
 
-    @typing.overload
     @abc.abstractmethod
     def add_select_menu(
         self,
-        type_: typing.Literal[components_.ComponentType.TEXT_SELECT_MENU, 3],
+        type_: typing.Union[components_.ComponentType, int],
         custom_id: str,
         /,
-    ) -> TextSelectMenuBuilder[Self]:
-        ...
+        *,
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> Self:
+        """Add a select menu component to this action row builder.
+
+        For channel select menus and text select menus see
+        `MessageActionRowBuilder.add_channel_menu` and
+        `MessageActionRowBuilder.add_text_menu`.
+
+        Parameters
+        ----------
+        type_ : typing.Union[hikari.components.ComponentType, int]
+            The type for the select menu.
+        custom_id : str
+            A developer-defined custom identifier used to identify which menu
+            triggered component interactions.
+        placeholder : hikari.undefined.UndefinedOr[str]
+            Placeholder text to show when no entries have been selected.
+        min_values : int
+            The minimum amount of entries which need to be selected.
+        max_values : int
+            The maximum amount of entries which can be selected.
+        is_disabled : bool
+            Whether this select menu should be marked as disabled.
+
+        Returns
+        -------
+        ActionRowBuilder
+            The action row builder to enable chained calls.
+
+        Raises
+        ------
+        ValueError
+            If an invalid select menu type is passed.
+        """
 
-    @typing.overload
     @abc.abstractmethod
-    def add_select_menu(
+    def add_channel_menu(
         self,
-        type_: typing.Literal[components_.ComponentType.CHANNEL_SELECT_MENU, 8],
         custom_id: str,
         /,
-    ) -> ChannelSelectMenuBuilder[Self]:
-        ...
+        *,
+        channel_types: typing.Sequence[channels.ChannelType] = (),
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> Self:
+        """Add a channel select menu component to this action row builder.
+
+        Parameters
+        ----------
+        custom_id : str
+            A developer-defined custom identifier used to identify which menu
+            triggered component interactions.
+        channel_types : typing.Sequence[hikari.channels.ChannelType]
+            The channel types this select menu should allow.
+
+            If left as an empty sequence then there will be no
+            channel type restriction.
+        placeholder : hikari.undefined.UndefinedOr[str]
+            Placeholder text to show when no entries have been selected.
+        min_values : int
+            The minimum amount of entries which need to be selected.
+        max_values : int
+            The maximum amount of entries which can be selected.
+        is_disabled : bool
+            Whether this select menu should be marked as disabled.
+
+        Returns
+        -------
+        ActionRowBuilder
+            The action row builder to enable chained calls.
+
+        Raises
+        ------
+        ValueError
+            If an invalid select menu type is passed.
+        """
 
     @abc.abstractmethod
-    def add_select_menu(
+    def add_text_menu(
         self,
-        type_: typing.Union[components_.ComponentType, int],
         custom_id: str,
         /,
-    ) -> SelectMenuBuilder[Self]:
+        *,
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> TextSelectMenuBuilder[Self]:
         """Add a select menu component to this action row builder.
 
         Parameters
         ----------
-        type_ : typing.Union[hikari.components.ComponentType, int]
-            The type for the select menu.
         custom_id : str
             A developer-defined custom identifier used to identify which menu
             triggered component interactions.
+        placeholder : hikari.undefined.UndefinedOr[str]
+            Placeholder text to show when no entries have been selected.
+        min_values : int
+            The minimum amount of entries which need to be selected.
+        max_values : int
+            The maximum amount of entries which can be selected.
+        is_disabled : bool
+            Whether this select menu should be marked as disabled.
 
         Returns
         -------
-        SelectMenuBuilder[Self]
-            Select menu builder object.
-            `SelectMenuBuilder.add_to_container` should be called to finalise the
-            component.
+        TextSelectMenuBuilder
+            The text select menu builder.
+
+            `TextSelectMenuBuilder.add_option` should be called to add
+            options to the returned builder then
+            `TextSelectMenuBuilder.parent` can be used to return to this
+            action row while chaining calls.
 
         Raises
         ------
@@ -2028,7 +2110,15 @@ def add_text_input(
         self,
         custom_id: str,
         label: str,
-    ) -> TextInputBuilder[Self]:
+        /,
+        *,
+        style: components_.TextInputStyle = components_.TextInputStyle.SHORT,
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        value: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        required: bool = True,
+        min_length: int = 0,
+        max_length: int = 4000,
+    ) -> Self:
         """Add a text input component to this action row builder.
 
         Parameters
@@ -2037,11 +2127,25 @@ def add_text_input(
             Developer set custom ID used for identifying this text input.
         label : str
             Label above this text input.
+        style : hikari.components.TextInputStyle
+            The text input's style.
+        placeholder : hikari.undefined.UndefinedOr[str]
+            Placeholder text to display when the text input is empty.
+        value : hikari.undefined.UndefinedOr[str]
+            Default text to pre-fill the field with.
+        required : bool
+            Whether text must be supplied for this text input.
+        min_length : int
+            Minimum length the input text can be.
+
+            This can be greater than or equal to 0 and less than or equal to 4000.
+        max_length : int
+            Maximum length the input text can be.
+
+            This can be greater than or equal to 1 and less than or equal to 4000.
 
         Returns
         -------
-        TextInputBuilder[Self]
-            Text input builder object.
-            `TextInputBuilder.add_to_container` should be called to finalise the
-            component.
+        ModalActionRowBuilder
+            The modal action row builder to enable call chaining.
         """
diff --git a/hikari/channels.py b/hikari/channels.py
index baa02f4e..ea2d4f0d 100644
--- a/hikari/channels.py
+++ b/hikari/channels.py
@@ -1001,7 +1001,7 @@ async def edit(
         auto_archive_duration: undefined.UndefinedOr[time.Intervalish] = undefined.UNDEFINED,
         locked: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         invitable: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
-        applied_tags: undefined.UndefinedOr[typing.Sequence[ForumTag]] = undefined.UNDEFINED,
+        applied_tags: undefined.UndefinedOr[snowflakes.SnowflakeishSequence[ForumTag]] = undefined.UNDEFINED,
         reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
     ) -> PartialChannel:
         """Edit the text channel.
@@ -1060,7 +1060,7 @@ async def edit(
         invitable : hikari.undefined.UndefinedOr[bool]
             If provided, the new setting for whether non-moderators can invite
             new members to a private thread. This only applies to threads.
-        applied_tags : hikari.undefined.UndefinedOr[typing.Sequence[ForumTag]]
+        applied_tags : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishSequence[hikari.channels.ForumTag]]
             If provided, the new tags to apply to the thread. This only applies
             to threads in a forum channel.
         reason : hikari.undefined.UndefinedOr[str]
diff --git a/hikari/guilds.py b/hikari/guilds.py
index 34893db9..d143bb1f 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -1022,6 +1022,18 @@ def icon_url(self) -> typing.Optional[files.URL]:
         """Role icon URL, if there is one."""
         return self.make_icon_url()
 
+    @property
+    def mention(self) -> str:
+        """Return a raw mention string for the role.
+
+        When this role represents @everyone mentions will only work if
+        `mentions_everyone` is `True`.
+        """
+        if self.guild_id == self.id:
+            return "@everyone"
+
+        return super().mention
+
     def make_icon_url(self, *, ext: str = "png", size: int = 4096) -> typing.Optional[files.URL]:
         """Generate the icon URL for this role, if set.
 
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index 5e1b42d3..34935f4a 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -1043,7 +1043,7 @@ async def edit_channel(
         locked: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         invitable: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         auto_archive_duration: undefined.UndefinedOr[time.Intervalish] = undefined.UNDEFINED,
-        applied_tags: undefined.UndefinedOr[typing.Sequence[channels_.ForumTag]] = undefined.UNDEFINED,
+        applied_tags: undefined.UndefinedOr[snowflakes.SnowflakeishSequence[channels_.ForumTag]] = undefined.UNDEFINED,
         reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
     ) -> channels_.PartialChannel:
         if isinstance(auto_archive_duration, datetime.timedelta):
@@ -1095,7 +1095,7 @@ async def edit_channel(
         body.put("auto_archive_duration", auto_archive_duration, conversion=time.timespan_to_int)
         body.put("locked", locked)
         body.put("invitable", invitable)
-        body.put_array("applied_tags", applied_tags, conversion=self._entity_factory.serialize_forum_tag)
+        body.put_snowflake_array("applied_tags", applied_tags)
 
         response = await self._request(route, json=body, reason=reason)
         assert isinstance(response, dict)
diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py
index 516a851d..f1459ede 100644
--- a/hikari/impl/special_endpoints.py
+++ b/hikari/impl/special_endpoints.py
@@ -38,6 +38,7 @@
     "InteractiveButtonBuilder",
     "LinkButtonBuilder",
     "SelectMenuBuilder",
+    "SelectOptionBuilder",
     "ChannelSelectMenuBuilder",
     "TextSelectMenuBuilder",
     "TextInputBuilder",
@@ -66,6 +67,7 @@
 from hikari.interactions import base_interactions
 from hikari.internal import attr_extensions
 from hikari.internal import data_binding
+from hikari.internal import deprecation
 from hikari.internal import mentions
 from hikari.internal import routes
 from hikari.internal import time
@@ -89,7 +91,6 @@
     from hikari.api import rest as rest_api
 
     _T = typing.TypeVar("_T")
-    _TextSelectMenuBuilderT = typing.TypeVar("_TextSelectMenuBuilderT", bound="TextSelectMenuBuilder[typing.Any]")
 
     class _RequestCallSig(typing.Protocol):
         async def __call__(
@@ -115,13 +116,8 @@ def __call__(
         ) -> _GuildThreadChannelCovT:
             raise NotImplementedError
 
-    # Hack around used to avoid recursive generic types leading to type checker issues in builders
-    class _ContainerProto(typing.Protocol):
-        def add_component(self, component: special_endpoints.ComponentBuilder, /) -> Self:
-            raise NotImplementedError
-
 
-_ContainerProtoT = typing.TypeVar("_ContainerProtoT", bound="_ContainerProto")
+_ParentT = typing.TypeVar("_ParentT")
 _GuildThreadChannelT = typing.TypeVar("_GuildThreadChannelT", bound=channels.GuildThreadChannel)
 _GuildThreadChannelCovT = typing.TypeVar("_GuildThreadChannelCovT", bound=channels.GuildThreadChannel, covariant=True)
 
@@ -1441,19 +1437,21 @@ def _build_emoji(
 
 @attr_extensions.with_copy
 @attr.define(kw_only=True, weakref_slot=False)
-class _ButtonBuilder(special_endpoints.ButtonBuilder[_ContainerProtoT]):
-    _container: _ContainerProtoT = attr.field(alias="container")
+class _ButtonBuilder(special_endpoints.ButtonBuilder):
     _style: typing.Union[int, component_models.ButtonStyle] = attr.field(alias="style")
-    _custom_id: undefined.UndefinedOr[str] = attr.field(alias="custom_id", default=undefined.UNDEFINED)
-    _url: undefined.UndefinedOr[str] = attr.field(alias="url", default=undefined.UNDEFINED)
+    _custom_id: undefined.UndefinedOr[str] = attr.field()
+    _url: undefined.UndefinedOr[str] = attr.field()
     _emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = attr.field(
-        default=undefined.UNDEFINED
+        alias="emoji", default=undefined.UNDEFINED
     )
-    _emoji_id: undefined.UndefinedOr[str] = attr.field(alias="emoji_id", default=undefined.UNDEFINED)
-    _emoji_name: undefined.UndefinedOr[str] = attr.field(alias="emoji_name", default=undefined.UNDEFINED)
+    _emoji_id: undefined.UndefinedOr[str] = attr.field(init=False, default=undefined.UNDEFINED)
+    _emoji_name: undefined.UndefinedOr[str] = attr.field(init=False, default=undefined.UNDEFINED)
     _label: undefined.UndefinedOr[str] = attr.field(alias="label", default=undefined.UNDEFINED)
     _is_disabled: bool = attr.field(alias="is_disabled", default=False)
 
+    def __attrs_post_init__(self) -> None:
+        self._emoji_id, self._emoji_name = _build_emoji(self._emoji)
+
     @property
     def type(self) -> typing.Literal[component_models.ComponentType.BUTTON]:
         return component_models.ComponentType.BUTTON
@@ -1491,10 +1489,6 @@ def set_is_disabled(self, state: bool, /) -> Self:
         self._is_disabled = state
         return self
 
-    def add_to_container(self) -> _ContainerProtoT:
-        self._container.add_component(self)
-        return self._container
-
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         data = data_binding.JSONObjectBuilder()
 
@@ -1516,9 +1510,13 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
 
 
 @attr.define(kw_only=True, weakref_slot=False)
-class LinkButtonBuilder(_ButtonBuilder[_ContainerProtoT], special_endpoints.LinkButtonBuilder[_ContainerProtoT]):
+class LinkButtonBuilder(_ButtonBuilder, special_endpoints.LinkButtonBuilder):
     """Builder class for link buttons."""
 
+    _custom_id: undefined.UndefinedType = attr.field(init=False, default=undefined.UNDEFINED)
+    _style: typing.Literal[component_models.ButtonStyle.LINK] = attr.field(
+        init=False, default=component_models.ButtonStyle.LINK
+    )
     _url: str = attr.field(alias="url")
 
     @property
@@ -1527,12 +1525,11 @@ def url(self) -> str:
 
 
 @attr.define(kw_only=True, weakref_slot=False)
-class InteractiveButtonBuilder(
-    _ButtonBuilder[_ContainerProtoT], special_endpoints.InteractiveButtonBuilder[_ContainerProtoT]
-):
+class InteractiveButtonBuilder(_ButtonBuilder, special_endpoints.InteractiveButtonBuilder):
     """Builder class for interactive buttons."""
 
     _custom_id: str = attr.field(alias="custom_id")
+    _url: undefined.UndefinedType = attr.field(init=False, default=undefined.UNDEFINED)
 
     @property
     def custom_id(self) -> str:
@@ -1540,20 +1537,24 @@ def custom_id(self) -> str:
 
 
 @attr_extensions.with_copy
-@attr.define(kw_only=True, weakref_slot=False)
-class _SelectOptionBuilder(special_endpoints.SelectOptionBuilder["_TextSelectMenuBuilderT"]):
+@attr.define(weakref_slot=False)
+class SelectOptionBuilder(special_endpoints.SelectOptionBuilder):
     """Builder class for select menu options."""
 
-    _menu: _TextSelectMenuBuilderT = attr.field(alias="menu")
     _label: str = attr.field(alias="label")
     _value: str = attr.field(alias="value")
-    _description: undefined.UndefinedOr[str] = attr.field(alias="description", default=undefined.UNDEFINED)
+    _description: undefined.UndefinedOr[str] = attr.field(
+        alias="description", default=undefined.UNDEFINED, kw_only=True
+    )
     _emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = attr.field(
-        alias="emoji", default=undefined.UNDEFINED
+        alias="emoji", default=undefined.UNDEFINED, kw_only=True
     )
-    _emoji_id: undefined.UndefinedOr[str] = attr.field(alias="emoji_id", default=undefined.UNDEFINED)
-    _emoji_name: undefined.UndefinedOr[str] = attr.field(alias="emoji_name", default=undefined.UNDEFINED)
-    _is_default: bool = attr.field(alias="is_default", default=False)
+    _emoji_id: undefined.UndefinedOr[str] = attr.field(init=False, default=undefined.UNDEFINED)
+    _emoji_name: undefined.UndefinedOr[str] = attr.field(init=False, default=undefined.UNDEFINED)
+    _is_default: bool = attr.field(alias="is_default", default=False, kw_only=True)
+
+    def __attrs_post_init__(self) -> None:
+        self._emoji_id, self._emoji_name = _build_emoji(self._emoji)
 
     @property
     def label(self) -> str:
@@ -1592,10 +1593,6 @@ def set_is_default(self, state: bool, /) -> Self:
         self._is_default = state
         return self
 
-    def add_to_menu(self) -> _TextSelectMenuBuilderT:
-        self._menu.add_raw_option(self)
-        return self._menu
-
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         data = data_binding.JSONObjectBuilder()
 
@@ -1615,10 +1612,9 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
 
 @attr_extensions.with_copy
 @attr.define(kw_only=True, weakref_slot=False)
-class SelectMenuBuilder(special_endpoints.SelectMenuBuilder[_ContainerProtoT]):
+class SelectMenuBuilder(special_endpoints.SelectMenuBuilder):
     """Builder class for select menus."""
 
-    _container: _ContainerProtoT = attr.field(alias="container")
     _type: typing.Union[component_models.ComponentType, int] = attr.field(alias="type")
     _custom_id: str = attr.field(alias="custom_id")
     _placeholder: undefined.UndefinedOr[str] = attr.field(alias="placeholder", default=undefined.UNDEFINED)
@@ -1666,10 +1662,6 @@ def set_max_values(self, value: int, /) -> Self:
         self._max_values = value
         return self
 
-    def add_to_container(self) -> _ContainerProtoT:
-        self._container.add_component(self)
-        return self._container
-
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         data = data_binding.JSONObjectBuilder()
 
@@ -1682,29 +1674,96 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
         return data
 
 
-@attr_extensions.with_copy
-@attr.define(kw_only=True, weakref_slot=False)
-class TextSelectMenuBuilder(
-    SelectMenuBuilder[_ContainerProtoT], special_endpoints.TextSelectMenuBuilder[_ContainerProtoT]
-):
+@attr.define(init=False, weakref_slot=False)
+class TextSelectMenuBuilder(SelectMenuBuilder, special_endpoints.TextSelectMenuBuilder[_ParentT]):
     """Builder class for text select menus."""
 
-    _type: typing.Literal[component_models.ComponentType.TEXT_SELECT_MENU] = attr.field(
-        default=component_models.ComponentType.TEXT_SELECT_MENU, init=False
-    )
-    # Any has to be used here as we can't access Self type in this context
-    _options: typing.List[special_endpoints.SelectOptionBuilder[typing.Any]] = attr.field(alias="options", factory=list)
+    _options: typing.List[special_endpoints.SelectOptionBuilder] = attr.field()
+    _parent: typing.Optional[_ParentT] = attr.field()
+    _type: typing.Literal[component_models.ComponentType.TEXT_SELECT_MENU] = attr.field()
+
+    if not typing.TYPE_CHECKING:
+        # This will not work with the generated for attrs copy methods.
+        __copy__ = None
+        __deepcopy__ = None
+
+    @typing.overload
+    def __init__(
+        self,
+        *,
+        custom_id: str,
+        parent: _ParentT,
+        options: typing.Sequence[special_endpoints.SelectOptionBuilder] = (),
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> None:
+        ...
+
+    @typing.overload
+    def __init__(
+        self: TextSelectMenuBuilder[typing.NoReturn],
+        *,
+        custom_id: str,
+        options: typing.Sequence[special_endpoints.SelectOptionBuilder] = (),
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> None:
+        ...
+
+    def __init__(
+        self,
+        *,
+        custom_id: str,
+        parent: typing.Optional[_ParentT] = None,
+        options: typing.Sequence[special_endpoints.SelectOptionBuilder] = (),
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> None:
+        super().__init__(
+            type=component_models.ComponentType.TEXT_SELECT_MENU,
+            custom_id=custom_id,
+            placeholder=placeholder,
+            min_values=min_values,
+            max_values=max_values,
+            is_disabled=is_disabled,
+        )
+        self._options = list(options)
+        self._parent = parent
+
+    @property
+    def parent(self) -> _ParentT:
+        if self._parent is None:
+            raise RuntimeError("This menu has no parent")
+
+        return self._parent
 
     @property
     def options(
         self,
-    ) -> typing.Sequence[special_endpoints.SelectOptionBuilder[Self]]:
+    ) -> typing.Sequence[special_endpoints.SelectOptionBuilder]:
         return self._options.copy()
 
-    def add_option(self, label: str, value: str, /) -> special_endpoints.SelectOptionBuilder[Self]:
-        return _SelectOptionBuilder(menu=self, label=label, value=value)
+    def add_option(
+        self,
+        label: str,
+        value: str,
+        /,
+        *,
+        description: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED,
+        is_default: bool = False,
+    ) -> Self:
+        return self.add_raw_option(
+            SelectOptionBuilder(label=label, value=value, description=description, emoji=emoji, is_default=is_default)
+        )
 
-    def add_raw_option(self, option: special_endpoints.SelectOptionBuilder[Self], /) -> Self:
+    def add_raw_option(self, option: special_endpoints.SelectOptionBuilder, /) -> Self:
         self._options.append(option)
         return self
 
@@ -1717,9 +1776,7 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
 
 @attr_extensions.with_copy
 @attr.define(kw_only=True, weakref_slot=False)
-class ChannelSelectMenuBuilder(
-    SelectMenuBuilder[_ContainerProtoT], special_endpoints.ChannelSelectMenuBuilder[_ContainerProtoT]
-):
+class ChannelSelectMenuBuilder(SelectMenuBuilder, special_endpoints.ChannelSelectMenuBuilder):
     """Builder class for channel select menus."""
 
     _channel_types: typing.Sequence[channels.ChannelType] = attr.field(alias="channel_types", factory=list)
@@ -1744,10 +1801,9 @@ def build(self) -> typing.MutableMapping[str, typing.Any]:
 
 @attr_extensions.with_copy
 @attr.define(kw_only=True, weakref_slot=False)
-class TextInputBuilder(special_endpoints.TextInputBuilder[_ContainerProtoT]):
+class TextInputBuilder(special_endpoints.TextInputBuilder):
     """Standard implementation of `hikari.api.special_endpoints.TextInputBuilder`."""
 
-    _container: _ContainerProtoT = attr.field(alias="container")
     _custom_id: str = attr.field(alias="custom_id")
     _label: str = attr.field(alias="label")
 
@@ -1756,9 +1812,9 @@ class TextInputBuilder(special_endpoints.TextInputBuilder[_ContainerProtoT]):
         alias="placeholder", default=undefined.UNDEFINED, kw_only=True
     )
     _value: undefined.UndefinedOr[str] = attr.field(alias="value", default=undefined.UNDEFINED, kw_only=True)
-    _required: undefined.UndefinedOr[bool] = attr.field(alias="required", default=undefined.UNDEFINED, kw_only=True)
-    _min_length: undefined.UndefinedOr[int] = attr.field(alias="min_length", default=undefined.UNDEFINED, kw_only=True)
-    _max_length: undefined.UndefinedOr[int] = attr.field(alias="max_length", default=undefined.UNDEFINED, kw_only=True)
+    _required: bool = attr.field(alias="required", default=True, kw_only=True)
+    _min_length: int = attr.field(alias="min_length", default=0, kw_only=True)
+    _max_length: int = attr.field(alias="max_length", default=4000, kw_only=True)
 
     @property
     def type(self) -> typing.Literal[component_models.ComponentType.TEXT_INPUT]:
@@ -1785,15 +1841,22 @@ def value(self) -> undefined.UndefinedOr[str]:
         return self._value
 
     @property
-    def required(self) -> undefined.UndefinedOr[bool]:
+    def required(self) -> bool:
+        deprecation.warn_deprecated(
+            ".required", removal_version="2.0.0.dev119", additional_info="Use .is_required", quote=False
+        )
+        return self._required
+
+    @property
+    def is_required(self) -> bool:
         return self._required
 
     @property
-    def min_length(self) -> undefined.UndefinedOr[int]:
+    def min_length(self) -> int:
         return self._min_length
 
     @property
-    def max_length(self) -> undefined.UndefinedOr[int]:
+    def max_length(self) -> int:
         return self._max_length
 
     def set_style(self, style: typing.Union[component_models.TextInputStyle, int], /) -> Self:
@@ -1808,11 +1871,11 @@ def set_label(self, label: str, /) -> Self:
         self._label = label
         return self
 
-    def set_placeholder(self, placeholder: str, /) -> Self:
+    def set_placeholder(self, placeholder: undefined.UndefinedOr[str], /) -> Self:
         self._placeholder = placeholder
         return self
 
-    def set_value(self, value: str, /) -> Self:
+    def set_value(self, value: undefined.UndefinedOr[str], /) -> Self:
         self._value = value
         return self
 
@@ -1828,10 +1891,6 @@ def set_max_length(self, max_length: int, /) -> Self:
         self._max_length = max_length
         return self
 
-    def add_to_container(self) -> _ContainerProtoT:
-        self._container.add_component(self)
-        return self._container
-
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         data = data_binding.JSONObjectBuilder()
 
@@ -1853,7 +1912,7 @@ class MessageActionRowBuilder(special_endpoints.MessageActionRowBuilder):
     """Standard implementation of `hikari.api.special_endpoints.ActionRowBuilder`."""
 
     _components: typing.List[special_endpoints.ComponentBuilder] = attr.field(alias="components", factory=list)
-    _stored_type: typing.Optional[component_models.ComponentType] = attr.field(default=None, init=False)
+    _stored_type: typing.Optional[int] = attr.field(default=None, init=False)
 
     @property
     def type(self) -> typing.Literal[component_models.ComponentType.ACTION_ROW]:
@@ -1864,7 +1923,6 @@ def components(self) -> typing.Sequence[special_endpoints.ComponentBuilder]:
         return self._components.copy()
 
     def _assert_can_add_type(self, type_: typing.Union[component_models.ComponentType, int], /) -> None:
-        type_ = component_models.ComponentType(type_)
         if self._stored_type is not None and self._stored_type != type_:
             raise ValueError(
                 f"{type_} component type cannot be added to a container which already holds {self._stored_type}"
@@ -1873,80 +1931,101 @@ def _assert_can_add_type(self, type_: typing.Union[component_models.ComponentTyp
         self._stored_type = type_
 
     def add_component(self, component: special_endpoints.ComponentBuilder, /) -> Self:
+        self._assert_can_add_type(component.type)
         self._components.append(component)
         return self
 
-    @typing.overload
-    def add_button(
-        self, style: component_models.InteractiveButtonTypesT, custom_id: str, /
-    ) -> special_endpoints.InteractiveButtonBuilder[Self]:
-        ...
-
-    @typing.overload
-    def add_button(
+    def add_interactive_button(
         self,
-        style: typing.Literal[component_models.ButtonStyle.LINK, 5],
-        url: str,
-        /,
-    ) -> special_endpoints.LinkButtonBuilder[Self]:
-        ...
-
-    @typing.overload
-    def add_button(
-        self,
-        style: typing.Union[int, component_models.ButtonStyle],
-        url_or_custom_id: str,
+        style: component_models.InteractiveButtonTypesT,
+        custom_id: str,
         /,
-    ) -> typing.Union[special_endpoints.LinkButtonBuilder[Self], special_endpoints.InteractiveButtonBuilder[Self],]:
-        ...
+        *,
+        emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED,
+        label: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        is_disabled: bool = False,
+    ) -> Self:
+        return self.add_component(
+            InteractiveButtonBuilder(
+                style=style, custom_id=custom_id, emoji=emoji, label=label, is_disabled=is_disabled
+            )
+        )
 
-    def add_button(
+    def add_link_button(
         self,
-        style: typing.Union[int, component_models.ButtonStyle],
-        url_or_custom_id: str,
+        url: str,
         /,
-    ) -> typing.Union[special_endpoints.LinkButtonBuilder[Self], special_endpoints.InteractiveButtonBuilder[Self],]:
-        self._assert_can_add_type(component_models.ComponentType.BUTTON)
-        if style in component_models.InteractiveButtonTypes:
-            return InteractiveButtonBuilder(container=self, style=style, custom_id=url_or_custom_id)
-
-        return LinkButtonBuilder(container=self, style=style, url=url_or_custom_id)
+        *,
+        emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED,
+        label: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        is_disabled: bool = False,
+    ) -> Self:
+        return self.add_component(LinkButtonBuilder(url=url, label=label, emoji=emoji, is_disabled=is_disabled))
 
-    @typing.overload
     def add_select_menu(
         self,
-        type_: typing.Literal[component_models.ComponentType.TEXT_SELECT_MENU, 3],
+        type_: typing.Union[component_models.ComponentType, int],
         custom_id: str,
         /,
-    ) -> special_endpoints.TextSelectMenuBuilder[Self]:
-        ...
+        *,
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> Self:
+        return self.add_component(
+            SelectMenuBuilder(
+                type=type_,
+                custom_id=custom_id,
+                placeholder=placeholder,
+                min_values=min_values,
+                max_values=max_values,
+                is_disabled=is_disabled,
+            )
+        )
 
-    @typing.overload
-    def add_select_menu(
+    def add_channel_menu(
         self,
-        type_: typing.Literal[component_models.ComponentType.CHANNEL_SELECT_MENU, 8],
         custom_id: str,
         /,
-    ) -> special_endpoints.ChannelSelectMenuBuilder[Self]:
-        ...
+        *,
+        channel_types: typing.Sequence[channels.ChannelType] = (),
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> Self:
+        return self.add_component(
+            ChannelSelectMenuBuilder(
+                custom_id=custom_id,
+                placeholder=placeholder,
+                channel_types=channel_types,
+                min_values=min_values,
+                max_values=max_values,
+                is_disabled=is_disabled,
+            )
+        )
 
-    def add_select_menu(
+    def add_text_menu(
         self,
-        type_: typing.Union[component_models.ComponentType, int],
         custom_id: str,
         /,
-    ) -> special_endpoints.SelectMenuBuilder[Self]:
-        if type_ not in component_models.SelectMenuTypes:
-            raise ValueError(f"{type_!r} is an invalid type option")
-
-        self._assert_can_add_type(type_)
-
-        if type_ == component_models.ComponentType.TEXT_SELECT_MENU:
-            return TextSelectMenuBuilder(container=self, custom_id=custom_id)
-        if type_ == component_models.ComponentType.CHANNEL_SELECT_MENU:
-            return ChannelSelectMenuBuilder(container=self, custom_id=custom_id)
-
-        return SelectMenuBuilder(container=self, type=type_, custom_id=custom_id)
+        *,
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        min_values: int = 0,
+        max_values: int = 1,
+        is_disabled: bool = False,
+    ) -> special_endpoints.TextSelectMenuBuilder[Self]:
+        component = TextSelectMenuBuilder(
+            custom_id=custom_id,
+            parent=self,
+            placeholder=placeholder,
+            min_values=min_values,
+            max_values=max_values,
+            is_disabled=is_disabled,
+        )
+        self.add_component(component)
+        return component
 
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         return {
@@ -1960,9 +2039,7 @@ class ModalActionRowBuilder(special_endpoints.ModalActionRowBuilder):
     """Standard implementation of `hikari.api.special_endpoints.ActionRowBuilder`."""
 
     _components: typing.List[special_endpoints.ComponentBuilder] = attr.field(alias="components", factory=list)
-    _stored_type: typing.Optional[component_models.ComponentType] = attr.field(
-        alias="stored_type", init=False, default=None
-    )
+    _stored_type: typing.Optional[int] = attr.field(init=False, default=None)
 
     @property
     def type(self) -> typing.Literal[component_models.ComponentType.ACTION_ROW]:
@@ -1972,7 +2049,7 @@ def type(self) -> typing.Literal[component_models.ComponentType.ACTION_ROW]:
     def components(self) -> typing.Sequence[special_endpoints.ComponentBuilder]:
         return self._components.copy()
 
-    def _assert_can_add_type(self, type_: component_models.ComponentType, /) -> None:
+    def _assert_can_add_type(self, type_: typing.Union[component_models.ComponentType, int], /) -> None:
         if self._stored_type is not None and self._stored_type != type_:
             raise ValueError(
                 f"{type_} component type cannot be added to a container which already holds {self._stored_type}"
@@ -1981,6 +2058,7 @@ def _assert_can_add_type(self, type_: component_models.ComponentType, /) -> None
         self._stored_type = type_
 
     def add_component(self, component: special_endpoints.ComponentBuilder, /) -> Self:
+        self._assert_can_add_type(component.type)
         self._components.append(component)
         return self
 
@@ -1988,9 +2066,27 @@ def add_text_input(
         self,
         custom_id: str,
         label: str,
-    ) -> special_endpoints.TextInputBuilder[Self]:
-        self._assert_can_add_type(component_models.ComponentType.TEXT_INPUT)
-        return TextInputBuilder(container=self, custom_id=custom_id, label=label)
+        /,
+        *,
+        style: component_models.TextInputStyle = component_models.TextInputStyle.SHORT,
+        placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        value: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        required: bool = True,
+        min_length: int = 0,
+        max_length: int = 4000,
+    ) -> Self:
+        return self.add_component(
+            TextInputBuilder(
+                custom_id=custom_id,
+                label=label,
+                style=style,
+                placeholder=placeholder,
+                value=value,
+                required=required,
+                min_length=min_length,
+                max_length=max_length,
+            )
+        )
 
     def build(self) -> typing.MutableMapping[str, typing.Any]:
         return {


Test patch:
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index 1a062e5d..c4673bd7 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -1610,9 +1610,7 @@ def test_interaction_message_builder(self, rest_client):
 
     def test_interaction_modal_builder(self, rest_client):
         result = rest_client.interaction_modal_builder("title", "custom")
-        result.add_component(
-            special_endpoints.ModalActionRowBuilder().add_text_input("idd", "labell").add_to_container()
-        )
+        result.add_component(special_endpoints.ModalActionRowBuilder().add_text_input("idd", "labell"))
 
         assert result.type == 9
         assert isinstance(result, special_endpoints.InteractionModalBuilder)
@@ -2216,7 +2214,7 @@ async def test_edit_channel(
             "invitable": True,
             "auto_archive_duration": 12322,
             "flags": 12,
-            "applied_tags": [{"id": 0, "name": "testing", "moderated": True, "emoji_id": None, "emoji_name": None}],
+            "applied_tags": ["0"],
         }
 
         result = await rest_client.edit_channel(
@@ -2251,7 +2249,7 @@ async def test_edit_channel(
             invitable=True,
             auto_archive_duration=auto_archive_duration,
             flags=12,
-            applied_tags=[channels.ForumTag(name="testing", moderated=True)],
+            applied_tags=[StubModel(0)],
         )
 
         assert result == mock_object
diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py
index 68abdae8..e59ca59e 100644
--- a/tests/hikari/impl/test_special_endpoints.py
+++ b/tests/hikari/impl/test_special_endpoints.py
@@ -35,6 +35,7 @@
 from hikari import permissions
 from hikari import snowflakes
 from hikari import undefined
+from hikari.api import special_endpoints as special_endpoints_api
 from hikari.impl import special_endpoints
 from hikari.interactions import base_interactions
 from hikari.internal import routes
@@ -1203,13 +1204,10 @@ class Test_ButtonBuilder:
     @pytest.fixture()
     def button(self):
         return special_endpoints._ButtonBuilder(
-            container=mock.Mock(),
             style=components.ButtonStyle.DANGER,
             custom_id="sfdasdasd",
             url="hi there",
             emoji=543123,
-            emoji_id="56554456",
-            emoji_name="hi there",
             label="a lebel",
             is_disabled=True,
         )
@@ -1258,74 +1256,55 @@ def test_set_is_disabled(self, button):
         assert button.is_disabled is False
 
     def test_build(self):
-        result = special_endpoints._ButtonBuilder(
-            container=object(),
+        button = special_endpoints._ButtonBuilder(
             style=components.ButtonStyle.DANGER,
-            url=undefined.UNDEFINED,
-            emoji_id=undefined.UNDEFINED,
-            emoji_name="emoji_name",
+            url="https://example.com",
             label="no u",
             custom_id="ooga booga",
+            emoji="emoji_name",
             is_disabled=True,
-        ).build()
+        )
 
-        assert result == {
+        assert button.build() == {
             "type": components.ComponentType.BUTTON,
             "style": components.ButtonStyle.DANGER,
+            "url": "https://example.com",
             "emoji": {"name": "emoji_name"},
             "label": "no u",
             "custom_id": "ooga booga",
             "disabled": True,
         }
 
-    def test_build_without_optional_fields(self):
-        result = special_endpoints._ButtonBuilder(
-            container=object(),
-            style=components.ButtonStyle.LINK,
-            url="OK",
-            emoji_id="123321",
-            emoji_name=undefined.UNDEFINED,
-            label=undefined.UNDEFINED,
-            custom_id=undefined.UNDEFINED,
-            is_disabled=False,
-        ).build()
+    @pytest.mark.parametrize("emoji", [123321, emojis.CustomEmoji(id=123321, name="", is_animated=True)])
+    def test_build_with_custom_emoji(self, emoji: typing.Union[int, emojis.Emoji]):
+        button = special_endpoints._ButtonBuilder(
+            style=components.ButtonStyle.DANGER, emoji=emoji, url=undefined.UNDEFINED, custom_id=undefined.UNDEFINED
+        )
 
-        assert result == {
+        assert button.build() == {
             "type": components.ComponentType.BUTTON,
-            "style": components.ButtonStyle.LINK,
+            "style": components.ButtonStyle.DANGER,
             "emoji": {"id": "123321"},
             "disabled": False,
-            "url": "OK",
         }
 
-    def test_add_to_container(self):
-        mock_container = mock.Mock()
+    def test_build_without_optional_fields(self):
         button = special_endpoints._ButtonBuilder(
-            container=mock_container,
-            style=components.ButtonStyle.DANGER,
-            url=undefined.UNDEFINED,
-            emoji_id=undefined.UNDEFINED,
-            emoji_name="emoji_name",
-            label="no u",
-            custom_id="ooga booga",
-            is_disabled=True,
+            style=components.ButtonStyle.LINK, url=undefined.UNDEFINED, custom_id=undefined.UNDEFINED
         )
 
-        assert button.add_to_container() is mock_container
-
-        mock_container.add_component.assert_called_once_with(button)
+        assert button.build() == {
+            "type": components.ComponentType.BUTTON,
+            "style": components.ButtonStyle.LINK,
+            "disabled": False,
+        }
 
 
 class TestLinkButtonBuilder:
     def test_url_property(self):
         button = special_endpoints.LinkButtonBuilder(
-            container=object(),
-            style=components.ButtonStyle.DANGER,
             url="hihihihi",
-            emoji_id=undefined.UNDEFINED,
-            emoji_name="emoji_name",
             label="no u",
-            custom_id="ooga booga",
             is_disabled=True,
         )
 
@@ -1335,11 +1314,7 @@ def test_url_property(self):
 class TestInteractiveButtonBuilder:
     def test_custom_id_property(self):
         button = special_endpoints.InteractiveButtonBuilder(
-            container=object(),
             style=components.ButtonStyle.DANGER,
-            url="hihihihi",
-            emoji_id=undefined.UNDEFINED,
-            emoji_name="emoji_name",
             label="no u",
             custom_id="ooga booga",
             is_disabled=True,
@@ -1348,10 +1323,10 @@ def test_custom_id_property(self):
         assert button.custom_id == "ooga booga"
 
 
-class Test_SelectOptionBuilder:
+class TestSelectOptionBuilder:
     @pytest.fixture()
     def option(self):
-        return special_endpoints._SelectOptionBuilder(menu=mock.Mock(), label="ok", value="ok2")
+        return special_endpoints.SelectOptionBuilder(label="ok", value="ok2")
 
     def test_label_property(self, option):
         assert option.label == "ok"
@@ -1397,20 +1372,12 @@ def test_set_is_default(self, option):
         assert option.set_is_default(True) is option
         assert option.is_default is True
 
-    def test_add_to_menu(self, option):
-        assert option.add_to_menu() is option._menu
-        option._menu.add_raw_option.assert_called_once_with(option)
-
-    def test_build_with_custom_emoji(self, option):
-        result = (
-            special_endpoints._SelectOptionBuilder(label="ok", value="ok2", menu=object())
-            .set_is_default(True)
-            .set_emoji(123312)
-            .set_description("very")
-            .build()
+    def test_build_with_custom_emoji(self):
+        option = special_endpoints.SelectOptionBuilder(
+            label="ok", value="ok2", is_default=True, emoji=123312, description="very"
         )
 
-        assert result == {
+        assert option.build() == {
             "label": "ok",
             "value": "ok2",
             "default": True,
@@ -1418,16 +1385,12 @@ def test_build_with_custom_emoji(self, option):
             "description": "very",
         }
 
-    def test_build_with_unicode_emoji(self, option):
-        result = (
-            special_endpoints._SelectOptionBuilder(label="ok", value="ok2", menu=object())
-            .set_is_default(True)
-            .set_emoji("hi")
-            .set_description("very")
-            .build()
+    def test_build_with_unicode_emoji(self):
+        option = special_endpoints.SelectOptionBuilder(
+            label="ok", value="ok2", is_default=True, emoji="hi", description="very"
         )
 
-        assert result == {
+        assert option.build() == {
             "label": "ok",
             "value": "ok2",
             "default": True,
@@ -1435,19 +1398,19 @@ def test_build_with_unicode_emoji(self, option):
             "description": "very",
         }
 
-    def test_build_partial(self, option):
-        result = special_endpoints._SelectOptionBuilder(label="ok", value="ok2", menu=object()).build()
+    def test_build_partial(self):
+        option = special_endpoints.SelectOptionBuilder(label="ok", value="ok2")
 
-        assert result == {"label": "ok", "value": "ok2", "default": False}
+        assert option.build() == {"label": "ok", "value": "ok2", "default": False}
 
 
 class TestSelectMenuBuilder:
     @pytest.fixture()
     def menu(self):
-        return special_endpoints.SelectMenuBuilder(container=mock.Mock(), custom_id="o2o2o2", type=5)
+        return special_endpoints.SelectMenuBuilder(custom_id="o2o2o2", type=5)
 
     def test_type_property(self):
-        menu = special_endpoints.SelectMenuBuilder(container=mock.Mock(), type=123, custom_id="hihihi")
+        menu = special_endpoints.SelectMenuBuilder(type=123, custom_id="hihihi")
 
         assert menu.type == 123
 
@@ -1470,15 +1433,30 @@ def test_set_max_values(self, menu):
         assert menu.set_max_values(25) is menu
         assert menu.max_values == 25
 
-    def test_add_to_container(self, menu):
-        assert menu.add_to_container() is menu._container
-        menu._container.add_component.assert_called_once_with(menu)
-
     def test_build(self):
-        result = special_endpoints.SelectMenuBuilder(container=object(), custom_id="o2o2o2", type=5).build()
+        menu = special_endpoints.SelectMenuBuilder(
+            custom_id="45234fsdf",
+            type=components.ComponentType.USER_SELECT_MENU,
+            placeholder="meep",
+            min_values=5,
+            max_values=23,
+            is_disabled=True,
+        )
 
-        assert result == {
-            "type": 5,
+        assert menu.build() == {
+            "type": components.ComponentType.USER_SELECT_MENU,
+            "custom_id": "45234fsdf",
+            "placeholder": "meep",
+            "disabled": True,
+            "min_values": 5,
+            "max_values": 23,
+        }
+
+    def test_build_without_optional_fields(self):
+        menu = special_endpoints.SelectMenuBuilder(custom_id="o2o2o2", type=components.ComponentType.ROLE_SELECT_MENU)
+
+        assert menu.build() == {
+            "type": components.ComponentType.ROLE_SELECT_MENU,
             "custom_id": "o2o2o2",
             "disabled": False,
             "min_values": 0,
@@ -1489,67 +1467,98 @@ def test_build(self):
 class TestTextSelectMenuBuilder:
     @pytest.fixture()
     def menu(self):
-        return special_endpoints.TextSelectMenuBuilder(container=mock.Mock(), custom_id="o2o2o2")
+        return special_endpoints.TextSelectMenuBuilder(custom_id="o2o2o2")
+
+    def test_parent_property(self):
+        mock_parent = object()
+        menu = special_endpoints.TextSelectMenuBuilder(custom_id="o2o2o2", parent=mock_parent)
+
+        assert menu.parent is mock_parent
+
+    def test_parent_property_when_none(self):
+        menu = special_endpoints.TextSelectMenuBuilder(custom_id="o2o2o2")
 
-    def test_type_property(self, menu: special_endpoints.TextSelectMenuBuilder[typing.Any]):
+        with pytest.raises(RuntimeError, match="This menu has no parent"):
+            menu.parent
+
+    def test_type_property(self, menu: special_endpoints.TextSelectMenuBuilder[typing.NoReturn]):
         assert menu.type is components.ComponentType.TEXT_SELECT_MENU
 
-    def test_add_add_option(self, menu):
-        option = menu.add_option("ok", "no u")
-        option.add_to_menu()
-        assert menu.options == [option]
+    def test_add_option(self, menu: special_endpoints.TextSelectMenuBuilder[typing.NoReturn]):
+        menu.add_option("ok", "no u", description="meow", emoji="e", is_default=True)
+
+        assert len(menu.options) == 1
+        option = menu.options[0]
+        assert option.label == "ok"
+        assert option.value == "no u"
+        assert option.description == "meow"
+        assert option.emoji == "e"
+        assert option.is_default is True
 
     def test_add_raw_option(self, menu):
         mock_option = object()
+
         menu.add_raw_option(mock_option)
+
         assert menu.options == [mock_option]
 
     def test_build(self):
-        result = (
-            special_endpoints.TextSelectMenuBuilder(container=object(), custom_id="o2o2o2")
-            .set_placeholder("hi")
-            .set_min_values(22)
-            .set_max_values(53)
-            .set_is_disabled(True)
-            .build()
+        menu = special_endpoints.TextSelectMenuBuilder(
+            custom_id="o2o2o2",
+            placeholder="hi",
+            min_values=22,
+            max_values=53,
+            is_disabled=True,
+            options=[special_endpoints.SelectOptionBuilder("meow", "vault")],
         )
 
-        assert result == {
-            "type": 3,
+        assert menu.build() == {
+            "type": components.ComponentType.TEXT_SELECT_MENU,
             "custom_id": "o2o2o2",
             "placeholder": "hi",
             "min_values": 22,
             "max_values": 53,
             "disabled": True,
+            "options": [{"label": "meow", "value": "vault", "default": False}],
+        }
+
+    def test_build_without_optional_fields(self):
+        menu = special_endpoints.TextSelectMenuBuilder(custom_id="fds  qw")
+
+        assert menu.build() == {
+            "type": components.ComponentType.TEXT_SELECT_MENU,
+            "custom_id": "fds  qw",
+            "min_values": 0,
+            "max_values": 1,
+            "disabled": False,
             "options": [],
         }
 
 
 class TestChannelSelectMenuBuilder:
     def test_type_property(self):
-        menu = special_endpoints.ChannelSelectMenuBuilder(container=mock.Mock(), custom_id="id")
+        menu = special_endpoints.ChannelSelectMenuBuilder(custom_id="id")
         assert menu.type is components.ComponentType.CHANNEL_SELECT_MENU
 
     def test_set_channel_types(self):
-        menu = special_endpoints.ChannelSelectMenuBuilder(container=mock.Mock(), custom_id="hi")
+        menu = special_endpoints.ChannelSelectMenuBuilder(custom_id="hi")
 
         menu.set_channel_types([channels.ChannelType.DM, channels.ChannelType.GUILD_FORUM])
 
         assert menu.channel_types == [channels.ChannelType.DM, channels.ChannelType.GUILD_FORUM]
 
     def test_build(self):
-        result = (
-            special_endpoints.ChannelSelectMenuBuilder(container=object(), custom_id="o2o2o2")
-            .set_placeholder("hi")
-            .set_min_values(22)
-            .set_max_values(53)
-            .set_is_disabled(True)
-            .set_channel_types([channels.ChannelType.GUILD_CATEGORY])
-            .build()
+        menu = special_endpoints.ChannelSelectMenuBuilder(
+            custom_id="o2o2o2",
+            placeholder="hi",
+            min_values=22,
+            max_values=53,
+            is_disabled=True,
+            channel_types=[channels.ChannelType.GUILD_CATEGORY],
         )
 
-        assert result == {
-            "type": 8,
+        assert menu.build() == {
+            "type": components.ComponentType.CHANNEL_SELECT_MENU,
             "custom_id": "o2o2o2",
             "placeholder": "hi",
             "min_values": 22,
@@ -1558,12 +1567,23 @@ def test_build(self):
             "channel_types": [channels.ChannelType.GUILD_CATEGORY],
         }
 
+    def test_build_without_optional_fields(self):
+        menu = special_endpoints.ChannelSelectMenuBuilder(custom_id="42312312")
+
+        assert menu.build() == {
+            "type": components.ComponentType.CHANNEL_SELECT_MENU,
+            "custom_id": "42312312",
+            "min_values": 0,
+            "max_values": 1,
+            "disabled": False,
+            "channel_types": [],
+        }
+
 
 class TestTextInput:
     @pytest.fixture()
     def text_input(self):
         return special_endpoints.TextInputBuilder(
-            container=mock.Mock(),
             custom_id="o2o2o2",
             label="label",
         )
@@ -1603,40 +1623,31 @@ def test_set_max_length(self, text_input):
         assert text_input.set_max_length(250) is text_input
         assert text_input.max_length == 250
 
-    def test_add_to_container(self, text_input):
-        assert text_input.add_to_container() is text_input._container
-        text_input._container.add_component.assert_called_once_with(text_input)
-
-    def test_build(self):
-        result = special_endpoints.TextInputBuilder(
-            container=object(),
-            custom_id="o2o2o2",
-            label="label",
-        ).build()
+    def test_build_partial(self):
+        text_input = special_endpoints.TextInputBuilder(custom_id="o2o2o2", label="label")
 
-        assert result == {
+        assert text_input.build() == {
             "type": components.ComponentType.TEXT_INPUT,
             "style": 1,
             "custom_id": "o2o2o2",
             "label": "label",
+            "required": True,
+            "min_length": 0,
+            "max_length": 4000,
         }
 
-    def test_build_partial(self):
-        result = (
-            special_endpoints.TextInputBuilder(
-                container=object(),
-                custom_id="o2o2o2",
-                label="label",
-            )
-            .set_placeholder("placeholder")
-            .set_value("value")
-            .set_required(False)
-            .set_min_length(10)
-            .set_max_length(250)
-            .build()
+    def test_build(self):
+        text_input = special_endpoints.TextInputBuilder(
+            custom_id="o2o2o2",
+            label="label",
+            placeholder="placeholder",
+            value="value",
+            required=False,
+            min_length=10,
+            max_length=250,
         )
 
-        assert result == {
+        assert text_input.build() == {
             "type": components.ComponentType.TEXT_INPUT,
             "style": 1,
             "custom_id": "o2o2o2",
@@ -1656,44 +1667,99 @@ def test_type_property(self):
         assert row.type is components.ComponentType.ACTION_ROW
 
     def test_components_property(self):
-        mock_component = object()
+        mock_component = mock.Mock()
         row = special_endpoints.MessageActionRowBuilder().add_component(mock_component)
         assert row.components == [mock_component]
 
-    def test_add_button_for_interactive(self):
+    def test_add_interactive_button(self):
         row = special_endpoints.MessageActionRowBuilder()
-        button = row.add_button(components.ButtonStyle.DANGER, "go home")
 
-        button.add_to_container()
+        row.add_interactive_button(
+            components.ButtonStyle.DANGER, "go home", emoji="emoji", label="libal", is_disabled=True
+        )
 
-        assert row.components == [button]
+        assert len(row.components) == 1
+        button = row.components[0]
+        assert isinstance(button, special_endpoints_api.InteractiveButtonBuilder)
+        assert button.type is components.ComponentType.BUTTON
+        assert button.style is components.ButtonStyle.DANGER
+        assert button.emoji == "emoji"
+        assert button.custom_id == "go home"
+        assert button.label == "libal"
+        assert button.is_disabled is True
 
-    def test_add_button_for_link(self):
+    def test_add_link_button(self):
         row = special_endpoints.MessageActionRowBuilder()
-        button = row.add_button(components.ButtonStyle.LINK, "go home")
 
-        button.add_to_container()
+        row.add_link_button("https://example.com", emoji="e", label="American made", is_disabled=True)
 
-        assert row.components == [button]
+        assert len(row.components) == 1
+        button = row.components[0]
+        assert isinstance(button, special_endpoints.LinkButtonBuilder)
+        assert button.type is components.ComponentType.BUTTON
+        assert button.style is components.ButtonStyle.LINK
+        assert button.emoji == "e"
+        assert button.url == "https://example.com"
+        assert button.label == "American made"
+        assert button.is_disabled is True
 
     def test_add_select_menu(self):
         row = special_endpoints.MessageActionRowBuilder()
-        menu = row.add_select_menu(components.ComponentType.TEXT_SELECT_MENU, "hihihi")
 
-        menu.add_to_container()
+        row.add_select_menu(
+            components.ComponentType.ROLE_SELECT_MENU,
+            "two trucks",
+            placeholder="holding hands",
+            min_values=6,
+            max_values=9,
+            is_disabled=True,
+        )
 
-        assert row.components == [menu]
+        assert len(row.components) == 1
+        component = row.components[0]
+        assert isinstance(component, special_endpoints_api.SelectMenuBuilder)
 
-    def test_build(self):
-        mock_component_1 = mock.Mock()
-        mock_component_2 = mock.Mock()
+    def test_add_channel_menu(self):
+        row = special_endpoints.MessageActionRowBuilder()
+
+        row.add_channel_menu(
+            "flex",
+            channel_types=[channels.ChannelType.GROUP_DM],
+            placeholder="The pasion",
+            min_values=4,
+            max_values=9,
+            is_disabled=True,
+        )
+
+        assert len(row.components) == 1
+        component = row.components[0]
+        assert isinstance(component, special_endpoints_api.SelectMenuBuilder)
 
+    def test_add_text_menu(self):
         row = special_endpoints.MessageActionRowBuilder()
-        row._components = [mock_component_1, mock_component_2]
 
-        result = row.build()
+        row.add_text_menu(
+            "Two pickup trucks", placeholder="American made", min_values=5, max_values=6, is_disabled=True
+        )
 
-        assert result == {
+        assert len(row.components) == 1
+        component = row.components[0]
+        assert isinstance(component, special_endpoints_api.SelectMenuBuilder)
+        assert component.custom_id == "Two pickup trucks"
+        assert component.placeholder == "American made"
+        assert component.min_values == 5
+        assert component.max_values == 6
+        assert component.is_disabled is True
+
+    def test_build(self):
+        mock_component_1 = mock.Mock(type=components.ComponentType.BUTTON)
+        mock_component_2 = mock.Mock(type=components.ComponentType.BUTTON)
+
+        row = (
+            special_endpoints.MessageActionRowBuilder().add_component(mock_component_1).add_component(mock_component_2)
+        )
+
+        assert row.build() == {
             "type": components.ComponentType.ACTION_ROW,
             "components": [mock_component_1.build.return_value, mock_component_2.build.return_value],
         }
@@ -1709,8 +1775,40 @@ def test_type_property(self):
 
     def test_add_text_input(self):
         row = special_endpoints.ModalActionRowBuilder()
-        menu = row.add_text_input("hihihi", "label")
+        menu = row.add_text_input(
+            "hihihi",
+            "lalbell",
+            style=components.TextInputStyle.PARAGRAPH,
+            placeholder="meep",
+            value="beep",
+            required=False,
+            min_length=444,
+            max_length=447,
+        )
+
+        assert len(row.components) == 1
+        menu = row.components[0]
+        assert isinstance(menu, special_endpoints_api.TextInputBuilder)
+        assert menu.custom_id == "hihihi"
+        assert menu.label == "lalbell"
+        assert menu.style is components.TextInputStyle.PARAGRAPH
+        assert menu.placeholder == "meep"
+        assert menu.value == "beep"
+        assert menu.is_required is False
+        assert menu.min_length == 444
+        assert menu.max_length == 447
 
-        menu.add_to_container()
+    def test_build(self):
+        mock_component_1 = mock.Mock(type=components.ComponentType.TEXT_INPUT)
+        mock_component_2 = mock.Mock(type=components.ComponentType.TEXT_INPUT)
+
+        row = special_endpoints.ModalActionRowBuilder().add_component(mock_component_1).add_component(mock_component_2)
+
+        result = row.build()
 
-        assert row.components == [menu]
+        assert result == {
+            "type": components.ComponentType.ACTION_ROW,
+            "components": [mock_component_1.build.return_value, mock_component_2.build.return_value],
+        }
+        mock_component_1.build.assert_called_once_with()
+        mock_component_2.build.assert_called_once_with()
diff --git a/tests/hikari/test_guilds.py b/tests/hikari/test_guilds.py
index b64e5ad6..37567d50 100644
--- a/tests/hikari/test_guilds.py
+++ b/tests/hikari/test_guilds.py
@@ -157,6 +157,13 @@ def test_icon_url_property(self, model):
 
             model.make_icon_url.assert_called_once_with()
 
+    def test_mention_property(self, model):
+        assert model.mention == "<@&979899100>"
+
+    def test_mention_property_when_is_everyone_role(self, model):
+        model.id = model.guild_id
+        assert model.mention == "@everyone"
+
     def test_make_icon_url_when_hash_is_None(self, model):
         model.icon_hash = None