@@ -169,7 +169,7 @@ def startup(self):
169
169
logger .info ("v%s" , __version__ )
170
170
logger .info ("Authors: kyb3r, fourjr, Taaku18" )
171
171
logger .line ()
172
- logger .info ("discord.py: v%s" , discord . __version__ )
172
+ logger .info ("discord.py: v2.5.2" )
173
173
logger .line ()
174
174
175
175
async def load_extensions (self ):
@@ -879,10 +879,172 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
879
879
return
880
880
sent_emoji , blocked_emoji = await self .retrieve_emoji ()
881
881
882
+ # Handle forwarded messages (Discord forwards)
883
+ # See: https://discord.com/developers/docs/resources/message#message-reference-content-attribution-forwards
884
+ import discord
885
+
886
+ # 1. Multi-forward (message_snapshots)
887
+ if hasattr (message , "flags" ) and getattr (message .flags , "has_snapshot" , False ):
888
+ if hasattr (message , "message_snapshots" ) and message .message_snapshots :
889
+ thread = await self .threads .find (recipient = message .author )
890
+ if thread is None :
891
+ delta = await self .get_thread_cooldown (message .author )
892
+ if delta :
893
+ await message .channel .send (
894
+ embed = discord .Embed (
895
+ title = self .config ["cooldown_thread_title" ],
896
+ description = self .config ["cooldown_thread_response" ].format (delta = delta ),
897
+ color = self .error_color ,
898
+ )
899
+ )
900
+ return
901
+ if self .config ["dm_disabled" ] in (DMDisabled .NEW_THREADS , DMDisabled .ALL_THREADS ):
902
+ embed = discord .Embed (
903
+ title = self .config ["disabled_new_thread_title" ],
904
+ color = self .error_color ,
905
+ description = self .config ["disabled_new_thread_response" ],
906
+ )
907
+ embed .set_footer (
908
+ text = self .config ["disabled_new_thread_footer" ],
909
+ icon_url = self .get_guild_icon (guild = message .guild , size = 128 ),
910
+ )
911
+ logger .info (
912
+ "A new thread was blocked from %s due to disabled Modmail." , message .author
913
+ )
914
+ await self .add_reaction (message , blocked_emoji )
915
+ return await message .channel .send (embed = embed )
916
+ thread = await self .threads .create (message .author , message = message )
917
+ else :
918
+ if self .config ["dm_disabled" ] == DMDisabled .ALL_THREADS :
919
+ embed = discord .Embed (
920
+ title = self .config ["disabled_current_thread_title" ],
921
+ color = self .error_color ,
922
+ description = self .config ["disabled_current_thread_response" ],
923
+ )
924
+ embed .set_footer (
925
+ text = self .config ["disabled_current_thread_footer" ],
926
+ icon_url = self .get_guild_icon (guild = message .guild , size = 128 ),
927
+ )
928
+ logger .info ("A message was blocked from %s due to disabled Modmail." , message .author )
929
+ await self .add_reaction (message , blocked_emoji )
930
+ return await message .channel .send (embed = embed )
931
+ for snap in message .message_snapshots :
932
+ author = getattr (snap , "author" , None )
933
+ author_name = getattr (author , "name" , "Unknown" ) if author else "Unknown"
934
+ author_avatar = getattr (author , "display_avatar" , None )
935
+ author_avatar_url = author_avatar .url if author_avatar else None
936
+ # fix: Only use '[This is a forwarded message.]' if snap.content is actually empty
937
+ content = snap .content if snap .content else "[This is a forwarded message.]"
938
+ if snap .embeds :
939
+ content += "\n " + "\n " .join (
940
+ [e .to_dict ().get ("description" , "[embed]" ) for e in snap .embeds ]
941
+ )
942
+ if snap .attachments :
943
+ content += "\n " + "\n " .join ([a .url for a in snap .attachments ])
944
+
945
+ class DummySnap :
946
+ def __init__ (self , snap , author , content ):
947
+ self .author = author
948
+ self .content = content
949
+ self .attachments = getattr (snap , "attachments" , [])
950
+ self .stickers = getattr (snap , "stickers" , [])
951
+ self .created_at = getattr (snap , "created_at" , message .created_at )
952
+ self .embeds = getattr (snap , "embeds" , [])
953
+ self .id = getattr (snap , "id" , 0 )
954
+
955
+ dummy_msg = DummySnap (snap , author , content )
956
+ await thread .send (dummy_msg )
957
+ await self .add_reaction (message , sent_emoji )
958
+ self .dispatch ("thread_reply" , thread , False , message , False , False )
959
+ return
960
+ else :
961
+ message .content = "[Forwarded message with no content]"
962
+ # 2. Single-message forward (MessageType.forward)
963
+ elif getattr (message , "type" , None ) == getattr (discord .MessageType , "forward" , None ):
964
+ # Check for message.reference and its type
965
+ ref = getattr (message , "reference" , None )
966
+ if ref and getattr (ref , "type" , None ) == getattr (discord , "MessageReferenceType" , None ).forward :
967
+ # Try to fetch the referenced message
968
+ ref_msg = None
969
+ try :
970
+ if ref .resolved :
971
+ ref_msg = ref .resolved
972
+ elif ref .message_id and ref .channel_id :
973
+ channel = self .get_channel (ref .channel_id ) or (
974
+ await self .fetch_channel (ref .channel_id )
975
+ )
976
+ ref_msg = await channel .fetch_message (ref .message_id )
977
+ except Exception :
978
+ ref_msg = None
979
+ if ref_msg :
980
+ # Forward the referenced message as if it was sent
981
+ thread = await self .threads .find (recipient = message .author )
982
+ if thread is None :
983
+ delta = await self .get_thread_cooldown (message .author )
984
+ if delta :
985
+ await message .channel .send (
986
+ embed = discord .Embed (
987
+ title = self .config ["cooldown_thread_title" ],
988
+ description = self .config ["cooldown_thread_response" ].format (delta = delta ),
989
+ color = self .error_color ,
990
+ )
991
+ )
992
+ return
993
+ if self .config ["dm_disabled" ] in (DMDisabled .NEW_THREADS , DMDisabled .ALL_THREADS ):
994
+ embed = discord .Embed (
995
+ title = self .config ["disabled_new_thread_title" ],
996
+ color = self .error_color ,
997
+ description = self .config ["disabled_new_thread_response" ],
998
+ )
999
+ embed .set_footer (
1000
+ text = self .config ["disabled_new_thread_footer" ],
1001
+ icon_url = self .get_guild_icon (guild = message .guild , size = 128 ),
1002
+ )
1003
+ logger .info (
1004
+ "A new thread was blocked from %s due to disabled Modmail." , message .author
1005
+ )
1006
+ await self .add_reaction (message , blocked_emoji )
1007
+ return await message .channel .send (embed = embed )
1008
+ thread = await self .threads .create (message .author , message = message )
1009
+ else :
1010
+ if self .config ["dm_disabled" ] == DMDisabled .ALL_THREADS :
1011
+ embed = discord .Embed (
1012
+ title = self .config ["disabled_current_thread_title" ],
1013
+ color = self .error_color ,
1014
+ description = self .config ["disabled_current_thread_response" ],
1015
+ )
1016
+ embed .set_footer (
1017
+ text = self .config ["disabled_current_thread_footer" ],
1018
+ icon_url = self .get_guild_icon (guild = message .guild , size = 128 ),
1019
+ )
1020
+ logger .info (
1021
+ "A message was blocked from %s due to disabled Modmail." , message .author
1022
+ )
1023
+ await self .add_reaction (message , blocked_emoji )
1024
+ return await message .channel .send (embed = embed )
1025
+ await thread .send (ref_msg )
1026
+ await self .add_reaction (message , sent_emoji )
1027
+ self .dispatch ("thread_reply" , thread , False , message , False , False )
1028
+ return
1029
+ else :
1030
+ message .content = "[Forwarded message with no content]"
1031
+
882
1032
if message .type not in [discord .MessageType .default , discord .MessageType .reply ]:
883
1033
return
884
1034
885
1035
thread = await self .threads .find (recipient = message .author )
1036
+ if thread and thread .snoozed :
1037
+ await thread .restore_from_snooze ()
1038
+ self .threads .cache [thread .id ] = thread
1039
+ # Update the DB with the new channel_id after restoration
1040
+ if thread .channel :
1041
+ await self .api .logs .update_one (
1042
+ {"recipient.id" : str (thread .id )},
1043
+ {"$set" : {"channel_id" : str (thread .channel .id )}}
1044
+ )
1045
+ # Re-fetch the thread object to ensure channel is valid
1046
+ thread = await self .threads .find (recipient = message .author )
1047
+
886
1048
if thread is None :
887
1049
delta = await self .get_thread_cooldown (message .author )
888
1050
if delta :
@@ -1820,7 +1982,7 @@ def main():
1820
1982
sys .exit (0 )
1821
1983
1822
1984
# check discord version
1823
- discord_version = "2.3 .2"
1985
+ discord_version = "2.5 .2"
1824
1986
if discord .__version__ != discord_version :
1825
1987
logger .error (
1826
1988
"Dependencies are not updated, run pipenv install. discord.py version expected %s, received %s" ,
0 commit comments