From 2d3c2c09747bb351a335a2a5e89d99b54a1d0a50 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Sat, 23 Sep 2023 01:55:53 -0400 Subject: [PATCH 01/21] fix: properly check if name is none for anno names (#1555) --- interactions/models/internal/application_commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/models/internal/application_commands.py b/interactions/models/internal/application_commands.py index 8ed71fddf..073184f1a 100644 --- a/interactions/models/internal/application_commands.py +++ b/interactions/models/internal/application_commands.py @@ -609,8 +609,8 @@ def _add_option_from_anno_method(self, name: str, option: SlashCommandOption) -> if not self.options: self.options = [] - if option.name is None: - option.name = name + if option.name.default is None: + option.name = LocalisedName.converter(name) else: option.argument_name = name From aa4b497957e2b7117adc1e75c07fb47bc3f60440 Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:51:03 -0400 Subject: [PATCH 02/21] feat: allow editing for guild role icons (#1558) * chore: Version bump Signed-off-by: LordOfPolls * Implemented role icon edit * added role icon editing to the edit method * Update misc_utils.py * ci: correct from checks. * fixed formatting, added return formatting indicator * Noticed that interactions.client.utils.serializer already had the functions I added to misc_utils. Undid those changes and tidied up the code in role.py to reflect the already existing converter. Added an error raise if both icon and unicode emojis are used in one edit (the api requires only one is provided.) * Cleaned up the docstring and moved the icon serialization cleaned up the docstring to better align with requirements, moved the icon serialization so the payload's code is a bit easier to follow at a glance. * Added UPLOADABLE_TYPE per comment --------- Signed-off-by: LordOfPolls Co-authored-by: Katelyn Gigante Co-authored-by: LordOfPolls Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- interactions/client/utils/misc_utils.py | 0 interactions/models/discord/role.py | 15 +++++++++++++-- pyproject.toml | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) mode change 100644 => 100755 interactions/client/utils/misc_utils.py diff --git a/interactions/client/utils/misc_utils.py b/interactions/client/utils/misc_utils.py old mode 100644 new mode 100755 diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index 5161f912d..2f81a0f92 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -2,12 +2,12 @@ from typing import Any, TYPE_CHECKING import attrs - from interactions.client.const import MISSING, T, Missing from interactions.client.utils import nulled_boolean_get from interactions.client.utils.attr_converters import optional as optional_c -from interactions.client.utils.serializer import dict_filter +from interactions.client.utils.serializer import dict_filter, to_image_data from interactions.models.discord.asset import Asset +from interactions.models.discord.file import UPLOADABLE_TYPE from interactions.models.discord.color import COLOR_TYPES, Color, process_color from interactions.models.discord.emoji import PartialEmoji from interactions.models.discord.enums import Permissions @@ -187,6 +187,8 @@ async def edit( color: Color | COLOR_TYPES | None = None, hoist: bool | None = None, mentionable: bool | None = None, + icon: bytes | UPLOADABLE_TYPE | None = None, + unicode_emoji: str | None = None, ) -> "Role": """ Edit this role, all arguments are optional. @@ -197,6 +199,8 @@ async def edit( color: The color of the role hoist: whether the role should be displayed separately in the sidebar mentionable: whether the role should be mentionable + icon: (Guild Level 2+) Bytes-like object representing the icon; supports PNG, JPEG and WebP + unicode_emoji: (Guild Level 2+) Unicode emoji for the role; can't be used with icon Returns: Role with updated information @@ -204,6 +208,11 @@ async def edit( """ color = process_color(color) + if icon and unicode_emoji: + raise ValueError("Cannot pass both icon and unicode_emoji") + if icon: + icon = to_image_data(icon) + payload = dict_filter( { "name": name, @@ -211,6 +220,8 @@ async def edit( "color": color, "hoist": hoist, "mentionable": mentionable, + "icon": icon, + "unicode_emoji": unicode_emoji, } ) diff --git a/pyproject.toml b/pyproject.toml index 68778cfea..1d28bd18b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "interactions.py" -version = "5.9.2" +version = "5.10.0" description = "Easy, simple, scalable and modular: a Python API wrapper for interactions." authors = [ "LordOfPolls ", From fab80e02d894f0f2da014c9e2e227d201c58593e Mon Sep 17 00:00:00 2001 From: Nathan Spees <84055084+Catalyst4222@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:34:04 -0400 Subject: [PATCH 03/21] docs: remove `Skins` in Extension docstring (#1559) https://discord.com/channels/789032594456576001/1160026100001427476 Signed-off-by: Nathan Spees <84055084+Catalyst4222@users.noreply.github.com> --- interactions/models/internal/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/models/internal/extension.py b/interactions/models/internal/extension.py index 178ff1070..844600b7a 100644 --- a/interactions/models/internal/extension.py +++ b/interactions/models/internal/extension.py @@ -22,7 +22,7 @@ class Extension: """ - A class that allows you to separate your commands and listeners into separate files. Skins require an entrypoint in the same file called `setup`, this function allows client to load the Extension. + A class that allows you to separate your commands and listeners into separate files. Extensions require an entrypoint in the same file called `setup`, this function allows client to load the Extension. ??? Hint "Example Usage:" ```python From 4236ac7c5311875a12b6241843349bdedb66b96d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 06:46:29 +0100 Subject: [PATCH 04/21] ci: weekly check. (#1532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: weekly check. updates: - [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0) - [github.com/astral-sh/ruff-pre-commit: v0.0.282 → v0.0.292](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.282...v0.0.292) - [github.com/psf/black: 23.7.0 → 23.9.1](https://github.com/psf/black/compare/23.7.0...23.9.1) * ci: correct from checks. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- interactions/models/internal/application_commands.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bae06de8e..e58e2859e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: requirements-txt-fixer name: Requirements @@ -30,12 +30,12 @@ repos: - id: check-merge-conflict name: Merge Conflicts - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.0.282' + rev: 'v0.0.292' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black name: Black Formatting diff --git a/interactions/models/internal/application_commands.py b/interactions/models/internal/application_commands.py index 073184f1a..b6cde4f0d 100644 --- a/interactions/models/internal/application_commands.py +++ b/interactions/models/internal/application_commands.py @@ -535,7 +535,7 @@ def _is_optional(anno: typing.Any) -> bool: def _remove_optional(t: OptionType | type) -> Any: - non_optional_args: tuple[type] = tuple(a for a in typing.get_args(t) if a is not types.NoneType) # noqa + non_optional_args: tuple[type] = tuple(a for a in typing.get_args(t) if a is not types.NoneType) if len(non_optional_args) == 1: return non_optional_args[0] return typing.Union[non_optional_args] # type: ignore From f344c40406471feaa30eb86d5c9ec3984dd41ba0 Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Sun, 29 Oct 2023 04:01:40 +0000 Subject: [PATCH 05/21] docs: update pypi docs link Signed-off-by: LordOfPolls --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 02cdc3a52..5aed815d4 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ ], project_urls={ "Discord": "https://discord.gg/KkgMBVuEkx", - "Documentation": "https://naff-docs.readthedocs.io/en/latest/", # TODO: replace + "Documentation": "https://interactions-py.github.io/interactions.py/", }, extras_require=extras_require, ) From 0d78f0230ff62f547fe2bc5fdf7552b7d2cd5ce8 Mon Sep 17 00:00:00 2001 From: ItsRqtl Date: Wed, 15 Nov 2023 10:24:15 -0800 Subject: [PATCH 06/21] fix: correct emoji cdn url domain Signed-off-by: ItsRqtl --- interactions/models/discord/emoji.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/models/discord/emoji.py b/interactions/models/discord/emoji.py index 7c59abc75..0364a7e5e 100644 --- a/interactions/models/discord/emoji.py +++ b/interactions/models/discord/emoji.py @@ -209,7 +209,7 @@ async def delete(self, reason: Optional[str] = None) -> None: @property def url(self) -> str: """CDN url for the emoji.""" - return f"https://cdn.discordapp.net/emojis/{self.id}.{'gif' if self.animated else 'png'}" + return f"https://cdn.discordapp.com/emojis/{self.id}.{'gif' if self.animated else 'png'}" def process_emoji_req_format(emoji: Optional[Union[PartialEmoji, dict, str]]) -> Optional[str]: From e4c1cbf0f40ec4a2a0598530adf2aeccfa013fe2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:27:11 -0500 Subject: [PATCH 07/21] ci: weekly check. (#1565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.292 → v0.1.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.292...v0.1.5) - [github.com/psf/black: 23.9.1 → 23.11.0](https://github.com/psf/black/compare/23.9.1...23.11.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e58e2859e..2153c3e0a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,12 +30,12 @@ repos: - id: check-merge-conflict name: Merge Conflicts - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.0.292' + rev: 'v0.1.5' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.11.0 hooks: - id: black name: Black Formatting From e5ef5c1f6446b0e40d207619b16420e2d72c938b Mon Sep 17 00:00:00 2001 From: Scratch Date: Thu, 16 Nov 2023 05:31:12 +1100 Subject: [PATCH 08/21] feat: support nitro basic PremiumType (#1563) * chore: Version bump Signed-off-by: LordOfPolls * feat: support nitro basic PremiumType --------- Signed-off-by: LordOfPolls Co-authored-by: Katelyn Gigante Co-authored-by: LordOfPolls --- interactions/models/discord/enums.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interactions/models/discord/enums.py b/interactions/models/discord/enums.py index c201f6d50..6957c818f 100644 --- a/interactions/models/discord/enums.py +++ b/interactions/models/discord/enums.py @@ -340,6 +340,8 @@ class PremiumType(CursedIntEnum): """Using Nitro Classic""" NITRO = 2 """Full Nitro membership""" + NITRO_BASIC = 3 + """Basic Nitro membership""" class MessageType(CursedIntEnum): From 0cea688b02a162c1ed3d24e296d272be4b949d32 Mon Sep 17 00:00:00 2001 From: Lacosst0 <68904262+Lacosst0@users.noreply.github.com> Date: Thu, 16 Nov 2023 18:44:08 +0400 Subject: [PATCH 09/21] fix: Snowflake encoding issue when msgspec is used fix (#1562) * fix: Snowflake encoding issue when msgspec is used Signed-off-by: Lacosst0 <68904262+Lacosst0@users.noreply.github.com> * Combine 2 isinstance into 1 Signed-off-by: Lacosst0 <68904262+Lacosst0@users.noreply.github.com> --------- Signed-off-by: Lacosst0 <68904262+Lacosst0@users.noreply.github.com> --- interactions/client/utils/input_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interactions/client/utils/input_utils.py b/interactions/client/utils/input_utils.py index a4fc47a99..66e27fc8f 100644 --- a/interactions/client/utils/input_utils.py +++ b/interactions/client/utils/input_utils.py @@ -2,6 +2,7 @@ import re import typing from enum import IntFlag +from interactions.models.discord.snowflake import Snowflake from typing import Any, Dict, Union, Optional import aiohttp # type: ignore @@ -25,8 +26,8 @@ import msgspec.json as json def enc_hook(obj: Any) -> int: - # msgspec doesnt support IntFlags - if isinstance(obj, IntFlag): + # msgspec doesnt support IntFlags or interactions.Snowflakes + if isinstance(obj, (IntFlag, Snowflake)): return int(obj) raise TypeError(f"Object of type {type(obj)} is not JSON serializable") From 78ab162f7225662117ed51edabfdc3d35f09de6f Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Thu, 16 Nov 2023 14:44:29 +0000 Subject: [PATCH 10/21] feat: add support select menu defaults (#1556) * feat: add support select menu defaults * feat: add new objects to local scope __all__ * fix: use discordobject as base for dfaultselectvalue --- interactions/models/discord/components.py | 144 +++++++++++++++++++++- 1 file changed, 139 insertions(+), 5 deletions(-) diff --git a/interactions/models/discord/components.py b/interactions/models/discord/components.py index 632089b5e..a5c023534 100644 --- a/interactions/models/discord/components.py +++ b/interactions/models/discord/components.py @@ -1,15 +1,22 @@ import contextlib import uuid from abc import abstractmethod -from typing import Any, Dict, Iterator, List, Optional, Union +from typing import Any, Dict, Iterator, List, Optional, Union, TYPE_CHECKING +import attrs import discord_typings +import interactions.models.discord as d_models +from interactions.models.discord.snowflake import Snowflake from interactions.client.const import ACTION_ROW_MAX_ITEMS, MISSING from interactions.client.mixins.serialization import DictSerializationMixin +from interactions.models.discord.base import DiscordObject from interactions.models.discord.emoji import PartialEmoji, process_emoji from interactions.models.discord.enums import ButtonStyle, ChannelType, ComponentType +if TYPE_CHECKING: + import interactions.models.discord + __all__ = ( "BaseComponent", "InteractiveComponent", @@ -26,6 +33,8 @@ "spread_to_rows", "get_components_ids", "TYPE_COMPONENT_MAPPING", + "SelectDefaultValues", + "DefaultableSelectMenu", ) @@ -325,6 +334,87 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData: } +@attrs.define(eq=False, order=False, hash=False, slots=False) +class SelectDefaultValues(DiscordObject): + id: Snowflake + """ID of a user, role, or channel""" + type: str + """Type of value that id represents. Either "user", "role", or "channel""" + + @classmethod + def from_object(cls, obj: DiscordObject) -> "SelectDefaultValues": + """Create a default value from a discord object.""" + match obj: + case d_models.User(): + return cls(id=obj.id, type="user") + case d_models.Member(): + return cls(id=obj.id, type="user") + case d_models.BaseChannel(): + return cls(id=obj.id, type="channel") + case d_models.Role(): + return cls(id=obj.id, type="role") + case _: + raise TypeError( + f"Cannot convert {obj} of type {type(obj)} to a SelectDefaultValues - Expected User, Channel, Member, or Role" + ) + + +class DefaultableSelectMenu(BaseSelectMenu): + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ] + ] | None = None + + def __init__( + self, + defaults: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ] + ] + | None = None, + **kwargs, + ) -> None: + super().__init__(**kwargs) + self.default_values = defaults + + def add_default_value( + self, + value: Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ) -> None: + if self.default_values is None: + self.default_values = [] + self.default_values.append(value) + + def to_dict(self) -> discord_typings.SelectMenuComponentData: + data = super().to_dict() + if self.default_values is not None: + data["default_values"] = [ # type: ignore # waiting on discord typings to update + value.to_dict() + if isinstance(value, SelectDefaultValues) + else SelectDefaultValues.from_object(value).to_dict() + for value in self.default_values + ] + + # Discord handles the type checking, no need to do it here + return data + + class StringSelectOption(BaseComponent): """ Represents a select option. @@ -461,7 +551,7 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData: } -class UserSelectMenu(BaseSelectMenu): +class UserSelectMenu(DefaultableSelectMenu): """ Represents a user select component. @@ -481,6 +571,16 @@ def __init__( min_values: int = 1, max_values: int = 1, custom_id: str | None = None, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, disabled: bool = False, ) -> None: super().__init__( @@ -489,12 +589,13 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.type: ComponentType = ComponentType.USER_SELECT -class RoleSelectMenu(BaseSelectMenu): +class RoleSelectMenu(DefaultableSelectMenu): """ Represents a user select component. @@ -515,6 +616,16 @@ def __init__( max_values: int = 1, custom_id: str | None = None, disabled: bool = False, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, ) -> None: super().__init__( placeholder=placeholder, @@ -522,12 +633,13 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.type: ComponentType = ComponentType.ROLE_SELECT -class MentionableSelectMenu(BaseSelectMenu): +class MentionableSelectMenu(DefaultableSelectMenu): def __init__( self, *, @@ -536,6 +648,16 @@ def __init__( max_values: int = 1, custom_id: str | None = None, disabled: bool = False, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, ) -> None: super().__init__( placeholder=placeholder, @@ -543,12 +665,13 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.type: ComponentType = ComponentType.MENTIONABLE_SELECT -class ChannelSelectMenu(BaseSelectMenu): +class ChannelSelectMenu(DefaultableSelectMenu): def __init__( self, *, @@ -558,6 +681,16 @@ def __init__( max_values: int = 1, custom_id: str | None = None, disabled: bool = False, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, ) -> None: super().__init__( placeholder=placeholder, @@ -565,6 +698,7 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.channel_types: list[ChannelType] | None = channel_types or [] From ab03fb8d69fec1bd309eca30791562b04beb4080 Mon Sep 17 00:00:00 2001 From: Curtis Larsen Date: Fri, 24 Nov 2023 19:34:13 -0700 Subject: [PATCH 11/21] Removed self as first parameter to functions (not methods) in Guides. (#1571) * chore: Version bump Signed-off-by: LordOfPolls * Removed self as first parameter to functions (not methods) in Guides. --------- Signed-off-by: LordOfPolls Co-authored-by: Katelyn Gigante Co-authored-by: LordOfPolls --- docs/src/Guides/03 Creating Commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/Guides/03 Creating Commands.md b/docs/src/Guides/03 Creating Commands.md index abe04237a..aa4f46c60 100644 --- a/docs/src/Guides/03 Creating Commands.md +++ b/docs/src/Guides/03 Creating Commands.md @@ -263,7 +263,7 @@ In there, you have three seconds to return whatever choices you want to the user from interactions import AutocompleteContext @my_command_function.autocomplete("string_option") -async def autocomplete(self, ctx: AutocompleteContext): +async def autocomplete(ctx: AutocompleteContext): string_option_input = ctx.input_text # can be empty # you can use ctx.kwargs.get("name") to get the current state of other options - note they can be empty too @@ -507,7 +507,7 @@ import traceback from interactions.api.events import CommandError @listen(CommandError, disable_default_listeners=True) # tell the dispatcher that this replaces the default listener -async def on_command_error(self, event: CommandError): +async def on_command_error(event: CommandError): traceback.print_exception(event.error) if not event.ctx.responded: await event.ctx.send("Something went wrong.") From 116c0f9be9090af9764c76415f0261f4e79676fb Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:31:16 -0500 Subject: [PATCH 12/21] fix: pass in level to wrapped logger when disconnecting (#1580) --- interactions/api/gateway/state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/api/gateway/state.py b/interactions/api/gateway/state.py index aa90917ae..2678ed7bc 100644 --- a/interactions/api/gateway/state.py +++ b/interactions/api/gateway/state.py @@ -128,7 +128,7 @@ async def _ws_connect(self) -> None: except Exception as e: self.client.dispatch(events.Disconnect()) - self.wrapped_logger("".join(traceback.format_exception(type(e), e, e.__traceback__))) + self.wrapped_logger(logging.ERROR, "".join(traceback.format_exception(type(e), e, e.__traceback__))) def wrapped_logger(self, level: int, message: str, **kwargs) -> None: """ From 3a1e3c5879315df6c3552f40c79c93cddf65e425 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:31:38 -0500 Subject: [PATCH 13/21] docs: improve hybrid cmd section (#1577) --- docs/src/Guides/03 Creating Commands.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/src/Guides/03 Creating Commands.md b/docs/src/Guides/03 Creating Commands.md index aa4f46c60..8232067a0 100644 --- a/docs/src/Guides/03 Creating Commands.md +++ b/docs/src/Guides/03 Creating Commands.md @@ -521,13 +521,16 @@ If your bot is complex enough, you might find yourself wanting to use custom mod To do this, you'll want to use a string option, and define a converter. Information on how to use converters can be found [on the converter page](../08 Converters). -## Prefixed/Text Commands +## Hybrid Commands -To use prefixed commands, instead of typing `/my_command`, you will need to type instead `!my_command`, provided that the prefix you set is `!`. +!!! note + Prefixed commands, called by Discord as "text commands" and sometimes called "message commands" (not to be confused with Context Menu Message Commands), are commands that are triggered when a user sends a normal message with a designated "prefix" in front of them (ie `!my_command`). + + interactions.py contains an extension for making these commands, which you can [read about here](/interactions.py/Guides/26 Prefixed Commands). Hybrid commands are are slash commands that also get converted to an equivalent prefixed command under the hood. They are their own extension, and require [prefixed commands to be set up beforehand](/interactions.py/Guides/26 Prefixed Commands). After that, use the `setup` function in the `hybrid_commands` extension in your main bot file. -Your setup can (but doesn't necessarily have to) look like this: +Your setup should look similar to this: ```python import interactions @@ -549,7 +552,7 @@ async def my_command_function(ctx: HybridContext): await ctx.send("Hello World") ``` -Suggesting you are using the default mention settings for your bot, you should be able to run this command by `@BotPing my_command`. +Suggesting you are using the default mention settings for your bot, you should be able to run this command by typing out `@BotPing my_command` or using the slash command `/my_command`. Both will work largely equivalently. As you can see, the only difference between hybrid commands and slash commands, from a developer perspective, is that they use `HybridContext`, which attempts to seamlessly allow using the same context for slash and prefixed commands. You can always get the underlying context via `inner_context`, though. From 6b2d2581b0ea5a9bf1aba28df2a37151753837ce Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:31:55 -0500 Subject: [PATCH 14/21] docs: improve mkdocs config (#1578) --- mkdocs.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 00030829d..2b7d2bed2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,11 +121,12 @@ plugins: show_if_no_docstring: True - minify: minify_html: true - # keep this at the bottom of the plugins list - if you are building without insiders, comment it out - - privacy: - # Downloads all external resources and stores them locally - externals: bundle + + - group: enabled: !ENV [ DEPLOY, False ] + plugins: + - privacy: + externals: bundle markdown_extensions: @@ -141,8 +142,8 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.superfences - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg - pymdownx.keys - pymdownx.saneheaders - pymdownx.smartsymbols From 69b9feb16696b59b6e7e975fe2afb73485bc1c4d Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:32:07 -0500 Subject: [PATCH 15/21] docs: slightly improve search experience (#1576) --- .../API Reference/API Reference/ext/hybrid_commands/index.md | 5 +++++ docs/src/API Reference/API Reference/ext/index.md | 5 +++++ .../API Reference/ext/prefixed_commands/index.md | 5 +++++ docs/src/API Reference/API Reference/models/Discord/index.md | 5 +++++ .../src/API Reference/API Reference/models/Internal/index.md | 5 +++++ docs/src/API Reference/API Reference/models/Misc/index.md | 5 +++++ docs/src/API Reference/API Reference/models/index.md | 5 +++++ docs/src/Guides/01 Getting Started.md | 5 +++++ docs/src/Guides/02 Creating Your Bot.md | 5 +++++ docs/src/Guides/03 Creating Commands.md | 5 +++++ docs/src/Guides/04 Context Menus.md | 5 +++++ docs/src/Guides/05 Components.md | 5 +++++ docs/src/Guides/06 Modals.md | 5 +++++ docs/src/Guides/08 Converters.md | 5 +++++ docs/src/Guides/10 Events.md | 5 +++++ docs/src/Guides/20 Extensions.md | 5 +++++ docs/src/Guides/22 Live Patching.md | 5 +++++ docs/src/Guides/23 Voice.md | 5 +++++ docs/src/Guides/24 Localisation.md | 5 +++++ docs/src/Guides/25 Error Tracking.md | 5 +++++ docs/src/Guides/26 Prefixed Commands.md | 5 +++++ docs/src/Guides/30 Pagination.md | 5 +++++ docs/src/Guides/40 Tasks.md | 5 +++++ docs/src/Guides/80 Sharding.md | 5 +++++ docs/src/Guides/90 Example.md | 5 +++++ docs/src/Guides/97 Migration From D.py.md | 5 +++++ docs/src/Guides/98 Migration from 4.X.md | 5 +++++ docs/src/Guides/99 2.x Migration_NAFF.md | 5 +++++ docs/src/Guides/index.md | 5 +++++ docs/src/index.md | 2 ++ 30 files changed, 147 insertions(+) diff --git a/docs/src/API Reference/API Reference/ext/hybrid_commands/index.md b/docs/src/API Reference/API Reference/ext/hybrid_commands/index.md index c2721707d..54b0d1cb4 100644 --- a/docs/src/API Reference/API Reference/ext/hybrid_commands/index.md +++ b/docs/src/API Reference/API Reference/ext/hybrid_commands/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Hybrid Commands Index - [Context](context) diff --git a/docs/src/API Reference/API Reference/ext/index.md b/docs/src/API Reference/API Reference/ext/index.md index 9e0753164..c59e1d35b 100644 --- a/docs/src/API Reference/API Reference/ext/index.md +++ b/docs/src/API Reference/API Reference/ext/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Ext Index These files contain useful features that help you develop a bot diff --git a/docs/src/API Reference/API Reference/ext/prefixed_commands/index.md b/docs/src/API Reference/API Reference/ext/prefixed_commands/index.md index 7c2f70db7..3f56f32df 100644 --- a/docs/src/API Reference/API Reference/ext/prefixed_commands/index.md +++ b/docs/src/API Reference/API Reference/ext/prefixed_commands/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Prefixed Commands Index - [Command](command) diff --git a/docs/src/API Reference/API Reference/models/Discord/index.md b/docs/src/API Reference/API Reference/models/Discord/index.md index a0281bbb7..d56908be7 100644 --- a/docs/src/API Reference/API Reference/models/Discord/index.md +++ b/docs/src/API Reference/API Reference/models/Discord/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Discord Models Index - [Activity](activity) diff --git a/docs/src/API Reference/API Reference/models/Internal/index.md b/docs/src/API Reference/API Reference/models/Internal/index.md index 05870d026..464717957 100644 --- a/docs/src/API Reference/API Reference/models/Internal/index.md +++ b/docs/src/API Reference/API Reference/models/Internal/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Internal Models Index - [Active Voice State](active_voice_state) diff --git a/docs/src/API Reference/API Reference/models/Misc/index.md b/docs/src/API Reference/API Reference/models/Misc/index.md index e58937c7e..15822e153 100644 --- a/docs/src/API Reference/API Reference/models/Misc/index.md +++ b/docs/src/API Reference/API Reference/models/Misc/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Misc Models Index - [Iterator](iterator) diff --git a/docs/src/API Reference/API Reference/models/index.md b/docs/src/API Reference/API Reference/models/index.md index 60181e494..d1eda2bc2 100644 --- a/docs/src/API Reference/API Reference/models/index.md +++ b/docs/src/API Reference/API Reference/models/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + # Models Within these pages, you will find a list of all available models within interactions.py. diff --git a/docs/src/Guides/01 Getting Started.md b/docs/src/Guides/01 Getting Started.md index ca59cc573..bda0b6058 100644 --- a/docs/src/Guides/01 Getting Started.md +++ b/docs/src/Guides/01 Getting Started.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Introduction Ready to get your Python on and create a Discord bot? This guide's got you covered with installation options and a basic bot code example. diff --git a/docs/src/Guides/02 Creating Your Bot.md b/docs/src/Guides/02 Creating Your Bot.md index e3d73e956..32f5265aa 100644 --- a/docs/src/Guides/02 Creating Your Bot.md +++ b/docs/src/Guides/02 Creating Your Bot.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Creating Your Bot To make a bot on Discord, you must first create an application on Discord. Thankfully, Discord has made this process very simple: diff --git a/docs/src/Guides/03 Creating Commands.md b/docs/src/Guides/03 Creating Commands.md index 8232067a0..0863ab90f 100644 --- a/docs/src/Guides/03 Creating Commands.md +++ b/docs/src/Guides/03 Creating Commands.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Slash Commands So you want to make a slash command (or interaction, as they are officially called), but don't know how to get started? diff --git a/docs/src/Guides/04 Context Menus.md b/docs/src/Guides/04 Context Menus.md index 7d9aa63f7..4f24864c4 100644 --- a/docs/src/Guides/04 Context Menus.md +++ b/docs/src/Guides/04 Context Menus.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Context Menus Context menus are interactions under the hood. Defining them is very similar. diff --git a/docs/src/Guides/05 Components.md b/docs/src/Guides/05 Components.md index 763d170f1..4c56240ee 100644 --- a/docs/src/Guides/05 Components.md +++ b/docs/src/Guides/05 Components.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Components Components (Buttons, Select Menus and soon Text Input Fields) can be added to any message by passing them to the `components` argument in any `.send()` method. diff --git a/docs/src/Guides/06 Modals.md b/docs/src/Guides/06 Modals.md index 2960bdf8f..29f870a99 100644 --- a/docs/src/Guides/06 Modals.md +++ b/docs/src/Guides/06 Modals.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Modals Modals are basically popups which a user can use to send text information to your bot. As of the writing of this guide, you can use two components in a modal: diff --git a/docs/src/Guides/08 Converters.md b/docs/src/Guides/08 Converters.md index 56c3e873d..fa58c019b 100644 --- a/docs/src/Guides/08 Converters.md +++ b/docs/src/Guides/08 Converters.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Converters If your bot is complex enough, you might find yourself wanting to use custom models in your commands. Converters are classes that allow you to do just that, and can be used in both slash and prefixed commands. diff --git a/docs/src/Guides/10 Events.md b/docs/src/Guides/10 Events.md index 1253eee59..931403398 100644 --- a/docs/src/Guides/10 Events.md +++ b/docs/src/Guides/10 Events.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Events Events (in interactions.py) are pieces of information that are sent whenever something happens in Discord or in the library itself - this includes channel updates, message sending, the bot starting up, and more. diff --git a/docs/src/Guides/20 Extensions.md b/docs/src/Guides/20 Extensions.md index c782f72d7..3e2489107 100644 --- a/docs/src/Guides/20 Extensions.md +++ b/docs/src/Guides/20 Extensions.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Extensions ## Introduction diff --git a/docs/src/Guides/22 Live Patching.md b/docs/src/Guides/22 Live Patching.md index 4644c314f..13604c8f8 100644 --- a/docs/src/Guides/22 Live Patching.md +++ b/docs/src/Guides/22 Live Patching.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Live Patching interactions.py has a few built-in extensions that add some features, primarily for debugging. One of these extensions that you can enable separately is to add [`jurigged`](https://github.com/breuleux/jurigged) for live patching of code. diff --git a/docs/src/Guides/23 Voice.md b/docs/src/Guides/23 Voice.md index aa3c708d0..9be431860 100644 --- a/docs/src/Guides/23 Voice.md +++ b/docs/src/Guides/23 Voice.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Voice Support So you want to start playing some 🎵tunes🎶 in voice channels? Well let's get that going for you. diff --git a/docs/src/Guides/24 Localisation.md b/docs/src/Guides/24 Localisation.md index 43dfa1cc0..f29d8317a 100644 --- a/docs/src/Guides/24 Localisation.md +++ b/docs/src/Guides/24 Localisation.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Localising So your bot has grown, and now you need to ~~localize~~ localise your bot. Well thank god we support localisation then, huh? diff --git a/docs/src/Guides/25 Error Tracking.md b/docs/src/Guides/25 Error Tracking.md index 14b7ffca2..5a348c6e0 100644 --- a/docs/src/Guides/25 Error Tracking.md +++ b/docs/src/Guides/25 Error Tracking.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Error Tracking So, you've finally got your bot running on a server somewhere. Chances are, you're not checking the console output 24/7, looking for exceptions. diff --git a/docs/src/Guides/26 Prefixed Commands.md b/docs/src/Guides/26 Prefixed Commands.md index e58d24b15..d1c2a4d99 100644 --- a/docs/src/Guides/26 Prefixed Commands.md +++ b/docs/src/Guides/26 Prefixed Commands.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Creating Prefixed Commands Prefixed commands, called by Discord as "text commands" and sometimes called "message commands" (not to be confused with Context Menu Message Commands), are commands that are triggered when a user sends a normal message with a designated "prefix" in front of them. diff --git a/docs/src/Guides/30 Pagination.md b/docs/src/Guides/30 Pagination.md index 21ed4c5a3..c0e1101a5 100644 --- a/docs/src/Guides/30 Pagination.md +++ b/docs/src/Guides/30 Pagination.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Pagination > Pagination, also known as paging, is the process of dividing a document into discrete pages, either electronic pages or printed pages. diff --git a/docs/src/Guides/40 Tasks.md b/docs/src/Guides/40 Tasks.md index f7efeb912..6310d4e24 100644 --- a/docs/src/Guides/40 Tasks.md +++ b/docs/src/Guides/40 Tasks.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Tasks Tasks are background processes that can be used to asynchronously run code with a specified trigger. diff --git a/docs/src/Guides/80 Sharding.md b/docs/src/Guides/80 Sharding.md index 0fafb64d9..9fb636f66 100644 --- a/docs/src/Guides/80 Sharding.md +++ b/docs/src/Guides/80 Sharding.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Sharding Oh damn, your bot is getting pretty big, huh? Well I guess its time we discuss sharding. diff --git a/docs/src/Guides/90 Example.md b/docs/src/Guides/90 Example.md index 70690371f..0f1ab9dce 100644 --- a/docs/src/Guides/90 Example.md +++ b/docs/src/Guides/90 Example.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Examples ## `main.py` diff --git a/docs/src/Guides/97 Migration From D.py.md b/docs/src/Guides/97 Migration From D.py.md index c5d67fff6..e1b86576f 100644 --- a/docs/src/Guides/97 Migration From D.py.md +++ b/docs/src/Guides/97 Migration From D.py.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Migrating from discord.py 1. interactions.py requires python 3.10 (as compared to dpy's 3.5), you may need to upgrade python. diff --git a/docs/src/Guides/98 Migration from 4.X.md b/docs/src/Guides/98 Migration from 4.X.md index c37bf2804..398c8d152 100644 --- a/docs/src/Guides/98 Migration from 4.X.md +++ b/docs/src/Guides/98 Migration from 4.X.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Migrating from 4.X Version 5.X (and beyond) is a major rewrite of interactions.py compared to 4.X, though there have been major improvements to compensate for the change. 5.X was designed to be more stable and flexible, solving many of the bugs and UX issues 4.X had while also adding additional features you may like. diff --git a/docs/src/Guides/99 2.x Migration_NAFF.md b/docs/src/Guides/99 2.x Migration_NAFF.md index 9170a1156..d873d4c51 100644 --- a/docs/src/Guides/99 2.x Migration_NAFF.md +++ b/docs/src/Guides/99 2.x Migration_NAFF.md @@ -1,3 +1,8 @@ +--- +search: + boost: 3 +--- + # Migrating from NAFF Oh hey! So you're migrating from NAFF to interactions.py? Well lets get you sorted. diff --git a/docs/src/Guides/index.md b/docs/src/Guides/index.md index a02ced783..0209e6ebe 100644 --- a/docs/src/Guides/index.md +++ b/docs/src/Guides/index.md @@ -1,3 +1,8 @@ +--- +search: + exclude: true +--- + Let's be honest; reading API documentation is a bit of a pain. These guides are meant to help you get started with the library and offer a point of reference. diff --git a/docs/src/index.md b/docs/src/index.md index cc1b5aeca..85dcc199c 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -3,6 +3,8 @@ hide: - navigation - toc - feedback +search: + exclude: true --- We hope this documentation is helpful for you, but don't just ++ctrl+c++ and ++ctrl+v++. From f04957090e592d5049caf1e4ded6cefe4ffa2c6e Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:36:39 -0500 Subject: [PATCH 16/21] feat: add Client.mention_command (#1579) * feat: add Client.mention_command This makes it much easier to mention a command if you don't know how to use bot internals. * docs: remove pointless typehint notes --- interactions/client/client.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/interactions/client/client.py b/interactions/client/client.py index 9ce1d7d30..96cdef32b 100644 --- a/interactions/client/client.py +++ b/interactions/client/client.py @@ -2461,6 +2461,19 @@ def get_bot_voice_state(self, guild_id: "Snowflake_Type") -> Optional[ActiveVoic """ return self._connection_state.get_voice_state(guild_id) + def mention_command(self, name: str, scope: int = 0) -> str: + """ + Returns a string that would mention the interaction specified. + + Args: + name: The name of the interaction. + scope: The scope of the interaction. Defaults to 0, the global scope. + + Returns: + str: The interaction's mention in the specified scope. + """ + return self.interactions_by_scope[scope][name].mention(scope) + async def change_presence( self, status: Optional[Union[str, Status]] = Status.ONLINE, From 15a72cda89dc67f1d441705885d5dea96f101aee Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:36:54 -0500 Subject: [PATCH 17/21] fix: ensure next_fire in TimeTrigger returns proper next time (#1575) * fix: ensure time trigger target is greater than last call time * refactor: adjust fix to be more "proper" * docs: document the weird fix --- interactions/models/internal/tasks/triggers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interactions/models/internal/tasks/triggers.py b/interactions/models/internal/tasks/triggers.py index ab4bc89ae..1c1f3a13e 100644 --- a/interactions/models/internal/tasks/triggers.py +++ b/interactions/models/internal/tasks/triggers.py @@ -105,7 +105,11 @@ def next_fire(self) -> datetime | None: ) if target.tzinfo == timezone.utc: target = target.astimezone(now.tzinfo) - target = target.replace(tzinfo=None) + # target can fall behind or go forward a day, but all we need is the time itself + # to be converted + # to ensure it's on the same day as "now" and not break the next if statement, + # we can just replace the date with now's date + target = target.replace(year=now.year, month=now.month, day=now.day, tzinfo=None) if target <= self.last_call_time: target += timedelta(days=1) From d29a03fb674e7987d0d68182cebaded4a312c9cd Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:37:29 -0500 Subject: [PATCH 18/21] fix: pass binding for prefixed variant of hybrid cmd (#1573) --- interactions/ext/hybrid_commands/hybrid_slash.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interactions/ext/hybrid_commands/hybrid_slash.py b/interactions/ext/hybrid_commands/hybrid_slash.py index 1a6e3f1ab..b367cf80b 100644 --- a/interactions/ext/hybrid_commands/hybrid_slash.py +++ b/interactions/ext/hybrid_commands/hybrid_slash.py @@ -327,6 +327,10 @@ def slash_to_prefixed(cmd: HybridSlashCommand) -> _HybridToPrefixedCommand: # n if cmd.aliases: prefixed_cmd.aliases.extend(cmd.aliases) + # copy over binding from slash command, if any + # can't be done in init due to how _binding works + prefixed_cmd._binding = cmd._binding + if not cmd.dm_permission: prefixed_cmd.add_check(guild_only()) From 633da191b5e5bb9f78a4066ee1997197f880a84b Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:38:32 -0500 Subject: [PATCH 19/21] fix: adjust and fix various issues with scheduled events (#1547) --- .../api/events/processors/scheduled_events.py | 18 ++++++----- .../http/http_requests/scheduled_events.py | 4 +-- interactions/client/smart_cache.py | 32 +++++++++++++++++++ interactions/models/discord/guild.py | 28 +++++++++++++--- .../models/discord/scheduled_event.py | 2 +- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/interactions/api/events/processors/scheduled_events.py b/interactions/api/events/processors/scheduled_events.py index 4993c1ca2..afc46aa8d 100644 --- a/interactions/api/events/processors/scheduled_events.py +++ b/interactions/api/events/processors/scheduled_events.py @@ -37,14 +37,16 @@ async def _on_raw_guild_scheduled_event_delete(self, event: "RawGatewayEvent") - @Processor.define() async def _on_raw_guild_scheduled_event_user_add(self, event: "RawGatewayEvent") -> None: - scheduled_event = self.cache.get_scheduled_event(event.data.get("guild_scheduled_event_id")) - user = self.cache.get_user(event.data.get("user_id")) - - self.dispatch(events.GuildScheduledEventUserAdd(event.data.get("guild_id"), scheduled_event, user)) + self.dispatch( + events.GuildScheduledEventUserAdd( + event.data["guild_id"], event.data["guild_scheduled_event_id"], event.data["user_id"] + ) + ) @Processor.define() async def _on_raw_guild_scheduled_event_user_remove(self, event: "RawGatewayEvent") -> None: - scheduled_event = self.cache.get_scheduled_event(event.data.get("guild_scheduled_event_id")) - user = self.cache.get_user(event.data.get("user_id")) - - self.dispatch(events.GuildScheduledEventUserRemove(event.data.get("guild_id"), scheduled_event, user)) + self.dispatch( + events.GuildScheduledEventUserRemove( + event.data["guild_id"], event.data["guild_scheduled_event_id"], event.data["user_id"] + ) + ) diff --git a/interactions/api/http/http_requests/scheduled_events.py b/interactions/api/http/http_requests/scheduled_events.py index 7649ca220..abee75bfa 100644 --- a/interactions/api/http/http_requests/scheduled_events.py +++ b/interactions/api/http/http_requests/scheduled_events.py @@ -58,11 +58,11 @@ async def get_scheduled_event( return await self.request( Route( "GET", - "/guilds/{guild_id}/scheduled-events/{scheduled_event_id}", + "/guilds/{guild_id}/scheduled-events/{scheduled_event_id}?with_user_count={with_user_count}", guild_id=guild_id, scheduled_event_id=scheduled_event_id, + with_user_count=with_user_count, ), - params={"with_user_count": with_user_count}, ) async def create_scheduled_event( diff --git a/interactions/client/smart_cache.py b/interactions/client/smart_cache.py index 575f69741..799ae619d 100644 --- a/interactions/client/smart_cache.py +++ b/interactions/client/smart_cache.py @@ -924,6 +924,38 @@ def get_scheduled_event(self, scheduled_event_id: "Snowflake_Type") -> Optional[ """ return self.scheduled_events_cache.get(to_snowflake(scheduled_event_id)) + async def fetch_scheduled_event( + self, + guild_id: "Snowflake_Type", + scheduled_event_id: "Snowflake_Type", + with_user_count: bool = False, + *, + force: bool = False, + ) -> "ScheduledEvent": + """ + Fetch a scheduled event based on the guild and its own ID. + + Args: + guild_id: The ID of the guild this event belongs to + scheduled_event_id: The ID of the event + with_user_count: Whether to include the user count in the response. + force: If the cache should be ignored, and the event should be fetched from the API + + Returns: + The scheduled event if found + """ + if not force: + if scheduled_event := self.get_scheduled_event(scheduled_event_id): + if int(scheduled_event._guild_id) == int(guild_id) and ( + not with_user_count or scheduled_event.user_count is not MISSING + ): + return scheduled_event + + scheduled_event_data = await self._client.http.get_scheduled_event( + guild_id, scheduled_event_id, with_user_count=with_user_count + ) + return self.place_scheduled_event_data(scheduled_event_data) + def place_scheduled_event_data(self, data: discord_typings.GuildScheduledEventData) -> "ScheduledEvent": """ Take json data representing a scheduled event, process it, and cache it. diff --git a/interactions/models/discord/guild.py b/interactions/models/discord/guild.py index 5551f2bf7..3b2e780be 100644 --- a/interactions/models/discord/guild.py +++ b/interactions/models/discord/guild.py @@ -1259,27 +1259,45 @@ async def list_scheduled_events(self, with_user_count: bool = False) -> List["mo scheduled_events_data = await self._client.http.list_schedules_events(self.id, with_user_count) return [self._client.cache.place_scheduled_event_data(data) for data in scheduled_events_data] + def get_scheduled_event(self, scheduled_event_id: Snowflake_Type) -> Optional["models.ScheduledEvent"]: + """ + Gets a scheduled event from the cache by id. + + Args: + scheduled_event_id: The id of the scheduled event. + + Returns: + The scheduled event. If the event does not exist, returns None. + + """ + event = self._client.cache.get_scheduled_event(scheduled_event_id) + return None if event and int(event._guild_id) != self.id else event + async def fetch_scheduled_event( - self, scheduled_event_id: Snowflake_Type, with_user_count: bool = False + self, + scheduled_event_id: Snowflake_Type, + with_user_count: bool = False, + *, + force: bool = False, ) -> Optional["models.ScheduledEvent"]: """ - Get a scheduled event by id. + Fetches a scheduled event by id. Args: scheduled_event_id: The id of the scheduled event. with_user_count: Whether to include the user count in the response. + force: If the cache should be ignored, and the event should be fetched from the API Returns: The scheduled event. If the event does not exist, returns None. """ try: - scheduled_event_data = await self._client.http.get_scheduled_event( - self.id, scheduled_event_id, with_user_count + return await self._client.cache.fetch_scheduled_event( + self.id, scheduled_event_id, with_user_count=with_user_count, force=force ) except NotFound: return None - return self._client.cache.place_scheduled_event_data(scheduled_event_data) async def create_scheduled_event( self, diff --git a/interactions/models/discord/scheduled_event.py b/interactions/models/discord/scheduled_event.py index a958ce4f5..2f2837299 100644 --- a/interactions/models/discord/scheduled_event.py +++ b/interactions/models/discord/scheduled_event.py @@ -49,7 +49,7 @@ class ScheduledEvent(DiscordObject): """The id of an entity associated with a guild scheduled event""" entity_metadata: Optional[Dict[str, Any]] = attrs.field(repr=False, default=MISSING) # TODO make this """The metadata associated with the entity_type""" - user_count: int = attrs.field(repr=False, default=MISSING) + user_count: Absent[int] = attrs.field(repr=False, default=MISSING) # TODO make this optional and None in 6.0 """Amount of users subscribed to the scheduled event""" cover: Asset | None = attrs.field(repr=False, default=None) """The cover image of this event""" From a9147e53e0853fed2a189b681bc99b6f84b8a893 Mon Sep 17 00:00:00 2001 From: Lendy Mulot <56113041+ZeGmX@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:38:54 +0100 Subject: [PATCH 20/21] fix: Paginator.create_from_list no longer misses overflowing strings (#1574) --- interactions/ext/paginators.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interactions/ext/paginators.py b/interactions/ext/paginators.py index 83f881329..07f345d06 100644 --- a/interactions/ext/paginators.py +++ b/interactions/ext/paginators.py @@ -245,11 +245,13 @@ def create_from_list( pages = [] page = "" for entry in content: + if len(entry) > page_size: + continue if len(page) + len(f"\n{entry}") <= page_size: page += f"{entry}\n" else: pages.append(Page(page, prefix=prefix, suffix=suffix)) - page = "" + page = "f{entry}\n" if page != "": pages.append(Page(page, prefix=prefix, suffix=suffix)) return cls(client, pages=pages, timeout_interval=timeout) From 6898459e57971ba2abbcbf24a03f1d8761356291 Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Thu, 30 Nov 2023 16:39:58 +0000 Subject: [PATCH 21/21] chore: Version Bump Signed-off-by: LordOfPolls --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1d28bd18b..903e92f06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "interactions.py" -version = "5.10.0" +version = "5.11.0" description = "Easy, simple, scalable and modular: a Python API wrapper for interactions." authors = [ "LordOfPolls ",