pincer.objects.guild.webhook   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 414
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 23
eloc 157
dl 0
loc 414
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A Webhook.execute_github() 0 20 1
A Webhook.edit() 0 35 3
A Webhook.execute_slack() 0 20 1
A Webhook.from_id() 0 24 2
A Webhook.edit_message() 0 41 3
A Webhook.delete() 0 12 2
A Webhook.delete_message() 0 14 2
A Webhook.get_message() 0 22 1
B Webhook.execute() 0 56 6
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 Enum, IntEnum
8
from typing import TYPE_CHECKING, overload
9
10
from ...exceptions import EmbedOverflow
11
from ...utils.api_object import APIObject
12
from ...utils.types import MISSING
13
14
if TYPE_CHECKING:
15
    from typing import List, Optional
16
17
    from ..message.attachment import Attachment
18
    from ..message.component import MessageComponent
19
    from ..message.embed import Embed
20
    from ..message.user_message import UserMessage
21
    from ..message.user_message import AllowedMentions
22
    from ..user.user import User
23
    from ..guild.guild import Guild
24
    from ..guild.channel import Channel
25
    from ...utils.types import APINullable
26
    from ...utils.snowflake import Snowflake
27
    from ...client import Client
28
29
30
class WebhookCompatibility(Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
31
    GitHub = "github"
32
    Slack = "slack"
33
    Default = ""
34
35
36
class WebhookType(IntEnum):
37
    """Represents the type of webhook.
38
39
    Attributes
40
    ----------
41
    INCOMING:
42
        Incoming Webhooks can post messages to channel with a
43
        generated token.
44
    CHANNEL_FOLLOWER:
45
        Channel Follower Webhooks are internal webhooks used with
46
        Channel Following to post new messages into channels.
47
    APPLICATION:
48
        Application webhooks are webhooks used with Interactions
49
    """
50
51
    INCOMING = 1
52
    CHANNEL_FOLLOWER = 2
53
    APPLICATION = 3
54
55
56
@dataclass(repr=False)
57
class Webhook(APIObject):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (12/7)
Loading history...
58
    """Represents a Discord channel webhook.
59
60
    Attributes
61
    ----------
62
    id: :class:`~pincer.utils.snowflake.Snowflake`
63
        The id of the webhook
64
    type: :class:`~pincer.objects.guild.webhook.WebhookType`
65
        The type of the webhook
66
    channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
67
        The channel id this webhook is for, if any
68
    name: Optional[:class:`str`]
69
        The default name of the webhook
70
    avatar: Optional[:class:`str`]
71
        The default user avatar hash of the webhook
72
    application_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
73
        The bot/OAuth2 application that created this webhook
74
    user: APINullable[:class:`~pincer.objects.user.user.User`]
75
        The user this webhook was created by
76
        (not returned when getting a webhook with its token)
77
    token: APINullable[:class:`str`]
78
        The secure token of the webhook
79
        (returned for Incoming Webhooks)
80
    source_guild: APINullable[:class:`~pincer.objects.guild.guild.Guild`]
81
        The guild of the channel that this webhook is following
82
        (returned for Channel Follower Webhooks)
83
    source_channel: APINullable[:class:`~pincer.objects.guild.channel.Channel`]
84
        The channel that this webhook is following
85
        (returned for Channel Follower Webhooks)
86
    url: APINullable[:class:`str`]
87
        The url used for executing the webhook
88
        (returned by the webhooks OAuth2 flow)
89
    guild_id: APINullable[Optional[:class:`~pincer.objects.guild.guild.Guild`]]
90
        The guild id this webhook is for, if any
91
    """
92
93
    id: Snowflake
94
    type: WebhookType
95
96
    channel_id: Optional[Snowflake] = None
97
    name: Optional[str] = None
98
    avatar: Optional[str] = None
99
    application_id: Optional[Snowflake] = None
100
101
    user: APINullable[User] = MISSING
102
    token: APINullable[str] = MISSING
103
    source_guild: APINullable[Guild] = MISSING
104
    source_channel: APINullable[Channel] = MISSING
105
    url: APINullable[str] = MISSING
106
107
    guild_id: APINullable[Optional[Snowflake]] = MISSING
108
109
    async def edit(
110
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
111
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
112
        name: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
113
        avatar: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
114
        channel_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
115
        token: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
116
    ) -> Webhook:
117
        """
118
        Modifies a webhook and returns it.
119
        Requires the ``MANAGE_WEBHOOKS`` permission.
120
121
        Parameters
122
        ----------
123
        name: Optional[:class:`str`]
124
            The new name of the webhook
125
        avatar: Optional[:class:`str`]
126
            The new avatar hash of the webhook
127
        channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
128
            The new channel id this webhook is for
129
        token: Optional[:class:`str`]
130
            The new token of the webhook
131
        """
132
        request_route = f"webhooks/{self.id}" + (f"/{token}" if token else "")
133
        request_data = {
134
            "name": name,
135
            "avatar": avatar,
136
            "channel_id": channel_id,
137
        }
138
139
        if token:
140
            del request_data["channel_id"]
141
142
        data = await self._http.patch(request_route, data=request_data)
143
        return Webhook.from_dict(data)
144
145
    async def delete(self, token: Optional[str] = None):
146
        """
147
        Deletes a webhook.
148
        Requires the ``MANAGE_WEBHOOKS`` permission.
149
150
        Parameters
151
        ----------
152
        token: Optional[:class:`str`]
153
            The token of the webhook
154
        """
155
        await self._http.delete(
156
            f"webhooks/{self.id}" + (f"/{token}" if token else "")
157
        )
158
159
    @overload
160
    async def execute(
161
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
162
        webhook_compatibility: WebhookCompatibility = WebhookCompatibility.Default,  # noqa: E501
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
163
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
164
        thread_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
165
        wait: Optional[bool] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
166
        content: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
167
        username: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
168
        avatar_url: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
169
        tts: Optional[bool] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
170
        embeds: Optional[List[Embed]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
171
        allowed_mentions: Optional[AllowedMentions] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
172
        components: Optional[List[MessageComponent]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
173
        files: Optional[str] = None,  # TODO: Add support for files
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
174
        payload_json: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
175
        attachments: Optional[List[Attachment]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
176
    ):
177
        """|coro|
178
        Executes a webhook.
179
180
        Note that when sending a message, you must provide a value
181
        for at least one of ``content``, ``embeds``, or ``file``.
182
183
        Parameters
184
        ----------
185
        webhook_compatibility: :class:`~pincer.objects.guild.webhook.WebhookCompatibility`
186
            The compatibility of the webhook
187
        thread_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
188
            ID of the thread to send message in
189
        wait: Optional[:class:`bool`]
190
            Waits for server confirmation of message send before
191
            response (defaults to ``true``, when ``false`` a message
192
            that is not saved does not return an error)
193
        content: Optional[:class:`str`]
194
            The message contents (up to 2000 characters)
195
        username: Optional[:class:`str`]
196
            Override the default username of the webhook
197
        avatar_url: Optional[:class:`str`]
198
            Override the default avatar of the webhook
199
        tts: Optional[:class:`bool`]
200
            True if this is a TTS message
201
        embeds: Optional[List[:class:`~pincer.objects.message.embed.Embed`]]
202
            Embedded ``rich`` content, up to 10 embeds
203
        allowed_mentions: Optional[:class:`~pincer.objects.message.user_message.AllowedMentions`]
204
            Allowed mentions for the message
205
        components: Optional[List[:class:`~pincer.objects.message.component.MessageComponent`]]
206
            The components to include in the message
207
        files: Optional[:class:`str`]
208
            The contents of the file being sent
209
        payload_json: Optional[:class:`str`]
210
            JSON encoded body of non-file params
211
        attachments: Optional[List[:class:`~pincer.objects.message.attachment.Attachment`]]
212
            Attachment objects with filename and description
213
        """
214
        ...
215
216
    async def execute(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
217
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
218
        webhook_compatibility: WebhookCompatibility = WebhookCompatibility.Default,  # noqa: E501
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
219
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
220
        thread_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
221
        wait: Optional[bool] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
222
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
223
    ):
224
        if len(kwargs.get("embeds", [])) > 10:
225
            raise EmbedOverflow("You can only include up to 10 embeds")
226
227
        request_route = f"webhooks/{self.id}/{self.token}"
228
229
        # Adding the subdirectory
230
        if webhook_compatibility.value:
231
            request_route += f"/{webhook_compatibility.value}"
232
233
        # Adding query params
234
        if wait is not None:
235
            request_route += f"?{wait=}"
236
        if thread_id is not None:
237
            request_route += "&?"[wait is None] + f"{thread_id=}"
238
239
        if webhook_compatibility == WebhookCompatibility.Default:
240
            request_data = kwargs
241
        else:
242
            request_data = None
243
244
        await self._http.post(request_route, data=request_data)
245
246
    async def execute_github(
247
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
248
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
249
        thread_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
250
        wait: Optional[bool] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
251
    ):
252
        """|coro|
253
        Executes a GitHub compatible webhook.
254
255
        Parameters
256
        ----------
257
        thread_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
258
            ID of the thread to send message in
259
        wait: Optional[:class:`bool`]
260
            Waits for server confirmation of message send before
261
            response (defaults to ``true``, when ``false`` a message
262
            that is not saved does not return an error)
263
        """
264
        await self.execute(
265
            WebhookCompatibility.GitHub, thread_id=thread_id, wait=wait
266
        )
267
268
    async def execute_slack(
269
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
270
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
271
        thread_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
272
        wait: Optional[bool] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
273
    ):
274
        """|coro|
275
        Executes a Slack compatible webhook.
276
277
        Parameters
278
        ----------
279
        thread_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
280
            ID of the thread to send message in
281
        wait: Optional[:class:`bool`]
282
            Waits for server confirmation of message send before
283
            response (defaults to ``true``, when ``false`` a message
284
            that is not saved does not return an error)
285
        """
286
        await self.execute(
287
            WebhookCompatibility.Slack, thread_id=thread_id, wait=wait
288
        )
289
290
    async def get_message(
291
        self, message_id: Snowflake, thread_id: Snowflake
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
292
    ) -> UserMessage:
293
        """|coro|
294
        Returns a previously-sent webhook message from the same token.
295
296
        Parameters
297
        ----------
298
        message_id: :class:`~pincer.utils.snowflake.Snowflake`
299
            The ID of the message to get
300
        thread_id: :class:`~pincer.utils.snowflake.Snowflake`
301
            The ID of the thread to get the message from
302
303
        Returns
304
        -------
305
        :class:`~pincer.objects.message.message.Message`
306
            The message
307
        """
308
        return UserMessage.from_dict(
309
            await self._http.get(
310
                f"webhooks/{self.id}/{self.token}/messages/{message_id}",
311
                params={"thread_id": thread_id},
312
            )
313
        )
314
315
    async def delete_message(self, message_id: Snowflake, thread_id: Snowflake):
316
        """|coro|
317
        Deletes a message created by a webhook.
318
319
        Parameters
320
        ----------
321
        message_id: :class:`~pincer.utils.snowflake.Snowflake`
322
            The ID of the message to delete
323
        thread_id: :class:`~pincer.utils.snowflake.Snowflake`
324
            The ID of the thread to delete the message from
325
        """
326
        await self._http.delete(
327
            f"webhooks/{self.id}/{self.token}/messages/{message_id}"
328
            + (f"?{thread_id=}" if thread_id else "")
329
        )
330
331
    @overload
332
    async def edit_message(
333
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
334
        message_id: Snowflake,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
335
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
336
        thread_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
337
        content: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
338
        embeds: Optional[List[Embed]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
339
        allowed_mentions: Optional[AllowedMentions] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
340
        components: Optional[List[MessageComponent]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
341
        files: Optional[str] = None,  # TODO: Add support for files
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
342
        payload_json: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
343
        attachments: Optional[List[Attachment]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
344
    ) -> UserMessage:
345
        """|coro|
346
        Edits a previously-sent webhook message from the same token.
347
348
        Parameters
349
        ----------
350
        message_id: :class:`~pincer.utils.snowflake.Snowflake`
351
            The ID of the message to edit
352
        thread_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
353
            ID of the thread the message is in
354
        content: Optional[:class:`str`]
355
            The new content of the message (up to 2000 characters)
356
        embeds: Optional[List[:class:`~pincer.objects.message.embed.Embed`]]
357
            Embedded ``rich`` content, up to 10 embeds
358
        allowed_mentions: Optional[:class:`~pincer.objects.message.user_message.AllowedMentions`]
359
            Allowed mentions for the message
360
        components: Optional[List[:class:`~pincer.objects.message.component.MessageComponent`]]
361
            The components to include in the message
362
        files: Optional[:class:`str`]
363
            The contents of the file being sent/edited
364
        payload_json: Optional[:class:`str`]
365
            JSON encoded body of non-file params
366
            (multipart/form-data only)
367
        attachments: Optional[List[:class:`~pincer.objects.message.attachment.Attachment`]]
368
            Attached files to keep and
369
            possible descriptions for new files
370
        """
371
        ...
372
373
    async def edit_message(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
374
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
375
        message_id: Snowflake,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
376
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
377
        thread_id: Optional[Snowflake] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
378
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
379
    ) -> UserMessage:
380
        if len(kwargs.get("embeds", [])) > 10:
381
            raise EmbedOverflow("You can only include up to 10 embeds")
382
383
        data = await self._http.patch(
384
            f"webhooks/{self.id}/{self.token}/messages/{message_id}"
385
            + (f"?{thread_id=}" if thread_id else ""),
386
            data=kwargs,
387
        )
388
        return UserMessage.from_dict(data)
389
390
    @classmethod
391
    async def from_id(
392
        cls, client: Client, id: Snowflake, token: Optional[str] = None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
393
    ) -> Webhook:
394
        """|coro|
395
        Gets a webhook by its ID.
396
397
        Parameters
398
        ----------
399
        client: `~pincer.client.Client`
400
            The client to use to make the request.
401
        id: `~pincer.utils.snowflake.Snowflake`
402
            The ID of the webhook to get.
403
        token: Optional[:class:`str`]
404
            The token of the webhook to get.
405
406
        Returns
407
        -------
408
        `~pincer.objects.guild.webhook.Webhook`
409
            The webhook with the given ID.
410
        """
411
        return cls.from_dict(
412
            await client.http.get(
413
                f"webhooks/{id}" + (f"/{token}" if token else "")
414
            )
415
        )
416