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
container_name: discord
restart: always # rebuild container always
image: kevinthedang/discord-ollama:0.5.10
image: kevinthedang/discord-ollama:0.5.11
environment:
CLIENT_TOKEN: ${CLIENT_TOKEN}
MODEL: ${MODEL}

4
package-lock.json generated
View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "discord-ollama",
"version": "0.5.10",
"version": "0.5.11",
"description": "Ollama Integration into discord",
"main": "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 { Capacity } from './capacity.js'
import { PrivateThreadCreate } from './threadPrivateCreate.js'
import { ChannelToggle } from './channelToggle.js'
import { ClearUserChannelHistory } from './cleanUserChannelHistory.js'
export default [
@@ -17,6 +16,5 @@ export default [
Disable,
Shutoff,
Capacity,
ChannelToggle,
ClearUserChannelHistory
] 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 { openThreadInfo } from '../utils/index.js'
import { openChannelInfo } from '../utils/index.js'
export const ThreadCreate: SlashCommand = {
name: 'thread',
@@ -19,11 +19,12 @@ export const ThreadCreate: SlashCommand = {
})
// 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
// store: thread.id, thread.name
openThreadInfo(`${thread.id}.json`, thread)
openChannelInfo(thread.id,
thread as ThreadChannel,
interaction.user.tag)
// user only 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 { openThreadInfo } from '../utils/index.js'
import { openChannelInfo } from '../utils/index.js'
export const PrivateThreadCreate: SlashCommand = {
name: 'private-thread',
@@ -23,7 +23,10 @@ export const PrivateThreadCreate: SlashCommand = {
// handle storing this chat channel
// store: thread.id, thread.name
openThreadInfo(`${thread.id}.json`, thread)
openChannelInfo(thread.id,
thread as ThreadChannel,
interaction.user.tag
)
// user only reply
return interaction.reply({

View File

@@ -1,7 +1,7 @@
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 { TextChannel, ThreadChannel } from 'discord.js'
import { TextChannel } from 'discord.js'
/**
* 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
*/
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}.`)
// Do not respond if bot talks in the chat
@@ -23,15 +23,12 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
try {
// Retrieve Server/Guild Preferences
const serverConfig: ServerConfig = await new Promise((resolve, reject) => {
await new Promise((resolve, reject) => {
getServerConfig(`${message.guildId}-config.json`, (config) => {
// check if config.json exists
if (config === undefined) {
// Allowing chat options to be available
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.'))
return
}
@@ -42,14 +39,6 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
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)
})
})
@@ -80,27 +69,35 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
})
})
// 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
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) => {
if (channelInfo?.messages)
resolve(channelInfo.messages)
else
log(`Channel ${message.channel}-${message.author.username} does not exist.`)
else {
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
let response: string
@@ -136,18 +133,11 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama
})
// only update the json on success
if (serverConfig.options['channel-toggle']) {
openChannelInfo(message.channelId,
message.channel as TextChannel,
message.author.tag,
msgHist.getItems()
)
} else {
openThreadInfo(`${message.channelId}.json`,
client.channels.fetch(message.channelId) as unknown as ThreadChannel,
msgHist.getItems()
)
}
openChannelInfo(message.channelId,
message.channel as TextChannel,
message.author.tag,
msgHist.getItems()
)
} catch (error: any) {
msgHist.pop() // remove message because of failure
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
*/
export default event(Events.ThreadDelete, ({ log }, thread: ThreadChannel) => {
const filePath = `data/${thread.id}.json`
if (fs.existsSync(filePath)) {
fs.unlink(filePath, (error) => {
if (error)
log(`Error deleting file ${filePath}`, error)
else
log(`Successfully deleted ${filePath} thread info`)
export default event(Events.ThreadDelete, async ({ log }, thread: ThreadChannel) => {
// iterate through every guild member in the thread and delete their history, except the bot
try {
log(`Number of User Guild Members in Thread being deleted: ${thread.memberCount!! - 1}`)
const dirPath = 'data/'
// read all files in data/
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 {
log(`File ${filePath} does not exist.`)
}
} catch (error) {
log(`Issue deleting user history files from ${thread.id}`)
}
})

View File

@@ -8,7 +8,6 @@ export interface UserConfiguration {
export interface ServerConfiguration {
'toggle-chat'?: boolean,
'channel-toggle'?: boolean
}
/**
@@ -35,12 +34,6 @@ export interface ServerConfig {
options: ServerConfiguration
}
export interface Thread {
readonly id: string
readonly name: string
messages: UserMessage[]
}
export interface Channel {
readonly id: string
readonly name: string
@@ -54,5 +47,5 @@ export interface Channel {
* @returns true if command is from Server Config, false otherwise
*/
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 { Configuration, Thread, Channel, UserMessage } from '../index.js'
import { Configuration, Channel, UserMessage } from '../index.js'
import fs from 'fs'
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
*
@@ -65,16 +10,15 @@ export async function getThread(filename: string, callback: (config: Thread | un
* @returns true if channel does not exist, false otherwise
*/
async function checkChannelInfoExists(channel: TextChannel, user: string) {
// thread exist handler
const isThread: boolean = await new Promise((resolve) => {
getThread(`${channel.id}-${user}.json`, (channelInfo) => {
const doesExists: boolean = await new Promise((resolve) => {
getChannelInfo(`${channel.id}-${user}.json`, (channelInfo) => {
if (channelInfo?.messages)
resolve(true)
else
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 messages their messages
*/
export async function openChannelInfo(filename: string, channel: TextChannel, 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
export async function openChannelInfo(filename: string, channel: TextChannel | ThreadChannel, user: string, messages: UserMessage[] = []): Promise<void> {
const fullFileName = `data/${filename}-${user}.json`
if (fs.existsSync(fullFileName)) {
fs.readFile(fullFileName, 'utf8', (error, data) => {

View File

@@ -22,6 +22,6 @@ describe('#commands', () => {
// test specific commands in the object
it('references specific commands', () => {
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')
})
})