11from datetime import datetime
22from typing import List , Optional , Union
33
4- from ...client .models .component import ActionRow , Button , SelectMenu
4+ from ...client .models .component import ActionRow , Button , SelectMenu , _build_components
55from .channel import Channel , ThreadMember
66from .member import Member
77from .message import Embed , Emoji , Message , MessageInteraction , Sticker
8- from .misc import MISSING , ClientStatus , DictSerializerMixin , Snowflake
8+ from .misc import MISSING , ClientStatus , DictSerializerMixin , File , Snowflake
99from .presence import PresenceActivity
1010from .role import Role
1111from .user import User
@@ -347,7 +347,7 @@ async def send(
347347 ]
348348 ] = MISSING ,
349349 tts : Optional [bool ] = MISSING ,
350- # attachments : Optional[List[Any]] = None, # TODO: post-v4: Replace with own file type.
350+ files : Optional [Union [ File , List [File ]]] = MISSING ,
351351 embeds : Optional [Union [Embed , List [Embed ]]] = MISSING ,
352352 allowed_mentions : Optional [MessageInteraction ] = MISSING ,
353353 ) -> Message :
@@ -360,6 +360,8 @@ async def send(
360360 :type components: Optional[Union[ActionRow, Button, SelectMenu, List[Actionrow], List[Button], List[SelectMenu]]]
361361 :param tts?: Whether the message utilizes the text-to-speech Discord programme or not.
362362 :type tts: Optional[bool]
363+ :param files?: A file or list of files to be attached to the message.
364+ :type files: Optional[Union[File, List[File]]]
363365 :param embeds?: An embed, or list of embeds for the message.
364366 :type embeds: Optional[Union[Embed, List[Embed]]]
365367 :param allowed_mentions?: The message interactions/mention limits that the message can refer to.
@@ -383,117 +385,30 @@ async def send(
383385
384386 if not components or components is MISSING :
385387 _components = []
386- # TODO: Break this obfuscation pattern down to a "builder" method.
387388 else :
388- _components : List [dict ] = [{"type" : 1 , "components" : []}]
389- if isinstance (components , list ) and all (
390- isinstance (action_row , ActionRow ) for action_row in components
391- ):
392- _components = [
393- {
394- "type" : 1 ,
395- "components" : [
396- (
397- component ._json
398- if component ._json .get ("custom_id" ) or component ._json .get ("url" )
399- else []
400- )
401- for component in action_row .components
402- ],
403- }
404- for action_row in components
405- ]
406- elif isinstance (components , list ) and all (
407- isinstance (component , (Button , SelectMenu )) for component in components
408- ):
409- for component in components :
410- if isinstance (component , SelectMenu ):
411- component ._json ["options" ] = [
412- options ._json if not isinstance (options , dict ) else options
413- for options in component ._json ["options" ]
414- ]
415- _components = [
416- {
417- "type" : 1 ,
418- "components" : [
419- (
420- component ._json
421- if component ._json .get ("custom_id" ) or component ._json .get ("url" )
422- else []
423- )
424- for component in components
425- ],
426- }
427- ]
428- elif isinstance (components , list ) and all (
429- isinstance (action_row , (list , ActionRow )) for action_row in components
430- ):
431- _components = []
432- for action_row in components :
433- for component in (
434- action_row if isinstance (action_row , list ) else action_row .components
435- ):
436- if isinstance (component , SelectMenu ):
437- component ._json ["options" ] = [
438- option ._json for option in component .options
439- ]
440- _components .append (
441- {
442- "type" : 1 ,
443- "components" : [
444- (
445- component ._json
446- if component ._json .get ("custom_id" )
447- or component ._json .get ("url" )
448- else []
449- )
450- for component in (
451- action_row
452- if isinstance (action_row , list )
453- else action_row .components
454- )
455- ],
456- }
457- )
458- elif isinstance (components , ActionRow ):
459- _components [0 ]["components" ] = [
460- (
461- component ._json
462- if component ._json .get ("custom_id" ) or component ._json .get ("url" )
463- else []
464- )
465- for component in components .components
466- ]
467- elif isinstance (components , Button ):
468- _components [0 ]["components" ] = (
469- [components ._json ]
470- if components ._json .get ("custom_id" ) or components ._json .get ("url" )
471- else []
472- )
473- elif isinstance (components , SelectMenu ):
474- components ._json ["options" ] = [
475- options ._json if not isinstance (options , dict ) else options
476- for options in components ._json ["options" ]
477- ]
478- _components [0 ]["components" ] = (
479- [components ._json ]
480- if components ._json .get ("custom_id" ) or components ._json .get ("url" )
481- else []
482- )
483-
484- # TODO: post-v4: Add attachments into Message obj.
389+ _components = _build_components (components = components )
390+
391+ if not files or files is MISSING :
392+ _files = []
393+ elif isinstance (files , list ):
394+ _files = [file ._json_payload (id ) for id , file in enumerate (files )]
395+ else :
396+ _files = [files ._json_payload (0 )]
397+ files = [files ]
398+
485399 payload = Message (
486400 content = _content ,
487401 tts = _tts ,
488- # file=file,
489- # attachments=_attachments,
402+ attachments = _files ,
490403 embeds = _embeds ,
491404 components = _components ,
492405 allowed_mentions = _allowed_mentions ,
493406 )
494407
495408 channel = Channel (** await self ._client .create_dm (recipient_id = int (self .user .id )))
496- res = await self ._client .create_message (channel_id = int (channel .id ), payload = payload ._json )
409+ res = await self ._client .create_message (
410+ channel_id = int (channel .id ), payload = payload ._json , files = files
411+ )
497412
498413 return Message (** res , _client = self ._client )
499414
0 commit comments