Passed
Pull Request — main (#155)
by Oliver
01:56
created

AllowedMentions.to_dict()   A

Complexity

Conditions 2

Size

Total Lines 12
Code Lines 10

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 10
dl 12
loc 12
rs 9.9
c 0
b 0
f 0
cc 2
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 json import dumps
7
from typing import TYPE_CHECKING
8
from dataclasses import dataclass
9
10
from ...utils.api_object import APIObject
11
12
if TYPE_CHECKING:
13
    from typing import Dict, Union, List, Optional, Tuple
0 ignored issues
show
introduced by
Imports from package typing are not grouped
Loading history...
14
15
    from aiohttp import FormData, Payload
16
17
    from .file import File
18
    from .embed import Embed
19
    from ..user.user import User
20
    from ..guild.role import Role
21
    from .component import MessageComponent
22
    from ...utils.snowflake import Snowflake
23
    from .user_message import AllowedMentionTypes
24
    from ...exceptions import CommandReturnIsEmpty
25
    from ..app.interactions import InteractionFlags
26
    from ..app.interaction_base import CallbackType
27
28
PILLOW_IMPORT = True
29
30
try:
31
    from PIL.Image import Image
32
except (ModuleNotFoundError, ImportError):
33
    PILLOW_IMPORT = False
34
35
36 View Code Duplication
@dataclass
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
37
class AllowedMentions(APIObject):
38
    """Represents the entities the client can mention
39
40
    Attributes
41
    ----------
42
    parse: List[:class:`~pincer.objects.message.user_message.AllowedMentionTypes`]
43
        An array of allowed mention types to parse from the content.
44
    roles: List[Union[:class:`~pincer.objects.guild.role.Role`, :class:`~pincer.utils.snowflake.Snowflake`]]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (108/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
45
        List of ``Role`` objects or snowflakes of allowed mentions.
46
    users: List[Union[:class:`~pincer.objects.user.user.User` :class:`~pincer.utils.snowflake.Snowflake`]]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
47
        List of ``user`` objects or snowflakes of allowed mentions.
48
    reply: :class:`bool`
49
        If replies should mention the author.
50
        |default| :data:`True`
51
    """  # noqa: E501
52
53
    parse: List[AllowedMentionTypes]
0 ignored issues
show
introduced by
The variable AllowedMentionTypes does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
54
    roles: List[Union[Role, Snowflake]]
0 ignored issues
show
introduced by
The variable Role does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
introduced by
The variable Snowflake does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
55
    users: List[Union[User, Snowflake]]
0 ignored issues
show
introduced by
The variable User does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
56
    reply: bool = True
57
58
    def to_dict(self):
59
        def get_str_id(obj: Union[Snowflake, User, Role]) -> str:
60
            if hasattr(obj, "id"):
61
                obj = obj.id
62
63
            return str(obj)
64
65
        return {
66
            "parse": self.parse,
67
            "roles": list(map(get_str_id, self.roles)),
68
            "users": list(map(get_str_id, self.users)),
69
            "replied_user": self.reply
70
        }
71
72
73
@dataclass
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
74
class Message:
75
    """A discord message that will be send to discord
76
77
    Attributes
78
    ----------
79
    content: :class:`str`
80
        The text in the message.
81
        |default| ``""``
82
    attachments: Optional[List[:class:`~pincer.objects.message.file.File`]]
83
        Attachments on the message. This is a File object. You can also attach
84
        a Pillow Image or string. Pillow images will be converted to PNGs. They
85
        will use the naming sceme ``image%`` where % is the images index in the
86
        attachments array. Strings will be read as a filepath. The name of the
87
        file that the string points to will be used as the name.
88
    tts: Optional[:class:`bool`]
89
        Whether the message should be spoken to the user.
90
        |default| :data:`False`
91
    embeds: Optional[List[:class:`~pincer.objects.message.embed.Embed`]]
92
        Embed attached to the message. This is an Embed object.
93
    allowed_mentions: Optional[:class:`~pincer.objects.message.message.AllowedMentions`]
94
        The allowed mentions for the message.
95
    components: Optional[List[:class:`~pincer.objects.message.component.MessageComponent`]]
96
        The components of the message.
97
    flags: Optional[:class:`~pincer.objects.app.interactions.InteractionFlags`]
98
        The interaction flags for the message.
99
    type: Optional[:class:`~pincer.objects.app.interaction_base.CallbackType`]
100
        The type of the callback.
101
    """  # noqa: E501
102
103
    content: str = ''
104
    attachments: Optional[List[File]] = None
0 ignored issues
show
introduced by
The variable File does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
105
    tts: Optional[bool] = False
106
    embeds: Optional[List[Embed]] = None
0 ignored issues
show
introduced by
The variable Embed does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
107
    allowed_mentions: Optional[AllowedMentions] = None
108
    components: Optional[List[MessageComponent]] = None
0 ignored issues
show
introduced by
The variable MessageComponent does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
109
    flags: Optional[InteractionFlags] = None
0 ignored issues
show
introduced by
The variable InteractionFlags does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
110
    type: Optional[CallbackType] = None
0 ignored issues
show
introduced by
The variable CallbackType does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
111
112
    def __post_init__(self):
113
114
        if not self.attachments:
115
            return
116
117
        attch = []
118
119
        for count, value in enumerate(self.attachments):
120
            if isinstance(value, File):
0 ignored issues
show
introduced by
The variable File does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
121
                attch.append(value)
122
            elif PILLOW_IMPORT and isinstance(value, Image):
123
                attch.append(File.from_pillow_image(
124
                    value,
125
                    f"image{count}.png",
126
                ))
127
            elif isinstance(value, str):
128
                attch.append(File.from_file(value))
129
            else:
130
                raise ValueError(f"Attachment {count} is invalid type.")
131
132
        self.attachments = attch
133
134
    @property
135
    def isempty(self) -> bool:
136
        """:class:`bool`: If the message is empty.
137
        """
138
139
        return (
140
            len(self.content) < 1
141
            and not self.embeds
142
            and not self.attachments
143
        )
144
145
    def to_dict(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
146
147
        allowed_mentions = (
148
            self.allowed_mentions.to_dict()
149
            if self.allowed_mentions else {}
150
        )
151
152
        # Attachments aren't serialized
153
        # because they are not sent as part of the json
154
        resp = {
155
            "content": self.content,
156
            "tts": self.tts,
157
            "flags": self.flags,
158
            "embeds": [embed.to_dict() for embed in (self.embeds or [])],
159
            "allowed_mentions": allowed_mentions,
160
            "components": [
161
                components.to_dict() for components in (self.components or [])
162
            ]
163
        }
164
165
        return {
166
            "type": self.type or CallbackType.MESSAGE,
0 ignored issues
show
introduced by
The variable CallbackType does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
167
            "data": {k: i for k, i in resp.items() if i}
168
        }
169
170
    def serialize(self) -> Tuple[str, Union[Payload, Dict]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
171
        if self.isempty:
172
            raise CommandReturnIsEmpty("Cannot return empty message.")
0 ignored issues
show
Bug introduced by
The variable CommandReturnIsEmpty was used before it was assigned.
Loading history...
introduced by
The variable CommandReturnIsEmpty does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
173
174
        if not self.attachments:
175
            return "application/json", self.to_dict()
176
177
        form = FormData()
0 ignored issues
show
Bug introduced by
The variable FormData was used before it was assigned.
Loading history...
introduced by
The variable FormData does not seem to be defined in case TYPE_CHECKING on line 12 is False. Are you sure this can never be the case?
Loading history...
178
        form.add_field("payload_json", dumps(self.to_dict()))
179
180
        for file in self.attachments:
181
            form.add_field("file", file.content, filename=file.filename)
182
183
        payload = form()
184
        return payload.headers["Content-Type"], payload
185