From 87fcfff6dcf9628fae53b6667c23cc3638c35de2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 22 Feb 2026 02:22:16 +0000
Subject: [PATCH 1/7] Initial plan
From f02185d687f74cf0689ae0077ac8dfd5a6ce919f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 22 Feb 2026 02:29:34 +0000
Subject: [PATCH 2/7] Add Slack support with message adapter pattern (resolves
#10)
- Add @slack/bolt dependency for Slack integration
- Create SlackMessageAdapter to normalize Slack events to Discord message interface
- Create slack.js entry point with Socket Mode support
- Add start:slack npm script
- Add Slack reminder daemon
- Update README.md and getting-started.md with Slack setup instructions
Co-authored-by: circa10a <21261388+circa10a@users.noreply.github.com>
---
README.md | 26 +-
docs/getting-started.md | 111 ++-
lib/adapters/slack.js | 139 ++++
package-lock.json | 1551 +++++++++++++++++++++++++++++++++++++--
package.json | 8 +-
slack.js | 117 +++
6 files changed, 1857 insertions(+), 95 deletions(-)
create mode 100644 lib/adapters/slack.js
create mode 100644 slack.js
diff --git a/README.md b/README.md
index e5241fd..e83888b 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](https://discord.com/api/oauth2/authorize?client_id=876487225716662302&permissions=34359863296&scope=bot)

-A discord implementation of the famous ava bot
+A discord and slack implementation of the famous ava bot
@@ -13,18 +13,32 @@ A discord implementation of the famous ava bot
## Usage
+### Discord
+
```bash
export AVA_DISCORD_TOKEN=
npm i
npm start
```
+### Slack
+
+```bash
+export AVA_SLACK_BOT_TOKEN=
+export AVA_SLACK_APP_TOKEN=
+npm i
+npm run start:slack
+```
+
## Config
| | | |
|----------------------------|----------|---------|
| Environment Variable | Required | Default |
-| `AVA_DISCORD_TOKEN` | Yes | `""` |
+| `AVA_DISCORD_TOKEN` | Yes (Discord) | `""` |
+| `AVA_SLACK_BOT_TOKEN` | Yes (Slack) | `""` |
+| `AVA_SLACK_APP_TOKEN` | Yes (Slack) | `""` |
+| `AVA_SLACK_SIGNING_SECRET` | No | `""` |
| `AVA_DB_DIR` | No | `./` |
| `AVA_ENABLE_REMINDERS` | No | `false` |
| `AVA_REDDIT_CLIENT_ID` | No | `""` |
@@ -65,10 +79,18 @@ npm start
## Docker
+### Discord
+
```bash
docker run -e AVA_DISCORD_TOKEN="" circa10a/ava
```
+### Slack
+
+```bash
+docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava npm run start:slack
+```
+
## Contribution
We welcome all contributions! Please visit the [contribution documentation](docs/CONTRIBUTION.md) to get started.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index e3ca6db..ba40910 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -2,16 +2,19 @@
This guide will help you get started with local development.
-In this guide, you will start a local server instance of Ava, create a Discord test bot, lastly, test Ava commands through the test bot in a test channel.
+In this guide, you will start a local server instance of Ava for either Discord or Slack.
## Prerequisites
* [Node.js](https://nodejs.org/en/) v16 or greater
-* [Discord](https://discord.com/) account
-* [A Discord channel](https://blog.discord.com/starting-your-first-discord-server-4dcacda8dad5)
-* [Docker](https://www.docker.com/products/docker-desktop)
+* [Docker](https://www.docker.com/products/docker-desktop) (optional)
+* A [Discord](https://discord.com/) account **or** a [Slack](https://slack.com/) workspace
-## Create Test bot
+---
+
+## Discord Setup
+
+### Create Test bot
Follow the steps in this [tutorial](https://www.freecodecamp.org/news/create-a-discord-bot-with-python/) to create an Ava test bot.
@@ -33,64 +36,110 @@ The next page will take you to the bot channel invite page. Go ahead and select

-## Bot Token
+### Bot Token
The bot token can be found on your test application's bot page. You will need the token value for the next step.

-## Start the local server
+### Start the local server
There are two methods for starting Ava locally. You can use Docker or run it as a node.js process.
-### Docker
+#### Docker
-This project creates a Docker image that contains the application logic to run Ava locally. Issue the command below to spin up a docker container running Ava.
+```shell
+docker run -e AVA_DISCORD_TOKEN="" circa10a/ava
+```
-**NOTE**: Ensure you provide a Discord token.
+#### Non-Docker
```shell
-docker run -e AVA_DISCORD_TOKEN="" circa10a/ava
+npm run start
```
You should see a message when the server is up and running.
+```
+[INFO] Ready! Logged in as ...
+```
-```shell
- docker run -e AVA_DISCORD_TOKEN=$AVA_DISCORD_TOKEN circa10a/ava
+### Verify Ava (Discord)
-> ava@1.0.0 start
-> node ./index.js
+Go visit your Discord test channel to verify the test bot is operational.
-[INFO] Ready!
-```
+Go ahead and issue the `ava help` command in the channel. If everything is working correctly, you should see a reply containing all the available Ava commands.
+
+
+
+---
+
+## Slack Setup
+
+### Create a Slack App
+
+1. Go to [api.slack.com/apps](https://api.slack.com/apps) and click **Create New App**.
+2. Choose **From scratch**, give it a name (e.g. `Ava`) and select your workspace.
+
+### Enable Socket Mode
-### Non-Docker
+1. In your app settings, go to **Socket Mode** and enable it.
+2. Generate an **App-Level Token** with the `connections:write` scope. Save this token — it is the `AVA_SLACK_APP_TOKEN` (starts with `xapp-`).
-Ava can run locally as a node.js process. To start Ava as a node.js process, issue the command into your terminal. You may have to issue `npm install` prior to starting the server if you have not done so prior.
+### Configure Bot Token Scopes
-**NOTE:** The command must be issued in the project root.
+Go to **OAuth & Permissions** and add the following **Bot Token Scopes**:
+
+- `chat:write` — Send messages
+- `channels:history` — Read messages in public channels
+- `groups:history` — Read messages in private channels
+- `im:history` — Read direct messages
+- `mpim:history` — Read group direct messages
+
+### Enable Event Subscriptions
+
+Go to **Event Subscriptions** and enable events. Under **Subscribe to bot events**, add:
+
+- `message.channels`
+- `message.groups`
+- `message.im`
+- `message.mpim`
+
+### Install to Workspace
+
+Go to **Install App** and install it to your workspace. Copy the **Bot User OAuth Token** — this is the `AVA_SLACK_BOT_TOKEN` (starts with `xoxb-`).
+
+### Invite the Bot
+
+Invite the bot to a channel by typing `/invite @Ava` in the channel.
+
+### Start the local server
+
+#### Docker
```shell
-npm run start
+docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava npm run start:slack
```
-You should see a message when the server is up and running.
-```
-> ava@1.0.0 start
-> node ./index.js
+#### Non-Docker
-[INFO] Ready!
+```shell
+export AVA_SLACK_BOT_TOKEN=
+export AVA_SLACK_APP_TOKEN=
+npm run start:slack
```
-## Verify Ava
+You should see a message when the server is up and running.
+```
+[INFO] ⚡️ Ava Slack bot is running!
+```
-Go vist your Discord test channel to verify the test bot is operational.
+### Verify Ava (Slack)
-Go ahead and issue the `ava list` command in the channel. If everything is working correctly, you should see a reply containing all the available Ava commands.
+Go to the Slack channel where you invited the bot and type `ava help`. If everything is working correctly, you should see a reply containing all the available Ava commands.
-
+---
## Closing
-If Ava returned all the commands then congratulations. You now have a local test server, and an ava test bot to verify new commands.
+If Ava returned all the commands then congratulations. You now have a local test server and an Ava test bot to verify new commands.
diff --git a/lib/adapters/slack.js b/lib/adapters/slack.js
new file mode 100644
index 0000000..8161e9b
--- /dev/null
+++ b/lib/adapters/slack.js
@@ -0,0 +1,139 @@
+import logger from '../logger/logger.js';
+
+/**
+ * Converts a Discord EmbedBuilder's data to Slack Block Kit blocks.
+ * This allows commands that use Discord embeds to work with Slack.
+ */
+const embedToBlocks = (embed) => {
+ const data = embed.data || embed;
+ const blocks = [];
+ const textParts = [];
+
+ // Title (with optional URL)
+ if (data.title) {
+ const titleText = data.url ? `<${data.url}|${data.title}>` : `*${data.title}*`;
+ blocks.push({
+ type: 'section',
+ text: { type: 'mrkdwn', text: titleText },
+ });
+ textParts.push(data.title);
+ }
+
+ // Description with optional thumbnail
+ if (data.description) {
+ const section = {
+ type: 'section',
+ text: { type: 'mrkdwn', text: data.description },
+ };
+ if (data.thumbnail && data.thumbnail.url) {
+ section.accessory = {
+ type: 'image',
+ image_url: data.thumbnail.url,
+ alt_text: data.title || 'thumbnail',
+ };
+ }
+ blocks.push(section);
+ textParts.push(data.description);
+ } else if (data.thumbnail && data.thumbnail.url) {
+ blocks.push({
+ type: 'image',
+ image_url: data.thumbnail.url,
+ alt_text: data.title || 'thumbnail',
+ });
+ }
+
+ // Fields
+ if (data.fields && data.fields.length > 0) {
+ const fields = data.fields.map((f) => ({
+ type: 'mrkdwn',
+ text: `*${f.name}*\n${f.value}`,
+ }));
+ // Slack allows max 10 fields per section
+ for (let i = 0; i < fields.length; i += 10) {
+ blocks.push({
+ type: 'section',
+ fields: fields.slice(i, i + 10),
+ });
+ }
+ }
+
+ // Image
+ if (data.image && data.image.url) {
+ blocks.push({
+ type: 'image',
+ image_url: data.image.url,
+ alt_text: data.title || 'image',
+ });
+ }
+
+ return { blocks, fallbackText: textParts.join('\n') || 'Message from Ava' };
+};
+
+/**
+ * Adapts a Slack message event to match the Discord message interface
+ * used by all existing commands. This allows command logic to be shared
+ * between Discord and Slack without modifying command files.
+ */
+class SlackMessageAdapter {
+ constructor({ event, client }) {
+ this._client = client;
+ this._event = event;
+ this._threadTs = event.thread_ts || event.ts;
+
+ this.content = event.text || '';
+ this.channelId = event.channel;
+ this.guildId = event.team;
+ this.id = event.ts;
+ this.createdTimestamp = Math.floor(parseFloat(event.ts) * 1000);
+
+ const userId = event.user;
+ this.author = {
+ username: userId,
+ toString: () => `<@${userId}>`,
+ };
+
+ // Provide channel.send() to mirror Discord's message.channel.send()
+ this.channel = {
+ send: async (content) => {
+ try {
+ if (typeof content === 'string') {
+ await client.chat.postMessage({
+ channel: event.channel,
+ text: content,
+ });
+ } else if (content && content.embeds) {
+ const allBlocks = [];
+ let fallback = '';
+ for (const embed of content.embeds) {
+ const { blocks, fallbackText } = embedToBlocks(embed);
+ allBlocks.push(...blocks);
+ fallback += fallbackText;
+ }
+ await client.chat.postMessage({
+ channel: event.channel,
+ blocks: allBlocks,
+ text: fallback,
+ });
+ }
+ } catch (e) {
+ logger.error(`Slack channel.send error: ${e}`);
+ }
+ },
+ };
+ }
+
+ // Mirror Discord's message.reply() - posts as a threaded reply in Slack
+ async reply(content) {
+ try {
+ await this._client.chat.postMessage({
+ channel: this._event.channel,
+ thread_ts: this._threadTs,
+ text: content,
+ });
+ } catch (e) {
+ logger.error(`Slack reply error: ${e}`);
+ }
+ }
+}
+
+export { SlackMessageAdapter, embedToBlocks };
diff --git a/package-lock.json b/package-lock.json
index 4a32b3c..5f7c873 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,14 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
+ "@eslint/eslintrc": "^3.3.3",
+ "@eslint/js": "^10.0.1",
+ "@slack/bolt": "^4.1.1",
"8ball": "^1.0.6",
"chrono-node": "^2.9.0",
"dammit": "^0.5.1",
"discord.js": "^14.25.1",
+ "globals": "^17.3.0",
"html-entities": "^2.6.0",
"image-search-engine": "^1.2.1",
"lowdb": "^7.0.1",
@@ -185,7 +189,7 @@
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"eslint-visitor-keys": "^3.4.3"
@@ -204,7 +208,7 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -217,7 +221,7 @@
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
@@ -227,7 +231,7 @@
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz",
"integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^3.0.2",
@@ -242,7 +246,7 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz",
"integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^1.1.0"
@@ -255,7 +259,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz",
"integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
@@ -264,11 +268,123 @@
"node": "^20.19.0 || ^22.13.0 || >=24"
}
},
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
+ "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "eslint": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@eslint/object-schema": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz",
"integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -278,7 +394,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz",
"integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^1.1.0",
@@ -292,7 +408,7 @@
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
@@ -302,7 +418,7 @@
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
@@ -316,7 +432,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.22"
@@ -330,7 +446,7 @@
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
@@ -437,6 +553,165 @@
"npm": ">=7.0.0"
}
},
+ "node_modules/@slack/bolt": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.1.1.tgz",
+ "integrity": "sha512-Sc8QUnEHCPgRlwrMxmvOl4Vhr/7ZBDXvLR0ir6TO+W5MaDtPsrmWLxqLmb+OhCGSjDp3d4AxO6jdFlC3yNKtsg==",
+ "license": "MIT",
+ "dependencies": {
+ "@slack/logger": "^4.0.0",
+ "@slack/oauth": "^3",
+ "@slack/socket-mode": "^2.0.2",
+ "@slack/types": "^2.13.0",
+ "@slack/web-api": "^7",
+ "@types/express": "^4.17.21",
+ "axios": "^1.7.4",
+ "express": "^5.0.0",
+ "path-to-regexp": "^8.1.0",
+ "raw-body": "^3",
+ "tsscmp": "^1.0.6"
+ },
+ "engines": {
+ "node": ">=18",
+ "npm": ">=8.6.0"
+ }
+ },
+ "node_modules/@slack/bolt/node_modules/axios": {
+ "version": "1.13.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
+ "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/@slack/bolt/node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@slack/logger": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz",
+ "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": ">=18.0.0"
+ },
+ "engines": {
+ "node": ">= 18",
+ "npm": ">= 8.6.0"
+ }
+ },
+ "node_modules/@slack/oauth": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.4.tgz",
+ "integrity": "sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==",
+ "license": "MIT",
+ "dependencies": {
+ "@slack/logger": "^4",
+ "@slack/web-api": "^7.10.0",
+ "@types/jsonwebtoken": "^9",
+ "@types/node": ">=18",
+ "jsonwebtoken": "^9"
+ },
+ "engines": {
+ "node": ">=18",
+ "npm": ">=8.6.0"
+ }
+ },
+ "node_modules/@slack/socket-mode": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-2.0.5.tgz",
+ "integrity": "sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@slack/logger": "^4",
+ "@slack/web-api": "^7.10.0",
+ "@types/node": ">=18",
+ "@types/ws": "^8",
+ "eventemitter3": "^5",
+ "ws": "^8"
+ },
+ "engines": {
+ "node": ">= 18",
+ "npm": ">= 8.6.0"
+ }
+ },
+ "node_modules/@slack/types": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.20.0.tgz",
+ "integrity": "sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0",
+ "npm": ">= 6.12.0"
+ }
+ },
+ "node_modules/@slack/web-api": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.14.1.tgz",
+ "integrity": "sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==",
+ "license": "MIT",
+ "dependencies": {
+ "@slack/logger": "^4.0.0",
+ "@slack/types": "^2.20.0",
+ "@types/node": ">=18.0.0",
+ "@types/retry": "0.12.0",
+ "axios": "^1.13.5",
+ "eventemitter3": "^5.0.1",
+ "form-data": "^4.0.4",
+ "is-electron": "2.2.2",
+ "is-stream": "^2",
+ "p-queue": "^6",
+ "p-retry": "^4",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">= 18",
+ "npm": ">= 8.6.0"
+ }
+ },
+ "node_modules/@slack/web-api/node_modules/axios": {
+ "version": "1.13.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
+ "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/@slack/web-api/node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/@so-ric/colorspace": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
@@ -447,25 +722,96 @@
"text-hex": "1.0.x"
}
},
+ "node_modules/@types/body-parser": {
+ "version": "1.19.6",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/esrecurse": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
"integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "dev": true,
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.25",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
+ "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.33",
+ "@types/qs": "*",
+ "@types/serve-static": "^1"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.19.8",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz",
+ "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.10",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+ "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"license": "MIT"
},
"node_modules/@types/node": {
@@ -477,6 +823,54 @@
"undici-types": "~7.16.0"
}
},
+ "node_modules/@types/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
+ "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "<1"
+ }
+ },
+ "node_modules/@types/serve-static/node_modules/@types/send": {
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
+ "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/triple-beam": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
@@ -515,12 +909,50 @@
"node": ">=4"
}
},
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/acorn": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
- "dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -532,7 +964,6 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -576,6 +1007,12 @@
"node": ">=0.2.6"
}
},
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@@ -640,7 +1077,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz",
"integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": "20 || >=22"
@@ -661,6 +1098,45 @@
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"license": "MIT"
},
+ "node_modules/body-parser": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+ "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.7.0",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.1",
+ "raw-body": "^3.0.1",
+ "type-is": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/body-parser/node_modules/qs": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
+ "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -671,7 +1147,7 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
@@ -680,6 +1156,21 @@
"node": "20 || >=22"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -727,6 +1218,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/camelcase": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
@@ -935,6 +1435,52 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
+ "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -974,7 +1520,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -1121,7 +1667,6 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1139,7 +1684,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/define-data-property": {
@@ -1168,6 +1713,15 @@
"node": ">=0.4.0"
}
},
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/discord-api-types": {
"version": "0.38.37",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.37.tgz",
@@ -1288,12 +1842,36 @@
"safer-buffer": "^2.1.0"
}
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
"node_modules/enabled": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
"license": "MIT"
},
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
@@ -1333,11 +1911,32 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -1350,8 +1949,9 @@
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.1.tgz",
"integrity": "sha512-20MV9SUdeN6Jd84xESsKhRly+/vxI+hwvpBMA93s+9dAcjdCuCojn4IqUGS3lvVaqjVYGYHSRMCpeFtF2rQYxQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2",
@@ -1422,7 +2022,7 @@
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz",
"integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"@types/esrecurse": "^4.3.1",
@@ -1441,7 +2041,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -1454,7 +2054,7 @@
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz",
"integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.16.0",
@@ -1472,7 +2072,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-3-Clause",
"dependencies": {
"estraverse": "^5.1.0"
@@ -1485,7 +2085,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
@@ -1498,7 +2098,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -1508,12 +2108,110 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
+ "node_modules/express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/qs": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
+ "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -1545,7 +2243,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/fecha": {
@@ -1581,7 +2279,7 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"flat-cache": "^4.0.0"
@@ -1590,11 +2288,32 @@
"node": ">=16.0.0"
}
},
+ "node_modules/finalhandler": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+ "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
@@ -1611,7 +2330,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
@@ -1625,7 +2344,7 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
- "dev": true,
+ "devOptional": true,
"license": "ISC"
},
"node_modules/fn.name": {
@@ -1689,6 +2408,24 @@
"node": ">=12.20.0"
}
},
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
@@ -1766,7 +2503,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
+ "devOptional": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
@@ -1785,6 +2522,18 @@
"process": "^0.11.10"
}
},
+ "node_modules/globals": {
+ "version": "17.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
+ "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1850,6 +2599,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -1906,6 +2670,32 @@
"entities": "^2.0.0"
}
},
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/http-errors/node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -1931,11 +2721,26 @@
"npm": ">=5.3.0"
}
},
+ "node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
@@ -1973,11 +2778,27 @@
}
}
},
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.8.19"
@@ -2002,11 +2823,26 @@
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
"license": "ISC"
},
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-electron": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz",
+ "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==",
+ "license": "MIT"
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -2016,7 +2852,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -2025,6 +2861,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "license": "MIT"
+ },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -2053,7 +2895,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
+ "devOptional": true,
"license": "ISC"
},
"node_modules/isstream": {
@@ -2062,6 +2904,18 @@
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"license": "MIT"
},
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@@ -2072,7 +2926,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/json-schema": {
@@ -2110,7 +2964,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/json-stringify-safe": {
@@ -2128,6 +2982,28 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@@ -2143,11 +3019,32 @@
"node": ">=0.6.0"
}
},
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
@@ -2163,7 +3060,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1",
@@ -2177,7 +3074,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
@@ -2231,6 +3128,42 @@
"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
"license": "MIT"
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
"node_modules/lodash.map": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
@@ -2243,6 +3176,12 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"license": "MIT"
},
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
"node_modules/lodash.pick": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
@@ -2327,6 +3266,27 @@
"node": ">= 0.4"
}
},
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -2361,7 +3321,7 @@
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz",
"integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
- "dev": true,
+ "devOptional": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
@@ -2408,9 +3368,18 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -2490,6 +3459,18 @@
"node": "*"
}
},
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -2499,6 +3480,27 @@
"node": ">= 0.4"
}
},
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
"node_modules/one-time": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
@@ -2528,7 +3530,7 @@
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"deep-is": "^0.1.3",
@@ -2542,11 +3544,20 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
@@ -2562,7 +3573,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
@@ -2574,6 +3585,74 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/p-queue": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
+ "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^4.0.4",
+ "p-timeout": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue/node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "license": "MIT"
+ },
+ "node_modules/p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
+ "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+ "license": "MIT",
+ "dependencies": {
+ "p-finally": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/path": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
@@ -2588,7 +3667,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -2598,12 +3677,22 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
+ "node_modules/path-to-regexp": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
+ "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -2614,7 +3703,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 0.8.0"
@@ -2662,6 +3751,25 @@
"node": ">=12.0.0"
}
},
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
@@ -2704,6 +3812,30 @@
"node": ">=14.18.0"
}
},
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -2724,6 +3856,7 @@
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
@@ -2784,6 +3917,40 @@
"request": "^2.34"
}
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -2819,6 +3986,88 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.1",
+ "mime-types": "^3.0.2",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/send/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/send/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -2836,11 +4085,17 @@
"node": ">= 0.4"
}
},
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -2853,12 +4108,84 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/snoowrap": {
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/snoowrap/-/snoowrap-1.23.0.tgz",
@@ -2927,6 +4254,15 @@
"node": "*"
}
},
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
@@ -2957,6 +4293,18 @@
"safe-buffer": "~5.2.0"
}
},
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/sylvester": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.12.tgz",
@@ -2971,6 +4319,15 @@
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
"license": "MIT"
},
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -3011,6 +4368,15 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
+ "node_modules/tsscmp": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+ "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.x"
+ }
+ },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -3033,7 +4399,7 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1"
@@ -3042,6 +4408,45 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
@@ -3092,6 +4497,15 @@
"node": ">=0.8.0"
}
},
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -3137,6 +4551,15 @@
"uuid": "bin/uuid"
}
},
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
@@ -3180,7 +4603,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
+ "devOptional": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -3250,7 +4673,7 @@
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3292,6 +4715,12 @@
"node": ">=0.4.0"
}
},
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
@@ -3326,7 +4755,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
diff --git a/package.json b/package.json
index fc51264..06f7ba7 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,13 @@
{
"dependencies": {
+ "@eslint/eslintrc": "^3.3.3",
+ "@eslint/js": "^10.0.1",
+ "@slack/bolt": "^4.1.1",
"8ball": "^1.0.6",
"chrono-node": "^2.9.0",
"dammit": "^0.5.1",
"discord.js": "^14.25.1",
+ "globals": "^17.3.0",
"html-entities": "^2.6.0",
"image-search-engine": "^1.2.1",
"lowdb": "^7.0.1",
@@ -19,7 +23,7 @@
},
"name": "ava",
"version": "1.0.0",
- "description": "OSS Ava discord bot",
+ "description": "OSS Ava discord and slack bot",
"main": "./index.js",
"type": "module",
"devDependencies": {
@@ -30,6 +34,7 @@
"lint": "eslint .",
"fix-lint": "eslint . --fix",
"start": "node --no-deprecation ./index.js",
+ "start:slack": "node --no-deprecation ./slack.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
@@ -38,6 +43,7 @@
},
"keywords": [
"discord",
+ "slack",
"bot"
],
"author": "circa10a",
diff --git a/slack.js b/slack.js
new file mode 100644
index 0000000..47f0e75
--- /dev/null
+++ b/slack.js
@@ -0,0 +1,117 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import pkg from '@slack/bolt';
+const { App } = pkg;
+
+import logger from './lib/logger/logger.js';
+import { commandsDir, dbDir, avaPrefix } from './config/config.js';
+import { initDB } from './lib/db/db.js';
+import { SlackMessageAdapter } from './lib/adapters/slack.js';
+import { sleep } from './lib/utils/utils.js';
+
+// Remindme is a special case where we need to pass in the db
+import remindMe from './commands/remindme.js';
+
+const { AVA_SLACK_BOT_TOKEN, AVA_SLACK_APP_TOKEN, AVA_SLACK_SIGNING_SECRET, AVA_ENABLE_REMINDERS } = process.env;
+
+logger.info(`Node version: ${process.version}`);
+
+if (!AVA_SLACK_BOT_TOKEN || !AVA_SLACK_APP_TOKEN) {
+ logger.error('Missing AVA_SLACK_BOT_TOKEN or AVA_SLACK_APP_TOKEN environment variables');
+ process.exit(1);
+}
+
+const app = new App({
+ token: AVA_SLACK_BOT_TOKEN,
+ appToken: AVA_SLACK_APP_TOKEN,
+ signingSecret: AVA_SLACK_SIGNING_SECRET || '',
+ socketMode: true,
+});
+
+const startSlackReminderDaemon = async (opts = {}) => {
+ const { db } = opts;
+ const checkInterval = 300000; // 5m
+ for (;;) {
+ await sleep(checkInterval);
+
+ await db.read();
+ const reminders = db.data.reminders;
+
+ for (let i = 0; i < reminders.length; i++) {
+ const reminder = reminders[i];
+ const remindTime = Date.parse(reminder.remindTime);
+ const now = new Date();
+
+ if (remindTime < now) {
+ try {
+ logger.debug(`Sending reminder to ${reminder.user}`);
+ await app.client.chat.postMessage({
+ channel: reminder.channelId,
+ thread_ts: reminder.messageId,
+ text: 'Here\'s your reminder',
+ });
+ reminders.splice(i, 1);
+ await db.write();
+ await sleep(100);
+ } catch (e) {
+ logger.error(`Reminder error: ${e}`);
+ if (e.data && e.data.error === 'channel_not_found') {
+ logger.error(`Unable to access channel for reminder from ${reminder.user}. Deleting...`);
+ reminders.splice(i, 1);
+ await db.write();
+ }
+ }
+ }
+ }
+ }
+};
+
+(async () => {
+ // Dynamically load all command modules
+ const commands = [];
+ const eventFiles = fs.readdirSync(`./${commandsDir}`).filter(file => file.endsWith('.js') && !file.startsWith('remindme'));
+
+ for (const file of eventFiles) {
+ const module = await import(`./${commandsDir}/${file}`);
+ commands.push(module.default);
+ }
+
+ // Handle reminders if enabled
+ if (AVA_ENABLE_REMINDERS) {
+ const dbFile = path.join(dbDir, 'db.json');
+ logger.info(`Reminders enabled. Initializing database at ${dbFile}`);
+
+ try {
+ const db = await initDB({ file: dbFile });
+ const remindMeCommand = remindMe({ db });
+ commands.push(remindMeCommand);
+
+ startSlackReminderDaemon({ db }).catch((e) => {
+ logger.error(e);
+ });
+ } catch (e) {
+ logger.error(e);
+ process.exit(1);
+ }
+ }
+
+ // Listen for messages starting with the ava prefix
+ app.message(new RegExp(`^${avaPrefix}`, 'i'), async ({ message, client }) => {
+ // Ignore bot messages to prevent loops
+ if (message.subtype === 'bot_message' || message.bot_id) {
+ return;
+ }
+
+ const adapter = new SlackMessageAdapter({ event: message, client });
+ for (const command of commands) {
+ try {
+ await command.execute(adapter);
+ } catch (e) {
+ logger.error(`Command execution error: ${e}`);
+ }
+ }
+ });
+
+ await app.start();
+ logger.info('⚡️ Ava Slack bot is running!');
+})();
From 7134b1864fe5fb021f6887a6ea378c2bf318526f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 22 Feb 2026 02:31:25 +0000
Subject: [PATCH 3/7] Move eslint peer deps to devDependencies
Co-authored-by: circa10a <21261388+circa10a@users.noreply.github.com>
---
package-lock.json | 142 ++++++++++++++++++++++++++--------------------
package.json | 8 +--
2 files changed, 85 insertions(+), 65 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 5f7c873..2994e0a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,14 +9,11 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
- "@eslint/eslintrc": "^3.3.3",
- "@eslint/js": "^10.0.1",
"@slack/bolt": "^4.1.1",
"8ball": "^1.0.6",
"chrono-node": "^2.9.0",
"dammit": "^0.5.1",
"discord.js": "^14.25.1",
- "globals": "^17.3.0",
"html-entities": "^2.6.0",
"image-search-engine": "^1.2.1",
"lowdb": "^7.0.1",
@@ -31,8 +28,11 @@
"winston": "^3.19.0"
},
"devDependencies": {
+ "@eslint/eslintrc": "^3.3.3",
+ "@eslint/js": "^10.0.1",
"eslint": "^10.0.1",
- "eslint-plugin-unused-imports": "^4.4.1"
+ "eslint-plugin-unused-imports": "^4.4.1",
+ "globals": "^17.3.0"
}
},
"node_modules/@colors/colors": {
@@ -189,7 +189,7 @@
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"eslint-visitor-keys": "^3.4.3"
@@ -208,7 +208,7 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -221,7 +221,7 @@
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
@@ -231,7 +231,7 @@
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz",
"integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^3.0.2",
@@ -246,7 +246,7 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz",
"integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^1.1.0"
@@ -259,7 +259,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz",
"integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
@@ -272,6 +272,7 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
@@ -295,12 +296,14 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -311,6 +314,7 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -323,6 +327,7 @@
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.15.0",
@@ -340,6 +345,7 @@
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -352,6 +358,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -364,6 +371,7 @@
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
"integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -384,7 +392,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz",
"integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -394,7 +402,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz",
"integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^1.1.0",
@@ -408,7 +416,7 @@
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
@@ -418,7 +426,7 @@
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
@@ -432,7 +440,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.22"
@@ -446,7 +454,7 @@
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
@@ -745,14 +753,14 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
"integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/express": {
@@ -789,7 +797,7 @@
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/jsonwebtoken": {
@@ -951,6 +959,7 @@
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "dev": true,
"license": "MIT",
"peer": true,
"bin": {
@@ -964,6 +973,7 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -1011,6 +1021,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
"license": "Python-2.0"
},
"node_modules/asn1": {
@@ -1077,7 +1088,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz",
"integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": "20 || >=22"
@@ -1147,7 +1158,7 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
@@ -1222,6 +1233,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -1439,6 +1451,7 @@
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/content-disposition": {
@@ -1520,7 +1533,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -1684,7 +1697,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/define-data-property": {
@@ -1936,7 +1949,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -1949,7 +1962,7 @@
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.1.tgz",
"integrity": "sha512-20MV9SUdeN6Jd84xESsKhRly+/vxI+hwvpBMA93s+9dAcjdCuCojn4IqUGS3lvVaqjVYGYHSRMCpeFtF2rQYxQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -2022,7 +2035,7 @@
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz",
"integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==",
- "devOptional": true,
+ "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@types/esrecurse": "^4.3.1",
@@ -2041,7 +2054,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -2054,7 +2067,7 @@
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz",
"integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==",
- "devOptional": true,
+ "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.16.0",
@@ -2072,7 +2085,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
- "devOptional": true,
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"estraverse": "^5.1.0"
@@ -2085,7 +2098,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "devOptional": true,
+ "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
@@ -2098,7 +2111,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "devOptional": true,
+ "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -2108,7 +2121,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "devOptional": true,
+ "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -2243,7 +2256,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/fecha": {
@@ -2279,7 +2292,7 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"flat-cache": "^4.0.0"
@@ -2313,7 +2326,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
@@ -2330,7 +2343,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
@@ -2344,7 +2357,7 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
- "devOptional": true,
+ "dev": true,
"license": "ISC"
},
"node_modules/fn.name": {
@@ -2503,7 +2516,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "devOptional": true,
+ "dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
@@ -2526,6 +2539,7 @@
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
"integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -2741,6 +2755,7 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
@@ -2782,6 +2797,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -2798,7 +2814,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.19"
@@ -2842,7 +2858,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -2852,7 +2868,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -2895,7 +2911,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "devOptional": true,
+ "dev": true,
"license": "ISC"
},
"node_modules/isstream": {
@@ -2908,6 +2924,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -2926,7 +2943,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/json-schema": {
@@ -2964,7 +2981,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/json-stringify-safe": {
@@ -3044,7 +3061,7 @@
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
@@ -3060,7 +3077,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1",
@@ -3074,7 +3091,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
@@ -3321,7 +3338,7 @@
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz",
"integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
- "devOptional": true,
+ "dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
@@ -3368,7 +3385,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/negotiator": {
@@ -3530,7 +3547,7 @@
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"deep-is": "^0.1.3",
@@ -3557,7 +3574,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
@@ -3573,7 +3590,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
@@ -3636,6 +3653,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
@@ -3667,7 +3685,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3677,7 +3695,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3703,7 +3721,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8.0"
@@ -3921,6 +3939,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -4095,7 +4114,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -4108,7 +4127,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -4297,6 +4316,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -4399,7 +4419,7 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1"
@@ -4603,7 +4623,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "devOptional": true,
+ "dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -4673,7 +4693,7 @@
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -4755,7 +4775,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
diff --git a/package.json b/package.json
index 06f7ba7..065fb60 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,10 @@
{
"dependencies": {
- "@eslint/eslintrc": "^3.3.3",
- "@eslint/js": "^10.0.1",
"@slack/bolt": "^4.1.1",
"8ball": "^1.0.6",
"chrono-node": "^2.9.0",
"dammit": "^0.5.1",
"discord.js": "^14.25.1",
- "globals": "^17.3.0",
"html-entities": "^2.6.0",
"image-search-engine": "^1.2.1",
"lowdb": "^7.0.1",
@@ -27,8 +24,11 @@
"main": "./index.js",
"type": "module",
"devDependencies": {
+ "@eslint/eslintrc": "^3.3.3",
+ "@eslint/js": "^10.0.1",
"eslint": "^10.0.1",
- "eslint-plugin-unused-imports": "^4.4.1"
+ "eslint-plugin-unused-imports": "^4.4.1",
+ "globals": "^17.3.0"
},
"scripts": {
"lint": "eslint .",
From 61de5ec28abee06c3bb4254d71294e5b8743d918 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 22 Feb 2026 17:16:44 +0000
Subject: [PATCH 4/7] Add Discord adapter, refactor to symmetric adapter
pattern, pre-compile regex, fix lint errors
- Create lib/adapters/discord.js with DiscordMessageAdapter
- Refactor index.js to use adapter pattern matching slack.js architecture
- Pre-compile regex in both slack.js and index.js (no per-message compilation)
- Remove Events import from all 25 command files (decouple from discord.js)
- Fix 6 pre-existing no-useless-assignment lint errors in commands and lib files
Co-authored-by: circa10a <21261388+circa10a@users.noreply.github.com>
---
commands/8ball.js | 3 --
commands/bored.js | 3 --
commands/brewery.js | 4 +--
commands/catfact.js | 3 --
commands/coffee.js | 4 +--
commands/compliment.js | 3 --
commands/contribute.js | 3 --
commands/emoji.js | 3 --
commands/floridaman.js | 5 +--
commands/fuck.js | 3 --
commands/gcp.js | 3 --
commands/help.js | 4 +--
commands/hp.js | 6 ++--
commands/insult.js | 3 --
commands/java.js | 3 --
commands/karen.js | 6 ++--
commands/meme.js | 6 ++--
commands/mock.js | 3 --
commands/recipe.js | 3 --
commands/remindme.js | 3 --
commands/takemehome.js | 3 --
commands/tldr.js | 3 --
commands/video.js | 3 --
commands/whiteclaw.js | 3 --
commands/yeet.js | 3 --
index.js | 68 +++++++++++++++++++++------------------
lib/adapters/discord.js | 23 +++++++++++++
lib/db/db.js | 4 +--
lib/reddit/submissions.js | 2 +-
slack.js | 5 ++-
30 files changed, 77 insertions(+), 114 deletions(-)
create mode 100644 lib/adapters/discord.js
diff --git a/commands/8ball.js b/commands/8ball.js
index c768f92..cbf19a3 100644
--- a/commands/8ball.js
+++ b/commands/8ball.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import eightball from '8ball';
import { messageForAva , splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
import dammit from 'dammit';
@@ -7,8 +6,6 @@ const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/bored.js b/commands/bored.js
index 77f8d19..861fe33 100644
--- a/commands/bored.js
+++ b/commands/bored.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import fetch from 'node-fetch';
import { messageForAva, splitArgs, getFileName} from '../lib/utils/utils.js';
@@ -8,8 +7,6 @@ const boredEndpoint = 'https://boredapi.com/api/activity';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/brewery.js b/commands/brewery.js
index 7ea43e8..c856422 100644
--- a/commands/brewery.js
+++ b/commands/brewery.js
@@ -1,4 +1,4 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { EmbedBuilder } from 'discord.js';
import fetch from 'node-fetch';
import { embedColor } from '../config/config.js';
import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
@@ -10,8 +10,6 @@ const beerThumbnail = 'https://i.imgur.com/6ZKnMaw.jpg';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/catfact.js b/commands/catfact.js
index aff752d..bc8b631 100644
--- a/commands/catfact.js
+++ b/commands/catfact.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import fetch from 'node-fetch';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
@@ -8,8 +7,6 @@ const catFactEndpoint = 'https://catfact.ninja/fact';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/coffee.js b/commands/coffee.js
index 79a735d..361b026 100644
--- a/commands/coffee.js
+++ b/commands/coffee.js
@@ -1,5 +1,5 @@
import { default as wiki } from 'wikijs';
-import { EmbedBuilder, Events } from 'discord.js';
+import { EmbedBuilder } from 'discord.js';
import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
import { embedColor } from '../config/config.js';
@@ -7,8 +7,6 @@ const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/compliment.js b/commands/compliment.js
index f214f9d..ca701b9 100644
--- a/commands/compliment.js
+++ b/commands/compliment.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import fetch from 'node-fetch';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
@@ -8,8 +7,6 @@ const complimentEndpoint = 'https://complimentr.com/api';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/contribute.js b/commands/contribute.js
index 281196f..ffb2ada 100644
--- a/commands/contribute.js
+++ b/commands/contribute.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,8 +6,6 @@ const projectURL = 'https://github.com/circa10a/ava';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/emoji.js b/commands/emoji.js
index 178a4b4..86539cc 100644
--- a/commands/emoji.js
+++ b/commands/emoji.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import imageFinder from 'image-search-engine';
import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
@@ -6,8 +5,6 @@ const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/floridaman.js b/commands/floridaman.js
index 8c085a2..c604590 100644
--- a/commands/floridaman.js
+++ b/commands/floridaman.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
import { getRandomSubmission } from '../lib/reddit/submissions.js';
@@ -8,8 +7,6 @@ const subreddit = 'FloridaMan';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
@@ -19,7 +16,7 @@ export default {
const userCmd = args[1];
if (userCmd === command) {
- let randomSubmission = {};
+ let randomSubmission;
try{
randomSubmission = await getRandomSubmission({subreddit});
} catch(e) {
diff --git a/commands/fuck.js b/commands/fuck.js
index 6df3ad1..79084f3 100644
--- a/commands/fuck.js
+++ b/commands/fuck.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import fetch from 'node-fetch';
import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
import { avaPrefix } from '../config/config.js';
@@ -21,8 +20,6 @@ const avaInsults = [
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async (message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/gcp.js b/commands/gcp.js
index 54ef88d..30f741c 100644
--- a/commands/gcp.js
+++ b/commands/gcp.js
@@ -1,12 +1,9 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getFileName} from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/help.js b/commands/help.js
index db4059f..a6ac3b4 100644
--- a/commands/help.js
+++ b/commands/help.js
@@ -1,4 +1,4 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { EmbedBuilder } from 'discord.js';
import * as fs from 'fs';
import { commandsDir, embedColor } from '../config/config.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
@@ -10,8 +10,6 @@ const availableCommands = eventFiles.map(event => event.replace('.js', ''));
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/hp.js b/commands/hp.js
index df570a0..832b4f8 100644
--- a/commands/hp.js
+++ b/commands/hp.js
@@ -1,4 +1,4 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { EmbedBuilder } from 'discord.js';
import { embedColor } from '../config/config.js';
import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
@@ -10,8 +10,6 @@ const subreddit = 'HarryPotterMemes';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async (message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
@@ -21,7 +19,7 @@ export default {
const userCmd = args[1];
if (userCmd === command) {
- let randomSubmission = {};
+ let randomSubmission;
try {
randomSubmission = await getRandomSubmissionWithImage({subreddit});
} catch(e) {
diff --git a/commands/insult.js b/commands/insult.js
index fa03abc..3208f2b 100644
--- a/commands/insult.js
+++ b/commands/insult.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { decode } from 'html-entities';
import fetch from 'node-fetch';
import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
@@ -9,8 +8,6 @@ const insultEndpoint = 'https://evilinsult.com/generate_insult.php?lang=en&type=
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/java.js b/commands/java.js
index 2f46938..1b23c67 100644
--- a/commands/java.js
+++ b/commands/java.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,8 +6,6 @@ const javaImage = 'https://i.redd.it/o4w97sa7iidz.jpg';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/karen.js b/commands/karen.js
index b93c827..c06ac07 100644
--- a/commands/karen.js
+++ b/commands/karen.js
@@ -1,4 +1,4 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { EmbedBuilder } from 'discord.js';
import { embedColor } from '../config/config.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
@@ -10,8 +10,6 @@ const subreddit = 'FuckYouKaren';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async (message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
@@ -21,7 +19,7 @@ export default {
const userCmd = args[1];
if (userCmd === command) {
- let randomSubmission = {};
+ let randomSubmission;
try {
randomSubmission = await getRandomSubmissionWithImage({subreddit});
} catch(e) {
diff --git a/commands/meme.js b/commands/meme.js
index 89f5314..5a81dc8 100644
--- a/commands/meme.js
+++ b/commands/meme.js
@@ -1,4 +1,4 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { EmbedBuilder } from 'discord.js';
import { embedColor } from '../config/config.js';
import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
@@ -10,8 +10,6 @@ const subreddit = 'dankmemes';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async (message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
@@ -21,7 +19,7 @@ export default {
const userCmd = args[1];
if (userCmd === command) {
- let randomSubmission = {};
+ let randomSubmission;
try {
randomSubmission = await getRandomSubmissionWithImage({subreddit});
} catch(e) {
diff --git a/commands/mock.js b/commands/mock.js
index 2700825..971c24b 100644
--- a/commands/mock.js
+++ b/commands/mock.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -13,8 +12,6 @@ const mockText = (str) => {
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/recipe.js b/commands/recipe.js
index 8d5d5f7..2137475 100644
--- a/commands/recipe.js
+++ b/commands/recipe.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { parse } from 'node-html-parser';
import { randomItemFromArray, messageForAva, getBody, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
@@ -9,8 +8,6 @@ const itemsHTMLClass ='#schema-lifestyle_1-0';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/remindme.js b/commands/remindme.js
index 9593d9d..af7b348 100644
--- a/commands/remindme.js
+++ b/commands/remindme.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import * as chrono from 'chrono-node';
import { messageForAva, splitArgs, getAllArgsAsStr } from '../lib/utils/utils.js';
@@ -7,8 +6,6 @@ const command = 'remindme';
const remindMe = (opts = {}) => {
return {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/takemehome.js b/commands/takemehome.js
index 57de00b..a19d2f6 100644
--- a/commands/takemehome.js
+++ b/commands/takemehome.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { sleep , messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,8 +6,6 @@ const wait = 2000;
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/tldr.js b/commands/tldr.js
index 9337bf1..59e26b1 100644
--- a/commands/tldr.js
+++ b/commands/tldr.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import {SummarizerManager as summarizerManager} from 'node-summarizer';
import extractor from 'unfluff';
import { messageForAva, splitArgs, getBody, stringIsAValidUrl, getFileName } from '../lib/utils/utils.js';
@@ -9,8 +8,6 @@ const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/video.js b/commands/video.js
index d7b3e1b..cb856b4 100644
--- a/commands/video.js
+++ b/commands/video.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import usetube from 'usetube';
import { randomItemFromArray, messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
@@ -6,8 +5,6 @@ const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute: async(message) => {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/whiteclaw.js b/commands/whiteclaw.js
index 86982f9..e39f5c7 100644
--- a/commands/whiteclaw.js
+++ b/commands/whiteclaw.js
@@ -1,12 +1,9 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/commands/yeet.js b/commands/yeet.js
index 6491098..cb879bb 100644
--- a/commands/yeet.js
+++ b/commands/yeet.js
@@ -1,4 +1,3 @@
-import { Events } from 'discord.js';
import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,8 +6,6 @@ const yeetImage = 'https://i.kym-cdn.com/entries/icons/original/000/031/544/cove
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
// Ensure message is intended for ava
if (!messageForAva(message)) {
diff --git a/index.js b/index.js
index 474fdea..753d252 100644
--- a/index.js
+++ b/index.js
@@ -3,8 +3,9 @@ import * as path from 'path';
import { Client, Events, GatewayIntentBits } from 'discord.js';
import logger from './lib/logger/logger.js';
-import { commandsDir, dbDir } from './config/config.js';
+import { commandsDir, dbDir, avaPrefix } from './config/config.js';
import { initDB, startReminderDaemon } from './lib/db/db.js';
+import { DiscordMessageAdapter } from './lib/adapters/discord.js';
const { AVA_DISCORD_TOKEN, AVA_ENABLE_REMINDERS } = process.env;
// Remindme is a special case where we need to pass in the db
@@ -29,36 +30,28 @@ client.once(Events.ClientReady, c => {
logger.info(`Ready! Logged in as ${c.user.tag}`);
});
-// Dynamic imports of modules/commands so that we don't need to statically define them.
-const eventFiles = fs.readdirSync(`./${commandsDir}`).filter(file => file.endsWith('.js') && !file.startsWith('remindme'));
-for (const file of eventFiles) {
- import(`./${commandsDir}/${file}`).then((module) => {
- const event = module.default;
- if (event.once) {
- client.once(event.name, (...args) => event.execute(...args));
- } else {
- client.on(event.name, (...args) => event.execute(...args));
- }
- });
-}
+// Pre-compile regex once
+const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
+
+(async () => {
+ // Dynamic imports of modules/commands so that we don't need to statically define them.
+ const commands = [];
+ const eventFiles = fs.readdirSync(`./${commandsDir}`).filter(file => file.endsWith('.js') && !file.startsWith('remindme'));
+ for (const file of eventFiles) {
+ const module = await import(`./${commandsDir}/${file}`);
+ commands.push(module.default);
+ }
-if (AVA_ENABLE_REMINDERS) {
- const dbFile = path.join(dbDir, 'db.json');
+ if (AVA_ENABLE_REMINDERS) {
+ const dbFile = path.join(dbDir, 'db.json');
- logger.info(`Reminders enabled. Initializing database at ${dbFile}`);
- (async () => {
+ logger.info(`Reminders enabled. Initializing database at ${dbFile}`);
const db = await initDB({
file: dbFile,
});
- const remindMeEvent = remindMe({db});
-
- // Start discord listener
- if (remindMeEvent.once) {
- client.once(remindMeEvent.name, (...args) => remindMeEvent.execute(...args));
- } else {
- client.on(remindMeEvent.name, (...args) => remindMeEvent.execute(...args));
- }
+ const remindMeCommand = remindMe({db});
+ commands.push(remindMeCommand);
// Start reminder daemon
startReminderDaemon({
@@ -66,11 +59,24 @@ if (AVA_ENABLE_REMINDERS) {
checkForRemindersInterval: 300000, // 5m
db: db
});
- })().catch((e) => {
- logger.error(e);
- process.exit(1);
- });
+ }
-}
+ // Listen for all messages and dispatch to commands via adapter
+ client.on(Events.MessageCreate, async (message) => {
+ if (!avaPrefixRegex.test(message.content)) return;
+
+ const adapter = new DiscordMessageAdapter(message);
+ for (const command of commands) {
+ try {
+ await command.execute(adapter);
+ } catch (e) {
+ logger.error(`Command execution error: ${e}`);
+ }
+ }
+ });
-client.login(AVA_DISCORD_TOKEN);
+ client.login(AVA_DISCORD_TOKEN);
+})().catch((e) => {
+ logger.error(e);
+ process.exit(1);
+});
diff --git a/lib/adapters/discord.js b/lib/adapters/discord.js
new file mode 100644
index 0000000..3cc7416
--- /dev/null
+++ b/lib/adapters/discord.js
@@ -0,0 +1,23 @@
+/**
+ * Adapts a Discord message to a common interface shared with the Slack adapter.
+ * This is a thin wrapper for architectural consistency — both platforms
+ * now use the same adapter pattern for dispatching commands.
+ */
+class DiscordMessageAdapter {
+ constructor(message) {
+ this._message = message;
+ this.content = message.content;
+ this.channelId = message.channelId;
+ this.guildId = message.guildId;
+ this.id = message.id;
+ this.createdTimestamp = message.createdTimestamp;
+ this.author = message.author;
+ this.channel = message.channel;
+ }
+
+ async reply(content) {
+ return this._message.reply(content);
+ }
+}
+
+export { DiscordMessageAdapter };
diff --git a/lib/db/db.js b/lib/db/db.js
index 123d3ff..b965b71 100644
--- a/lib/db/db.js
+++ b/lib/db/db.js
@@ -46,8 +46,8 @@ const startReminderDaemon = async(opts = {
const reminder = reminders[i];
const remindTime = Date.parse(reminder.remindTime);
const now = new Date;
- let channel = {};
- let message = {};
+ let channel;
+ let message;
// If remindtime has passed
if (remindTime < now) {
diff --git a/lib/reddit/submissions.js b/lib/reddit/submissions.js
index 0d50345..290984a 100644
--- a/lib/reddit/submissions.js
+++ b/lib/reddit/submissions.js
@@ -13,7 +13,7 @@ const getRandomSubmission = async(opts = {}) => {
return e;
}
- let randomSubmission = [];
+ let randomSubmission;
try {
const r = new Snoowrap(reddit);
randomSubmission = await r.getSubreddit(subreddit).getHot();
diff --git a/slack.js b/slack.js
index 47f0e75..69a77a5 100644
--- a/slack.js
+++ b/slack.js
@@ -28,6 +28,9 @@ const app = new App({
socketMode: true,
});
+// Pre-compile regex once
+const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
+
const startSlackReminderDaemon = async (opts = {}) => {
const { db } = opts;
const checkInterval = 300000; // 5m
@@ -96,7 +99,7 @@ const startSlackReminderDaemon = async (opts = {}) => {
}
// Listen for messages starting with the ava prefix
- app.message(new RegExp(`^${avaPrefix}`, 'i'), async ({ message, client }) => {
+ app.message(avaPrefixRegex, async ({ message, client }) => {
// Ignore bot messages to prevent loops
if (message.subtype === 'bot_message' || message.bot_id) {
return;
From 0672c98ce4f002b4bf0e6b2ec0b6ade9f49f4911 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 21:47:28 +0000
Subject: [PATCH 5/7] Unify entry point: single `npm start` runs Discord,
Slack, or both simultaneously
- Refactor index.js to auto-detect platforms based on which env vars are set
- Extract platform logic into lib/platforms/discord.js and lib/platforms/slack.js
- Remove separate slack.js entry point and start:slack script
- Docker image works for all configs via env vars alone (no CMD override)
- Update README and docs to show unified usage
Co-authored-by: circa10a <21261388+circa10a@users.noreply.github.com>
---
README.md | 27 ++++-----
docs/getting-started.md | 28 ++++++++-
index.js | 74 ++++++++----------------
lib/platforms/discord.js | 53 +++++++++++++++++
lib/platforms/slack.js | 88 ++++++++++++++++++++++++++++
package-lock.json | 3 -
package.json | 1 -
slack.js | 120 ---------------------------------------
8 files changed, 205 insertions(+), 189 deletions(-)
create mode 100644 lib/platforms/discord.js
create mode 100644 lib/platforms/slack.js
delete mode 100644 slack.js
diff --git a/README.md b/README.md
index e83888b..aa617aa 100644
--- a/README.md
+++ b/README.md
@@ -13,21 +13,23 @@ A discord and slack implementation of the famous ava bot
## Usage
-### Discord
+Ava starts whichever platforms have tokens configured. Set Discord tokens, Slack tokens, or both to run them simultaneously.
```bash
+# Discord only
export AVA_DISCORD_TOKEN=
-npm i
-npm start
-```
-### Slack
+# Slack only
+export AVA_SLACK_BOT_TOKEN=
+export AVA_SLACK_APP_TOKEN=
-```bash
+# Both platforms simultaneously
+export AVA_DISCORD_TOKEN=
export AVA_SLACK_BOT_TOKEN=
export AVA_SLACK_APP_TOKEN=
+
npm i
-npm run start:slack
+npm start
```
## Config
@@ -79,16 +81,15 @@ npm run start:slack
## Docker
-### Discord
-
```bash
+# Discord only
docker run -e AVA_DISCORD_TOKEN="" circa10a/ava
-```
-### Slack
+# Slack only
+docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava
-```bash
-docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava npm run start:slack
+# Both platforms simultaneously
+docker run -e AVA_DISCORD_TOKEN="" -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava
```
## Contribution
diff --git a/docs/getting-started.md b/docs/getting-started.md
index ba40910..70b88de 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -56,12 +56,14 @@ docker run -e AVA_DISCORD_TOKEN="" circa10a/ava
#### Non-Docker
```shell
+export AVA_DISCORD_TOKEN=
npm run start
```
You should see a message when the server is up and running.
```
-[INFO] Ready! Logged in as ...
+[INFO] Discord platform enabled
+[INFO] Discord ready! Logged in as ...
```
### Verify Ava (Discord)
@@ -118,7 +120,7 @@ Invite the bot to a channel by typing `/invite @Ava` in the channel.
#### Docker
```shell
-docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava npm run start:slack
+docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava
```
#### Non-Docker
@@ -126,11 +128,12 @@ docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="
export AVA_SLACK_APP_TOKEN=
-npm run start:slack
+npm run start
```
You should see a message when the server is up and running.
```
+[INFO] Slack platform enabled
[INFO] ⚡️ Ava Slack bot is running!
```
@@ -140,6 +143,25 @@ Go to the Slack channel where you invited the bot and type `ava help`. If everyt
---
+## Running Both Platforms Simultaneously
+
+Ava can run Discord and Slack at the same time. Simply provide tokens for both platforms:
+
+```shell
+export AVA_DISCORD_TOKEN=
+export AVA_SLACK_BOT_TOKEN=
+export AVA_SLACK_APP_TOKEN=
+npm run start
+```
+
+Or with Docker:
+
+```shell
+docker run -e AVA_DISCORD_TOKEN="" -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava
+```
+
+---
+
## Closing
If Ava returned all the commands then congratulations. You now have a local test server and an Ava test bot to verify new commands.
diff --git a/index.js b/index.js
index 753d252..9abce89 100644
--- a/index.js
+++ b/index.js
@@ -1,38 +1,26 @@
import * as fs from 'fs';
import * as path from 'path';
-import { Client, Events, GatewayIntentBits } from 'discord.js';
import logger from './lib/logger/logger.js';
-import { commandsDir, dbDir, avaPrefix } from './config/config.js';
-import { initDB, startReminderDaemon } from './lib/db/db.js';
-import { DiscordMessageAdapter } from './lib/adapters/discord.js';
-const { AVA_DISCORD_TOKEN, AVA_ENABLE_REMINDERS } = process.env;
+import { commandsDir, dbDir } from './config/config.js';
+import { initDB } from './lib/db/db.js';
+import { startDiscord } from './lib/platforms/discord.js';
+import { startSlack } from './lib/platforms/slack.js';
-// Remindme is a special case where we need to pass in the db
import remindMe from './commands/remindme.js';
+const { AVA_DISCORD_TOKEN, AVA_SLACK_BOT_TOKEN, AVA_SLACK_APP_TOKEN, AVA_ENABLE_REMINDERS } = process.env;
+
logger.info(`Node version: ${process.version}`);
-if (!AVA_DISCORD_TOKEN) {
- logger.error('Missing AVA_DISCORD_TOKEN environment variable');
+const discordEnabled = !!AVA_DISCORD_TOKEN;
+const slackEnabled = !!(AVA_SLACK_BOT_TOKEN && AVA_SLACK_APP_TOKEN);
+
+if (!discordEnabled && !slackEnabled) {
+ logger.error('No platform configured. Set AVA_DISCORD_TOKEN for Discord and/or AVA_SLACK_BOT_TOKEN + AVA_SLACK_APP_TOKEN for Slack.');
process.exit(1);
}
-// Setup client
-const client = new Client({ intents: [
- GatewayIntentBits.Guilds,
- GatewayIntentBits.GuildMessages,
- GatewayIntentBits.MessageContent,
-]});
-
-client.setMaxListeners(0);
-client.once(Events.ClientReady, c => {
- logger.info(`Ready! Logged in as ${c.user.tag}`);
-});
-
-// Pre-compile regex once
-const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
-
(async () => {
// Dynamic imports of modules/commands so that we don't need to statically define them.
const commands = [];
@@ -42,40 +30,28 @@ const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
commands.push(module.default);
}
+ let db = null;
if (AVA_ENABLE_REMINDERS) {
const dbFile = path.join(dbDir, 'db.json');
-
logger.info(`Reminders enabled. Initializing database at ${dbFile}`);
- const db = await initDB({
- file: dbFile,
- });
-
- const remindMeCommand = remindMe({db});
+ db = await initDB({ file: dbFile });
+ const remindMeCommand = remindMe({ db });
commands.push(remindMeCommand);
-
- // Start reminder daemon
- startReminderDaemon({
- client: client,
- checkForRemindersInterval: 300000, // 5m
- db: db
- });
}
- // Listen for all messages and dispatch to commands via adapter
- client.on(Events.MessageCreate, async (message) => {
- if (!avaPrefixRegex.test(message.content)) return;
+ const startups = [];
- const adapter = new DiscordMessageAdapter(message);
- for (const command of commands) {
- try {
- await command.execute(adapter);
- } catch (e) {
- logger.error(`Command execution error: ${e}`);
- }
- }
- });
+ if (discordEnabled) {
+ logger.info('Discord platform enabled');
+ startups.push(startDiscord({ commands, db }));
+ }
+
+ if (slackEnabled) {
+ logger.info('Slack platform enabled');
+ startups.push(startSlack({ commands, db }));
+ }
- client.login(AVA_DISCORD_TOKEN);
+ await Promise.all(startups);
})().catch((e) => {
logger.error(e);
process.exit(1);
diff --git a/lib/platforms/discord.js b/lib/platforms/discord.js
new file mode 100644
index 0000000..0f3851d
--- /dev/null
+++ b/lib/platforms/discord.js
@@ -0,0 +1,53 @@
+import { Client, Events, GatewayIntentBits } from 'discord.js';
+
+import logger from '../logger/logger.js';
+import { avaPrefix } from '../../config/config.js';
+import { startReminderDaemon } from '../db/db.js';
+import { DiscordMessageAdapter } from '../adapters/discord.js';
+
+// Pre-compile regex once
+const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
+
+const startDiscord = async ({ commands, db }) => {
+ const { AVA_DISCORD_TOKEN } = process.env;
+
+ if (!AVA_DISCORD_TOKEN) {
+ throw new Error('Missing AVA_DISCORD_TOKEN environment variable');
+ }
+
+ const client = new Client({ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent,
+ ]});
+
+ client.setMaxListeners(0);
+ client.once(Events.ClientReady, c => {
+ logger.info(`Discord ready! Logged in as ${c.user.tag}`);
+ });
+
+ if (db) {
+ startReminderDaemon({
+ client: client,
+ checkForRemindersInterval: 300000,
+ db: db
+ });
+ }
+
+ client.on(Events.MessageCreate, async (message) => {
+ if (!avaPrefixRegex.test(message.content)) return;
+
+ const adapter = new DiscordMessageAdapter(message);
+ for (const command of commands) {
+ try {
+ await command.execute(adapter);
+ } catch (e) {
+ logger.error(`Command execution error: ${e}`);
+ }
+ }
+ });
+
+ await client.login(AVA_DISCORD_TOKEN);
+};
+
+export { startDiscord };
diff --git a/lib/platforms/slack.js b/lib/platforms/slack.js
new file mode 100644
index 0000000..5b14fa6
--- /dev/null
+++ b/lib/platforms/slack.js
@@ -0,0 +1,88 @@
+import pkg from '@slack/bolt';
+const { App } = pkg;
+
+import logger from '../logger/logger.js';
+import { avaPrefix } from '../../config/config.js';
+import { SlackMessageAdapter } from '../adapters/slack.js';
+import { sleep } from '../utils/utils.js';
+
+// Pre-compile regex once
+const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
+
+const startSlackReminderDaemon = async ({ app, db }) => {
+ const checkInterval = 300000; // 5m
+ for (;;) {
+ await sleep(checkInterval);
+
+ await db.read();
+ const reminders = db.data.reminders;
+
+ for (let i = 0; i < reminders.length; i++) {
+ const reminder = reminders[i];
+ const remindTime = Date.parse(reminder.remindTime);
+ const now = new Date();
+
+ if (remindTime < now) {
+ try {
+ logger.debug(`Sending reminder to ${reminder.user}`);
+ await app.client.chat.postMessage({
+ channel: reminder.channelId,
+ thread_ts: reminder.messageId,
+ text: 'Here\'s your reminder',
+ });
+ reminders.splice(i, 1);
+ await db.write();
+ await sleep(100);
+ } catch (e) {
+ logger.error(`Reminder error: ${e}`);
+ if (e.data && e.data.error === 'channel_not_found') {
+ logger.error(`Unable to access channel for reminder from ${reminder.user}. Deleting...`);
+ reminders.splice(i, 1);
+ await db.write();
+ }
+ }
+ }
+ }
+ }
+};
+
+const startSlack = async ({ commands, db }) => {
+ const { AVA_SLACK_BOT_TOKEN, AVA_SLACK_APP_TOKEN, AVA_SLACK_SIGNING_SECRET } = process.env;
+
+ if (!AVA_SLACK_BOT_TOKEN || !AVA_SLACK_APP_TOKEN) {
+ throw new Error('Missing AVA_SLACK_BOT_TOKEN or AVA_SLACK_APP_TOKEN environment variables');
+ }
+
+ const app = new App({
+ token: AVA_SLACK_BOT_TOKEN,
+ appToken: AVA_SLACK_APP_TOKEN,
+ signingSecret: AVA_SLACK_SIGNING_SECRET || '',
+ socketMode: true,
+ });
+
+ if (db) {
+ startSlackReminderDaemon({ app, db }).catch((e) => {
+ logger.error(e);
+ });
+ }
+
+ app.message(avaPrefixRegex, async ({ message, client }) => {
+ if (message.subtype === 'bot_message' || message.bot_id) {
+ return;
+ }
+
+ const adapter = new SlackMessageAdapter({ event: message, client });
+ for (const command of commands) {
+ try {
+ await command.execute(adapter);
+ } catch (e) {
+ logger.error(`Command execution error: ${e}`);
+ }
+ }
+ });
+
+ await app.start();
+ logger.info('⚡️ Ava Slack bot is running!');
+};
+
+export { startSlack };
diff --git a/package-lock.json b/package-lock.json
index 2994e0a..33777f9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -961,7 +961,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1964,7 +1963,6 @@
"integrity": "sha512-20MV9SUdeN6Jd84xESsKhRly+/vxI+hwvpBMA93s+9dAcjdCuCojn4IqUGS3lvVaqjVYGYHSRMCpeFtF2rQYxQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2",
@@ -3874,7 +3872,6 @@
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
diff --git a/package.json b/package.json
index 065fb60..d510379 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,6 @@
"lint": "eslint .",
"fix-lint": "eslint . --fix",
"start": "node --no-deprecation ./index.js",
- "start:slack": "node --no-deprecation ./slack.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
diff --git a/slack.js b/slack.js
deleted file mode 100644
index 69a77a5..0000000
--- a/slack.js
+++ /dev/null
@@ -1,120 +0,0 @@
-import * as fs from 'fs';
-import * as path from 'path';
-import pkg from '@slack/bolt';
-const { App } = pkg;
-
-import logger from './lib/logger/logger.js';
-import { commandsDir, dbDir, avaPrefix } from './config/config.js';
-import { initDB } from './lib/db/db.js';
-import { SlackMessageAdapter } from './lib/adapters/slack.js';
-import { sleep } from './lib/utils/utils.js';
-
-// Remindme is a special case where we need to pass in the db
-import remindMe from './commands/remindme.js';
-
-const { AVA_SLACK_BOT_TOKEN, AVA_SLACK_APP_TOKEN, AVA_SLACK_SIGNING_SECRET, AVA_ENABLE_REMINDERS } = process.env;
-
-logger.info(`Node version: ${process.version}`);
-
-if (!AVA_SLACK_BOT_TOKEN || !AVA_SLACK_APP_TOKEN) {
- logger.error('Missing AVA_SLACK_BOT_TOKEN or AVA_SLACK_APP_TOKEN environment variables');
- process.exit(1);
-}
-
-const app = new App({
- token: AVA_SLACK_BOT_TOKEN,
- appToken: AVA_SLACK_APP_TOKEN,
- signingSecret: AVA_SLACK_SIGNING_SECRET || '',
- socketMode: true,
-});
-
-// Pre-compile regex once
-const avaPrefixRegex = new RegExp(`^${avaPrefix}`, 'i');
-
-const startSlackReminderDaemon = async (opts = {}) => {
- const { db } = opts;
- const checkInterval = 300000; // 5m
- for (;;) {
- await sleep(checkInterval);
-
- await db.read();
- const reminders = db.data.reminders;
-
- for (let i = 0; i < reminders.length; i++) {
- const reminder = reminders[i];
- const remindTime = Date.parse(reminder.remindTime);
- const now = new Date();
-
- if (remindTime < now) {
- try {
- logger.debug(`Sending reminder to ${reminder.user}`);
- await app.client.chat.postMessage({
- channel: reminder.channelId,
- thread_ts: reminder.messageId,
- text: 'Here\'s your reminder',
- });
- reminders.splice(i, 1);
- await db.write();
- await sleep(100);
- } catch (e) {
- logger.error(`Reminder error: ${e}`);
- if (e.data && e.data.error === 'channel_not_found') {
- logger.error(`Unable to access channel for reminder from ${reminder.user}. Deleting...`);
- reminders.splice(i, 1);
- await db.write();
- }
- }
- }
- }
- }
-};
-
-(async () => {
- // Dynamically load all command modules
- const commands = [];
- const eventFiles = fs.readdirSync(`./${commandsDir}`).filter(file => file.endsWith('.js') && !file.startsWith('remindme'));
-
- for (const file of eventFiles) {
- const module = await import(`./${commandsDir}/${file}`);
- commands.push(module.default);
- }
-
- // Handle reminders if enabled
- if (AVA_ENABLE_REMINDERS) {
- const dbFile = path.join(dbDir, 'db.json');
- logger.info(`Reminders enabled. Initializing database at ${dbFile}`);
-
- try {
- const db = await initDB({ file: dbFile });
- const remindMeCommand = remindMe({ db });
- commands.push(remindMeCommand);
-
- startSlackReminderDaemon({ db }).catch((e) => {
- logger.error(e);
- });
- } catch (e) {
- logger.error(e);
- process.exit(1);
- }
- }
-
- // Listen for messages starting with the ava prefix
- app.message(avaPrefixRegex, async ({ message, client }) => {
- // Ignore bot messages to prevent loops
- if (message.subtype === 'bot_message' || message.bot_id) {
- return;
- }
-
- const adapter = new SlackMessageAdapter({ event: message, client });
- for (const command of commands) {
- try {
- await command.execute(adapter);
- } catch (e) {
- logger.error(`Command execution error: ${e}`);
- }
- }
- });
-
- await app.start();
- logger.info('⚡️ Ava Slack bot is running!');
-})();
From f6acd5d1e0db695d46094af57b37f160aecb0efb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 21:48:49 +0000
Subject: [PATCH 6/7] Fix splice-while-iterating: iterate backwards in Slack
reminder daemon
Co-authored-by: circa10a <21261388+circa10a@users.noreply.github.com>
---
lib/platforms/slack.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/platforms/slack.js b/lib/platforms/slack.js
index 5b14fa6..05cf0bd 100644
--- a/lib/platforms/slack.js
+++ b/lib/platforms/slack.js
@@ -17,7 +17,7 @@ const startSlackReminderDaemon = async ({ app, db }) => {
await db.read();
const reminders = db.data.reminders;
- for (let i = 0; i < reminders.length; i++) {
+ for (let i = reminders.length - 1; i >= 0; i--) {
const reminder = reminders[i];
const remindTime = Date.parse(reminder.remindTime);
const now = new Date();
From ac60d33ead28d80e319e3e02404cecd6c472c166 Mon Sep 17 00:00:00 2001
From: circa10a
Date: Tue, 3 Mar 2026 20:03:21 -0800
Subject: [PATCH 7/7] rewrite the whole fuckin thing
---
.dockerignore | 3 +
.github/workflows/docker.yaml | 10 +-
.github/workflows/test.yaml | 7 +-
Dockerfile | 7 +-
commands/8ball.js | 46 +-
commands/bored.js | 36 +-
commands/brewery.js | 71 +-
commands/catfact.js | 36 +-
commands/coffee.js | 45 +-
commands/compliment.js | 49 +-
commands/contribute.js | 13 +-
commands/emoji.js | 22 +-
commands/floridaman.js | 31 +-
commands/fuck.js | 82 +--
commands/gcp.js | 13 +-
commands/help.js | 31 +-
commands/hp.js | 48 +-
commands/insult.js | 50 +-
commands/java.js | 13 +-
commands/karen.js | 48 +-
commands/meme.js | 48 +-
commands/mock.js | 28 +-
commands/recipe.js | 71 +-
commands/remindme.js | 59 +-
commands/takemehome.js | 33 +-
commands/tldr.js | 83 ++-
commands/video.js | 36 +-
commands/whiteclaw.js | 13 +-
commands/yeet.js | 13 +-
config/config.js | 17 +-
docs/CONTRIBUTION.md | 24 +-
docs/getting-started.md | 13 +-
index.js | 9 +-
lib/commands/redditEmbed.js | 35 +
lib/db/db.js | 20 +-
lib/platforms/discord.js | 11 +-
lib/platforms/slack.js | 11 +-
lib/reddit/submissions.js | 51 +-
lib/utils/utils.js | 3 -
lib/validations/reddit/creds.js | 11 -
package-lock.json | 1223 ++-----------------------------
package.json | 19 +-
test/adapters/discord.test.js | 95 +++
test/adapters/slack.test.js | 235 ++++++
test/platforms/discord.test.js | 15 +
test/platforms/slack.test.js | 18 +
46 files changed, 949 insertions(+), 1906 deletions(-)
create mode 100644 lib/commands/redditEmbed.js
delete mode 100644 lib/validations/reddit/creds.js
create mode 100644 test/adapters/discord.test.js
create mode 100644 test/adapters/slack.test.js
create mode 100644 test/platforms/discord.test.js
create mode 100644 test/platforms/slack.test.js
diff --git a/.dockerignore b/.dockerignore
index 1d84211..e02b65f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,9 @@
node_modules
.eslintrc.json
+eslint.config.mjs
.gitignore
*.md
.github
.do
+test
+docs
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
index dbe9884..cfc582b 100644
--- a/.github/workflows/docker.yaml
+++ b/.github/workflows/docker.yaml
@@ -13,18 +13,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ github.repository_owner }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push ava
- uses: docker/build-push-action@v4
+ uses: docker/build-push-action@v6
with:
platforms: ${{ env.PLATFORMS }}
push: true
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index ff9e38a..47510e9 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -9,10 +9,13 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
+ with:
+ node-version: 'lts/*'
- run: npm ci
- run: npm run lint
env:
CI: true
+ - run: npm test
diff --git a/Dockerfile b/Dockerfile
index 5fbf4d3..f86b57d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
-FROM node
+FROM node:22-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
+COPY package.json package-lock.json ./
+RUN npm ci --omit=dev
COPY . .
-RUN npm install
-CMD ["npm", "start"]
\ No newline at end of file
+CMD ["node", "--no-deprecation", "./index.js"]
\ No newline at end of file
diff --git a/commands/8ball.js b/commands/8ball.js
index cbf19a3..cf5ab2b 100644
--- a/commands/8ball.js
+++ b/commands/8ball.js
@@ -1,30 +1,36 @@
-import eightball from '8ball';
-import { messageForAva , splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
-import dammit from 'dammit';
+import { randomItemFromArray, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
+const eightBallResponses = [
+ 'It is certain', 'It is decidedly so', 'Without a doubt',
+ 'Yes definitely', 'You may rely on it', 'As I see it, yes',
+ 'Most likely', 'Outlook good', 'Yes', 'Signs point to yes',
+ 'Reply hazy try again', 'Ask again later', 'Better not tell you now',
+ 'Cannot predict now', 'Concentrate and ask again',
+ 'Don\'t count on it', 'My reply is no', 'My sources say no',
+ 'Outlook not so good', 'Very doubtful',
+];
+
+const dammitPhrases = [
+ 'Dammit', 'Damn', 'Damnit', 'Well damn', 'Oh damn',
+ 'Son of a bitch', 'Hell', 'Crap', 'Shit', 'Bloody hell',
+ 'For fuck\'s sake', 'What the hell', 'God dammit',
+];
+
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const question = getAllArgsAsStr(args);
- if (userCmd === command) {
- if (!question) {
- message.reply('No question provided');
- return;
- }
- try {
- message.reply(`${dammit({NSFW: true})}... ${eightball()}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ if (!question) {
+ message.reply('No question provided');
+ return;
+ }
+ try {
+ message.reply(`${randomItemFromArray(dammitPhrases)}... ${randomItemFromArray(eightBallResponses)}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/bored.js b/commands/bored.js
index 861fe33..ea3c385 100644
--- a/commands/bored.js
+++ b/commands/bored.js
@@ -1,5 +1,4 @@
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getFileName} from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,27 +6,18 @@ const boredEndpoint = 'https://boredapi.com/api/activity';
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- try {
- const response = await fetch(boredEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.reply(jsonResponse.activity);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ execute: async (message) => {
+ try {
+ const response = await fetch(boredEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.reply(jsonResponse.activity);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/brewery.js b/commands/brewery.js
index c856422..3a158dc 100644
--- a/commands/brewery.js
+++ b/commands/brewery.js
@@ -1,7 +1,6 @@
import { EmbedBuilder } from 'discord.js';
-import fetch from 'node-fetch';
import { embedColor } from '../config/config.js';
-import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -10,48 +9,40 @@ const beerThumbnail = 'https://i.imgur.com/6ZKnMaw.jpg';
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const city = args[2];
- if (userCmd === command) {
- if (!city) {
- message.reply('No city provided');
- return;
- }
- try {
- const response = await fetch(`${breweryEndpoint}?by_city=${city}`, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const breweries = await response.json();
- const randomBrewery = randomItemFromArray(breweries);
- if (!randomBrewery) {
- message.reply('City not found');
- return;
+ if (!city) {
+ message.reply('No city provided');
+ return;
+ }
+ try {
+ const response = await fetch(`${breweryEndpoint}?by_city=${city}`, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
}
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomBrewery.name)
- .setURL(randomBrewery.website_url)
- .setDescription(`${randomBrewery.street}\n${randomBrewery.city}, ${randomBrewery.state}`)
- .setThumbnail(beerThumbnail)
- .addFields(
- { name: 'Google Maps Link', value: `https://www.google.com/maps?q=${randomBrewery.latitude},${randomBrewery.longitude}`},
- { name: 'Phone', value: randomBrewery.phone ?? 'Not listed' },
- { name: 'Brewery Type', value: randomBrewery.brewery_type ?? 'Not listed' },
- );
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
+ });
+ const breweries = await response.json();
+ const randomBrewery = randomItemFromArray(breweries);
+ if (!randomBrewery) {
+ message.reply('City not found');
+ return;
}
+ const embed = new EmbedBuilder()
+ .setColor(embedColor)
+ .setTitle(randomBrewery.name)
+ .setURL(randomBrewery.website_url)
+ .setDescription(`${randomBrewery.street}\n${randomBrewery.city}, ${randomBrewery.state}`)
+ .setThumbnail(beerThumbnail)
+ .addFields(
+ { name: 'Google Maps Link', value: `https://www.google.com/maps?q=${randomBrewery.latitude},${randomBrewery.longitude}`},
+ { name: 'Phone', value: randomBrewery.phone ?? 'Not listed' },
+ { name: 'Brewery Type', value: randomBrewery.brewery_type ?? 'Not listed' },
+ );
+ message.channel.send({ embeds: [embed] });
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/catfact.js b/commands/catfact.js
index bc8b631..e530148 100644
--- a/commands/catfact.js
+++ b/commands/catfact.js
@@ -1,5 +1,4 @@
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,27 +6,18 @@ const catFactEndpoint = 'https://catfact.ninja/fact';
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- try {
- const response = await fetch(catFactEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.reply(jsonResponse.fact);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ execute: async (message) => {
+ try {
+ const response = await fetch(catFactEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.reply(jsonResponse.fact);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/coffee.js b/commands/coffee.js
index 361b026..941a2d5 100644
--- a/commands/coffee.js
+++ b/commands/coffee.js
@@ -1,39 +1,30 @@
import { default as wiki } from 'wikijs';
import { EmbedBuilder } from 'discord.js';
-import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getFileName } from '../lib/utils/utils.js';
import { embedColor } from '../config/config.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- try {
- const coffeeBrands = await wiki().pagesInCategory('Category:Coffee_brands');
- const randomBrand = randomItemFromArray(coffeeBrands);
- const wikiPage = await wiki().page(randomBrand);
- const wikiPageUrl = wikiPage.fullurl;
- const brandTitle = wikiPage.title;
- const [pageImage, summary] = await Promise.all([wikiPage.pageImage(), wikiPage.summary()]);
+ execute: async (message) => {
+ try {
+ const coffeeBrands = await wiki().pagesInCategory('Category:Coffee_brands');
+ const randomBrand = randomItemFromArray(coffeeBrands);
+ const wikiPage = await wiki().page(randomBrand);
+ const wikiPageUrl = wikiPage.fullurl;
+ const brandTitle = wikiPage.title;
+ const [pageImage, summary] = await Promise.all([wikiPage.pageImage(), wikiPage.summary()]);
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(brandTitle)
- .setURL(wikiPageUrl)
- .setDescription(summary)
- .setThumbnail(pageImage);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ const embed = new EmbedBuilder()
+ .setColor(embedColor)
+ .setTitle(brandTitle)
+ .setURL(wikiPageUrl)
+ .setDescription(summary)
+ .setThumbnail(pageImage);
+ message.channel.send({ embeds: [embed] });
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/compliment.js b/commands/compliment.js
index ca701b9..e3f59d5 100644
--- a/commands/compliment.js
+++ b/commands/compliment.js
@@ -1,5 +1,4 @@
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,37 +6,29 @@ const complimentEndpoint = 'https://complimentr.com/api';
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
let userToCompliment = args[2];
- if (userCmd === command) {
- if (!userToCompliment) {
- message.reply('No user to compliment provided');
- return;
- }
+ if (!userToCompliment) {
+ message.reply('No user to compliment provided');
+ return;
+ }
- if (userToCompliment === 'me') {
- userToCompliment = message.author.toString();
- }
+ if (userToCompliment === 'me') {
+ userToCompliment = message.author.toString();
+ }
- try {
- const response = await fetch(complimentEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.channel.send(`${userToCompliment}, ${jsonResponse.compliment}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ const response = await fetch(complimentEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.channel.send(`${userToCompliment}, ${jsonResponse.compliment}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/contribute.js b/commands/contribute.js
index ffb2ada..426852c 100644
--- a/commands/contribute.js
+++ b/commands/contribute.js
@@ -1,4 +1,4 @@
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,15 +7,6 @@ const projectURL = 'https://github.com/circa10a/ava';
export default {
commandName: command,
execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd == command) {
- message.reply(projectURL);
- }
+ message.reply(projectURL);
},
};
diff --git a/commands/emoji.js b/commands/emoji.js
index 86539cc..db22e1e 100644
--- a/commands/emoji.js
+++ b/commands/emoji.js
@@ -1,26 +1,18 @@
import imageFinder from 'image-search-engine';
-import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const searchTerms = getAllArgsAsStr(args);
- if (userCmd === command) {
- try {
- const img = await imageFinder.find(searchTerms, { size: 'small', color: 'transparent' });
- message.reply(img);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ const img = await imageFinder.find(searchTerms, { size: 'small', color: 'transparent' });
+ message.reply(img);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/floridaman.js b/commands/floridaman.js
index c604590..37ed02e 100644
--- a/commands/floridaman.js
+++ b/commands/floridaman.js
@@ -1,4 +1,4 @@
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
import { getRandomSubmission } from '../lib/reddit/submissions.js';
const command = getFileName(import.meta.url);
@@ -7,27 +7,18 @@ const subreddit = 'FloridaMan';
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
+ execute: async (message) => {
+ let randomSubmission;
+ try {
+ randomSubmission = await getRandomSubmission({ subreddit });
+ } catch(e) {
+ message.reply(e.toString());
return;
}
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission;
- try{
- randomSubmission = await getRandomSubmission({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try {
- message.reply(`${randomSubmission.title}\n${randomSubmission.url_overridden_by_dest}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ message.reply(`${randomSubmission.title}\n${randomSubmission.url_overridden_by_dest}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
},
};
diff --git a/commands/fuck.js b/commands/fuck.js
index 79084f3..ab94b0b 100644
--- a/commands/fuck.js
+++ b/commands/fuck.js
@@ -1,5 +1,4 @@
-import fetch from 'node-fetch';
-import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getFileName } from '../lib/utils/utils.js';
import { avaPrefix } from '../config/config.js';
const foaasRootEndpoint = 'https://foaas.com';
@@ -18,56 +17,51 @@ const avaInsults = [
'yourself',
];
+// Cache filtered FOAAS operations (static list, fetched once)
+let cachedOps = null;
+
+const getFilteredOps = async () => {
+ if (cachedOps) return cachedOps;
+ const response = await fetch('https://foaas.com/operations');
+ const data = await response.json();
+ cachedOps = data.filter(item => item.fields.length === 2 && item.url.includes(':name') && !notFucks.includes(item.name));
+ return cachedOps;
+};
+
export default {
commandName: command,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
-
- const args = splitArgs(message);
- const cmd = args[1];
+ execute: async (message, args) => {
const thingToInsult = args[2];
- if (cmd === command) {
- // Ava fights back if people diss her
- if (avaInsults.includes(thingToInsult.toLowerCase())) {
- message.reply('No, fuck you!');
- return;
- }
-
- // Someone said 'ava fuck'
- if (args.length < 3) {
- message.reply('Fuck you');
- return;
- }
-
- try {
- // Get the list of operations (filtered)
- const ops = await fetch('https://foaas.com/operations').
- then(response => response.json()).
- then(data => data.filter(item => item.fields.length === 2 && item.url.includes(':name') && !notFucks.includes(item.name)));
+ // Ava fights back if people diss her
+ if (avaInsults.includes(thingToInsult?.toLowerCase())) {
+ message.reply('No, fuck you!');
+ return;
+ }
- // Pick a random one
- const selection = randomItemFromArray(ops);
- const userArg = encodeURIComponent(args.slice(2).join(' '));
+ // Someone said 'ava fuck'
+ if (args.length < 3) {
+ message.reply('Fuck you');
+ return;
+ }
- // Do the needful
- const selectedUrl = selection.url.replace(':name', userArg).replace(':from', message.author.username);
+ try {
+ const ops = await getFilteredOps();
+ const selection = randomItemFromArray(ops);
+ const userArg = encodeURIComponent(args.slice(2).join(' '));
+ const selectedUrl = selection.url.replace(':name', userArg).replace(':from', message.author.username);
- const resp = await fetch(foaasRootEndpoint + selectedUrl, {
- method: 'get',
- headers: {
- 'Accept': 'text/plain'
- }
- });
+ const resp = await fetch(foaasRootEndpoint + selectedUrl, {
+ method: 'get',
+ headers: {
+ 'Accept': 'text/plain'
+ }
+ });
- const msg = await resp.text();
- message.channel.send(msg);
- } catch (e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ const msg = await resp.text();
+ message.channel.send(msg);
+ } catch (e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/gcp.js b/commands/gcp.js
index 30f741c..eb1e1f8 100644
--- a/commands/gcp.js
+++ b/commands/gcp.js
@@ -1,19 +1,10 @@
-import { messageForAva, splitArgs, getFileName} from '../lib/utils/utils.js';
+import { getFileName} from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- message.reply('GCP is trash and Google should be embarrassed');
- }
+ message.reply('GCP is trash and Google should be embarrassed');
},
};
diff --git a/commands/help.js b/commands/help.js
index a6ac3b4..47c1a1f 100644
--- a/commands/help.js
+++ b/commands/help.js
@@ -1,24 +1,13 @@
import { EmbedBuilder } from 'discord.js';
-import * as fs from 'fs';
-import { commandsDir, embedColor } from '../config/config.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { embedColor } from '../config/config.js';
-const command = getFileName(import.meta.url);
+const command = 'help';
-const eventFiles = fs.readdirSync(`./${commandsDir}`).filter(file => file.endsWith('.js'));
-const availableCommands = eventFiles.map(event => event.replace('.js', ''));
-
-export default {
- commandName: command,
- execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
+const help = (opts = {}) => {
+ return {
+ commandName: command,
+ execute(message) {
+ const { availableCommands = [] } = opts;
const embed = new EmbedBuilder()
.setColor(embedColor)
.setTitle('Available Commands')
@@ -27,6 +16,8 @@ export default {
.setThumbnail('https://i.imgur.com/XbO6CSl.jpg');
message.channel.send({ embeds: [embed] });
- }
- },
+ },
+ };
};
+
+export default help;
diff --git a/commands/hp.js b/commands/hp.js
index 832b4f8..09dbca4 100644
--- a/commands/hp.js
+++ b/commands/hp.js
@@ -1,43 +1,7 @@
-import { EmbedBuilder } from 'discord.js';
+import { createRedditEmbedCommand } from '../lib/commands/redditEmbed.js';
+import { getFileName } from '../lib/utils/utils.js';
-import { embedColor } from '../config/config.js';
-import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
-
-const command = getFileName(import.meta.url);
-
-const subreddit = 'HarryPotterMemes';
-
-export default {
- commandName: command,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission;
- try {
- randomSubmission = await getRandomSubmissionWithImage({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try {
- let description = randomSubmission.selftext || subreddit;
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomSubmission.title)
- .setURL(`https://reddit.com${randomSubmission.permalink}`)
- .setDescription(description)
- .setImage(randomSubmission.url_overridden_by_dest);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
- }
- },
-};
+export default createRedditEmbedCommand({
+ subreddit: 'HarryPotterMemes',
+ commandName: getFileName(import.meta.url),
+});
diff --git a/commands/insult.js b/commands/insult.js
index 3208f2b..6b32ecf 100644
--- a/commands/insult.js
+++ b/commands/insult.js
@@ -1,6 +1,5 @@
import { decode } from 'html-entities';
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -8,38 +7,29 @@ const insultEndpoint = 'https://evilinsult.com/generate_insult.php?lang=en&type=
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
-
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
let thingToInsult = getAllArgsAsStr(args);
- if (userCmd === command) {
- if (!thingToInsult) {
- message.reply('Nothing to insult');
- return;
- }
+ if (!thingToInsult) {
+ message.reply('Nothing to insult');
+ return;
+ }
- if (thingToInsult === 'me') {
- thingToInsult = message.author.toString();
- }
+ if (thingToInsult === 'me') {
+ thingToInsult = message.author.toString();
+ }
- try {
- const response = await fetch(insultEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.channel.send(`${thingToInsult}, ${decode(jsonResponse.insult)}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ const response = await fetch(insultEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.channel.send(`${thingToInsult}, ${decode(jsonResponse.insult)}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/java.js b/commands/java.js
index 1b23c67..5f8e01d 100644
--- a/commands/java.js
+++ b/commands/java.js
@@ -1,4 +1,4 @@
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,15 +7,6 @@ const javaImage = 'https://i.redd.it/o4w97sa7iidz.jpg';
export default {
commandName: command,
execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd == command) {
- message.reply(javaImage);
- }
+ message.reply(javaImage);
},
};
diff --git a/commands/karen.js b/commands/karen.js
index c06ac07..8494a97 100644
--- a/commands/karen.js
+++ b/commands/karen.js
@@ -1,43 +1,7 @@
-import { EmbedBuilder } from 'discord.js';
+import { createRedditEmbedCommand } from '../lib/commands/redditEmbed.js';
+import { getFileName } from '../lib/utils/utils.js';
-import { embedColor } from '../config/config.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
-import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
-
-const command = getFileName(import.meta.url);
-
-const subreddit = 'FuckYouKaren';
-
-export default {
- commandName: command,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission;
- try {
- randomSubmission = await getRandomSubmissionWithImage({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try{
- let description = randomSubmission.selftext || subreddit;
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomSubmission.title)
- .setURL(`https://reddit.com${randomSubmission.permalink}`)
- .setDescription(description)
- .setImage(randomSubmission.url_overridden_by_dest);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
- }
- },
-};
+export default createRedditEmbedCommand({
+ subreddit: 'FuckYouKaren',
+ commandName: getFileName(import.meta.url),
+});
diff --git a/commands/meme.js b/commands/meme.js
index 5a81dc8..2b27491 100644
--- a/commands/meme.js
+++ b/commands/meme.js
@@ -1,43 +1,7 @@
-import { EmbedBuilder } from 'discord.js';
+import { createRedditEmbedCommand } from '../lib/commands/redditEmbed.js';
+import { getFileName } from '../lib/utils/utils.js';
-import { embedColor } from '../config/config.js';
-import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
-
-const command = getFileName(import.meta.url);
-
-const subreddit = 'dankmemes';
-
-export default {
- commandName: command,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission;
- try {
- randomSubmission = await getRandomSubmissionWithImage({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try {
- let description = randomSubmission.selftext || subreddit;
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomSubmission.title)
- .setURL(`https://reddit.com${randomSubmission.permalink}`)
- .setDescription(description)
- .setImage(randomSubmission.url_overridden_by_dest);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
- }
- },
-};
+export default createRedditEmbedCommand({
+ subreddit: 'dankmemes',
+ commandName: getFileName(import.meta.url),
+});
diff --git a/commands/mock.js b/commands/mock.js
index 971c24b..2e0faa4 100644
--- a/commands/mock.js
+++ b/commands/mock.js
@@ -1,32 +1,18 @@
-import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
-const mockText = (str) => {
- let newStr = '';
- str.split('').forEach((el, idx) => {
- newStr += idx % 2 === 0 ? el.toLowerCase() : el.toUpperCase();
- });
- return newStr;
-};
+const mockText = (str) => str.split('').map((ch, i) => i % 2 === 0 ? ch.toLowerCase() : ch.toUpperCase()).join('');
export default {
commandName: command,
- execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute(message, args) {
const userArg = getAllArgsAsStr(args);
- if (userCmd === command) {
- if (!userArg) {
- message.reply('Nothing to mock');
- return;
- }
- message.reply(mockText(userArg));
+ if (!userArg) {
+ message.reply('Nothing to mock');
+ return;
}
+ message.reply(mockText(userArg));
},
};
diff --git a/commands/recipe.js b/commands/recipe.js
index 2137475..8c153d2 100644
--- a/commands/recipe.js
+++ b/commands/recipe.js
@@ -1,5 +1,5 @@
import { parse } from 'node-html-parser';
-import { randomItemFromArray, messageForAva, getBody, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getBody, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
const seriousEatsBaseURL = 'https://www.seriouseats.com';
@@ -8,51 +8,42 @@ const itemsHTMLClass ='#schema-lifestyle_1-0';
export default {
commandName: command,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
-
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const searchTerms = getAllArgsAsStr(args);
- if (userCmd === command) {
- try {
- // If search terms specified
- if (searchTerms) {
- const body = await getBody(`${seriousEatsBaseURL}/search?q=${encodeURIComponent(searchTerms)}`);
- const root = parse(body);
- const results = root.querySelectorAll('.card');
- if (results.length >= 1 ) {
- const firstSearchResult = results[0]._attrs.href;
- message.reply(firstSearchResult);
- return;
- } else {
- message.reply('No recipes found');
- return;
- }
+ try {
+ // If search terms specified
+ if (searchTerms) {
+ const body = await getBody(`${seriousEatsBaseURL}/search?q=${encodeURIComponent(searchTerms)}`);
+ const root = parse(body);
+ const results = root.querySelectorAll('.card');
+ if (results.length >= 1 ) {
+ const firstSearchResult = results[0]._attrs.href;
+ message.reply(firstSearchResult);
+ return;
+ } else {
+ message.reply('No recipes found');
+ return;
}
+ }
- // Random (if no search terms)
- if (!searchTerms) {
- const body = await getBody(allRecipesEndpoint);
- const root = parse(body);
- // JSON in