Skip to content

Commit 291cfcd

Browse files
authored
Merge branch 'development' into development
2 parents 051ec7b + d2b5042 commit 291cfcd

File tree

3 files changed

+99
-22
lines changed

3 files changed

+99
-22
lines changed

cogs/plugins.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class Plugins(commands.Cog):
113113
These addons could have a range of features from moderation to simply
114114
making your life as a moderator easier!
115115
Learn how to create a plugin yourself here:
116-
https://github.com/modmail-dev/modmail/wiki/Plugins
116+
https://docs.modmail.dev/usage-guide/plugins
117117
"""
118118

119119
def __init__(self, bot):
@@ -332,7 +332,7 @@ async def parse_user_input(self, ctx, plugin_name, check_version=False):
332332
embed = discord.Embed(
333333
description="Invalid plugin name, double check the plugin name "
334334
"or use one of the following formats: "
335-
"username/repo/plugin-name, username/repo/plugin-name@branch, local/plugin-name.",
335+
"username/repo/plugin-name, username/repo/plugin-name@branch, @local/plugin-name.",
336336
color=self.bot.error_color,
337337
)
338338
await ctx.send(embed=embed)
@@ -357,7 +357,7 @@ async def plugins_add(self, ctx, *, plugin_name: str):
357357
358358
`plugin_name` can be the name of the plugin found in `{prefix}plugin registry`,
359359
or a direct reference to a GitHub hosted plugin (in the format `user/repo/name[@branch]`)
360-
or `local/name` for local plugins.
360+
or `@local/name` for local plugins.
361361
"""
362362

363363
plugin = await self.parse_user_input(ctx, plugin_name, check_version=True)
@@ -444,7 +444,7 @@ async def plugins_remove(self, ctx, *, plugin_name: str):
444444
Remove an installed plugin of the bot.
445445
446446
`plugin_name` can be the name of the plugin found in `{prefix}plugin registry`, or a direct reference
447-
to a GitHub hosted plugin (in the format `user/repo/name[@branch]`) or `local/name` for local plugins.
447+
to a GitHub hosted plugin (in the format `user/repo/name[@branch]`) or `@local/name` for local plugins.
448448
"""
449449
plugin = await self.parse_user_input(ctx, plugin_name)
450450
if plugin is None:
@@ -526,7 +526,7 @@ async def plugins_update(self, ctx, *, plugin_name: str = None):
526526
Update a plugin for the bot.
527527
528528
`plugin_name` can be the name of the plugin found in `{prefix}plugin registry`, or a direct reference
529-
to a GitHub hosted plugin (in the format `user/repo/name[@branch]`) or `local/name` for local plugins.
529+
to a GitHub hosted plugin (in the format `user/repo/name[@branch]`) or `@local/name` for local plugins.
530530
531531
To update all plugins, do `{prefix}plugins update`.
532532
"""

cogs/threadmenu.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ def typecheck(m):
178178
if label.lower() == "cancel":
179179
return await ctx.send("Cancelled.")
180180

181+
if label.lower() == "main menu":
182+
return await ctx.send("You cannot use that label.")
183+
181184
if sanitized_label in conf["options"]:
182185
await ctx.send("That option already exists. Use `threadmenu edit` to edit it.")
183186
return

