83
83
from .types .message import MessageApplication as MessageApplicationPayload
84
84
from .types .message import MessageCall as MessageCallPayload
85
85
from .types .message import MessageReference as MessageReferencePayload
86
+ from .types .message import ForwardedMessage as ForwardedMessagePayload
87
+ from .types .message import MessageSnapshot as MessageSnapshotPayload
86
88
from .types .message import Reaction as ReactionPayload
87
89
from .types .poll import Poll as PollPayload
88
90
from .types .snowflake import SnowflakeList
101
103
"MessageReference" ,
102
104
"MessageCall" ,
103
105
"DeletedReferencedMessage" ,
106
+ "ForwardedMessage"
104
107
)
105
108
106
109
@@ -477,8 +480,8 @@ class MessageReference:
477
480
478
481
Attributes
479
482
----------
480
- type: Optional[:class:`MessageReferenceType`]
481
- The type of message reference. If this is not provided, assume default behavior.
483
+ type: Optional[:class:`~discord. MessageReferenceType`]
484
+ The type of message reference. If this is not provided, assume default behavior (reply) .
482
485
483
486
.. versionadded:: 2.7
484
487
@@ -523,11 +526,11 @@ def __init__(
523
526
channel_id : int ,
524
527
guild_id : int | None = None ,
525
528
fail_if_not_exists : bool = True ,
526
- type : MessageReferenceType | None = None
529
+ type : MessageReferenceType = MessageReferenceType . default
527
530
):
528
531
self ._state : ConnectionState | None = None
529
532
self .resolved : Message | DeletedReferencedMessage | None = None
530
- self .type : MessageReferenceType | None = type
533
+ self .type : MessageReferenceType = type
531
534
self .message_id : int | None = message_id
532
535
self .channel_id : int = channel_id
533
536
self .guild_id : int | None = guild_id
@@ -538,9 +541,9 @@ def with_state(
538
541
cls : type [MR ], state : ConnectionState , data : MessageReferencePayload
539
542
) -> MR :
540
543
self = cls .__new__ (cls )
541
- self .type = try_enum (MessageReferenceType , data .get ("type" ))
544
+ self .type = try_enum (MessageReferenceType , data .get ("type" )) or MessageReferenceType . default
542
545
self .message_id = utils ._get_as_snowflake (data , "message_id" )
543
- self .channel_id = int ( data . pop ( "channel_id" ) )
546
+ self .channel_id = utils . _get_as_snowflake ( data , "channel_id" )
544
547
self .guild_id = utils ._get_as_snowflake (data , "guild_id" )
545
548
self .fail_if_not_exists = data .get ("fail_if_not_exists" , True )
546
549
self ._state = state
@@ -549,7 +552,7 @@ def with_state(
549
552
550
553
@classmethod
551
554
def from_message (
552
- cls : type [MR ], message : Message , * , fail_if_not_exists : bool = True , type : MessageReferenceType = None
555
+ cls : type [MR ], message : Message , * , fail_if_not_exists : bool = True , type : MessageReferenceType = MessageReferenceType . default
553
556
) -> MR :
554
557
"""Creates a :class:`MessageReference` from an existing :class:`~discord.Message`.
555
558
@@ -565,8 +568,8 @@ def from_message(
565
568
566
569
.. versionadded:: 1.7
567
570
568
- type: Optional[:class:`MessageReferenceType`]
569
- The type of reference to create. Defaults to reply.
571
+ type: Optional[:class:`~discord. MessageReferenceType`]
572
+ The type of reference to create. Defaults to :attr:`MessageReferenceType.default` ( reply) .
570
573
571
574
.. versionadded:: 2.7
572
575
@@ -602,14 +605,16 @@ def jump_url(self) -> str:
602
605
def __repr__ (self ) -> str :
603
606
return (
604
607
f"<MessageReference message_id={ self .message_id !r} "
605
- f" channel_id={ self .channel_id !r} guild_id={ self .guild_id !r} >"
608
+ f" channel_id={ self .channel_id !r} guild_id={ self .guild_id !r} "
609
+ f" type={ self .type !r} >"
606
610
)
607
611
608
612
def to_dict (self ) -> MessageReferencePayload :
609
613
result : MessageReferencePayload = (
610
614
{"message_id" : self .message_id } if self .message_id is not None else {}
611
615
)
612
616
result ["channel_id" ] = self .channel_id
617
+ result ["type" ] = self .type and self .type .value
613
618
if self .guild_id is not None :
614
619
result ["guild_id" ] = self .guild_id
615
620
if self .fail_if_not_exists is not None :
@@ -647,6 +652,106 @@ def ended_at(self) -> datetime.datetime | None:
647
652
return self ._ended_timestamp
648
653
649
654
655
+ class ForwardedMessage :
656
+ """Represents the snapshotted contents from a forwarded message. Forwarded messages are immutable; any updates to the original message won't be reflected.
657
+
658
+ .. versionadded:: 2.7
659
+
660
+ Attributes
661
+ ----------
662
+ type: :class:`MessageType`
663
+ The type of message. In most cases this should not be checked, but it is helpful
664
+ in cases where it might be a system message for :attr:`system_content`.
665
+ content: :class:`str`
666
+ The contents of the original message.
667
+ embeds: List[:class:`Embed`]
668
+ A list of embeds the original message had.
669
+ attachments: List[:class:`Attachment`]
670
+ A list of attachments given to the original message.
671
+ flags: :class:`MessageFlags`
672
+ Extra features of the message.
673
+ mentions: List[Union[:class:`abc.User`, :class:`Object`]]
674
+ A list of :class:`Member` that were mentioned.
675
+ role_mentions: List[Union[:class:`Role`, :class:`Object`]]
676
+ A list of :class:`Role` that were mentioned.
677
+ stickers: List[:class:`StickerItem`]
678
+ A list of sticker items given to the original message.
679
+ components: List[:class:`Component`]
680
+ A list of components in the original message.
681
+ """
682
+
683
+ __slots__ = (
684
+ "message_id" ,
685
+ "channel_id" ,
686
+ "guild_id" ,
687
+ "fail_if_not_exists" ,
688
+ "resolved" ,
689
+ "type" ,
690
+ "_state" ,
691
+ )
692
+
693
+ def __init__ (
694
+ self ,
695
+ * ,
696
+ state : ConnectionState ,
697
+ reference : MessageReference ,
698
+ data : ForwardedMessagePayload ,
699
+ ):
700
+ self ._state : ConnectionState = state
701
+ self .id : int = reference .message_id
702
+ self .channel = state .get_channel (reference .channel_id ) or (reference .channel_id and Object (reference .channel_id ))
703
+ self .guild = state ._get_guild (reference .guild_id ) or (reference .guild_id and Object (reference .guild_id ))
704
+ self .content : str = data ["content" ]
705
+ self .embeds : list [Embed ] = [Embed .from_dict (a ) for a in data ["embeds" ]]
706
+ self .attachments : list [Attachment ] = [
707
+ Attachment (data = a , state = state ) for a in data ["attachments" ]
708
+ ]
709
+ self .flags : MessageFlags = MessageFlags ._from_value (data .get ("flags" , 0 ))
710
+ self .stickers : list [StickerItem ] = [
711
+ StickerItem (data = d , state = state ) for d in data .get ("sticker_items" , [])
712
+ ]
713
+ self .components : list [Component ] = [
714
+ _component_factory (d ) for d in data .get ("components" , [])
715
+ ]
716
+ self ._edited_timestamp : datetime .datetime | None = utils .parse_time (
717
+ data ["edited_timestamp" ]
718
+ )
719
+
720
+ @property
721
+ def created_at (self ) -> datetime .datetime :
722
+ """The original message's creation time in UTC."""
723
+ return utils .snowflake_time (self .id )
724
+
725
+ @property
726
+ def edited_at (self ) -> datetime .datetime | None :
727
+ """An aware UTC datetime object containing the
728
+ edited time of the original message.
729
+ """
730
+ return self ._edited_timestamp
731
+
732
+
733
+ class MessageSnapshot :
734
+ """Represents a message snapshot.
735
+
736
+ .. versionadded:: 2.7
737
+
738
+ Attributes
739
+ ----------
740
+ message: :class:`ForwardedMessage`
741
+ The forwarded message, which includes a minimal subset of fields from the original message.
742
+ """
743
+
744
+ def __init__ (
745
+ self ,
746
+ * ,
747
+ state : ConnectionState ,
748
+ reference : MessageReference ,
749
+ data : MessageSnapshotPayload ,
750
+ ):
751
+ self ._state : ConnectionState = state
752
+ self .message : ForwardedMessage = ForwardedMessage (state = state , reference = reference , data = data )
753
+
754
+
650
755
def flatten_handlers (cls ):
651
756
prefix = len ("_handle_" )
652
757
handlers = [
@@ -799,6 +904,10 @@ class Message(Hashable):
799
904
The call information associated with this message, if applicable.
800
905
801
906
.. versionadded:: 2.6
907
+ snapshots: Optional[List[:class:`MessageSnapshots`]]
908
+ The snapshots attached to this message, if applicable.
909
+
910
+ .. versionadded:: 2.7
802
911
"""
803
912
804
913
__slots__ = (
@@ -837,6 +946,7 @@ class Message(Hashable):
837
946
"thread" ,
838
947
"_poll" ,
839
948
"call" ,
949
+ "snapshots" ,
840
950
)
841
951
842
952
if TYPE_CHECKING :
@@ -916,6 +1026,14 @@ def __init__(
916
1026
# the channel will be the correct type here
917
1027
ref .resolved = self .__class__ (channel = chan , data = resolved , state = state ) # type: ignore
918
1028
1029
+ self .snapshots : list [MessageSnapshot ]
1030
+ try :
1031
+ self .snapshots = [MessageSnapshot (
1032
+ state = state , reference = self .reference , data = ms ,
1033
+ ) for ms in data ["message_snapshots" ]]
1034
+ except KeyError :
1035
+ self .snapshots = []
1036
+
919
1037
from .interactions import InteractionMetadata , MessageInteraction
920
1038
921
1039
self ._interaction : MessageInteraction | None
@@ -1939,7 +2057,38 @@ async def reply(self, content: str | None = None, **kwargs) -> Message:
1939
2057
you specified both ``file`` and ``files``.
1940
2058
"""
1941
2059
1942
- return await self .channel .send (content , reference = self , ** kwargs )
2060
+ return await self .channel .send (content , reference = self .to_reference (), ** kwargs )
2061
+
2062
+ async def forward (self , channel : MessageableChannel | PartialMessageableChannel , ** kwargs ) -> Message :
2063
+ """|coro|
2064
+
2065
+ A shortcut method to :meth:`.abc.Messageable.send` to forward the
2066
+ :class:`.Message` to a channel.
2067
+
2068
+ .. versionadded:: 2.7
2069
+
2070
+ Parameters
2071
+ ----------
2072
+ channel: Union[:class:`Emoji`, :class:`Reaction`, :class:`PartialEmoji`, :class:`str`]
2073
+ The emoji to react with.
2074
+
2075
+ Returns
2076
+ -------
2077
+ :class:`.Message`
2078
+ The message that was sent.
2079
+
2080
+ Raises
2081
+ ------
2082
+ ~discord.HTTPException
2083
+ Sending the message failed.
2084
+ ~discord.Forbidden
2085
+ You do not have the proper permissions to send the message.
2086
+ ~discord.InvalidArgument
2087
+ The ``files`` list is not of the appropriate size, or
2088
+ you specified both ``file`` and ``files``.
2089
+ """
2090
+
2091
+ return await channel .send (reference = self .to_reference (type = MessageReferenceType .forward ))
1943
2092
1944
2093
async def end_poll (self ) -> Message :
1945
2094
"""|coro|
@@ -1969,7 +2118,7 @@ async def end_poll(self) -> Message:
1969
2118
1970
2119
return message
1971
2120
1972
- def to_reference (self , * , fail_if_not_exists : bool = True ) -> MessageReference :
2121
+ def to_reference (self , * , fail_if_not_exists : bool = True , type : MessageReferenceType = None ) -> MessageReference :
1973
2122
"""Creates a :class:`~discord.MessageReference` from the current message.
1974
2123
1975
2124
.. versionadded:: 1.6
@@ -1982,20 +2131,26 @@ def to_reference(self, *, fail_if_not_exists: bool = True) -> MessageReference:
1982
2131
1983
2132
.. versionadded:: 1.7
1984
2133
2134
+ type: Optional[:class:`~discord.MessageReferenceType`]
2135
+ The type of message reference. Defaults to a reply.
2136
+
2137
+ .. versionadded:: 2.7
2138
+
1985
2139
Returns
1986
2140
-------
1987
2141
:class:`~discord.MessageReference`
1988
2142
The reference to this message.
1989
2143
"""
1990
2144
1991
2145
return MessageReference .from_message (
1992
- self , fail_if_not_exists = fail_if_not_exists
2146
+ self , fail_if_not_exists = fail_if_not_exists , type = type
1993
2147
)
1994
2148
1995
- def to_message_reference_dict (self ) -> MessageReferencePayload :
2149
+ def to_message_reference_dict (self , type : MessageReferenceType = None ) -> MessageReferencePayload :
1996
2150
data : MessageReferencePayload = {
1997
2151
"message_id" : self .id ,
1998
2152
"channel_id" : self .channel .id ,
2153
+ "type" : type and type .value ,
1999
2154
}
2000
2155
2001
2156
if self .guild is not None :
0 commit comments