Compare commits

...

4 Commits

Author SHA1 Message Date
Kevin Dang
43fb2ea94e User Preferences and Setup Docs (#20)
* added message style command

* docker setup scripts

* reformat messageStyle.ts

* fix: register unregister on deploy

* add: messageStream preference

* add: json config handler

* update: messageCreate gets config

* update: shifted chat to config callback

* fix: naming conventions based on discord

* update: setup in docs now

* add: static docker ips

* version increment

* add: bot message for no config

* fix: no config case

* add: clarification for subnetting

* update: version increment in lock file

---------

Co-authored-by: JT2M0L3Y <jtsmoley@icloud.com>
2024-03-22 10:37:06 -07:00
Kevin Dang
5e74736c57 Small Documentation and Refactoring (#18)
* cleanup and documentation

* added dev message for parser

* grammar and other type replacements
2024-02-18 17:39:00 -08:00
Kevin Dang
1c62958c9f docker setup instructions 2024-02-07 11:41:15 -08:00
Kevin Dang
ca6b8c3f9c Docker Container Setup (#15)
* minor package update and env

* added docker scripts

* added working docker compose

* fixed docker container bridge
2024-02-07 09:59:06 -08:00
24 changed files with 508 additions and 450 deletions

View File

@@ -1,14 +1,24 @@
# Discord token for the bot
CLIENT_TOKEN = INSERT_BOT_TOKEN
CLIENT_TOKEN = BOT_TOKEN
# id token of a discord server
GUILD_ID = INSERT_GUILD_ID
GUILD_ID = GUILD_ID
# Channel where the bot listens to messages
CHANNEL_ID = INSERT_CHANNEL_ID
CHANNEL_ID = CHANNEL_ID
# model for the bot to query from (i.e. llama2 [llama2:13b], mistral, ... )
MODEL = INSERT_MODEL_NAME
# model for the bot to query from (i.e. llama2 [llama2:13b], mistral, codellama, etc... )
MODEL = MODEL_NAME
# discord bot user id for mentions
BOT_UID = INSERT_BOT_USER_ID
CLIENT_UID = BOT_USER_ID
# ip/port address of docker container, I use 172.18.X.X for docker, 127.0.0.1 for local
OLLAMA_IP = IP_ADDRESS
OLLAMA_PORT = PORT
# ip address for discord bot container, I use 172.18.X.X, use different IP than ollama_ip
DISCORD_IP = IP_ADDRESS
# subnet address, ex. 172.18.0.0 as we use /16.
SUBNET_ADDRESS = ADDRESS

6
.gitignore vendored
View File

@@ -1,9 +1,15 @@
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
# config
config.json
# builds
build/
dist/
app/
tmp/
data/
# dotenv environment variable files
.env

19
Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
# use node LTS image for version 18
FROM node:18.18.2
# set working directory inside container
WORKDIR /app
# copy package.json and the lock file into the container, and src files
COPY ./src ./src
COPY ./*.json ./
COPY ./.env ./
# install dependencies, breaks
RUN npm install
# build the typescript code
RUN npm run build
# start the application
CMD ["npm", "run", "prod"]

View File

@@ -1,34 +1,28 @@
# Discord Ollama Integration [![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC_BY--NC_4.0-darkgreen.svg)](https://creativecommons.org/licenses/by-nc/4.0/) [![Release Badge](https://img.shields.io/github/v/release/kevinthedang/discord-ollama?logo=github)](https://github.com/kevinthedang/discord-ollama/releases/latest)
Ollama is an AI model management tool that allows users to install and use custom large language models locally. The goal is to create a discord bot that will utilize Ollama and chat with it on a Discord!
## Ollama Setup
* Go to Ollama's [Linux download page](https://ollama.ai/download/linux) and run the simple curl command they provide. The command should be `curl https://ollama.ai/install.sh | sh`.
* Now the the following commands in separate terminals to test out how it works!
* In terminal 1 -> `ollama serve` to setup ollama
* In terminal 2 -> `ollama run [model name]`, for example `ollama run llama2`
* The models can vary as you can create your own model. You can also view ollama's [library](https://ollama.ai/library) of models.
* This can also be done in [wsl](https://learn.microsoft.com/en-us/windows/wsl/install) for Windows machines.
* You can now interact with the model you just ran (it might take a second to startup).
* Response time varies with processing power!
## To Run
## Environment Setup
* Clone this repo using `git clone https://github.com/kevinthedang/discord-ollama.git` or just use [GitHub Desktop](https://desktop.github.com/) to clone the repo.
* Run `npm install` to install the npm packages.
* You will need a `.env` file in the root of the project directory with the bot's token.
* You will need a `.env` file in the root of the project directory with the bot's token. There is a `.env.sample` is provided for you as a reference for what environment variables.
* For example, `CLIENT_TOKEN = [Bot Token]`
* Now, you can run the bot by running `npm run start` which will build and run the decompiled typescript and run the setup for ollama.
* **IMPORTANT**: This must be ran in the wsl/Linux instance to work properly! Using Command Prompt/Powershell/Git Bash/etc. will not work on Windows (at least in my experience).
* Refer to the [resources](#resources) on what node version to use.
* Please refer to the docs for bot setup. **NOTE**: These guides assume you already know how to setup a bot account for discord.
* [Local Machine Setup](./docs/setup-local.md)
* [Docker Setup for Servers and Local Machines](./docs/setup-docker.md)
* Local use is not recommended.
## Resources
* [NodeJS](https://nodejs.org/en)
* This project uses `v20.10.0` (npm `10.2.5`). Consider using [nvm](https://github.com/nvm-sh/nvm) for multiple NodeJS versions.
* This project uses `v20.10.0+` (npm `10.2.5`). Consider using [nvm](https://github.com/nvm-sh/nvm) for multiple NodeJS versions.
* To run dev in `ts-node`, using `v18.18.2` is recommended. **CAUTION**: `v18.19.0` or `lts/hydrogen` will not run properly.
* To run dev with `tsx`, you can use `v20.10.0` or earlier.
* This project supports any NodeJS version above `16.x.x` to only allow ESModules.
* [Ollama](https://ollama.ai/)
* [Ollama Docker Image](https://hub.docker.com/r/ollama/ollama)
* **IMPORTANT**: For Nvidia GPU setup, **install** `nvidia container toolkit/runtime` then **configure** it with Docker to utilize Nvidia driver.
* [Discord Developer Portal](https://discord.com/developers/docs/intro)
* [Discord.js Docs](https://discord.js.org/docs/packages/discord.js/main)
* [Setting up Docker (Ubuntu 20.04)](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04)
* [Setting up Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
## Acknowledgement
* [Kevin Dang](https://github.com/kevinthedang)

54
docker-compose.yml Normal file
View File

@@ -0,0 +1,54 @@
# creates the docker compose
version: '3.7'
# build individual services
services:
# setup discord bot container
discord:
build: ./ # find docker file in designated path
container_name: discord
restart: always # rebuild container always
image: discord/bot:0.2.0
environment:
CLIENT_TOKEN: ${CLIENT_TOKEN}
GUILD_ID: ${GUILD_ID}
CHANNEL_ID: ${CHANNEL_ID}
MODEL: ${MODEL}
CLIENT_UID: ${CLIENT_UID}
OLLAMA_IP: ${OLLAMA_IP}
OLLAMA_PORT: ${OLLAMA_PORT}
networks:
ollama-net:
ipv4_address: ${DISCORD_IP}
volumes:
- discord:/src/app # docker will not make this for you, make it yourself
# setup ollama container
ollama:
image: ollama/ollama:latest # build the image using ollama
container_name: ollama
restart: always
networks:
ollama-net:
ipv4_address: ${OLLAMA_IP}
# runtime: nvidia # use Nvidia Container Toolkit for GPU support
# devices:
# - /dev/nvidia0
volumes:
- ollama:/root/.ollama
ports:
- ${OLLAMA_PORT}:${OLLAMA_PORT}
# create a network that supports giving addresses withing a specific subnet
networks:
ollama-net:
driver: bridge
ipam:
driver: default
config:
- subnet: ${SUBNET_ADDRESS}/16
volumes:
ollama:
discord:

37
docs/setup-docker.md Normal file
View File

@@ -0,0 +1,37 @@
## Docker Setup
* Follow this guide to setup [Docker](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04)
* If on Windows, download [Docker Desktop](https://docs.docker.com/desktop/install/windows-install/) to get the docker engine.
* Please also install [Docker Compose](https://docs.docker.com/compose/install/linux/) for easy running. If not, there are [scripts](#manual-run-with-docker) to set everything up.
## To Run (with Docker and Docker Compose)
* With the inclusion of subnets in the `docker-compose.yml`, you will need to set the `SUBNET_ADDRESS`, `OLLAMA_IP`, `OLLAMA_PORT`, and `DISCORD_IP`. Here are some default values if you don't care:
* `OLLAMA_IP = 172.18.0.2`
* `OLLAMA_PORT = 11434`
* `DISCORD_IP = 172.18.0.3`
* `SUBNET_ADDRESS = 172.18.0.0`
* Don't understand any of this? watch a Networking video to understand subnetting.
* You will need a model in the container for this to work properly, on Docker Desktop go to the `Containers` tab, select the `ollama` container, and select `Exec` to run as root on your container. Now, run `ollama pull [model name]` to get your model.
* For Linux Servers, you need another shell to pull the model, or if you run `docker compose build && docker compose up -d`, then it will run in the background to keep your shell. Run `docker exec -it ollama bash` to get into the container and run the samme pull command above.
* Otherwise, there is no need to install any npm packages for this, you just need to run `npm run start` to pull the containers and spin them up.
* For cleaning up on Linux (or Windows), run the following commands:
* `docker compose stop`
* `docker compose rm`
* `docker ps` to check if containers have been removed.
* You can also use `npm run clean` to clean up the containers and remove the network to address a possible `Address already in use` problem.
## Manual Run (with Docker)
* Run the following commands:
* `npm run docker:build`
* `npm run docker:ollama`
* `npm run docker:client`
* `docker ps` to see if the containers are there!
* Names should be **discord** and **ollama**.
* You can also just run `npm run docker:start` now for the above commands.
* Clean-up:
* `docker ps` for the conatiner id's. Use `-a` flag as necessary.
* `docker rm -f discord && docker rm -f ollama` to remove the containers.
* `docker rm -f CONTAINER_ID` do for both containers if naming issues arise.
* `docker network rm ollama-net` removes the network.
* `docker network prune` will also work so long as the network is unused.
* Remove Image:
* If you need to remove the image run `docker image rm IMAGE_ID`. You can get the image id by running `docker images`.

19
docs/setup-local.md Normal file
View File

@@ -0,0 +1,19 @@
## Ollama Setup
* Go to Ollama's [Linux download page](https://ollama.ai/download/linux) and run the simple curl command they provide. The command should be `curl https://ollama.ai/install.sh | sh`.
* Now the the following commands in separate terminals to test out how it works!
* In terminal 1 -> `ollama serve` to setup ollama
* In terminal 2 -> `ollama run [model name]`, for example `ollama run llama2`
* The models can vary as you can create your own model. You can also view ollama's [library](https://ollama.ai/library) of models.
* If there are any issues running ollama because of missing LLMs, run `ollama pull [model name]` as it will pull the model if Ollama has it in their library.
* This can also be done in [wsl](https://learn.microsoft.com/en-us/windows/wsl/install) for Windows machines.
* You can now interact with the model you just ran (it might take a second to startup).
* Response time varies with processing power!
## To Run Locally (without Docker)
* Run `npm install` to install the npm packages.
* Ensure that your [.env](../.env.sample) file's `OLLAMA_IP` is `127.0.0.1` to work properly.
* Now, you can run the bot by running `npm run client` which will build and run the decompiled typescript and run the setup for ollama.
* **IMPORTANT**: This must be ran in the wsl/Linux instance to work properly! Using Command Prompt/Powershell/Git Bash/etc. will not work on Windows (at least in my experience).
* Refer to the [resources](../README.md#resources) on what node version to use.
* Open up a separate terminal/shell (you will need wsl for this if on windows) and run `ollama serve` to startup ollama.
* If you do not have a model, you will need to run `ollama pull [model name]` in a separate terminal to get it.

436
package-lock.json generated
View File

@@ -1,19 +1,18 @@
{
"name": "discord-ollama",
"version": "0.1.4",
"version": "0.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "discord-ollama",
"version": "0.1.4",
"version": "0.3.0",
"license": "ISC",
"dependencies": {
"axios": "^1.6.2",
"concurrently": "^8.2.2",
"discord.js": "^14.14.1",
"dotenv": "^16.3.1",
"ollama": "^0.4.3"
"ollama": "^0.4.6"
},
"devDependencies": {
"@types/node": "^20.10.5",
@@ -26,17 +25,6 @@
"node": ">=16.0.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
"integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -516,17 +504,17 @@
}
},
"node_modules/@fastify/busboy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz",
"integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"engines": {
"node": ">=14"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
@@ -603,9 +591,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.11.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.13.tgz",
"integrity": "sha512-5G4zQwdiQBSWYTDAH1ctw2eidqdhMJaNsiIDKHFr55ihz5Trl2qqR8fdrT732yPBho5gkNxXm67OxWFBqX9aPg==",
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -654,28 +642,6 @@
"node": ">=0.4.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -701,11 +667,11 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"version": "1.6.8",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
"integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
"dependencies": {
"follow-redirects": "^1.15.4",
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
@@ -717,12 +683,15 @@
"dev": true
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/brace-expansion": {
@@ -747,43 +716,11 @@
"node": ">=8"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -796,39 +733,13 @@
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -846,53 +757,12 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/concurrently": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
"integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
"dependencies": {
"chalk": "^4.1.2",
"date-fns": "^2.30.0",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"spawn-command": "0.0.2",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.2"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": "^14.13.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
"dependencies": {
"@babel/runtime": "^7.21.0"
},
"engines": {
"node": ">=0.11"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/date-fns"
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -957,21 +827,16 @@
}
},
"node_modules/dotenv": {
"version": "16.4.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz",
"integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==",
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
"url": "https://dotenvx.com"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/esbuild": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
@@ -1010,14 +875,6 @@
"@esbuild/win32-x64": "0.19.12"
}
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"engines": {
"node": ">=6"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -1036,9 +893,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
@@ -1081,18 +938,10 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-tsconfig": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz",
"integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==",
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz",
"integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==",
"dev": true,
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
@@ -1114,11 +963,12 @@
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=8"
"node": ">=4"
}
},
"node_modules/ignore-by-default": {
@@ -1148,14 +998,6 @@
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -1200,9 +1042,9 @@
}
},
"node_modules/magic-bytes.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.8.0.tgz",
"integrity": "sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="
},
"node_modules/make-error": {
"version": "1.3.6",
@@ -1248,9 +1090,9 @@
"dev": true
},
"node_modules/nodemon": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
"integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
"integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
@@ -1275,27 +1117,6 @@
"url": "https://opencollective.com/nodemon"
}
},
"node_modules/nodemon/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/nodemon/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
@@ -1321,9 +1142,9 @@
}
},
"node_modules/ollama": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/ollama/-/ollama-0.4.4.tgz",
"integrity": "sha512-vAOZ3LM3DUE98Yy+iUJHu9Y9juKmjDAMntYoe8XqGAEkJkU5ZRkO1lwJxu5sRNU6WkEQ3MoHsvgeVIWr+cQmbg==",
"version": "0.4.9",
"resolved": "https://registry.npmjs.org/ollama/-/ollama-0.4.9.tgz",
"integrity": "sha512-hVf5xix5zwswfMN6ydt3K2TS886aOk9RT9xksajXxdIHhi2yokEsdzJYE7IGg+1bjQSPZ6JHzHbESYjNB0pTCg==",
"dependencies": {
"whatwg-fetch": "^3.6.20"
}
@@ -1363,19 +1184,6 @@
"node": ">=8.10.0"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
@@ -1385,18 +1193,10 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
@@ -1408,14 +1208,6 @@
"node": ">=10"
}
},
"node_modules/shell-quote": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
"integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/simple-update-notifier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
@@ -1428,47 +1220,16 @@
"node": ">=10"
}
},
"node_modules/spawn-command": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ=="
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
"node": ">=4"
}
},
"node_modules/to-regex-range": {
@@ -1495,18 +1256,10 @@
"nodetouch": "bin/nodetouch.js"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/ts-mixer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="
},
"node_modules/ts-node": {
"version": "10.9.2",
@@ -1557,9 +1310,9 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/tsx": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.0.tgz",
"integrity": "sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==",
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz",
"integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==",
"dev": true,
"dependencies": {
"esbuild": "~0.19.10",
@@ -1576,9 +1329,9 @@
}
},
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -1621,22 +1374,6 @@
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
"integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
@@ -1657,45 +1394,12 @@
}
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"engines": {
"node": ">=10"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "discord-ollama",
"version": "0.1.4",
"version": "0.3.0",
"description": "Ollama Integration into discord",
"main": "build/index.js",
"exports": "./build/index.js",
@@ -10,17 +10,22 @@
"build": "tsc",
"prod": "node .",
"client": "npm run build && npm run prod",
"API": "ollama serve",
"start": "concurrently \"npm:API\" \"npm:client\""
"clean": "docker compose down && docker rmi $(docker images | grep 0.2.0 | tr -s ' ' | cut -d ' ' -f 3) && docker rmi $(docker images --filter \"dangling=true\" -q --no-trunc)",
"start": "docker compose build --no-cache && docker compose up -d",
"docker:start": "npm run docker:network && npm run docker:build && npm run docker:client && npm run docker:ollama",
"docker:clean": "docker rmi $(docker images --filter \"dangling=true\" -q --no-trunc)",
"docker:network": "docker network create --subnet=172.18.0.0/16 ollama-net",
"docker:build": "docker build --no-cache -t discord/bot:0.2.0 .",
"docker:client": "docker run -d -v discord:/src/app --name discord --network ollama-net --ip 172.18.0.3 discord",
"docker:ollama": "docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama --network ollama-net --ip 172.18.0.2 ollama/ollama:latest"
},
"author": "Kevin Dang",
"license": "ISC",
"dependencies": {
"axios": "^1.6.2",
"concurrently": "^8.2.2",
"discord.js": "^14.14.1",
"dotenv": "^16.3.1",
"ollama": "^0.4.3"
"ollama": "^0.4.6"
},
"devDependencies": {
"@types/node": "^20.10.5",

View File

@@ -1,6 +1,7 @@
import { Client, GatewayIntentBits } from 'discord.js'
import { registerEvents } from './utils/events.js'
import { UserMessage, registerEvents } from './utils/events.js'
import Events from './events/index.js'
import { Ollama } from 'ollama'
// Import keys/tokens
import Keys from './keys.js'
@@ -16,7 +17,13 @@ const client = new Client({
]
});
const messageHistory = [
// initialize connection to ollama container
const ollama = new Ollama({
host: `http://${Keys.ipAddress}:${Keys.portAddress}`,
})
// Create Queue managed by Events
const messageHistory: [UserMessage] = [
{
role: 'system',
content: 'Your name is Ollama GU'
@@ -30,7 +37,7 @@ const messageHistory = [
* @param client the bot reference
* @param Keys tokens from .env files
*/
registerEvents(client, Events, messageHistory, Keys)
registerEvents(client, Events, messageHistory, Keys, ollama)
// Try to log in the client
await client.login(Keys.clientToken)

View File

@@ -1,6 +1,10 @@
import { SlashCommand } from '../utils/commands.js'
import { ThreadCreate } from './threadCreate.js'
import { MessageStyle } from './messageStyle.js'
import { MessageStream } from './messageStream.js'
export default [
ThreadCreate
ThreadCreate,
MessageStyle,
MessageStream
] as SlashCommand[]

View File

@@ -0,0 +1,33 @@
import { ApplicationCommandOptionType, ChannelType, Client, CommandInteraction } from 'discord.js'
import { SlashCommand } from '../utils/commands.js'
import { openFile } from '../utils/jsonHandler.js'
export const MessageStream: SlashCommand = {
name: 'message-stream',
description: 'change preference on message streaming from ollama. WARNING: can be very slow.',
// user option(s) for setting stream
options: [
{
name: 'stream',
description: 'enable or disable stream preference',
type: ApplicationCommandOptionType.Boolean,
required: true
}
],
// change preferences based on command
run: async (client: Client, interaction: CommandInteraction) => {
// verify channel
const channel = await client.channels.fetch(interaction.channelId)
if (!channel || channel.type !== ChannelType.GuildText) return
// save value to json and write to it
openFile('config.json', interaction.commandName, interaction.options.get('stream')?.value)
interaction.reply({
content: `Message streaming preferences for embed set to: \`${interaction.options.get('stream')?.value}\``,
ephemeral: true
})
}
}

View File

@@ -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 MessageStyle: SlashCommand = {
name: 'message-style',
description: 'sets the message style to embed or normal',
// set available user options to pass to the command
options: [
{
name: 'embed',
description: 'toggle embedded or normal message',
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 the message style
openFile('config.json', interaction.commandName, interaction.options.get('embed')?.value)
interaction.reply({
content: `Message style preferences for embed set to: \`${interaction.options.get('embed')?.value}\``,
ephemeral: true
})
}
}

View File

@@ -17,11 +17,11 @@ export const ThreadCreate: SlashCommand = {
})
// Send a message in the thread
thread.send(`**User:** ${interaction.user}`)
thread.send(`**User:** ${interaction.user} \n**People in Coversation:** ${thread.memberCount}`)
// user only reply
return interaction.reply({
content: 'I can help you in the Thread below.',
content: `I can help you in the Thread below. \n**Thread ID:** ${thread.id}`,
ephemeral: true
})
}

View File

@@ -8,7 +8,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.client.user.tag}.`)
log(`Interaction called \'${interaction.commandName}\' from ${interaction.user.tag}.`)
// ensure command exists, otherwise kill event
const command = commands.find(command => command.name === interaction.commandName)

View File

@@ -1,11 +1,13 @@
import { embedMessage, event, Events } from '../utils/index.js'
import { ChatResponse } from 'ollama'
import { embedMessage, event, Events, normalMessage } from '../utils/index.js'
import { Configuration, getConfig } from '../utils/jsonHandler.js'
/**
* Max Message length for free users is 2000 characters (bot or not).
* @param message the message received from the channel
*/
export default event(Events.MessageCreate, async ({ log, msgHist, tokens }, message) => {
log(`Message created \"${message.content}\" from ${message.author.tag}.`)
export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama }, message) => {
log(`Message \"${message.content}\" from ${message.author.tag} in channel/thread ${message.channelId}.`)
// Hard-coded channel to test output there only, in our case "ollama-endpoint"
if (message.channelId != tokens.channel) return
@@ -22,18 +24,36 @@ export default event(Events.MessageCreate, async ({ log, msgHist, tokens }, mess
content: message.content
})
// Try to query and send embed
const response = await embedMessage(message, tokens, msgHist)
// Try to query and send embed
try {
const config: Configuration = await new Promise((resolve, reject) => {
getConfig('config.json', (config) => {
if (config === undefined) {
reject(new Error('No Configuration is set up.'))
return
}
resolve(config)
})
})
// Try to query and send message
// log(normalMessage(message, tokens, msgHist))
let response: ChatResponse
// If something bad happened, remove user query and stop
if (response == undefined) { msgHist.pop(); return }
// undefined or false, use normal, otherwise use embed
if (config.options['message-style'])
response = await embedMessage(message, ollama, tokens, msgHist)
else
response = await normalMessage(message, ollama, tokens, msgHist)
// successful query, save it as history
msgHist.push({
role: 'assistant',
content: response.message.content
})
// If something bad happened, remove user query and stop
if (response == undefined) { msgHist.pop(); return }
// successful query, save it as history
msgHist.push({
role: 'assistant',
content: response.message.content
})
} catch (error: any) {
msgHist.pop() // remove message because of failure
message.reply(`**Response generation failed.**\n\nReason: ${error.message}\n\nPlease use any config slash command.`)
}
})

