Skip to content

Commit af13ec4

Browse files
committed
Merge branch 'Jerrie-Aries-topic-regex' into development
2 parents 5f113f7 + 4eca273 commit af13ec4

File tree

3 files changed

+106
-38
lines changed

3 files changed

+106
-38
lines changed

cogs/modmail.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1855,7 +1855,7 @@ async def repair(self, ctx):
18551855
and message.embeds[0].color.value == self.bot.main_color
18561856
and message.embeds[0].footer.text
18571857
):
1858-
user_id = match_user_id(message.embeds[0].footer.text)
1858+
user_id = match_user_id(message.embeds[0].footer.text, any_string=True)
18591859
other_recipients = match_other_recipients(ctx.channel.topic)
18601860
for n, uid in enumerate(other_recipients):
18611861
other_recipients[n] = self.bot.get_user(uid) or await self.bot.fetch_user(uid)

core/thread.py

+49-22
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
from core.utils import (
1818
is_image_url,
1919
days,
20+
parse_channel_topic,
2021
match_title,
2122
match_user_id,
22-
match_other_recipients,
2323
truncate,
2424
get_top_role,
2525
create_thread_channel,
@@ -119,17 +119,16 @@ def cancelled(self, flag: bool):
119119

120120
@classmethod
121121
async def from_channel(cls, manager: "ThreadManager", channel: discord.TextChannel) -> "Thread":
122-
recipient_id = match_user_id(
123-
channel.topic
124-
) # there is a chance it grabs from another recipient's main thread
122+
# there is a chance it grabs from another recipient's main thread
123+
_, recipient_id, other_ids = parse_channel_topic(channel.topic)
125124

126125
if recipient_id in manager.cache:
127126
thread = manager.cache[recipient_id]
128127
else:
129128
recipient = manager.bot.get_user(recipient_id) or await manager.bot.fetch_user(recipient_id)
130129

131130
other_recipients = []
132-
for uid in match_other_recipients(channel.topic):
131+
for uid in other_ids:
133132
try:
134133
other_recipient = manager.bot.get_user(uid) or await manager.bot.fetch_user(uid)
135134
except discord.NotFound:
@@ -1136,10 +1135,16 @@ def get_notifications(self) -> str:
11361135
return " ".join(set(mentions))
11371136

11381137
async def set_title(self, title: str) -> None:
1138+
topic = f"Title: {title}\n"
1139+
11391140
user_id = match_user_id(self.channel.topic)
1140-
ids = ",".join(i.id for i in self._other_recipients)
1141+
topic += f"User ID: {user_id}"
1142+
1143+
if self._other_recipients:
1144+
ids = ",".join(str(i.id) for i in self._other_recipients)
1145+
topic += f"\nOther Recipients: {ids}"
11411146

1142-
await self.channel.edit(topic=f"Title: {title}\nUser ID: {user_id}\nOther Recipients: {ids}")
1147+
await self.channel.edit(topic=topic)
11431148

11441149
async def _update_users_genesis(self):
11451150
genesis_message = await self.get_genesis_message()
@@ -1162,24 +1167,37 @@ async def _update_users_genesis(self):
11621167
await genesis_message.edit(embed=embed)
11631168

11641169
async def add_users(self, users: typing.List[typing.Union[discord.Member, discord.User]]) -> None:
1165-
title = match_title(self.channel.topic)
1166-
user_id = match_user_id(self.channel.topic)
1167-
self._other_recipients += users
1170+
topic = ""
1171+
title, user_id, _ = parse_channel_topic(self.channel.topic)
1172+
if title is not None:
1173+
topic += f"Title: {title}\n"
11681174

1175+
topic += f"User ID: {user_id}"
1176+
1177+
self._other_recipients += users
11691178
ids = ",".join(str(i.id) for i in self._other_recipients)
1170-
await self.channel.edit(topic=f"Title: {title}\nUser ID: {user_id}\nOther Recipients: {ids}")
11711179

1180+
topic += f"\nOther Recipients: {ids}"
1181+
1182+
await self.channel.edit(topic=topic)
11721183
await self._update_users_genesis()
11731184

11741185
async def remove_users(self, users: typing.List[typing.Union[discord.Member, discord.User]]) -> None:
1175-
title = match_title(self.channel.topic)
1176-
user_id = match_user_id(self.channel.topic)
1186+
topic = ""
1187+
title, user_id, _ = parse_channel_topic(self.channel.topic)
1188+
if title is not None:
1189+
topic += f"Title: {title}\n"
1190+
1191+
topic += f"User ID: {user_id}"
1192+
11771193
for u in users:
11781194
self._other_recipients.remove(u)
11791195

1180-
ids = ",".join(str(i.id) for i in self._other_recipients)
1181-
await self.channel.edit(topic=f"Title: {title}\nUser ID: {user_id}\nOther Recipients: {ids}")
1196+
if self._other_recipients:
1197+
ids = ",".join(str(i.id) for i in self._other_recipients)
1198+
topic += f"\nOther Recipients: {ids}"
11821199

1200+
await self.channel.edit(topic=topic)
11831201
await self._update_users_genesis()
11841202

11851203

@@ -1240,16 +1258,24 @@ async def find(
12401258
await thread.close(closer=self.bot.user, silent=True, delete_channel=False)
12411259
thread = None
12421260
else:
1261+
1262+
def check(topic):
1263+
_, user_id, other_ids = parse_channel_topic(topic)
1264+
return recipient_id == user_id or recipient_id in other_ids
1265+
12431266
channel = discord.utils.find(
1244-
lambda x: str(recipient_id) in x.topic if x.topic else False,
1267+
lambda x: (check(x.topic)) if x.topic else False,
12451268
self.bot.modmail_guild.text_channels,
12461269
)
12471270

12481271
if channel:
12491272
thread = await Thread.from_channel(self, channel)
12501273
if thread.recipient:
1251-
# only save if data is valid
1252-
self.cache[recipient_id] = thread
1274+
# only save if data is valid.
1275+
# also the recipient_id here could belong to other recipient,
1276+
# it would be wrong if we set it as the dict key,
1277+
# so we use the thread id instead
1278+
self.cache[thread.id] = thread
12531279
thread.ready = True
12541280

12551281
if thread and recipient_id not in [x.id for x in thread.recipients]:
@@ -1265,10 +1291,11 @@ async def _find_from_channel(self, channel):
12651291
searching channel history for genesis embed and
12661292
extracts user_id from that.
12671293
"""
1268-
user_id = -1
12691294

1270-
if channel.topic:
1271-
user_id = match_user_id(channel.topic)
1295+
if not channel.topic:
1296+
return None
1297+
1298+
_, user_id, other_ids = parse_channel_topic(channel.topic)
12721299

12731300
if user_id == -1:
12741301
return None
@@ -1282,7 +1309,7 @@ async def _find_from_channel(self, channel):
12821309
recipient = None
12831310

12841311
other_recipients = []
1285-
for uid in match_other_recipients(channel.topic):
1312+
for uid in other_ids:
12861313
try:
12871314
other_recipient = self.bot.get_user(uid) or await self.bot.fetch_user(uid)
12881315
except discord.NotFound:

core/utils.py

+56-15
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
"human_join",
2121
"days",
2222
"cleanup_code",
23+
"parse_channel_topic",
2324
"match_title",
2425
"match_user_id",
2526
"match_other_recipients",
27+
"create_thread_channel",
2628
"create_not_found_embed",
2729
"parse_alias",
2830
"normalize_alias",
@@ -218,9 +220,45 @@ def cleanup_code(content: str) -> str:
218220
return content.strip("` \n")
219221

220222

221-
TOPIC_OTHER_RECIPIENTS_REGEX = re.compile(r"Other Recipients:\s*((?:\d{17,21},*)+)", flags=re.IGNORECASE)
222-
TOPIC_TITLE_REGEX = re.compile(r"\bTitle: (.*)\n(?:User ID: )\b", flags=re.IGNORECASE | re.DOTALL)
223-
TOPIC_UID_REGEX = re.compile(r"\bUser ID:\s*(\d{17,21})\b", flags=re.IGNORECASE)
223+
TOPIC_REGEX = re.compile(
224+
r"(?:\bTitle:\s*(?P<title>.*)\n)?"
225+
r"\bUser ID:\s*(?P<user_id>\d{17,21})\b"
226+
r"(?:\nOther Recipients:\s*(?P<other_ids>\d{17,21}(?:(?:\s*,\s*)\d{17,21})*)\b)?",
227+
flags=re.IGNORECASE | re.DOTALL,
228+
)
229+
UID_REGEX = re.compile(r"\bUser ID:\s*(\d{17,21})\b", flags=re.IGNORECASE)
230+
231+
232+
def parse_channel_topic(text: str) -> typing.Tuple[typing.Optional[str], int, typing.List[int]]:
233+
"""
234+
A helper to parse channel topics and respectivefully returns all the required values
235+
at once.
236+
237+
Parameters
238+
----------
239+
text : str
240+
The text of channel topic.
241+
242+
Returns
243+
-------
244+
Tuple[Optional[str], int, List[int]]
245+
A tuple of title, user ID, and other recipients IDs.
246+
"""
247+
title, user_id, other_ids = None, -1, []
248+
match = TOPIC_REGEX.search(text)
249+
if match is not None:
250+
groupdict = match.groupdict()
251+
title = groupdict["title"]
252+
253+
# user ID string is the required one in regex, so if match is found
254+
# the value of this won't be None
255+
user_id = int(groupdict["user_id"])
256+
257+
oth_ids = groupdict["other_ids"]
258+
if oth_ids:
259+
other_ids = list(map(int, oth_ids.split(",")))
260+
261+
return title, user_id, other_ids
224262

225263

226264
def match_title(text: str) -> str:
@@ -237,29 +275,35 @@ def match_title(text: str) -> str:
237275
Optional[str]
238276
The title if found.
239277
"""
240-
match = TOPIC_TITLE_REGEX.search(text)
241-
if match is not None:
242-
return match.group(1)
278+
return parse_channel_topic(text)[0]
243279

244280

245-
def match_user_id(text: str) -> int:
281+
def match_user_id(text: str, any_string: bool = False) -> int:
246282
"""
247283
Matches a user ID in the format of "User ID: 12345".
248284
249285
Parameters
250286
----------
251287
text : str
252288
The text of the user ID.
289+
any_string: bool
290+
Whether to search any string that matches the UID_REGEX, e.g. not from channel topic.
291+
Defaults to False.
253292
254293
Returns
255294
-------
256295
int
257296
The user ID if found. Otherwise, -1.
258297
"""
259-
match = TOPIC_UID_REGEX.search(text)
260-
if match is not None:
261-
return int(match.group(1))
262-
return -1
298+
user_id = -1
299+
if any_string:
300+
match = UID_REGEX.search(text)
301+
if match is not None:
302+
user_id = int(match.group(1))
303+
else:
304+
user_id = parse_channel_topic(text)[1]
305+
306+
return user_id
263307

264308

265309
def match_other_recipients(text: str) -> typing.List[int]:
@@ -276,10 +320,7 @@ def match_other_recipients(text: str) -> typing.List[int]:
276320
List[int]
277321
The list of other recipients IDs.
278322
"""
279-
match = TOPIC_OTHER_RECIPIENTS_REGEX.search(text)
280-
if match is not None:
281-
return list(map(int, match.group(1).split(",")))
282-
return []
323+
return parse_channel_topic(text)[2]
283324

284325

285326
def create_not_found_embed(word, possibilities, name, n=2, cutoff=0.6) -> discord.Embed:

0 commit comments

Comments
 (0)