/
user.py
207 lines (181 loc) · 7.99 KB
/
user.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
from typing import TYPE_CHECKING, List, Optional, Union
from ...utils.attrs_utils import ClientSerializerMixin, define, field
from ...utils.missing import MISSING
from ..error import LibraryException
from .flags import UserFlags
from .misc import AllowedMentions, File, IDMixin, Snowflake
if TYPE_CHECKING:
from ...client.models.component import ActionRow, Button, SelectMenu
from .gw import Presence
from .message import Attachment, Embed, Message
__all__ = ("User",)
@define()
class User(ClientSerializerMixin, IDMixin):
"""
A class object representing a user.
:ivar Snowflake id: The User ID
:ivar str username: The Username associated (not necessarily unique across the platform)
:ivar str discriminator: The User's 4-digit discord-tag (i.e.: XXXX)
:ivar Optional[str] avatar?: The user's avatar hash, if any
:ivar Optional[bool] bot?: A status denoting if the user is a bot
:ivar Optional[bool] system?: A status denoting if the user is an Official Discord System user
:ivar Optional[bool] mfa_enabled?: A status denoting if the user has 2fa on their account
:ivar Optional[str] banner?: The user's banner hash, if any
:ivar Optional[str] banner_color?: The user's banner color as a hex, if any
:ivar Optional[int] accent_color?: The user's banner color as an integer represented of hex color codes
:ivar Optional[str] locale?: The user's chosen language option
:ivar Optional[bool] verified?: Whether the email associated with this account has been verified
:ivar Optional[str] email?: The user's email, if any
:ivar Optional[UserFlags] flags?: The user's flags
:ivar Optional[int] premium_type?: The type of Nitro subscription the user has
:ivar Optional[UserFlags] public_flags?: The user's public flags
"""
id: Snowflake = field(converter=Snowflake)
username: str = field(repr=True)
discriminator: str = field(repr=True)
avatar: Optional[str] = field(default=None, repr=False)
bot: Optional[bool] = field(default=None)
system: Optional[bool] = field(default=None, repr=False)
mfa_enabled: Optional[bool] = field(default=None)
banner: Optional[str] = field(default=None, repr=False)
accent_color: Optional[int] = field(default=None, repr=False)
banner_color: Optional[str] = field(default=None, repr=False)
locale: Optional[str] = field(default=None)
verified: Optional[bool] = field(default=None)
email: Optional[str] = field(default=None)
flags: Optional[UserFlags] = field(converter=UserFlags, default=None, repr=False)
premium_type: Optional[int] = field(default=None, repr=False)
public_flags: Optional[UserFlags] = field(converter=UserFlags, default=None, repr=False)
bio: Optional[str] = field(default=None)
def __str__(self) -> str:
return self.username
def has_public_flag(self, flag: Union[UserFlags, int]) -> bool:
if self.public_flags == 0 or self.public_flags is None:
return False
return bool(int(self.public_flags) & flag)
@property
def mention(self) -> str:
"""
Returns a string that allows you to mention the given user.
:return: The string of the mentioned user.
:rtype: str
"""
return f"<@{self.id}>"
@property
def avatar_url(self) -> str:
"""
Returns the URL of the user's avatar
:return: URL of the user's avatar.
:rtype: str
"""
url = "https://cdn.discordapp.com/"
if self.avatar:
url += f"avatars/{int(self.id)}/{self.avatar}"
url += ".gif" if self.avatar.startswith("a_") else ".png"
else:
url += f"embed/avatars/{int(self.discriminator) % 5}.png"
return url
@property
def banner_url(self) -> Optional[str]:
"""
Returns the URL of the user's banner.
:return: URL of the user's banner (None will be returned if no banner is set)
:rtype: str
"""
if not self.banner:
return None
url = f"https://cdn.discordapp.com/banners/{int(self.id)}/{self.banner}"
url += ".gif" if self.banner.startswith("a_") else ".png"
return url
@property
def presence(self) -> Optional["Presence"]:
"""
Returns the presence of the user.
:return: Presence of the user (None will be returned if not cached)
:rtype: Optional[Presence]
"""
from .gw import Presence
return self._client.cache[Presence].get(self.id)
async def send(
self,
content: Optional[str] = MISSING,
*,
components: Optional[
Union[
"ActionRow",
"Button",
"SelectMenu",
List["ActionRow"],
List["Button"],
List["SelectMenu"],
]
] = MISSING,
tts: Optional[bool] = MISSING,
attachments: Optional[List["Attachment"]] = MISSING,
files: Optional[Union[File, List[File]]] = MISSING,
embeds: Optional[Union["Embed", List["Embed"]]] = MISSING,
allowed_mentions: Optional[Union[AllowedMentions, dict]] = MISSING,
) -> "Message":
"""
Sends a DM to the user.
:param content?: The contents of the message as a string or string-converted value.
:type content?: Optional[str]
:param components?: A component, or list of components for the message.
:type components?: Optional[Union[ActionRow, Button, SelectMenu, List[ActionRow], List[Button], List[SelectMenu]]]
:param tts?: Whether the message utilizes the text-to-speech Discord programme or not.
:type tts?: Optional[bool]
:param attachments?: The attachments to attach to the message. Needs to be uploaded to the CDN first
:type attachments?: Optional[List[Attachment]]
:param files?: A file or list of files to be attached to the message.
:type files?: Optional[Union[File, List[File]]]
:param embeds?: An embed, or list of embeds for the message.
:type embeds?: Optional[Union[Embed, List[Embed]]]
:param allowed_mentions?: The allowed mentions for the message.
:type allowed_mentions?: Optional[Union[AllowedMentions, dict]]
:return: The sent message as an object.
:rtype: Message
"""
if not self._client:
raise LibraryException(code=13)
from ...client.models.component import _build_components
from .message import Message
_content: str = "" if content is MISSING else content
_tts: bool = False if tts is MISSING else tts
_attachments = [] if attachments is MISSING else [a._json for a in attachments]
_embeds: list = (
[]
if not embeds or embeds is MISSING
else ([embed._json for embed in embeds] if isinstance(embeds, list) else [embeds._json])
)
_allowed_mentions: dict = (
{}
if allowed_mentions is MISSING
else allowed_mentions._json
if isinstance(allowed_mentions, AllowedMentions)
else allowed_mentions
)
if not components or components is MISSING:
_components = []
else:
_components = _build_components(components=components)
if not files or files is MISSING:
_files = []
elif isinstance(files, list):
_files = [file._json_payload(id) for id, file in enumerate(files)]
else:
_files = [files._json_payload(0)]
files = [files]
_files.extend(_attachments)
payload = dict(
content=_content,
tts=_tts,
attachments=_files,
embeds=_embeds,
components=_components,
allowed_mentions=_allowed_mentions,
)
channel = await self._client.create_dm(recipient_id=int(self.id))
res = await self._client.create_message(
channel_id=int(channel["id"]), payload=payload, files=files
)
return Message(**res, _client=self._client)