Skip to content

Commit 25a42b5

Browse files
committed
feat: Add support for voice message meta-fields
Signed-off-by: Mathieu Corsham <[email protected]>
1 parent 8304659 commit 25a42b5

File tree

4 files changed

+87
-11
lines changed

4 files changed

+87
-11
lines changed

discord/file.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2424
DEALINGS IN THE SOFTWARE.
2525
"""
26+
from __future__ import annotations
2627

2728
import os.path
2829

@@ -71,14 +72,18 @@ class File:
7172
The file description can be set to attached images to show a alternative text.
7273
"""
7374

74-
__slots__ = ('fp', 'filename', 'description', 'spoiler', '_original_pos', '_owner', '_closer')
75-
76-
def __init__(self,
77-
fp: Union[io.IOBase, str, bytes, 'PathLike[str]', 'PathLike[bytes]', int],
78-
filename: Optional[str] = None,
79-
description: Optional[str] = None,
80-
*,
81-
spoiler: bool = False):
75+
__slots__ = ('fp', 'filename', 'description', 'spoiler', 'duration', 'waveform', '_original_pos', '_owner', '_closer')
76+
77+
def __init__(
78+
self,
79+
fp: Union[io.IOBase, str, bytes, PathLike[str], PathLike[bytes], int],
80+
filename: Optional[str] = None,
81+
description: Optional[str] = None,
82+
*,
83+
spoiler: bool = False,
84+
duration: Optional[float] = None,
85+
waveform: Optional[str] = None
86+
):
8287
self.fp = fp
8388

8489
if isinstance(fp, io.IOBase):
@@ -113,6 +118,8 @@ def __init__(self,
113118
self.filename = 'SPOILER_' + self.filename
114119

115120
self.spoiler = spoiler or (self.filename is not None and self.filename.startswith('SPOILER_'))
121+
self.duration: Optional[float] = duration
122+
self.waveform: Optional[str] = waveform
116123

117124
def reset(self, *, seek=True):
118125
# The `seek` parameter is needed because
@@ -132,11 +139,17 @@ def close(self):
132139
self._closer()
133140

134141
def to_dict(self, file_index: int) -> dict:
135-
return {
142+
base = {
136143
'id': file_index,
137144
'description': self.description,
138145
'filename': self.filename
139146
}
147+
if self.duration is not None:
148+
base['duration_secs'] = self.duration
149+
if self.waveform is not None:
150+
base['waveform'] = self.waveform
151+
return base
152+
140153

141154

142155
class UploadFile(File):

discord/message.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import asyncio
2929
from datetime import datetime
30+
from base64 import b64decode
3031
import re
3132
import io
3233

@@ -134,7 +135,7 @@ class Attachment(Hashable):
134135
.. versionchanged:: 1.7
135136
Attachment can now be cast to :class:`str` and is hashable.
136137
.. versionchanged:: 2.0
137-
The :attr:`ephemeral` and :attr:`description` attributes were added.
138+
The :attr:`ephemeral`, :attr:`description`, :attr:`duration` and :attr:`waveform` attributes were added.
138139
139140
Attributes
140141
------------
@@ -161,10 +162,12 @@ class Attachment(Hashable):
161162
Whether the attachment is ephemeral (part of an ephemeral message or was provided in a slash-command option).
162163
description: Optional[:class:`str`]
163164
The description for the file.
165+
duration: Optional[:class:`float`]
166+
The duration of the audio file (currently only for voice messages).
164167
"""
165168

166169
__slots__ = ('id', 'size', 'height', 'width', 'ephemeral', 'description',
167-
'filename', 'url', 'proxy_url', '_http', 'content_type')
170+
'filename', 'url', 'proxy_url', '_http', 'content_type', 'duration', '_waveform')
168171

169172
def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
170173
self.id: int = int(data['id'])
@@ -178,10 +181,17 @@ def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
178181
self.proxy_url = data.get('proxy_url')
179182
self._http: HTTPClient = state.http
180183
self.content_type: str = data.get('content_type')
184+
self.duration: Optional[float] = data.get('duration_secs')
185+
self._waveform: Optional[List[int]] = data.get('waveform')
181186

182187
def is_spoiler(self) -> bool:
183188
""":class:`bool`: Whether this attachment contains a spoiler."""
184189
return self.filename.startswith('SPOILER_')
190+
191+
@property
192+
def waveform(self) -> Optional[bytearray]:
193+
""":class:`Optional[List[:class:`int`]]`: The waveform of the audio file (currently only for voice messages)."""
194+
return b64decode(self._waveform) if self._waveform else None
185195

186196
def __repr__(self) -> str:
187197
return '<Attachment id={0.id} filename={0.filename!r} url={0.url!r}>'.format(self)

discord/types/guild.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from .sticker import GuildSticker
4343

4444
__all__ = (
45+
'PermissionFlags',
4546
'UnavailableGuild',
4647
'PartialGuild',
4748
'Guild',
@@ -104,6 +105,56 @@
104105
'ROLE_SUBSCRIPTIONS_ENABLED',
105106
'ROLE_SUBSCRIPTIONS_ENABLED_FOR_PURCHASE'
106107
]
108+
PermissionFlags = Literal[
109+
'create_instant_invite',
110+
'kick_members',
111+
'ban_members',
112+
'administrator',
113+
'manage_channels',
114+
'manage_guild',
115+
'add_reactions',
116+
'view_audit_log',
117+
'priority_speaker',
118+
'stream',
119+
'read_messages',
120+
'send_messages',
121+
'send_tts_messages',
122+
'manage_messages',
123+
'embed_links',
124+
'attach_files',
125+
'read_message_history',
126+
'mention_everyone',
127+
'external_emojis',
128+
'view_guild_insights',
129+
'connect',
130+
'speak',
131+
'mute_members',
132+
'deafen_members',
133+
'move_members',
134+
'use_voice_activation',
135+
'change_nickname',
136+
'manage_nicknames',
137+
'manage_roles',
138+
'manage_webhooks',
139+
'manage_expressions',
140+
'create_expressions',
141+
'use_slash_commands',
142+
'request_to_speak',
143+
'manage_events',
144+
'create_events',
145+
'manage_threads',
146+
'create_public_threads',
147+
'create_private_threads',
148+
'use_external_stickers',
149+
'send_messages_in_threads',
150+
'start_embedded_activities',
151+
'moderate_members',
152+
'view_creator_monetization_analytics',
153+
'use_soundboard',
154+
'use_external_sounds',
155+
'send_voice_messages'
156+
]
157+
107158

108159

109160
class UnavailableGuild(TypedDict):

discord/types/message.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ class Attachment(TypedDict):
165165
height: NotRequired[Optional[int]]
166166
width: NotRequired[Optional[int]]
167167
ephemeral: NotRequired[Literal[True]]
168+
duration_secs: NotRequired[float]
169+
waveform: NotRequired[str]
168170

169171

170172
class PartialMessage(TypedDict):

0 commit comments

Comments
 (0)