Skip to content

Commit 3795ec0

Browse files
committed
core
Fully merged `threadmenu config` into the main bot’s config system. Added threadmenu embed customization capability.
1 parent 04a33b7 commit 3795ec0

File tree

5 files changed

+128
-83
lines changed

5 files changed

+128
-83
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ Features:
5151
* `threadmenu option edit/remove/show`: Manage or inspect an existing option.
5252
* `threadmenu submenu create/delete/list/show`: Manage submenus.
5353
* `threadmenu submenu option add/edit/remove`: Manage options inside a submenu.
54-
* `threadmenu config <timeout|close_on_timeout|embed_text|dropdown_placeholder|anonymous_menu>`: Adjust global settings.
5554
* Configuration / Behavior:
5655
* Per-option `category` targeting when creating a thread; falls back to `main_category_id` if invalid/missing.
5756
* Optional selection logging (`thread_creation_menu_selection_log`) posts the chosen option in the new thread.

cogs/threadmenu.py

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def _get_conf(self) -> dict:
3131
or "Please select an option.",
3232
"dropdown_placeholder": self.bot.config.get("thread_creation_menu_dropdown_placeholder")
3333
or "Select an option to contact the staff team.",
34+
"embed_title": self.bot.config.get("thread_creation_menu_embed_title"),
35+
"embed_footer": self.bot.config.get("thread_creation_menu_embed_footer"),
36+
"embed_thumbnail_url": self.bot.config.get("thread_creation_menu_embed_thumbnail_url"),
37+
"embed_footer_icon_url": self.bot.config.get("thread_creation_menu_embed_footer_icon_url"),
38+
"embed_color": self.bot.config.get("thread_creation_menu_embed_color"),
3439
}
3540

3641
async def _save_conf(self, conf: dict):
@@ -49,6 +54,17 @@ async def _save_conf(self, conf: dict):
4954
"thread_creation_menu_dropdown_placeholder",
5055
conf.get("dropdown_placeholder", "Select an option to contact the staff team."),
5156
)
57+
await self.bot.config.set("thread_creation_menu_embed_title", conf.get("embed_title"))
58+
await self.bot.config.set("thread_creation_menu_embed_footer", conf.get("embed_footer"))
59+
await self.bot.config.set("thread_creation_menu_embed_thumbnail_url", conf.get("embed_thumbnail_url"))
60+
await self.bot.config.set(
61+
"thread_creation_menu_embed_footer_icon_url", conf.get("embed_footer_icon_url")
62+
)
63+
if conf.get("embed_color"):
64+
try:
65+
await self.bot.config.set("thread_creation_menu_embed_color", conf.get("embed_color"))
66+
except Exception:
67+
pass
5268
await self.bot.config.update()
5369

5470
# ----- commands -----
@@ -58,80 +74,6 @@ async def threadmenu(self, ctx):
5874
"""Thread-creation menu settings (core)."""
5975
await ctx.send_help(ctx.command)
6076

