Passed
Pull Request — main (#157)
by
unknown
01:23
created

UserMessage.get_most_recent()   A

Complexity

Conditions 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
# Copyright Pincer 2021-Present
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
# Full MIT License can be found in `LICENSE` at the project root.
3
4
from __future__ import annotations
5
6
from dataclasses import dataclass
7
from enum import IntEnum, Enum
8
from typing import Any, Generator, List, Optional, Union, TYPE_CHECKING
9
10
from ..app.application import Application
11
from ..app.interaction_base import MessageInteraction
12
from ..guild.channel import Channel, ChannelMention
13
from ..guild.member import GuildMember, PartialGuildMember
14
from ..guild.role import Role
15
from ..message.attachment import Attachment
16
from ..message.component import MessageComponent
17
from ..message.embed import Embed
18
from ..message.reaction import Reaction
19
from ..message.reference import MessageReference
20
from ..message.sticker import StickerItem
21
from ..user import User
22
from ..._config import GatewayConfig
23
from ...utils.api_object import APIObject
24
from ...utils.conversion import construct_client_dict, convert
25
from ...utils.snowflake import Snowflake
26
from ...utils.timestamp import Timestamp
0 ignored issues
show
introduced by
Cannot import 'utils.timestamp' due to syntax error 'invalid syntax (<unknown>, line 70)'
Loading history...
Bug introduced by
The name timestamp does not seem to exist in module pincer.utils.
Loading history...
27
from ...utils.types import MISSING
28
29
if TYPE_CHECKING:
30
    from ...utils import APINullable
31
32
33
class MessageActivityType(IntEnum):
34
    """
35
    The activity people can perform on a rich presence activity.
36
37
    Such an activity could for example be a spotify listen.
38
    """
39
    JOIN = 1
40
    SPECTATE = 2
41
    LISTEN = 3
42
    JOIN_REQUEST = 5
43
44
45
class MessageFlags(IntEnum):
46
    """
47
    Special message properties.
48
49
    :param CROSSPOSTED:
50
        the message has been published to subscribed
51
        channels (via Channel Following)
52
53
    :param IS_CROSSPOST:
54
        this message originated from a message
55
        in another channel (via Channel Following)
56
57
    :param SUPPRESS_EMBEDS:
58
        do not include any embeds when serializing this message
59
60
    :param SOURCE_MESSAGE_DELETED:
61
        the source message for this crosspost
62
        has been deleted (via Channel Following)
63
64
    :param URGENT:
65
        this message came from the urgent message system
66
67
    :param HAS_THREAD:
68
        this message has an associated thread,
69
        with the same id as the message
70
71
    :param EPHEMERAL:
72
        this message is only visible to the user
73
        who invoked the Interaction
74
75
    :param LOADING:
76
        this message is an Interaction
77
        Response and the bot is "thinking"
78
    """
79
    CROSSPOSTED = 1 << 0
80
    IS_CROSSPOST = 1 << 1
81
    SUPPRESS_EMBEDS = 1 << 2
82
    SOURCE_MESSAGE_DELETED = 1 << 3
83
    URGENT = 1 << 4
84
    HAS_THREAD = 1 << 5
85
    EPHEMERAL = 1 << 6
86
    LOADING = 1 << 7
87
88
89
class MessageType(IntEnum):
90
    """
91
    Represents the type of the message.
92
    """
93
    DEFAULT = 0
94
    RECIPIENT_ADD = 1
95
    RECIPIENT_REMOVE = 2
96
    CALL = 3
97
    CHANNEL_NAME_CHANGE = 4
98
    CHANNEL_ICON_CHANGE = 5
99
    CHANNEL_PINNED_MESSAGE = 6
100
    GUILD_MEMBER_JOIN = 7
101
    USER_PREMIUM_GUILD_SUBSCRIPTION = 8
102
    USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9
103
    USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10
104
    USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11
105
    CHANNEL_FOLLOW_ADD = 12
106
    GUILD_DISCOVERY_DISQUALIFIED = 14
107
    GUILD_DISCOVERY_REQUALIFIED = 15
108
    GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16
109
    GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17
110
    THREAD_CREATED = 18
111
    REPLY = 19
112
    APPLICATION_COMMAND = 20
113
114
    if GatewayConfig.version < 8:
115
        REPLY = 0
116
        APPLICATION_COMMAND = 0
117
118
    if GatewayConfig.version >= 9:
119
        THREAD_STARTER_MESSAGE = 21
120
121
    GUILD_INVITE_REMINDER = 22
122
123
124
@dataclass
125
class MessageActivity(APIObject):
126
    """
127
    Represents a Discord Message Activity object
128
129
    :param type:
130
        type of message activity
131
132
    :param party_id:
133
        party_id from a Rich Presence event
134
    """
135
    type: MessageActivityType
136
    party_id: APINullable[str] = MISSING
137
138
139
class AllowedMentionTypes(str, Enum):
140
    """
141
    The allowed mentions.
142
143
    :param ROLES:
144
        Controls role mentions
145
146
    :param USERS:
147
        Controls user mentions
148
149
    :param EVERYONE:
150
        Controls @everyone and @here mentions
151
    """
152
    ROLES = "roles"
153
    USERS = "users"
154
    EVERYONE = "everyone"
155
156
157
@dataclass
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
158
class AllowedMentions(APIObject):
159
    parse: List[AllowedMentionTypes]
160
    roles: List[Union[Role, Snowflake]]
161
    users: List[Union[User, Snowflake]]
162
    reply: bool = True
163
164
    def to_dict(self):
165
        def get_str_id(obj: Union[Snowflake, User, Role]) -> str:
166
            if hasattr(obj, "id"):
167
                obj = obj.id
168
169
            return str(obj)
170
171
        return {
172
            "parse": self.parse,
173
            "roles": list(map(get_str_id, self.roles)),
174
            "users": list(map(get_str_id, self.users)),
175
            "replied_user": self.reply
176
        }
177
178
179
@dataclass
0 ignored issues
show
best-practice introduced by
Too many instance attributes (30/7)
Loading history...
180
class UserMessage(APIObject):
181
    """
182
    Represents a message sent in a channel within Discord.
183
184
    :param id:
185
        id of the message
186
187
    :param channel_id:
188
        id of the channel the message was sent in
189
190
    :param guild_id:
191
        id of the guild the message was sent in
192
193
    :param author:
194
        the author of this message (not guaranteed to be a valid user)
195
196
    :param member:
197
        member properties for this message's author
198
199
    :param content:
200
        contents of the message
201
202
    :param timestamp:
203
        when this message was sent
204
205
    :param edited_timestamp:
206
        when this message was edited (or null if never)
207
208
    :param tts:
209
        whether this was a TTS message
210
211
    :param mention_everyone:
212
        whether this message mentions everyone
213
214
    :param mentions:
215
        users specifically mentioned in the message
216
217
    :param mention_roles:
218
        roles specifically mentioned in this message
219
220
    :param mention_channels:
221
        channels specifically mentioned in this message
222
223
    :param attachments:
224
        any attached files
225
226
    :param embeds:
227
        any embedded content
228
229
    :param reactions:
230
        reactions to the message
231
232
    :param nonce:
233
        user for validating a message was sent
234
235
    :param pinned:
236
        whether this message is pinned
237
238
    :param webhook_id:
239
        if the message is generated by a webhook,
240
        this is the webhook's id
241
242
    :param type:
243
        type of message
244
245
    :param activity:
246
        sent with Rich Presence-related chat embeds
247
248
    :param application:
249
        sent with Rich Presence-related chat embeds
250
251
    :param application_id:
252
        if the message is a response to an Interaction,
253
        this is the id of the interaction's application
254
255
    :param message_reference:
256
        data showing the source of a crosspost,
257
        channel follow add, pin, or reply message
258
259
    :param flags:
260
        message flags combined as a bitfield
261
262
    :param referenced_message:
263
        the message associated with the message_reference
264
265
    :param interaction:
266
        sent if the message is a response to an Interaction
267
268
    :param thread:
269
        the thread that was started from this message,
270
        includes thread member object
271
272
    :param components:
273
        sent if the message contains components like buttons,
274
        action rows, or other interactive components
275
276
    :param sticker_items:
277
        sent if the message contains stickers
278
    """
279
    id: Snowflake
280
    channel_id: Snowflake
281
    author: User
282
    content: str
283
    timestamp: Timestamp
284
    tts: bool
285
    mention_everyone: bool
286
    mentions: List[GuildMember]
287
    mention_roles: List[Role]
288
    attachments: List[Attachment]
289
    embeds: List[Embed]
290
    pinned: bool
291
    type: MessageType
292
293
    edited_timestamp: APINullable[Timestamp] = MISSING
294
    mention_channels: APINullable[List[ChannelMention]] = MISSING
295
    guild_id: APINullable[Snowflake] = MISSING
296
    member: APINullable[PartialGuildMember] = MISSING
297
    reactions: APINullable[List[Reaction]] = MISSING
298
    nonce: APINullable[Union[int, str]] = MISSING
299
    webhook_id: APINullable[Snowflake] = MISSING
300
    activity: APINullable[MessageActivity] = MISSING
301
    application: APINullable[Application] = MISSING
302
    application_id: APINullable[Snowflake] = MISSING
303
    message_reference: APINullable[MessageReference] = MISSING
304
    flags: APINullable[MessageFlags] = MISSING
305
    referenced_message: APINullable[Optional[UserMessage]] = MISSING
306
    interaction: APINullable[MessageInteraction] = MISSING
307
    thread: APINullable[Channel] = MISSING
308
    components: APINullable[List[MessageComponent]] = MISSING
309
    sticker_items: APINullable[List[StickerItem]] = MISSING
310
311
    def __post_init__(self):
312
        self.id = convert(self.id, Snowflake.from_string)
313
        self.channel_id = convert(self.channel_id, Snowflake.from_string)
314
        self.author = convert(self.author, User.from_dict, client=self._client)
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _client.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
315
        self.timestamp = convert(self.timestamp, Timestamp)
316
        self.edited_timestamp = convert(self.edited_timestamp, Timestamp)
317
        self.mentions = convert(self.mentions, PartialGuildMember.from_dict,
318
                                client=self._client)
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _client.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
319
        self.mention_roles = convert(self.mention_roles, Role.from_dict)
320
        self.attachments = convert(self.attachments, Attachment.from_dict)
321
        self.embeds = convert(self.embeds, Embed.from_dict)
322
        self.mention_channels = convert(
323
            self.mention_channels,
324
            ChannelMention.from_dict
325
        )
326
        self.guild_id = convert(self.guild_id, Snowflake.from_string)
327
        self.member = convert(self.member, GuildMember.from_dict,
328
                              client=self._client)
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _client.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
329
        self.reactions = convert(self.reactions, Reaction.from_dict)
330
        self.webhook_id = convert(self.webhook_id, Snowflake.from_string)
331
        self.activity = convert(self.activity, MessageActivity.from_dict)
332
        self.application = convert(self.application, Application.from_dict)
333
        self.application_id = convert(
334
            self.application_id,
335
            Snowflake.from_string
336
        )
337
        self.message_reference = convert(
338
            self.message_reference,
339
            MessageReference.from_dict
340
        )
341
        # self.flags = convert(self.flags, MessageFlags.from_bytes)
342
        # self.referenced_message = convert(
343
        #     self.referenced_message,
344
        #     Message.from_dict
345
        # )
346
        self.interaction = convert(
347
            self.interaction,
348
            MessageInteraction.from_dict
349
        )
350
        self.thread = convert(self.thread, Channel.from_dict,
351
                              client=self._client)
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _client.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
352
        self.components = convert(self.components, MessageComponent.from_dict)
353
        self.sticker_items = convert(self.sticker_items, StickerItem.from_dict)
354
355
    async def get_most_recent(self):
356
        """
357
        Certain Discord methods don't return the message object data after its
358
        updated. This function can be run to get the most recent version of the
359
        message object.
360
        """
361
362
        return self.from_dict(
363
            construct_client_dict(
364
                self._client,
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _client.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
365
                await self._http.get(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
366
                    f"/channels/{self.channel_id}/messages/{self.id}"
367
                )
368
            )
369
        )
370
371
    async def add_reaction(self, emoji: str):
372
        """
373
        Create a reaction for the message. Requires the
374
        ``READ_MESSAGE_HISTORY` itent. ``ADD_REACTIONS`` intent is required if
375
        nobody else has reacted using the emoji.
376
377
        :param emoji:
378
            Character for emoji. Does not need to be URL encoded.
379
        """
380
381
        await self._http.put(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
382
            f"/channels/{self.channel_id}/messages/{self.id}/reactions/{emoji}/@me"
383
        )
384
385
    async def delete_own_reaction(self, emoji: str):
386
        """
387
        Delete a reaction the current user has made for the message.
388
389
        :param emoji:
390
            Character for emoji. Does not need to be URL encoded.
391
        """
392
393
        await self._http.delete(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
394
            f"/channels/{self.channel_id}/messages/{self.id}/reactions/{emoji}/@me"
395
        )
396
397
    async def delete_user_reaction(self, emoji: str, user_id: Snowflake):
398
        """
399
        Deletes another user's reaction. Requires the ``MANAGE_MESSAGES``
400
        intent.
401
402
        :param emoji:
403
            Character for emoji. Does not need to be URL encoded.
404
405
        :param user_id:
406
            User ID
407
        """
408
409
        await self._http.delete(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
410
            f"/channels/{self.channel_id}/messages/{self.id}/reactions/{emoji}"
411
            f"/{user_id}"
412
        )
413
414
    async def get_reactions(
415
        self, emoji: str, after: Snowflake = 0, limit=25
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
416
    ) -> Generator[User, None, None]:
417
        # TODO: HTTP Client will need to refactored to allow parameters using aiohttp's system.
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
418
        """
419
        Returns the users that reacted with this emoji.
420
421
        :param after:
422
            Get users after this user ID. Returns all users if not provided.
423
424
        :param limit:
425
            Max number of users to return (1-100). 25 is not provided.
426
        """
427
428
        for user in await self._http.get(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
429
            f"/channels/{self.channel_id}/messages/{self.id}/reactions/{emoji}"
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
430
            f"?after={after}&limit={limit}"
431
        ):
432
            yield User.from_dict(user)
433
434
    async def delete_all_reactions(self):
435
        """
436
        Delete all reactions on a message. Requires the ``MANAGE_MESSAGES``
437
        intent.
438
        """
439
440
        await self._http.delete(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
441
            f"/channels/{self.channel_id}/messages/{self.id}/reactions"
442
        )
443
444
    async def delete_emoji(self, emoji):
445
        """
446
        Deletes all the reactions for a given emoji on a message. Requires the
447
        ``MANAGE_MESSAGES`` intent.
448
449
        :param emoji:
450
            Character for emoji. Does not need to be URL encoded.
451
        """
452
453
        await self._http.delete(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
454
            f"/channels/{self.channel_id}/messages/{self.id}/reactions/{emoji}"
455
        )
456
457
    # TODO: Implement file (https://discord.com/developers/docs/resources/channel#edit-message)
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
458
    async def edit(
0 ignored issues
show
best-practice introduced by
Too many arguments (7/5)
Loading history...
459
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
460
        content: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
461
        embeds: List[Embed] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
462
        flags: int = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
463
        allowed_mentions: AllowedMentions = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
464
        attachments: List[Attachment] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
465
        components: List[MessageComponent] = None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
466
    ):
467
        """
468
        Edit a previously sent message. The fields content, embeds, and flags
469
        can be edited by the original message author. Other users can only
470
        edit flags and only if they have the ``MANAGE_MESSAGES`` permission in
471
        the corresponding channel. When specifying flags, ensure to include
472
        all previously set flags/bits in addition to ones that you are
473
        modifying.
474
475
        :param content:
476
            The message contents (up to 2000 characters)
477
478
        :param embeds:
479
            Embedded rich content (up to 6000 characters)
480
481
        :param flags:
482
            Edit the flags of a message (only ``SUPPRESS_EMBEDS`` can
483
            currently be set/unset)
484
485
        :param allowed_mentions:
486
            allowed mentions for the message
487
488
        :param attachments:
489
            attached files to keep
490
491
        :param components:
492
            the components to include with the message
493
        """
494
495
        data = {}
496
497
        def set_if_not_none(value: Any, name: str):
498
            if isinstance(value, APIObject):
499
                data[name] = value.to_dict()
500
            elif not value is None:
501
                data[name] = value
502
503
        set_if_not_none(content, "content")
504
        set_if_not_none(embeds, "embeds")
505
        set_if_not_none(flags, "flags")
506
        set_if_not_none(allowed_mentions, "allowed_mentions")
507
        set_if_not_none(attachments, "attachments")
508
        set_if_not_none(components, "components")
509
510
        await self._http.patch(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
511
            f"/channels/{self.channel_id}/messages/{self.id}",
512
            data=data
513
        )
514
515
    async def delete(self):
516
        """
517
        Delete a message. Requires the ``MANAGE_MESSAGES`` intent if the
518
        message was not sent by the current user.
519
        """
520
521
        await self._http.delete(
0 ignored issues
show
Bug introduced by
The Instance of UserMessage does not seem to have a member named _http.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
522
            f"/channels/{self.channel_id}/messages/{self.id}"
523
        )
524