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

pincer.objects.message.message.Message.serialize()   A

Complexity

Conditions 4

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 24
rs 9.85
c 0
b 0
f 0
cc 4
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 json import dumps
8
from typing import Dict, Tuple, Union, List, Optional, TYPE_CHECKING
9
10
from aiohttp import FormData, Payload
11
12
from ..app.interaction_base import CallbackType
13
from ..message.embed import Embed
14
from ..message.file import File
15
from ..message.user_message import AllowedMentions
16
from ...exceptions import CommandReturnIsEmpty
17
18
PILLOW_IMPORT = True
19
20
try:
21
    from PIL.Image import Image
22
except (ModuleNotFoundError, ImportError):
23
    PILLOW_IMPORT = False
24
25
if TYPE_CHECKING:
26
    from ..app import InteractionFlags
27
    from .component import MessageComponent
28
29
@dataclass
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
30
class Message:
31
    # TODO: Docs for tts, allowed_mentions, components, flags, and type.
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
32
33
    """
34
    A discord message that will be send to discord
35
36
    :param content:
37
        The text in the message.
38
39
    :param attachments:
40
        Attachments on the message. This is a File object. You can also attach
41
        a Pillow Image or string. Pillow images will be converted to PNGs. They
42
        will use the naming sceme ``image%`` where % is the images index in the
43
        attachments array. Strings will be read as a filepath. The name of the
44
        file that the string points to will be used as the name.
45
46
    :param embeds:
47
        Embed attached to the message. This is an Embed object.
48
    """
49
    content: str = ''
50
    attachments: Optional[List[File]] = None
51
    tts: Optional[bool] = False
52
    embeds: Optional[List[Embed]] = None
53
    allowed_mentions: Optional[AllowedMentions] = None
54
    components: Optional[List[MessageComponent]] = None
55
    flags: Optional[InteractionFlags] = None
56
    type: Optional[CallbackType] = None
57
58
    def __post_init__(self):
59
60
        if not self.attachments:
61
            return
62
63
        attch = []
64
65
        for count, value in enumerate(self.attachments):
66
            if isinstance(value, File):
67
                attch.append(value)
68
            elif PILLOW_IMPORT and isinstance(value, Image):
69
                attch.append(File.from_pillow_image(
70
                    value,
71
                    f"image{count}.png",
72
                ))
73
            elif isinstance(value, str):
74
                attch.append(File.from_file(value))
75
            else:
76
                raise ValueError(f"Attachment {count} is invalid type.")
77
78
        self.attachments = attch
79
80
    @property
81
    def isempty(self) -> bool:
82
        """
83
        :return:
84
            Returns true if a message is empty.
85
        """
86
87
        return (
88
                len(self.content) < 1
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 4 spaces).
Loading history...
89
                and not self.embeds
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 4 spaces).
Loading history...
90
                and not self.attachments
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 4 spaces).
Loading history...
91
        )
92
93
    def to_dict(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
94
95
        allowed_mentions = (
96
            self.allowed_mentions.to_dict()
97
            if self.allowed_mentions else {}
98
        )
99
100
        # Attachments aren't serialized
101
        # because they are not sent as part of the json
102
        resp = {
103
            "content": self.content,
104
            "tts": self.tts,
105
            "flags": self.flags,
106
            "embeds": [embed.to_dict() for embed in (self.embeds or [])],
107
            "allowed_mentions": allowed_mentions,
108
            "components": [
109
                components.to_dict() for components in (self.components or [])
110
            ]
111
        }
112
113
        return {
114
            "type": self.type or CallbackType.MESSAGE,
115
            "data": {k: i for k, i in resp.items() if i}
116
        }
117
118
    def serialize(self) -> Tuple[str, Union[Payload, Dict]]:
119
        """
120
        Creates the data that the discord API wants for the message object
121
122
        :return: (content_type, data)
123
124
        :raises CommandReturnIsEmpty:
125
            Command does not have content, an embed, or attachment.
126
        """
127
128
        if self.isempty:
129
            raise CommandReturnIsEmpty("Cannot return empty message.")
130
131
        if not self.attachments:
132
            return "application/json", self.to_dict()
133
134
        form = FormData()
135
        form.add_field("payload_json", dumps(self.to_dict()))
136
137
        for file in self.attachments:
138
            form.add_field("file", file.content, filename=file.filename)
139
140
        payload = form()
141
        return payload.headers["Content-Type"], payload
142