core/thread.py

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -855,11 +855,9 @@ async def send_genesis_message():
855855
if getattr(self, "_selected_thread_creation_menu_option", None) and self.bot.config.get(
856856
"thread_creation_menu_selection_log"
857857
):
858-
opt = self._selected_thread_creation_menu_option
858+
path = self._selected_thread_creation_menu_option
859859
try:
860-
log_txt = f"Selected menu option: {opt.get('label')} ({opt.get('type')})"
861-
if opt.get("type") == "command":
862-
log_txt += f" -> {opt.get('callback')}"
860+
log_txt = f"Selected menu path: {' -> '.join(path)}"
863861
await channel.send(embed=discord.Embed(description=log_txt, color=self.bot.mod_color))
864862
except Exception:
865863
logger.warning(
@@ -2665,29 +2663,44 @@ async def create(
26652663
placeholder = "Select an option to contact the staff team."
26662664
timeout = 20
26672665

2668-
options = self.bot.config.get("thread_creation_menu_options") or {}
2669-
submenus = self.bot.config.get("thread_creation_menu_submenus") or {}
2670-
26712666
# Minimal inline view implementation (avoid importing plugin code)
26722667

26732668
thread.ready = False # not ready yet
26742669

26752670
class _ThreadCreationMenuSelect(discord.ui.Select):
2676-
def __init__(self, outer_thread: Thread):
2671+
def __init__(
2672+
self,
2673+
bot,
2674+
outer_thread: Thread,
2675+
option_data: dict,
2676+
menu_msg: discord.Message,
2677+
path: list,
2678+
is_home: bool = True,
2679+
):
2680+
self.bot = bot
26772681
self.outer_thread = outer_thread
2678-
opts = [
2682+
self.option_data = option_data
2683+
self.menu_msg = menu_msg
2684+
self.path = path
2685+
options = [
26792686
discord.SelectOption(
26802687
label=o["label"],
26812688
description=o["description"],
26822689
emoji=o["emoji"],
26832690
)
2684-
for o in options.values()
2691+
for o in option_data.values()
26852692
]
2693+
if not is_home:
2694+
options.append(
2695+
discord.SelectOption(
2696+
label="main menu", description="Return to the main menu", emoji="🏠"
2697+
)
2698+
)
26862699
super().__init__(
26872700
placeholder=placeholder,
26882701
min_values=1,
26892702
max_values=1,
2690-
options=opts,
2703+
options=options,
26912704
)
26922705

26932706
async def callback(self, interaction: discord.Interaction):
@@ -2702,8 +2715,45 @@ async def callback(self, interaction: discord.Interaction):
27022715
chosen_label = self.values[0]
27032716
# Resolve option key
27042717
key = chosen_label.lower().replace(" ", "_")
2705-
selected = options.get(key)
2706-
self.outer_thread._selected_thread_creation_menu_option = selected
2718+
if key == "main_menu":
2719+
option_data = self.bot.config.get("thread_creation_menu_options") or {}
2720+
new_view = _ThreadCreationMenuView(
2721+
self.bot,
2722+
self.outer_thread,
2723+
option_data,
2724+
self.menu_msg,
2725+
path=[],
2726+
is_home=True,
2727+
)
2728+
return await self.menu_msg.edit(view=new_view)
2729+
selected: dict = self.option_data.get(key, {})
2730+
next_path = [*self.path, chosen_label]
2731+
if selected.get("type", "command") == "submenu":
2732+
submenu_data = self.bot.config.get("thread_creation_menu_submenus") or {}
2733+
submenu_key = selected.get("callback", key)
2734+
option_data = submenu_data.get(submenu_key, {})
2735+
if not option_data:
2736+
home_options = self.bot.config.get("thread_creation_menu_options") or {}
2737+
new_view = _ThreadCreationMenuView(
2738+
self.bot,
2739+
self.outer_thread,
2740+
home_options,
2741+
self.menu_msg,
2742+
path=[],
2743+
is_home=True,
2744+
)
2745+
return await self.menu_msg.edit(view=new_view)
2746+
new_view = _ThreadCreationMenuView(
2747+
self.bot,
2748+
self.outer_thread,
2749+
option_data,
2750+
self.menu_msg,
2751+
path=next_path,
2752+
is_home=False,
2753+
)
2754+
return await self.menu_msg.edit(view=new_view)
2755+
2756+
self.outer_thread._selected_thread_creation_menu_option = next_path
27072757
# Reflect the selection in the original DM by editing the embed/body
27082758
try:
27092759
msg = getattr(interaction, "message", None)
@@ -2942,10 +2992,30 @@ async def callback(self, interaction: discord.Interaction):
29422992
ctx_.command.checks = old_checks
29432993

29442994
class _ThreadCreationMenuView(discord.ui.View):
2945-
def __init__(self, outer_thread: Thread):
2995+
def __init__(
2996+
self,
2997+
bot,
2998+
outer_thread: Thread,
2999+
option_data: dict,
3000+
menu_msg: discord.Message,
3001+
path: list,
3002+
is_home: bool = True,
3003+
):
29463004
super().__init__(timeout=timeout)
29473005
self.outer_thread = outer_thread
2948-
self.add_item(_ThreadCreationMenuSelect(outer_thread))
3006+
self.path = path
3007+
self.menu_msg = menu_msg
3008+
self.option_data = option_data
3009+
self.add_item(
3010+
_ThreadCreationMenuSelect(
3011+
bot,
3012+
outer_thread,
3013+
option_data=option_data,
3014+
menu_msg=menu_msg,
3015+
path=self.path,
3016+
is_home=is_home,
3017+
)
3018+
)
29493019

29503020
async def on_timeout(self):
29513021
# Timeout -> abort thread creation
@@ -3067,8 +3137,12 @@ async def on_timeout(self):
30673137
embed.set_thumbnail(url=embed_thumb)
30683138
except Exception as e:
30693139
logger.debug("Thumbnail set failed (ignored): %s", e)
3070-
menu_view = _ThreadCreationMenuView(thread)
3071-
menu_msg = await recipient.send(embed=embed, view=menu_view)
3140+
menu_msg = await recipient.send(embed=embed)
3141+
option_data = self.bot.config.get("thread_creation_menu_options") or {}
3142+
menu_view = _ThreadCreationMenuView(
3143+
self.bot, thread, option_data, menu_msg, path=[], is_home=True
3144+
)
3145+
menu_msg = await menu_msg.edit(view=menu_view)
30723146
# mark thread as pending menu selection
30733147
thread._pending_menu = True
30743148
# Explicitly attach the message to the view for safety in callbacks

0 commit comments

Comments
 (0)