Repository: hikari-py/hikari
Pull request #806 (2021-09-30 14:55:21)

Add old guild attribute to guild leave event
### Summary
Add old guild attribute to guild leave event

### 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
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0db5e025..2f27d3b5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,8 +3,6 @@ name: CI
 on:
   push:
   pull_request:
-  release:
-    types: [published]
 
 jobs:
   test:
@@ -179,49 +177,3 @@ jobs:
           else
             exit 1
           fi
-
-  deploy:
-    if: github.event_name == 'release'
-    needs: [ci-done]
-
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Checkout repo
-        uses: actions/checkout@v2.3.4
-        with:
-          token: ${{ secrets.PAT_TOKEN }}
-
-      - name: Setup python
-        uses: actions/setup-python@v2.2.2
-        with:
-          python-version: 3.8
-
-      - name: Setup git config
-        run: |
-          git config --global user.name "hikari-bot"
-          git config --global user.email "90276125+hikari-bot[bot]@users.noreply.github.com"
-
-      - name: Temporarily disable "include administrators" branch protection
-        uses: benjefferies/branch-protection-bot@master
-        with:
-          access_token: ${{ secrets.PAT_TOKEN }}
-          enforce_admins: false
-
-      - name: Deploy
-        env:
-          GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
-          VERSION: ${{ github.event.release.tag_name }}
-          REF: ${{ github.sha }}
-          GITHUB_REPO_SLUG: ${{ github.repository }}
-          DEPLOY_WEBHOOK_URL: ${{ secrets.DEPLOY_WEBHOOK_URL }}
-          TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
-          TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
-        run: bash scripts/deploy.sh
-
-      - name: Re-enable "include administrators" branch protection
-        uses: benjefferies/branch-protection-bot@master
-        if: always()
-        with:
-          access_token: ${{ secrets.PAT_TOKEN }}
-          enforce_admins: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..27438dfb
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,49 @@
+name: Release
+
+on:
+  release:
+    types: [published]
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@v2.3.4
+        with:
+          token: ${{ secrets.PAT_TOKEN }}
+
+      - name: Setup python
+        uses: actions/setup-python@v2.2.2
+        with:
+          python-version: 3.8
+
+      - name: Setup git config
+        run: |
+          git config --global user.name "hikari-bot"
+          git config --global user.email "90276125+hikari-bot[bot]@users.noreply.github.com"
+
+      - name: Temporarily disable "include administrators" branch protection
+        uses: benjefferies/branch-protection-bot@master
+        with:
+          access_token: ${{ secrets.PAT_TOKEN }}
+          enforce_admins: false
+
+      - name: Deploy
+        env:
+          GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
+          VERSION: ${{ github.event.release.tag_name }}
+          REF: ${{ github.sha }}
+          GITHUB_REPO_SLUG: ${{ github.repository }}
+          DEPLOY_WEBHOOK_URL: ${{ secrets.DEPLOY_WEBHOOK_URL }}
+          TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
+          TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
+        run: bash scripts/deploy.sh
+
+      - name: Re-enable "include administrators" branch protection
+        uses: benjefferies/branch-protection-bot@master
+        if: always()
+        with:
+          access_token: ${{ secrets.PAT_TOKEN }}
+          enforce_admins: true
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 088fea8a..52feadd1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,6 +28,12 @@ match that of the versioning scheme. There are utilities under `hikari.internal.
 
 To aid with the generation of `CHANGELOG.md` as well as the releases changelog we use `towncrier`.
 
+You will need to install `towncrier` and `hikari` from source before making changelog additions.
+```bash
+pip install towncrier
+pip install -e .
+```
+
 For every pull request made to this project, there should be a short explanation of the change under `changes/`
 with the following format: `{pull_request_number}.{type}.md`,
 
@@ -44,7 +50,7 @@ description so that a maintainer can skip the job that checks for newly added fr
 Best way to create the fragments is to run `towncrier create {pull_request_number}.{type}.md` after creating the
 pull request, edit the created file and committing the changes.
 
-Multiple fragments types can be created per pull request if it covers multiple areas.
+Multiple fragment types can be created per pull request if it covers multiple areas.
 
 # Branches
 
@@ -65,6 +71,11 @@ with a small description of the branch.
 
 We have nox to help out with running pipelines locally and provides some helpful functionality.
 
+You will need to install `nox` locally before running any pipelines.
+```bash
+pip install nox
+```
+
 Nox is similar to tox, but uses a pure Python configuration instead of an INI based configuration. Nox and tox are
 both tools for generating virtual environments and running commands in those environments. Examples of usage include
 installing, configuring, and running flake8, running pytest, etc.
diff --git a/changes/804.doc.md b/changes/804.doc.md
new file mode 100644
index 00000000..7e1e6309
--- /dev/null
+++ b/changes/804.doc.md
@@ -0,0 +1,1 @@
+Add docstrings to the remaining undocumented `GatewayBot` methods
diff --git a/changes/804.feature.md b/changes/804.feature.md
new file mode 100644
index 00000000..3c7eedd5
--- /dev/null
+++ b/changes/804.feature.md
@@ -0,0 +1,1 @@
+Add the `add_component` method to `hikari.api.special_endpoints.ActionRowBuilder`
diff --git a/changes/806.feature.md b/changes/806.feature.md
new file mode 100644
index 00000000..4f2405cb
--- /dev/null
+++ b/changes/806.feature.md
@@ -0,0 +1,2 @@
+Add `old_guild` attribute to `GuildLeaveEvent`.
+
diff --git a/hikari/api/event_factory.py b/hikari/api/event_factory.py
index e01efd91..49e0807a 100644
--- a/hikari/api/event_factory.py
+++ b/hikari/api/event_factory.py
@@ -260,7 +260,7 @@ def deserialize_guild_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_guild: typing.Optional[guild_models.Guild],
+        old_guild: typing.Optional[guild_models.GatewayGuild],
     ) -> guild_events.GuildUpdateEvent:
         """Parse a raw payload from Discord into a guild update event object.
 
@@ -270,7 +270,7 @@ def deserialize_guild_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
-        old_guild : typing.Optional[hikari.guilds.Guild]
+        old_guild : typing.Optional[hikari.guilds.GatewayGuild]
             The guild object or `builtins.None`.
 
         Returns
@@ -281,7 +281,11 @@ def deserialize_guild_update_event(
 
     @abc.abstractmethod
     def deserialize_guild_leave_event(
-        self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
+        self,
+        shard: gateway_shard.GatewayShard,
+        payload: data_binding.JSONObject,
+        *,
+        old_guild: typing.Optional[guild_models.GatewayGuild],
     ) -> guild_events.GuildLeaveEvent:
         """Parse a raw payload from Discord into a guild leave event object.
 
@@ -291,6 +295,8 @@ def deserialize_guild_leave_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+        old_guild : typing.Optional[hikari.guilds.GatewayGuild]
+            The guild object or `builtins.None`.
 
         Returns
         -------
diff --git a/hikari/api/event_manager.py b/hikari/api/event_manager.py
index f0dc6810..90ec286c 100644
--- a/hikari/api/event_manager.py
+++ b/hikari/api/event_manager.py
@@ -231,9 +231,11 @@ async def on_everyone_mentioned(event):
 
         See Also
         --------
-        Subscribe: `hikari.api.event_manager.EventManager.subscribe`
+        Listen: `hikari.api.event_manager.EventManager.listen`
         Stream: `hikari.api.event_manager.EventManager.stream`
-        Wait for: `hikari.api.event_manager.EventManager.wait_for`
+        Subscribe: `hikari.api.event_manager.EventManager.subscribe`
+        Unsubscribe: `hikari.api.event_manager.EventManager.unsubscribe`
+        Wait_for: `hikari.api.event_manager.EventManager.wait_for`
         """
 
     # Yes, this is not generic. The reason for this is MyPy complains about
@@ -271,9 +273,11 @@ async def on_message(event):
 
         See Also
         --------
+        Dispatch: `hikari.api.event_manager.EventManager.dispatch`
         Listen: `hikari.api.event_manager.EventManager.listen`
         Stream: `hikari.api.event_manager.EventManager.stream`
-        Wait for: `hikari.api.event_manager.EventManager.wait_for`
+        Unsubscribe: `hikari.api.event_manager.EventManager.unsubscribe`
+        Wait_for: `hikari.api.event_manager.EventManager.wait_for`
         """
 
     # Yes, this is not generic. The reason for this is MyPy complains about
@@ -306,6 +310,14 @@ async def on_message(event):
 
         bot.unsubscribe(MessageCreateEvent, on_message)
         ```
