Compare commits
9 Commits
v0.8.2
...
feature/ch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2c954136d | ||
|
|
5820583609 | ||
|
|
a5faca87aa | ||
|
|
4c96b3863a | ||
|
|
40783818b9 | ||
|
|
ed0d8600df | ||
|
|
03939ef562 | ||
|
|
456f70b9e1 | ||
|
|
5b542aca1a |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -33,6 +33,7 @@ jobs:
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo REDIS_IP = ${{ secrets.REDIS_IP }} >> .env
|
||||
echo REDIS_PORT = ${{ secrets.REDIS_PORT }} >> .env
|
||||
|
||||
@@ -61,6 +62,7 @@ jobs:
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo REDIS_IP = ${{ secrets.REDIS_IP }} >> .env
|
||||
echo REDIS_PORT = ${{ secrets.REDIS_PORT }} >> .env
|
||||
|
||||
|
||||
3
.github/workflows/coverage.yml
vendored
3
.github/workflows/coverage.yml
vendored
@@ -30,12 +30,13 @@ jobs:
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo REDIS_IP = ${{ secrets.REDIS_IP }} >> .env
|
||||
echo REDIS_PORT = ${{ secrets.REDIS_PORT }} >> .env
|
||||
|
||||
- name: Collect Code Coverage
|
||||
run: |
|
||||
LINE_PCT=$(npm run test:coverage | tail -2 | head -1 | awk '{print $3}')
|
||||
LINE_PCT=$(npm run coverage | tail -2 | head -1 | awk '{print $3}')
|
||||
echo "COVERAGE=$LINE_PCT" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload Code Coverage
|
||||
|
||||
133
.github/workflows/deploy.yml
vendored
Normal file
133
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
name: Deploy
|
||||
run-name: Deploy Application Latest
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
Deploy-Application:
|
||||
runs-on: self-hosted
|
||||
environment: deploy
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Generate Secret File for Compose case
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.CLIENT }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo DISCORD_IP = ${{ secrets.DISCORD_IP }} >> .env
|
||||
echo SUBNET_ADDRESS = ${{ secrets.SUBNET_ADDRESS }} >> .env
|
||||
echo REDIS_IP = ${{ secrets.REDIS_IP }} >> .env
|
||||
echo REDIS_PORT = ${{ secrets.REDIS_PORT }} >> .env
|
||||
|
||||
- name: Check if directory exists and delete it
|
||||
run: |
|
||||
if [ -d "${{ secrets.PATH }}" ]; then
|
||||
echo "Directory exists, deleting old version..."
|
||||
rm -rf ${{ secrets.PATH }}
|
||||
else
|
||||
echo "Directory does not exist."
|
||||
fi
|
||||
|
||||
- name: Clone Repo onto Server
|
||||
run: |
|
||||
git clone https://github.com/kevinthedang/discord-ollama.git ${{ secrets.PATH }}
|
||||
cd ${{ secrets.PATH }}
|
||||
|
||||
- name: Install nvm and Node.js lts/jod
|
||||
run: |
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
echo "NVM installed successfully."
|
||||
nvm install lts/jod
|
||||
nvm alias default lts/jod
|
||||
node -v
|
||||
npm -v
|
||||
|
||||
- name: Build Application
|
||||
run: |
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
npm install
|
||||
|
||||
IMAGE="kevinthedang/discord-ollama"
|
||||
REDIS="redis"
|
||||
OLLAMA="ollama/ollama"
|
||||
|
||||
if docker images | grep -q $IMAGE; then
|
||||
IMAGE_ID=$(docker images -q $IMAGE)
|
||||
CONTAINER_IDS=$(docker ps -q --filter "ancestor=$IMAGE_ID")
|
||||
|
||||
if [ ! -z "$CONTAINER_IDS" ]; then
|
||||
# Stop and remove the running containers
|
||||
docker stop $CONTAINER_IDS
|
||||
echo "Stopped and removed the containers using the image $IMAGE"
|
||||
fi
|
||||
docker rmi $IMAGE_ID
|
||||
echo "Old $IMAGE Image Removed"
|
||||
fi
|
||||
|
||||
if docker images | grep -q $REDIS; then
|
||||
IMAGE_ID=$(docker images -q $REDIS)
|
||||
CONTAINER_IDS=$(docker ps -q --filter "ancestor=$IMAGE_ID")
|
||||
|
||||
if [ ! -z "$CONTAINER_IDS" ]; then
|
||||
# Stop and remove the running containers
|
||||
docker stop $CONTAINER_IDS
|
||||
echo "Stopped and removed the containers using the image $REDIS"
|
||||
fi
|
||||
docker rmi $IMAGE_ID
|
||||
echo "Old $REDIS Image Removed"
|
||||
fi
|
||||
|
||||
if docker images | grep -q $OLLAMA; then
|
||||
IMAGE_ID=$(docker images -q $OLLAMA)
|
||||
CONTAINER_IDS=$(docker ps -q --filter "ancestor=$IMAGE_ID")
|
||||
|
||||
if [ ! -z "$CONTAINER_IDS" ]; then
|
||||
# Stop and remove the running containers
|
||||
docker stop $CONTAINER_IDS
|
||||
echo "Stopped and removed the containers using the image $OLLAMA"
|
||||
fi
|
||||
docker rmi $IMAGE_ID
|
||||
echo "Old $OLLAMA Image Removed"
|
||||
fi
|
||||
|
||||
docker network prune -f
|
||||
docker system prune -a -f
|
||||
|
||||
npm run docker:build-latest
|
||||
|
||||
- name: Start Application
|
||||
run: |
|
||||
docker network create --subnet=${{ secrets.SUBNET_ADDRESS }}/16 ollama-net || true
|
||||
docker run --rm -d \
|
||||
-v ollama:/root/.ollama \
|
||||
-p ${{ secrets.OLLAMA_PORT }}:${{ secrets.OLLAMA_PORT }} \
|
||||
--name ollama \
|
||||
--network ollama-net \
|
||||
--ip ${{ secrets.OLLAMA_IP }} \
|
||||
ollama/ollama:latest
|
||||
|
||||
docker run --rm -d \
|
||||
-v redis:/root/.redis \
|
||||
-p ${{ secrets.REDIS_PORT }}:${{ secrets.REDIS_PORT }} \
|
||||
--name redis \
|
||||
--network ollama-net \
|
||||
--ip ${{ secrets.REDIS_IP }} \
|
||||
redis:latest
|
||||
|
||||
docker run --rm -d \
|
||||
-v discord:/src/app \
|
||||
--name discord \
|
||||
--network ollama-net \
|
||||
--ip ${{ secrets.DISCORD_IP }} \
|
||||
kevinthedang/discord-ollama
|
||||
50
.github/workflows/release.yml
vendored
50
.github/workflows/release.yml
vendored
@@ -1,50 +0,0 @@
|
||||
name: Deploy
|
||||
run-name: Release Docker Image
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
Release-Docker-Image:
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
timeout-minutes: 3
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment lts/jod
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/jod
|
||||
cache: "npm"
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = NOT_REAL_TOKEN >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
echo REDIS_IP = ${{ secrets.REDIS_IP }} >> .env
|
||||
echo REDIS_PORT = ${{ secrets.REDIS_PORT }} >> .env
|
||||
|
||||
- name: Get Version from package.json
|
||||
run: echo "VERSION=$(jq -r '.version' package.json)" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Image
|
||||
run: |
|
||||
npm run docker:build
|
||||
|
||||
- name: Build Image as Latest
|
||||
run: |
|
||||
npm run docker:build-latest
|
||||
|
||||
- name: Log into Docker
|
||||
run: |
|
||||
docker login --username ${{ vars.DOCKER_USER }} --password ${{ secrets.DOCKER_PASS }}
|
||||
|
||||
- name: Release Docker Image
|
||||
run: |
|
||||
docker push ${{ vars.DOCKER_USER }}/discord-ollama:${{ env.VERSION }}
|
||||
docker push ${{ vars.DOCKER_USER }}/discord-ollama:latest
|
||||
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
@@ -41,9 +41,10 @@ jobs:
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo REDIS_IP = ${{ secrets.REDIS_IP }} >> .env
|
||||
echo REDIS_PORT = ${{ secrets.REDIS_PORT }} >> .env
|
||||
|
||||
- name: Test Application
|
||||
run: |
|
||||
npm run test:run
|
||||
npm run tests
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p><a href="#"></a><a href="https://creativecommons.org/licenses/by/4.0/"><img alt="License" src="https://img.shields.io/badge/License-CC_BY_4.0-darkgreen.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/releases/latest"><img alt="Release" src="https://img.shields.io/github/v/release/kevinthedang/discord-ollama?logo=github" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/build.yml"><img alt="Build Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/build.yml/badge.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/release.yml"><img alt="Release Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/release.yml/badge.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/deploy.yml"><img alt="Deploy Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/deploy.yml/badge.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/test.yml"><img alt="Testing Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/test.yml/badge.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/coverage.yml"><img alt="Code Coverage" src="https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/kevinthedang/bc7b5dcfa16561ab02bb3df67a99b22d/raw/coverage.json"></a>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,12 @@ services:
|
||||
build: ./ # find docker file in designated path
|
||||
container_name: discord
|
||||
restart: always # rebuild container always
|
||||
image: kevinthedang/discord-ollama:0.8.2
|
||||
image: kevinthedang/discord-ollama:0.8.4
|
||||
environment:
|
||||
CLIENT_TOKEN: ${CLIENT_TOKEN}
|
||||
OLLAMA_IP: ${OLLAMA_IP}
|
||||
OLLAMA_PORT: ${OLLAMA_PORT}
|
||||
MODEL: ${MODEL}
|
||||
REDIS_IP: ${REDIS_IP}
|
||||
REDIS_PORT: ${REDIS_PORT}
|
||||
networks:
|
||||
@@ -28,7 +29,6 @@ services:
|
||||
networks:
|
||||
ollama-net:
|
||||
ipv4_address: ${OLLAMA_IP}
|
||||
|
||||
runtime: nvidia # use Nvidia Container Toolkit for GPU support
|
||||
devices:
|
||||
- /dev/nvidia0
|
||||
|
||||
1287
package-lock.json
generated
1287
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "discord-ollama",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.4",
|
||||
"description": "Ollama Integration into discord",
|
||||
"main": "build/index.js",
|
||||
"exports": "./build/index.js",
|
||||
"scripts": {
|
||||
"test:run": "vitest run",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"tests": "vitest run",
|
||||
"coverage": "vitest run --coverage",
|
||||
"watch": "tsx watch src",
|
||||
"build": "tsc",
|
||||
"prod": "node .",
|
||||
@@ -27,22 +27,22 @@
|
||||
"author": "Kevin Dang",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.16.3",
|
||||
"discord.js": "^14.18.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"ollama": "^0.5.11",
|
||||
"ollama": "^0.5.14",
|
||||
"redis": "^4.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.2",
|
||||
"@vitest/coverage-v8": "^2.1.8",
|
||||
"@types/node": "^22.13.14",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vitest": "^2.1.4"
|
||||
"tsx": "^4.19.3",
|
||||
"typescript": "^5.8.2",
|
||||
"vitest": "^3.0.4"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"npm": ">=10.9.0",
|
||||
"node": ">=22.12.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,12 +33,15 @@ const messageHistory: Queue<UserMessage> = new Queue<UserMessage>
|
||||
registerEvents(client, Events, messageHistory, ollama, Keys.defaultModel)
|
||||
|
||||
// Try to connect to redis
|
||||
await redis.connect()
|
||||
.then(() => console.log('[Redis] Connected'))
|
||||
.catch((error) => {
|
||||
console.error('[Redis] Connection Error', error)
|
||||
process.exit(1)
|
||||
})
|
||||
try {
|
||||
await redis.connect()
|
||||
console.log('[Redis] Successfully Connected')
|
||||
} catch(error) {
|
||||
console.error('[Redis] Connection Error. See error below:\n', error)
|
||||
console.warn('[Redis] Failed to connect to Redis Database, using local system')
|
||||
// TODO: create boolean flag that will probably be used in messageCreate.ts if redis database is down
|
||||
// When implementing this boolean flag, move connection to database BEFORE the registerEvents method
|
||||
}
|
||||
|
||||
// Try to log in the client
|
||||
await client.login(Keys.clientToken)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, CommandInteraction, ApplicationCommandOptionType } from 'discord.js'
|
||||
import { Client, CommandInteraction, ApplicationCommandOptionType, MessageFlags } from 'discord.js'
|
||||
import { openConfig, SlashCommand, UserCommand } from '../utils/index.js'
|
||||
|
||||
export const Capacity: SlashCommand = {
|
||||
@@ -22,11 +22,13 @@ export const Capacity: SlashCommand = {
|
||||
if (!channel || !UserCommand.includes(channel.type)) return
|
||||
|
||||
// set state of bot chat features
|
||||
openConfig(`${interaction.user.username}-config.json`, interaction.commandName, interaction.options.get('context-capacity')?.value)
|
||||
openConfig(`${interaction.user.username}-config.json`, interaction.commandName,
|
||||
interaction.options.get('context-capacity')?.value
|
||||
)
|
||||
|
||||
interaction.reply({
|
||||
content: `Max message history is now set to \`${interaction.options.get('context-capacity')?.value}\``,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Channel, Client, CommandInteraction, TextChannel } from 'discord.js'
|
||||
import { Channel, Client, CommandInteraction, MessageFlags, TextChannel } from 'discord.js'
|
||||
import { clearChannelInfo, SlashCommand, UserCommand } from '../utils/index.js'
|
||||
|
||||
export const ClearUserChannelHistory: SlashCommand = {
|
||||
@@ -14,20 +14,22 @@ export const ClearUserChannelHistory: SlashCommand = {
|
||||
if (!channel || !UserCommand.includes(channel.type)) return
|
||||
|
||||
// clear channel info for user
|
||||
const successfulWipe = await clearChannelInfo(interaction.channelId,
|
||||
interaction.channel as TextChannel,
|
||||
interaction.user.username)
|
||||
const successfulWipe = await clearChannelInfo(
|
||||
interaction.channelId,
|
||||
interaction.channel as TextChannel,
|
||||
interaction.user.username
|
||||
)
|
||||
|
||||
// check result of clearing history
|
||||
if (successfulWipe)
|
||||
interaction.reply({
|
||||
content: `History cleared in **this channel** cleared for **${interaction.user.username}**.`,
|
||||
ephemeral: true
|
||||
interaction.reply({
|
||||
content: `History cleared in **this channel** cleared for **${interaction.user.username}**.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
else
|
||||
interaction.reply({
|
||||
content: `History was not be found for **${interaction.user.username}** in **this channel**.\n\nPlease chat with **${client.user?.username}** to start a chat history.`,
|
||||
ephemeral: true
|
||||
interaction.reply({
|
||||
content: `History was not be found for **${interaction.user.username}** in **this channel**.\n\nPlease chat with **${client.user?.username}** to start a chat history.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApplicationCommandOptionType, Client, CommandInteraction } from 'discord.js'
|
||||
import { ApplicationCommandOptionType, Client, CommandInteraction, MessageFlags } from 'discord.js'
|
||||
import { UserCommand, SlashCommand } from '../utils/index.js'
|
||||
import { ollama } from '../client.js'
|
||||
import { ModelResponse } from 'ollama'
|
||||
@@ -6,7 +6,7 @@ import { ModelResponse } from 'ollama'
|
||||
export const DeleteModel: SlashCommand = {
|
||||
name: 'delete-model',
|
||||
description: 'deletes a model from the local list of models. Administrator Only.',
|
||||
|
||||
|
||||
// set available user options to pass to the command
|
||||
options: [
|
||||
{
|
||||
@@ -31,7 +31,7 @@ export const DeleteModel: SlashCommand = {
|
||||
if (!interaction.memberPermissions?.has('Administrator')) {
|
||||
interaction.reply({
|
||||
content: `${interaction.commandName} is an admin command.\n\nPlease contact a server admin to pull the model you want.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export const DeleteModel: SlashCommand = {
|
||||
// could not delete the model
|
||||
interaction.reply({
|
||||
content: `Could not delete the **${modelInput}** model. It probably doesn't exist or you spelled it incorrectly.\n\nPlease try again if this is a mistake.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, CommandInteraction, ApplicationCommandOptionType } from 'discord.js'
|
||||
import { Client, CommandInteraction, ApplicationCommandOptionType, MessageFlags } from 'discord.js'
|
||||
import { AdminCommand, openConfig, SlashCommand } from '../utils/index.js'
|
||||
|
||||
export const Disable: SlashCommand = {
|
||||
@@ -25,17 +25,19 @@ export const Disable: SlashCommand = {
|
||||
if (!interaction.memberPermissions?.has('Administrator')) {
|
||||
interaction.reply({
|
||||
content: `${interaction.commandName} is an admin command.\n\nPlease contact an admin to use this command for you.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// set state of bot chat features
|
||||
openConfig(`${interaction.guildId}-config.json`, interaction.commandName, interaction.options.get('enabled')?.value)
|
||||
openConfig(`${interaction.guildId}-config.json`, interaction.commandName,
|
||||
interaction.options.get('enabled')?.value
|
||||
)
|
||||
|
||||
interaction.reply({
|
||||
content: `${client.user?.username} is now **${interaction.options.get('enabled')?.value ? "enabled" : "disabled" }**.`,
|
||||
ephemeral: true
|
||||
content: `${client.user?.username} is now **${interaction.options.get('enabled')?.value ? "enabled" : "disabled"}**.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApplicationCommandOptionType, Client, CommandInteraction } from 'discord.js'
|
||||
import { ApplicationCommandOptionType, Client, CommandInteraction, MessageFlags } from 'discord.js'
|
||||
import { openConfig, SlashCommand, UserCommand } from '../utils/index.js'
|
||||
|
||||
export const MessageStream: SlashCommand = {
|
||||
@@ -22,11 +22,13 @@ export const MessageStream: SlashCommand = {
|
||||
if (!channel || !UserCommand.includes(channel.type)) return
|
||||
|
||||
// save value to json and write to it
|
||||
openConfig(`${interaction.user.username}-config.json`, interaction.commandName, interaction.options.get('stream')?.value)
|
||||
openConfig(`${interaction.user.username}-config.json`, interaction.commandName,
|
||||
interaction.options.get('stream')?.value
|
||||
)
|
||||
|
||||
interaction.reply({
|
||||
content: `Message streaming is now set to: \`${interaction.options.get('stream')?.value}\``,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApplicationCommandOptionType, Client, CommandInteraction } from "discord.js"
|
||||
import { ApplicationCommandOptionType, Client, CommandInteraction, MessageFlags } from "discord.js"
|
||||
import { ollama } from "../client.js"
|
||||
import { ModelResponse } from "ollama"
|
||||
import { UserCommand, SlashCommand } from "../utils/index.js"
|
||||
@@ -31,7 +31,7 @@ export const PullModel: SlashCommand = {
|
||||
if (!interaction.memberPermissions?.has('Administrator')) {
|
||||
interaction.reply({
|
||||
content: `${interaction.commandName} is an admin command.\n\nPlease contact a server admin to pull the model you want.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, CommandInteraction } from 'discord.js'
|
||||
import { Client, CommandInteraction, MessageFlags } from 'discord.js'
|
||||
import { AdminCommand, SlashCommand } from '../utils/index.js'
|
||||
|
||||
export const Shutoff: SlashCommand = {
|
||||
@@ -18,7 +18,7 @@ export const Shutoff: SlashCommand = {
|
||||
if (!interaction.memberPermissions?.has('Administrator')) {
|
||||
interaction.reply({
|
||||
content: `**Shutdown Aborted:**\n\n${interaction.user.tag}, You do not have permission to shutoff **${client.user?.tag}**.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
return // stop from shutting down
|
||||
}
|
||||
@@ -26,8 +26,9 @@ export const Shutoff: SlashCommand = {
|
||||
// Shutoff cleared, do it
|
||||
interaction.reply({
|
||||
content: `${client.user?.tag} is shutting down.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
|
||||
console.log(`[Command: shutoff] ${client.user?.tag} is shutting down.`)
|
||||
|
||||
// clean up client instance and stop
|
||||
|
||||
@@ -31,20 +31,20 @@ export const SwitchModel: SlashCommand = {
|
||||
// Phase 1: Switch to the model
|
||||
let switchSuccess = false
|
||||
await ollama.list()
|
||||
.then(response => {
|
||||
for (const model in response.models) {
|
||||
const currentModel: ModelResponse = response.models[model]
|
||||
if (currentModel.name.startsWith(modelInput)) {
|
||||
openConfig(`${interaction.user.username}-config.json`, interaction.commandName, modelInput)
|
||||
.then(response => {
|
||||
for (const model in response.models) {
|
||||
const currentModel: ModelResponse = response.models[model]
|
||||
if (currentModel.name.startsWith(modelInput)) {
|
||||
openConfig(`${interaction.user.username}-config.json`, interaction.commandName, modelInput)
|
||||
|
||||
// successful switch
|
||||
interaction.editReply({
|
||||
content: `Successfully switched to **${modelInput}** as the preferred model for ${interaction.user.username}.`
|
||||
})
|
||||
switchSuccess = true
|
||||
// successful switch
|
||||
interaction.editReply({
|
||||
content: `Successfully switched to **${modelInput}** as the preferred model for ${interaction.user.username}.`
|
||||
})
|
||||
switchSuccess = true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
// todo: problem can be here if async messes up
|
||||
if (switchSuccess) {
|
||||
// set model now that it exists
|
||||
@@ -55,7 +55,7 @@ export const SwitchModel: SlashCommand = {
|
||||
// Phase 2: Notify user of failure to find model.
|
||||
interaction.editReply({
|
||||
content: `Could not find **${modelInput}** in local model library.\n\nPlease contact an server admin for access to this model.`
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
// could not resolve user model switch
|
||||
interaction.editReply({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChannelType, Client, CommandInteraction, TextChannel, ThreadChannel } from 'discord.js'
|
||||
import { ChannelType, Client, CommandInteraction, MessageFlags, TextChannel, ThreadChannel } from 'discord.js'
|
||||
import { AdminCommand, openChannelInfo, SlashCommand } from '../utils/index.js'
|
||||
|
||||
export const ThreadCreate: SlashCommand = {
|
||||
@@ -21,14 +21,12 @@ export const ThreadCreate: SlashCommand = {
|
||||
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 message.`)
|
||||
|
||||
// handle storing this chat channel
|
||||
openChannelInfo(thread.id,
|
||||
thread as ThreadChannel,
|
||||
interaction.user.tag)
|
||||
openChannelInfo(thread.id, thread as ThreadChannel, interaction.user.tag)
|
||||
|
||||
// user only reply
|
||||
return interaction.reply({
|
||||
content: `I can help you in <#${thread.id}> below.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChannelType, Client, CommandInteraction, TextChannel, ThreadChannel } from 'discord.js'
|
||||
import { ChannelType, Client, CommandInteraction, MessageFlags, TextChannel, ThreadChannel } from 'discord.js'
|
||||
import { AdminCommand, openChannelInfo, SlashCommand } from '../utils/index.js'
|
||||
|
||||
export const PrivateThreadCreate: SlashCommand = {
|
||||
@@ -22,15 +22,12 @@ export const PrivateThreadCreate: SlashCommand = {
|
||||
|
||||
// handle storing this chat channel
|
||||
// store: thread.id, thread.name
|
||||
openChannelInfo(thread.id,
|
||||
thread as ThreadChannel,
|
||||
interaction.user.tag
|
||||
)
|
||||
openChannelInfo(thread.id, thread as ThreadChannel, interaction.user.tag)
|
||||
|
||||
// user only reply
|
||||
return interaction.reply({
|
||||
content: `I can help you in <#${thread.id}>.`,
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import commands from '../commands/index.js'
|
||||
*/
|
||||
export default event(Events.InteractionCreate, async ({ log, client }, interaction) => {
|
||||
if (!interaction.isCommand() || !interaction.isChatInputCommand()) return
|
||||
|
||||
|
||||
log(`Interaction called \'${interaction.commandName}\' from ${interaction.user.tag}.`)
|
||||
|
||||
// ensure command exists, otherwise kill event
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { TextChannel } from 'discord.js'
|
||||
import { event, Events, normalMessage, UserMessage, clean, getTextFileAttachmentData } from '../utils/index.js'
|
||||
import { getChannelInfo, getServerConfig, getUserConfig, openChannelInfo, openConfig, UserConfig, getAttachmentData } from '../utils/index.js'
|
||||
import { event, Events, normalMessage, UserMessage, clean } from '../utils/index.js'
|
||||
import {
|
||||
getChannelInfo, getServerConfig, getUserConfig, openChannelInfo,
|
||||
openConfig, UserConfig, getAttachmentData, getTextFileAttachmentData
|
||||
} from '../utils/index.js'
|
||||
|
||||
/**
|
||||
* Max Message length for free users is 2000 characters (bot or not).
|
||||
@@ -25,7 +28,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
// Params for Preferences Fetching
|
||||
const maxRetries = 3
|
||||
const delay = 1000 // in millisecons
|
||||
|
||||
|
||||
try {
|
||||
// Retrieve Server/Guild Preferences
|
||||
let attempt = 0
|
||||
@@ -39,7 +42,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
openConfig(`${message.guildId}-config.json`, 'toggle-chat', true)
|
||||
reject(new Error('Failed to locate or create Server Preferences\n\nPlease try chatting again...'))
|
||||
}
|
||||
|
||||
|
||||
// check if chat is disabled
|
||||
else if (!config.options['toggle-chat'])
|
||||
reject(new Error('Admin(s) have disabled chat features.\n\n Please contact your server\'s admin(s).'))
|
||||
@@ -48,12 +51,12 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
})
|
||||
})
|
||||
break // successful
|
||||
} catch (error) {
|
||||
} catch (error) {
|
||||
++attempt
|
||||
if (attempt < maxRetries) {
|
||||
log(`Attempt ${attempt} failed for Server Preferences. Retrying in ${delay}ms...`)
|
||||
await new Promise(ret => setTimeout(ret, delay))
|
||||
} else
|
||||
} else
|
||||
throw new Error(`Could not retrieve Server Preferences, please try chatting again...`)
|
||||
}
|
||||
}
|
||||
@@ -73,7 +76,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
reject(new Error('No User Preferences is set up.\n\nCreating preferences file with \`message-style\` set as \`false\` for regular message style.\nPlease try chatting again.'))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// check if there is a set capacity in config
|
||||
else if (typeof config.options['modify-capacity'] !== 'number')
|
||||
log(`Capacity is undefined, using default capacity of ${msgHist.capacity}.`)
|
||||
@@ -89,18 +92,18 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
|
||||
if (typeof config.options['switch-model'] !== 'string')
|
||||
reject(new Error(`No Model was set. Please set a model by running \`/switch-model <model of choice>\`.\n\nIf you do not have any models. Run \`/pull-model <model name>\`.`))
|
||||
|
||||
|
||||
resolve(config)
|
||||
})
|
||||
})
|
||||
break // successful
|
||||
} catch (error) {
|
||||
++attempt
|
||||
if (attempt < maxRetries) {
|
||||
log(`Attempt ${attempt} failed for User Preferences. Retrying in ${delay}ms...`)
|
||||
await new Promise(ret => setTimeout(ret, delay))
|
||||
} else
|
||||
throw new Error(`Could not retrieve User Preferences, please try chatting again...`)
|
||||
} catch (error) {
|
||||
++attempt
|
||||
if (attempt < maxRetries) {
|
||||
log(`Attempt ${attempt} failed for User Preferences. Retrying in ${delay}ms...`)
|
||||
await new Promise(ret => setTimeout(ret, delay))
|
||||
} else
|
||||
throw new Error(`Could not retrieve User Preferences, please try chatting again...`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +122,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
|
||||
if (chatMessages.length === 0) {
|
||||
chatMessages = await new Promise((resolve, reject) => {
|
||||
openChannelInfo(message.channelId,
|
||||
openChannelInfo(message.channelId,
|
||||
message.channel as TextChannel,
|
||||
message.author.tag
|
||||
)
|
||||
@@ -145,7 +148,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
cleanedMessage += await getTextFileAttachmentData(attachment)
|
||||
else if (attachment)
|
||||
messageAttachment = await getAttachmentData(attachment)
|
||||
|
||||
|
||||
const model: string = userConfig.options['switch-model']
|
||||
|
||||
// set up new queue
|
||||
@@ -160,7 +163,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
content: cleanedMessage,
|
||||
images: messageAttachment || []
|
||||
})
|
||||
|
||||
|
||||
// response string for ollama to put its response
|
||||
const response: string = await normalMessage(message, ollama, model, msgHist, shouldStream)
|
||||
|
||||
@@ -171,16 +174,16 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
||||
while (msgHist.size() >= msgHist.capacity) msgHist.dequeue()
|
||||
|
||||
// successful query, save it in context history
|
||||
msgHist.enqueue({
|
||||
role: 'assistant',
|
||||
msgHist.enqueue({
|
||||
role: 'assistant',
|
||||
content: response,
|
||||
images: messageAttachment || []
|
||||
})
|
||||
|
||||
// only update the json on success
|
||||
openChannelInfo(message.channelId,
|
||||
openChannelInfo(message.channelId,
|
||||
message.channel as TextChannel,
|
||||
message.author.tag,
|
||||
message.author.tag,
|
||||
msgHist.getItems()
|
||||
)
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -20,8 +20,8 @@ export default event(Events.ThreadDelete, async ({ log }, thread: ThreadChannel)
|
||||
|
||||
// filter files by thread id being deleted
|
||||
const filesToDiscard = files.filter(
|
||||
file => file.startsWith(`${thread.id}-`) &&
|
||||
file.endsWith('.json'))
|
||||
file => file.startsWith(`${thread.id}-`) &&
|
||||
file.endsWith('.json'))
|
||||
|
||||
// remove files by unlinking
|
||||
filesToDiscard.forEach(file => {
|
||||
@@ -36,5 +36,5 @@ export default event(Events.ThreadDelete, async ({ log }, thread: ThreadChannel)
|
||||
})
|
||||
} catch (error) {
|
||||
log(`Issue deleting user history files from ${thread.id}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -17,7 +17,7 @@ export class Queue<T> implements IQueue<T> {
|
||||
* Set up Queue
|
||||
* @param capacity max length of queue
|
||||
*/
|
||||
constructor(public capacity: number = 5) {}
|
||||
constructor(public capacity: number = 5) { }
|
||||
|
||||
/**
|
||||
* Put item in front of queue
|
||||
|
||||
@@ -21,14 +21,13 @@ export function getEnvVar(name: string, fallback?: string): string {
|
||||
throw new Error(`Environment variable ${name} is not set.`)
|
||||
|
||||
// validate User-Generated Discord Application Tokens
|
||||
if (name === "CLIENT_TOKEN")
|
||||
if (value.length < 72) throw new Error(`The "CLIENT_TOKEN" provided is not of at least length 72.
|
||||
if (name === "CLIENT_TOKEN" && value.length > 72)
|
||||
throw new Error(`The "CLIENT_TOKEN" provided is not of at least length 72.
|
||||
This is probably an invalid token unless Discord updated their token policy. Please provide a valid token.`)
|
||||
|
||||
// validate IPv4 address found in environment variables
|
||||
if (name.endsWith("_IP") || name.endsWith("_ADDRESS"))
|
||||
if (!ipValidate.test(value))
|
||||
throw new Error(`Environment variable ${name} does not follow IPv4 formatting.`)
|
||||
if ((name.endsWith("_IP") || name.endsWith("_ADDRESS")) && !ipValidate.test(value))
|
||||
throw new Error(`Environment variable ${name} does not follow IPv4 formatting.`)
|
||||
|
||||
// return env variable
|
||||
return value
|
||||
|
||||
@@ -15,8 +15,8 @@ export type EventKeys = keyof ClientEvents // only wants keys of ClientEvents ob
|
||||
* @param msgHist message history
|
||||
*/
|
||||
export type ChatParams = {
|
||||
model: string,
|
||||
ollama: Ollama,
|
||||
model: string,
|
||||
ollama: Ollama,
|
||||
msgHist: UserMessage[]
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export type ChatParams = {
|
||||
* Format for the messages to be stored when communicating when the bot
|
||||
* @param role either assistant, user, or system
|
||||
* @param content string of the message the user or assistant provided
|
||||
* @param images array of images that the user or assistant provided
|
||||
*/
|
||||
export type UserMessage = {
|
||||
role: string,
|
||||
@@ -33,12 +34,18 @@ export type UserMessage = {
|
||||
|
||||
// Event properties
|
||||
export interface EventProps {
|
||||
client: Client
|
||||
log: LogMethod
|
||||
msgHist: Queue<UserMessage>
|
||||
client: Client,
|
||||
log: LogMethod,
|
||||
msgHist: Queue<UserMessage>,
|
||||
ollama: Ollama,
|
||||
defaultModel: String
|
||||
}
|
||||
|
||||
/**
|
||||
* Format for the callback function tied to an event
|
||||
* @param props the properties of the event
|
||||
* @param args the arguments of the event
|
||||
*/
|
||||
export type EventCallback<T extends EventKeys> = (
|
||||
props: EventProps,
|
||||
...args: ClientEvents[T]
|
||||
@@ -50,6 +57,12 @@ export interface Event<T extends EventKeys = EventKeys> {
|
||||
callback: EventCallback<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create an event object
|
||||
* @param key type of event
|
||||
* @param callback function to run when event is triggered
|
||||
* @returns event object
|
||||
*/
|
||||
export function event<T extends EventKeys>(key: T, callback: EventCallback<T>): Event<T> {
|
||||
return { key, callback }
|
||||
}
|
||||
@@ -62,8 +75,8 @@ export function event<T extends EventKeys>(key: T, callback: EventCallback<T>):
|
||||
* @param ollama the initialized ollama instance
|
||||
*/
|
||||
export function registerEvents(
|
||||
client: Client,
|
||||
events: Event[],
|
||||
client: Client,
|
||||
events: Event[],
|
||||
msgHist: Queue<UserMessage>,
|
||||
ollama: Ollama,
|
||||
defaultModel: String
|
||||
|
||||
@@ -80,7 +80,14 @@ export async function openChannelInfo(filename: string, channel: TextChannel | T
|
||||
}
|
||||
})
|
||||
} else { // file doesn't exist, create it
|
||||
const object: Configuration = JSON.parse(`{ \"id\": \"${channel?.id}\", \"name\": \"${channel?.name}\", \"user\": \"${user}\", \"messages\": []}`)
|
||||
const object: Configuration = JSON.parse(
|
||||
`{
|
||||
\"id\": \"${channel?.id}\",
|
||||
\"name\": \"${channel?.name}\",
|
||||
\"user\": \"${user}\",
|
||||
\"messages\": []
|
||||
}`
|
||||
)
|
||||
|
||||
const directory = path.dirname(fullFileName)
|
||||
if (!fs.existsSync(directory))
|
||||
@@ -103,7 +110,7 @@ export async function getChannelInfo(filename: string, callback: (config: Channe
|
||||
if (fs.existsSync(fullFileName)) {
|
||||
fs.readFile(fullFileName, 'utf8', (error, data) => {
|
||||
if (error) {
|
||||
callback(undefined)
|
||||
callback(undefined)
|
||||
return // something went wrong... stop
|
||||
}
|
||||
callback(JSON.parse(data))
|
||||
|
||||
@@ -12,7 +12,7 @@ import path from 'path'
|
||||
// add type of change (server, user)
|
||||
export function openConfig(filename: string, key: string, value: any) {
|
||||
const fullFileName = `data/${filename}`
|
||||
|
||||
|
||||
// check if the file exists, if not then make the config file
|
||||
if (fs.existsSync(fullFileName)) {
|
||||
fs.readFile(fullFileName, 'utf8', (error, data) => {
|
||||
@@ -58,7 +58,7 @@ export async function getServerConfig(filename: string, callback: (config: Serve
|
||||
if (fs.existsSync(fullFileName)) {
|
||||
fs.readFile(fullFileName, 'utf8', (error, data) => {
|
||||
if (error) {
|
||||
callback(undefined)
|
||||
callback(undefined)
|
||||
return // something went wrong... stop
|
||||
}
|
||||
callback(JSON.parse(data))
|
||||
@@ -81,7 +81,7 @@ export async function getUserConfig(filename: string, callback: (config: UserCon
|
||||
if (fs.existsSync(fullFileName)) {
|
||||
fs.readFile(fullFileName, 'utf8', (error, data) => {
|
||||
if (error) {
|
||||
callback(undefined)
|
||||
callback(undefined)
|
||||
return // something went wrong... stop
|
||||
}
|
||||
callback(JSON.parse(data))
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AbortableAsyncIterator } from "ollama/src/utils.js"
|
||||
/**
|
||||
* Method to query the Ollama client for async generation
|
||||
* @param params
|
||||
* @returns Asyn
|
||||
* @returns AsyncIterator<ChatResponse> generated by the Ollama client
|
||||
*/
|
||||
export async function streamResponse(params: ChatParams): Promise<AbortableAsyncIterator<ChatResponse>> {
|
||||
return await params.ollama.chat({
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function normalMessage(
|
||||
model: model,
|
||||
ollama: ollama,
|
||||
msgHist: msgHist.getItems()
|
||||
}
|
||||
}
|
||||
|
||||
// run query based on stream preference, true = stream, false = block
|
||||
if (stream) {
|
||||
@@ -40,14 +40,15 @@ export async function normalMessage(
|
||||
result = portion.message.content
|
||||
|
||||
// new message block, wait for it to send and assign new block to respond.
|
||||
await channel.send("Creating new stream block...").then(sentMessage => { messageBlock = sentMessage })
|
||||
await channel.send("Creating new stream block...")
|
||||
.then(sentMessage => { messageBlock = sentMessage })
|
||||
} else {
|
||||
result += portion.message.content
|
||||
|
||||
|
||||
// ensure block is not empty
|
||||
if (result.length > 5)
|
||||
messageBlock.edit(result)
|
||||
}
|
||||
messageBlock.edit(result)
|
||||
}
|
||||
console.log(result)
|
||||
}
|
||||
}
|
||||
@@ -70,8 +71,8 @@ export async function normalMessage(
|
||||
channel.send(result)
|
||||
} else // edit the 'generic' response to new message since <2000
|
||||
sentMessage.edit(result)
|
||||
}
|
||||
} catch(error: any) {
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(`[Util: messageNormal] Error creating message: ${error.message}`)
|
||||
if (error.message.includes('try pulling it first'))
|
||||
sentMessage.edit(`**Response generation failed.**\n\nReason: You do not have the ${model} downloaded. Ask an admin to pull it using the \`pull-model\` command.`)
|
||||
|
||||
@@ -34,7 +34,8 @@ describe('Commands Existence', () => {
|
||||
// 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-stream, toggle-chat, shutoff, modify-capacity, clear-user-channel-history, pull-model, switch-model, delete-model')
|
||||
const expectedCommands = ['thread', 'private-thread', 'message-stream', 'toggle-chat', 'shutoff', 'modify-capacity', 'clear-user-channel-history', 'pull-model', 'switch-model', 'delete-model']
|
||||
expect(commandsString).toBe(expectedCommands.join(', '))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -49,23 +50,23 @@ describe('User Command Tests', () => {
|
||||
})
|
||||
|
||||
it('run clear-user-channel-history command', () => {
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('run message-stream command', () => {
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('run message-style command', () => {
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
it('run thread command', () => {
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('run private-thread command', () => {
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
@@ -75,10 +76,10 @@ describe('User Command Tests', () => {
|
||||
*/
|
||||
describe('Admin Command Tests', () => {
|
||||
it('run shutoff command', () => {
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('run toggle-chat command', () => {
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
@@ -8,7 +8,7 @@ import { Queue } from '../src/queues/queue.js'
|
||||
* @param fn function holding tests to run
|
||||
*/
|
||||
describe('Queue Structure', () => {
|
||||
let queue= new Queue<string>()
|
||||
let queue = new Queue<string>()
|
||||
|
||||
// test for queue creation
|
||||
it('creates a new queue', () => {
|
||||
|
||||
Reference in New Issue
Block a user