-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add InteractionHandler class (#64)
* feat: add InteractionHandler class * feat: add tests for the class * chore: run tests on all branches * chore: make actions nicer to remove duplicate runs * fix: tests * fix: tests
- Loading branch information
Showing
5 changed files
with
158 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from suggestions import SuggestionsBot | ||
|
||
|
||
class InteractionHandler: | ||
"""A generic interaction response class to allow for easier | ||
testing and generification of interaction responses. | ||
This class also aims to move the custom add-ons out of | ||
the underlying disnake classes to help promote easier | ||
version upgrading in the future. | ||
""" | ||
|
||
def __init__( | ||
self, interaction: disnake.Interaction, ephemeral: bool, with_message: bool | ||
): | ||
self.interaction: disnake.Interaction = interaction | ||
self.ephemeral: bool = ephemeral | ||
self.with_message: bool = with_message | ||
self.is_deferred: bool = False | ||
|
||
# This is useful in error handling to stop | ||
# getting discord "Interaction didn't respond" | ||
# errors if we haven't yet sent anything | ||
self.has_sent_something: bool = False | ||
|
||
async def send( | ||
self, | ||
content: str | None = None, | ||
*, | ||
embed: disnake.Embed | None = None, | ||
file: disnake.File | None = None, | ||
components: list | None = None, | ||
): | ||
data = {} | ||
if content is not None: | ||
data["content"] = content | ||
if embed is not None: | ||
data["embed"] = embed | ||
if file is not None: | ||
data["file"] = file | ||
if components is not None: | ||
data["components"] = components | ||
|
||
if not data: | ||
raise ValueError("Expected at-least one value to send.") | ||
|
||
await self.interaction.send(ephemeral=self.ephemeral, **data) | ||
self.has_sent_something = True | ||
|
||
@classmethod | ||
async def new_handler( | ||
cls, | ||
interaction: disnake.Interaction, | ||
bot: SuggestionsBot, | ||
*, | ||
ephemeral: bool = True, | ||
with_message: bool = True, | ||
) -> InteractionHandler: | ||
"""Generate a new instance and defer the interaction.""" | ||
instance = cls(interaction, ephemeral, with_message) | ||
await interaction.response.defer(ephemeral=ephemeral, with_message=with_message) | ||
instance.is_deferred = True | ||
|
||
# Register this on the bot instance so other areas can | ||
# request the interaction handler, such as error handlers | ||
bot.state.interaction_handlers.add_entry(interaction.application_id, instance) | ||
|
||
return instance | ||
|
||
@classmethod | ||
async def fetch_handler( | ||
cls, application_id: int, bot: SuggestionsBot | ||
) -> InteractionHandler: | ||
"""Fetch a registered handler for the given interaction.""" | ||
return bot.state.interaction_handlers.get_entry(application_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
from unittest.mock import AsyncMock | ||
|
||
import pytest | ||
from bot_base import NonExistentEntry | ||
|
||
from suggestions.interaction_handler import InteractionHandler | ||
|
||
|
||
async def test_send(interaction_handler: InteractionHandler): | ||
assert interaction_handler.has_sent_something is False | ||
|
||
with pytest.raises(ValueError): | ||
await interaction_handler.send() | ||
|
||
await interaction_handler.send("Hello world") | ||
assert interaction_handler.has_sent_something is True | ||
assert interaction_handler.interaction.send.assert_called_with( | ||
content="Hello world", ephemeral=True | ||
) | ||
|
||
interaction_handler.interaction = AsyncMock() | ||
await interaction_handler.send( | ||
"Hello world", embed="Embed", file="File", components=["Test"] | ||
) | ||
assert interaction_handler.interaction.send.assert_called_with( | ||
content="Hello world", | ||
ephemeral=True, | ||
embed="Embed", | ||
file="File", | ||
components=["Test"], | ||
) | ||
|
||
|
||
async def test_new_handler(bot): | ||
assert bot.state.interaction_handlers.cache == {} | ||
handler: InteractionHandler = await InteractionHandler.new_handler(AsyncMock(), bot) | ||
assert bot.state.interaction_handlers.cache != {} | ||
assert handler.has_sent_something is False | ||
assert handler.is_deferred is True | ||
|
||
handler_2: InteractionHandler = await InteractionHandler.new_handler( | ||
AsyncMock(), bot, ephemeral=False, with_message=False | ||
) | ||
assert handler_2.ephemeral is False | ||
assert handler_2.with_message is False | ||
|
||
|
||
async def test_fetch_handler(bot): | ||
application_id = 123456789 | ||
with pytest.raises(NonExistentEntry): | ||
await InteractionHandler.fetch_handler(application_id, bot) | ||
|
||
mock = AsyncMock() | ||
mock.application_id = application_id | ||
await InteractionHandler.new_handler(mock, bot, with_message=False) | ||
handler: InteractionHandler = await InteractionHandler.fetch_handler( | ||
application_id, bot | ||
) | ||
assert handler.with_message is False | ||
assert handler.ephemeral is True |