Skip to content

Commit 6cb7155

Browse files
authored
Merge branch 'development' into development
Signed-off-by: Taku <45324516+Taaku18@users.noreply.github.com>
2 parents dd48ce7 + 829a364 commit 6cb7155

File tree

7 files changed

+62
-75
lines changed

7 files changed

+62
-75
lines changed

CHANGELOG.md

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,32 @@ however, insignificant breaking changes do not guarantee a major version bump, s
88

99
# v4.2.1
1010

11+
### Added
12+
* `unsnooze_history_limit`: Limits the number of messages replayed when unsnoozing (genesis message and notes are always shown).
13+
* `snooze_behavior`: Choose between `delete` (legacy) or `move` behavior for snoozing.
14+
* `snoozed_category_id`: Target category for `move` snoozing; required when `snooze_behavior` is `move`.
15+
* Thread-creation menu: Adds an interactive select step before a thread channel is created.
16+
* Commands:
17+
* `threadmenu toggle`: Enable/disable the menu.
18+
* `threadmenu show`: List current top-level options.
19+
* `threadmenu option add`: Interactive wizard to create an option.
20+
* `threadmenu option edit/remove/show`: Manage or inspect an existing option.
21+
* `threadmenu submenu create/delete/list/show`: Manage submenus.
22+
* `threadmenu submenu option add/edit/remove`: Manage options inside a submenu.
23+
* Configuration / Behavior:
24+
* Per-option `category` targeting when creating a thread; falls back to `main_category_id` if invalid/missing.
25+
* Optional selection logging (`thread_creation_menu_selection_log`) posts the chosen option in the new thread.
26+
* Anonymous prompt support (`thread_creation_menu_anonymous_menu`).
27+
28+
### Changed
29+
- Renamed `max_snooze_time` to `snooze_default_duration`. The old config will be invalidated.
30+
- When `snooze_behavior` is set to `move`, the snoozed category now has a hard limit of 49 channels. New snoozes are blocked once it’s full until space is freed.
31+
- When switching `snooze_behavior` to `move` via `?config set`, the bot reminds admins to set `snoozed_category_id` if it’s missing.
32+
- Thread-creation menu options & submenu options now support an optional per-option `category` target. The interactive wizards (`threadmenu option add` / `threadmenu submenu option add`) and edit commands allow specifying or updating a category. If the stored category is missing or invalid at selection time, channel creation automatically falls back to `main_category_id`.
33+
34+
35+
# v4.2.0
36+
1137
Upgraded discord.py to version 2.6.3, added support for CV2.
1238
Forwarded messages now properly show in threads, rather than showing as an empty embed.
1339

@@ -18,7 +44,7 @@ Forwarded messages now properly show in threads, rather than showing as an empty
1844
- Eliminated duplicate logs and notes.
1945
- Addressed inconsistent use of `logkey` after ticket restoration.
2046
- Fixed issues with identifying the user who sent internal messages.
21-
- Solved an ancient bug where closing with words like `evening` wouldnt work.
47+
- Solved an ancient bug where closing with words like `evening` wouldn't work.
2248
- Fixed the command from being included in the reply in rare conditions.
2349

2450
### Added
@@ -29,39 +55,16 @@ Commands:
2955
* `clearsnoozed`: Clears all snoozed items.
3056

3157
Configuration Options:
32-
* `snooze_default_duration`: Sets the maximum duration for snooze.
58+
* `max_snooze_time`: Sets the maximum duration for snooze.
3359
* `snooze_title`: Customizes the title for snooze notifications.
3460
* `snooze_text`: Customizes the text for snooze notifications.
3561
* `unsnooze_text`: Customizes the text for unsnooze notifications.
3662
* `unsnooze_notify_channel`: Specifies the channel for unsnooze notifications.
37-
* `unsnooze_history_limit`: Limits the number of messages replayed when unsnoozing (genesis message and notes are always shown).
38-
* `snooze_behavior`: Choose between `delete` (legacy) or `move` behavior for snoozing.
39-
* `snoozed_category_id`: Target category for `move` snoozing; required when `snooze_behavior` is `move`.
4063
* `thread_min_characters`: Minimum number of characters required.
4164
* `thread_min_characters_title`: Title shown when the message is too short.
4265
* `thread_min_characters_response`: Response shown to the user if their message is too short.
4366
* `thread_min_characters_footer`: Footer displaying the minimum required characters.
4467