61-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
62-
@threadmenu.group(name="config", invoke_without_command=True)
63-
async def threadmenu_config(self, ctx):
64-
"""Thread-creation menu config settings (core)."""
65-
await ctx.send_help(ctx.command)
66-
67-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
68-
@threadmenu_config.command(name="get")
69-
async def threadmenu_config_get(self, ctx):
70-
"""Get current core thread-creation menu config."""
71-
conf = self._get_conf()
72-
embed = discord.Embed(
73-
title="Thread-creation menu config (Core)",
74-
description="The current config for the thread menu.",
75-
color=discord.Color.blurple(),
76-
)
77-
embed.add_field(name="Enabled", value=conf["enabled"])
78-
embed.add_field(name="Timeout", value=conf["timeout"])
79-
embed.add_field(name="Close on timeout", value=conf["close_on_timeout"])
80-
embed.add_field(name="Embed text", value=conf["embed_text"], inline=False)
81-
embed.add_field(name="Dropdown placeholder", value=conf["dropdown_placeholder"], inline=False)
82-
await ctx.send(embed=embed)
83-
84-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
85-
@threadmenu_config.command(name="timeout")
86-
async def threadmenu_config_timeout(self, ctx, timeout: int):
87-
"""Set the menu interaction timeout in seconds.
88-
89-
After this period of inactivity the view times out; if
90-
`close_on_timeout` is true the menu message is removed.
91-
"""
92-
if timeout < 1:
93-
return await ctx.send("Timeout must be greater than 1.")
94-
conf = self._get_conf()
95-
conf["timeout"] = timeout
96-
await self._save_conf(conf)
97-
await ctx.send("Timeout set.")
98-
99-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
100-
@threadmenu_config.command(name="close_on_timeout")
101-
async def threadmenu_config_close_on_timeout(self, ctx, close_on_timeout: bool):
102-
"""Toggle deleting the menu message when it times out."""
103-
conf = self._get_conf()
104-
conf["close_on_timeout"] = close_on_timeout
105-
await self._save_conf(conf)
106-
await ctx.send("Done.")
107-
108-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
109-
@threadmenu_config.command(name="embed_text")
110-
async def threadmenu_config_embed_text(self, ctx, *, embed_text: str):
111-
"""Set the embed body text shown above the select menu."""
112-
conf = self._get_conf()
113-
conf["embed_text"] = embed_text
114-
await self._save_conf(conf)
115-
await ctx.send("Done.")
116-
117-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
118-
@threadmenu_config.command(name="dropdown_placeholder")
119-
async def threadmenu_config_dropdown_placeholder(self, ctx, *, dropdown_placeholder: str):
120-
"""Set the placeholder text inside the dropdown before selection."""
121-
conf = self._get_conf()
122-
conf["dropdown_placeholder"] = dropdown_placeholder
123-
await self._save_conf(conf)
124-
await ctx.send("Done.")
125-
126-
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
127-
@threadmenu_config.command(name="anonymous_menu")
128-
async def threadmenu_config_anonymous_menu(self, ctx, option: bool):
129-
"""Toggle whether the menu is posted anonymously (no user mention)."""
130-
conf = self._get_conf()
131-
conf["anonymous_menu"] = option
132-
await self._save_conf(conf)
133-
await ctx.send("Done.")
134-
13577
@checks.has_permissions(PermissionLevel.ADMINISTRATOR)
13678
@threadmenu.command(name="toggle")
13779
async def threadmenu_toggle(self, ctx):

core/config.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ class ConfigManager:
158158
"thread_creation_menu_dropdown_placeholder": "Select an option to contact the staff team.",
159159
"thread_creation_menu_selection_log": True, # log selected option in newly created thread channel
160160
"thread_creation_menu_precreate_channel": False,
161+
# thread-creation menu embed customization
162+
"thread_creation_menu_embed_title": None,
163+
"thread_creation_menu_embed_footer": None,
164+
"thread_creation_menu_embed_thumbnail_url": None,
165+
"thread_creation_menu_embed_footer_icon_url": None,
166+
"thread_creation_menu_embed_color": str(discord.Color.green()),
161167
}
162168

163169
private_keys = {
@@ -214,7 +220,7 @@ class ConfigManager:
214220
"data_collection": True,
215221
}
216222

217-
colors = {"mod_color", "recipient_color", "main_color", "error_color"}
223+
colors = {"mod_color", "recipient_color", "main_color", "error_color", "thread_creation_menu_embed_color"}
218224

219225
time_deltas = {"account_age", "guild_age", "thread_auto_close", "thread_cooldown", "log_expiration"}
220226