View File

@@ -4,8 +4,6 @@ import commands from '../commands/index.js'
// Log when the bot successfully logs in and export it
export default event(Events.ClientReady, ({ log }, client) => {
log(`Logged in as ${client.user.username}.`)
// Register the commands associated with the bot upon loggin in
registerCommands(client, commands)
@@ -14,4 +12,6 @@ export default event(Events.ClientReady, ({ log }, client) => {
name: 'Powered by Ollama',
type: ActivityType.Custom
})
log(`Logged in as ${client.user.username}.`)
})

View File

@@ -5,7 +5,9 @@ export const Keys = {
channel: getEnvVar('CHANNEL_ID'),
model: getEnvVar('MODEL'),
clientUid: getEnvVar('CLIENT_UID'),
guildId: getEnvVar('GUILD_ID')
guildId: getEnvVar('GUILD_ID'),
ipAddress: getEnvVar('OLLAMA_IP'),
portAddress: getEnvVar('OLLAMA_PORT')
} as const // readonly keys
export default Keys

View File

@@ -1,10 +1,14 @@
import { CommandInteraction, ChatInputApplicationCommandData, Client } from 'discord.js'
import { CommandInteraction, ChatInputApplicationCommandData, Client, ApplicationCommandOption } from 'discord.js'
/**
* interface for how slash commands should be run
*/
export interface SlashCommand extends ChatInputApplicationCommandData {
run: (client: Client, interaction: CommandInteraction) => void
run: (
client: Client,
interaction: CommandInteraction,
options?: ApplicationCommandOption[]
) => void
}
/**
@@ -16,7 +20,28 @@ export function registerCommands(client: Client, commands: SlashCommand[]): void
// ensure the bot is online before registering
if (!client.application) return
// map commands into an array of names, used to checking registered commands
const commandsToRegister: string[] = commands.map(command => command.name)
// fetch all the commands and delete them
client.application.commands.fetch().then((fetchedCommands) => {
for (const command of fetchedCommands.values()) {
if (!commandsToRegister.includes(command.name)) {
command.delete().catch(console.error)
console.log(`[Command: ${command.name}] Removed from Discord`)
}
}
})
// clear the cache of the commands
client.application.commands.cache.clear()
// iterate through all commands and register them with the bot
for (const command of commands)
client.application.commands.create(command)
client.application.commands
.create(command)
.then((c) => {
console.log(`[Command: ${c.name}] Registered on Discord`)
c.options?.forEach((o) => console.log(` - ${o.name}`))
})
}

View File

@@ -1,4 +1,5 @@
import type { ClientEvents, Awaitable, Client } from 'discord.js'
import { Ollama } from 'ollama'
// Export events through here to reduce amount of imports
export { Events } from 'discord.js'
@@ -6,16 +7,35 @@ export { Events } from 'discord.js'
export type LogMethod = (...args: unknown[]) => void
export type EventKeys = keyof ClientEvents // only wants keys of ClientEvents object
/**
* Tokens to run the bot as intended
* @param channel the channel where the bot will respond to queries
* @param model chosen model for the ollama to utilize
* @param clientUid the discord id for the bot
*/
export type Tokens = {
channel: string,
model: string,
clientUid: string
}
/**
* 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
*/
export type UserMessage = {
role: string,
content: string
}
// Event properties
export interface EventProps {
client: Client
log: LogMethod
msgHist: { role: string, content: string }[]
tokens: {
channel: string,
model: string,
clientUid: string
}
tokens: Tokens,
ollama: Ollama
}
export type EventCallback<T extends EventKeys> = (
props: EventProps,
@@ -32,15 +52,20 @@ export function event<T extends EventKeys>(key: T, callback: EventCallback<T>):
return { key, callback }
}
/**
* Method to register events to the bot per file in the events directory
* @param client initialized bot client
* @param events all the exported events from the index.ts in the events dir
* @param msgHist The message history of the bot
* @param tokens the passed in environment tokens for the service
* @param ollama the initialized ollama instance
*/
export function registerEvents(
client: Client,
events: Event[],
msgHist: { role: string, content: string }[],
tokens: {
channel: string,
model: string,
clientUid: string
}
msgHist: UserMessage[],
tokens: Tokens,
ollama: Ollama
): void {
for (const { key, callback } of events) {
client.on(key, (...args) => {
@@ -49,7 +74,7 @@ export function registerEvents(
// Handle Errors, call callback, log errors as needed
try {
callback({ client, log, msgHist, tokens }, ...args)
callback({ client, log, msgHist, tokens, ollama }, ...args)
} catch (error) {
log('[Uncaught Error]', error)
}

56
src/utils/jsonHandler.ts Normal file
View File

@@ -0,0 +1,56 @@
import fs from 'fs'
export interface Configuration {
readonly name: string
options: {
'message-stream'?: boolean,
'message-style'?: boolean
}
}
/**
* Method to open a file in the working directory and modify/create it
*
* @param filename name of the file
* @param key key value to access
* @param value new value to assign
*/
export function openFile(filename: string, key: string, value: any) {
// check if the file exists, if not then make the config file
if (fs.existsSync(filename)) {
fs.readFile(filename, 'utf8', (error, data) => {
if (error)
console.log(`[Error: openFile] Incorrect file format`)
else {
const object = JSON.parse(data)
object['options'][key] = value
fs.writeFileSync(filename, JSON.stringify(object, null, 2))
}
})
} else {
const object: Configuration = JSON.parse('{ \"name\": \"Discord Ollama Confirgurations\" }')
// set standard information for config file and options
object['options'] = {
[key]: value
}
fs.writeFileSync(filename, JSON.stringify(object, null, 2))
console.log(`[Util: openFile] Created 'config.json' in working directory`)
}
}
export async function getConfig(filename: string, callback: (config: Configuration | undefined) => void): Promise<void> {
// attempt to read the file and get the configuration
if (fs.existsSync(filename)) {
fs.readFile(filename, 'utf8', (error, data) => {
if (error) {
callback(undefined)
return // something went wrong... stop
}
callback(JSON.parse(data))
})
} else {
callback(undefined) // file not found
}
}

View File

@@ -1,5 +1,6 @@
import { EmbedBuilder, Message } from 'discord.js'
import ollama, { ChatResponse } from 'ollama'
import { ChatResponse, Ollama } from 'ollama'
import { UserMessage } from './events.js'
/**
* Method to send replies as normal text on discord like any other user
@@ -8,15 +9,13 @@ import ollama, { ChatResponse } from 'ollama'
* @param msgHist message history between user and model
*/
export async function embedMessage(
message: Message,
message: Message,
ollama: Ollama,
tokens: {
channel: string,
model: string
},
msgHist: {
role: string,
content: string
}[]
msgHist: UserMessage[]
) {
// bot response
let response: ChatResponse
@@ -44,17 +43,19 @@ export async function embedMessage(
stream: false
})
// dummy message to let user know that query is underway
const newEmbed = new EmbedBuilder()
.setTitle(`Responding to ${message.author.tag}`)
.setDescription(response.message.content)
.setDescription(response.message.content || 'No Content to Provide...')
.setColor('#00FF00')
// edit the message
sentMessage.edit({ embeds: [newEmbed] })
} catch(error: any) {
console.log(`[Util: messageEmbed] Error creating message: ${error.message}`)
const errorEmbed = new EmbedBuilder()
.setTitle(`Responding to ${message.author.tag}`)
.setDescription(error.error)
.setDescription(`**Response generation failed.**\n\nReason: ${error.message}`)
.setColor('#00FF00')
// send back error

View File

@@ -1,5 +1,6 @@
import { Message } from 'discord.js'
import ollama, { ChatResponse } from 'ollama'
import { ChatResponse, Ollama } from 'ollama'
import { UserMessage } from './events.js'
/**
* Method to send replies as normal text on discord like any other user
@@ -7,21 +8,19 @@ import ollama, { ChatResponse } from 'ollama'
* @param tokens tokens to run query
* @param msgHist message history between user and model
*/
export function normalMessage(
message: Message,
export async function normalMessage(
message: Message,
ollama: Ollama,
tokens: {
channel: string,
model: string
},
msgHist: {
role: string,
content: string
}[]
msgHist: UserMessage[]
) {
// bot's respnse
let response: ChatResponse
message.reply('Generating Response . . .').then(async sentMessage => {
await message.reply('Generating Response . . .').then(async sentMessage => {
try {
// Attempt to query model for message
response = await ollama.chat({
@@ -39,7 +38,8 @@ export function normalMessage(
// edit the 'generic' response to new message
sentMessage.edit(response.message.content)
} catch(error: any) {
sentMessage.edit(error.error)
console.log(`[Util: messageNormal] Error creating message: ${error.message}`)
sentMessage.edit(`**Response generation failed.**\n\nReason: ${error.message}`)
}
})

View File

@@ -2,6 +2,10 @@ import { AxiosResponse } from 'axios'
/**
* When running a /api/chat stream, the output needs to be parsed into an array of objects
* This method is used for development purposes and testing
*
* This will not work as intended with the inclusion of ollama-js, needs to be modified to work with it
*
* @param stream Axios response to from Ollama
*/
export function parseStream(stream: AxiosResponse<any, any>) {