From fcb0267559696ab9cbcf16742466edf40b8916cf Mon Sep 17 00:00:00 2001 From: Kevin Dang <77701718+kevinthedang@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:43:19 -0700 Subject: [PATCH] Shutoff Bot Command (#30) * add: disable chat command * update: workflow name * add: shutoff using admin env list * update: sample env for admins * fix: shutdown booleans * update: version increment --- .env.sample | 5 +++- .github/workflows/build-test.yml | 3 +- docker-compose.yml | 2 +- package-lock.json | 4 +-- package.json | 2 +- src/commands/disable.ts | 33 ++++++++++++++++++++++ src/commands/index.ts | 6 +++- src/commands/shutoff.ts | 48 ++++++++++++++++++++++++++++++++ src/events/messageCreate.ts | 11 ++++++-- src/keys.ts | 3 +- src/utils/jsonHandler.ts | 3 +- 11 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 src/commands/disable.ts create mode 100644 src/commands/shutoff.ts diff --git a/.env.sample b/.env.sample index 7e4dd37..33e8e33 100644 --- a/.env.sample +++ b/.env.sample @@ -21,4 +21,7 @@ OLLAMA_PORT = PORT DISCORD_IP = IP_ADDRESS # subnet address, ex. 172.18.0.0 as we use /16. -SUBNET_ADDRESS = ADDRESS \ No newline at end of file +SUBNET_ADDRESS = ADDRESS + +# list of admins to handle admin commands for the bot, use single quotes +ADMINS=['username1', 'username2', 'username3', ...] \ No newline at end of file diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 07984b1..a04cc0b 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,4 +1,4 @@ -name: Test Discord-Ollama Builds +name: Builds run-name: Validate Node and Docker Builds on: push: @@ -74,7 +74,6 @@ jobs: run: | (docker images | grep -q 'discord/bot' && docker images | grep -qE 'ollama/ollama') || exit 1 - - name: Check Containers Exist run: | (docker ps | grep -q 'ollama' && docker ps | grep -q 'discord') || exit 1 diff --git a/docker-compose.yml b/docker-compose.yml index 54a2ca4..107e60d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: build: ./ # find docker file in designated path container_name: discord restart: always # rebuild container always - image: discord/bot:0.2.0 + image: discord/bot:0.3.4 environment: CLIENT_TOKEN: ${CLIENT_TOKEN} GUILD_ID: ${GUILD_ID} diff --git a/package-lock.json b/package-lock.json index c117054..f270b05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "discord-ollama", - "version": "0.3.3", + "version": "0.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "discord-ollama", - "version": "0.3.3", + "version": "0.3.4", "license": "ISC", "dependencies": { "axios": "^1.6.2", diff --git a/package.json b/package.json index 4c569d6..fcb351e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord-ollama", - "version": "0.3.3", + "version": "0.3.4", "description": "Ollama Integration into discord", "main": "build/index.js", "exports": "./build/index.js", diff --git a/src/commands/disable.ts b/src/commands/disable.ts new file mode 100644 index 0000000..f26440c --- /dev/null +++ b/src/commands/disable.ts @@ -0,0 +1,33 @@ +import { ChannelType, Client, CommandInteraction, ApplicationCommandOptionType } from 'discord.js' +import { SlashCommand } from '../utils/commands.js' +import { openFile } from '../utils/jsonHandler.js' + +export const Disable: SlashCommand = { + name: 'toggle-chat', + description: 'toggle all chat features, slash commands will still work.', + + // set available user options to pass to the command + options: [ + { + name: 'enabled', + description: 'true = enabled, false = disabled', + type: ApplicationCommandOptionType.Boolean, + required: true + } + ], + + // Query for message information and set the style + run: async (client: Client, interaction: CommandInteraction) => { + // fetch channel and message + const channel = await client.channels.fetch(interaction.channelId) + if (!channel || channel.type !== ChannelType.GuildText) return + + // set state of bot chat features + openFile('config.json', interaction.commandName, interaction.options.get('enabled')?.value) + + interaction.reply({ + content: `Chat features has been \`${interaction.options.get('enabled')?.value ? "enabled" : "disabled" }\``, + ephemeral: true + }) + } +} \ No newline at end of file diff --git a/src/commands/index.ts b/src/commands/index.ts index 6dcfb8d..e9d8490 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -2,9 +2,13 @@ import { SlashCommand } from '../utils/commands.js' import { ThreadCreate } from './threadCreate.js' import { MessageStyle } from './messageStyle.js' import { MessageStream } from './messageStream.js' +import { Disable } from './disable.js' +import { Shutoff } from './shutoff.js' export default [ ThreadCreate, MessageStyle, - MessageStream + MessageStream, + Disable, + Shutoff ] as SlashCommand[] \ No newline at end of file diff --git a/src/commands/shutoff.ts b/src/commands/shutoff.ts new file mode 100644 index 0000000..8e9a573 --- /dev/null +++ b/src/commands/shutoff.ts @@ -0,0 +1,48 @@ +import { ChannelType, Client, CommandInteraction, ApplicationCommandOptionType } from 'discord.js' +import { SlashCommand } from '../utils/commands.js' +import Keys from '../keys.js' + +export const Shutoff: SlashCommand = { + name: 'shutoff', + description: 'shutdown the bot. You will need to manually bring it online again.', + + // set available user options to pass to the command + options: [ + { + name: 'are-you-sure', + description: 'true = yes, false = I\'m scared', + type: ApplicationCommandOptionType.Boolean, + required: true + } + ], + + // Query for message information and set the style + run: async (client: Client, interaction: CommandInteraction) => { + // fetch channel and message + const channel = await client.channels.fetch(interaction.channelId) + if (!channel || channel.type !== ChannelType.GuildText) return + + // log this, this will probably be improtant for logging who did this + console.log(`User -> ${interaction.user.tag} attempting to shutdown ${client.user!!.tag}`) + + // create list of superUsers based on string parse + const superUsers: string[] = JSON.parse(Keys.superUser.replace(/'/g, '"')) + + // check if admin or false on shutdown + if (interaction.user.tag in superUsers || !(!interaction.options.get('are-you-sure')?.value && interaction.user.tag in superUsers)) { + interaction.reply({ + content: `Shutdown failed:\n\n${interaction.user.tag}, You do not have permission to shutoff **${client.user?.tag}**, otherwise, you just didn't want to.`, + ephemeral: true + }) + return // stop from shutting down + } + + interaction.reply({ + content: `${client.user?.tag} is ${interaction.options.get('are-you-sure')?.value ? "shutting down now." : "not shutting down." }`, + ephemeral: true + }) + + // clean up client instance and stop + client.destroy() + } +} \ No newline at end of file diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 97bbbea..9bac64f 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -28,8 +28,15 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama try { const config: Configuration = await new Promise((resolve, reject) => { getConfig('config.json', (config) => { + // check if config.json exists if (config === undefined) { - reject(new Error('No Configuration is set up.')) + reject(new Error('No Configuration is set up.\n\nCreating \`config.json\` with \`message-style\` set as \`true\` for embedded messages.\nPlease try chatting again.')) + return + } + + // check if chat is disabled + if(!config.options['toggle-chat']) { + reject(new Error('Admin(s) have disabled chat features.\n\n Please contact your server\'s admin(s).')) return } resolve(config) @@ -55,6 +62,6 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama } catch (error: any) { msgHist.pop() // remove message because of failure openFile('config.json', 'message-style', true) - message.reply(`**Response generation failed.**\n\n**Reason:** *${error.message}*\n\nCreating \`config.json\` with \`message-style\` set as \`true\` for embedded messages.\nPlease try chatting again.`) + message.reply(`**Response generation failed.**\n\n**Reason:** *${error.message}*`) } }) \ No newline at end of file diff --git a/src/keys.ts b/src/keys.ts index 20c6db6..3c2a6d4 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -7,7 +7,8 @@ export const Keys = { clientUid: getEnvVar('CLIENT_UID'), guildId: getEnvVar('GUILD_ID'), ipAddress: getEnvVar('OLLAMA_IP'), - portAddress: getEnvVar('OLLAMA_PORT') + portAddress: getEnvVar('OLLAMA_PORT'), + superUser: getEnvVar('ADMINS') } as const // readonly keys export default Keys \ No newline at end of file diff --git a/src/utils/jsonHandler.ts b/src/utils/jsonHandler.ts index dde619f..4bb257b 100644 --- a/src/utils/jsonHandler.ts +++ b/src/utils/jsonHandler.ts @@ -4,7 +4,8 @@ export interface Configuration { readonly name: string options: { 'message-stream'?: boolean, - 'message-style'?: boolean + 'message-style'?: boolean, + 'toggle-chat'?: boolean } }