45-
Features:
46-
* Thread-creation menu: Adds an interactive select step before a thread channel is created.
47-
* Commands:
48-
* `threadmenu toggle`: Enable/disable the menu.
49-
* `threadmenu show`: List current top-level options.
50-
* `threadmenu option add`: Interactive wizard to create an option.
51-
* `threadmenu option edit/remove/show`: Manage or inspect an existing option.
52-
* `threadmenu submenu create/delete/list/show`: Manage submenus.
53-
* `threadmenu submenu option add/edit/remove`: Manage options inside a submenu.
54-
* Configuration / Behavior:
55-
* Per-option `category` targeting when creating a thread; falls back to `main_category_id` if invalid/missing.
56-
* Optional selection logging (`thread_creation_menu_selection_log`) posts the chosen option in the new thread.
57-
* Anonymous prompt support (`thread_creation_menu_anonymous_menu`).
58-
59-
60-
Behavioral changes:
61-
- When `snooze_behavior` is set to `move`, the snoozed category now has a hard limit of 49 channels. New snoozes are blocked once it’s full until space is freed.
62-
- When switching `snooze_behavior` to `move` via `?config set`, the bot reminds admins to set `snoozed_category_id` if it’s missing.
63-
- Thread-creation menu options & submenu options now support an optional per-option `category` target. The interactive wizards (`threadmenu option add` / `threadmenu submenu option add`) and edit commands allow specifying or updating a category. If the stored category is missing or invalid at selection time, channel creation automatically falls back to `main_category_id`.
64-
6568
# v4.1.2
6669

6770
### Fixed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<br>
77

