Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions interactions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
CustomEmojiConverter,
DateTrigger,
DefaultNotificationLevel,
DefaultReaction,
DM,
dm_only,
DMChannel,
Expand Down Expand Up @@ -233,12 +234,14 @@
process_color,
process_colour,
process_components,
process_default_reaction,
process_embeds,
process_emoji,
process_emoji_req_format,
process_message_payload,
process_message_reference,
process_permission_overwrites,
process_thread_tag,
Reaction,
ReactionUsers,
Resolved,
Expand Down Expand Up @@ -425,6 +428,7 @@
"CustomEmojiConverter",
"DateTrigger",
"DefaultNotificationLevel",
"DefaultReaction",
"DISCORD_EPOCH",
"DM",
"dm_only",
Expand Down Expand Up @@ -564,12 +568,14 @@
"process_color",
"process_colour",
"process_components",
"process_default_reaction",
"process_embeds",
"process_emoji",
"process_emoji_req_format",
"process_message_payload",
"process_message_reference",
"process_permission_overwrites",
"process_thread_tag",
"Reaction",
"ReactionUsers",
"Resolved",
Expand Down
6 changes: 6 additions & 0 deletions interactions/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
ComponentType,
CustomEmoji,
DefaultNotificationLevel,
DefaultReaction,
DM,
DMChannel,
DMGroup,
Expand Down Expand Up @@ -117,12 +118,14 @@
process_color,
process_colour,
process_components,
process_default_reaction,
process_embeds,
process_emoji,
process_emoji_req_format,
process_message_payload,
process_message_reference,
process_permission_overwrites,
process_thread_tag,
Reaction,
ReactionUsers,
Role,
Expand Down Expand Up @@ -373,6 +376,7 @@
"CustomEmojiConverter",
"DateTrigger",
"DefaultNotificationLevel",
"DefaultReaction",
"DM",
"dm_only",
"DMChannel",
Expand Down Expand Up @@ -493,12 +497,14 @@
"process_color",
"process_colour",
"process_components",
"process_default_reaction",
"process_embeds",
"process_emoji",
"process_emoji_req_format",
"process_message_payload",
"process_message_reference",
"process_permission_overwrites",
"process_thread_tag",
"Reaction",
"ReactionUsers",
"Resolved",
Expand Down
5 changes: 4 additions & 1 deletion interactions/models/discord/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
from .stage_instance import StageInstance
from .sticker import Sticker, StickerItem, StickerPack
from .team import Team, TeamMember
from .thread import ThreadList, ThreadMember, ThreadTag
from .thread import ThreadList, ThreadMember, ThreadTag, DefaultReaction, process_thread_tag, process_default_reaction
from .timestamp import Timestamp, TimestampStyles
from .user import BaseUser, Member, User, ClientUser
from .voice_state import VoiceRegion, VoiceState
Expand Down Expand Up @@ -211,6 +211,7 @@
"ComponentType",
"CustomEmoji",
"DefaultNotificationLevel",
"DefaultReaction",
"DM",
"DMChannel",
"DMGroup",
Expand Down Expand Up @@ -284,12 +285,14 @@
"process_color",
"process_colour",
"process_components",
"process_default_reaction",
"process_embeds",
"process_emoji",
"process_emoji_req_format",
"process_message_payload",
"process_message_reference",
"process_permission_overwrites",
"process_thread_tag",
"Reaction",
"ReactionUsers",
"Role",
Expand Down
4 changes: 3 additions & 1 deletion interactions/models/discord/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
to_optional_snowflake,
SnowflakeObject,
)
from interactions.models.discord.thread import ThreadTag
from interactions.models.discord.thread import DefaultReaction, ThreadTag
from interactions.models.misc.context_manager import Typing
from interactions.models.misc.iterator import AsyncIterator
from .enums import (
Expand Down Expand Up @@ -2387,6 +2387,8 @@ async def close_stage(self, reason: Absent[Optional[str]] = MISSING) -> None:
class GuildForum(GuildChannel):
available_tags: List[ThreadTag] = attrs.field(repr=False, factory=list)
"""A list of tags available to assign to threads"""
default_reaction_emoji: Optional[DefaultReaction] = attrs.field(repr=False, default=None)
"""The default emoji to react with for posts"""
last_message_id: Optional[Snowflake_Type] = attrs.field(repr=False, default=None)
# TODO: Implement "template" once the API supports them

Expand Down
8 changes: 7 additions & 1 deletion interactions/models/discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from interactions.client.const import Absent, MISSING, PREMIUM_GUILD_LIMITS
from interactions.client.errors import EventLocationNotProvided, NotFound
from interactions.client.mixins.serialization import DictSerializationMixin
from interactions.client.utils.attr_converters import optional
from interactions.client.utils.attr_converters import optional, list_converter
from interactions.client.utils.attr_converters import timestamp_converter
from interactions.client.utils.attr_utils import docs
from interactions.client.utils.deserialise_app_cmds import deserialize_app_cmds
Expand Down Expand Up @@ -1011,6 +1011,8 @@ async def create_forum_channel(
category: Union[Snowflake_Type, "models.GuildCategory"] = None,
nsfw: bool = False,
rate_limit_per_user: int = 0,
default_reaction_emoji: Absent[Union[dict, "models.PartialEmoji", "models.DefaultReaction", str]] = MISSING,
available_tags: Absent["list[dict | models.ThreadTag] | dict | models.ThreadTag"] = MISSING,
layout: ForumLayoutType = ForumLayoutType.NOT_SET,
reason: Absent[Optional[str]] = MISSING,
) -> "models.GuildForum":
Expand All @@ -1025,6 +1027,8 @@ async def create_forum_channel(
category: The category this forum channel should be within
nsfw: Should this forum be marked nsfw
rate_limit_per_user: The time users must wait between sending messages
default_reaction_emoji: The default emoji to react with when creating a thread
available_tags: The available tags for this forum channel
layout: The layout of the forum channel
reason: The reason for creating this channel

Expand All @@ -1041,6 +1045,8 @@ async def create_forum_channel(
category=category,
nsfw=nsfw,
rate_limit_per_user=rate_limit_per_user,
default_reaction_emoji=models.process_default_reaction(default_reaction_emoji),
available_tags=list_converter(models.process_thread_tag)(available_tags) if available_tags else MISSING,
default_forum_layout=layout,
reason=reason,
)
Expand Down
106 changes: 104 additions & 2 deletions interactions/models/discord/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

import interactions.models as models
from interactions.client.const import MISSING
from interactions.client.mixins.serialization import DictSerializationMixin
from interactions.client.mixins.send import SendMixin
from interactions.client.utils.attr_converters import optional
from interactions.client.utils.attr_converters import timestamp_converter
from interactions.models.discord.emoji import PartialEmoji
from interactions.models.discord.emoji import PartialEmoji, process_emoji
from interactions.models.discord.snowflake import to_snowflake
from interactions.models.discord.timestamp import Timestamp
from .base import DiscordObject, ClientObject
Expand All @@ -25,6 +26,9 @@
"ThreadMember",
"ThreadList",
"ThreadTag",
"DefaultReaction",
"process_thread_tag",
"process_default_reaction",
)


Expand Down Expand Up @@ -122,11 +126,46 @@ class ThreadTag(DiscordObject):
name: str = attrs.field(
repr=False,
)
emoji_id: "Snowflake_Type" = attrs.field(repr=False, default=None)
moderated: bool = attrs.field(repr=False)
emoji_id: "Snowflake_Type | None" = attrs.field(repr=False, default=None)
emoji_name: str | None = attrs.field(repr=False, default=None)

_parent_channel_id: "Snowflake_Type" = attrs.field(repr=False, default=MISSING)

@classmethod
def create(
cls,
name: str,
*,
moderated: bool = False,
emoji: Union["models.PartialEmoji", dict, str, None] = None,
) -> "ThreadTag":
"""
Create a new thread tag - this is useful if you're making a new forum

!!! warning
This does not create the tag on Discord, it only creates a local object
Do not expect the tag to contain valid values or for its methods to work

Args:
name: The name for this tag
moderated: Whether this tag is moderated
emoji: The emoji for this tag

Returns:
This object
"""
if emoji := models.process_emoji(emoji):
return cls(
client=None,
moderated=moderated,
id=0,
name=name,
emoji_id=emoji.get("id"),
emoji_name=emoji.get("name"),
)
return cls(client=None, moderated=moderated, id=0, name=name)

@property
def parent_channel(self) -> "GuildForum":
"""The parent forum for this tag."""
Expand Down Expand Up @@ -170,3 +209,66 @@ async def delete(self) -> None:
"""Delete this tag."""
data = await self._client.http.delete_tag(self._parent_channel_id, self.id)
self._client.cache.place_channel_data(data)


@attrs.define(eq=False, order=False, hash=False, kw_only=True)
class DefaultReaction(DictSerializationMixin):
"""Represents a default reaction for a forum."""

emoji_id: "Snowflake_Type | None" = attrs.field(default=None)
emoji_name: str | None = attrs.field(default=None)

@classmethod
def from_emoji(cls, emoji: PartialEmoji) -> "DefaultReaction":
"""Create a default reaction from an emoji."""
if emoji.id:
return cls(emoji_id=emoji.id)
return cls(emoji_name=emoji.name)


def process_thread_tag(tag: Optional[dict | ThreadTag]) -> Optional[dict]:
"""
Processes the tag parameter into the dictionary format required by the API.

Args:
tag: The tag to process

Returns:
formatted dictionary for discrd
"""
if not tag:
return tag

if isinstance(tag, ThreadTag):
return tag.to_dict()

if isinstance(tag, dict):
return tag

raise ValueError(f"Invalid tag: {tag}")


def process_default_reaction(reaction: Optional[dict | DefaultReaction | PartialEmoji | str]) -> Optional[dict]:
"""
Processes the reaction parameter into the dictionary format required by the API.

Args:
reaction: The reaction to process.

Returns:
formatted dictionary for discrd
"""
if not reaction:
return reaction

if isinstance(reaction, dict):
return reaction

if not isinstance(reaction, DefaultReaction):
emoji = process_emoji(reaction)
if emoji_id := emoji.get("id"):
reaction = DefaultReaction(emoji_id=emoji_id)
else:
reaction = DefaultReaction(emoji_name=emoji["name"])

return reaction.to_dict()