From ca1c399a262ec528eca52fb9ca03dfda769aa5a8 Mon Sep 17 00:00:00 2001 From: Hypeism Date: Fri, 16 Jun 2023 17:52:08 +0900 Subject: [PATCH] added repo command --- .github/CHANGELOG.md | 3 + .github/command-docs.md | 16 +-- src/commands/general/chatgpt.ts | 84 +++++++------- src/commands/general/repo.ts | 75 ++++++++++++ src/commands/index.ts | 26 +++-- src/constants.ts | 58 +++++----- src/index.ts | 197 ++++++++++++++++---------------- src/lib/createCommand.ts | 32 +++--- 8 files changed, 288 insertions(+), 203 deletions(-) create mode 100644 src/commands/general/repo.ts diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 87ad80f..53d1e62 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,3 +1,6 @@ +# June 2023 +- feat: (06/16/2023): Add repo link command + # May 2023 - feat: (05/25/2023): Add new role handler, update gpt command, changelog - fix: (05/26/2023): Fix permissions loophole in 'roles' command diff --git a/.github/command-docs.md b/.github/command-docs.md index 4ba8306..4eedae9 100644 --- a/.github/command-docs.md +++ b/.github/command-docs.md @@ -2,19 +2,21 @@ ### General -| Command | Description | -|:--------|:----------------------| -| /chat | Interact with chatgpt | +| Command | Description | +| :------ | :---------------------------- | +| /chat | Interact with chatgpt | +| /repo | Fetch one of the OWDDM repos. | + ### Memes | Command | Description | -|:--------|:------------------------------------------| +| :------ | :---------------------------------------- | | /cowsay | emulates the famous UNIX program 'cowsay' | ### Moderation -| Command | Description | -|:--------|:----------------------------------------------------------------------------------------------------| -| /roles | | +| Command | Description | +| :------ | :---------- | +| /roles | | diff --git a/src/commands/general/chatgpt.ts b/src/commands/general/chatgpt.ts index dfe9b8d..c2a9984 100644 --- a/src/commands/general/chatgpt.ts +++ b/src/commands/general/chatgpt.ts @@ -1,11 +1,11 @@ import Logger, {CommandDefinition, makeEmbed, InputCommandOptions, makeLines} from '../../lib'; import { CommandCategory, ResponseType } from '../../constants'; import { openai_api } from '../../index'; -import { EmbedBuilder, Colors } from "discord.js"; +import { EmbedBuilder, Colors } from 'discord.js'; const chatgptOptions: InputCommandOptions[] = [{ - name: 'prompt', - description: 'what will you ask?' + name: 'prompt', + description: 'what will you ask?' }]; export const chatgpt: CommandDefinition = { @@ -17,46 +17,46 @@ export const chatgpt: CommandDefinition = { interaction: async (interaction) => { const input = interaction.options.getString(chatgptOptions[0].name)?.trim() ?? 'no text provided'; - const maxChars = 300; - if(input.length > maxChars) { - const overMaxEmbed = new EmbedBuilder({ - title: '🚫 Error: Character Limit Exceeded', - description: makeLines([ - `Please provide a prompt less than ${maxChars} characters long.`, - `You provided ${input.length} characters.`, - '', - `For prompts longer than the maximum, please visit https://chat.openai.com` - ]), - color: Colors.Red, - }) - await interaction.reply({ - embeds: [overMaxEmbed], - ephemeral: true, - }); - return; - } + const maxChars = 300; + if(input.length > maxChars) { + const overMaxEmbed = new EmbedBuilder({ + title: '🚫 Error: Character Limit Exceeded', + description: makeLines([ + `Please provide a prompt less than ${maxChars} characters long.`, + `You provided ${input.length} characters.`, + '', + 'For prompts longer than the maximum, please visit https://chat.openai.com' + ]), + color: Colors.Red, + }); + await interaction.reply({ + embeds: [overMaxEmbed], + ephemeral: true, + }); + return; + } - await interaction.deferReply(); - try { - console.log(`${input.length}`) - const response = await openai_api.createChatCompletion({ - model: 'gpt-3.5-turbo', - messages: [{role:'user', content: input}], - }); + await interaction.deferReply(); + try { + console.log(`${input.length}`); + const response = await openai_api.createChatCompletion({ + model: 'gpt-3.5-turbo', + messages: [{role:'user', content: input}], + }); - const responseEmbed = makeEmbed({ - title: input, - description: response.data.choices[0].message?.content, - footer: { - text: 'Model provided by OpenAI', - } - }) + const responseEmbed = makeEmbed({ + title: input, + description: response.data.choices[0].message?.content, + footer: { + text: 'Model provided by OpenAI', + } + }); - await interaction.followUp({embeds: [responseEmbed]}); + await interaction.followUp({embeds: [responseEmbed]}); - } catch (error) { - Logger.error(error) - await interaction.reply('error: ' + error) - } - } -} + } catch (error) { + Logger.error(error); + await interaction.reply('error: ' + error); + } + } +}; diff --git a/src/commands/general/repo.ts b/src/commands/general/repo.ts new file mode 100644 index 0000000..8e66a8f --- /dev/null +++ b/src/commands/general/repo.ts @@ -0,0 +1,75 @@ +import Logger, { CommandDefinition, makeEmbed, makeLines, InputCommandOptions } from '../../lib'; +import { CommandCategory, ResponseType } from '../../constants'; +import { throws } from 'assert'; + +const repos = new Map ([ + ['org', 'https://github.com/owddm/org'], + ['site','https://github.com/owddm/owddm.com'], + ['data', 'https://github.com/owddm/public'], + ['bot', 'https://github.com/owddm/discord-bot'], + ['accounting', 'https://github.com/owddm/accounting'], + ['survey', 'https://github.com/owddm/survey'], + ['presentations', 'https://github.com/owddm/presentations'], + ['legacy_site', 'https://github.com/owddm/owddm.github.io'] +]); + +const repoOptions: InputCommandOptions[] = [{ + name: 'repo', + description: 'enter a valid repo name to get it\'s url' +}]; + +function getRepoUrl(option: string): string { + const repo = repos.get(option); + if (repo === undefined) { + throw Object.assign(new Error(`Error: ${option} not found`), {code: 404}); + } + + return repo; +} + +const errorEmbed = makeEmbed({ + title: '🚫 Error: Repository not found', + description: makeLines([ + 'The indicated repository wasn\'t found, please enter one of the following repositories: ', + '', + '• org', + '', + '• site', + '', + '• data', + '', + '• bot', + '', + '• accounting', + '', + '• survey', + '', + '• presentations', + '', + '• legacy_site' + ]) +}); + + +export const repo: CommandDefinition = { + name: 'repo', + options: repoOptions, + description: 'Fetch one of the OWDDM repos.', + category: CommandCategory.GENERAL, + response: ResponseType.EDIT, + interaction: async (interaction) => { + const input = interaction.options.getString(repoOptions[0].name)?.trim() ?? 'no text provided'; + + try { + const response = getRepoUrl(input); + + interaction.reply({ + content: response + }); + } catch (error) { + interaction.reply({ + embeds: [errorEmbed] + }); + } + } +}; diff --git a/src/commands/index.ts b/src/commands/index.ts index 10bf828..c762fdd 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,21 +1,23 @@ -import Logger, { CommandDefinition } from "../lib"; +import Logger, { CommandDefinition } from '../lib'; import { cowsay } from './memes/cowsay'; -import { chatgpt } from "./general/chatgpt"; +import { chatgpt } from './general/chatgpt'; import { roleSelect } from './moderation/role_assignment'; +import { repo } from './general/repo'; export const commands: CommandDefinition[] = [ - cowsay, - chatgpt, - roleSelect, -] + cowsay, + chatgpt, + roleSelect, + repo, +]; const commandsObject: { [k: string]: CommandDefinition } = {}; for (const def of commands) { - for (const name of def.name) { - if (commandsObject[def.name]) { - Logger.warn(`Duplicate command/alias inserted: ${name}`); - } - } - commandsObject[def.name] = def; + for (const name of def.name) { + if (commandsObject[def.name]) { + Logger.warn(`Duplicate command/alias inserted: ${name}`); + } + } + commandsObject[def.name] = def; } diff --git a/src/constants.ts b/src/constants.ts index 787c49e..dcecb73 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -33,34 +33,34 @@ export const enum Roles { } export const regionRoles = [ - 'Osaka', - 'Kyoto', - 'Kobe', - 'Tokyo', - 'Aichi', - 'Abroad' -] + 'Osaka', + 'Kyoto', + 'Kobe', + 'Tokyo', + 'Aichi', + 'Abroad' +]; export const techRoles = [ - 'typescript', - 'javascript', - 'csharp', - 'java', - 'kotlin', - 'php', - 'dart', - 'html', - 'css', - 'ruby', - 'rust', - 'clang', - 'cpp', - 'python', - 'react', - 'angular', - 'svelte', - 'dotnet', - 'flutter', - 'vue', - 'solidjs', -] + 'typescript', + 'javascript', + 'csharp', + 'java', + 'kotlin', + 'php', + 'dart', + 'html', + 'css', + 'ruby', + 'rust', + 'clang', + 'cpp', + 'python', + 'react', + 'angular', + 'svelte', + 'dotnet', + 'flutter', + 'vue', + 'solidjs', +]; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 65cacc2..cabfdba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,119 +3,122 @@ import dotenv from 'dotenv'; import Logger, {createCommand, makeLines} from './lib'; import { commands } from './commands/index'; import { Configuration, OpenAIApi } from 'openai'; -import { roleHandler } from "./handlers/role"; -import { roleSelect } from './commands/moderation/role_assignment'; -import { Channels } from './constants'; +import { roleHandler } from './handlers/role'; dotenv.config(); export const client = new Client({ - intents: [ - 'Guilds', - 'GuildMembers', - 'GuildPresences', - 'GuildMessages', - 'GuildMessageReactions', - 'GuildEmojisAndStickers', - 'DirectMessages', - 'DirectMessageReactions', - 'DirectMessageTyping', - 'MessageContent', - ], - partials: [ - Partials.Channel, - Partials.Message, - Partials.GuildMember, - Partials.Reaction, - Partials.User, - Partials.ThreadMember, - ] + intents: [ + 'Guilds', + 'GuildMembers', + 'GuildPresences', + 'GuildMessages', + 'GuildMessageReactions', + 'GuildEmojisAndStickers', + 'DirectMessages', + 'DirectMessageReactions', + 'DirectMessageTyping', + 'MessageContent', + ], + partials: [ + Partials.Channel, + Partials.Message, + Partials.GuildMember, + Partials.Reaction, + Partials.User, + Partials.ThreadMember, + ] }); export const openai_api: OpenAIApi = new OpenAIApi(new Configuration({ - organization: `${process.env.OPENAI_ORG}`, - apiKey: `${process.env.OPENAI_TOKEN}`, + organization: `${process.env.OPENAI_ORG}`, + apiKey: `${process.env.OPENAI_TOKEN}`, })); client.login(process.env.DISCORD_TOKEN) - .then() - .catch((e) => { - Logger.error(e); - process.exit(1); - }); + .then() + .catch((e) => { + Logger.error(e); + process.exit(1); + }); client.on('ready', async() => { - if(client.user == null) { - console.log('error, client not found'); - } else { - console.log(`Logged in as ${client.user.username}`) - } - - try { - const existingCommands = await client.application?.commands.fetch()!; - for (const existingCommand of existingCommands.values()) { - const commandExists = commands.find((cmd) => cmd.name.includes(existingCommand.name)); - if (!commandExists) { - await client.application?.commands.delete(existingCommand.id); - Logger.info(`Deleted old command: ${existingCommand.name}`); - } - } - } catch (error) { - Logger.error(`Failed to delete old commands: ${error}`); - } - - for (const command of commands) { - const slashCommand = createCommand(command); - client.application?.commands.create(slashCommand); - Logger.info(`Created command ${slashCommand.name}!`) - } + if(client.user == null) { + console.log('error, client not found'); + } else { + console.log(`Logged in as ${client.user.username}`); + } + + const existingCommands = await client.application?.commands.fetch(); + + if (existingCommands == undefined) { + return; + } + + try { + for (const existingCommand of existingCommands.values()) { + const commandExists = commands.find((cmd) => cmd.name.includes(existingCommand.name)); + if (!commandExists) { + await client.application?.commands.delete(existingCommand.id); + Logger.info(`Deleted old command: ${existingCommand.name}`); + } + } + } catch (error) { + Logger.error(`Failed to delete old commands: ${error}`); + } + + for (const command of commands) { + const slashCommand = createCommand(command); + client.application?.commands.create(slashCommand); + Logger.info(`Created command ${slashCommand.name}!`); + } }); client.on('interactionCreate', async (interaction: any) => { - if(!interaction.guild) { - Logger.error('bailing because interaction in DM'); - return; - } - - if(interaction.user.bot) { - Logger.error('Bailing due to bot message'); - } - - if (interaction.isStringSelectMenu()) { - await roleHandler(interaction) - } - - - - for(const command of commands) { - if (interaction.commandName == command.name) { - if(command.interaction == undefined) { - Logger.error(`Error: ${command.name} has no interaction`); - break; - } - - if(command.requiredPermissions) { - // ignore if user doesn't have permissions - if(!interaction.member.permissions.has(command.requiredPermissions)) { - interaction.reply({ - content: makeLines([ - `you do not have sufficient permissions to use this command.`, - '', - `(missing: ${command.requiredPermissions.join(', ')})` - ]), - ephemeral: true - }); - Logger.info(`User ${interaction.member.user.username} doesn't have permissions to run ${command.name}`) - return; - } - } - - Logger.info(`Running command ${command.name}`) - await command.interaction(interaction) - - } - } + if(!interaction.guild) { + Logger.error('bailing because interaction in DM'); + return; + } + + if(interaction.user.bot) { + Logger.error('Bailing due to bot message'); + } + + if (interaction.isStringSelectMenu()) { + await roleHandler(interaction); + } + + + + for(const command of commands) { + if (interaction.commandName == command.name) { + if(command.interaction == undefined) { + Logger.error(`Error: ${command.name} has no interaction`); + break; + } + + if(command.requiredPermissions) { + // ignore if user doesn't have permissions + if(!interaction.member.permissions.has(command.requiredPermissions)) { + interaction.reply({ + content: makeLines([ + 'you do not have sufficient permissions to use this command.', + '', + `(missing: ${command.requiredPermissions.join(', ')})` + ]), + ephemeral: true + }); + Logger.info(`User ${interaction.member.user.username} doesn't have permissions to run ${command.name}`); + return; + } + } + + Logger.info(`Running command ${command.name}`); + await command.interaction(interaction); + + } + } }); diff --git a/src/lib/createCommand.ts b/src/lib/createCommand.ts index 658a10e..f10e2c0 100644 --- a/src/lib/createCommand.ts +++ b/src/lib/createCommand.ts @@ -4,25 +4,25 @@ import Logger from './logger'; import {APIPost, CommandDefinition} from './CommandDefinition'; function addStringOption(builder: SlashCommandBuilder, name: string, description: string) { - return builder.addStringOption(option => - option.setName(name) - .setDescription(description) - .setRequired(true) - ); + return builder.addStringOption(option => + option.setName(name) + .setDescription(description) + .setRequired(true) + ); } export function createCommand(command: CommandDefinition): APIPost { - const builder = new SlashCommandBuilder() - .setName(command.name) - .setDescription(command.description); + const builder = new SlashCommandBuilder() + .setName(command.name) + .setDescription(command.description); - if (command.options) { - command.options.forEach(option => { - addStringOption(builder, option.name, option.description); - }); - } else if (command.response !== ResponseType.STATIC) { - Logger.error(`Error: ${command.name} is of type ${command.response} with no option name or description`); - } + if (command.options) { + command.options.forEach(option => { + addStringOption(builder, option.name, option.description); + }); + } else if (command.response !== ResponseType.STATIC) { + Logger.error(`Error: ${command.name} is of type ${command.response} with no option name or description`); + } - return builder.toJSON(); + return builder.toJSON(); }