Skip to content

Commit 236e8ec

Browse files
committed
feat: Add SharedClientTheme support for customizable message appearance
1 parent ec7419c commit 236e8ec

10 files changed

Lines changed: 310 additions & 1 deletion

File tree

discord/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
from .role import *
6969
from .scheduled_events import *
7070
from .shard import *
71+
from .shared_client_theme import *
7172
from .soundboard import *
7273
from .stage_instance import *
7374
from .sticker import *

discord/abc.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from .scheduled_events import ScheduledEvent
5959
from .sticker import GuildSticker, StickerItem
6060
from .utils import warn_deprecated
61+
from .shared_client_theme import SharedClientThemeBaseType
6162

6263
__all__ = (
6364
"Snowflake",
@@ -89,6 +90,7 @@
8990
from .member import Member
9091
from .message import Message, MessageReference, PartialMessage
9192
from .poll import Poll
93+
from .shared_client_theme import SharedClientTheme
9294
from .state import ConnectionState
9395
from .threads import Thread
9496
from .types.channel import Channel as ChannelPayload
@@ -1388,6 +1390,7 @@ async def send(
13881390
suppress: bool = ...,
13891391
suppress_embeds: bool = ...,
13901392
silent: bool = ...,
1393+
shared_client_theme: SharedClientTheme = ...,
13911394
) -> Message: ...
13921395

13931396
@overload
@@ -1410,6 +1413,7 @@ async def send(
14101413
suppress: bool = ...,
14111414
suppress_embeds: bool = ...,
14121415
silent: bool = ...,
1416+
shared_client_theme: SharedClientTheme = ...,
14131417
) -> Message: ...
14141418

14151419
@overload
@@ -1432,6 +1436,7 @@ async def send(
14321436
suppress: bool = ...,
14331437
suppress_embeds: bool = ...,
14341438
silent: bool = ...,
1439+
shared_client_theme: SharedClientTheme = ...,
14351440
) -> Message: ...
14361441

14371442
@overload
@@ -1454,6 +1459,7 @@ async def send(
14541459
suppress: bool = ...,
14551460
suppress_embeds: bool = ...,
14561461
silent: bool = ...,
1462+
shared_client_theme: SharedClientTheme = ...,
14571463
) -> Message: ...
14581464

14591465
async def send(
@@ -1477,6 +1483,7 @@ async def send(
14771483
suppress=None,
14781484
suppress_embeds=None,
14791485
silent=None,
1486+
shared_client_theme=None,
14801487
):
14811488
"""|coro|
14821489
@@ -1496,6 +1503,9 @@ async def send(
14961503
parameter should be used with a :class:`list` of :class:`~discord.Embed` objects.
14971504
**Specifying both parameters will lead to an exception**.
14981505
1506+
To upload a shared client theme, the ``shared_client_theme`` parameter should be used
1507+
with a :class:`~discord.SharedClientTheme` object.
1508+
14991509
Parameters
15001510
----------
15011511
content: Optional[:class:`str`]
@@ -1568,6 +1578,10 @@ async def send(
15681578
The poll to send.
15691579
15701580
.. versionadded:: 2.6
1581+
shared_client_theme: :class:`SharedClientTheme`
1582+
The shared client theme to send.
1583+
1584+
.. versionadded:: 2.8
15711585
15721586
Returns
15731587
-------
@@ -1585,13 +1599,35 @@ async def send(
15851599
you specified both ``file`` and ``files``,
15861600
or you specified both ``embed`` and ``embeds``,
15871601
or the ``reference`` object is not a :class:`~discord.Message`,
1588-
:class:`~discord.MessageReference` or :class:`~discord.PartialMessage`.
1602+
:class:`~discord.MessageReference` or :class:`~discord.PartialMessage`,
1603+
or the ``shared_client_theme`` object does not meet the required criteria.
15891604
"""
15901605

15911606
channel = await self._get_channel()
15921607
state = self._state
15931608
content = str(content) if content is not None else None
15941609

1610+
if shared_client_theme is not None:
1611+
if shared_client_theme.colors is None or len(shared_client_theme.colors) == 0:
1612+
raise InvalidArgument("shared_client_theme must have at least one color")
1613+
if len(shared_client_theme.colors) > 5:
1614+
raise InvalidArgument("shared_client_theme cannot have more than 5 colors")
1615+
1616+
if shared_client_theme.gradient_angle is None :
1617+
raise InvalidArgument("shared_client_theme must have a gradient angle")
1618+
if shared_client_theme.gradient_angle < 0 or shared_client_theme.gradient_angle > 360:
1619+
raise InvalidArgument("shared_client_theme gradient angle must be between 0 and 360 degrees")
1620+
1621+
if shared_client_theme.base_mix is None :
1622+
raise InvalidArgument("shared_client_theme must have a base mix value")
1623+
if shared_client_theme.base_mix < 0 or shared_client_theme.base_mix > 100 :
1624+
raise InvalidArgument("shared_client_theme base mix must be between 0 and 100")
1625+
1626+
if shared_client_theme.base_theme is not None and not isinstance(shared_client_theme.base_theme, SharedClientThemeBaseType):
1627+
raise InvalidArgument("shared_client_theme base theme must be a SharedClientThemeBaseType enum value")
1628+
1629+
shared_client_theme = shared_client_theme.to_dict()
1630+
15951631
if embed is not None and embeds is not None:
15961632
raise InvalidArgument(
15971633
"cannot pass both embed and embeds parameter to send()"
@@ -1706,6 +1742,7 @@ async def send(
17061742
components=components,
17071743
flags=flags.value,
17081744
poll=poll,
1745+
shared_client_theme=shared_client_theme,
17091746
)
17101747
finally:
17111748
for f in files:
@@ -1725,6 +1762,7 @@ async def send(
17251762
components=components,
17261763
flags=flags.value,
17271764
poll=poll,
1765+
shared_client_theme=shared_client_theme,
17281766
)
17291767

17301768
ret = state.create_message(channel=channel, data=data)

discord/enums.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"SelectDefaultValueType",
8989
"ApplicationEventWebhookStatus",
9090
"InviteTargetUsersJobStatusCode",
91+
"SharedClientThemeBaseType",
9192
)
9293

9394

@@ -1212,6 +1213,23 @@ class InviteTargetUsersJobStatusCode(Enum):
12121213
failed = 3
12131214

12141215

1216+
class SharedClientThemeBaseType(Enum):
1217+
"""The base theme mode of a :class:`SharedClientTheme`.
1218+
1219+
.. versionadded:: 2.8
1220+
1221+
.. note::
1222+
1223+
``unset`` is treated as equivalent to ``dark`` by Discord.
1224+
"""
1225+
1226+
unset = 0
1227+
dark = 1
1228+
light = 2
1229+
darker = 3
1230+
midnight = 4
1231+
1232+
12151233
T = TypeVar("T")
12161234

12171235

discord/http.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
poll,
8585
role,
8686
scheduled_events,
87+
)
88+
from .types import shared_client_theme as shared_client_theme_type
89+
from .types import (
8790
sticker,
8891
template,
8992
threads,
@@ -507,6 +510,7 @@ def send_message(
507510
components: list[components.Component] | None = None,
508511
flags: int | None = None,
509512
poll: poll.Poll | None = None,
513+
shared_client_theme: shared_client_theme_type.SharedClientTheme | None = None,
510514
) -> Response[message.Message]:
511515
r = Route("POST", "/channels/{channel_id}/messages", channel_id=channel_id)
512516
payload = {}
@@ -547,6 +551,9 @@ def send_message(
547551
if poll:
548552
payload["poll"] = poll
549553

554+
if shared_client_theme:
555+
payload["shared_client_theme"] = shared_client_theme
556+
550557
return self.request(r, json=payload)
551558

552559
def send_typing(self, channel_id: Snowflake) -> Response[None]:
@@ -571,6 +578,7 @@ def send_multipart_helper(
571578
components: list[components.Component] | None = None,
572579
flags: int | None = None,
573580
poll: poll.Poll | None = None,
581+
shared_client_theme: shared_client_theme_type.SharedClientTheme | None = None,
574582
) -> Response[message.Message]:
575583
form = []
576584

@@ -598,6 +606,9 @@ def send_multipart_helper(
598606
if poll:
599607
payload["poll"] = poll
600608

609+
if shared_client_theme:
610+
payload["shared_client_theme"] = shared_client_theme
611+
601612
attachments = []
602613
form.append({"name": "payload_json"})
603614
for index, file in enumerate(files):
@@ -641,6 +652,7 @@ def send_files(
641652
components: list[components.Component] | None = None,
642653
flags: int | None = None,
643654
poll: poll.Poll | None = None,
655+
shared_client_theme: shared_client_theme_type.SharedClientTheme | None = None,
644656
) -> Response[message.Message]:
645657
r = Route("POST", "/channels/{channel_id}/messages", channel_id=channel_id)
646658
return self.send_multipart_helper(
@@ -658,6 +670,7 @@ def send_files(
658670
components=components,
659671
flags=flags,
660672
poll=poll,
673+
shared_client_theme=shared_client_theme,
661674
)
662675

663676
def edit_multipart_helper(

discord/message.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from .partial_emoji import PartialEmoji
5959
from .poll import Poll
6060
from .reaction import Reaction
61+
from .shared_client_theme import SharedClientTheme
6162
from .sticker import StickerItem
6263
from .threads import Thread
6364
from .utils import MISSING, escape_mentions, find, warn_deprecated
@@ -90,6 +91,7 @@
9091
from .types.message import MessageSnapshot as MessageSnapshotPayload
9192
from .types.message import Reaction as ReactionPayload
9293
from .types.poll import Poll as PollPayload
94+
from .types.shared_client_theme import SharedClientTheme as SharedClientThemePayload
9395
from .types.snowflake import SnowflakeList
9496
from .types.threads import ThreadArchiveDuration
9597
from .types.user import User as UserPayload
@@ -1039,6 +1041,10 @@ class Message(Hashable):
10391041
The poll associated with this message, if applicable.
10401042
10411043
.. versionadded:: 2.6
1044+
shared_client_theme: Optional[:class:`SharedClientTheme`]
1045+
The shared client theme transmitted via this message, if applicable.
1046+
1047+
.. versionadded:: 2.8
10421048
call: Optional[:class:`MessageCall`]
10431049
The call information associated with this message, if applicable.
10441050
@@ -1087,6 +1093,7 @@ class Message(Hashable):
10871093
"_poll",
10881094
"call",
10891095
"snapshots",
1096+
"shared_client_theme",
10901097
)
10911098

10921099
if TYPE_CHECKING:
@@ -1217,6 +1224,14 @@ def __init__(
12171224
except KeyError:
12181225
self.call = None
12191226

1227+
self.shared_client_theme: SharedClientTheme | None
1228+
try:
1229+
self.shared_client_theme = SharedClientTheme.from_dict(
1230+
data["shared_client_theme"]
1231+
)
1232+
except KeyError:
1233+
self.shared_client_theme = None
1234+
12201235
for handler in ("author", "member", "mentions", "mention_roles"):
12211236
try:
12221237
getattr(self, f"_handle_{handler}")(data[handler])
@@ -1328,6 +1343,9 @@ def _handle_poll(self, value: PollPayload) -> None:
13281343
self._poll = Poll.from_dict(value, self)
13291344
self._state.store_poll(self._poll, self.id)
13301345

1346+
def _handle_shared_client_theme(self, value: SharedClientThemePayload) -> None:
1347+
self.shared_client_theme = SharedClientTheme.from_dict(value)
1348+
13311349
def _handle_author(self, author: UserPayload) -> None:
13321350
self.author = self._state.store_user(author)
13331351
if isinstance(self.guild, Guild):

0 commit comments

Comments
 (0)