Removed Channel Toggle Command (#115)

* Remove: channel-toggle as command and server config

* Remove: Thread interface

* Fix: Users Thread files will now delete

* Fix: Any user can chat in threads now

* Fix: Thread history files are now deleted with multiple users

* Update: version increment
This commit is contained in:
Kevin Dang
2024-09-14 13:34:40 -07:00
committed by GitHub
parent b49b464afb
commit 36a0cd309b
12 changed files with 86 additions and 182 deletions

View File

@@ -7,7 +7,7 @@ services:
build: ./ # find docker file in designated path build: ./ # find docker file in designated path
container_name: discord container_name: discord
restart: always # rebuild container always restart: always # rebuild container always
image: kevinthedang/discord-ollama:0.5.10 image: kevinthedang/discord-ollama:0.5.11
environment: environment:
CLIENT_TOKEN: ${CLIENT_TOKEN} CLIENT_TOKEN: ${CLIENT_TOKEN}
MODEL: ${MODEL} MODEL: ${MODEL}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "discord-ollama", "name": "discord-ollama",
"version": "0.5.10", "version": "0.5.11",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "discord-ollama", "name": "discord-ollama",
"version": "0.5.10", "version": "0.5.11",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"discord.js": "^14.15.3", "discord.js": "^14.15.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "discord-ollama", "name": "discord-ollama",
"version": "0.5.10", "version": "0.5.11",
"description": "Ollama Integration into discord", "description": "Ollama Integration into discord",
"main": "build/index.js", "main": "build/index.js",
"exports": "./build/index.js", "exports": "./build/index.js",

View File

@@ -1,33 +0,0 @@
import { ApplicationCommandOptionType, ChannelType, Client, CommandInteraction } from 'discord.js'
import { SlashCommand } from '../utils/commands.js'
import { openConfig } from '../utils/index.js'
export const ChannelToggle: SlashCommand = {
name: 'channel-toggle',
description: 'toggles channel or thread usage.',
// set user option for toggling
options: [
{
name: 'toggle-channel',
description: 'toggle channel usage, otherwise threads',
type: ApplicationCommandOptionType.Boolean,
required: true
}
],
// Query for chatting preference
run: async (client: Client, interaction: CommandInteraction) => {
// fetch channel location
const channel = await client.channels.fetch(interaction.channelId)
if (!channel || channel.type !== (ChannelType.PrivateThread && ChannelType.PublicThread && ChannelType.GuildText)) return
// set state of bot channel preferences
openConfig(`${interaction.guildId}-config.json`, interaction.commandName, interaction.options.get('toggle-channel')?.value)
interaction.reply({
content: `Channel Preferences have for Regular Channels set to \`${interaction.options.get('toggle-channel')?.value}\``,
ephemeral: true
})
}
}

View File

@@ -6,7 +6,6 @@ import { Disable } from './disable.js'
import { Shutoff } from './shutoff.js' import { Shutoff } from './shutoff.js'
import { Capacity } from './capacity.js' import { Capacity } from './capacity.js'
import { PrivateThreadCreate } from './threadPrivateCreate.js' import { PrivateThreadCreate } from './threadPrivateCreate.js'
import { ChannelToggle } from './channelToggle.js'
import { ClearUserChannelHistory } from './cleanUserChannelHistory.js' import { ClearUserChannelHistory } from './cleanUserChannelHistory.js'
export default [ export default [
@@ -17,6 +16,5 @@ export default [
Disable, Disable,
Shutoff, Shutoff,
Capacity, Capacity,
ChannelToggle,
ClearUserChannelHistory ClearUserChannelHistory
] as SlashCommand[] ] as SlashCommand[]

View File

@@ -1,6 +1,6 @@
import { ChannelType, Client, CommandInteraction, TextChannel } from 'discord.js' import { ChannelType, Client, CommandInteraction, TextChannel, ThreadChannel } from 'discord.js'
import { SlashCommand } from '../utils/commands.js' import { SlashCommand } from '../utils/commands.js'
import { openThreadInfo } from '../utils/index.js' import { openChannelInfo } from '../utils/index.js'
export const ThreadCreate: SlashCommand = { export const ThreadCreate: SlashCommand = {
name: 'thread', name: 'thread',
@@ -19,11 +19,12 @@ export const ThreadCreate: SlashCommand = {
}) })
// Send a message in the thread // Send a message in the thread
thread.send(`Hello ${interaction.user} and others! \n\nIt's nice to meet you. Please talk to me by typing **@${client.user?.username}** with your prompt.\n\nIf I do not respond, ensure \`channel-toggle\` is set to \`false\``) thread.send(`Hello ${interaction.user} and others! \n\nIt's nice to meet you. Please talk to me by typing **@${client.user?.username}** with your prompt.`)
// handle storing this chat channel // handle storing this chat channel
// store: thread.id, thread.name openChannelInfo(thread.id,
openThreadInfo(`${thread.id}.json`, thread) thread as ThreadChannel,
interaction.user.tag)
// user only reply // user only reply
return interaction.reply({ return interaction.reply({

View File

@@ -1,6 +1,6 @@
import { ChannelType, Client, CommandInteraction, TextChannel } from 'discord.js' import { ChannelType, Client, CommandInteraction, TextChannel, ThreadChannel } from 'discord.js'
import { SlashCommand } from '../utils/commands.js' import { SlashCommand } from '../utils/commands.js'
import { openThreadInfo } from '../utils/index.js' import { openChannelInfo } from '../utils/index.js'
export const PrivateThreadCreate: SlashCommand = { export const PrivateThreadCreate: SlashCommand = {
name: 'private-thread', name: 'private-thread',
@@ -23,7 +23,10 @@ export const PrivateThreadCreate: SlashCommand = {
// handle storing this chat channel // handle storing this chat channel
// store: thread.id, thread.name // store: thread.id, thread.name
openThreadInfo(`${thread.id}.json`, thread) openChannelInfo(thread.id,
thread as ThreadChannel,
interaction.user.tag
)
// user only reply // user only reply
return interaction.reply({ return interaction.reply({

View File

@@ -1,7 +1,7 @@
import { embedMessage, event, Events, normalMessage, UserMessage } from '../utils/index.js' import { embedMessage, event, Events, normalMessage, UserMessage } from '../utils/index.js'
import { getChannelInfo, getServerConfig, getThread, getUserConfig, openChannelInfo, openConfig, openThreadInfo, ServerConfig, UserConfig } from '../utils/index.js' import { getChannelInfo, getServerConfig, getUserConfig, openChannelInfo, openConfig, ServerConfig, UserConfig } from '../utils/index.js'
import { clean } from '../utils/mentionClean.js' import { clean } from '../utils/mentionClean.js'
import { TextChannel, ThreadChannel } from 'discord.js' import { TextChannel } from 'discord.js'
/** /**
* Max Message length for free users is 2000 characters (bot or not). * Max Message length for free users is 2000 characters (bot or not).
@@ -9,7 +9,7 @@ import { TextChannel, ThreadChannel } from 'discord.js'
* *
* @param message the message received from the channel * @param message the message received from the channel
*/ */
export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama, client }, message) => { export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama }, message) => {
log(`Message \"${clean(message.content)}\" from ${message.author.tag} in channel/thread ${message.channelId}.`) log(`Message \"${clean(message.content)}\" from ${message.author.tag} in channel/thread ${message.channelId}.`)
// Do not respond if bot talks in the chat // Do not respond if bot talks in the chat
@@ -23,15 +23,12 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
try { try {
// Retrieve Server/Guild Preferences // Retrieve Server/Guild Preferences
const serverConfig: ServerConfig = await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
getServerConfig(`${message.guildId}-config.json`, (config) => { getServerConfig(`${message.guildId}-config.json`, (config) => {
// check if config.json exists // check if config.json exists
if (config === undefined) { if (config === undefined) {
// Allowing chat options to be available // Allowing chat options to be available
openConfig(`${message.guildId}-config.json`, 'toggle-chat', true) openConfig(`${message.guildId}-config.json`, 'toggle-chat', true)
// default to channel scope chats
openConfig(`${message.guildId}-config.json`, 'channel-toggle', true)
reject(new Error('No Server Preferences is set up.\n\nCreating default server preferences file...\nPlease try chatting again.')) reject(new Error('No Server Preferences is set up.\n\nCreating default server preferences file...\nPlease try chatting again.'))
return return
} }
@@ -42,14 +39,6 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
return return
} }
// ensure channel json exists, if not create it
if (config.options['channel-toggle']) {
openChannelInfo(message.channelId,
message.channel as TextChannel,
message.author.username
)
}
resolve(config) resolve(config)
}) })
}) })
@@ -80,27 +69,35 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
}) })
}) })
// need new check for "open/active" threads/channels here! // need new check for "open/active" threads/channels here!
const chatMessages: UserMessage[] = await new Promise((resolve) => { let chatMessages: UserMessage[] = await new Promise((resolve) => {
// set new queue to modify // set new queue to modify
if (serverConfig.options['channel-toggle']) { getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => {
if (channelInfo?.messages)
resolve(channelInfo.messages)
else {
log(`Channel/Thread ${message.channel}-${message.author.username} does not exist. File will be created shortly...`)
resolve([])
}
})
})
if (chatMessages.length === 0) {
chatMessages = await new Promise((resolve, reject) => {
openChannelInfo(message.channelId,
message.channel as TextChannel,
message.author.tag
)
getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => { getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => {
if (channelInfo?.messages) if (channelInfo?.messages)
resolve(channelInfo.messages) resolve(channelInfo.messages)
else else {
log(`Channel ${message.channel}-${message.author.username} does not exist.`) log(`Channel/Thread ${message.channel}-${message.author.username} does not exist. File will be created shortly...`)
reject(new Error(`Failed to find ${message.author.username}'s history. Try chatting again.`))
}
}) })
} else { })
getThread(`${message.channelId}.json`, (threadInfo) => { }
if (threadInfo?.messages)
resolve(threadInfo.messages)
else
log(`Thread ${message.channelId} does not exist.`)
})
}
})
// response string for ollama to put its response // response string for ollama to put its response
let response: string let response: string
@@ -136,18 +133,11 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
}) })
// only update the json on success // only update the json on success
if (serverConfig.options['channel-toggle']) { openChannelInfo(message.channelId,
openChannelInfo(message.channelId, message.channel as TextChannel,
message.channel as TextChannel, message.author.tag,
message.author.tag, msgHist.getItems()
msgHist.getItems() )
)
} else {
openThreadInfo(`${message.channelId}.json`,
client.channels.fetch(message.channelId) as unknown as ThreadChannel,
msgHist.getItems()
)
}
} catch (error: any) { } catch (error: any) {
msgHist.pop() // remove message because of failure msgHist.pop() // remove message because of failure
message.reply(`**Error Occurred:**\n\n**Reason:** *${error.message}*`) message.reply(`**Error Occurred:**\n\n**Reason:** *${error.message}*`)

View File

@@ -5,16 +5,36 @@ import fs from 'fs'
/** /**
* Event to remove the associated .json file for a thread once deleted * Event to remove the associated .json file for a thread once deleted
*/ */
export default event(Events.ThreadDelete, ({ log }, thread: ThreadChannel) => { export default event(Events.ThreadDelete, async ({ log }, thread: ThreadChannel) => {
const filePath = `data/${thread.id}.json` // iterate through every guild member in the thread and delete their history, except the bot
if (fs.existsSync(filePath)) { try {
fs.unlink(filePath, (error) => { log(`Number of User Guild Members in Thread being deleted: ${thread.memberCount!! - 1}`)
if (error) const dirPath = 'data/'
log(`Error deleting file ${filePath}`, error)
else // read all files in data/
log(`Successfully deleted ${filePath} thread info`) fs.readdir(dirPath, (error, files) => {
if (error) {
log(`Error reading directory ${dirPath}`, error)
return
}
// filter files by thread id being deleted
const filesToDiscard = files.filter(
file => file.startsWith(`${thread.id}-`) &&
file.endsWith('.json'))
// remove files by unlinking
filesToDiscard.forEach(file => {
const filePath = dirPath + file
fs.unlink(filePath, error => {
if (error)
log(`Error deleting file ${filePath}`, error)
else
log(`Successfully deleted ${filePath} thread information`)
})
})
}) })
} else { } catch (error) {
log(`File ${filePath} does not exist.`) log(`Issue deleting user history files from ${thread.id}`)
} }
}) })

View File

@@ -8,7 +8,6 @@ export interface UserConfiguration {
export interface ServerConfiguration { export interface ServerConfiguration {
'toggle-chat'?: boolean, 'toggle-chat'?: boolean,
'channel-toggle'?: boolean
} }
/** /**
@@ -35,12 +34,6 @@ export interface ServerConfig {
options: ServerConfiguration options: ServerConfiguration
} }
export interface Thread {
readonly id: string
readonly name: string
messages: UserMessage[]
}
export interface Channel { export interface Channel {
readonly id: string readonly id: string
readonly name: string readonly name: string
@@ -54,5 +47,5 @@ export interface Channel {
* @returns true if command is from Server Config, false otherwise * @returns true if command is from Server Config, false otherwise
*/ */
export function isServerConfigurationKey(key: string): key is keyof ServerConfiguration { export function isServerConfigurationKey(key: string): key is keyof ServerConfiguration {
return ['toggle-chat', 'channel-toggle'].includes(key); return ['toggle-chat'].includes(key);
} }

View File

@@ -1,63 +1,8 @@
import { TextChannel, ThreadChannel } from 'discord.js' import { TextChannel, ThreadChannel } from 'discord.js'
import { Configuration, Thread, Channel, UserMessage } from '../index.js' import { Configuration, Channel, UserMessage } from '../index.js'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
/**
* Method to open/create and modify a json file containing thread information
*
* @param filename name of the thread file
* @param thread the thread with all of the interactions
* @param message message contents and from who
*/
export function openThreadInfo(filename: string, thread: ThreadChannel, messages: UserMessage[] = []) {
// check if the file exists, if not then make the config file
const fullFileName = `data/${filename}`
if (fs.existsSync(fullFileName)) {
fs.readFile(fullFileName, 'utf8', (error, data) => {
if (error)
console.log(`[Error: openThreadInfo] Incorrect file format`)
else {
const object = JSON.parse(data)
object['messages'] = messages as []
fs.writeFileSync(fullFileName, JSON.stringify(object, null, 2))
}
})
} else { // file doesn't exist, create it
const object: Configuration = JSON.parse(`{ \"id\": \"${thread?.id}\", \"name\": \"${thread?.name}\", \"messages\": []}`)
const directory = path.dirname(fullFileName)
if (!fs.existsSync(directory))
fs.mkdirSync(directory, { recursive: true })
// only creating it, no need to add anything
fs.writeFileSync(fullFileName, JSON.stringify(object, null, 2))
console.log(`[Util: openThreadInfo] Created '${fullFileName}' in working directory`)
}
}
/**
* Method to obtain the configurations of the message chat/thread
*
* @param filename name of the configuration file to get
* @param callback function to allow a promise from getting the config
*/
export async function getThread(filename: string, callback: (config: Thread | undefined) => void): Promise<void> {
// attempt to read the file and get the configuration
const fullFileName = `data/${filename}`
if (fs.existsSync(fullFileName)) {
fs.readFile(fullFileName, 'utf8', (error, data) => {
if (error) {
callback(undefined)
return // something went wrong... stop
}
callback(JSON.parse(data))
})
} else {
callback(undefined) // file not found
}
}
/** /**
* Method to check if a thread history file exists * Method to check if a thread history file exists
* *
@@ -65,16 +10,15 @@ export async function getThread(filename: string, callback: (config: Thread | un
* @returns true if channel does not exist, false otherwise * @returns true if channel does not exist, false otherwise
*/ */
async function checkChannelInfoExists(channel: TextChannel, user: string) { async function checkChannelInfoExists(channel: TextChannel, user: string) {
// thread exist handler const doesExists: boolean = await new Promise((resolve) => {
const isThread: boolean = await new Promise((resolve) => { getChannelInfo(`${channel.id}-${user}.json`, (channelInfo) => {
getThread(`${channel.id}-${user}.json`, (channelInfo) => {
if (channelInfo?.messages) if (channelInfo?.messages)
resolve(true) resolve(true)
else else
resolve(false) resolve(false)
}) })
}) })
return isThread return doesExists
} }
/** /**
@@ -121,19 +65,7 @@ export async function clearChannelInfo(filename: string, channel: TextChannel, u
* @param user the user's name * @param user the user's name
* @param messages their messages * @param messages their messages
*/ */
export async function openChannelInfo(filename: string, channel: TextChannel, user: string, messages: UserMessage[] = []): Promise<void> { export async function openChannelInfo(filename: string, channel: TextChannel | ThreadChannel, user: string, messages: UserMessage[] = []): Promise<void> {
const isThread: boolean = await new Promise((resolve) => {
getThread(`${channel.id}.json`, (threadInfo) => {
if (threadInfo?.messages)
resolve(true)
else
resolve(false)
})
})
// this is a thread channel, do not duplicate files
if (isThread) return
const fullFileName = `data/${filename}-${user}.json` const fullFileName = `data/${filename}-${user}.json`
if (fs.existsSync(fullFileName)) { if (fs.existsSync(fullFileName)) {
fs.readFile(fullFileName, 'utf8', (error, data) => { fs.readFile(fullFileName, 'utf8', (error, data) => {

View File

@@ -22,6 +22,6 @@ describe('#commands', () => {
// test specific commands in the object // test specific commands in the object
it('references specific commands', () => { it('references specific commands', () => {
const commandsString = commands.map(e => e.name).join(', ') const commandsString = commands.map(e => e.name).join(', ')
expect(commandsString).toBe('thread, private-thread, message-style, message-stream, toggle-chat, shutoff, modify-capacity, channel-toggle, clear-user-channel-history') expect(commandsString).toBe('thread, private-thread, message-style, message-stream, toggle-chat, shutoff, modify-capacity, clear-user-channel-history')
}) })
}) })