From edcc9404df6ea7f87e55d09b8b0bd48a3147a91b Mon Sep 17 00:00:00 2001 From: vycdev2 Date: Sun, 9 Aug 2026 20:42:32 +0000 Subject: [PATCH 1/2] fix: document optional command parameters correctly --- COMMANDS.md | 164 ++++++++++++++--------------- CONTRIBUTING.md | 6 ++ tools/generate_commands_md.py | 39 ++++--- tools/test_generate_commands_md.py | 62 +++++++++++ 4 files changed, 175 insertions(+), 96 deletions(-) create mode 100644 tools/test_generate_commands_md.py diff --git a/COMMANDS.md b/COMMANDS.md index c2f4b5d..39e0bfd 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -27,7 +27,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Hidden: Yes - Rate limit: 3 use(s) per 30 second(s) - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `guildcount` @@ -210,7 +210,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: BanMembers - Requires guild context: Yes - Parameters: - - `null` — SocketChannel? channel = — Optional + - `channel` — SocketChannel? — Optional ### `setlevelupmessageschannel` @@ -221,7 +221,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — SocketChannel? channel = — Optional + - `channel` — SocketChannel? — Optional ### `setlevelupquoteschannel` @@ -232,7 +232,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — SocketChannel? channel = — Optional + - `channel` — SocketChannel? — Optional ### `setpinschannel` @@ -243,7 +243,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — SocketChannel? channel = — Optional + - `channel` — SocketChannel? — Optional ### `setprefix` @@ -254,7 +254,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — string? prefix = — Optional + - `prefix` — string? — Optional ### `setquoteaddapprovals` @@ -287,7 +287,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — SocketChannel? channel = — Optional + - `channel` — SocketChannel? — Optional ### `setwchannel` @@ -298,7 +298,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — SocketChannel? channel = — Optional + - `channel` — SocketChannel? — Optional ### `toggleglobalquotes` @@ -346,7 +346,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Displays a list of commands. - Rate limit: 1 use(s) per 30 second(s) - Parameters: - - `null` — string? command = — Optional + - `command` — string? — Optional ## Image (8 commands) @@ -359,9 +359,9 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: AttachFiles - Required bot permission: EmbedLinks - Parameters: - - `4` — int pixelScale = — Optional - - `6` — int maxOffset = — Optional - - `2` — int smoothPasses = — Optional + - `pixelScale` — int — Optional + - `maxOffset` — int — Optional + - `smoothPasses` — int — Optional ### `captcha` @@ -414,7 +414,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: AttachFiles - Required bot permission: EmbedLinks - Parameters: - - `""` — string text = — Optional + - `text` — string — Optional ### `qrcode` @@ -437,7 +437,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `activitygraph180day` @@ -449,7 +449,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past180days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `activitygraph30day` @@ -461,7 +461,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past30days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `activitygraph7day` @@ -473,7 +473,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `activitygraph90day` @@ -485,7 +485,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past90days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `activitygraphcumulative` @@ -497,7 +497,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalactivitygraph` @@ -508,7 +508,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 2 use(s) per 60 second(s) - Required bot permission: AttachFiles - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalactivitygraph180day` @@ -519,7 +519,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 2 use(s) per 60 second(s) - Required bot permission: AttachFiles - Parameters: - - `"past180days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalactivitygraph30day` @@ -530,7 +530,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 2 use(s) per 60 second(s) - Required bot permission: AttachFiles - Parameters: - - `"past30days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalactivitygraph7day` @@ -541,7 +541,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 2 use(s) per 60 second(s) - Required bot permission: AttachFiles - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalactivitygraph90day` @@ -552,7 +552,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 2 use(s) per 60 second(s) - Required bot permission: AttachFiles - Parameters: - - `"past90days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalactivitygraphcumulative` @@ -563,7 +563,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 2 use(s) per 60 second(s) - Required bot permission: AttachFiles - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional - `mentionedUsers` — params IUser[] — Required ### `globalleaderboard` @@ -573,7 +573,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Displays the global leaderboard of users based on their levels across all guilds. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `globalleaderboardavglength` @@ -582,7 +582,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Displays the global leaderboard by average message length across all guilds (all time). Weighted by message count. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `globalleaderboardmessages` @@ -591,7 +591,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Displays the global leaderboard by number of messages sent across all guilds (all time). - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `globalleaderboardmessagespast` @@ -601,7 +601,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 60 second(s) - Parameters: - `days` — int — Required - - `1` — int page = — Optional + - `page` — int — Optional ### `globalleaderboardpast` @@ -611,7 +611,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 60 second(s) - Parameters: - `days` — int — Required - - `1` — int page = — Optional + - `page` — int — Optional ### `guildactivitygraph` @@ -622,7 +622,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional ### `guildactivitygraph180day` @@ -633,7 +633,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past180days"` — string days = — Optional + - `days` — string — Optional ### `guildactivitygraph30day` @@ -644,7 +644,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past30days"` — string days = — Optional + - `days` — string — Optional ### `guildactivitygraph7day` @@ -655,7 +655,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional ### `guildactivitygraph90day` @@ -666,7 +666,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past90days"` — string days = — Optional + - `days` — string — Optional ### `guildactivitygraphcumulative` @@ -677,7 +677,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Requires guild context: Yes - Parameters: - - `"past7days"` — string days = — Optional + - `days` — string — Optional ### `invalidatexp` @@ -696,7 +696,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `leaderboardavglength` @@ -706,7 +706,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `leaderboardmessages` @@ -716,7 +716,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `leaderboardmessagespast` @@ -727,7 +727,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Requires guild context: Yes - Parameters: - `days` — int — Required - - `1` — int page = — Optional + - `page` — int — Optional ### `leaderboardpast` @@ -738,7 +738,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Requires guild context: Yes - Parameters: - `days` — int — Required - - `1` — int page = — Optional + - `page` — int — Optional ### `level` @@ -789,7 +789,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Flips a coin, or multiple coins. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `"1"` — string input = — Optional + - `input` — string — Optional ### `echo` @@ -798,7 +798,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Echoes the input. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `""` — string input = — Optional + - `input` — string — Optional ### `fact` @@ -824,7 +824,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `null` — string? _ = — Optional + - `_` — string? — Optional ### `hash` @@ -844,7 +844,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `null` — SocketGuildUser? user = — Optional + - `user` — SocketGuildUser? — Optional ### `info` @@ -869,7 +869,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Requires guild context: Yes - Parameters: - `user1` — SocketGuildUser — Required - - `null` — SocketGuildUser? user2 = — Optional + - `user2` — SocketGuildUser? — Optional ### `ping` @@ -887,7 +887,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: AttachFiles - Parameters: - `ip` — string — Required - - `25565` — int port = — Optional + - `port` — int — Optional ### `profilepic` @@ -897,7 +897,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `null` — SocketGuildUser? user = — Optional + - `user` — SocketGuildUser? — Optional ### `randomcolor` @@ -923,7 +923,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Rolls a die, or multiple dice. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `"1d6"` — string input = — Optional + - `input` — string — Optional ### `rps` @@ -948,7 +948,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Displays the current time of the server. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `null` — string? _ = — Optional + - `_` — string? — Optional ### `timeuntil` @@ -966,7 +966,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Returns definitions from urban dictionary - Rate limit: 5 use(s) per 30 second(s) - Parameters: - - `null` — string? word = — Optional + - `word` — string? — Optional ### `uptime` @@ -983,7 +983,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `null` — SocketGuildUser? user = — Optional + - `user` — SocketGuildUser? — Optional ### `yearpercentage` @@ -1021,9 +1021,9 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional - - `"oldest"` — string sort = — Optional - - `false` — bool approvedOnly = — Optional + - `page` — int — Optional + - `sort` — string — Optional + - `approvedOnly` — bool — Optional ### `listquotesglobal` @@ -1032,9 +1032,9 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Lists quotes across all guilds (paginated). - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `1` — int page = — Optional - - `"oldest"` — string sort = — Optional - - `false` — bool approvedOnly = — Optional + - `page` — int — Optional + - `sort` — string — Optional + - `approvedOnly` — bool — Optional ### `quoteoftheday` @@ -1042,7 +1042,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Aliases: qotd - Summary: Shows the quote with the highest total score in the last day. Use true to restrict to the current guild. - Parameters: - - `false` — bool guildOnly = — Optional + - `guildOnly` — bool — Optional ### `quoteofthemonth` @@ -1050,7 +1050,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Aliases: qotm - Summary: Shows the quote with the highest total score in the last month. Use true to restrict to the current guild. - Parameters: - - `false` — bool guildOnly = — Optional + - `guildOnly` — bool — Optional ### `quoteoftheweek` @@ -1058,7 +1058,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Aliases: qotw - Summary: Shows the quote with the highest total score in the last week. Use true to restrict to the current guild. - Parameters: - - `false` — bool guildOnly = — Optional + - `guildOnly` — bool — Optional ### `rate` @@ -1111,7 +1111,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: ManageRoles - Requires guild context: Yes - Parameters: - - `""` — string remainder = — Optional + - `remainder` — string — Optional ## Slots (2 commands) @@ -1141,7 +1141,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `globalbalanceleaderboard` @@ -1150,7 +1150,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Shows the top balances among users globally. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `globalwealthleaderboard` @@ -1159,7 +1159,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Summary: Shows the top net worth (cash + holdings) among users globally. - Rate limit: 3 use(s) per 10 second(s) - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `stock balance` @@ -1169,7 +1169,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `null` — IUser? mentionedUser = — Optional + - `mentionedUser` — IUser? — Optional ### `stock buy` @@ -1190,7 +1190,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `stock globalgainers` @@ -1200,7 +1200,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `stock globallosers` @@ -1210,7 +1210,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `stock info` @@ -1230,7 +1230,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ### `stock movers` @@ -1248,7 +1248,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `null` — IUser? mentionedUser = — Optional + - `mentionedUser` — IUser? — Optional ### `stock sell` @@ -1259,7 +1259,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Requires guild context: Yes - Parameters: - `target` — string — Required - - `"all"` — string amountStr = — Optional + - `amountStr` — string — Optional ### `stock transfer` @@ -1271,7 +1271,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Parameters: - `target` — string — Required - `targetUser` — IUser — Required - - `"all"` — string amountStr = — Optional + - `amountStr` — string — Optional ### `transfer` @@ -1292,7 +1292,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Rate limit: 3 use(s) per 10 second(s) - Requires guild context: Yes - Parameters: - - `1` — int page = — Optional + - `page` — int — Optional ## Subscriptions (9 commands) @@ -1305,7 +1305,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: Administrator - Requires guild context: Yes - Parameters: - - `null` — ITextChannel? channel = — Optional + - `channel` — ITextChannel? — Optional ### `setxkcdchannel` @@ -1317,7 +1317,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: ManageWebhooks - Requires guild context: Yes - Parameters: - - `null` — ITextChannel? channel = — Optional + - `channel` — ITextChannel? — Optional ### `subscriberss` @@ -1329,7 +1329,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: ManageWebhooks - Requires guild context: Yes - Parameters: - - `null` — string? input = — Optional + - `input` — string? — Optional ### `subscribetwitch` @@ -1341,7 +1341,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: ManageWebhooks - Requires guild context: Yes - Parameters: - - `null` — string? input = — Optional + - `input` — string? — Optional ### `subscribeyoutube` @@ -1353,7 +1353,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required bot permission: ManageWebhooks - Requires guild context: Yes - Parameters: - - `null` — string? input = — Optional + - `input` — string? — Optional ### `subscriptions` @@ -1384,7 +1384,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Requires guild context: Yes - Parameters: - `streamer` — string — Required - - `null` — ITextChannel? channel = — Optional + - `channel` — ITextChannel? — Optional ### `unsubscribeyoutube` @@ -1396,7 +1396,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Requires guild context: Yes - Parameters: - `youtubeChannel` — string — Required - - `null` — ITextChannel? channel = — Optional + - `channel` — ITextChannel? — Optional ## Utility (2 commands) @@ -1409,7 +1409,7 @@ This file is auto-generated by `tools/generate_commands_md.py`. - Required permission: ManageMessages - Requires guild context: Yes - Parameters: - - `null` — string? _ = — Optional + - `_` — string? — Optional ### `reminder` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe2a2f5..2450278 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,6 +35,12 @@ python .\tools\generate_commands_md.py If you update or add commands, regenerate `COMMANDS.md` and include the updated file in your PR. +Run the generator regression tests with Python's standard library test runner: + +```powershell +python -m unittest tools.test_generate_commands_md +``` + ## Migrations and database changes - Do not edit historical migration files unless you know what you're doing and the migration has not been applied anywhere important. diff --git a/tools/generate_commands_md.py b/tools/generate_commands_md.py index fd99e04..0b5e122 100644 --- a/tools/generate_commands_md.py +++ b/tools/generate_commands_md.py @@ -48,6 +48,29 @@ def clean_string_literal(s: str): return s +def parse_parameter(raw: str): + """Return documentation fields for one C# parameter declaration.""" + # Attributes such as [Remainder] belong to the parameter, not its type. + declaration = re.sub(r"\[[^\]]+\]", "", raw).strip() + optional = '=' in declaration + # A default expression is not part of the parameter name. Removing it + # before splitting also handles quoted defaults containing spaces. + declaration = declaration.split('=', 1)[0].strip() + + toks = declaration.split() + if len(toks) >= 2: + ptype = ' '.join(toks[:-1]) + pname = toks[-1] + elif toks: + ptype = toks[0] + pname = '' + else: + ptype = '' + pname = '' + + return {'raw': raw.strip(), 'type': ptype, 'name': pname, 'optional': optional} + + def extract_methods_from_file(path: Path): text = path.read_text(encoding='utf-8') results = [] @@ -147,19 +170,7 @@ def extract_methods_from_file(path: Path): p = p.strip() if not p: continue - # remove attributes like [Remainder] - p_clean = re.sub(r"\[[^\]]+\]", "", p).strip() - # detect default - optional = '=' in p_clean - # split type and name (take last token as name) - toks = p_clean.split() - if len(toks) >= 2: - ptype = ' '.join(toks[:-1]) - pname = toks[-1] - else: - ptype = toks[0] - pname = '' - params.append({'raw': p.strip(), 'type': ptype, 'name': pname, 'optional': optional}) + params.append(parse_parameter(p)) results.append({ 'class': class_name, @@ -174,7 +185,7 @@ def extract_methods_from_file(path: Path): 'required_permission': required_permission, 'required_bot_permission': required_bot_permission, # 'requires_db_guild' removed per request - 'source': str(path.relative_to(Path.cwd())) + 'source': str(path.relative_to(Path.cwd())).replace('/', '\\') }) return results diff --git a/tools/test_generate_commands_md.py b/tools/test_generate_commands_md.py new file mode 100644 index 0000000..682a6d6 --- /dev/null +++ b/tools/test_generate_commands_md.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Regression tests for the command documentation generator.""" +import importlib.util +import os +import tempfile +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT = ROOT / "tools" / "generate_commands_md.py" +SPEC = importlib.util.spec_from_file_location("generate_commands_md", SCRIPT) +assert SPEC is not None and SPEC.loader is not None +GENERATOR = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(GENERATOR) + + +class ParameterParsingTests(unittest.TestCase): + def test_optional_default_is_not_reported_as_parameter_name(self): + parsed = GENERATOR.parse_parameter("int page = 1") + + self.assertEqual({"raw": "int page = 1", "type": "int", "name": "page", "optional": True}, parsed) + + def test_parameter_attributes_and_quoted_defaults_are_supported(self): + parsed = GENERATOR.parse_parameter('[Remainder] string? text = "hello world"') + + self.assertEqual("string?", parsed["type"]) + self.assertEqual("text", parsed["name"]) + self.assertTrue(parsed["optional"]) + + def test_generated_markdown_uses_parameter_name_and_type(self): + source = ''' +using System.Threading.Tasks; + +public class ExampleModule +{ + [Command("example")] + [Summary("Shows an example.")] + public Task Example(int page = 1, string sort = "oldest") => Task.CompletedTask; +} +''' + with tempfile.TemporaryDirectory(dir=ROOT) as temporary: + path = Path(temporary) / "Nested" / "ExampleModule.cs" + path.parent.mkdir() + path.write_text(source, encoding="utf-8") + old_cwd = Path.cwd() + try: + os.chdir(ROOT) + commands = GENERATOR.extract_methods_from_file(path) + finally: + os.chdir(old_cwd) + + markdown = GENERATOR.generate_markdown(commands) + self.assertIn("`page` — int — Optional", markdown) + self.assertIn("`sort` — string — Optional", markdown) + self.assertNotIn("`1` — int page", markdown) + expected_source = str(path.relative_to(ROOT)).replace("/", chr(92)) + self.assertIn(f"- Source: `{expected_source}`", markdown) + + +if __name__ == "__main__": + unittest.main() From 294b72db6d62e3038c31d8ec7afa187b0d4fcbf1 Mon Sep 17 00:00:00 2001 From: vycdev Date: Mon, 10 Aug 2026 10:47:02 +0000 Subject: [PATCH 2/2] fix: select command module in docs generator --- tools/generate_commands_md.py | 6 ++++-- tools/test_generate_commands_md.py | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/generate_commands_md.py b/tools/generate_commands_md.py index 0b5e122..3a831fb 100644 --- a/tools/generate_commands_md.py +++ b/tools/generate_commands_md.py @@ -75,8 +75,10 @@ def extract_methods_from_file(path: Path): text = path.read_text(encoding='utf-8') results = [] - # Find class name - class_match = re.search(r"class\s+(\w+)", text) + # Prefer the command module when helper classes appear earlier in the file. + class_match = re.search(r"class\s+(\w+Module)\b", text) + if class_match is None: + class_match = re.search(r"class\s+(\w+)", text) class_name = class_match.group(1) if class_match else path.stem # iterate attribute blocks followed by a method signature diff --git a/tools/test_generate_commands_md.py b/tools/test_generate_commands_md.py index 682a6d6..5961b18 100644 --- a/tools/test_generate_commands_md.py +++ b/tools/test_generate_commands_md.py @@ -32,6 +32,10 @@ def test_generated_markdown_uses_parameter_name_and_type(self): source = ''' using System.Threading.Tasks; +internal sealed class ExampleGuildScore +{ +} + public class ExampleModule { [Command("example")] @@ -51,6 +55,8 @@ def test_generated_markdown_uses_parameter_name_and_type(self): os.chdir(old_cwd) markdown = GENERATOR.generate_markdown(commands) + self.assertIn("## Example (1 command)", markdown) + self.assertNotIn("## ExampleGuildScore", markdown) self.assertIn("`page` — int — Optional", markdown) self.assertIn("`sort` — string — Optional", markdown) self.assertNotIn("`1` — int page", markdown)