88
<a href="#">
9-
<img src="https://img.shields.io/badge/Latest%20Version-v4.1.2-7289da?style=for-the-badge&logo=data:image/gif;base64,R0lGODlhGAAYAPcAAAAAADQ+Yj5MdThCaEFOekNQfWt5e1lqfEdVhVpriVx1iVtsnFZpll1xlF1znFx5mGNtjGR7hWNzjGd5iWJunGNslmN0lGl1lGN5lWt7lWR0nGp1nWR5nGt8nHJ9l3J9jFxto1xtqlZqqF5zpF15pV10rVx5q1Z2ql1us150s114sV50uVd1smJupGNurGN0pGp1pGR6o2t7o2F0rml0rGR5rGt8q3J9pGNus2Fvu2Bzs2p2smR5s2t7tGF0u2N6vGp7u2l1uXJ+uGJ1wWV7w2l+xWl+yl5zwHWDiWyCmHODnHiGmHSLkW2CpGuEqnSCpHqMo3ODrHWKrXyMq3iHp3yTqGyEtWqFunKDs3uLtHSGvXSMvHuMu3eItnyTs3yTu3iUuW+RsV+ApmuCxG2Dy22LxnSMxHuMxHGGzXSLzXqLzXWFxXyTw3uSy3WSx26E0W+N2m+L1XKG03SL1XmL1HOL2HmL23WF2XiS1m+QyHOK4XmL4XaH4nGM5ImXm4WOlJSjnoOOq4OOpYSUrImXqJOcqYONt4OUtIybtIOUu4ycvImYt5OcuIyiqZekqYyiupOjvJurvJimtp+yvqKruae2ua23trLCucPHvIOMxoOUxIuaw4eYyJKbyISW1YiW0o2kyJWlxJqpxZWkzJury5aoxp6xxZ2zyZio1Jyy2Yuk0aOsyKOyxKOzzKy8zKq2yLO7xqOr1KSz1Ku706W026y73Ki21bK716u75LS95KSu4Zyx6bPDzLjGx63B2LPD1LTE3LvL3brI17/S2bbKzrTE47rF47zM5L3M67bH577R5L7R67zK8rjM8MLM3MPK1sTT3crU3MjW2dLZ2srUy8PN5MTO68nP68jL48TS5MnT5cbR68jS7M3b7Mva5dTb6MTO8srO8szF8MXR8snS8c3a8cnY+NXb9tDL7dnm6dzk8tro99Pq9uTp6+r17fr77eTr/efr+Oz09OXz/Ov0/e37/uj39fX19fX89fz+9vP1/fX9/f7//vr2/ODl4sK75yH5BAEAAAAALAAAAAAYABgAAAj/AAEAmFOnoMGDCAvGmRNHYMKHEOdIhEgRIcGKGDMeTJVO00WNBj194/frFik5IOvgqdaP371N26xpefORYq57/HI+s7Vtmy0eKCmmapcz575FPXt+SfHmociiRae1SroNWIoVTQ2uxAk15yJg1awl7ZSCaUE82uRxhTpt1TNXq0Ip2pSobIqCc0iVoqsI0ipXr1bhywfVXxYdiA3Sourz2dqc8lbNQJz4bDWqix73E7ZjhgrKOjYmvfWLbZcZqCdTPjjHGLdtibi2U5Q6NWiEqqq64ifvVe3at1nX2tTuWQ/QyIMfxHOGi+rkyeMklPMDuvWaBqlbj469IPUU2ykPCOw+53t4AAEBADs=">
9+
<img src="https://img.shields.io/badge/Latest%20Version-v4.2.0-7289da?style=for-the-badge&logo=data:image/gif;base64,R0lGODlhGAAYAPcAAAAAADQ+Yj5MdThCaEFOekNQfWt5e1lqfEdVhVpriVx1iVtsnFZpll1xlF1znFx5mGNtjGR7hWNzjGd5iWJunGNslmN0lGl1lGN5lWt7lWR0nGp1nWR5nGt8nHJ9l3J9jFxto1xtqlZqqF5zpF15pV10rVx5q1Z2ql1us150s114sV50uVd1smJupGNurGN0pGp1pGR6o2t7o2F0rml0rGR5rGt8q3J9pGNus2Fvu2Bzs2p2smR5s2t7tGF0u2N6vGp7u2l1uXJ+uGJ1wWV7w2l+xWl+yl5zwHWDiWyCmHODnHiGmHSLkW2CpGuEqnSCpHqMo3ODrHWKrXyMq3iHp3yTqGyEtWqFunKDs3uLtHSGvXSMvHuMu3eItnyTs3yTu3iUuW+RsV+ApmuCxG2Dy22LxnSMxHuMxHGGzXSLzXqLzXWFxXyTw3uSy3WSx26E0W+N2m+L1XKG03SL1XmL1HOL2HmL23WF2XiS1m+QyHOK4XmL4XaH4nGM5ImXm4WOlJSjnoOOq4OOpYSUrImXqJOcqYONt4OUtIybtIOUu4ycvImYt5OcuIyiqZekqYyiupOjvJurvJimtp+yvqKruae2ua23trLCucPHvIOMxoOUxIuaw4eYyJKbyISW1YiW0o2kyJWlxJqpxZWkzJury5aoxp6xxZ2zyZio1Jyy2Yuk0aOsyKOyxKOzzKy8zKq2yLO7xqOr1KSz1Ku706W026y73Ki21bK716u75LS95KSu4Zyx6bPDzLjGx63B2LPD1LTE3LvL3brI17/S2bbKzrTE47rF47zM5L3M67bH577R5L7R67zK8rjM8MLM3MPK1sTT3crU3MjW2dLZ2srUy8PN5MTO68nP68jL48TS5MnT5cbR68jS7M3b7Mva5dTb6MTO8srO8szF8MXR8snS8c3a8cnY+NXb9tDL7dnm6dzk8tro99Pq9uTp6+r17fr77eTr/efr+Oz09OXz/Ov0/e37/uj39fX19fX89fz+9vP1/fX9/f7//vr2/ODl4sK75yH5BAEAAAAALAAAAAAYABgAAAj/AAEAmFOnoMGDCAvGmRNHYMKHEOdIhEgRIcGKGDMeTJVO00WNBj194/frFik5IOvgqdaP371N26xpefORYq57/HI+s7Vtmy0eKCmmapcz575FPXt+SfHmociiRae1SroNWIoVTQ2uxAk15yJg1awl7ZSCaUE82uRxhTpt1TNXq0Ip2pSobIqCc0iVoqsI0ipXr1bhywfVXxYdiA3Sourz2dqc8lbNQJz4bDWqix73E7ZjhgrKOjYmvfWLbZcZqCdTPjjHGLdtibi2U5Q6NWiEqqq64ifvVe3at1nX2tTuWQ/QyIMfxHOGi+rkyeMklPMDuvWaBqlbj469IPUU2ykPCOw+53t4AAEBADs=">
1010
</a>
1111