+
+        See Also
+        --------
+        Dispatch: `hikari.api.event_manager.EventManager.dispatch`
+        Listen: `hikari.api.event_manager.EventManager.listen`
+        Stream: `hikari.api.event_manager.EventManager.stream`
+        Subscribe: `hikari.api.event_manager.EventManager.subscribe`
+        Wait_for: `hikari.api.event_manager.EventManager.wait_for`
         """
 
     @abc.abstractmethod
@@ -373,7 +385,7 @@ def listen(
         Stream: `hikari.api.event_manager.EventManager.stream`
         Subscribe: `hikari.api.event_manager.EventManager.subscribe`
         Unsubscribe: `hikari.api.event_manager.EventManager.unsubscribe`
-        Wait for: `hikari.api.event_manager.EventManager.wait_for`
+        Wait_for: `hikari.api.event_manager.EventManager.wait_for`
         """
 
     @abc.abstractmethod
@@ -439,7 +451,7 @@ def stream(
         Listen: `hikari.api.event_manager.EventManager.listen`
         Subscribe: `hikari.api.event_manager.EventManager.subscribe`
         Unsubscribe: `hikari.api.event_manager.EventManager.unsubscribe`
-        Wait for: `hikari.api.event_manager.EventManager.wait_for`
+        Wait_for: `hikari.api.event_manager.EventManager.wait_for`
         """
 
     @abc.abstractmethod
@@ -486,8 +498,9 @@ async def wait_for(
 
         See Also
         --------
+        Dispatch: `hikari.api.event_manager.EventManager.dispatch`
         Listen: `hikari.api.event_manager.EventManager.listen`
         Stream: `hikari.api.event_manager.EventManager.stream`
         Subscribe: `hikari.api.event_manager.EventManager.subscribe`
-        Dispatch: `hikari.api.event_manager.EventManager.dispatch`
+        Unsubscribe: `hikari.api.event_manager.EventManager.unsubscribe`
         """
diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py
index 2f8a9003..6d73ca5f 100644
--- a/hikari/api/special_endpoints.py
+++ b/hikari/api/special_endpoints.py
@@ -1491,6 +1491,30 @@ def components(self) -> typing.Sequence[ComponentBuilder]:
             Sequence of the component builders registered within this action row.
         """
 
+    @abc.abstractmethod
+    def add_component(
+        self: _T,
+        component: ComponentBuilder,
+        /,
+    ) -> _T:
+        """Add a component to this action row builder.
+
+        !!! warning
+            It is generally better to use `ActionRowBuilder.add_button`
+            and `ActionRowBuilder.add_select_menu` to add your
+            component to the builder. Those methods utilize this one.
+
+        Parameters
+        ----------
+        component : ComponentBuilder
+            The component builder to add to the action row.
+
+        Returns
+        -------
+        ActionRowBuilder
+            The builder object to enable chained calls.
+        """
+
     @typing.overload
     @abc.abstractmethod
     def add_button(
diff --git a/hikari/events/guild_events.py b/hikari/events/guild_events.py
index 9d194395..6a2e43b7 100644
--- a/hikari/events/guild_events.py
+++ b/hikari/events/guild_events.py
@@ -255,6 +255,12 @@ class GuildLeaveEvent(GuildVisibilityEvent):
     guild_id: snowflakes.Snowflake = attr.field()
     # <<inherited docstring from GuildEvent>>.
 
+    old_guild: typing.Optional[guilds.GatewayGuild] = attr.field()
+    """The old guild object.
+
+    This will be `builtins.None` if the guild missing from the cache.
+    """
+
     if typing.TYPE_CHECKING:
         # This should always fail.
         async def fetch_guild(self) -> typing.NoReturn:
@@ -286,7 +292,7 @@ class GuildUpdateEvent(GuildEvent):
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
-    old_guild: typing.Optional[guilds.Guild] = attr.field()
+    old_guild: typing.Optional[guilds.GatewayGuild] = attr.field()
     """The old guild object.
 
     This will be `builtins.None` if the guild missing from the cache.
diff --git a/hikari/guilds.py b/hikari/guilds.py
index 3cba854f..6c2202da 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -2931,7 +2931,7 @@ def get_voice_state(
     def get_emoji(
         self, emoji: snowflakes.SnowflakeishOr[emojis_.CustomEmoji]
     ) -> typing.Optional[emojis_.KnownCustomEmoji]:
-        """Get a cached role that belongs to the guild by it's ID or object.
+        """Get a cached emoji that belongs to the guild by it's ID or object.
 
         Parameters
         ----------
diff --git a/hikari/impl/bot.py b/hikari/impl/bot.py
index 96519772..50892b61 100644
--- a/hikari/impl/bot.py
+++ b/hikari/impl/bot.py
@@ -418,11 +418,112 @@ async def handle(name: str, awaitable: typing.Awaitable[typing.Any]) -> None:
         self._closed_event = None
 
     def dispatch(self, event: event_manager_.EventT_inv) -> asyncio.Future[typing.Any]:
+        """Dispatch an event.
+
+        Parameters
+        ----------
+        event : hikari.events.base_events.Event
+            The event to dispatch.
+
+        Example
+        -------
+        We can dispatch custom events by first defining a class that
+        derives from `hikari.events.base_events.Event`.
+
+        ```py
+        import attr
+
+        from hikari.traits import RESTAware
+        from hikari.events.base_events import Event
+        from hikari.users import User
+        from hikari.snowflakes import Snowflake
+
+        @attr.define()
+        class EveryoneMentionedEvent(Event):
+            app: RESTAware = attr.field()
+
+            author: User = attr.field()
+            '''The user who mentioned everyone.'''
+
+            content: str = attr.field()
+            '''The message that was sent.'''
+
+            message_id: Snowflake = attr.field()
+            '''The message ID.'''
+
+            channel_id: Snowflake = attr.field()
+            '''The channel ID.'''
+        ```
+
+        We can then dispatch our event as we see fit.
+
+        ```py
+        from hikari.events.messages import MessageCreateEvent
+
+        @bot.listen(MessageCreateEvent)
+        async def on_message(event):
+            if "@everyone" in event.content or "@here" in event.content:
+                event = EveryoneMentionedEvent(
+                    author=event.author,
+                    content=event.content,
+                    message_id=event.id,
+                    channel_id=event.channel_id,
+                )
+
+                bot.dispatch(event)
+        ```
+
+        This event can be listened to elsewhere by subscribing to it with
+        `EventManager.subscribe`.
+
+        ```py
+        @bot.listen(EveryoneMentionedEvent)
+        async def on_everyone_mentioned(event):
+            print(event.user, "just pinged everyone in", event.channel_id)
+        ```
+
+        Returns
+        -------
+        asyncio.Future[typing.Any]
+            A future that can be optionally awaited. If awaited, the future
+            will complete once all corresponding event listeners have been
+            invoked. If not awaited, this will schedule the dispatch of the
+            events in the background for later.
+
+        See Also
+        --------
+        Listen: `hikari.impl.bot.GatewayBot.listen`
+        Stream: `hikari.impl.bot.GatewayBot.stream`
+        Subscribe: `hikari.impl.bot.GatewayBot.subscribe`
+        Unubscribe: `hikari.impl.bot.GatewayBot.unsubscribe`
+        Wait_for: `hikari.impl.bot.GatewayBot.wait_for`
+        """
         return self._event_manager.dispatch(event)
 
     def get_listeners(
         self, event_type: typing.Type[event_manager_.EventT_co], /, *, polymorphic: bool = True
     ) -> typing.Collection[event_manager_.CallbackT[event_manager_.EventT_co]]:
+        """Get the listeners for a given event type, if there are any.
+
+        Parameters
+        ----------
+        event_type : typing.Type[T]
+            The event type to look for.
+            `T` must be a subclass of `hikari.events.base_events.Event`.
+        polymorphic : builtins.bool
+            If `builtins.True`, this will also return the listeners of the
+            subclasses of the given event type. If `builtins.False`, then
+            only listeners for this class specifically are returned. The
+            default is `builtins.True`.
+
+        Returns
+        -------
+        typing.Collection[typing.Callable[[T], typing.Coroutine[typing.Any, typing.Any, builtins.None]]
+            A copy of the collection of listeners for the event. Will return
+            an empty collection if nothing is registered.
+
+            `T` must be a subclass of `hikari.events.base_events.Event`.
+        """
         return self._event_manager.get_listeners(event_type, polymorphic=polymorphic)
 
     async def join(self, until_close: bool = True) -> None:
@@ -440,6 +541,34 @@ def listen(
         [event_manager_.CallbackT[event_manager_.EventT_co]],
         event_manager_.CallbackT[event_manager_.EventT_co],
     ]:
+        """Generate a decorator to subscribe a callback to an event type.
+
+        This is a second-order decorator.
+
+        Parameters
+        ----------
+        event_type : typing.Optional[typing.Type[T]]
+            The event type to subscribe to. The implementation may allow this
+            to be undefined. If this is the case, the event type will be inferred
+            instead from the type hints on the function signature.
+
+            `T` must be a subclass of `hikari.events.base_events.Event`.
+
+        Returns
+        -------
+        typing.Callable[[T], T]
+            A decorator for a coroutine function that passes it to
+            `EventManager.subscribe` before returning the function
+            reference.
+
+        See Also
+        --------
+        Dispatch: `hikari.impl.bot.GatewayBot.dispatch`
+        Stream: `hikari.impl.bot.GatewayBot.stream`
+        Subscribe: `hikari.impl.bot.GatewayBot.subscribe`
+        Unubscribe: `hikari.impl.bot.GatewayBot.unsubscribe`
+        Wait_for: `hikari.impl.bot.GatewayBot.wait_for`
+        """
         return self._event_manager.listen(event_type)
 
     @staticmethod
@@ -872,13 +1001,138 @@ def stream(
         timeout: typing.Union[float, int, None],
         limit: typing.Optional[int] = None,
     ) -> event_manager_.EventStream[event_manager_.EventT_co]:
+        """Return a stream iterator for the given event and sub-events.
+
+        Parameters
+        ----------
+        event_type : typing.Type[hikari.events.base_events.Event]
+            The event type to listen for. This will listen for subclasses of
+            this type additionally.
+        timeout : typing.Optional[builtins.int, builtins.float]
+            How long this streamer should wait for the next event before
+            ending the iteration. If `builtins.None` then this will continue
+            until explicitly broken from.
+        limit : typing.Optional[builtins.int]
+            The limit for how many events this should queue at one time before
+            dropping extra incoming events, leave this as `builtins.None` for
+            the cache size to be unlimited.
+
+        Returns
+        -------
+        EventStream[hikari.events.base_events.Event]
+            The async iterator to handle streamed events. This must be started
+            with `async with stream:` or `await stream.open()` before
+            asynchronously iterating over it.
+
+        !!! warning
+            If you use `await stream.open()` to start the stream then you must
+            also close it with `await stream.close()` otherwise it may queue
+            events in memory indefinitely.
+
+        Examples
+        --------
+
+        ```py
+        async with bot.stream(events.ReactionAddEvent, timeout=30).filter(("message_id", message.id)) as stream:
+            async for user_id in stream.map("user_id").limit(50):
+                ...
+        ```
+
+        or using await `open()` and await `close()`
+
+        ```py
+        stream = bot.stream(events.ReactionAddEvent, timeout=30).filter(("message_id", message.id))
+        await stream.open()
+
+        async for user_id in stream.map("user_id").limit(50)
+            ...
+
+        await stream.close()
+        ```
+
+        See Also
+        --------
+        Dispatch: `hikari.impl.bot.GatewayBot.dispatch`
+        Listen: `hikari.impl.bot.GatewayBot.listen`
+        Subscribe: `hikari.impl.bot.GatewayBot.subscribe`
+        Unubscribe: `hikari.impl.bot.GatewayBot.unsubscribe`
+        Wait_for: `hikari.impl.bot.GatewayBot.wait_for`
+        """
         self._check_if_alive()
         return self._event_manager.stream(event_type, timeout=timeout, limit=limit)
 
     def subscribe(self, event_type: typing.Type[typing.Any], callback: event_manager_.CallbackT[typing.Any]) -> None:
+        """Subscribe a given callback to a given event type.
+
+        Parameters
+        ----------
+        event_type : typing.Type[T]
+            The event type to listen for. This will also listen for any
+            subclasses of the given type.
+            `T` must be a subclass of `hikari.events.base_events.Event`.
+        callback
+            Must be a coroutine function to invoke. This should
+            consume an instance of the given event, or an instance of a valid
+            subclass if one exists. Any result is discarded.
+
+        Example
+        -------
+        The following demonstrates subscribing a callback to message creation
+        events.
+
+        ```py
+        from hikari.events.messages import MessageCreateEvent
+
+        async def on_message(event):
+            ...
+
+        bot.subscribe(MessageCreateEvent, on_message)
+        ```
+
+        See Also
+        --------
+        Dispatch: `hikari.impl.bot.GatewayBot.dispatch`
+        Listen: `hikari.impl.bot.GatewayBot.listen`
+        Stream: `hikari.impl.bot.GatewayBot.stream`
+        Unubscribe: `hikari.impl.bot.GatewayBot.unsubscribe`
+        Wait_for: `hikari.impl.bot.GatewayBot.wait_for`
+        """
         self._event_manager.subscribe(event_type, callback)
 
     def unsubscribe(self, event_type: typing.Type[typing.Any], callback: event_manager_.CallbackT[typing.Any]) -> None:
+        """Unsubscribe a given callback from a given event type, if present.
+
+        Parameters
+        ----------
+        event_type : typing.Type[T]
+            The event type to unsubscribe from. This must be the same exact
+            type as was originally subscribed with to be removed correctly.
+            `T` must derive from `hikari.events.base_events.Event`.
+        callback
+            The callback to unsubscribe.
+
+        Example
+        -------
+        The following demonstrates unsubscribing a callback from a message
+        creation event.
+
+        ```py
+        from hikari.events.messages import MessageCreateEvent
+
+        async def on_message(event):
+            ...
+
+        bot.unsubscribe(MessageCreateEvent, on_message)
+        ```
+
+        See Also
+        --------
+        Dispatch: `hikari.impl.bot.GatewayBot.dispatch`
+        Listen: `hikari.impl.bot.GatewayBot.listen`
+        Stream: `hikari.impl.bot.GatewayBot.stream`
+        Subscribe: `hikari.impl.bot.GatewayBot.subscribe`
+        Wait_for: `hikari.impl.bot.GatewayBot.wait_for`
+        """
         self._event_manager.unsubscribe(event_type, callback)
 
     async def wait_for(
@@ -888,6 +1142,48 @@ async def wait_for(
         timeout: typing.Union[float, int, None],
         predicate: typing.Optional[event_manager_.PredicateT[event_manager_.EventT_co]] = None,
     ) -> event_manager_.EventT_co:
+        """Wait for a given event to occur once, then return the event.
+
+        Parameters
+        ----------
+        event_type : typing.Type[hikari.events.base_events.Event]
+            The event type to listen for. This will listen for subclasses of
+            this type additionally.
+        predicate
+            A function taking the event as the single parameter.
+            This should return `builtins.True` if the event is one you want to
+            return, or `builtins.False` if the event should not be returned.
+            If left as `None` (the default), then the first matching event type
+            that the bot receives (or any subtype) will be the one returned.
+
+            !!! warning
+                Async predicates are not supported.
+        timeout : typing.Union[builtins.float, builtins.int, builtins.None]
+            The amount of time to wait before raising an `asyncio.TimeoutError`
+            and giving up instead. This is measured in seconds. If
+            `builtins.None`, then no timeout will be waited for (no timeout can
+            result in "leaking" of coroutines that never complete if called in
+            an uncontrolled way, so is not recommended).
+
+        Returns
+        -------
+        hikari.events.base_events.Event
+            The event that was provided.
+
+        Raises
+        ------
+        asyncio.TimeoutError
+            If the timeout is not `builtins.None` and is reached before an
+            event is received that the predicate returns `builtins.True` for.
+
+        See Also
+        --------
+        Dispatch: `hikari.impl.bot.GatewayBot.dispatch`
+        Listen: `hikari.impl.bot.GatewayBot.listen`
+        Stream: `hikari.impl.bot.GatewayBot.stream`
+        Subscribe: `hikari.impl.bot.GatewayBot.subscribe`
+        Unubscribe: `hikari.impl.bot.GatewayBot.unsubscribe`
+        """
         self._check_if_alive()
         return await self._event_manager.wait_for(event_type, timeout=timeout, predicate=predicate)
 
diff --git a/hikari/impl/event_factory.py b/hikari/impl/event_factory.py
index 80a23b24..7a243a63 100644
--- a/hikari/impl/event_factory.py
+++ b/hikari/impl/event_factory.py
@@ -222,7 +222,7 @@ def deserialize_guild_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_guild: typing.Optional[guild_models.Guild],
+        old_guild: typing.Optional[guild_models.GatewayGuild],
     ) -> guild_events.GuildUpdateEvent:
         guild_information = self._app.entity_factory.deserialize_gateway_guild(payload)
         return guild_events.GuildUpdateEvent(
@@ -234,9 +234,15 @@ def deserialize_guild_update_event(
         )
 
     def deserialize_guild_leave_event(
-        self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
+        self,
+        shard: gateway_shard.GatewayShard,
+        payload: data_binding.JSONObject,
+        *,
+        old_guild: typing.Optional[guild_models.GatewayGuild],
     ) -> guild_events.GuildLeaveEvent:
-        return guild_events.GuildLeaveEvent(app=self._app, shard=shard, guild_id=snowflakes.Snowflake(payload["id"]))
+        return guild_events.GuildLeaveEvent(
+            app=self._app, shard=shard, guild_id=snowflakes.Snowflake(payload["id"]), old_guild=old_guild
+        )
 
     def deserialize_guild_unavailable_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
diff --git a/hikari/impl/event_manager.py b/hikari/impl/event_manager.py
index 8c36fb5b..34aac5be 100644
--- a/hikari/impl/event_manager.py
+++ b/hikari/impl/event_manager.py
@@ -218,18 +218,20 @@ async def on_guild_delete(self, shard: gateway_shard.GatewayShard, payload: data
                 self._cache.set_guild_availability(event.guild_id, False)
 
         else:
-            event = self._event_factory.deserialize_guild_leave_event(shard, payload)
-
+            old: typing.Optional[guilds.GatewayGuild] = None
             if self._cache:
+                guild_id = snowflakes.Snowflake(payload["id"])
                 #  TODO: this doesn't work in all intent scenarios
-                self._cache.delete_guild(event.guild_id)
-                self._cache.clear_voice_states_for_guild(event.guild_id)
-                self._cache.clear_invites_for_guild(event.guild_id)
-                self._cache.clear_members_for_guild(event.guild_id)
-                self._cache.clear_presences_for_guild(event.guild_id)
-                self._cache.clear_guild_channels_for_guild(event.guild_id)
-                self._cache.clear_emojis_for_guild(event.guild_id)
-                self._cache.clear_roles_for_guild(event.guild_id)
+                old = self._cache.delete_guild(guild_id)
+                self._cache.clear_voice_states_for_guild(guild_id)
+                self._cache.clear_invites_for_guild(guild_id)
+                self._cache.clear_members_for_guild(guild_id)
+                self._cache.clear_presences_for_guild(guild_id)
+                self._cache.clear_guild_channels_for_guild(guild_id)
+                self._cache.clear_emojis_for_guild(guild_id)
+                self._cache.clear_roles_for_guild(guild_id)
+
+            event = self._event_factory.deserialize_guild_leave_event(shard, payload, old_guild=old)
 
         await self.dispatch(event)
 
diff --git a/pages/CNAME b/pages/CNAME
new file mode 100644
index 00000000..feec4f38
--- /dev/null
+++ b/pages/CNAME
@@ -0,0 +1,1 @@
+www.hikari-py.dev
diff --git a/pipelines/format.nox.py b/pipelines/format.nox.py
index 7badbef4..0498b37a 100644
--- a/pipelines/format.nox.py
+++ b/pipelines/format.nox.py
@@ -101,10 +101,10 @@ def remove_trailing_whitespaces_for_file(file: str, session: nox.Session, check_
             return False
 
         if check_only:
-            session.log("Trailing whitespaces found in", file)
+            session.log(f"Trailing whitespaces found in {file}")
             return True
 
-        session.log("Removing trailing whitespaces present in", file)
+        session.log(f"Removing trailing whitespaces present in {file}")
 
         with open(file, "wb") as fp:
             fp.writelines(new_lines)


Test patch:
diff --git a/tests/hikari/impl/test_event_factory.py b/tests/hikari/impl/test_event_factory.py
index a6727b53..65025af3 100644
--- a/tests/hikari/impl/test_event_factory.py
+++ b/tests/hikari/impl/test_event_factory.py
@@ -268,13 +268,15 @@ def test_deserialize_guild_update_event(self, event_factory, mock_app, mock_shar
 
     def test_deserialize_guild_leave_event(self, event_factory, mock_app, mock_shard):
         mock_payload = {"id": "43123123"}
+        mock_old_guild = object()
 
-        event = event_factory.deserialize_guild_leave_event(mock_shard, mock_payload)
+        event = event_factory.deserialize_guild_leave_event(mock_shard, mock_payload, old_guild=mock_old_guild)
 
         assert isinstance(event, guild_events.GuildLeaveEvent)
         assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.guild_id == 43123123
+        assert event.old_guild is mock_old_guild
 
     def test_deserialize_guild_unavailable_event(self, event_factory, mock_app, mock_shard):
         mock_payload = {"id": "6541233"}
diff --git a/tests/hikari/impl/test_event_manager.py b/tests/hikari/impl/test_event_manager.py
index 8ec18f8d..2308ba7a 100644
--- a/tests/hikari/impl/test_event_manager.py
+++ b/tests/hikari/impl/test_event_manager.py
@@ -376,7 +376,7 @@ async def test_on_guild_update_stateless(self, stateless_event_manager, shard, e
 
     @pytest.mark.asyncio()
     async def test_on_guild_delete_stateful_when_available(self, event_manager, shard, event_factory):
-        payload = {"unavailable": False}
+        payload = {"unavailable": False, "id": "123"}
         event = mock.Mock(guild_id=123)
 
         event_factory.deserialize_guild_leave_event.return_value = event
@@ -391,12 +391,14 @@ async def test_on_guild_delete_stateful_when_available(self, event_manager, shar
         event_manager._cache.clear_guild_channels_for_guild.assert_called_once_with(123)
         event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123)
         event_manager._cache.clear_roles_for_guild.assert_called_once_with(123)
-        event_factory.deserialize_guild_leave_event.assert_called_once_with(shard, payload)
+        event_factory.deserialize_guild_leave_event.assert_called_once_with(
+            shard, payload, old_guild=event_manager._cache.delete_guild.return_value
+        )
         event_manager.dispatch.assert_awaited_once_with(event)
 
     @pytest.mark.asyncio()
     async def test_on_guild_delete_stateful_when_unavailable(self, event_manager, shard, event_factory):
-        payload = {"unavailable": True}
+        payload = {"unavailable": True, "id": "123"}
         event = mock.Mock(guild_id=123)
 
         event_factory.deserialize_guild_unavailable_event.return_value = event
@@ -409,11 +411,11 @@ async def test_on_guild_delete_stateful_when_unavailable(self, event_manager, sh
 
     @pytest.mark.asyncio()
     async def test_on_guild_delete_stateless_when_available(self, stateless_event_manager, shard, event_factory):
-        payload = {"unavailable": False}
+        payload = {"unavailable": False, "id": "123"}
 
         await stateless_event_manager.on_guild_delete(shard, payload)
 
-        event_factory.deserialize_guild_leave_event.assert_called_once_with(shard, payload)
+        event_factory.deserialize_guild_leave_event.assert_called_once_with(shard, payload, old_guild=None)
         stateless_event_manager.dispatch.assert_awaited_once_with(
             event_factory.deserialize_guild_leave_event.return_value
         )