Skip to content

Commit 93e6d26

Browse files
committed
feat: support new pin methods
Untested!
1 parent cb0ed98 commit 93e6d26

File tree

4 files changed

+126
-11
lines changed

4 files changed

+126
-11
lines changed

interactions/api/http/http_requests/channels.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, Optional, Sequence, cast, overload
1+
from typing import TYPE_CHECKING, Any, Optional, Sequence, cast, overload
22

33
import discord_typings
44

@@ -491,6 +491,17 @@ async def get_pinned_messages(self, channel_id: "Snowflake_Type") -> list[discor
491491
result = await self.request(Route("GET", "/channels/{channel_id}/pins", channel_id=channel_id))
492492
return cast(list[discord_typings.MessageData], result)
493493

494+
async def paginate_pinned_messages(
495+
self,
496+
channel_id: "Snowflake_Type",
497+
limit: int = 50,
498+
before: "Snowflake_Type | None" = None,
499+
) -> dict[str, Any]:
500+
return await self.request(
501+
Route("GET", "/channels/{channel_id}/messages/pins", channel_id=channel_id),
502+
params=dict_filter_none({"before": before, "limit": limit}),
503+
)
504+
494505
async def create_stage_instance(
495506
self,
496507
channel_id: "Snowflake_Type",

interactions/api/http/http_requests/messages.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,30 +103,51 @@ async def get_message(
103103
)
104104
return cast(discord_typings.MessageData, result)
105105

106-
async def pin_message(self, channel_id: "Snowflake_Type", message_id: "Snowflake_Type") -> None:
106+
async def pin_message(
107+
self,
108+
channel_id: "Snowflake_Type",
109+
message_id: "Snowflake_Type",
110+
reason: str | None = None,
111+
) -> None:
107112
"""
108113
Pin a message to a channel.
109114
110115
Args:
111116
channel_id: Channel to pin message to
112117
message_id: Message to pin
118+
reason: The reason for this action
113119
114120
"""
115121
await self.request(
116-
Route("PUT", "/channels/{channel_id}/pins/{message_id}", channel_id=channel_id, message_id=message_id)
122+
Route(
123+
"PUT", "/channels/{channel_id}/messages/pins/{message_id}", channel_id=channel_id, message_id=message_id
124+
),
125+
reason=reason,
117126
)
118127

119-
async def unpin_message(self, channel_id: "Snowflake_Type", message_id: "Snowflake_Type") -> None:
128+
async def unpin_message(
129+
self,
130+
channel_id: "Snowflake_Type",
131+
message_id: "Snowflake_Type",
132+
reason: str | None = None,
133+
) -> None:
120134
"""
121135
Unpin a message to a channel.
122136
123137
Args:
124138
channel_id: Channel to unpin message to
125139
message_id: Message to unpin
140+
reason: The reason for this action
126141
127142
"""
128143
await self.request(
129-
Route("DELETE", "/channels/{channel_id}/pins/{message_id}", channel_id=channel_id, message_id=message_id)
144+
Route(
145+
"DELETE",
146+
"/channels/{channel_id}/messages/pins/{message_id}",
147+
channel_id=channel_id,
148+
message_id=message_id,
149+
),
150+
reason=reason,
130151
)
131152

132153
async def edit_message(

interactions/models/discord/channel.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,43 @@ async def fetch(self) -> list["GuildForumPost"]:
164164
raise QueueEmpty
165165

166166

167+
class ChannelPins(AsyncIterator):
168+
def __init__(self, channel: "BaseChannel", limit: int = 50, before: Snowflake_Type = None) -> None:
169+
self.channel: "BaseChannel" = channel
170+
self.before: Snowflake_Type = before
171+
self._more: bool = True
172+
super().__init__(limit)
173+
174+
if self.before:
175+
self.last = self.before
176+
177+
@property
178+
def get_limit(self) -> int:
179+
"""Get how the maximum number of items that should be retrieved."""
180+
return min(self._limit - len(self._retrieved_objects), 50) if self._limit else 50
181+
182+
async def fetch(self) -> list["Message"]:
183+
if self._more:
184+
expected = self.get_limit
185+
186+
rcv = await self.channel._client.http.paginate_pinned_messages(
187+
self.channel.id,
188+
limit=expected,
189+
before=to_snowflake(self.last) if self.last else None,
190+
)
191+
messages = [
192+
self.channel._client.cache.place_message_data({"pinned_at": data["pinned_at"], **data["message"]})
193+
for data in rcv["items"]
194+
]
195+
196+
if not rcv:
197+
raise QueueEmpty
198+
199+
self._more = rcv.get("has_more", False)
200+
return messages
201+
raise QueueEmpty
202+
203+
167204
@attrs.define(eq=False, order=False, hash=False, kw_only=True)
168205
class PermissionOverwrite(SnowflakeObject, DictSerializationMixin):
169206
"""
@@ -366,8 +403,38 @@ async def fetch_pinned_messages(self) -> List["models.Message"]:
366403
A list of messages fetched.
367404
368405
"""
369-
messages_data = await self._client.http.get_pinned_messages(self.id)
370-
return [self._client.cache.place_message_data(message_data) for message_data in messages_data]
406+
return await ChannelPins(self, limit=0).flatten()
407+
408+
async def pinned_messages(
409+
self,
410+
limit: int = 50,
411+
before: Snowflake_Type = None,
412+
) -> ChannelPins:
413+
"""
414+
Get an async iterator for the pinned messages in this channel.
415+
416+
Args:
417+
limit: The maximum number of messages to return (set to 0 for no limit), max 50
418+
before: get messages before this message ID
419+
420+
??? Hint "Example Usage:"
421+
```python
422+
async for message in channel.pinned_messages(limit=0):
423+
if message.author.id == 174918559539920897:
424+
print("Found author's message")
425+
# ...
426+
break
427+
```
428+
or
429+
```python
430+
messages = await channel.pinned_messages(limit=50).flatten()
431+
```
432+
433+
Returns:
434+
ChannelPins (AsyncIterator)
435+
436+
"""
437+
return ChannelPins(self, limit=limit, before=before)
371438

372439
async def delete_messages(
373440
self,

interactions/models/discord/message.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,10 @@ class Message(BaseMessage):
424424
"""Used for validating a message was sent"""
425425
pinned: bool = attrs.field(repr=False, default=False)
426426
"""Whether this message is pinned"""
427+
pinned_at: Optional["models.Timestamp"] = attrs.field(
428+
repr=False, default=None, converter=optional_c(timestamp_converter)
429+
)
430+
"""When this message was pinned, only present when fetching pinned messages"""
427431
webhook_id: Optional["Snowflake_Type"] = attrs.field(repr=False, default=None, converter=to_optional_snowflake)
428432
"""If the message is generated by a webhook, this is the webhook's id"""
429433
type: MessageType = attrs.field(repr=False, default=MISSING, converter=optional_c(MessageType))
@@ -929,13 +933,25 @@ async def clear_all_reactions(self) -> None:
929933
"""Clear all emojis from a message."""
930934
await self._client.http.clear_reactions(self._channel_id, self.id)
931935

932-
async def pin(self) -> None:
933-
"""Pin message."""
936+
async def pin(self, reason: str | None = None) -> None:
937+
"""
938+
Pin message.
939+
940+
Args:
941+
reason: The optional reason for pinning this message
942+
943+
"""
934944
await self._client.http.pin_message(self._channel_id, self.id)
935945
self.pinned = True
936946

937-
async def unpin(self) -> None:
938-
"""Unpin message."""
947+
async def unpin(self, reason: str | None = None) -> None:
948+
"""
949+
Unpin message.
950+
951+
Args:
952+
reason: The optional reason for unpinning this message
953+
954+
"""
939955
await self._client.http.unpin_message(self._channel_id, self.id)
940956
self.pinned = False
941957

0 commit comments

Comments
 (0)