1212
<br>

bot.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -953,10 +953,7 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
953953
)
954954
)
955955
return
956-
if self.config["dm_disabled"] in (
957-
DMDisabled.NEW_THREADS,
958-
DMDisabled.ALL_THREADS,
959-
):
956+
if self.config["dm_disabled"] in (DMDisabled.NEW_THREADS, DMDisabled.ALL_THREADS):
960957
embed = discord.Embed(
961958
title=self.config["disabled_new_thread_title"],
962959
color=self.error_color,
@@ -967,8 +964,7 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
967964
icon_url=self.get_guild_icon(guild=message.guild, size=128),
968965
)
969966
logger.info(
970-
"A new thread was blocked from %s due to disabled Modmail.",
971-
message.author,
967+
"A new thread was blocked from %s due to disabled Modmail.", message.author
972968
)
973969
await self.add_reaction(message, blocked_emoji)
974970
return await message.channel.send(embed=embed)
@@ -984,10 +980,7 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
984980
text=self.config["disabled_current_thread_footer"],
985981
icon_url=self.get_guild_icon(guild=message.guild, size=128),
986982
)
987-
logger.info(
988-
"A message was blocked from %s due to disabled Modmail.",
989-
message.author,
990-
)
983+
logger.info("A message was blocked from %s due to disabled Modmail.", message.author)
991984
await self.add_reaction(message, blocked_emoji)
992985
return await message.channel.send(embed=embed)
993986
# Extract forwarded content using utility function
@@ -1044,10 +1037,7 @@ def __init__(self, original_message, forwarded_content):
10441037
)
10451038
)
10461039
return
1047-
if self.config["dm_disabled"] in (
1048-
DMDisabled.NEW_THREADS,
1049-
DMDisabled.ALL_THREADS,
1050-
):
1040+
if self.config["dm_disabled"] in (DMDisabled.NEW_THREADS, DMDisabled.ALL_THREADS):
10511041
embed = discord.Embed(
10521042
title=self.config["disabled_new_thread_title"],
10531043
color=self.error_color,
@@ -1058,8 +1048,7 @@ def __init__(self, original_message, forwarded_content):
10581048
icon_url=self.get_guild_icon(guild=message.guild, size=128),
10591049
)
10601050
logger.info(
1061-
"A new thread was blocked from %s due to disabled Modmail.",
1062-
message.author,
1051+
"A new thread was blocked from %s due to disabled Modmail.", message.author
10631052
)
10641053
await self.add_reaction(message, blocked_emoji)
10651054
return await message.channel.send(embed=embed)
@@ -1076,8 +1065,7 @@ def __init__(self, original_message, forwarded_content):
10761065
icon_url=self.get_guild_icon(guild=message.guild, size=128),
10771066
)
10781067
logger.info(
1079-
"A message was blocked from %s due to disabled Modmail.",
1080-
message.author,
1068+
"A message was blocked from %s due to disabled Modmail.", message.author
10811069
)
10821070
await self.add_reaction(message, blocked_emoji)
10831071
return await message.channel.send(embed=embed)

