mirror of
https://github.com/ReVanced/revanced-bots.git
synced 2026-01-11 05:46:16 +00:00
chore: use alternative ways to bundle
This commit is contained in:
@@ -1,5 +1,2 @@
|
|||||||
# Safety measures, do not remove
|
|
||||||
IS_USING_DOT_ENV=1
|
|
||||||
|
|
||||||
# Your Wit.ai token
|
# Your Wit.ai token
|
||||||
WIT_AI_TOKEN="YOUR_TOKEN_HERE"
|
WIT_AI_TOKEN="YOUR_TOKEN_HERE"
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./config.schema.json",
|
|
||||||
|
|
||||||
"address": "127.0.0.1",
|
|
||||||
"port": 3000,
|
|
||||||
"ocrConcurrentQueues": 3,
|
|
||||||
"logLevel": "log"
|
|
||||||
}
|
|
||||||
@@ -42,4 +42,4 @@ The possible levels (sorted by their importance descendingly) are:
|
|||||||
|
|
||||||
The next page will tell you how to run and bundle the server.
|
The next page will tell you how to run and bundle the server.
|
||||||
|
|
||||||
Continue: [🏃🏻♂️ Running the server](./2_running.md)
|
Continue: [🏃🏻♂️ Running the server](./2_running_and_deploying.md)
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
# 🏃🏻♂️ Running the server
|
|
||||||
|
|
||||||
There are many methods to run the server. Choose one that suits best for the situation.
|
|
||||||
|
|
||||||
## 👷🏻 Development mode (recommended)
|
|
||||||
|
|
||||||
There will be no compilation step, and Bun will automatically watch changes and restart the server for you.
|
|
||||||
|
|
||||||
You can quickly start the server by running:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
bun dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🌐 Production mode
|
|
||||||
|
|
||||||
Production mode runs no different from the development server, it simply has less debugging information printed to console by default. However, more production-specific features may come.
|
|
||||||
|
|
||||||
To start the server in production mode, you'll have to:
|
|
||||||
|
|
||||||
1. Set the `NODE_ENV` environment variable to `production`
|
|
||||||
|
|
||||||
> It is very possible to set the value in the `.env` file and let Bun load it, **but it is recommended to set the variable before Bun even starts**.
|
|
||||||
|
|
||||||
2. Start the server
|
|
||||||
```sh
|
|
||||||
bun dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📦 Building
|
|
||||||
|
|
||||||
If you're looking to build and host the server somewhere else, you can run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
bun bundle
|
|
||||||
```
|
|
||||||
|
|
||||||
The files will be placed in the `dist` directory. **Configurations and `.env` files will NOT be copied automatically.**
|
|
||||||
|
|
||||||
To start up the server, you'll need to install `tesseract.js` first.
|
|
||||||
```sh
|
|
||||||
bun install tesseract.js
|
|
||||||
# or
|
|
||||||
bun install tesseract.js -g
|
|
||||||
|
|
||||||
# Run the server
|
|
||||||
bun run index.js
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will tell you about packets.
|
|
||||||
|
|
||||||
Continue: [📨 Packets](./3_packets.md)
|
|
||||||
59
apis/websocket/docs/2_running_and_deploying.md
Normal file
59
apis/websocket/docs/2_running_and_deploying.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# 🏃🏻♂️ Running and deploying the server
|
||||||
|
|
||||||
|
There are many methods to run the server. Choose one that suits best for the situation.
|
||||||
|
|
||||||
|
## 👷🏻 Development mode
|
||||||
|
|
||||||
|
There will be no compilation step, and Bun will automatically watch changes and restart the server for you.
|
||||||
|
|
||||||
|
You can quickly start the server by running:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Building
|
||||||
|
|
||||||
|
If you're looking to build and host the server somewhere else, you can run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
The distribution files will be placed inside the `dist` directory. Inside will include:
|
||||||
|
|
||||||
|
- The default configuration for the API
|
||||||
|
- Compiled source files of the API
|
||||||
|
|
||||||
|
You'll need to also copy the `node_modules` directory dereferenced if you want to run the distribution files somewhere else.
|
||||||
|
|
||||||
|
## ✈️ Deploying
|
||||||
|
|
||||||
|
To deploy the API, you'll need to:
|
||||||
|
|
||||||
|
1. [Build the API as seen in the previous step](#-building)
|
||||||
|
|
||||||
|
2. Copy contents of the `dist` directory
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# For instance, we'll copy them both to /usr/src/api
|
||||||
|
cp -R ./dist/* /usr/src/api
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Replace the default configuration *(optional)*
|
||||||
|
|
||||||
|
4. Configure environment variables
|
||||||
|
As seen in [`.env.example`](../.env.example). You can also optionally use a `.env` file which **Bun will automatically load**.
|
||||||
|
|
||||||
|
5. Finally, you can run the API using these commands
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /usr/src/api
|
||||||
|
bun run index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⏭️ What's next
|
||||||
|
|
||||||
|
The next page will tell you about packets.
|
||||||
|
|
||||||
|
Continue: [📨 Packets](./3_packets.md)
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
"description": "🧦 WebSocket API server for bots assisting ReVanced",
|
"description": "🧦 WebSocket API server for bots assisting ReVanced",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bundle": "bun build src/index.ts --outdir=dist --target=bun -e tesseract.js",
|
"bundle": "bun run scripts/build.ts",
|
||||||
"dev": "bun run src/index.ts --watch",
|
"dev": "bun run src/index.ts --watch",
|
||||||
"build": "bun bundle",
|
"build": "bun bundle",
|
||||||
"watch": "bun dev"
|
"watch": "bun dev"
|
||||||
@@ -30,9 +30,11 @@
|
|||||||
"@revanced/bot-shared": "workspace:*",
|
"@revanced/bot-shared": "workspace:*",
|
||||||
"@sapphire/async-queue": "^1.5.2",
|
"@sapphire/async-queue": "^1.5.2",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"tesseract.js": "^5.1.0"
|
"tesseract.js": "^5.1.0",
|
||||||
|
"ws": "^8.17.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/ws": "^8.5.10",
|
||||||
"typed-emitter": "^2.1.0"
|
"typed-emitter": "^2.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
apis/websocket/scripts/build.ts
Normal file
23
apis/websocket/scripts/build.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { createLogger } from '@revanced/bot-shared'
|
||||||
|
import { cp } from 'fs/promises'
|
||||||
|
|
||||||
|
async function build(): Promise<void> {
|
||||||
|
const logger = createLogger()
|
||||||
|
|
||||||
|
logger.info('Building Tesseract.js worker...')
|
||||||
|
await Bun.build({
|
||||||
|
entrypoints: ['../../node_modules/tesseract.js/src/worker-script/node/index.js'],
|
||||||
|
target: 'bun',
|
||||||
|
outdir: './dist/worker',
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info('Building WebSocket API...')
|
||||||
|
await Bun.build({
|
||||||
|
entrypoints: ['./src/index.ts'],
|
||||||
|
outdir: './dist',
|
||||||
|
target: 'bun',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await build()
|
||||||
|
await cp('config.json', 'dist/config.json')
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { createWorker as createTesseractWorker } from 'tesseract.js'
|
import { OEM, createWorker as createTesseractWorker } from 'tesseract.js'
|
||||||
|
|
||||||
|
import { join as joinPath } from 'path'
|
||||||
import { inspect as inspectObject } from 'util'
|
import { inspect as inspectObject } from 'util'
|
||||||
|
import { exists as pathExists } from 'fs/promises'
|
||||||
|
|
||||||
import Client from './classes/Client'
|
import Client from './classes/Client'
|
||||||
|
|
||||||
@@ -36,10 +38,6 @@ if (!['development', 'production'].includes(environment)) {
|
|||||||
|
|
||||||
logger.info(`Running in ${environment} mode...`)
|
logger.info(`Running in ${environment} mode...`)
|
||||||
|
|
||||||
if (environment === 'production' && process.env['IS_USING_DOT_ENV']) {
|
|
||||||
logger.warn('You seem to be using .env files, this is generally not a good idea in production...')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env['WIT_AI_TOKEN']) {
|
if (!process.env['WIT_AI_TOKEN']) {
|
||||||
logger.error('WIT_AI_TOKEN is not defined in the environment variables')
|
logger.error('WIT_AI_TOKEN is not defined in the environment variables')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
@@ -47,7 +45,14 @@ if (!process.env['WIT_AI_TOKEN']) {
|
|||||||
|
|
||||||
// Workers and API clients
|
// Workers and API clients
|
||||||
|
|
||||||
const tesseract = await createTesseractWorker('eng')
|
const TesseractWorkerPath = joinPath(import.meta.dir, 'worker', 'index.js')
|
||||||
|
const TesseractCompiledWorkerExists = await pathExists(TesseractWorkerPath)
|
||||||
|
const tesseract = await createTesseractWorker(
|
||||||
|
'eng',
|
||||||
|
OEM.DEFAULT,
|
||||||
|
TesseractCompiledWorkerExists ? { workerPath: TesseractWorkerPath } : undefined,
|
||||||
|
)
|
||||||
|
|
||||||
const wit = {
|
const wit = {
|
||||||
token: process.env['WIT_AI_TOKEN']!,
|
token: process.env['WIT_AI_TOKEN']!,
|
||||||
async fetch(route: string, options?: RequestInit) {
|
async fetch(route: string, options?: RequestInit) {
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"lib": ["ESNext"],
|
"lib": ["ESNext"],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"],
|
"exclude": ["node_modules", "dist"],
|
||||||
"include": ["./*.json", "src/**/*.ts"]
|
"include": ["./*.json", "src/**/*.ts", "scripts/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import { PermissionFlagsBits } from 'discord.js'
|
|
||||||
import type { Config } from './config.schema'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
owners: ['629368283354628116', '737323631117598811', '282584705218510848'],
|
|
||||||
guilds: ['952946952348270622'],
|
|
||||||
messageScan: {
|
|
||||||
filter: {
|
|
||||||
// Team, Mod, Immunity
|
|
||||||
roles: ['952987191401926697', '955220417969262612', '1027874293192863765'],
|
|
||||||
users: [],
|
|
||||||
// Team, Development
|
|
||||||
channels: ['952987428786941952', '953965039105232906'],
|
|
||||||
whitelist: false,
|
|
||||||
},
|
|
||||||
humanCorrections: {
|
|
||||||
falsePositiveLabel: 'false_positive',
|
|
||||||
allow: {
|
|
||||||
members: {
|
|
||||||
// Team, Supporter
|
|
||||||
roles: ['952987191401926697', '1019903194941362198'],
|
|
||||||
permissions: PermissionFlagsBits.ManageMessages,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allowedAttachmentMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
|
|
||||||
responses: [
|
|
||||||
{
|
|
||||||
triggers: {
|
|
||||||
text: [{ label: 'false_positive', threshold: 0 }],
|
|
||||||
},
|
|
||||||
response: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
logLevel: 'debug',
|
|
||||||
api: {
|
|
||||||
url: 'ws://127.0.0.1:3000',
|
|
||||||
disconnectLimit: 3,
|
|
||||||
},
|
|
||||||
} satisfies Config as Config
|
|
||||||
@@ -1,18 +1,22 @@
|
|||||||
# ⚙️ Configuration
|
# ⚙️ Configuration
|
||||||
|
|
||||||
|
This page tells you how to configure the bot.
|
||||||
|
|
||||||
|
## 📄 JSON config
|
||||||
|
|
||||||
See [`config.ts`](../config.ts).
|
See [`config.ts`](../config.ts).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `config.owners`
|
#### `config.owners`
|
||||||
|
|
||||||
User IDs of the owners of the bot. Only add owners when needed.
|
User IDs of the owners of the bot. Only add owners when needed.
|
||||||
|
|
||||||
### `config.guilds`
|
#### `config.guilds`
|
||||||
|
|
||||||
Servers the bot is allowed to be and register commands in.
|
Servers the bot is allowed to be and register commands in.
|
||||||
|
|
||||||
### `config.logLevel`
|
#### `config.logLevel`
|
||||||
|
|
||||||
The level of logs to print to console. If the level is more important or equally important to set level, it will be forwarded to the console.
|
The level of logs to print to console. If the level is more important or equally important to set level, it will be forwarded to the console.
|
||||||
|
|
||||||
@@ -26,14 +30,42 @@ The possible levels (sorted by their importance descendingly) are:
|
|||||||
- `log`
|
- `log`
|
||||||
- `debug`
|
- `debug`
|
||||||
|
|
||||||
### `config.api.websocketUrl`
|
#### `config.api.url`
|
||||||
|
|
||||||
The WebSocket URL to connect to (including port). Soon auto-discovery will be implemented.
|
WebSocket URL to connect to (including port). Soon auto-discovery will be implemented.
|
||||||
|
|
||||||
### `config.messageScan`
|
#### `config.api.disconnectLimit`
|
||||||
|
|
||||||
|
Amount of times to allow disconnecting before exiting with code `1`.
|
||||||
|
|
||||||
|
#### `config.messageScan`
|
||||||
|
|
||||||
[Please see the next page.](./2_adding_autoresponses.md)
|
[Please see the next page.](./2_adding_autoresponses.md)
|
||||||
|
|
||||||
|
#### `config.moderation`
|
||||||
|
|
||||||
|
TBD.
|
||||||
|
|
||||||
|
#### `config.rolePresets`
|
||||||
|
|
||||||
|
TBD.
|
||||||
|
|
||||||
|
## 🟰 Environment variables
|
||||||
|
|
||||||
|
See [`.env.example`](../.env.example).
|
||||||
|
You can set environment variables in your shell or use a `.env` file which **Bun will automatically load**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `DISCORD_TOKEN`
|
||||||
|
|
||||||
|
The Discord bot token.
|
||||||
|
|
||||||
|
#### `DATABASE_URL`
|
||||||
|
|
||||||
|
The database URL, since we're using SQLite, we'll be using the `file` protocol.
|
||||||
|
Example values are: `file:./revanced.db`, `file:./db.sqlite`, `file:./discord_bot.sqlite`
|
||||||
|
|
||||||
## ⏭️ What's next
|
## ⏭️ What's next
|
||||||
|
|
||||||
The next page will tell you how to configure auto-responses.
|
The next page will tell you how to configure auto-responses.
|
||||||
|
|||||||
@@ -86,4 +86,4 @@ filterOverride: {
|
|||||||
|
|
||||||
The next page will tell you how to run and bundle the bot.
|
The next page will tell you how to run and bundle the bot.
|
||||||
|
|
||||||
Continue: [🏃🏻♂️ Running the bot](./3_running.md)
|
Continue: [🏃🏻♂️ Running the bot](./3_running_and_deploying.md)
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# 🏃🏻♂️ Running the bot
|
|
||||||
|
|
||||||
There are two methods to run the bot. Choose one that suits best for the situation.
|
|
||||||
|
|
||||||
## 👷🏻 Development mode (recommended)
|
|
||||||
|
|
||||||
There will be no compilation step, and Bun will automatically watch changes and restart the bot for you.
|
|
||||||
|
|
||||||
You can quickly start the bot by running:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
bun dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📦 Building
|
|
||||||
|
|
||||||
There's unfortunately no way to build/bundle the bot yet due to how dynamic imports currently work, though we have a few ideas that may work.
|
|
||||||
As a workaround, you can zip up the whole project, unzip, and run it in development mode using Bun.
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will tell you how to add commands and listen to events to the bot.
|
|
||||||
|
|
||||||
Continue: [✨ Adding commands and listening to events](./4_commands_and_events.md)
|
|
||||||
68
bots/discord/docs/3_running_and_deploying.md
Normal file
68
bots/discord/docs/3_running_and_deploying.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# 🏃🏻♂️ Running and deploying
|
||||||
|
|
||||||
|
There are two methods to run the bot. Choose one that suits best for the situation.
|
||||||
|
|
||||||
|
## 👷🏻 Development mode (recommended)
|
||||||
|
|
||||||
|
There will be no compilation step, and Bun will automatically watch changes and restart the bot for you.
|
||||||
|
|
||||||
|
You can quickly start the bot by running:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Building
|
||||||
|
|
||||||
|
To build the bot, you can run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
The distribution files will be placed inside the `dist` directory. Inside will include:
|
||||||
|
|
||||||
|
- The default configuration for the bot
|
||||||
|
- An empty database for the bot with schemas configured
|
||||||
|
- Compiled source files of the bot
|
||||||
|
|
||||||
|
## ✈️ Deploying
|
||||||
|
|
||||||
|
To deploy the bot, you'll need to:
|
||||||
|
|
||||||
|
1. Replace the `config.ts` file with your own configuration _(optional)_
|
||||||
|
2. [Build the bot as seen in the previous step](#-building)
|
||||||
|
3. Run the `reload-slash-commands` script
|
||||||
|
This is to ensure all commands are registered, so they can be used.
|
||||||
|
**It may take up to 2 hours until **global** commands are updated. This is a Discord limitation.**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Assuming you're in the workspace's root (NOT REPOSITORY ROOT)
|
||||||
|
bun run scripts/reload-slash-commands.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Copy contents of the `dist` directory
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# For instance, we'll copy them both to /usr/src/discord-bot
|
||||||
|
# Assuming you're in the workspace's root (NOT REPOSITORY ROOT)
|
||||||
|
cp -R ./dist/* /usr/src/discord-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Replace the default empty database with your own _(optional)_
|
||||||
|
|
||||||
|
6. Configure environment variables
|
||||||
|
As seen in [`.env.example`](../.env.example). You can also optionally use a `.env` file which **Bun will automatically load**.
|
||||||
|
|
||||||
|
7. Finally, run the bot
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /usr/src/discord-bot
|
||||||
|
bun run src/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⏭️ What's next
|
||||||
|
|
||||||
|
The next page will tell you how to add commands and listen to events to the bot.
|
||||||
|
|
||||||
|
Continue: [✨ Adding commands and listening to events](./4_commands_and_events.md)
|
||||||
@@ -74,34 +74,49 @@ export default {
|
|||||||
|
|
||||||
Events are a bit different. We have 2 different event systems for both Discord API and our own bot API. This means the [`src/events`](../src/events) directory will have 2 separate directories inside. They are specific to the respective API, but the utility functions make the experience with both of them very similar.
|
Events are a bit different. We have 2 different event systems for both Discord API and our own bot API. This means the [`src/events`](../src/events) directory will have 2 separate directories inside. They are specific to the respective API, but the utility functions make the experience with both of them very similar.
|
||||||
|
|
||||||
To start adding events, you can use this template:
|
To start adding events, you can use these templates:
|
||||||
|
|
||||||
|
##### Discord event template
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// For Discord events (remove functions you do not use)
|
import { on, once, withContext } from '$utils/discord/events'
|
||||||
import { on, once } from '$utils/discord/events'
|
|
||||||
|
|
||||||
// You will have auto-complete and types for all of them, don't worry!
|
on('eventName', async (arg1, arg2, ...) => {
|
||||||
// WARNING: The first argument is the `context` object for Discord events
|
// Do something when the event is triggered
|
||||||
// This is intended by design because Discord events usually always use it.
|
})
|
||||||
on('eventName', async (context, arg1, arg2, ...) => {
|
|
||||||
// Do something in here when the event is triggered
|
once('eventName', async (arg1, arg2, ...) => {
|
||||||
|
// Do something for only a single time after it's triggered, never again
|
||||||
|
})
|
||||||
|
|
||||||
|
withContext(on, 'eventName', async (context, arg1, arg2, ...) => {
|
||||||
|
// Do some other thing that requires the context object
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### API events template
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// For "Helper" events (remove functions you do not use)
|
|
||||||
import { on, once } from '$utils/api/events'
|
import { on, once } from '$utils/api/events'
|
||||||
|
|
||||||
// You will have auto-complete and types for all of them, don't worry!
|
|
||||||
on('eventName', async (arg1, arg2, ...) => {
|
on('eventName', async (arg1, arg2, ...) => {
|
||||||
// Do something in here when the event is triggered
|
// Do something when the event is triggered
|
||||||
|
})
|
||||||
|
|
||||||
|
once('eventName', async (arg1, arg2, ...) => {
|
||||||
|
// Do something for only a single time after it's triggered, never again
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
API events are stored in [`src/events/api`](../src/events/api), and Discord events are in [`src/events/discord`](../src/events/discord).
|
API events are stored in [`src/events/api`](../src/events/api), and Discord events are in [`src/events/discord`](../src/events/discord).
|
||||||
|
|
||||||
|
### 📛 Event file naming conventions
|
||||||
|
|
||||||
|
Since a single event file can have multiple listeners, you should name exactly what the file handles.
|
||||||
|
For example, when a nickname change happens, a member joins, or a member sends a message, the bot is required to cure their nickname. Therefore we would name the event file `curedRequired.ts`.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> If you need multiple event listeners for the same exact event, you can put them in a directory with the event name and rename the listeners to what they handle specifically. You can see how we do it in [`src/events/discord/interactionCreate`](../src/events/discord/interactionCreate).
|
> If you need multiple event listeners for the same exact event **but also need more abstraction**, you can put them in a directory with the event name and rename the listeners to what they handle specifically. You can see how we do it in [`src/events/discord/interactionCreate`](../src/events/discord/interactionCreate).
|
||||||
|
|
||||||
## ⏭️ What's next
|
## ⏭️ What's next
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
# 🫙 Storing data
|
|
||||||
|
|
||||||
We use SQLite to store every piece of persistent data. By using Bun, we get access to the `bun:sqlite` module which allows us to easily do SQLite operations.
|
|
||||||
|
|
||||||
## 🪄 Creating a database
|
|
||||||
|
|
||||||
You can easily create a database by initializing the `BasicDatabase` class:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface MyDatabase {
|
|
||||||
field: string
|
|
||||||
key: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const db = new BasicDatabase<MyDatabase>(
|
|
||||||
// File path
|
|
||||||
'database_file.db',
|
|
||||||
// Database schema, in SQL
|
|
||||||
`field TEXT NOT NULL, key TEXT PRIMARY KEY NOT NULL`,
|
|
||||||
// Custom table name (optional, defaults to 'data'),
|
|
||||||
'data'
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📝 Writing data
|
|
||||||
|
|
||||||
Initializing `MyDatabase` will immediately create/open the `database_file.db` file. To write data, you can use the `insert` or `update` method:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const key = 'my key'
|
|
||||||
const field = 'some data'
|
|
||||||
|
|
||||||
// Order is according to the schema
|
|
||||||
// db.insert(...columns)
|
|
||||||
db.insert(field, key)
|
|
||||||
|
|
||||||
const field2 = 'some other data'
|
|
||||||
|
|
||||||
// db.update(data, filter)
|
|
||||||
db.update({
|
|
||||||
field: field2
|
|
||||||
}, `key = ${key}`)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also delete a row:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
db.delete(`key = ${key}`)
|
|
||||||
|
|
||||||
console.log(db.select(`key = ${key}`)) // null
|
|
||||||
```
|
|
||||||
|
|
||||||
## 👀 Reading data
|
|
||||||
|
|
||||||
To get data using a filter, you can use the `select` method:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// We insert it back
|
|
||||||
db.insert(field, key)
|
|
||||||
|
|
||||||
const data = db.select('*', `key = ${key}`)
|
|
||||||
console.log(data) // { key: 'my key', field: 'some other data' }
|
|
||||||
|
|
||||||
const { key: someKey } = db.select('key', `field = '${field2}'`)
|
|
||||||
console.log(someKey) // 'my key'
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
If the existing abstractions aren't enough, you can also use the `run`, `prepare`, or `query` method:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Enable WAL
|
|
||||||
db.run('PRAGMA journal_mode=WAL')
|
|
||||||
|
|
||||||
const selectFromKey = db.prepare('SELECT * FROM data WHERE key = $key')
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
selectFromKey.get({
|
|
||||||
$key: key
|
|
||||||
})
|
|
||||||
) // { key: 'my key', field: 'some other data' }
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
selectFromKey.get({
|
|
||||||
$key: 'non existent key'
|
|
||||||
})
|
|
||||||
) // null
|
|
||||||
```
|
|
||||||
@@ -4,13 +4,15 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "🤖 Discord bot assisting ReVanced",
|
"description": "🤖 Discord bot assisting ReVanced",
|
||||||
"main": "dist/index.js",
|
"main": "src/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"register": "bun run scripts/reload-slash-commands.ts",
|
"register": "bun run scripts/reload-slash-commands.ts",
|
||||||
"dev": "bun --watch src/index.ts",
|
"start": "bun run scripts/generate-indexes.ts && bun run src/index.ts",
|
||||||
"prepare": "drizzle-kit push",
|
"dev": "bun run scripts/generate-indexes.ts && bun --watch src/index.ts",
|
||||||
"build": "tsc",
|
"build:config": "bun build config.ts --outdir=dist",
|
||||||
"watch": "bun dev"
|
"build": "bun prepare && bun build:config && bun build src/index.ts -e ./config.js --target=bun --outdir=dist/src && DATABASE_URL=dist/db.sqlite3 drizzle-kit push",
|
||||||
|
"watch": "bun dev",
|
||||||
|
"prepare": "bun run scripts/generate-indexes.ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -28,15 +30,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/revanced/revanced-helper#readme",
|
"homepage": "https://github.com/revanced/revanced-helper#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@discordjs/builders": "^1.8.2",
|
||||||
|
"@discordjs/rest": "^2.3.0",
|
||||||
|
"@libsql/client": "^0.6.2",
|
||||||
"@revanced/bot-api": "workspace:*",
|
"@revanced/bot-api": "workspace:*",
|
||||||
"@revanced/bot-shared": "workspace:*",
|
"@revanced/bot-shared": "workspace:*",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"decancer": "^3.2.2",
|
"decancer": "^3.2.2",
|
||||||
"discord.js": "^14.15.3",
|
"discord.js": "^14.15.3",
|
||||||
|
"drizzle-kit": "^0.22.7",
|
||||||
"drizzle-orm": "^0.31.2"
|
"drizzle-orm": "^0.31.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@libsql/client": "^0.6.2",
|
"discord-api-types": "^0.37.91"
|
||||||
"drizzle-kit": "^0.22.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
bots/discord/scripts/generate-indexes.ts
Normal file
6
bots/discord/scripts/generate-indexes.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { join } from 'path'
|
||||||
|
import { generateCommandsIndex, generateEventsIndex } from '../src/utils/fs'
|
||||||
|
|
||||||
|
await generateCommandsIndex(join(import.meta.dir, '../src/commands'))
|
||||||
|
await generateEventsIndex(join(import.meta.dir, '../src/events/discord'))
|
||||||
|
await generateEventsIndex(join(import.meta.dir, '../src/events/api'))
|
||||||
@@ -2,12 +2,12 @@ import { inspect } from 'util'
|
|||||||
import { SlashCommandBuilder } from 'discord.js'
|
import { SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import { createSuccessEmbed } from '$/utils/discord/embeds'
|
import { createSuccessEmbed } from '$/utils/discord/embeds'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('eval')
|
.setName('eval')
|
||||||
.setDescription('Evaluates something')
|
.setDescription('Make the bot less sentient by evaluating code')
|
||||||
.addStringOption(option => option.setName('code').setDescription('The code to evaluate').setRequired(true))
|
.addStringOption(option => option.setName('code').setDescription('The code to evaluate').setRequired(true))
|
||||||
.setDMPermission(true)
|
.setDMPermission(true)
|
||||||
.toJSON(),
|
.toJSON(),
|
||||||
@@ -15,8 +15,7 @@ export default {
|
|||||||
ownerOnly: true,
|
ownerOnly: true,
|
||||||
global: true,
|
global: true,
|
||||||
|
|
||||||
// @ts-expect-error: Needed for science
|
async execute(_, interaction) {
|
||||||
async execute(context, interaction) {
|
|
||||||
const code = interaction.options.getString('code', true)
|
const code = interaction.options.getString('code', true)
|
||||||
|
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
|
|||||||
@@ -1,41 +1,26 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js'
|
import { SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('exception-test')
|
.setName('exception-test')
|
||||||
.setDescription('throw up pls')
|
.setDescription('Makes the bot intentionally hate you by throwing an exception')
|
||||||
.addStringOption(option =>
|
.addStringOption(option =>
|
||||||
option
|
option
|
||||||
.setName('type')
|
.setName('type')
|
||||||
.setDescription('The type of exception to throw')
|
.setDescription('The type of exception to throw')
|
||||||
.addChoices({
|
.setRequired(true)
|
||||||
name: 'process exception',
|
.addChoices(
|
||||||
value: 'Process',
|
Object.keys(CommandErrorType).map(
|
||||||
})
|
k =>
|
||||||
.addChoices({
|
({
|
||||||
name: 'generic error',
|
name: k,
|
||||||
value: 'Generic',
|
value: k,
|
||||||
})
|
}) as const,
|
||||||
.addChoices({
|
),
|
||||||
name: 'invalid argument',
|
),
|
||||||
value: 'InvalidArgument',
|
|
||||||
})
|
|
||||||
.addChoices({
|
|
||||||
name: 'invalid channel',
|
|
||||||
value: 'InvalidChannel',
|
|
||||||
})
|
|
||||||
.addChoices({
|
|
||||||
name: 'invalid user',
|
|
||||||
value: 'InvalidUser',
|
|
||||||
})
|
|
||||||
.addChoices({
|
|
||||||
name: 'invalid duration',
|
|
||||||
value: 'InvalidDuration',
|
|
||||||
})
|
|
||||||
.setRequired(true),
|
|
||||||
)
|
)
|
||||||
.setDMPermission(true)
|
.setDMPermission(true)
|
||||||
.toJSON(),
|
.toJSON(),
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js'
|
import { SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder().setName('stop').setDescription('Stops the bot').setDMPermission(true).toJSON(),
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('stop')
|
||||||
|
.setDescription(
|
||||||
|
"You don't want to run this unless the bot starts to go insane, and like, you really need to stop it.",
|
||||||
|
)
|
||||||
|
.setDMPermission(true)
|
||||||
|
.toJSON(),
|
||||||
|
|
||||||
ownerOnly: true,
|
ownerOnly: true,
|
||||||
global: true,
|
global: true,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
|
|||||||
|
|
||||||
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'
|
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder().setName('coinflip').setDescription('Do a coinflip!').setDMPermission(true).toJSON(),
|
data: new SlashCommandBuilder().setName('coinflip').setDescription('Do a coinflip!').setDMPermission(true).toJSON(),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { SlashCommandBuilder, type TextBasedChannel } from 'discord.js'
|
import { SlashCommandBuilder, type TextBasedChannel } from 'discord.js'
|
||||||
|
|
||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -1,56 +1,16 @@
|
|||||||
import type { SlashCommandBuilder } from '@discordjs/builders'
|
// AUTO-GENERATED BY A SCRIPT, DON'T TOUCH
|
||||||
import type { ChatInputCommandInteraction } from 'discord.js'
|
|
||||||
|
|
||||||
// Temporary system
|
import './index'
|
||||||
export type Command = {
|
import './fun/reply'
|
||||||
data: ReturnType<SlashCommandBuilder['toJSON']>
|
import './fun/coinflip'
|
||||||
// The function has to return void or Promise<void>
|
import './development/eval'
|
||||||
// because TS may complain about some code paths not returning a value
|
import './development/stop'
|
||||||
/**
|
import './development/exception-test'
|
||||||
* The function to execute when this command is triggered
|
import './moderation/purge'
|
||||||
* @param interaction The interaction that triggered this command
|
import './moderation/cure'
|
||||||
*/
|
import './moderation/role-preset'
|
||||||
execute: (
|
import './moderation/mute'
|
||||||
context: typeof import('../context'),
|
import './moderation/unmute'
|
||||||
interaction: ChatInputCommandInteraction,
|
import './moderation/unban'
|
||||||
info: Info,
|
import './moderation/slowmode'
|
||||||
) => Promise<void> | void
|
import './moderation/ban'
|
||||||
memberRequirements?: {
|
|
||||||
/**
|
|
||||||
* The mode to use when checking for requirements.
|
|
||||||
* - `all` means that the user needs meet all requirements specified.
|
|
||||||
* - `any` means that the user needs to meet any of the requirements specified.
|
|
||||||
*
|
|
||||||
* @default "all"
|
|
||||||
*/
|
|
||||||
mode?: 'all' | 'any'
|
|
||||||
/**
|
|
||||||
* The permissions required to use this command (in BitFields).
|
|
||||||
*
|
|
||||||
* - **0n** means that everyone can use this command.
|
|
||||||
* - **-1n** means that only bot owners can use this command.
|
|
||||||
* @default -1n
|
|
||||||
*/
|
|
||||||
permissions?: bigint
|
|
||||||
/**
|
|
||||||
* The roles required to use this command.
|
|
||||||
* By default, this is set to `[]`.
|
|
||||||
*/
|
|
||||||
roles?: string[]
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Whether this command can only be used by bot owners.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
ownerOnly?: boolean
|
|
||||||
/**
|
|
||||||
* Whether to register this command as a global slash command.
|
|
||||||
* This is set to `false` and commands will be registered in allowed guilds only by default.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
global?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Info {
|
|
||||||
userIsOwner: boolean
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js'
|
import { SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js'
|
import { SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
import { createSuccessEmbed } from '$/utils/discord/embeds'
|
import { createSuccessEmbed } from '$/utils/discord/embeds'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { SlashCommandBuilder } from 'discord.js'
|
|||||||
|
|
||||||
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
||||||
import { applyRolePreset } from '$/utils/discord/rolePresets'
|
import { applyRolePreset } from '$/utils/discord/rolePresets'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
import { createModerationActionEmbed } from '$/utils/discord/embeds'
|
import { createModerationActionEmbed } from '$/utils/discord/embeds'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
|||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
|
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
|
||||||
|
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
|||||||
import { sendPresetReplyAndLogs } from '$/utils/discord/moderation'
|
import { sendPresetReplyAndLogs } from '$/utils/discord/moderation'
|
||||||
import { applyRolePreset, removeRolePreset } from '$/utils/discord/rolePresets'
|
import { applyRolePreset, removeRolePreset } from '$/utils/discord/rolePresets'
|
||||||
import { parseDuration } from '$/utils/duration'
|
import { parseDuration } from '$/utils/duration'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { SlashCommandBuilder } from 'discord.js'
|
|||||||
|
|
||||||
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
import CommandError, { CommandErrorType } from '$/classes/CommandError'
|
||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js'
|
import { SlashCommandBuilder } from 'discord.js'
|
||||||
|
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
import { config } from '$/context'
|
import { config } from '$/context'
|
||||||
import { createModerationActionEmbed } from '$/utils/discord/embeds'
|
import { createModerationActionEmbed } from '$/utils/discord/embeds'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { createModerationActionEmbed } from '$/utils/discord/embeds'
|
|||||||
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
|
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
|
||||||
import { removeRolePreset } from '$/utils/discord/rolePresets'
|
import { removeRolePreset } from '$/utils/discord/rolePresets'
|
||||||
import { and, eq } from 'drizzle-orm'
|
import { and, eq } from 'drizzle-orm'
|
||||||
import type { Command } from '..'
|
import type { Command } from '../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
56
bots/discord/src/commands/types.ts
Normal file
56
bots/discord/src/commands/types.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import type { SlashCommandBuilder } from '@discordjs/builders'
|
||||||
|
import type { ChatInputCommandInteraction } from 'discord.js'
|
||||||
|
|
||||||
|
// Temporary system
|
||||||
|
export type Command = {
|
||||||
|
data: ReturnType<SlashCommandBuilder['toJSON']>
|
||||||
|
// The function has to return void or Promise<void>
|
||||||
|
// because TS may complain about some code paths not returning a value
|
||||||
|
/**
|
||||||
|
* The function to execute when this command is triggered
|
||||||
|
* @param interaction The interaction that triggered this command
|
||||||
|
*/
|
||||||
|
execute: (
|
||||||
|
context: typeof import('../context'),
|
||||||
|
interaction: ChatInputCommandInteraction,
|
||||||
|
info: Info,
|
||||||
|
) => Promise<void> | void
|
||||||
|
memberRequirements?: {
|
||||||
|
/**
|
||||||
|
* The mode to use when checking for requirements.
|
||||||
|
* - `all` means that the user needs meet all requirements specified.
|
||||||
|
* - `any` means that the user needs to meet any of the requirements specified.
|
||||||
|
*
|
||||||
|
* @default "all"
|
||||||
|
*/
|
||||||
|
mode?: 'all' | 'any'
|
||||||
|
/**
|
||||||
|
* The permissions required to use this command (in BitFields).
|
||||||
|
*
|
||||||
|
* - **0n** means that everyone can use this command.
|
||||||
|
* - **-1n** means that only bot owners can use this command.
|
||||||
|
* @default -1n
|
||||||
|
*/
|
||||||
|
permissions?: bigint
|
||||||
|
/**
|
||||||
|
* The roles required to use this command.
|
||||||
|
* By default, this is set to `[]`.
|
||||||
|
*/
|
||||||
|
roles?: string[]
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Whether this command can only be used by bot owners.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
ownerOnly?: boolean
|
||||||
|
/**
|
||||||
|
* Whether to register this command as a global slash command.
|
||||||
|
* This is set to `false` and commands will be registered in allowed guilds only by default.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
global?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Info {
|
||||||
|
userIsOwner: boolean
|
||||||
|
}
|
||||||
@@ -4,13 +4,15 @@ import { createLogger } from '@revanced/bot-shared'
|
|||||||
import { ActivityType, Client as DiscordClient, Partials } from 'discord.js'
|
import { ActivityType, Client as DiscordClient, Partials } from 'discord.js'
|
||||||
import { drizzle } from 'drizzle-orm/bun-sqlite'
|
import { drizzle } from 'drizzle-orm/bun-sqlite'
|
||||||
|
|
||||||
import config from '../config'
|
// Export config first, as commands require them
|
||||||
|
import config from '../config.js'
|
||||||
|
export { config }
|
||||||
|
|
||||||
|
import * as commands from './commands'
|
||||||
import * as schemas from './database/schemas'
|
import * as schemas from './database/schemas'
|
||||||
|
|
||||||
import { loadCommands } from '$utils/discord/commands'
|
import type { Command } from './commands/types'
|
||||||
import { pathJoinCurrentDir } from '$utils/fs'
|
|
||||||
|
|
||||||
export { config }
|
|
||||||
export const logger = createLogger({
|
export const logger = createLogger({
|
||||||
level: config.logLevel === 'none' ? Number.MAX_SAFE_INTEGER : config.logLevel,
|
level: config.logLevel === 'none' ? Number.MAX_SAFE_INTEGER : config.logLevel,
|
||||||
})
|
})
|
||||||
@@ -59,5 +61,5 @@ export const discord = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
commands: await loadCommands(pathJoinCurrentDir(import.meta.url, 'commands')),
|
commands: Object.fromEntries(Object.values<Command>(commands).map((cmd) => [cmd.data.name, cmd])) as Record<string, Command>,
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
5
bots/discord/src/events/api/index.ts
Normal file
5
bots/discord/src/events/api/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// AUTO-GENERATED BY A SCRIPT, DON'T TOUCH
|
||||||
|
|
||||||
|
import './ready'
|
||||||
|
import './disconnect'
|
||||||
|
import './index'
|
||||||
10
bots/discord/src/events/discord/index.ts
Normal file
10
bots/discord/src/events/discord/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// AUTO-GENERATED BY A SCRIPT, DON'T TOUCH
|
||||||
|
|
||||||
|
import './ready'
|
||||||
|
import './cureRequired'
|
||||||
|
import './index'
|
||||||
|
import './messageCreate/messageScanRequired'
|
||||||
|
import './messageReactionAdd/correctResponse'
|
||||||
|
import './interactionCreate/chatCommand'
|
||||||
|
import './interactionCreate/correctResponse'
|
||||||
|
import './guildMemberAdd/applyRolePresets'
|
||||||
2
bots/discord/src/events/register.ts
Normal file
2
bots/discord/src/events/register.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import './discord'
|
||||||
|
import './api'
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { api, discord, logger } from '$/context'
|
import { api, discord, logger } from '$/context'
|
||||||
import { listAllFilesRecursive, pathJoinCurrentDir } from '$utils/fs'
|
|
||||||
import { getMissingEnvironmentVariables } from '@revanced/bot-shared'
|
import { getMissingEnvironmentVariables } from '@revanced/bot-shared'
|
||||||
|
|
||||||
|
import './events/register'
|
||||||
|
|
||||||
// Check if token exists
|
// Check if token exists
|
||||||
const missingEnvs = getMissingEnvironmentVariables(['DISCORD_TOKEN', 'DATABASE_URL'])
|
const missingEnvs = getMissingEnvironmentVariables(['DISCORD_TOKEN', 'DATABASE_URL'])
|
||||||
if (missingEnvs.length) {
|
if (missingEnvs.length) {
|
||||||
@@ -9,14 +10,5 @@ if (missingEnvs.length) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const event of listAllFilesRecursive(pathJoinCurrentDir(import.meta.url, 'events', 'api'))) {
|
|
||||||
await import(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
api.client.connect()
|
api.client.connect()
|
||||||
|
|
||||||
for (const event of listAllFilesRecursive(pathJoinCurrentDir(import.meta.url, 'events', 'discord'))) {
|
|
||||||
await import(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
discord.client.login()
|
discord.client.login()
|
||||||
|
|||||||
5
bots/discord/src/types.d.ts
vendored
5
bots/discord/src/types.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
type IfExtends<T, U, True, False> = T extends U ? True : False
|
|
||||||
type IfTrue<Condition, True, False> = IfExtends<Condition, true, True, False>
|
|
||||||
type EmptyObject<K = PropertyKey> = Record<K, never>
|
|
||||||
type ValuesOf<T> = T[keyof T]
|
|
||||||
type MaybeArray<T> = T | T[]
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Command } from '$commands'
|
import type { Command } from '$commands/types'
|
||||||
import { listAllFilesRecursive } from '$utils/fs'
|
import { listAllFilesRecursive } from '$utils/fs'
|
||||||
|
|
||||||
export const loadCommands = async (dir: string) => {
|
export const loadCommands = async (dir: string) => {
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
import { readdirSync } from 'fs'
|
import { readdirSync, writeFileSync } from 'fs'
|
||||||
import { dirname, join } from 'path'
|
import { join, relative } from 'path'
|
||||||
import { fileURLToPath } from 'bun'
|
|
||||||
|
|
||||||
export const listAllFilesRecursive = (dir: string): string[] =>
|
export const listAllFilesRecursive = (dir: string): string[] =>
|
||||||
readdirSync(dir, { recursive: true, withFileTypes: true })
|
readdirSync(dir, { recursive: true, withFileTypes: true })
|
||||||
.filter(x => x.isFile())
|
.filter(x => x.isFile())
|
||||||
.map(x => join(x.parentPath, x.name))
|
.map(x => join(x.parentPath, x.name))
|
||||||
|
|
||||||
export const pathJoinCurrentDir = (importMetaUrl: string, ...objects: [string, ...string[]]) =>
|
export const generateCommandsIndex = (dirPath: string) => generateIndexes(dirPath, x => !x.endsWith('types.ts'))
|
||||||
join(dirname(fileURLToPath(importMetaUrl)), ...objects)
|
|
||||||
|
export const generateEventsIndex = (dirPath: string) => generateIndexes(dirPath)
|
||||||
|
|
||||||
|
const generateIndexes = async (dirPath: string, pathFilter?: (path: string) => boolean) => {
|
||||||
|
const files = listAllFilesRecursive(dirPath)
|
||||||
|
.filter(x => (x.endsWith('.ts') && !x.endsWith('index.ts') && pathFilter ? pathFilter(x) : true))
|
||||||
|
.map(x => relative(dirPath, x))
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
join(dirPath, 'index.ts'),
|
||||||
|
`// AUTO-GENERATED BY A SCRIPT, DON'T TOUCH\n\n${files.map(c => `import './${c.split('.').at(-2)}'`).join('\n')}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,8 +18,18 @@
|
|||||||
"$commands": ["./src/commands/index.ts"],
|
"$commands": ["./src/commands/index.ts"],
|
||||||
"$commands/*": ["./src/commands/*"]
|
"$commands/*": ["./src/commands/*"]
|
||||||
},
|
},
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"transform": "typescript-transform-path-rewrite"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"],
|
"exclude": [
|
||||||
"include": ["./**/*.ts", "./*.ts"]
|
"node_modules",
|
||||||
|
"dist",
|
||||||
|
"./config.schema.ts",
|
||||||
|
"./drizzle.config.ts"
|
||||||
|
],
|
||||||
|
"include": ["./src/**/*.ts", "./scripts/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,24 @@ To start developing, you'll need to set up the development environment first.
|
|||||||
|
|
||||||
3. Install dependencies
|
3. Install dependencies
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Build packages/libraries
|
4. Install Git hooks for linting (optional, but recommended)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun run build
|
bunx lefthook install
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Change your directory to a project's root
|
5. Build packages/libraries
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun run build:packages
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Change your directory to a project's root
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# WebSocket API
|
# WebSocket API
|
||||||
cd apis/websocket
|
cd apis/websocket
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -6,14 +6,14 @@
|
|||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
|
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
|
||||||
"workspaces": ["apis/*", "bots/*", "packages/*"],
|
"workspaces": ["packages/*", "apis/*", "bots/*"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
|
"build:packages": "turbo run build --filter=\"./packages/*\"",
|
||||||
"watch": "turbo run watch",
|
"watch": "turbo run watch",
|
||||||
"flint": "biome check --apply .",
|
"flint": "biome check --write .",
|
||||||
"flint:check": "biome check .",
|
"flint:check": "biome check .",
|
||||||
"clint": "commitlint --edit",
|
"clint": "commitlint --edit"
|
||||||
"prepare": "lefthook install"
|
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/revanced/revanced-helper#readme",
|
"homepage": "https://github.com/revanced/revanced-helper#readme",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"Palm <contact@palmdevs.me> (https://palmdevs.me)",
|
"Palm <contact@palmdevs.me> (https://palmdevs.me)",
|
||||||
"ReVanced <nosupport@revanced.app> (https://revanced.app)"
|
"ReVanced <nosupport@revanced.app> (https://revanced.app)"
|
||||||
],
|
],
|
||||||
|
"packageManager": "pnpm@9.4.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.8.2",
|
"@biomejs/biome": "^1.8.2",
|
||||||
"@commitlint/cli": "^19.3.0",
|
"@commitlint/cli": "^19.3.0",
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"conventional-changelog-conventionalcommits": "^7.0.2",
|
"conventional-changelog-conventionalcommits": "^7.0.2",
|
||||||
"lefthook": "^1.6.18",
|
"lefthook": "^1.6.18",
|
||||||
"turbo": "^1.13.4",
|
"turbo": "2",
|
||||||
"typescript": "^5.5.2"
|
"typescript": "^5.5.2"
|
||||||
},
|
},
|
||||||
"trustedDependencies": ["@biomejs/biome", "esbuild", "lefthook"]
|
"trustedDependencies": ["@biomejs/biome", "esbuild", "lefthook"]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"ws": "^8.17.1"
|
"ws": "^8.17.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/ws": "^8.5.10",
|
||||||
"typed-emitter": "^2.1.0"
|
"typed-emitter": "^2.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,6 @@
|
|||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"noEmit": false
|
"noEmit": false
|
||||||
}
|
},
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,6 @@
|
|||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"noEmit": false
|
"noEmit": false
|
||||||
}
|
},
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,7 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"allowImportingTsExtensions": false
|
"allowImportingTsExtensions": false
|
||||||
}
|
},
|
||||||
|
"include": ["./**/*"],
|
||||||
|
"exclude": ["./packages/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"pipeline": {
|
"tasks": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": ["dist/**"],
|
"outputs": ["dist/**"],
|
||||||
"outputMode": "errors-only"
|
"outputLogs": "errors-only"
|
||||||
},
|
},
|
||||||
"watch": {
|
"watch": {
|
||||||
"dependsOn": ["^watch"],
|
"dependsOn": ["^watch"],
|
||||||
"outputMode": "errors-only"
|
"outputLogs": "errors-only"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user