From da476a32df8a11f1d77712c230d10af2c23b7229 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Mon, 5 Feb 2024 15:25:52 +0000 Subject: [PATCH 01/18] Added script to automatically retrieve command info --- src/utils/updateWiki.ts | 234 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 src/utils/updateWiki.ts diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts new file mode 100644 index 00000000..e089964d --- /dev/null +++ b/src/utils/updateWiki.ts @@ -0,0 +1,234 @@ +import { statSync } from 'fs'; +import { appendFile, writeFile, readdir, readFile } from 'fs/promises'; + +// File Paths +const wikiPath = 'docs/COMMAND-WIKI.md'; +const commandsDir = 'src/commands'; +const commandDetailsDir = 'src/commandDetails'; + +// Commands with subcommands, to be handled separately +const compoundCommands = ['coin', 'company', 'interviewer', 'leetcode', 'profile']; + +// RegEx patterns to extract info +const commandDetailsPattern = /.*const .+CommandDetails: CodeyCommandDetails = {([\s\S]*?)\n}/; +const namePattern = /name: '(.*?)'/; +const aliasesPattern = /aliases: \[([\s\S]*?)\]/; +const descriptionPattern = /description: '(.*?)'/; +const detailedDescriptionPattern = /detailedDescription: `([\s\S]*?)`,/; +const optionsPattern = /options: \[([\s\S]*?)\]/; +const subcommandDetailsPattern = /subcommandDetails: {([\s\S]*?)},/; + +// ----------------------------------- START OF UTILITY FUNCTIONS ---------------------------- // + +const refreshWiki = async () => { + await writeFile(wikiPath, ''); +}; + +const formatCommandName = async (name: string | undefined) => { + let formattedName = ''; + if (name === undefined) { + formattedName = 'None'; + } + else { + formattedName = name; + } + await appendFile(wikiPath, `## ${formattedName}\n`); +}; + +const formatSubcommandName = async (name: string | undefined, basecmd: string) => { + let formattedName = ''; + if (name === undefined) { + formattedName = 'None'; + } + else { + formattedName = name; + } + await appendFile(wikiPath, `## ${basecmd} ${formattedName}\n`); +}; + +const formatAliases = async (aliases: string | undefined) => { + let formattedAliases = ''; + if (aliases === undefined || aliases === '') { + formattedAliases = 'None'; + } + else { + formattedAliases = aliases.replace(/'/g, '').split(', ').map(alias => `\`${alias}\``).join(', ') + } + await appendFile(wikiPath, `- **Aliases:** ${formattedAliases}\n`); +}; + +const formatDescription = async (descr: string | undefined) => { + let formattedDescription = ''; + if (descr === undefined) { + formattedDescription = 'None'; + } + else { + formattedDescription = descr; + } + await appendFile(wikiPath, `- **Description:** ${formattedDescription}\n`); +}; + +const formatDetailedDescription = async (listStr: string | undefined) => { + let formattedDetailedDescription = ''; + if (listStr === undefined) { + formattedDetailedDescription = 'None'; + } + else { + const newlineList = listStr.replace(/\n/g, "
"); + const botPrefixList = newlineList.replace(new RegExp('\\${container.botPrefix}', 'g'), "."); + const bulletList = botPrefixList.replace(/\\/g, ''); + formattedDetailedDescription = bulletList; + } + await appendFile(wikiPath, `- ${formattedDetailedDescription}\n`); +}; + +const formatOptions = async (options: string[] | undefined) => { + if (options === undefined || options[0] === '') { + await appendFile(wikiPath, `- **Options:** None\n`); + } + else { + let fieldMatch; + const extractedOptions = []; + const optionFieldsPattern = /\{\s*name: '(.*?)',\s*description: '(.*?)'/g; + while ((fieldMatch = optionFieldsPattern.exec(options![0])) !== null) { + const name = fieldMatch[1]; + const description = fieldMatch[2]; + extractedOptions.push({ name, description }); + } + await appendFile(wikiPath, `- **Options:** \n`); + for (const desc of extractedOptions) { + await appendFile(wikiPath, ` - \`\`${desc.name}\`\`: ${desc.description}\n`); + } + } +}; + +const formatSubcommandDetails = async (subcommandDetails: string | undefined) => { + let formattedSubcommandDetails = ''; + if (subcommandDetails === undefined || subcommandDetails === '') { + formattedSubcommandDetails = 'None'; + } + else { + const regex = /\b(\w+)\s*:/g; + const matches = subcommandDetails.match(regex); + if (matches) { + const subcommandList = matches.map(match => match.trim().replace(/:$/, '')).join(', '); + formattedSubcommandDetails = subcommandList.replace(/'/g, '').split(', ').map(subcmd => `\`${subcmd}\``).join(', ') + } + } + await appendFile(wikiPath, `- **Subcommands:** ${formattedSubcommandDetails}\n\n`); +}; + +// ----------------------------------- END OF UTILITY FUNCTIONS ---------------------------- // + +export const updateWiki = async () => { + // Refresh COMMAND-WIKI.md + await refreshWiki(); + + const filesAndDirs = await readdir(commandDetailsDir); + const directories = filesAndDirs.filter(file => (statSync(`${commandDetailsDir}/${file}`).isDirectory())); + + for (const dir of directories) { + if (!compoundCommands.includes(dir)) { + await appendFile(wikiPath, `# ${dir.toUpperCase()}\n`); + const subDir = `${commandDetailsDir}/${dir}`; + const files = await readdir(subDir); + for (const file of files) { + const filePath = `${subDir}/${file}`; + const content = await readFile(filePath, 'utf-8'); + const match = content.match(commandDetailsPattern); + if (match) { + const codeSection = match[1]; + + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Format the info + await formatCommandName(name); + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); + } + } + } + else { + await appendFile(wikiPath, `# ${dir.toUpperCase()}\n`); + const mainCommandDir = `${commandsDir}/${dir}`; + const subCommandsdir = `${commandDetailsDir}/${dir}`; + + // Retrieve overview info + const mainCommandFiles = await readdir(mainCommandDir); + for (const file of mainCommandFiles) { + const filePath = `${mainCommandDir}/${file}`; + const content = await readFile(filePath, 'utf-8'); + const match = content.match(commandDetailsPattern); + if (match) { + const codeSection = match[1]; + + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Format the info + await formatCommandName(name); + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); + } + } + + // Retrieve subcommand infos + const subCommandFiles = await readdir(subCommandsdir); + for (const file of subCommandFiles) { + const filePath = `${subCommandsdir}/${file}`; + const content = await readFile(filePath, 'utf-8'); + const match = content.match(commandDetailsPattern); + if (match) { + const codeSection = match[1]; + + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Format the info + await formatSubcommandName(name, dir); + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); + } + } + } + } + + // Harcoding info for suggestion until it can be migrated to CodeyCommand framework + await appendFile(wikiPath, `# Suggestion \n`); + await appendFile(wikiPath, `## suggestion \n`); + await appendFile(wikiPath, `- **Aliases:** suggest\n`); + await appendFile(wikiPath, `- **Description:** Handle suggestion functions.\n`); + await appendFile(wikiPath, `- This command will forward a suggestion to the CSC Discord Mods. \ + Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. \ + If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. + **Examples:** + \`.suggestion I want a new Discord channel named #hobbies.\`\n`); + await appendFile(wikiPath, `- **Options:** \n`); + await appendFile(wikiPath, ` - \`\`details\`\`: Details of your suggestion\n`); + await appendFile(wikiPath, `- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\`\n\n`); +}; From b5edb6ccc73c567a2aca2fab289b2cd716f0778d Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Mon, 5 Feb 2024 15:26:32 +0000 Subject: [PATCH 02/18] Added updateWiki function to run and update command wiki --- src/events/ready.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/events/ready.ts b/src/events/ready.ts index 105277f7..2b7aba1c 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -5,6 +5,7 @@ import { initEmojis } from '../components/emojis'; import { vars } from '../config'; import { logger } from '../logger/default'; import { getRepositoryReleases } from '../utils/github'; +import { updateWiki } from '../utils/updateWiki'; const dev = process.env.NODE_ENV !== 'production'; @@ -45,4 +46,5 @@ export const initReady = (client: Client): void => { sendReady(client); initCrons(client); initEmojis(client); + updateWiki(); }; From 9b1f76808e44467218970a1f61ae18dc2e84090e Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Mon, 5 Feb 2024 15:28:22 +0000 Subject: [PATCH 03/18] Added section about command wiki --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d5c61359..03a71709 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ You can follow the instructions outlined [in this document](docs/SETUP.md). You can follow the steps [in this document](docs/CONTRIBUTING.md). +## Command Wiki + +Check out our [Command Wiki](docs/COMMAND-WIKI.md) for more details on CodeyBot commands! + ## License All rights reserved for images. From 3fcacc221eb96d763e1b9b157c507c2a10fbebfc Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Mon, 5 Feb 2024 15:59:04 +0000 Subject: [PATCH 04/18] Added section for coffechat --- src/utils/updateWiki.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index e089964d..8e313c54 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -231,4 +231,15 @@ export const updateWiki = async () => { await appendFile(wikiPath, `- **Options:** \n`); await appendFile(wikiPath, ` - \`\`details\`\`: Details of your suggestion\n`); await appendFile(wikiPath, `- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\`\n\n`); + + // Harcoding info for coffechat until it can be migrated to CodeyCommand framework + await appendFile(wikiPath, `# coffee \n`); + await appendFile(wikiPath, `## coffee \n`); + await appendFile(wikiPath, `- **Aliases:** None\n`); + await appendFile(wikiPath, `- **Description:** Handle coffee chat functions.\n`); + await appendFile(wikiPath, `- **Examples:** + \`.coffee match\` + \`.coffee test 10\`\n`); + await appendFile(wikiPath, `- **Options:** None\n`); + await appendFile(wikiPath, `- **Subcommands:** \`\`match\`\`, \`\`test\`\`\n\n`); }; From 78d3c8a46953d36e86eacce5c09adab285ce9759 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Mon, 5 Feb 2024 16:16:01 +0000 Subject: [PATCH 05/18] Added wiki for commands --- docs/COMMAND-WIKI.md | 327 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 docs/COMMAND-WIKI.md diff --git a/docs/COMMAND-WIKI.md b/docs/COMMAND-WIKI.md new file mode 100644 index 00000000..ff30b8e2 --- /dev/null +++ b/docs/COMMAND-WIKI.md @@ -0,0 +1,327 @@ +# ADMIN +## ban +- **Aliases:** None +- **Description:** Ban a user. +- **Examples:**
`.ban @jeff spam` +- **Options:** + - ``user``: The user to ban. + - ``reason``: The reason why we are banning the user. +- **Subcommands:** None + +# COIN +## coin +- **Aliases:** None +- **Description:** Handle coin functions. +- **Examples:**
`.coin adjust @Codey 100`
`.coin adjust @Codey -100 Codey broke.`
`.coin`
`.coin check @Codey`
`.coin c @Codey`
`.coin info`
`.coin i`
`.coin update @Codey 100`
`.coin update @Codey 0 Reset Codey's balance.`
`.coin transfer @Codey 10`
`.coin transfer @Codey 15 Lost a bet to Codey ` +- **Options:** None +- **Subcommands:** `adjust`, `check`, `info`, `update`, `leaderboard`, `transfer` + +## coin adjust +- **Aliases:** `a` +- **Description:** Adjust the coin balance of a user. +- **Examples:**
`.coin adjust @Codey 100`
`.coin adjust @Codey -100 Codey broke.` +- **Options:** + - ``user``: The user to adjust the balance of. + - ``amount``: The amount to adjust the balance of the specified user by. + - ``reason``: The reason why we are adjusting the balance. +- **Subcommands:** None + +## coin check +- **Aliases:** `c`, `b`, `balance`, `bal` +- **Description:** The user to check the balance of. +- **Examples:**
`.coin check @Codey`
`.coin c @Codey` +- **Options:** + - ``user``: The user to check the balance of. +- **Subcommands:** None + +## coin info +- **Aliases:** `information`, `i` +- **Description:** Get info about Codey coin. +- **Examples:**
`.coin info`
`.coin information`
`.coin i` +- **Options:** None +- **Subcommands:** None + +## coin leaderboard +- **Aliases:** `lb` +- **Description:** Get the current coin leaderboard. +- **Examples:**
`.coin lb`
`.coin leaderboard` +- **Options:** None +- **Subcommands:** None + +## coin transfer +- **Aliases:** `t` +- **Description:** Transfer coins from your balance to another user. +- **Examples:**
`.coin transfer @Codey 10`
`.coin transfer @Codey 10 Lost a bet to @Codey` +- **Options:** + - ``user``: The user to transfer coins to. + - ``amount``: The amount to transfer to the specified user. + - ``reason``: The reason for transferring. +- **Subcommands:** None + +## coin update +- **Aliases:** `u` +- **Description:** Update the coin balance of a user. +- **Examples:**
`.coin update @Codey 100` +- **Options:** + - ``user``: The user to update the balance of. + - ``amount``: The amount to update the balance of the specified user to. + - ``reason``: The reason why we are updating the balance. +- **Subcommands:** None + +# COMPANY +## company +- **Aliases:** None +- **Description:** None +- **Examples:**
`.company add coinbase SRE`
`.company find coinbase`
+- **Options:** None +- **Subcommands:** `enroll`, `add`, `remove`, `find`, `profile` + +## company add +- **Aliases:** `a` +- **Description:** Add a company to your profile +- **Examples:**
`.company add https://www.crunchbase.com/organization/microsoft`
`.company a microsoft ` +- **Options:** +- **Subcommands:** None + +## company enroll +- **Aliases:** `e` +- **Description:** None +- **Examples:**
`.company enroll https://www.crunchbase.com/organization/microsoft`
`.company enroll microsoft` +- **Options:** +- **Subcommands:** None + +## company find +- **Aliases:** `f` +- **Description:** Find all individuals that work at the company. +- **Examples:**
`.company find https://www.crunchbase.com/organization/microsoft`
`.company f microsoft` +- **Options:** +- **Subcommands:** None + +## company profile +- **Aliases:** `p` +- **Description:** List all the companies you are associated with +- **Examples:**
`.company profile`
`.company p` +- **Options:** None +- **Subcommands:** None + +## company remove +- **Aliases:** `r` +- **Description:** Remove a company to your profile +- **Examples:**
`.company remove https://www.crunchbase.com/organization/microsoft`
`.company r microsoft ` +- **Options:** +- **Subcommands:** None + +# FUN +## flipcoin +- **Aliases:** `fc`, `flip`, `flip-coin`, `coin-flip`, `coinflip` +- **Description:** None +- **Examples:**
`.flip-coin`
`.fc`
`.flip`
`.coin-flip`
`.coinflip`
`.flipcoin` +- **Options:** None +- **Subcommands:** None + +## rolldice +- **Aliases:** `rd`, `roll`, `roll-dice`, `dice-roll`, `diceroll`, `dice` +- **Description:** Roll a dice! :game_die: +- **Examples:**
`.roll-dice 6`
`.dice-roll 30`
`.roll 100`
`.rd 4`
`.diceroll 2`
`.dice 1`
`.rolldice 10` +- **Options:** + - ``sides``: The number of sides on the die. +- **Subcommands:** None + +# GAMES +## bj +- **Aliases:** `blj`, `blackjack`, `21` +- **Description:** Play a Blackjack game to win some Codey coins! +- **Examples:**
`.bj 100`
`.blj 100` +- **Options:** + - ``bet``: A valid bet amount +- **Subcommands:** None + +## connect4 +- **Aliases:** None +- **Description:** Play Connect 4! +- **Examples:**
`.connect4`
`.connect 4 @user` +- **Options:** None +- **Subcommands:** None + +## rps +- **Aliases:** None +- **Description:** Play Rock, Paper, Scissors! +- **Examples:**
`.rps`
`.rps 10` +- **Options:** + - ``bet``: How much to bet - default is 10. +- **Subcommands:** None + +# INTERVIEWER +## interviewers +- **Aliases:** `int`, `interviewer` +- **Description:** Handle interviewer functions. +- **Examples:**
`.interviewer`
`.interviewer frontend` +- **Options:** None +- **Subcommands:** `clear`, `domain`, `pause`, `profile`, `resume`, `signup`, `list` + +## interviewer clear +- **Aliases:** `clr` +- **Description:** Clear all your interviewer data +- **Examples:**
`.interviewer clear` +- **Options:** None +- **Subcommands:** None + +## interviewer domain +- **Aliases:** `domain` +- **Description:** Add/remove a domain of your choice +- **Examples:**
`.interviewer domain frontend` +- **Options:** + - ``domain_name``: A valid domain name +- **Subcommands:** None + +## interviewer list +- **Aliases:** `ls` +- **Description:** List all interviewers or those under a specific domain +- **Examples:**
`.interviewer list`
`.interviewer list backend` +- **Options:** + - ``domain``: The domain to be examined +- **Subcommands:** None + +## interviewer pause +- **Aliases:** `ps` +- **Description:** Put your interviewer profile on pause +- **Examples:**
`.interviewer pause` +- **Options:** None +- **Subcommands:** None + +## interviewer profile +- **Aliases:** `pf` +- **Description:** Display your interviewer profile data +- **Examples:**
`.interviewer profile` +- **Options:** None +- **Subcommands:** None + +## interviewer resume +- **Aliases:** `resume` +- **Description:** Resume your interviewer profile +- **Examples:**
`.interviewer resume` +- **Options:** None +- **Subcommands:** None + +## interviewer signup +- **Aliases:** `signup` +- **Description:** Sign yourself up to be an interviewer! +- **Examples:**
`.interviewer signup www.calendly.com` +- **Options:** + - ``calendar_url``: A valid calendly.com or x.ai calendar link +- **Subcommands:** None + +# LEETCODE +## leetcode +- **Aliases:** None +- **Description:** Handle LeetCode functions. +- +- **Options:** None +- **Subcommands:** `random`, `specific` + +## leetcode random +- **Aliases:** `r` +- **Description:** Get a random LeetCode problem. +- **Examples:**
`.leetcode`n
`.leetcode random` +- **Options:** + - ``difficulty``: The difficulty of the problem. +- **Subcommands:** None + +## leetcode specific +- **Aliases:** `spec`, `s` +- **Description:** Get a LeetCode problem with specified problem ID. +- **Examples:**
`.leetcode specific 1` +- **Options:** + - ``problem-id``: The problem ID. +- **Subcommands:** None + +# MISCELLANEOUS +## help +- **Aliases:** `wiki` +- **Description:** Get the URL to the wiki page. +- **Examples:**
`.help`
`.wiki` +- **Options:** None +- **Subcommands:** None + +## info +- **Aliases:** None +- **Description:** Get Codey information - app version, repository link and issue templates. +- **Examples:**
`.info` +- **Options:** None +- **Subcommands:** None + +## member +- **Aliases:** None +- **Description:** Get CSC membership information of a user. +- **Examples:**
`.member [id]` +- **Options:** + - ``uwid``: The Quest ID of the user. +- **Subcommands:** None + +## ping +- **Aliases:** `pong` +- **Description:** Ping the bot to see if it is alive. :ping_pong: +- **Examples:**
`.ping`
`.pong` +- **Options:** None +- **Subcommands:** None + +## uptime +- **Aliases:** `up`, `timeup` +- **Description:** None +- **Examples:**
`.uptime`
`.up`
`.timeup` +- **Options:** None +- **Subcommands:** None + +# PROFILE +## profile +- **Aliases:** `userprofile`, `aboutme` +- **Description:** Handle user profile functions. +- **Examples:**
`.profile @Codey` +- **Options:** None +- **Subcommands:** `about`, `grad`, `set` + +## profile about +- **Aliases:** `a` +- **Description:** Display user profile. +- **Examples:**
`.profile about @Codey`
`.profile a @Codey` +- **Options:** + - ``user``: The user to give profile of. +- **Subcommands:** None + +## profile grad +- **Aliases:** `g` +- **Description:** Update Grad Roles. +- **Examples:**
`.profile grad`
`.profile g` +- **Options:** None +- **Subcommands:** None + +## profile set +- **Aliases:** `s` +- **Description:** Set parameters of user profile. +- **Examples:**
`.profile set @Codey`
`.profile a @Codey` +- **Options:** + - ``customization``: The customization to be set for the user. + - ``description``: The description of the customization to be set for the user. +- **Subcommands:** None + +# Suggestion +## suggestion +- **Aliases:** suggest +- **Description:** Handle suggestion functions. +- This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. + **Examples:** + `.suggestion I want a new Discord channel named #hobbies.` +- **Options:** + - ``details``: Details of your suggestion +- **Subcommands:** ``list``, ``update``, ``create`` + +# coffee +## coffee +- **Aliases:** None +- **Description:** Handle coffee chat functions. +- **Examples:** + `.coffee match` + `.coffee test 10` +- **Options:** None +- **Subcommands:** ``match``, ``test`` + From fa95fec2d43777c93e98ef32dd6188e4a0214947 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Tue, 6 Feb 2024 01:01:36 +0000 Subject: [PATCH 06/18] Updated link to command wiki --- src/commandDetails/miscellaneous/help.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commandDetails/miscellaneous/help.ts b/src/commandDetails/miscellaneous/help.ts index aa9a90d1..a0e6effd 100644 --- a/src/commandDetails/miscellaneous/help.ts +++ b/src/commandDetails/miscellaneous/help.ts @@ -5,7 +5,7 @@ import { SapphireMessageResponse, } from '../../codeyCommand'; -const wikiLink = 'https://github.com/uwcsc/codeybot/wiki/Command-Help'; +const wikiLink = 'https://github.com/uwcsc/codeybot/blob/main/docs/COMMAND-WIKI.md'; const helpExecuteCommand: SapphireMessageExecuteType = ( _client, From e4a3af9cc0627d6f95525f5bc9f3ce760f901941 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Tue, 6 Feb 2024 01:11:49 +0000 Subject: [PATCH 07/18] Fixed linter issues --- src/utils/updateWiki.ts | 380 ++++++++++++++++++++-------------------- 1 file changed, 194 insertions(+), 186 deletions(-) diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index 8e313c54..ef7f8026 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -21,225 +21,233 @@ const subcommandDetailsPattern = /subcommandDetails: {([\s\S]*?)},/; // ----------------------------------- START OF UTILITY FUNCTIONS ---------------------------- // const refreshWiki = async () => { - await writeFile(wikiPath, ''); + await writeFile(wikiPath, ''); }; const formatCommandName = async (name: string | undefined) => { - let formattedName = ''; - if (name === undefined) { - formattedName = 'None'; - } - else { - formattedName = name; - } - await appendFile(wikiPath, `## ${formattedName}\n`); + let formattedName = ''; + if (name === undefined) { + formattedName = 'None'; + } else { + formattedName = name; + } + await appendFile(wikiPath, `## ${formattedName}\n`); }; const formatSubcommandName = async (name: string | undefined, basecmd: string) => { - let formattedName = ''; - if (name === undefined) { - formattedName = 'None'; - } - else { - formattedName = name; - } - await appendFile(wikiPath, `## ${basecmd} ${formattedName}\n`); + let formattedName = ''; + if (name === undefined) { + formattedName = 'None'; + } else { + formattedName = name; + } + await appendFile(wikiPath, `## ${basecmd} ${formattedName}\n`); }; const formatAliases = async (aliases: string | undefined) => { - let formattedAliases = ''; - if (aliases === undefined || aliases === '') { - formattedAliases = 'None'; - } - else { - formattedAliases = aliases.replace(/'/g, '').split(', ').map(alias => `\`${alias}\``).join(', ') - } - await appendFile(wikiPath, `- **Aliases:** ${formattedAliases}\n`); + let formattedAliases = ''; + if (aliases === undefined || aliases === '') { + formattedAliases = 'None'; + } else { + formattedAliases = aliases + .replace(/'/g, '') + .split(', ') + .map((alias) => `\`${alias}\``) + .join(', '); + } + await appendFile(wikiPath, `- **Aliases:** ${formattedAliases}\n`); }; const formatDescription = async (descr: string | undefined) => { - let formattedDescription = ''; - if (descr === undefined) { - formattedDescription = 'None'; - } - else { - formattedDescription = descr; - } - await appendFile(wikiPath, `- **Description:** ${formattedDescription}\n`); + let formattedDescription = ''; + if (descr === undefined) { + formattedDescription = 'None'; + } else { + formattedDescription = descr; + } + await appendFile(wikiPath, `- **Description:** ${formattedDescription}\n`); }; const formatDetailedDescription = async (listStr: string | undefined) => { - let formattedDetailedDescription = ''; - if (listStr === undefined) { - formattedDetailedDescription = 'None'; - } - else { - const newlineList = listStr.replace(/\n/g, "
"); - const botPrefixList = newlineList.replace(new RegExp('\\${container.botPrefix}', 'g'), "."); - const bulletList = botPrefixList.replace(/\\/g, ''); - formattedDetailedDescription = bulletList; - } - await appendFile(wikiPath, `- ${formattedDetailedDescription}\n`); + let formattedDetailedDescription = ''; + if (listStr === undefined) { + formattedDetailedDescription = 'None'; + } else { + const newlineList = listStr.replace(/\n/g, '
'); + const botPrefixList = newlineList.replace(new RegExp('\\${container.botPrefix}', 'g'), '.'); + const bulletList = botPrefixList.replace(/\\/g, ''); + formattedDetailedDescription = bulletList; + } + await appendFile(wikiPath, `- ${formattedDetailedDescription}\n`); }; const formatOptions = async (options: string[] | undefined) => { - if (options === undefined || options[0] === '') { - await appendFile(wikiPath, `- **Options:** None\n`); + if (options === undefined || options[0] === '') { + await appendFile(wikiPath, `- **Options:** None\n`); + } else { + let fieldMatch; + const extractedOptions = []; + const optionFieldsPattern = /\{\s*name: '(.*?)',\s*description: '(.*?)'/g; + while ((fieldMatch = optionFieldsPattern.exec(options![0])) !== null) { + const name = fieldMatch[1]; + const description = fieldMatch[2]; + extractedOptions.push({ name, description }); } - else { - let fieldMatch; - const extractedOptions = []; - const optionFieldsPattern = /\{\s*name: '(.*?)',\s*description: '(.*?)'/g; - while ((fieldMatch = optionFieldsPattern.exec(options![0])) !== null) { - const name = fieldMatch[1]; - const description = fieldMatch[2]; - extractedOptions.push({ name, description }); - } - await appendFile(wikiPath, `- **Options:** \n`); - for (const desc of extractedOptions) { - await appendFile(wikiPath, ` - \`\`${desc.name}\`\`: ${desc.description}\n`); - } + await appendFile(wikiPath, `- **Options:** \n`); + for (const desc of extractedOptions) { + await appendFile(wikiPath, ` - \`\`${desc.name}\`\`: ${desc.description}\n`); } + } }; const formatSubcommandDetails = async (subcommandDetails: string | undefined) => { - let formattedSubcommandDetails = ''; - if (subcommandDetails === undefined || subcommandDetails === '') { - formattedSubcommandDetails = 'None'; - } - else { - const regex = /\b(\w+)\s*:/g; - const matches = subcommandDetails.match(regex); - if (matches) { - const subcommandList = matches.map(match => match.trim().replace(/:$/, '')).join(', '); - formattedSubcommandDetails = subcommandList.replace(/'/g, '').split(', ').map(subcmd => `\`${subcmd}\``).join(', ') - } + let formattedSubcommandDetails = ''; + if (subcommandDetails === undefined || subcommandDetails === '') { + formattedSubcommandDetails = 'None'; + } else { + const regex = /\b(\w+)\s*:/g; + const matches = subcommandDetails.match(regex); + if (matches) { + const subcommandList = matches.map((match) => match.trim().replace(/:$/, '')).join(', '); + formattedSubcommandDetails = subcommandList + .replace(/'/g, '') + .split(', ') + .map((subcmd) => `\`${subcmd}\``) + .join(', '); } - await appendFile(wikiPath, `- **Subcommands:** ${formattedSubcommandDetails}\n\n`); + } + await appendFile(wikiPath, `- **Subcommands:** ${formattedSubcommandDetails}\n\n`); }; // ----------------------------------- END OF UTILITY FUNCTIONS ---------------------------- // -export const updateWiki = async () => { - // Refresh COMMAND-WIKI.md - await refreshWiki(); - - const filesAndDirs = await readdir(commandDetailsDir); - const directories = filesAndDirs.filter(file => (statSync(`${commandDetailsDir}/${file}`).isDirectory())); - - for (const dir of directories) { - if (!compoundCommands.includes(dir)) { - await appendFile(wikiPath, `# ${dir.toUpperCase()}\n`); - const subDir = `${commandDetailsDir}/${dir}`; - const files = await readdir(subDir); - for (const file of files) { - const filePath = `${subDir}/${file}`; - const content = await readFile(filePath, 'utf-8'); - const match = content.match(commandDetailsPattern); - if (match) { - const codeSection = match[1]; - - // Extract info pieces - const name = codeSection.match(namePattern)?.[1]; - const aliases = codeSection.match(aliasesPattern)?.[1]; - const description = codeSection.match(descriptionPattern)?.[1]; - const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; - const options = codeSection.match(optionsPattern)?.[1].split(', '); - const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; - - // Format the info - await formatCommandName(name); - await formatAliases(aliases); - await formatDescription(description); - await formatDetailedDescription(detailedDescription); - await formatOptions(options); - await formatSubcommandDetails(subcommandDetails); - } - } +export const updateWiki = async (): Promise => { + // Refresh COMMAND-WIKI.md + await refreshWiki(); + + const filesAndDirs = await readdir(commandDetailsDir); + const directories = filesAndDirs.filter((file) => + statSync(`${commandDetailsDir}/${file}`).isDirectory(), + ); + + for (const dir of directories) { + if (!compoundCommands.includes(dir)) { + await appendFile(wikiPath, `# ${dir.toUpperCase()}\n`); + const subDir = `${commandDetailsDir}/${dir}`; + const files = await readdir(subDir); + for (const file of files) { + const filePath = `${subDir}/${file}`; + const content = await readFile(filePath, 'utf-8'); + const match = content.match(commandDetailsPattern); + if (match) { + const codeSection = match[1]; + + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Format the info + await formatCommandName(name); + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); + } + } + } else { + await appendFile(wikiPath, `# ${dir.toUpperCase()}\n`); + const mainCommandDir = `${commandsDir}/${dir}`; + const subCommandsdir = `${commandDetailsDir}/${dir}`; + + // Retrieve overview info + const mainCommandFiles = await readdir(mainCommandDir); + for (const file of mainCommandFiles) { + const filePath = `${mainCommandDir}/${file}`; + const content = await readFile(filePath, 'utf-8'); + const match = content.match(commandDetailsPattern); + if (match) { + const codeSection = match[1]; + + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Format the info + await formatCommandName(name); + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); } - else { - await appendFile(wikiPath, `# ${dir.toUpperCase()}\n`); - const mainCommandDir = `${commandsDir}/${dir}`; - const subCommandsdir = `${commandDetailsDir}/${dir}`; - - // Retrieve overview info - const mainCommandFiles = await readdir(mainCommandDir); - for (const file of mainCommandFiles) { - const filePath = `${mainCommandDir}/${file}`; - const content = await readFile(filePath, 'utf-8'); - const match = content.match(commandDetailsPattern); - if (match) { - const codeSection = match[1]; - - // Extract info pieces - const name = codeSection.match(namePattern)?.[1]; - const aliases = codeSection.match(aliasesPattern)?.[1]; - const description = codeSection.match(descriptionPattern)?.[1]; - const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; - const options = codeSection.match(optionsPattern)?.[1].split(', '); - const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; - - // Format the info - await formatCommandName(name); - await formatAliases(aliases); - await formatDescription(description); - await formatDetailedDescription(detailedDescription); - await formatOptions(options); - await formatSubcommandDetails(subcommandDetails); - } - } - - // Retrieve subcommand infos - const subCommandFiles = await readdir(subCommandsdir); - for (const file of subCommandFiles) { - const filePath = `${subCommandsdir}/${file}`; - const content = await readFile(filePath, 'utf-8'); - const match = content.match(commandDetailsPattern); - if (match) { - const codeSection = match[1]; - - // Extract info pieces - const name = codeSection.match(namePattern)?.[1]; - const aliases = codeSection.match(aliasesPattern)?.[1]; - const description = codeSection.match(descriptionPattern)?.[1]; - const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; - const options = codeSection.match(optionsPattern)?.[1].split(', '); - const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; - - // Format the info - await formatSubcommandName(name, dir); - await formatAliases(aliases); - await formatDescription(description); - await formatDetailedDescription(detailedDescription); - await formatOptions(options); - await formatSubcommandDetails(subcommandDetails); - } - } + } + + // Retrieve subcommand infos + const subCommandFiles = await readdir(subCommandsdir); + for (const file of subCommandFiles) { + const filePath = `${subCommandsdir}/${file}`; + const content = await readFile(filePath, 'utf-8'); + const match = content.match(commandDetailsPattern); + if (match) { + const codeSection = match[1]; + + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Format the info + await formatSubcommandName(name, dir); + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); } + } } - - // Harcoding info for suggestion until it can be migrated to CodeyCommand framework - await appendFile(wikiPath, `# Suggestion \n`); - await appendFile(wikiPath, `## suggestion \n`); - await appendFile(wikiPath, `- **Aliases:** suggest\n`); - await appendFile(wikiPath, `- **Description:** Handle suggestion functions.\n`); - await appendFile(wikiPath, `- This command will forward a suggestion to the CSC Discord Mods. \ + } + + // Harcoding info for suggestion until it can be migrated to CodeyCommand framework + await appendFile(wikiPath, `# Suggestion \n`); + await appendFile(wikiPath, `## suggestion \n`); + await appendFile(wikiPath, `- **Aliases:** suggest\n`); + await appendFile(wikiPath, `- **Description:** Handle suggestion functions.\n`); + await appendFile( + wikiPath, + `- This command will forward a suggestion to the CSC Discord Mods. \ Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. \ If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. **Examples:** - \`.suggestion I want a new Discord channel named #hobbies.\`\n`); - await appendFile(wikiPath, `- **Options:** \n`); - await appendFile(wikiPath, ` - \`\`details\`\`: Details of your suggestion\n`); - await appendFile(wikiPath, `- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\`\n\n`); - - // Harcoding info for coffechat until it can be migrated to CodeyCommand framework - await appendFile(wikiPath, `# coffee \n`); - await appendFile(wikiPath, `## coffee \n`); - await appendFile(wikiPath, `- **Aliases:** None\n`); - await appendFile(wikiPath, `- **Description:** Handle coffee chat functions.\n`); - await appendFile(wikiPath, `- **Examples:** + \`.suggestion I want a new Discord channel named #hobbies.\`\n`, + ); + await appendFile(wikiPath, `- **Options:** \n`); + await appendFile(wikiPath, ` - \`\`details\`\`: Details of your suggestion\n`); + await appendFile(wikiPath, `- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\`\n\n`); + + // Harcoding info for coffechat until it can be migrated to CodeyCommand framework + await appendFile(wikiPath, `# coffee \n`); + await appendFile(wikiPath, `## coffee \n`); + await appendFile(wikiPath, `- **Aliases:** None\n`); + await appendFile(wikiPath, `- **Description:** Handle coffee chat functions.\n`); + await appendFile( + wikiPath, + `- **Examples:** \`.coffee match\` - \`.coffee test 10\`\n`); - await appendFile(wikiPath, `- **Options:** None\n`); - await appendFile(wikiPath, `- **Subcommands:** \`\`match\`\`, \`\`test\`\`\n\n`); + \`.coffee test 10\`\n`, + ); + await appendFile(wikiPath, `- **Options:** None\n`); + await appendFile(wikiPath, `- **Subcommands:** \`\`match\`\`, \`\`test\`\`\n\n`); }; From 7b6509aa8f4343b0801af88583510ba6fd0e0e62 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Tue, 6 Feb 2024 01:17:52 +0000 Subject: [PATCH 08/18] Updated titles for suggestion and coffe chat --- docs/COMMAND-WIKI.md | 4 ++-- src/utils/updateWiki.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/COMMAND-WIKI.md b/docs/COMMAND-WIKI.md index ff30b8e2..69babaeb 100644 --- a/docs/COMMAND-WIKI.md +++ b/docs/COMMAND-WIKI.md @@ -304,7 +304,7 @@ - ``description``: The description of the customization to be set for the user. - **Subcommands:** None -# Suggestion +# SUGGESTION ## suggestion - **Aliases:** suggest - **Description:** Handle suggestion functions. @@ -315,7 +315,7 @@ - ``details``: Details of your suggestion - **Subcommands:** ``list``, ``update``, ``create`` -# coffee +# COFFEE CHAT ## coffee - **Aliases:** None - **Description:** Handle coffee chat functions. diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index ef7f8026..70348678 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -221,7 +221,7 @@ export const updateWiki = async (): Promise => { } // Harcoding info for suggestion until it can be migrated to CodeyCommand framework - await appendFile(wikiPath, `# Suggestion \n`); + await appendFile(wikiPath, `# SUGGESTION \n`); await appendFile(wikiPath, `## suggestion \n`); await appendFile(wikiPath, `- **Aliases:** suggest\n`); await appendFile(wikiPath, `- **Description:** Handle suggestion functions.\n`); @@ -238,7 +238,7 @@ export const updateWiki = async (): Promise => { await appendFile(wikiPath, `- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\`\n\n`); // Harcoding info for coffechat until it can be migrated to CodeyCommand framework - await appendFile(wikiPath, `# coffee \n`); + await appendFile(wikiPath, `# COFFEE CHAT \n`); await appendFile(wikiPath, `## coffee \n`); await appendFile(wikiPath, `- **Aliases:** None\n`); await appendFile(wikiPath, `- **Description:** Handle coffee chat functions.\n`); From 7cd0bc1527606e42d1cfc7b41d4c4ec69ed40ed3 Mon Sep 17 00:00:00 2001 From: probro27 Date: Thu, 8 Feb 2024 09:19:08 -0500 Subject: [PATCH 09/18] Mounted the docs folder so wiki updates in the repository --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 1134b407..68fb4dd9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,3 +10,4 @@ services: - ./src:/usr/app/src - ./logs:/usr/app/logs - ./db:/usr/app/db + - ./docs:/usr/app/docs From 8e66a738f8790091849e6fdb0f6e1491942ed45d Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Thu, 8 Feb 2024 14:33:16 +0000 Subject: [PATCH 10/18] Updated script to be more efficient --- src/utils/updateWiki.ts | 78 +++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index 70348678..df181ce7 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -1,4 +1,4 @@ -import { statSync } from 'fs'; +import { statSync, readdirSync } from 'fs'; import { appendFile, writeFile, readdir, readFile } from 'fs/promises'; // File Paths @@ -7,7 +7,27 @@ const commandsDir = 'src/commands'; const commandDetailsDir = 'src/commandDetails'; // Commands with subcommands, to be handled separately -const compoundCommands = ['coin', 'company', 'interviewer', 'leetcode', 'profile']; +const retrieveCompoundCommands = (): string[] => { + let compoundCommands: string[] = []; + + const filesAndDirs = readdirSync(commandsDir); + const directories = filesAndDirs.filter((file) => + statSync(`${commandsDir}/${file}`).isDirectory(), + ); + + for (const dir of directories) { + const subDir = `${commandsDir}/${dir}`; + const files = readdirSync(subDir); + if (files.length === 1 && files[0] === `${dir}.ts`) { + compoundCommands.push(dir); + } + } + + return compoundCommands; +}; + +// As of Feb 8 2024, this should be ['coin', 'company', 'interviewer', 'leetcode', 'profile']; +const compoundCommands: string[] = retrieveCompoundCommands(); // RegEx patterns to extract info const commandDetailsPattern = /.*const .+CommandDetails: CodeyCommandDetails = {([\s\S]*?)\n}/; @@ -20,10 +40,6 @@ const subcommandDetailsPattern = /subcommandDetails: {([\s\S]*?)},/; // ----------------------------------- START OF UTILITY FUNCTIONS ---------------------------- // -const refreshWiki = async () => { - await writeFile(wikiPath, ''); -}; - const formatCommandName = async (name: string | undefined) => { let formattedName = ''; if (name === undefined) { @@ -123,7 +139,7 @@ const formatSubcommandDetails = async (subcommandDetails: string | undefined) => export const updateWiki = async (): Promise => { // Refresh COMMAND-WIKI.md - await refreshWiki(); + await writeFile(wikiPath, ''); const filesAndDirs = await readdir(commandDetailsDir); const directories = filesAndDirs.filter((file) => @@ -221,33 +237,27 @@ export const updateWiki = async (): Promise => { } // Harcoding info for suggestion until it can be migrated to CodeyCommand framework - await appendFile(wikiPath, `# SUGGESTION \n`); - await appendFile(wikiPath, `## suggestion \n`); - await appendFile(wikiPath, `- **Aliases:** suggest\n`); - await appendFile(wikiPath, `- **Description:** Handle suggestion functions.\n`); - await appendFile( - wikiPath, - `- This command will forward a suggestion to the CSC Discord Mods. \ - Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. \ - If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. - **Examples:** - \`.suggestion I want a new Discord channel named #hobbies.\`\n`, - ); - await appendFile(wikiPath, `- **Options:** \n`); - await appendFile(wikiPath, ` - \`\`details\`\`: Details of your suggestion\n`); - await appendFile(wikiPath, `- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\`\n\n`); + const suggestionContents = `# SUGGESTION + ## suggestion + - **Aliases:** suggest + - **Description:** Handle suggestion functions. + - This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. + **Examples:** + \`\`.suggestion I want a new Discord channel named #hobbies.\`\` + - **Options:** + - \`\`details\`\`: Details of your suggestion + - **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\``; + await appendFile(wikiPath, `${suggestionContents}\n\n`); // Harcoding info for coffechat until it can be migrated to CodeyCommand framework - await appendFile(wikiPath, `# COFFEE CHAT \n`); - await appendFile(wikiPath, `## coffee \n`); - await appendFile(wikiPath, `- **Aliases:** None\n`); - await appendFile(wikiPath, `- **Description:** Handle coffee chat functions.\n`); - await appendFile( - wikiPath, - `- **Examples:** - \`.coffee match\` - \`.coffee test 10\`\n`, - ); - await appendFile(wikiPath, `- **Options:** None\n`); - await appendFile(wikiPath, `- **Subcommands:** \`\`match\`\`, \`\`test\`\`\n\n`); + const coffeeContents = `# COFFEE CHAT + ## coffee + - **Aliases:** None + - **Description:** Handle coffee chat functions. + - **Examples:** + \`\`.coffee match\`\` + \`\`.coffee test 10\`\` + - **Options:** None + - **Subcommands:** \`\`match\`\`, \`\`test\`\``; + await appendFile(wikiPath, `${coffeeContents}\n\n`); }; From 581e9d08a25ef5dc5f6b863bc94b6590b7ee1665 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Thu, 8 Feb 2024 14:33:58 +0000 Subject: [PATCH 11/18] Updated wiki --- docs/COMMAND-WIKI.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/COMMAND-WIKI.md b/docs/COMMAND-WIKI.md index 69babaeb..9dd97f45 100644 --- a/docs/COMMAND-WIKI.md +++ b/docs/COMMAND-WIKI.md @@ -305,23 +305,23 @@ - **Subcommands:** None # SUGGESTION -## suggestion -- **Aliases:** suggest -- **Description:** Handle suggestion functions. -- This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. - **Examples:** - `.suggestion I want a new Discord channel named #hobbies.` -- **Options:** - - ``details``: Details of your suggestion -- **Subcommands:** ``list``, ``update``, ``create`` + ## suggestion + - **Aliases:** suggest + - **Description:** Handle suggestion functions. + - This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. + **Examples:** + ``.suggestion I want a new Discord channel named #hobbies.`` + - **Options:** + - ``details``: Details of your suggestion + - **Subcommands:** ``list``, ``update``, ``create`` # COFFEE CHAT -## coffee -- **Aliases:** None -- **Description:** Handle coffee chat functions. -- **Examples:** - `.coffee match` - `.coffee test 10` -- **Options:** None -- **Subcommands:** ``match``, ``test`` + ## coffee + - **Aliases:** None + - **Description:** Handle coffee chat functions. + - **Examples:** + ``.coffee match`` + ``.coffee test 10`` + - **Options:** None + - **Subcommands:** ``match``, ``test`` From 5f33fd72f66c2c3517d8098224fe9784715ce651 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Thu, 8 Feb 2024 14:41:12 +0000 Subject: [PATCH 12/18] Allowed Docker container to properly modify wiki --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 1134b407..68fb4dd9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,3 +10,4 @@ services: - ./src:/usr/app/src - ./logs:/usr/app/logs - ./db:/usr/app/db + - ./docs:/usr/app/docs From 9d1f963d6c621c1a25a75f12e94ff57dd6d10f7b Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Thu, 8 Feb 2024 15:07:20 +0000 Subject: [PATCH 13/18] Fixed linter issues --- src/utils/updateWiki.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index df181ce7..6e32279f 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -8,7 +8,7 @@ const commandDetailsDir = 'src/commandDetails'; // Commands with subcommands, to be handled separately const retrieveCompoundCommands = (): string[] => { - let compoundCommands: string[] = []; + const compoundCommands: string[] = []; const filesAndDirs = readdirSync(commandsDir); const directories = filesAndDirs.filter((file) => From cffd8460b68e43f97fa7b3fa2bc1e97d7c54b2d0 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Thu, 8 Feb 2024 15:20:12 +0000 Subject: [PATCH 14/18] Added logs for updating wiki --- src/events/ready.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/events/ready.ts b/src/events/ready.ts index 2b7aba1c..147353b2 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -46,5 +46,11 @@ export const initReady = (client: Client): void => { sendReady(client); initCrons(client); initEmojis(client); + logger.info({ + message: 'Updating wiki...', + }); updateWiki(); + logger.info({ + message: 'Wiki successfully updated', + }); }; From aee5041b7cb9689989fb8e088b00ef53d9849976 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Fri, 9 Feb 2024 02:02:16 +0000 Subject: [PATCH 15/18] Reformatted suggestion and coffee --- docs/COMMAND-WIKI.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/COMMAND-WIKI.md b/docs/COMMAND-WIKI.md index 9dd97f45..787770ec 100644 --- a/docs/COMMAND-WIKI.md +++ b/docs/COMMAND-WIKI.md @@ -305,23 +305,23 @@ - **Subcommands:** None # SUGGESTION - ## suggestion - - **Aliases:** suggest - - **Description:** Handle suggestion functions. - - This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. - **Examples:** - ``.suggestion I want a new Discord channel named #hobbies.`` - - **Options:** - - ``details``: Details of your suggestion - - **Subcommands:** ``list``, ``update``, ``create`` +## suggestion +- **Aliases:** ``suggest`` +- **Description:** Handle suggestion functions. +- This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. + **Examples:** + ``.suggestion I want a new Discord channel named #hobbies.`` +- **Options:** + - ``details``: Details of your suggestion +- **Subcommands:** ``list``, ``update``, ``create`` # COFFEE CHAT - ## coffee - - **Aliases:** None - - **Description:** Handle coffee chat functions. - - **Examples:** - ``.coffee match`` - ``.coffee test 10`` - - **Options:** None - - **Subcommands:** ``match``, ``test`` +## coffee +- **Aliases:** None +- **Description:** Handle coffee chat functions. +- **Examples:** + ``.coffee match`` + ``.coffee test 10`` +- **Options:** None +- **Subcommands:** ``match``, ``test`` From b3d69be73cd152e3fc0c5e91a027212c46d5b477 Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Fri, 9 Feb 2024 02:02:42 +0000 Subject: [PATCH 16/18] Removed logger for wiki update --- src/events/ready.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/events/ready.ts b/src/events/ready.ts index 147353b2..2b7aba1c 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -46,11 +46,5 @@ export const initReady = (client: Client): void => { sendReady(client); initCrons(client); initEmojis(client); - logger.info({ - message: 'Updating wiki...', - }); updateWiki(); - logger.info({ - message: 'Wiki successfully updated', - }); }; From e3fdac2eedf786b1d9ad899fccff765451c988ee Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Fri, 9 Feb 2024 02:03:05 +0000 Subject: [PATCH 17/18] Added loggers in updateWiki() --- src/utils/updateWiki.ts | 43 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index 6e32279f..c5a765cd 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -1,5 +1,6 @@ import { statSync, readdirSync } from 'fs'; import { appendFile, writeFile, readdir, readFile } from 'fs/promises'; +import { logger } from '../logger/default'; // File Paths const wikiPath = 'docs/COMMAND-WIKI.md'; @@ -138,6 +139,10 @@ const formatSubcommandDetails = async (subcommandDetails: string | undefined) => // ----------------------------------- END OF UTILITY FUNCTIONS ---------------------------- // export const updateWiki = async (): Promise => { + logger.info({ + message: 'Updating wiki...', + }); + // Refresh COMMAND-WIKI.md await writeFile(wikiPath, ''); @@ -238,26 +243,30 @@ export const updateWiki = async (): Promise => { // Harcoding info for suggestion until it can be migrated to CodeyCommand framework const suggestionContents = `# SUGGESTION - ## suggestion - - **Aliases:** suggest - - **Description:** Handle suggestion functions. - - This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. - **Examples:** - \`\`.suggestion I want a new Discord channel named #hobbies.\`\` - - **Options:** - - \`\`details\`\`: Details of your suggestion - - **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\``; +## suggestion +- **Aliases:** \`\`suggest\`\` +- **Description:** Handle suggestion functions. +- This command will forward a suggestion to the CSC Discord Mods. Please note that your suggestion is not anonymous, your Discord username and ID will be recorded. If you don't want to make a suggestion in public, you could use this command via a DM to Codey instead. + **Examples:** + \`\`.suggestion I want a new Discord channel named #hobbies.\`\` +- **Options:** + - \`\`details\`\`: Details of your suggestion +- **Subcommands:** \`\`list\`\`, \`\`update\`\`, \`\`create\`\``; await appendFile(wikiPath, `${suggestionContents}\n\n`); // Harcoding info for coffechat until it can be migrated to CodeyCommand framework const coffeeContents = `# COFFEE CHAT - ## coffee - - **Aliases:** None - - **Description:** Handle coffee chat functions. - - **Examples:** - \`\`.coffee match\`\` - \`\`.coffee test 10\`\` - - **Options:** None - - **Subcommands:** \`\`match\`\`, \`\`test\`\``; +## coffee +- **Aliases:** None +- **Description:** Handle coffee chat functions. +- **Examples:** + \`\`.coffee match\`\` + \`\`.coffee test 10\`\` +- **Options:** None +- **Subcommands:** \`\`match\`\`, \`\`test\`\``; await appendFile(wikiPath, `${coffeeContents}\n\n`); + + logger.info({ + message: 'Wiki successfully updated.', + }); }; From c69d15b51bee8f97c07196de3937444056a69abd Mon Sep 17 00:00:00 2001 From: Di Nguyen Date: Fri, 9 Feb 2024 19:17:05 +0000 Subject: [PATCH 18/18] Modularized extracting and formatting command detals, added exception check --- src/utils/updateWiki.ts | 100 +++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 57 deletions(-) diff --git a/src/utils/updateWiki.ts b/src/utils/updateWiki.ts index c5a765cd..8ced3243 100644 --- a/src/utils/updateWiki.ts +++ b/src/utils/updateWiki.ts @@ -41,24 +41,22 @@ const subcommandDetailsPattern = /subcommandDetails: {([\s\S]*?)},/; // ----------------------------------- START OF UTILITY FUNCTIONS ---------------------------- // -const formatCommandName = async (name: string | undefined) => { +const formatCommandName = async ( + name: string | undefined, + baseCmd: string | undefined = undefined, +) => { let formattedName = ''; if (name === undefined) { formattedName = 'None'; } else { formattedName = name; } - await appendFile(wikiPath, `## ${formattedName}\n`); -}; -const formatSubcommandName = async (name: string | undefined, basecmd: string) => { - let formattedName = ''; - if (name === undefined) { - formattedName = 'None'; + if (baseCmd === undefined) { + await appendFile(wikiPath, `## ${formattedName}\n`); } else { - formattedName = name; + await appendFile(wikiPath, `## ${baseCmd} ${formattedName}\n`); } - await appendFile(wikiPath, `## ${basecmd} ${formattedName}\n`); }; const formatAliases = async (aliases: string | undefined) => { @@ -136,6 +134,39 @@ const formatSubcommandDetails = async (subcommandDetails: string | undefined) => await appendFile(wikiPath, `- **Subcommands:** ${formattedSubcommandDetails}\n\n`); }; +const extractAndFormat = async ( + codeSection: string, + baseCmd: string | undefined = undefined, +): Promise => { + // Extract info pieces + const name = codeSection.match(namePattern)?.[1]; + const aliases = codeSection.match(aliasesPattern)?.[1]; + const description = codeSection.match(descriptionPattern)?.[1]; + const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; + const options = codeSection.match(optionsPattern)?.[1].split(', '); + const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; + + // Just in case command name cannot be extracted, which is not to be expected + if (name === undefined) { + logger.error({ + message: `Could not find command name from ${codeSection}`, + }); + return; + } + + // Format the info + if (baseCmd === undefined) { + await formatCommandName(name); + } else { + await formatCommandName(name, baseCmd); + } + await formatAliases(aliases); + await formatDescription(description); + await formatDetailedDescription(detailedDescription); + await formatOptions(options); + await formatSubcommandDetails(subcommandDetails); +}; + // ----------------------------------- END OF UTILITY FUNCTIONS ---------------------------- // export const updateWiki = async (): Promise => { @@ -162,22 +193,7 @@ export const updateWiki = async (): Promise => { const match = content.match(commandDetailsPattern); if (match) { const codeSection = match[1]; - - // Extract info pieces - const name = codeSection.match(namePattern)?.[1]; - const aliases = codeSection.match(aliasesPattern)?.[1]; - const description = codeSection.match(descriptionPattern)?.[1]; - const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; - const options = codeSection.match(optionsPattern)?.[1].split(', '); - const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; - - // Format the info - await formatCommandName(name); - await formatAliases(aliases); - await formatDescription(description); - await formatDetailedDescription(detailedDescription); - await formatOptions(options); - await formatSubcommandDetails(subcommandDetails); + await extractAndFormat(codeSection); } } } else { @@ -193,22 +209,7 @@ export const updateWiki = async (): Promise => { const match = content.match(commandDetailsPattern); if (match) { const codeSection = match[1]; - - // Extract info pieces - const name = codeSection.match(namePattern)?.[1]; - const aliases = codeSection.match(aliasesPattern)?.[1]; - const description = codeSection.match(descriptionPattern)?.[1]; - const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; - const options = codeSection.match(optionsPattern)?.[1].split(', '); - const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; - - // Format the info - await formatCommandName(name); - await formatAliases(aliases); - await formatDescription(description); - await formatDetailedDescription(detailedDescription); - await formatOptions(options); - await formatSubcommandDetails(subcommandDetails); + await extractAndFormat(codeSection); } } @@ -220,22 +221,7 @@ export const updateWiki = async (): Promise => { const match = content.match(commandDetailsPattern); if (match) { const codeSection = match[1]; - - // Extract info pieces - const name = codeSection.match(namePattern)?.[1]; - const aliases = codeSection.match(aliasesPattern)?.[1]; - const description = codeSection.match(descriptionPattern)?.[1]; - const detailedDescription = codeSection.match(detailedDescriptionPattern)?.[1]; - const options = codeSection.match(optionsPattern)?.[1].split(', '); - const subcommandDetails = codeSection.match(subcommandDetailsPattern)?.[1]; - - // Format the info - await formatSubcommandName(name, dir); - await formatAliases(aliases); - await formatDescription(description); - await formatDetailedDescription(detailedDescription); - await formatOptions(options); - await formatSubcommandDetails(subcommandDetails); + await extractAndFormat(codeSection, dir); } } }