cogs/modmail.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ def _resolve_user(self, user_str):
9999
return int(match.group(1))
100100
return None
101101

102+
def _resolve_user(self, user_str):
103+
"""Helper to resolve a user from mention, ID, or username."""
104+
import re
105+
106+
if not user_str:
107+
return None
108+
if user_str.isdigit():
109+
return int(user_str)
110+
match = re.match(r"<@!?(\d+)>", user_str)
111+
if match:
112+
return int(match.group(1))
113+
return None
114+
102115
@commands.command()
103116
@trigger_typing
104117
@checks.has_permissions(PermissionLevel.OWNER)
@@ -220,8 +233,7 @@ async def snippet(self, ctx, *, name: str.lower = None):
220233
description = format_description(i, names)
221234
embed = discord.Embed(color=self.bot.main_color, description=description)
222235
embed.set_author(
223-
name="Snippets",
224-
icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128),
236+
name="Snippets", icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128)
225237
)
226238
embeds.append(embed)
227239

@@ -256,10 +268,7 @@ async def snippet(self, ctx, *, name: str.lower = None):
256268

257269
embeds = [discord.Embed(color=self.bot.main_color) for _ in range((len(self.bot.snippets) // 10) + 1)]
258270
for embed in embeds:
259-
embed.set_author(
260-
name="Snippets",
261-
icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128),
262-
)
271+
embed.set_author(name="Snippets", icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128))
263272

264273
for i, snippet in enumerate(sorted(self.bot.snippets.items())):
265274
embeds[i // 10].add_field(
@@ -974,8 +983,7 @@ async def adduser(self, ctx, *users_arg: Union[discord.Member, discord.Role, str
974983
if self.bot.config["show_timestamp"]:
975984
em.timestamp = discord.utils.utcnow()
976985
em.set_footer(
977-
text=f"{users[0]}",
978-
icon_url=users[0].display_avatar.url if users[0].display_avatar else None,
986+
text=f"{users[0]}", icon_url=users[0].display_avatar.url if users[0].display_avatar else None
979987
)
980988

981989
for i in ctx.thread.recipients:
@@ -1074,8 +1082,7 @@ async def removeuser(self, ctx, *users_arg: Union[discord.Member, discord.Role,
10741082
if self.bot.config["show_timestamp"]:
10751083
em.timestamp = discord.utils.utcnow()
10761084
em.set_footer(
1077-
text=f"{users[0]}",
1078-
icon_url=users[0].display_avatar.url if users[0].display_avatar else None,
1085+
text=f"{users[0]}", icon_url=users[0].display_avatar.url if users[0].display_avatar else None
10791086
)
10801087

10811088
for i in ctx.thread.recipients:
@@ -1171,8 +1178,7 @@ async def anonadduser(self, ctx, *users_arg: Union[discord.Member, discord.Role,
11711178
if self.bot.config["show_timestamp"]:
11721179
em.timestamp = discord.utils.utcnow()
11731180
em.set_footer(
1174-
text=f"{users[0]}",
1175-
icon_url=users[0].display_avatar.url if users[0].display_avatar else None,
1181+
text=f"{users[0]}", icon_url=users[0].display_avatar.url if users[0].display_avatar else None
11761182
)
11771183

11781184
for i in ctx.thread.recipients:
@@ -1263,8 +1269,7 @@ async def anonremoveuser(self, ctx, *users_arg: Union[discord.Member, discord.Ro
12631269
if self.bot.config["show_timestamp"]:
12641270
em.timestamp = discord.utils.utcnow()
12651271
em.set_footer(
1266-
text=f"{users[0]}",
1267-
icon_url=users[0].display_avatar.url if users[0].display_avatar else None,
1272+
text=f"{users[0]}", icon_url=users[0].display_avatar.url if users[0].display_avatar else None
12681273
)
12691274

12701275
for i in ctx.thread.recipients:
@@ -1859,8 +1864,7 @@ async def contact(
18591864
if self.bot.config["show_timestamp"]:
18601865
em.timestamp = discord.utils.utcnow()
18611866
em.set_footer(
1862-
text=f"{creator}",
1863-
icon_url=creator.display_avatar.url if creator.display_avatar else None,
1867+
text=f"{creator}", icon_url=creator.display_avatar.url if creator.display_avatar else None
18641868
)
18651869

18661870
for u in users:

cogs/utility.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ async def format_cog_help(self, cog, *, no_cog=False):
9393

9494
name = cog.qualified_name + " - Help" if not no_cog else "Miscellaneous Commands"
9595
embed.set_author(
96-
name=name,
97-
icon_url=bot.user.display_avatar.url if bot.user.display_avatar else None,
96+
name=name, icon_url=bot.user.display_avatar.url if bot.user.display_avatar else None
9897
)
9998

10099
embed.set_footer(

core/thread.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
DenyButton,
3535
ConfirmThreadCreationView,
3636
DummyParam,
37+
extract_forwarded_content,
3738
)
3839

3940
logger = getLogger(__name__)
@@ -700,18 +701,12 @@ async def _ensure_genesis(force: bool = False):
700701
if self.log_key:
701702
result = await self.bot.api.logs.update_one(
702703
{"key": self.log_key},
703-
{
704-
"$set": {"channel_id": str(channel.id)},
705-
"$unset": {"snoozed": "", "snooze_data": ""},
706-
},
704+
{"$set": {"channel_id": str(channel.id)}, "$unset": {"snoozed": "", "snooze_data": ""}},
707705
)
708706
else:
709707
result = await self.bot.api.logs.update_one(
710708
{"recipient.id": str(self.id)},
711-
{
712-
"$set": {"channel_id": str(channel.id)},
713-
"$unset": {"snoozed": "", "snooze_data": ""},
714-
},
709+
{"$set": {"channel_id": str(channel.id)}, "$unset": {"snoozed": "", "snooze_data": ""}},
715710
)
716711
if result.modified_count == 0:
717712
result = await self.bot.api.logs.update_one(
@@ -1781,8 +1776,7 @@ async def send(
17811776

17821777
# Create embed for note with Discord system message style
17831778
embed = discord.Embed(
1784-
description=content,
1785-
color=0x5865F2, # Discord blurple color for system messages
1779+
description=content, color=0x5865F2 # Discord blurple color for system messages
17861780
)
17871781

17881782
# Set author with note icon and username
@@ -1792,8 +1786,7 @@ async def send(
17921786
note_type = "Note"
17931787

17941788
embed.set_author(
1795-
name=f"📝 {note_type} ({message.author.name})",
1796-
icon_url=message.author.display_avatar.url,
1789+
name=f"📝 {note_type} ({message.author.name})", icon_url=message.author.display_avatar.url
17971790
)
17981791

17991792
# Add timestamp if enabled

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ urllib3==2.5.0; python_version >= '3.9'
4444
uvloop==0.22.1; sys_platform != 'win32'
4545
webencodings==0.5.1
4646
yarl==1.22.0; python_version >= '3.9'
47-
zstandard==0.25.0; python_version >= '3.9'
47+
zstandard==0.25.0; python_version >= '3.9'

0 commit comments

Comments
 (0)