Spaces:
Paused
Paused
Upload 13 files
Browse files- .dockerignore +5 -0
- .env +57 -0
- .eslintrc.json +53 -0
- .gitignore +5 -0
- Dockerfile +7 -0
- Makefile +27 -0
- README.md +43 -9
- assets/screenshot.png +0 -0
- docker-compose.yml +9 -0
- package-lock.json +378 -0
- package.json +14 -0
- src/bot.js +434 -0
- src/index.js +30 -0
.dockerignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package-lock.json
|
| 2 |
+
pnpm-lock.yaml
|
| 3 |
+
pnpm-lock.yml
|
| 4 |
+
node_modules/
|
| 5 |
+
.env
|
.env
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Discord bot token
|
| 2 |
+
TOKEN=MTIzNTE0MjgyODE5MTkxMTk2Ng.GTigmW.AkCl8B8TWqsrbRrgvuu1QWO7vgr-GLWN51qMW8
|
| 3 |
+
|
| 4 |
+
# What language model to use, orca is one of the lower-end models that doesn't require as much computer power as llama2
|
| 5 |
+
MODEL=tinydolphin
|
| 6 |
+
|
| 7 |
+
# Ollama URL (if you want to use multiple, separate them by commas)
|
| 8 |
+
OLLAMA=https://mahiatlinux-ollama-server.hf.space
|
| 9 |
+
|
| 10 |
+
# What Discord channels to enable it in (by ID)
|
| 11 |
+
CHANNELS=1235145472470159391
|
| 12 |
+
|
| 13 |
+
# System message that the language model can understand
|
| 14 |
+
# Feel free to change this
|
| 15 |
+
#SYSTEM="The current date and time is <date>.
|
| 16 |
+
|
| 17 |
+
Basic markdown is supported.
|
| 18 |
+
Bold: **bold text here**
|
| 19 |
+
Italics: _italic text here_
|
| 20 |
+
Underlined: __underlined text here__
|
| 21 |
+
Strikethrough: ~~strikethrough text here~~
|
| 22 |
+
Spoiler: ||spoiler text here||
|
| 23 |
+
Block quotes: Start the line with a > followed by a space, e.g
|
| 24 |
+
> Hello there
|
| 25 |
+
|
| 26 |
+
Inline code blocks are supported by surrounding text in backticks, e.g `print('Hello');`, block code is supported by surrounding text in three backticks, e.g ```print('Hello');```.
|
| 27 |
+
Surround code that is produced in code blocks. Use a code block with three backticks if the code has multiple lines, otherwise use an inline code block with one backtick.
|
| 28 |
+
|
| 29 |
+
Links are supported by wrapping the text in square brackets and the link in parenthesis, e.g [Example](https://example.com)
|
| 30 |
+
|
| 31 |
+
Lists are supported by starting the line with a dash followed by a space, e.g - List
|
| 32 |
+
Numbered lists are supported by starting the line with a number followed by a dot and a space, e.g 1. List.
|
| 33 |
+
Images, links, tables, LaTeX, and anything else is not supported.
|
| 34 |
+
|
| 35 |
+
If you need to use the symbols >, |, _, *, ~, @, #, :, `, put a backslash before them to escape them.
|
| 36 |
+
|
| 37 |
+
If the user is chatting casually, your responses should be only a few sentences, unless they are asking for help or a question.
|
| 38 |
+
Don't use unicode emoji unless needed."
|
| 39 |
+
|
| 40 |
+
# Use the system message above? (true/false)
|
| 41 |
+
USE_SYSTEM=true
|
| 42 |
+
|
| 43 |
+
# Use the model's system message? (true/false) If both are specified, model system message will be first
|
| 44 |
+
USE_MODEL_SYSTEM=true
|
| 45 |
+
|
| 46 |
+
# Require users to mention the bot to interact with it? (true/false)
|
| 47 |
+
REQUIRES_MENTION=true
|
| 48 |
+
|
| 49 |
+
# Whether to show a message at the start of a conversation
|
| 50 |
+
SHOW_START_OF_CONVERSATION=true
|
| 51 |
+
|
| 52 |
+
# Whether to use a random Ollama server or use the first available one
|
| 53 |
+
RANDOM_SERVER=false
|
| 54 |
+
|
| 55 |
+
# Whether to add a message before the first prompt of the conversation
|
| 56 |
+
INITIAL_PROMPT=""
|
| 57 |
+
USE_INITIAL_PROMPT=false
|
.eslintrc.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"extends": "eslint:recommended",
|
| 3 |
+
"env": {
|
| 4 |
+
"node": true,
|
| 5 |
+
"es6": true
|
| 6 |
+
},
|
| 7 |
+
"parserOptions": {
|
| 8 |
+
"ecmaVersion": 2021,
|
| 9 |
+
"sourceType": "module"
|
| 10 |
+
},
|
| 11 |
+
"rules": {
|
| 12 |
+
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
| 13 |
+
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
| 14 |
+
"comma-dangle": ["error", "never"],
|
| 15 |
+
"comma-spacing": "error",
|
| 16 |
+
"comma-style": "error",
|
| 17 |
+
"curly": ["error", "multi-line", "consistent"],
|
| 18 |
+
"dot-location": ["error", "property"],
|
| 19 |
+
"handle-callback-err": "off",
|
| 20 |
+
"indent": ["error", "tab", { "SwitchCase": 1 }],
|
| 21 |
+
"keyword-spacing": "error",
|
| 22 |
+
"max-nested-callbacks": ["error", { "max": 4 }],
|
| 23 |
+
"max-statements-per-line": ["error", { "max": 2 }],
|
| 24 |
+
"no-console": "off",
|
| 25 |
+
"no-empty": "warn",
|
| 26 |
+
"no-empty-function": "error",
|
| 27 |
+
"no-floating-decimal": "error",
|
| 28 |
+
"no-inline-comments": "error",
|
| 29 |
+
"no-lonely-if": "error",
|
| 30 |
+
"no-multi-spaces": "error",
|
| 31 |
+
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
| 32 |
+
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
| 33 |
+
"no-trailing-spaces": ["error"],
|
| 34 |
+
"no-var": "error",
|
| 35 |
+
"object-curly-spacing": ["error", "always"],
|
| 36 |
+
"prefer-const": "error",
|
| 37 |
+
"quotes": ["error", "double"],
|
| 38 |
+
"semi": ["error", "always"],
|
| 39 |
+
"space-before-blocks": "error",
|
| 40 |
+
"space-before-function-paren": ["error", {
|
| 41 |
+
"anonymous": "never",
|
| 42 |
+
"named": "never",
|
| 43 |
+
"asyncArrow": "always"
|
| 44 |
+
}],
|
| 45 |
+
"space-in-parens": "error",
|
| 46 |
+
"space-infix-ops": "error",
|
| 47 |
+
"space-unary-ops": "error",
|
| 48 |
+
"spaced-comment": "error",
|
| 49 |
+
"yoda": "error",
|
| 50 |
+
"default-case-last": "error",
|
| 51 |
+
"switch-colon-spacing": ["error", {"after": true, "before": false}]
|
| 52 |
+
}
|
| 53 |
+
}
|
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package-lock.json
|
| 2 |
+
pnpm-lock.yaml
|
| 3 |
+
pnpm-lock.yml
|
| 4 |
+
node_modules/
|
| 5 |
+
.env
|
Dockerfile
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:20
|
| 2 |
+
|
| 3 |
+
COPY . .
|
| 4 |
+
RUN npm i --omit=dev --no-package-lock
|
| 5 |
+
USER node
|
| 6 |
+
|
| 7 |
+
CMD ["node","./src/index.js"]
|
Makefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# As long as you have Make running on your machine you should be able to use this file.
|
| 2 |
+
# make <command-name> runs a given command (e.g. make compose-up)
|
| 3 |
+
# Command-names are given by starting a line without a tab and followed by a colon (i.e.':').
|
| 4 |
+
# what the command runs is the line below the colon and that line must start with a tab of size 4.
|
| 5 |
+
# Running make without a command after it will run the first command in the file.
|
| 6 |
+
|
| 7 |
+
# starts the discord-ai-bot
|
| 8 |
+
compose-up:
|
| 9 |
+
$(MAKE) setup_env && docker compose -p discord-ai up
|
| 10 |
+
|
| 11 |
+
# Stops docker compose without removing the containers from the system.
|
| 12 |
+
compose-stop:
|
| 13 |
+
docker compose -p discord-ai stop
|
| 14 |
+
|
| 15 |
+
# Stops docker compose and removes the containers from the system
|
| 16 |
+
compose-down:
|
| 17 |
+
docker compose -p discord-ai down
|
| 18 |
+
|
| 19 |
+
# Run the local node project with make and without docker
|
| 20 |
+
local:
|
| 21 |
+
$(MAKE) setup_env && npm i && node ./src/index.js
|
| 22 |
+
|
| 23 |
+
# This copies the .env.example (source) file to the .env (destination) file location
|
| 24 |
+
# The -n or no clobber means it will not overwrite the .env file if it already exists.
|
| 25 |
+
# The || : basically ignores the error code of the previous command and always succeeds.
|
| 26 |
+
setup_env:
|
| 27 |
+
cp -n ./.env.example ./.env 2>/dev/null || :
|
README.md
CHANGED
|
@@ -1,10 +1,44 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
pinned: false
|
| 8 |
-
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div align="center">
|
| 2 |
+
<h1><a href="#"></a>Discord AI Bot</h1>
|
| 3 |
+
<h3 align="center"><a href="#"></a>Discord bot to interact with <a href="https://github.com/jmorganca/ollama">Ollama</a> as a chatbot</h3>
|
| 4 |
+
<h3><a href="#"></a><img alt="Stars" src="https://img.shields.io/github/stars/mekb-turtle/discord-ai-bot?display_name=tag&style=for-the-badge" /></h3>
|
| 5 |
+
<h3><a href="#"></a><img alt="Discord chat with the bot" src="assets/screenshot.png" /></h3>
|
| 6 |
+
</div>
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
### Archived
|
| 9 |
+
I have decided to archive this project as I no longer have the time to maintain it. If you would like to take over the project, [please let me know](https://github.com/mekb-turtle).
|
| 10 |
+
|
| 11 |
+
### Set-up instructions
|
| 12 |
+
1. Install [Node.js](https://nodejs.org) (if you have a package manager, use that instead to install this)
|
| 13 |
+
- Make sure to install at least v14 of Node.js
|
| 14 |
+
2. Install [Ollama](https://github.com/jmorganca/ollama) (ditto)
|
| 15 |
+
3. Pull (download) a model, e.g `ollama pull orca` or `ollama pull llama2`
|
| 16 |
+
4. Start Ollama by running `ollama serve`
|
| 17 |
+
5. [Create a Discord bot](https://discord.com/developers/applications)
|
| 18 |
+
- Under Application » Bot
|
| 19 |
+
- Enable Message Content Intent
|
| 20 |
+
- Enable Server Members Intent (for replacing user mentions with the username)
|
| 21 |
+
6. Invite the bot to a server
|
| 22 |
+
1. Go to Application » OAuth2 » URL Generator
|
| 23 |
+
2. Enable `bot`
|
| 24 |
+
3. Enable Send Messages, Read Messages/View Channels, and Read Message History
|
| 25 |
+
4. Under Generated URL, click Copy and paste the URL in your browser
|
| 26 |
+
7. Rename `.env.example` to `.env` and edit the `.env` file
|
| 27 |
+
- You can get the token from Application » Bot » Token, **never share this with anyone**
|
| 28 |
+
- Make sure to change the model if you aren't using `orca`
|
| 29 |
+
- Ollama URL can be kept the same unless you have changed the port
|
| 30 |
+
- You can use multiple Ollama servers at the same time by separating the URLs with commas
|
| 31 |
+
- Set the channels to the channel ID, comma separated
|
| 32 |
+
1. In Discord, go to User Settings » Advanced, and enable Developer Mode
|
| 33 |
+
2. Right click on a channel you want to use, and click Copy Channel ID
|
| 34 |
+
- You can edit the system message the bot uses, or disable it entirely
|
| 35 |
+
8. Start the bot with `npm start`
|
| 36 |
+
9. You can interact with the bot by @mentioning it with your message
|
| 37 |
+
|
| 38 |
+
### Set-up instructions with Docker
|
| 39 |
+
1. Install [Docker](https://docs.docker.com/get-docker/)
|
| 40 |
+
- Should be atleast compatible with version 3 of compose (docker engine 1.13.0+)
|
| 41 |
+
2. Repeat steps 2—7 from the other setup instructions
|
| 42 |
+
3. Start the bot with `make compose-up` if you have Make installed
|
| 43 |
+
- Otherwise, try `docker compose -p discord-ai up` instead
|
| 44 |
+
4. You can interact with the bot by @mentioning it with your message
|
assets/screenshot.png
ADDED
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: "3"
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
bot:
|
| 5 |
+
build: .
|
| 6 |
+
env_file: .env
|
| 7 |
+
environment:
|
| 8 |
+
- OLLAMA=http://host.docker.internal:11434
|
| 9 |
+
restart: unless-stopped
|
package-lock.json
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "discord-ai-bot",
|
| 3 |
+
"lockfileVersion": 3,
|
| 4 |
+
"requires": true,
|
| 5 |
+
"packages": {
|
| 6 |
+
"": {
|
| 7 |
+
"name": "discord-ai-bot",
|
| 8 |
+
"dependencies": {
|
| 9 |
+
"axios": "^1.6.3",
|
| 10 |
+
"discord.js": "^14.14.1",
|
| 11 |
+
"dotenv": "^16.3.1",
|
| 12 |
+
"meklog": "^1.0.2"
|
| 13 |
+
}
|
| 14 |
+
},
|
| 15 |
+
"node_modules/@discordjs/builders": {
|
| 16 |
+
"version": "1.7.0",
|
| 17 |
+
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz",
|
| 18 |
+
"integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==",
|
| 19 |
+
"dependencies": {
|
| 20 |
+
"@discordjs/formatters": "^0.3.3",
|
| 21 |
+
"@discordjs/util": "^1.0.2",
|
| 22 |
+
"@sapphire/shapeshift": "^3.9.3",
|
| 23 |
+
"discord-api-types": "0.37.61",
|
| 24 |
+
"fast-deep-equal": "^3.1.3",
|
| 25 |
+
"ts-mixer": "^6.0.3",
|
| 26 |
+
"tslib": "^2.6.2"
|
| 27 |
+
},
|
| 28 |
+
"engines": {
|
| 29 |
+
"node": ">=16.11.0"
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"node_modules/@discordjs/collection": {
|
| 33 |
+
"version": "1.5.3",
|
| 34 |
+
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
|
| 35 |
+
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
|
| 36 |
+
"engines": {
|
| 37 |
+
"node": ">=16.11.0"
|
| 38 |
+
}
|
| 39 |
+
},
|
| 40 |
+
"node_modules/@discordjs/formatters": {
|
| 41 |
+
"version": "0.3.3",
|
| 42 |
+
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz",
|
| 43 |
+
"integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==",
|
| 44 |
+
"dependencies": {
|
| 45 |
+
"discord-api-types": "0.37.61"
|
| 46 |
+
},
|
| 47 |
+
"engines": {
|
| 48 |
+
"node": ">=16.11.0"
|
| 49 |
+
}
|
| 50 |
+
},
|
| 51 |
+
"node_modules/@discordjs/rest": {
|
| 52 |
+
"version": "2.2.0",
|
| 53 |
+
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz",
|
| 54 |
+
"integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==",
|
| 55 |
+
"dependencies": {
|
| 56 |
+
"@discordjs/collection": "^2.0.0",
|
| 57 |
+
"@discordjs/util": "^1.0.2",
|
| 58 |
+
"@sapphire/async-queue": "^1.5.0",
|
| 59 |
+
"@sapphire/snowflake": "^3.5.1",
|
| 60 |
+
"@vladfrangu/async_event_emitter": "^2.2.2",
|
| 61 |
+
"discord-api-types": "0.37.61",
|
| 62 |
+
"magic-bytes.js": "^1.5.0",
|
| 63 |
+
"tslib": "^2.6.2",
|
| 64 |
+
"undici": "5.27.2"
|
| 65 |
+
},
|
| 66 |
+
"engines": {
|
| 67 |
+
"node": ">=16.11.0"
|
| 68 |
+
}
|
| 69 |
+
},
|
| 70 |
+
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
|
| 71 |
+
"version": "2.0.0",
|
| 72 |
+
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz",
|
| 73 |
+
"integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==",
|
| 74 |
+
"engines": {
|
| 75 |
+
"node": ">=18"
|
| 76 |
+
}
|
| 77 |
+
},
|
| 78 |
+
"node_modules/@discordjs/util": {
|
| 79 |
+
"version": "1.0.2",
|
| 80 |
+
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz",
|
| 81 |
+
"integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==",
|
| 82 |
+
"engines": {
|
| 83 |
+
"node": ">=16.11.0"
|
| 84 |
+
}
|
| 85 |
+
},
|
| 86 |
+
"node_modules/@discordjs/ws": {
|
| 87 |
+
"version": "1.0.2",
|
| 88 |
+
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz",
|
| 89 |
+
"integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==",
|
| 90 |
+
"dependencies": {
|
| 91 |
+
"@discordjs/collection": "^2.0.0",
|
| 92 |
+
"@discordjs/rest": "^2.1.0",
|
| 93 |
+
"@discordjs/util": "^1.0.2",
|
| 94 |
+
"@sapphire/async-queue": "^1.5.0",
|
| 95 |
+
"@types/ws": "^8.5.9",
|
| 96 |
+
"@vladfrangu/async_event_emitter": "^2.2.2",
|
| 97 |
+
"discord-api-types": "0.37.61",
|
| 98 |
+
"tslib": "^2.6.2",
|
| 99 |
+
"ws": "^8.14.2"
|
| 100 |
+
},
|
| 101 |
+
"engines": {
|
| 102 |
+
"node": ">=16.11.0"
|
| 103 |
+
}
|
| 104 |
+
},
|
| 105 |
+
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
|
| 106 |
+
"version": "2.0.0",
|
| 107 |
+
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz",
|
| 108 |
+
"integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==",
|
| 109 |
+
"engines": {
|
| 110 |
+
"node": ">=18"
|
| 111 |
+
}
|
| 112 |
+
},
|
| 113 |
+
"node_modules/@fastify/busboy": {
|
| 114 |
+
"version": "2.1.1",
|
| 115 |
+
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
|
| 116 |
+
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
|
| 117 |
+
"engines": {
|
| 118 |
+
"node": ">=14"
|
| 119 |
+
}
|
| 120 |
+
},
|
| 121 |
+
"node_modules/@sapphire/async-queue": {
|
| 122 |
+
"version": "1.5.2",
|
| 123 |
+
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz",
|
| 124 |
+
"integrity": "sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==",
|
| 125 |
+
"engines": {
|
| 126 |
+
"node": ">=v14.0.0",
|
| 127 |
+
"npm": ">=7.0.0"
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"node_modules/@sapphire/shapeshift": {
|
| 131 |
+
"version": "3.9.7",
|
| 132 |
+
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz",
|
| 133 |
+
"integrity": "sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==",
|
| 134 |
+
"dependencies": {
|
| 135 |
+
"fast-deep-equal": "^3.1.3",
|
| 136 |
+
"lodash": "^4.17.21"
|
| 137 |
+
},
|
| 138 |
+
"engines": {
|
| 139 |
+
"node": ">=v16"
|
| 140 |
+
}
|
| 141 |
+
},
|
| 142 |
+
"node_modules/@sapphire/snowflake": {
|
| 143 |
+
"version": "3.5.1",
|
| 144 |
+
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz",
|
| 145 |
+
"integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==",
|
| 146 |
+
"engines": {
|
| 147 |
+
"node": ">=v14.0.0",
|
| 148 |
+
"npm": ">=7.0.0"
|
| 149 |
+
}
|
| 150 |
+
},
|
| 151 |
+
"node_modules/@types/node": {
|
| 152 |
+
"version": "20.12.7",
|
| 153 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
|
| 154 |
+
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
|
| 155 |
+
"dependencies": {
|
| 156 |
+
"undici-types": "~5.26.4"
|
| 157 |
+
}
|
| 158 |
+
},
|
| 159 |
+
"node_modules/@types/ws": {
|
| 160 |
+
"version": "8.5.9",
|
| 161 |
+
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz",
|
| 162 |
+
"integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==",
|
| 163 |
+
"dependencies": {
|
| 164 |
+
"@types/node": "*"
|
| 165 |
+
}
|
| 166 |
+
},
|
| 167 |
+
"node_modules/@vladfrangu/async_event_emitter": {
|
| 168 |
+
"version": "2.2.4",
|
| 169 |
+
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz",
|
| 170 |
+
"integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==",
|
| 171 |
+
"engines": {
|
| 172 |
+
"node": ">=v14.0.0",
|
| 173 |
+
"npm": ">=7.0.0"
|
| 174 |
+
}
|
| 175 |
+
},
|
| 176 |
+
"node_modules/asynckit": {
|
| 177 |
+
"version": "0.4.0",
|
| 178 |
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
| 179 |
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
| 180 |
+
},
|
| 181 |
+
"node_modules/axios": {
|
| 182 |
+
"version": "1.6.8",
|
| 183 |
+
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
|
| 184 |
+
"integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
|
| 185 |
+
"dependencies": {
|
| 186 |
+
"follow-redirects": "^1.15.6",
|
| 187 |
+
"form-data": "^4.0.0",
|
| 188 |
+
"proxy-from-env": "^1.1.0"
|
| 189 |
+
}
|
| 190 |
+
},
|
| 191 |
+
"node_modules/combined-stream": {
|
| 192 |
+
"version": "1.0.8",
|
| 193 |
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
| 194 |
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
| 195 |
+
"dependencies": {
|
| 196 |
+
"delayed-stream": "~1.0.0"
|
| 197 |
+
},
|
| 198 |
+
"engines": {
|
| 199 |
+
"node": ">= 0.8"
|
| 200 |
+
}
|
| 201 |
+
},
|
| 202 |
+
"node_modules/delayed-stream": {
|
| 203 |
+
"version": "1.0.0",
|
| 204 |
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
| 205 |
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
| 206 |
+
"engines": {
|
| 207 |
+
"node": ">=0.4.0"
|
| 208 |
+
}
|
| 209 |
+
},
|
| 210 |
+
"node_modules/discord-api-types": {
|
| 211 |
+
"version": "0.37.61",
|
| 212 |
+
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
|
| 213 |
+
"integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw=="
|
| 214 |
+
},
|
| 215 |
+
"node_modules/discord.js": {
|
| 216 |
+
"version": "14.14.1",
|
| 217 |
+
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz",
|
| 218 |
+
"integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==",
|
| 219 |
+
"dependencies": {
|
| 220 |
+
"@discordjs/builders": "^1.7.0",
|
| 221 |
+
"@discordjs/collection": "1.5.3",
|
| 222 |
+
"@discordjs/formatters": "^0.3.3",
|
| 223 |
+
"@discordjs/rest": "^2.1.0",
|
| 224 |
+
"@discordjs/util": "^1.0.2",
|
| 225 |
+
"@discordjs/ws": "^1.0.2",
|
| 226 |
+
"@sapphire/snowflake": "3.5.1",
|
| 227 |
+
"@types/ws": "8.5.9",
|
| 228 |
+
"discord-api-types": "0.37.61",
|
| 229 |
+
"fast-deep-equal": "3.1.3",
|
| 230 |
+
"lodash.snakecase": "4.1.1",
|
| 231 |
+
"tslib": "2.6.2",
|
| 232 |
+
"undici": "5.27.2",
|
| 233 |
+
"ws": "8.14.2"
|
| 234 |
+
},
|
| 235 |
+
"engines": {
|
| 236 |
+
"node": ">=16.11.0"
|
| 237 |
+
}
|
| 238 |
+
},
|
| 239 |
+
"node_modules/dotenv": {
|
| 240 |
+
"version": "16.4.5",
|
| 241 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
| 242 |
+
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
|
| 243 |
+
"engines": {
|
| 244 |
+
"node": ">=12"
|
| 245 |
+
},
|
| 246 |
+
"funding": {
|
| 247 |
+
"url": "https://dotenvx.com"
|
| 248 |
+
}
|
| 249 |
+
},
|
| 250 |
+
"node_modules/fast-deep-equal": {
|
| 251 |
+
"version": "3.1.3",
|
| 252 |
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
| 253 |
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
| 254 |
+
},
|
| 255 |
+
"node_modules/follow-redirects": {
|
| 256 |
+
"version": "1.15.6",
|
| 257 |
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
| 258 |
+
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
| 259 |
+
"funding": [
|
| 260 |
+
{
|
| 261 |
+
"type": "individual",
|
| 262 |
+
"url": "https://github.com/sponsors/RubenVerborgh"
|
| 263 |
+
}
|
| 264 |
+
],
|
| 265 |
+
"engines": {
|
| 266 |
+
"node": ">=4.0"
|
| 267 |
+
},
|
| 268 |
+
"peerDependenciesMeta": {
|
| 269 |
+
"debug": {
|
| 270 |
+
"optional": true
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
},
|
| 274 |
+
"node_modules/form-data": {
|
| 275 |
+
"version": "4.0.0",
|
| 276 |
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
| 277 |
+
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
| 278 |
+
"dependencies": {
|
| 279 |
+
"asynckit": "^0.4.0",
|
| 280 |
+
"combined-stream": "^1.0.8",
|
| 281 |
+
"mime-types": "^2.1.12"
|
| 282 |
+
},
|
| 283 |
+
"engines": {
|
| 284 |
+
"node": ">= 6"
|
| 285 |
+
}
|
| 286 |
+
},
|
| 287 |
+
"node_modules/lodash": {
|
| 288 |
+
"version": "4.17.21",
|
| 289 |
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
| 290 |
+
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
| 291 |
+
},
|
| 292 |
+
"node_modules/lodash.snakecase": {
|
| 293 |
+
"version": "4.1.1",
|
| 294 |
+
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
|
| 295 |
+
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
|
| 296 |
+
},
|
| 297 |
+
"node_modules/magic-bytes.js": {
|
| 298 |
+
"version": "1.10.0",
|
| 299 |
+
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
|
| 300 |
+
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="
|
| 301 |
+
},
|
| 302 |
+
"node_modules/meklog": {
|
| 303 |
+
"version": "1.0.2",
|
| 304 |
+
"resolved": "https://registry.npmjs.org/meklog/-/meklog-1.0.2.tgz",
|
| 305 |
+
"integrity": "sha512-9jkTaZzWEpO0tiWQl0xoj/DPktcHELg74nSzsEaLaN7IYGmcczMXgQXQmZT1cXWykj2FAhv0BBRUGpnFCZn/dg=="
|
| 306 |
+
},
|
| 307 |
+
"node_modules/mime-db": {
|
| 308 |
+
"version": "1.52.0",
|
| 309 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
| 310 |
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
| 311 |
+
"engines": {
|
| 312 |
+
"node": ">= 0.6"
|
| 313 |
+
}
|
| 314 |
+
},
|
| 315 |
+
"node_modules/mime-types": {
|
| 316 |
+
"version": "2.1.35",
|
| 317 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
| 318 |
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
| 319 |
+
"dependencies": {
|
| 320 |
+
"mime-db": "1.52.0"
|
| 321 |
+
},
|
| 322 |
+
"engines": {
|
| 323 |
+
"node": ">= 0.6"
|
| 324 |
+
}
|
| 325 |
+
},
|
| 326 |
+
"node_modules/proxy-from-env": {
|
| 327 |
+
"version": "1.1.0",
|
| 328 |
+
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
| 329 |
+
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
| 330 |
+
},
|
| 331 |
+
"node_modules/ts-mixer": {
|
| 332 |
+
"version": "6.0.4",
|
| 333 |
+
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
|
| 334 |
+
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="
|
| 335 |
+
},
|
| 336 |
+
"node_modules/tslib": {
|
| 337 |
+
"version": "2.6.2",
|
| 338 |
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
| 339 |
+
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
| 340 |
+
},
|
| 341 |
+
"node_modules/undici": {
|
| 342 |
+
"version": "5.27.2",
|
| 343 |
+
"resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz",
|
| 344 |
+
"integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==",
|
| 345 |
+
"dependencies": {
|
| 346 |
+
"@fastify/busboy": "^2.0.0"
|
| 347 |
+
},
|
| 348 |
+
"engines": {
|
| 349 |
+
"node": ">=14.0"
|
| 350 |
+
}
|
| 351 |
+
},
|
| 352 |
+
"node_modules/undici-types": {
|
| 353 |
+
"version": "5.26.5",
|
| 354 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
| 355 |
+
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
| 356 |
+
},
|
| 357 |
+
"node_modules/ws": {
|
| 358 |
+
"version": "8.14.2",
|
| 359 |
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
|
| 360 |
+
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
|
| 361 |
+
"engines": {
|
| 362 |
+
"node": ">=10.0.0"
|
| 363 |
+
},
|
| 364 |
+
"peerDependencies": {
|
| 365 |
+
"bufferutil": "^4.0.1",
|
| 366 |
+
"utf-8-validate": ">=5.0.2"
|
| 367 |
+
},
|
| 368 |
+
"peerDependenciesMeta": {
|
| 369 |
+
"bufferutil": {
|
| 370 |
+
"optional": true
|
| 371 |
+
},
|
| 372 |
+
"utf-8-validate": {
|
| 373 |
+
"optional": true
|
| 374 |
+
}
|
| 375 |
+
}
|
| 376 |
+
}
|
| 377 |
+
}
|
| 378 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "discord-ai-bot",
|
| 3 |
+
"main": "src/index.js",
|
| 4 |
+
"scripts": {
|
| 5 |
+
"start": "node src/index.js"
|
| 6 |
+
},
|
| 7 |
+
"dependencies": {
|
| 8 |
+
"axios": "^1.6.3",
|
| 9 |
+
"discord.js": "^14.14.1",
|
| 10 |
+
"dotenv": "^16.3.1",
|
| 11 |
+
"meklog": "^1.0.2"
|
| 12 |
+
},
|
| 13 |
+
"type": "module"
|
| 14 |
+
}
|
src/bot.js
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Client, Events, GatewayIntentBits, MessageType, Partials } from "discord.js";
|
| 2 |
+
import { Logger, LogLevel } from "meklog";
|
| 3 |
+
import dotenv from "dotenv";
|
| 4 |
+
import axios from "axios";
|
| 5 |
+
|
| 6 |
+
dotenv.config();
|
| 7 |
+
|
| 8 |
+
const model = process.env.MODEL;
|
| 9 |
+
const servers = process.env.OLLAMA.split(",").map(url => ({ url: new URL(url), available: true }));
|
| 10 |
+
const channels = process.env.CHANNELS.split(",");
|
| 11 |
+
|
| 12 |
+
if (servers.length == 0) {
|
| 13 |
+
throw new Error("No servers available");
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
let log;
|
| 17 |
+
process.on("message", data => {
|
| 18 |
+
if (data.shardID) client.shardID = data.shardID;
|
| 19 |
+
if (data.logger) log = new Logger(data.logger);
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
+
const logError = (error) => {
|
| 23 |
+
if (error.response) {
|
| 24 |
+
let str = `Error ${error.response.status} ${error.response.statusText}: ${error.request.method} ${error.request.path}`;
|
| 25 |
+
if (error.response.data?.error) {
|
| 26 |
+
str += ": " + error.response.data.error;
|
| 27 |
+
}
|
| 28 |
+
log(LogLevel.Error, str);
|
| 29 |
+
} else {
|
| 30 |
+
log(LogLevel.Error, error);
|
| 31 |
+
}
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
function shuffleArray(array) {
|
| 35 |
+
for (let i = array.length - 1; i > 0; i--) {
|
| 36 |
+
const j = Math.floor(Math.random() * (i + 1));
|
| 37 |
+
[array[i], array[j]] = [array[j], array[i]];
|
| 38 |
+
}
|
| 39 |
+
return array;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
async function makeRequest(path, method, data) {
|
| 43 |
+
while (servers.filter(server => server.available).length == 0) {
|
| 44 |
+
// wait until a server is available
|
| 45 |
+
await new Promise(res => setTimeout(res, 1000));
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
let error = null;
|
| 49 |
+
// randomly loop through the servers available, don't shuffle the actual array because we want to be notified of any updates
|
| 50 |
+
let order = new Array(servers.length).fill().map((_, i) => i);
|
| 51 |
+
if (randomServer) order = shuffleArray(order);
|
| 52 |
+
for (const j in order) {
|
| 53 |
+
if (!order.hasOwnProperty(j)) continue;
|
| 54 |
+
const i = order[j];
|
| 55 |
+
// try one until it succeeds
|
| 56 |
+
try {
|
| 57 |
+
// make a request to ollama
|
| 58 |
+
if (!servers[i].available) continue;
|
| 59 |
+
const url = new URL(servers[i].url); // don't modify the original URL
|
| 60 |
+
|
| 61 |
+
servers[i].available = false;
|
| 62 |
+
|
| 63 |
+
if (path.startsWith("/")) path = path.substring(1);
|
| 64 |
+
if (!url.pathname.endsWith("/")) url.pathname += "/"; // safety
|
| 65 |
+
url.pathname += path;
|
| 66 |
+
log(LogLevel.Debug, `Making request to ${url}`);
|
| 67 |
+
const result = await axios({
|
| 68 |
+
method, url, data,
|
| 69 |
+
responseType: "text"
|
| 70 |
+
});
|
| 71 |
+
servers[i].available = true;
|
| 72 |
+
return result.data;
|
| 73 |
+
} catch (err) {
|
| 74 |
+
servers[i].available = true;
|
| 75 |
+
error = err;
|
| 76 |
+
logError(error);
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
if (!error) {
|
| 80 |
+
throw new Error("No servers available");
|
| 81 |
+
}
|
| 82 |
+
throw error;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
const client = new Client({
|
| 86 |
+
intents: [
|
| 87 |
+
GatewayIntentBits.Guilds,
|
| 88 |
+
GatewayIntentBits.GuildMessages,
|
| 89 |
+
GatewayIntentBits.GuildMembers,
|
| 90 |
+
GatewayIntentBits.DirectMessages,
|
| 91 |
+
GatewayIntentBits.MessageContent
|
| 92 |
+
],
|
| 93 |
+
allowedMentions: { users: [], roles: [], repliedUser: false },
|
| 94 |
+
partials: [
|
| 95 |
+
Partials.Channel
|
| 96 |
+
]
|
| 97 |
+
});
|
| 98 |
+
|
| 99 |
+
client.once(Events.ClientReady, async () => {
|
| 100 |
+
await client.guilds.fetch();
|
| 101 |
+
client.user.setPresence({ activities: [], status: "online" });
|
| 102 |
+
});
|
| 103 |
+
|
| 104 |
+
const messages = {};
|
| 105 |
+
|
| 106 |
+
// split text so it fits in a Discord message
|
| 107 |
+
function splitText(str, length) {
|
| 108 |
+
// trim matches different characters to \s
|
| 109 |
+
str = str
|
| 110 |
+
.replace(/\r\n/g, "\n").replace(/\r/g, "\n")
|
| 111 |
+
.replace(/^\s+|\s+$/g, "");
|
| 112 |
+
const segments = [];
|
| 113 |
+
let segment = "";
|
| 114 |
+
let word, suffix;
|
| 115 |
+
function appendSegment() {
|
| 116 |
+
segment = segment.replace(/^\s+|\s+$/g, "");
|
| 117 |
+
if (segment.length > 0) {
|
| 118 |
+
segments.push(segment);
|
| 119 |
+
segment = "";
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
// match a word
|
| 123 |
+
while ((word = str.match(/^[^\s]*(?:\s+|$)/)) != null) {
|
| 124 |
+
suffix = "";
|
| 125 |
+
word = word[0];
|
| 126 |
+
if (word.length == 0) break;
|
| 127 |
+
if (segment.length + word.length > length) {
|
| 128 |
+
// prioritise splitting by newlines over other whitespaces
|
| 129 |
+
if (segment.includes("\n")) {
|
| 130 |
+
// append up all but last paragraph
|
| 131 |
+
const beforeParagraph = segment.match(/^.*\n/s);
|
| 132 |
+
if (beforeParagraph != null) {
|
| 133 |
+
const lastParagraph = segment.substring(beforeParagraph[0].length, segment.length);
|
| 134 |
+
segment = beforeParagraph[0];
|
| 135 |
+
appendSegment();
|
| 136 |
+
segment = lastParagraph;
|
| 137 |
+
continue;
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
appendSegment();
|
| 141 |
+
// if word is larger than the split length
|
| 142 |
+
if (word.length > length) {
|
| 143 |
+
word = word.substring(0, length);
|
| 144 |
+
if (length > 1 && word.match(/^[^\s]+$/)) {
|
| 145 |
+
// try to hyphenate word
|
| 146 |
+
word = word.substring(0, word.length - 1);
|
| 147 |
+
suffix = "-";
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
str = str.substring(word.length, str.length);
|
| 152 |
+
segment += word + suffix;
|
| 153 |
+
}
|
| 154 |
+
appendSegment();
|
| 155 |
+
return segments;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
function getBoolean(str) {
|
| 159 |
+
return !!str && str != "false" && str != "no" && str != "off" && str != "0";
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
function parseJSONMessage(str) {
|
| 163 |
+
return str.split(/[\r\n]+/g).map(function(line) {
|
| 164 |
+
const result = JSON.parse(`"${line}"`);
|
| 165 |
+
if (typeof result !== "string") throw new "Invalid syntax in .env file";
|
| 166 |
+
return result;
|
| 167 |
+
}).join("\n");
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
function parseEnvString(str) {
|
| 171 |
+
return typeof str === "string" ?
|
| 172 |
+
parseJSONMessage(str).replace(/<date>/gi, new Date().toUTCString()) : null;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
const customSystemMessage = parseEnvString(process.env.SYSTEM);
|
| 176 |
+
const useCustomSystemMessage = getBoolean(process.env.USE_SYSTEM) && !!customSystemMessage;
|
| 177 |
+
const useModelSystemMessage = getBoolean(process.env.USE_MODEL_SYSTEM);
|
| 178 |
+
const showStartOfConversation = getBoolean(process.env.SHOW_START_OF_CONVERSATION);
|
| 179 |
+
const randomServer = getBoolean(process.env.RANDOM_SERVER);
|
| 180 |
+
let modelInfo = null;
|
| 181 |
+
const initialPrompt = parseEnvString(process.env.INITIAL_PROMPT);
|
| 182 |
+
const useInitialPrompt = getBoolean(process.env.USE_INITIAL_PROMPT) && !!initialPrompt;
|
| 183 |
+
|
| 184 |
+
const requiresMention = getBoolean(process.env.REQUIRES_MENTION);
|
| 185 |
+
|
| 186 |
+
async function replySplitMessage(replyMessage, content) {
|
| 187 |
+
const responseMessages = splitText(content, 2000).map(content => ({ content }));
|
| 188 |
+
|
| 189 |
+
const replyMessages = [];
|
| 190 |
+
for (let i = 0; i < responseMessages.length; ++i) {
|
| 191 |
+
if (i == 0) {
|
| 192 |
+
replyMessages.push(await replyMessage.reply(responseMessages[i]));
|
| 193 |
+
} else {
|
| 194 |
+
replyMessages.push(await replyMessage.channel.send(responseMessages[i]));
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
return replyMessages;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
client.on(Events.MessageCreate, async message => {
|
| 201 |
+
let typing = false;
|
| 202 |
+
try {
|
| 203 |
+
await message.fetch();
|
| 204 |
+
|
| 205 |
+
// return if not in the right channel
|
| 206 |
+
const channelID = message.channel.id;
|
| 207 |
+
if (message.guild && !channels.includes(channelID)) return;
|
| 208 |
+
|
| 209 |
+
// return if user is a bot, or non-default message
|
| 210 |
+
if (!message.author.id) return;
|
| 211 |
+
if (message.author.bot || message.author.id == client.user.id) return;
|
| 212 |
+
|
| 213 |
+
const botRole = message.guild?.members?.me?.roles?.botRole;
|
| 214 |
+
const myMention = new RegExp(`<@((!?${client.user.id}${botRole ? `)|(&${botRole.id}` : ""}))>`, "g"); // RegExp to match a mention for the bot
|
| 215 |
+
|
| 216 |
+
if (typeof message.content !== "string" || message.content.length == 0) {
|
| 217 |
+
return;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
let context = null;
|
| 221 |
+
if (message.type == MessageType.Reply) {
|
| 222 |
+
const reply = await message.fetchReference();
|
| 223 |
+
if (!reply) return;
|
| 224 |
+
if (reply.author.id != client.user.id) return;
|
| 225 |
+
if (messages[channelID] == null) return;
|
| 226 |
+
if ((context = messages[channelID][reply.id]) == null) return;
|
| 227 |
+
} else if (message.type != MessageType.Default) {
|
| 228 |
+
return;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
// fetch info about the model like the template and system message
|
| 232 |
+
if (modelInfo == null) {
|
| 233 |
+
modelInfo = (await makeRequest("/api/show", "post", {
|
| 234 |
+
name: model
|
| 235 |
+
}));
|
| 236 |
+
if (typeof modelInfo === "string") modelInfo = JSON.parse(modelInfo);
|
| 237 |
+
if (typeof modelInfo !== "object") throw "failed to fetch model information";
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
const systemMessages = [];
|
| 241 |
+
|
| 242 |
+
if (useModelSystemMessage && modelInfo.system) {
|
| 243 |
+
systemMessages.push(modelInfo.system);
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
if (useCustomSystemMessage) {
|
| 247 |
+
systemMessages.push(customSystemMessage);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
// join them together
|
| 251 |
+
const systemMessage = systemMessages.join("\n\n");
|
| 252 |
+
|
| 253 |
+
// deal with commands first before passing to LLM
|
| 254 |
+
let userInput = message.content
|
| 255 |
+
.replace(new RegExp("^\s*" + myMention.source, ""), "").trim();
|
| 256 |
+
|
| 257 |
+
// may change this to slash commands in the future
|
| 258 |
+
// i'm using regular text commands currently because the bot interacts with text content anyway
|
| 259 |
+
if (userInput.startsWith(".")) {
|
| 260 |
+
const args = userInput.substring(1).split(/\s+/g);
|
| 261 |
+
const cmd = args.shift();
|
| 262 |
+
switch (cmd) {
|
| 263 |
+
case "reset":
|
| 264 |
+
case "clear":
|
| 265 |
+
if (messages[channelID] != null) {
|
| 266 |
+
// reset conversation
|
| 267 |
+
const cleared = messages[channelID].amount;
|
| 268 |
+
|
| 269 |
+
// clear
|
| 270 |
+
delete messages[channelID];
|
| 271 |
+
|
| 272 |
+
if (cleared > 0) {
|
| 273 |
+
await message.reply({ content: `Cleared conversation of ${cleared} messages` });
|
| 274 |
+
break;
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
await message.reply({ content: "No messages to clear" });
|
| 278 |
+
break;
|
| 279 |
+
case "help":
|
| 280 |
+
case "?":
|
| 281 |
+
case "h":
|
| 282 |
+
await message.reply({ content: "Commands:\n- `.reset` `.clear`\n- `.help` `.?` `.h`\n- `.ping`\n- `.model`\n- `.system`" });
|
| 283 |
+
break;
|
| 284 |
+
case "model":
|
| 285 |
+
await message.reply({
|
| 286 |
+
content: `Current model: ${model}`
|
| 287 |
+
});
|
| 288 |
+
break;
|
| 289 |
+
case "system":
|
| 290 |
+
await replySplitMessage(message, `System message:\n\n${systemMessage}`);
|
| 291 |
+
break;
|
| 292 |
+
case "ping":
|
| 293 |
+
// get ms difference
|
| 294 |
+
const beforeTime = Date.now();
|
| 295 |
+
const reply = await message.reply({ content: "Ping" });
|
| 296 |
+
const afterTime = Date.now();
|
| 297 |
+
const difference = afterTime - beforeTime;
|
| 298 |
+
await reply.edit({ content: `Ping: ${difference}ms` });
|
| 299 |
+
break;
|
| 300 |
+
case "":
|
| 301 |
+
break;
|
| 302 |
+
default:
|
| 303 |
+
await message.reply({ content: "Unknown command, type `.help` for a list of commands" });
|
| 304 |
+
break;
|
| 305 |
+
}
|
| 306 |
+
return;
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
if (message.type == MessageType.Default && (requiresMention && message.guild && !message.content.match(myMention))) return;
|
| 310 |
+
|
| 311 |
+
if (message.guild) {
|
| 312 |
+
await message.guild.channels.fetch();
|
| 313 |
+
await message.guild.members.fetch();
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
userInput = userInput
|
| 317 |
+
.replace(myMention, "")
|
| 318 |
+
.replace(/<#([0-9]+)>/g, (_, id) => {
|
| 319 |
+
if (message.guild) {
|
| 320 |
+
const chn = message.guild.channels.cache.get(id);
|
| 321 |
+
if (chn) return `#${chn.name}`;
|
| 322 |
+
}
|
| 323 |
+
return "#unknown-channel";
|
| 324 |
+
})
|
| 325 |
+
.replace(/<@!?([0-9]+)>/g, (_, id) => {
|
| 326 |
+
if (id == message.author.id) return message.author.username;
|
| 327 |
+
if (message.guild) {
|
| 328 |
+
const mem = message.guild.members.cache.get(id);
|
| 329 |
+
if (mem) return `@${mem.user.username}`;
|
| 330 |
+
}
|
| 331 |
+
return "@unknown-user";
|
| 332 |
+
})
|
| 333 |
+
.replace(/<:([a-zA-Z0-9_]+):([0-9]+)>/g, (_, name) => {
|
| 334 |
+
return `emoji:${name}:`;
|
| 335 |
+
})
|
| 336 |
+
.trim();
|
| 337 |
+
|
| 338 |
+
if (userInput.length == 0) return;
|
| 339 |
+
|
| 340 |
+
// create conversation
|
| 341 |
+
if (messages[channelID] == null) {
|
| 342 |
+
messages[channelID] = { amount: 0, last: null };
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
// log user's message
|
| 346 |
+
log(LogLevel.Debug, `${message.guild ? `#${message.channel.name}` : "DMs"} - ${message.author.username}: ${userInput}`);
|
| 347 |
+
|
| 348 |
+
// start typing
|
| 349 |
+
typing = true;
|
| 350 |
+
await message.channel.sendTyping();
|
| 351 |
+
let typingInterval = setInterval(async () => {
|
| 352 |
+
try {
|
| 353 |
+
await message.channel.sendTyping();
|
| 354 |
+
} catch (error) {
|
| 355 |
+
if (typingInterval != null) {
|
| 356 |
+
clearInterval(typingInterval);
|
| 357 |
+
}
|
| 358 |
+
typingInterval = null;
|
| 359 |
+
}
|
| 360 |
+
}, 7000);
|
| 361 |
+
|
| 362 |
+
let response;
|
| 363 |
+
try {
|
| 364 |
+
// context if the message is not a reply
|
| 365 |
+
if (context == null) {
|
| 366 |
+
context = messages[channelID].last;
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
if (useInitialPrompt && messages[channelID].amount == 0) {
|
| 370 |
+
userInput = `${initialPrompt}\n\n${userInput}`;
|
| 371 |
+
log(LogLevel.Debug, "Adding initial prompt to message");
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
// make request to model
|
| 375 |
+
response = (await makeRequest("/api/generate", "post", {
|
| 376 |
+
model: model,
|
| 377 |
+
prompt: userInput,
|
| 378 |
+
system: systemMessage,
|
| 379 |
+
context
|
| 380 |
+
}));
|
| 381 |
+
|
| 382 |
+
if (typeof response != "string") {
|
| 383 |
+
log(LogLevel.Debug, response);
|
| 384 |
+
throw new TypeError("response is not a string, this may be an error with ollama");
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
response = response.split("\n").filter(e => !!e).map(e => {
|
| 388 |
+
return JSON.parse(e);
|
| 389 |
+
});
|
| 390 |
+
} catch (error) {
|
| 391 |
+
if (typingInterval != null) {
|
| 392 |
+
clearInterval(typingInterval);
|
| 393 |
+
}
|
| 394 |
+
typingInterval = null;
|
| 395 |
+
throw error;
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
if (typingInterval != null) {
|
| 399 |
+
clearInterval(typingInterval);
|
| 400 |
+
}
|
| 401 |
+
typingInterval = null;
|
| 402 |
+
|
| 403 |
+
let responseText = response.map(e => e.response).filter(e => e != null).join("").trim();
|
| 404 |
+
if (responseText.length == 0) {
|
| 405 |
+
responseText = "(No response)";
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
log(LogLevel.Debug, `Response: ${responseText}`);
|
| 409 |
+
|
| 410 |
+
const prefix = showStartOfConversation && messages[channelID].amount == 0 ?
|
| 411 |
+
"> This is the beginning of the conversation, type `.help` for help.\n\n" : "";
|
| 412 |
+
|
| 413 |
+
// reply (will automatically stop typing)
|
| 414 |
+
const replyMessageIDs = (await replySplitMessage(message, `${prefix}${responseText}`)).map(msg => msg.id);
|
| 415 |
+
|
| 416 |
+
// add response to conversation
|
| 417 |
+
context = response.filter(e => e.done && e.context)[0].context;
|
| 418 |
+
for (let i = 0; i < replyMessageIDs.length; ++i) {
|
| 419 |
+
messages[channelID][replyMessageIDs[i]] = context;
|
| 420 |
+
}
|
| 421 |
+
messages[channelID].last = context;
|
| 422 |
+
++messages[channelID].amount;
|
| 423 |
+
} catch (error) {
|
| 424 |
+
if (typing) {
|
| 425 |
+
try {
|
| 426 |
+
// return error
|
| 427 |
+
await message.reply({ content: "Error, please check the console" });
|
| 428 |
+
} catch (ignored) {}
|
| 429 |
+
}
|
| 430 |
+
logError(error);
|
| 431 |
+
}
|
| 432 |
+
});
|
| 433 |
+
|
| 434 |
+
client.login(process.env.TOKEN);
|
src/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ShardingManager, Events } from "discord.js";
|
| 2 |
+
import path from "node:path";
|
| 3 |
+
import { fileURLToPath } from "node:url";
|
| 4 |
+
import { Logger, LogLevel } from "meklog";
|
| 5 |
+
import dotenv from "dotenv";
|
| 6 |
+
|
| 7 |
+
dotenv.config();
|
| 8 |
+
|
| 9 |
+
const production = process.env.NODE_ENV == "prod" || process.env.NODE_ENV == "production";
|
| 10 |
+
const log = new Logger(production, "Shard Manager");
|
| 11 |
+
|
| 12 |
+
log(LogLevel.Info, "Loading");
|
| 13 |
+
|
| 14 |
+
const filePath = path.join(path.dirname(fileURLToPath(import.meta.url)), "bot.js");
|
| 15 |
+
const manager = new ShardingManager(filePath, { token: process.env.TOKEN });
|
| 16 |
+
|
| 17 |
+
manager.on("shardCreate", async shard => {
|
| 18 |
+
const shardLog = new Logger(production, `Shard #${shard.id}`);
|
| 19 |
+
|
| 20 |
+
shardLog(LogLevel.Info, "Created shard");
|
| 21 |
+
|
| 22 |
+
shard.once(Events.ClientReady, async () => {
|
| 23 |
+
shard.send({ shardID: shard.id, logger: shardLog.data });
|
| 24 |
+
|
| 25 |
+
shardLog(LogLevel.Info, "Shard ready");
|
| 26 |
+
});
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
manager.spawn();
|
| 30 |
+
|