core/config_help.json

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,7 @@
14251425
"default": "30 (seconds)",
14261426
"description": "Number of seconds to wait for a user to pick a menu option before timing out.",
14271427
"examples": [
1428-
"`{prefix}threadmenu config timeout 30`"
1428+
"`{prefix}config set thread_creation_menu_timeout 30`"
14291429
],
14301430
"notes": [
14311431
"If the timeout is reached, `thread_creation_menu_close_on_timeout` controls whether the attempt is aborted or the user is asked to message again.",
@@ -1436,7 +1436,7 @@
14361436
"default": "No",
14371437
"description": "Silently aborts thread creation if the user does not select an option before the timeout expires.",
14381438
"examples": [
1439-
"`{prefix}threadmenu config close_on_timeout yes`"
1439+
"`{prefix}config set thread_creation_menu_close_on_timeout yes`"
14401440
],
14411441
"notes": [
14421442
"If disabled, the user receives a message telling them to send a new DM to start again.",
@@ -1447,7 +1447,7 @@
14471447
"default": "No",
14481448
"description": "If enabled, the initial menu prompt relayed to staff (after creation) is anonymized; only selection details are logged, not the original prompt message author context.",
14491449
"examples": [
1450-
"`{prefix}threadmenu config anonymous_menu yes`"
1450+
"`{prefix}config set thread_creation_menu_anonymous_menu yes`"
14511451
],
14521452
"notes": [
14531453
"Does not affect the DM the user sees, only how it's displayed/logged internally.",
@@ -1458,7 +1458,7 @@
14581458
"default": "\"Please select an option.\"",
14591459
"description": "Text shown in the embed above the selection dropdown in the user's DM.",
14601460
"examples": [
1461-
"`{prefix}threadmenu config embed_text Please choose a department`"
1461+
"`{prefix}config set thread_creation_menu_embed_text Please choose a department`"
14621462
],
14631463
"notes": [
14641464
"Keep this concise; users cannot proceed until choosing.",
@@ -1469,7 +1469,7 @@
14691469
"default": "\"Select an option to contact the staff team.\"",
14701470
"description": "Placeholder text displayed in the dropdown before selection.",
14711471
"examples": [
1472-
"`{prefix}threadmenu config dropdown_placeholder Pick a topic...`"
1472+
"`{prefix}config set thread_creation_menu_dropdown_placeholder Pick a topic...`"
14731473
],
14741474
"notes": [
14751475
"Appears grayed out inside the select component.",
@@ -1499,5 +1499,61 @@
14991499
"Confirmation (`confirm_thread_creation`) is only applied in the deferred mode; enabling precreate bypasses the confirm step for menu flows.",
15001500
"See also: `thread_creation_menu_enabled`, `thread_creation_menu_options`, `confirm_thread_creation`."
15011501
]
1502+
},
1503+
"thread_creation_menu_embed_title": {
1504+
"default": "Empty (no title)",
1505+
"description": "Optional title at the top of the thread-creation menu embed in the user's DM.",
1506+
"examples": [
1507+
"`{prefix}config set thread_creation_menu_embed_title Contact the staff team`",
1508+
"`{prefix}config delete thread_creation_menu_embed_title` (clear)"
1509+
],
1510+
"notes": [
1511+
"See also: `thread_creation_menu_embed_text`."
1512+
]
1513+
},
1514+
"thread_creation_menu_embed_footer": {
1515+
"default": "Empty (no footer)",
1516+
"description": "Optional footer text at the bottom of the menu embed.",
1517+
"examples": [
1518+
"`{prefix}config set thread_creation_menu_embed_footer Please choose the most relevant option`",
1519+
"`{prefix}config delete thread_creation_menu_embed_footer` (clear)"
1520+
],
1521+
"notes": [
1522+
"You can also configure an optional footer icon via `thread_creation_menu_embed_footer_icon_url`."
1523+
]
1524+
},
1525+
"thread_creation_menu_embed_footer_icon_url": {
1526+
"default": "Empty (no icon)",
1527+
"description": "Optional URL for the small footer icon displayed next to the footer text.",
1528+
"examples": [
1529+
"`{prefix}config set thread_creation_menu_embed_footer_icon_url https://example.com/icon.png`",
1530+
"`{prefix}config delete thread_creation_menu_embed_footer_icon_url` (clear)"
1531+
],
1532+
"notes": [
1533+
"Use a direct image URL (PNG/JPEG/GIF)."
1534+
]
1535+
},
1536+
"thread_creation_menu_embed_thumbnail_url": {
1537+
"default": "Empty (no thumbnail)",
1538+
"description": "Optional thumbnail image shown in the top-right of the menu embed.",
1539+
"examples": [
1540+
"`{prefix}config set thread_creation_menu_embed_thumbnail_url https://example.com/logo.png`",
1541+
"`{prefix}config delete thread_creation_menu_embed_thumbnail_url` (clear)"
1542+
],
1543+
"notes": [
1544+
"Use a direct image URL; recommended square image.",
1545+
"Consider file size/CDN reliability so it loads quickly for users."
1546+
]
1547+
},
1548+
"thread_creation_menu_embed_color": {
1549+
"default": "Green (hex for Discord Color.green)",
1550+
"description": "Color for the menu embed's side strip. Accepts hex (e.g. #5865F2) or one of the supported color names.",
1551+
"examples": [
1552+
"`{prefix}config set thread_creation_menu_embed_color #5865F2`",
1553+
"`{prefix}config set thread_creation_menu_embed_color blurple`"
1554+
],
1555+
"notes": [
1556+
"Color names map to the built-in palette (e.g., 'red', 'green', 'blurple')."
1557+
]
15021558
}
15031559
}

core/thread.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,7 +2670,28 @@ async def on_timeout(self):
26702670

26712671
# Send DM prompt
26722672
try:
2673-
embed = discord.Embed(description=embed_text, color=self.bot.mod_color)
2673+
# Build embed with new customizable settings
2674+
try:
2675+
embed_title = self.bot.config.get("thread_creation_menu_embed_title")
2676+
embed_footer = self.bot.config.get("thread_creation_menu_embed_footer")
2677+
embed_thumb = self.bot.config.get("thread_creation_menu_embed_thumbnail_url")
2678+
embed_footer_icon = self.bot.config.get("thread_creation_menu_embed_footer_icon_url")
2679+
embed_color_raw = self.bot.config.get("thread_creation_menu_embed_color")
2680+
except Exception:
2681+
embed_title = None
2682+
embed_footer = None
2683+
embed_thumb = None
2684+
embed_footer_icon = None
2685+
embed_color_raw = None
2686+
embed_color = embed_color_raw or self.bot.mod_color
2687+
embed = discord.Embed(title=embed_title, description=embed_text, color=embed_color)
2688+
if embed_footer:
2689+
embed.set_footer(text=embed_footer, icon_url=embed_footer_icon or discord.Embed.Empty)
2690+
if embed_thumb:
2691+
try:
2692+
embed.set_thumbnail(url=embed_thumb)
2693+
except Exception:
2694+
pass
26742695
menu_view = _ThreadCreationMenuView(thread)
26752696
menu_msg = await recipient.send(embed=embed, view=menu_view)
26762697
# mark thread as pending menu selection
@@ -2812,7 +2833,28 @@ async def on_timeout(self):
28122833
pass
28132834

28142835
try:
2815-
embed = discord.Embed(description=embed_text, color=self.bot.mod_color)
2836+
# Build embed with new customizable settings (precreate flow)
2837+
try:
2838+
embed_title = self.bot.config.get("thread_creation_menu_embed_title")
2839+
embed_footer = self.bot.config.get("thread_creation_menu_embed_footer")
2840+
embed_thumb = self.bot.config.get("thread_creation_menu_embed_thumbnail_url")
2841+
embed_footer_icon = self.bot.config.get("thread_creation_menu_embed_footer_icon_url")
2842+
embed_color_raw = self.bot.config.get("thread_creation_menu_embed_color")
2843+
except Exception:
2844+
embed_title = None
2845+
embed_footer = None
2846+
embed_thumb = None
2847+
embed_footer_icon = None
2848+
embed_color_raw = None
2849+
embed_color = embed_color_raw or self.bot.mod_color
2850+
embed = discord.Embed(title=embed_title, description=embed_text, color=embed_color)
2851+
if embed_footer:
2852+
embed.set_footer(text=embed_footer, icon_url=embed_footer_icon or discord.Embed.Empty)
2853+
if embed_thumb:
2854+
try:
2855+
embed.set_thumbnail(url=embed_thumb)
2856+
except Exception:
2857+
pass
28162858
menu_view = _PrecreateMenuView(thread)
28172859
# Send menu DM AFTER channel creation initiation (channel will be created below)
28182860
menu_msg = await recipient.send(embed=embed, view=menu_view)

0 commit comments

Comments
 (0)