1
1
import asyncio
2
+ import re
2
3
from datetime import datetime
3
4
from itertools import zip_longest
4
5
from typing import Optional , Union
14
15
from core import checks
15
16
from core .models import PermissionLevel , getLogger
16
17
from core .paginator import EmbedPaginatorSession
18
+ from core .thread import Thread
17
19
from core .time import UserFriendlyTime , human_timedelta
18
20
from core .utils import (
19
21
format_preview ,
22
24
format_description ,
23
25
trigger_typing ,
24
26
escape_code_block ,
27
+ match_user_id ,
28
+ format_channel_name ,
25
29
)
26
30
27
31
logger = getLogger (__name__ )
@@ -841,23 +845,6 @@ async def note(self, ctx, *, msg: str = ""):
841
845
msg = await ctx .thread .note (ctx .message )
842
846
await msg .pin ()
843
847
844
- async def find_linked_message (self , ctx , message_id ):
845
- linked_message_id = None
846
-
847
- async for msg in ctx .channel .history ():
848
- if message_id is None and msg .embeds :
849
- embed = msg .embeds [0 ]
850
- if embed .color .value != self .bot .mod_color or not embed .author .url :
851
- continue
852
- # TODO: use regex to find the linked message id
853
- linked_message_id = str (embed .author .url ).split ("/" )[- 1 ]
854
-
855
- elif message_id and msg .id == message_id :
856
- url = msg .embeds [0 ].author .url
857
- linked_message_id = str (url ).split ("/" )[- 1 ]
858
-
859
- return linked_message_id
860
-
861
848
@commands .command ()
862
849
@checks .has_permissions (PermissionLevel .SUPPORTER )
863
850
@checks .thread_only ()
@@ -867,12 +854,14 @@ async def edit(self, ctx, message_id: Optional[int] = None, *, message: str):
867
854
868
855
If no `message_id` is provided,
869
856
the last message sent by a staff will be edited.
857
+
858
+ Note: attachments **cannot** be edited.
870
859
"""
871
860
thread = ctx .thread
872
861
873
- linked_message_id = await self . find_linked_message ( ctx , message_id )
874
-
875
- if linked_message_id is None :
862
+ try :
863
+ await thread . edit_message ( message_id , message )
864
+ except ValueError :
876
865
return await ctx .send (
877
866
embed = discord .Embed (
878
867
title = "Failed" ,
@@ -881,16 +870,8 @@ async def edit(self, ctx, message_id: Optional[int] = None, *, message: str):
881
870
)
882
871
)
883
872
884
- await asyncio .gather (
885
- thread .edit_message (linked_message_id , message ),
886
- self .bot .api .edit_message (linked_message_id , message ),
887
- )
888
-
889
873
sent_emoji , _ = await self .bot .retrieve_emoji ()
890
- try :
891
- await ctx .message .add_reaction (sent_emoji )
892
- except (discord .HTTPException , discord .InvalidArgument ):
893
- pass
874
+ return await ctx .message .add_reaction (sent_emoji )
894
875
895
876
@commands .command ()
896
877
@checks .has_permissions (PermissionLevel .SUPPORTER )
@@ -1168,7 +1149,7 @@ async def unblock(self, ctx, *, user: User = None):
1168
1149
@commands .command ()
1169
1150
@checks .has_permissions (PermissionLevel .SUPPORTER )
1170
1151
@checks .thread_only ()
1171
- async def delete (self , ctx , message_id : Optional [ int ] = None ):
1152
+ async def delete (self , ctx , message_id : int = None ):
1172
1153
"""
1173
1154
Delete a message that was sent using the reply command or a note.
1174
1155
@@ -1179,15 +1160,9 @@ async def delete(self, ctx, message_id: Optional[int] = None):
1179
1160
"""
1180
1161
thread = ctx .thread
1181
1162
1182
- if message_id is not None :
1183
- try :
1184
- message_id = int (message_id )
1185
- except ValueError :
1186
- raise commands .BadArgument ("A message ID needs to be specified." )
1187
-
1188
- linked_message_id = await self .find_linked_message (ctx , message_id )
1189
-
1190
- if linked_message_id is None :
1163
+ try :
1164
+ await thread .delete_message (message_id )
1165
+ except ValueError :
1191
1166
return await ctx .send (
1192
1167
embed = discord .Embed (
1193
1168
title = "Failed" ,
@@ -1196,12 +1171,119 @@ async def delete(self, ctx, message_id: Optional[int] = None):
1196
1171
)
1197
1172
)
1198
1173
1199
- await thread .delete_message (linked_message_id )
1200
1174
sent_emoji , _ = await self .bot .retrieve_emoji ()
1201
- try :
1202
- await ctx .message .add_reaction (sent_emoji )
1203
- except (discord .HTTPException , discord .InvalidArgument ):
1204
- pass
1175
+ return await ctx .message .add_reaction (sent_emoji )
1176
+
1177
+ @commands .command ()
1178
+ @checks .has_permissions (PermissionLevel .SUPPORTER )
1179
+ async def repair (self , ctx ):
1180
+ """
1181
+ Repair a thread broken by Discord.
1182
+ """
1183
+ sent_emoji , blocked_emoji = await self .bot .retrieve_emoji ()
1184
+
1185
+ if ctx .thread :
1186
+ user_id = match_user_id (ctx .channel .topic )
1187
+ if user_id == - 1 :
1188
+ logger .info ("Setting current channel's topic to User ID." )
1189
+ await ctx .channel .edit (topic = f"User ID: { ctx .thread .id } " )
1190
+ return await ctx .message .add_reaction (sent_emoji )
1191
+
1192
+ logger .info ("Attempting to fix a broken thread %s." , ctx .channel .name )
1193
+
1194
+ # Search cache for channel
1195
+ user_id , thread = next (
1196
+ ((k , v ) for k , v in self .bot .threads .cache .items () if v .channel == ctx .channel ),
1197
+ (- 1 , None ),
1198
+ )
1199
+ if thread is not None :
1200
+ logger .debug ("Found thread with tempered ID." )
1201
+ await ctx .channel .edit (reason = "Fix broken Modmail thread" , topic = f"User ID: { user_id } " )
1202
+ return await ctx .message .add_reaction (sent_emoji )
1203
+
1204
+ # find genesis message to retrieve User ID
1205
+ async for message in ctx .channel .history (limit = 10 , oldest_first = True ):
1206
+ if (
1207
+ message .author == self .bot .user
1208
+ and message .embeds
1209
+ and message .embeds [0 ].color
1210
+ and message .embeds [0 ].color .value == self .bot .main_color
1211
+ and message .embeds [0 ].footer .text
1212
+ ):
1213
+ user_id = match_user_id (message .embeds [0 ].footer .text )
1214
+ if user_id != - 1 :
1215
+ recipient = self .bot .get_user (user_id )
1216
+ if recipient is None :
1217
+ self .bot .threads .cache [user_id ] = thread = Thread (
1218
+ self .bot .threads , user_id , ctx .channel
1219
+ )
1220
+ else :
1221
+ self .bot .threads .cache [user_id ] = thread = Thread (
1222
+ self .bot .threads , recipient , ctx .channel
1223
+ )
1224
+ thread .ready = True
1225
+ logger .info (
1226
+ "Setting current channel's topic to User ID and created new thread."
1227
+ )
1228
+ await ctx .channel .edit (
1229
+ reason = "Fix broken Modmail thread" , topic = f"User ID: { user_id } "
1230
+ )
1231
+ return await ctx .message .add_reaction (sent_emoji )
1232
+
1233
+ else :
1234
+ logger .warning ("No genesis message found." )
1235
+
1236
+ # match username from channel name
1237
+ # username-1234, username-1234_1, username-1234_2
1238
+ m = re .match (r"^(.+)-(\d{4})(?:_\d+)?$" , ctx .channel .name )
1239
+ if m is not None :
1240
+ users = set (
1241
+ filter (
1242
+ lambda member : member .name == m .group (1 )
1243
+ and member .discriminator == m .group (2 ),
1244
+ ctx .guild .members ,
1245
+ )
1246
+ )
1247
+ if len (users ) == 1 :
1248
+ user = users [0 ]
1249
+ name = format_channel_name (
1250
+ user , self .bot .modmail_guild , exclude_channel = ctx .channel
1251
+ )
1252
+ recipient = self .bot .get_user (user .id )
1253
+ if user .id in self .bot .threads .cache :
1254
+ thread = self .bot .threads .cache [user .id ]
1255
+ if thread .channel :
1256
+ embed = discord .Embed (
1257
+ title = "Delete Channel" ,
1258
+ description = "This thread channel is no longer in use. "
1259
+ f"All messages will be directed to { ctx .channel .mention } instead." ,
1260
+ color = self .bot .error_color ,
1261
+ )
1262
+ embed .set_footer (
1263
+ text = 'Please manually delete this channel, do not use "{prefix}close".'
1264
+ )
1265
+ try :
1266
+ await thread .channel .send (embed = embed )
1267
+ except discord .HTTPException :
1268
+ pass
1269
+ if recipient is None :
1270
+ self .bot .threads .cache [user .id ] = thread = Thread (
1271
+ self .bot .threads , user_id , ctx .channel
1272
+ )
1273
+ else :
1274
+ self .bot .threads .cache [user .id ] = thread = Thread (
1275
+ self .bot .threads , recipient , ctx .channel
1276
+ )
1277
+ thread .ready = True
1278
+ logger .info ("Setting current channel's topic to User ID and created new thread." )
1279
+ await ctx .channel .edit (
1280
+ reason = "Fix broken Modmail thread" , name = name , topic = f"User ID: { user .id } "
1281
+ )
1282
+ return await ctx .message .add_reaction (sent_emoji )
1283
+
1284
+ elif len (users ) >= 2 :
1285
+ logger .info ("Multiple users with the same name and discriminator." )
1286
+ return await ctx .message .add_reaction (blocked_emoji )
1205
1287
1206
1288
@commands .command ()
1207
1289
@checks .has_permissions (PermissionLevel .ADMINISTRATOR )
0 commit comments