21
21
match_user_id ,
22
22
match_other_recipients ,
23
23
truncate ,
24
- format_channel_name ,
25
24
get_top_hoisted_role ,
25
+ create_thread_channel ,
26
+ get_joint_id ,
26
27
)
27
28
28
29
logger = getLogger (__name__ )
@@ -36,7 +37,7 @@ def __init__(
36
37
manager : "ThreadManager" ,
37
38
recipient : typing .Union [discord .Member , discord .User , int ],
38
39
channel : typing .Union [discord .DMChannel , discord .TextChannel ] = None ,
39
- other_recipients : typing .List [typing .Union [discord .Member , discord .User ]] = [] ,
40
+ other_recipients : typing .List [typing .Union [discord .Member , discord .User ]] = None ,
40
41
):
41
42
self .manager = manager
42
43
self .bot = manager .bot
@@ -48,7 +49,7 @@ def __init__(
48
49
raise CommandError ("Recipient cannot be a bot." )
49
50
self ._id = recipient .id
50
51
self ._recipient = recipient
51
- self ._other_recipients = other_recipients
52
+ self ._other_recipients = other_recipients or []
52
53
self ._channel = channel
53
54
self .genesis_message = None
54
55
self ._ready_event = asyncio .Event ()
@@ -127,9 +128,13 @@ async def from_channel(cls, manager: "ThreadManager", channel: discord.TextChann
127
128
else :
128
129
recipient = manager .bot .get_user (recipient_id ) or await manager .bot .fetch_user (recipient_id )
129
130
130
- other_recipients = match_other_recipients (channel .topic )
131
- for n , uid in enumerate (other_recipients ):
132
- other_recipients [n ] = manager .bot .get_user (uid ) or await manager .bot .fetch_user (uid )
131
+ other_recipients = []
132
+ for uid in match_other_recipients (channel .topic ):
133
+ try :
134
+ other_recipient = manager .bot .get_user (uid ) or await manager .bot .fetch_user (uid )
135
+ except discord .NotFound :
136
+ continue
137
+ other_recipients .append (other_recipient )
133
138
134
139
thread = cls (manager , recipient or recipient_id , channel , other_recipients )
135
140
@@ -149,33 +154,19 @@ async def setup(self, *, creator=None, category=None, initial_message=None):
149
154
overwrites = None
150
155
151
156
try :
152
- channel = await self .bot .modmail_guild .create_text_channel (
153
- name = format_channel_name (self .bot , recipient ),
154
- category = category ,
155
- overwrites = overwrites ,
156
- reason = "Creating a thread channel." ,
157
- )
158
- except discord .HTTPException as e :
159
- # try again but null-discrim (name could be banned)
160
- try :
161
- channel = await self .bot .modmail_guild .create_text_channel (
162
- name = format_channel_name (self .bot , recipient , force_null = True ),
163
- category = category ,
164
- overwrites = overwrites ,
165
- reason = "Creating a thread channel." ,
166
- )
167
- except discord .HTTPException as e : # Failed to create due to missing perms.
168
- logger .critical ("An error occurred while creating a thread." , exc_info = True )
169
- self .manager .cache .pop (self .id )
157
+ channel = await create_thread_channel (self .bot , recipient , category , overwrites )
158
+ except discord .HTTPException as e : # Failed to create due to missing perms.
159
+ logger .critical ("An error occurred while creating a thread." , exc_info = True )
160
+ self .manager .cache .pop (self .id )
170
161
171
- embed = discord .Embed (color = self .bot .error_color )
172
- embed .title = "Error while trying to create a thread."
173
- embed .description = str (e )
174
- embed .add_field (name = "Recipient" , value = recipient .mention )
162
+ embed = discord .Embed (color = self .bot .error_color )
163
+ embed .title = "Error while trying to create a thread."
164
+ embed .description = str (e )
165
+ embed .add_field (name = "Recipient" , value = recipient .mention )
175
166
176
- if self .bot .log_channel is not None :
177
- await self .bot .log_channel .send (embed = embed )
178
- return
167
+ if self .bot .log_channel is not None :
168
+ await self .bot .log_channel .send (embed = embed )
169
+ return
179
170
180
171
self ._channel = channel
181
172
@@ -209,7 +200,7 @@ async def send_genesis_message():
209
200
logger .error ("Failed unexpectedly:" , exc_info = True )
210
201
211
202
async def send_recipient_genesis_message ():
212
- # Once thread is ready, tell the recipient.
203
+ # Once thread is ready, tell the recipient (don't send if using contact on others)
213
204
thread_creation_response = self .bot .config ["thread_creation_response" ]
214
205
215
206
embed = discord .Embed (
@@ -585,7 +576,7 @@ async def find_linked_messages(
585
576
either_direction : bool = False ,
586
577
message1 : discord .Message = None ,
587
578
note : bool = True ,
588
- ) -> typing .Tuple [discord .Message , typing .Optional [discord .Message ]]:
579
+ ) -> typing .Tuple [discord .Message , typing .List [ typing . Optional [discord .Message ] ]]:
589
580
if message1 is not None :
590
581
if not message1 .embeds or not message1 .embeds [0 ].author .url or message1 .author != self .bot .user :
591
582
raise ValueError ("Malformed thread message." )
@@ -698,49 +689,84 @@ async def delete_message(
698
689
if tasks :
699
690
await asyncio .gather (* tasks )
700
691
701
- async def find_linked_message_from_dm (self , message , either_direction = False ):
702
- if either_direction and message .embeds and message .embeds [0 ].author .url :
703
- compare_url = message .embeds [0 ].author .url
704
- compare_id = compare_url .split ("#" )[- 1 ]
705
- else :
706
- compare_url = None
707
- compare_id = None
692
+ async def find_linked_message_from_dm (
693
+ self , message , either_direction = False
694
+ ) -> typing .List [discord .Message ]:
695
+
696
+ joint_id = None
697
+ if either_direction :
698
+ joint_id = get_joint_id (message )
699
+ # could be None too, if that's the case we'll reassign this variable from
700
+ # thread message we fetch in the next step
708
701
702
+ linked_messages = []
709
703
if self .channel is not None :
710
- async for linked_message in self .channel .history ():
711
- if not linked_message .embeds :
704
+ async for msg in self .channel .history ():
705
+ if not msg .embeds :
712
706
continue
713
- url = linked_message .embeds [0 ].author .url
714
- if not url :
715
- continue
716
- if url == compare_url :
717
- return linked_message
718
707
719
- msg_id = url . split ( "#" )[ - 1 ]
720
- if not msg_id . isdigit () :
708
+ msg_joint_id = get_joint_id ( msg )
709
+ if msg_joint_id is None :
721
710
continue
722
- msg_id = int (msg_id )
723
- if int (msg_id ) == message .id :
724
- return linked_message
725
711
726
- if compare_id is not None and compare_id . isdigit () :
727
- if int ( msg_id ) == int ( compare_id ):
728
- return linked_message
712
+ if msg_joint_id == message . id :
713
+ linked_messages . append ( msg )
714
+ break
729
715
716
+ if joint_id is not None and msg_joint_id == joint_id :
717
+ linked_messages .append (msg )
718
+ break
719
+ else :
720
+ raise ValueError ("Thread channel message not found." )
721
+ else :
730
722
raise ValueError ("Thread channel message not found." )
731
723
724
+ if joint_id is None :
725
+ joint_id = get_joint_id (linked_messages [0 ])
726
+ if joint_id is None :
727
+ # still None, supress this and return the thread message
728
+ logger .error ("Malformed thread message." )
729
+ return linked_messages
730
+
731
+ for user in self .recipients :
732
+ if user .dm_channel == message .channel :
733
+ continue
734
+ async for other_msg in user .history ():
735
+ if either_direction :
736
+ if other_msg .id == joint_id :
737
+ linked_messages .append (other_msg )
738
+ break
739
+
740
+ if not other_msg .embeds :
741
+ continue
742
+
743
+ other_joint_id = get_joint_id (other_msg )
744
+ if other_joint_id is not None and other_joint_id == joint_id :
745
+ linked_messages .append (other_msg )
746
+ break
747
+ else :
748
+ logger .error ("Linked message from recipient %s not found." , user )
749
+
750
+ return linked_messages
751
+
732
752
async def edit_dm_message (self , message : discord .Message , content : str ) -> None :
733
753
try :
734
- linked_message = await self .find_linked_message_from_dm (message )
754
+ linked_messages = await self .find_linked_message_from_dm (message )
735
755
except ValueError :
736
756
logger .warning ("Failed to edit message." , exc_info = True )
737
757
raise
738
- embed = linked_message .embeds [0 ]
739
- embed .add_field (name = "**Edited, former message:**" , value = embed .description )
740
- embed .description = content
741
- await asyncio .gather (self .bot .api .edit_message (message .id , content ), linked_message .edit (embed = embed ))
742
758
743
- async def note (self , message : discord .Message , persistent = False , thread_creation = False ) -> None :
759
+ for msg in linked_messages :
760
+ embed = msg .embeds [0 ]
761
+ if isinstance (msg .channel , discord .TextChannel ):
762
+ # just for thread channel, we put the old message in embed field
763
+ embed .add_field (name = "**Edited, former message:**" , value = embed .description )
764
+ embed .description = content
765
+ await asyncio .gather (self .bot .api .edit_message (message .id , content ), msg .edit (embed = embed ))
766
+
767
+ async def note (
768
+ self , message : discord .Message , persistent = False , thread_creation = False
769
+ ) -> discord .Message :
744
770
if not message .content and not message .attachments :
745
771
raise MissingRequiredArgument (SimpleNamespace (name = "msg" ))
746
772
@@ -758,7 +784,9 @@ async def note(self, message: discord.Message, persistent=False, thread_creation
758
784
759
785
return msg
760
786
761
- async def reply (self , message : discord .Message , anonymous : bool = False , plain : bool = False ) -> None :
787
+ async def reply (
788
+ self , message : discord .Message , anonymous : bool = False , plain : bool = False
789
+ ) -> typing .Tuple [discord .Message , discord .Message ]:
762
790
if not message .content and not message .attachments :
763
791
raise MissingRequiredArgument (SimpleNamespace (name = "msg" ))
764
792
if not any (g .get_member (self .id ) for g in self .bot .guilds ):
@@ -854,7 +882,8 @@ async def send(
854
882
thread_creation : bool = False ,
855
883
) -> None :
856
884
857
- self .bot .loop .create_task (self ._restart_close_timer ()) # Start or restart thread auto close
885
+ if not note and from_mod :
886
+ self .bot .loop .create_task (self ._restart_close_timer ()) # Start or restart thread auto close
858
887
859
888
if self .close_task is not None :
860
889
# cancel closing if a thread message is sent.
@@ -1208,9 +1237,13 @@ async def _find_from_channel(self, channel):
1208
1237
except discord .NotFound :
1209
1238
recipient = None
1210
1239
1211
- other_recipients = match_other_recipients (channel .topic )
1212
- for n , uid in enumerate (other_recipients ):
1213
- other_recipients [n ] = self .bot .get_user (uid ) or await self .bot .fetch_user (uid )
1240
+ other_recipients = []
1241
+ for uid in match_other_recipients (channel .topic ):
1242
+ try :
1243
+ other_recipient = self .bot .get_user (uid ) or await self .bot .fetch_user (uid )
1244
+ except discord .NotFound :
1245
+ continue
1246
+ other_recipients .append (other_recipient )
1214
1247
1215
1248
if recipient is None :
1216
1249
thread = Thread (self , user_id , channel , other_recipients )
0 commit comments