Compare commits

...

28 Commits

Author SHA1 Message Date
semantic-release-bot
dfa08397d4 chore(release): 1.5.3 [skip ci]
## @revanced/discord-bot [1.5.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.5.2...@revanced/discord-bot@1.5.3) (2026-01-17)

### Bug Fixes

* **bots/discord:** more logging during apply role preset on member join ([77626c9](77626c9aed))
2026-01-17 14:22:11 +00:00
PalmDevs
9626c4d9ba chore(bots/discord): revert c68cfd1
Revert "feat(bots/discord): support training without label"
2026-01-17 21:21:20 +07:00
PalmDevs
5f74f2dcf8 chore(bots/discord): revert fbd9480
Revert "fix(bots/discord): pass non-empty out of scope label to discord"
2026-01-17 21:21:19 +07:00
PalmDevs
77626c9aed fix(bots/discord): more logging during apply role preset on member join
It seems to keep skipping some members for some reason...
2026-01-17 21:14:22 +07:00
semantic-release-bot
2c8740e489 chore(release): 1.5.2 [skip ci]
## @revanced/discord-bot [1.5.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.5.1...@revanced/discord-bot@1.5.2) (2025-09-25)

### Bug Fixes

* **bots/discord:** correct `respondToReply` logic ([6fe1530](6fe15301a2))
2025-09-25 16:19:07 +00:00
PalmDevs
6fe15301a2 fix(bots/discord): correct respondToReply logic 2025-09-25 23:17:29 +07:00
semantic-release-bot
6885e18976 chore(release): 1.5.1 [skip ci]
## @revanced/discord-bot [1.5.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.5.0...@revanced/discord-bot@1.5.1) (2025-09-13)

### Bug Fixes

* **bots/discord:** only fetch reference when it exists when `respondToReply` is set ([42038e6](42038e6b38))
2025-09-13 17:34:48 +00:00
Palm
42038e6b38 fix(bots/discord): only fetch reference when it exists when respondToReply is set
whoops, testing in production
2025-09-14 00:34:00 +07:00
semantic-release-bot
51c0252b44 chore(release): 1.5.0 [skip ci]
# @revanced/discord-bot [1.5.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.4.1...@revanced/discord-bot@1.5.0) (2025-09-13)

### Features

* **bots/discord:** add additional options for `respondToReply` ([399c201](399c201f8c))
2025-09-13 13:43:19 +00:00
PalmDevs
399c201f8c feat(bots/discord): add additional options for respondToReply 2025-09-13 20:42:34 +07:00
semantic-release-bot
0a5a9c3e27 chore(release): 1.4.1 [skip ci]
## @revanced/discord-bot [1.4.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.4.0...@revanced/discord-bot@1.4.1) (2025-09-13)

### Bug Fixes

* **bots/discord/database:** use non-deprecated way to create indexes ([8cc2377](8cc2377cbf))
* **bots/discord:** allow partial users ([dd3e7d2](dd3e7d2ee0))
* **bots/discord:** error in cache `keepOverLimit` comparison ([567c5d2](567c5d2c7f))
2025-09-13 12:28:35 +00:00
PalmDevs
50a205c430 chore: update dependencies 2025-09-13 19:27:45 +07:00
PalmDevs
dd3e7d2ee0 fix(bots/discord): allow partial users 2025-09-13 19:27:43 +07:00
PalmDevs
567c5d2c7f fix(bots/discord): error in cache keepOverLimit comparison 2025-09-13 19:27:42 +07:00
PalmDevs
8cc2377cbf fix(bots/discord/database): use non-deprecated way to create indexes 2025-09-13 19:27:41 +07:00
semantic-release-bot
f3c199d573 chore(release): 1.4.0 [skip ci]
# @revanced/discord-bot [1.4.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.3.0...@revanced/discord-bot@1.4.0) (2025-07-11)

### Bug Fixes

* **bots/discord:** pass non-empty out of scope label to discord ([fbd9480](fbd9480036))

### Features

* **bots/discord:** react to label classified response ([96a9b83](96a9b83c48))
2025-07-11 18:23:24 +00:00
PalmDevs
42c0facef1 chore: format 2025-07-12 01:22:30 +07:00
PalmDevs
96a9b83c48 feat(bots/discord): react to label classified response 2025-07-12 01:22:28 +07:00
PalmDevs
fbd9480036 fix(bots/discord): pass non-empty out of scope label to discord 2025-07-12 01:22:27 +07:00
semantic-release-bot
cc02c0a775 chore(release): 1.3.0 [skip ci]
# @revanced/discord-bot [1.3.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.3...@revanced/discord-bot@1.3.0) (2025-07-11)

### Features

* **bots/discord:** support training without label ([c68cfd1](c68cfd1c01))
2025-07-11 18:08:35 +00:00
semantic-release-bot
c83e219088 chore(release): 1.1.0 [skip ci]
# @revanced/bot-websocket-api [1.1.0](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.2...@revanced/bot-websocket-api@1.1.0) (2025-07-11)

### Features

* **apis/websocket:** support training without label ([670cc70](670cc70057))
2025-07-11 18:07:46 +00:00
PalmDevs
6164e6c1a5 chore: format 2025-07-12 01:06:45 +07:00
PalmDevs
47d2f8c015 chore: update dependencies and remove outdated patches 2025-07-12 01:06:43 +07:00
PalmDevs
c68cfd1c01 feat(bots/discord): support training without label 2025-07-12 01:06:41 +07:00
PalmDevs
2b1928e116 feat(packages/api): support training without label 2025-07-12 01:06:39 +07:00
PalmDevs
670cc70057 feat(apis/websocket): support training without label 2025-07-12 01:06:37 +07:00
Palm
439f301872 chore: merge dev to main 2025-07-11 21:05:10 +07:00
Pun Butrach
aeccf12906 ci: Use default fetch-depth of 1 for checkout step (#47) 2025-07-08 18:14:54 +02:00
48 changed files with 865 additions and 296 deletions

View File

@@ -20,11 +20,6 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
# Make sure the release step uses its own credentials:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Cache dependencies - name: Cache dependencies
uses: actions/cache@v4 uses: actions/cache@v4

View File

@@ -1,3 +1,10 @@
# @revanced/bot-websocket-api [1.1.0](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.2...@revanced/bot-websocket-api@1.1.0) (2025-07-11)
### Features
* **apis/websocket:** support training without label ([670cc70](https://github.com/revanced/revanced-bots/commit/670cc700570b32924738c3f24acb9f5312d2dcdb))
## @revanced/bot-websocket-api [1.0.2](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.1...@revanced/bot-websocket-api@1.0.2) (2025-06-23) ## @revanced/bot-websocket-api [1.0.2](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.1...@revanced/bot-websocket-api@1.0.2) (2025-06-23)
## @revanced/bot-websocket-api [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0...@revanced/bot-websocket-api@1.0.1) (2025-04-09) ## @revanced/bot-websocket-api [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0...@revanced/bot-websocket-api@1.0.1) (2025-04-09)

View File

@@ -2,7 +2,7 @@
"name": "@revanced/bot-websocket-api", "name": "@revanced/bot-websocket-api",
"type": "module", "type": "module",
"private": true, "private": true,
"version": "1.0.2", "version": "1.1.0",
"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": {
@@ -29,9 +29,9 @@
"dependencies": { "dependencies": {
"@revanced/bot-shared": "workspace:*", "@revanced/bot-shared": "workspace:*",
"@sapphire/async-queue": "^1.5.5", "@sapphire/async-queue": "^1.5.5",
"chalk": "^5.4.1", "chalk": "^5.6.2",
"tesseract.js": "^6.0.1", "tesseract.js": "^6.0.1",
"ws": "^8.18.2" "ws": "^8.18.3"
}, },
"devDependencies": { "devDependencies": {
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",

View File

@@ -23,12 +23,12 @@ export const wit = {
if (!res.ok) throw new Error(`Failed to fetch from Wit.ai: ${res.statusText} (${res.status})`) if (!res.ok) throw new Error(`Failed to fetch from Wit.ai: ${res.statusText} (${res.status})`)
return await res.json() return (await res.json()) as WitMessageResponse
}, },
message(text: string) { message(text: string) {
return this.fetch(`/message?q=${encodeURIComponent(text)}&n=8`) as Promise<WitMessageResponse> return this.fetch(`/message?q=${encodeURIComponent(text)}&n=8`) as Promise<WitMessageResponse>
}, },
async train(text: string, label: string) { async train(text: string, label?: string) {
await this.fetch('/utterances', { await this.fetch('/utterances', {
body: JSON.stringify([ body: JSON.stringify([
{ {
@@ -41,7 +41,14 @@ export const wit = {
method: 'POST', method: 'POST',
}) })
}, },
} as const } satisfies Wit
export interface Wit {
token: string
fetch(route: string, options?: RequestInit): Promise<WitMessageResponse>
message(text: string): Promise<WitMessageResponse>
train(text: string, label?: string): Promise<void>
}
export interface WitMessageResponse { export interface WitMessageResponse {
text: string text: string

View File

@@ -1,7 +1,7 @@
import type { ClientOperation, Logger } from '@revanced/bot-shared' import type { ClientOperation, Logger } from '@revanced/bot-shared'
import type { Worker as TesseractWorker } from 'tesseract.js' import type { Worker as TesseractWorker } from 'tesseract.js'
import type { ClientPacketObject } from '../classes/Client' import type { ClientPacketObject } from '../classes/Client'
import type { WitMessageResponse } from '../context' import type { Wit } from '../context'
import type { Config } from '../utils/config' import type { Config } from '../utils/config'
export { default as parseImageEventHandler } from './parseImage' export { default as parseImageEventHandler } from './parseImage'
@@ -14,10 +14,7 @@ export type EventHandler<POp extends ClientOperation> = (
) => void | Promise<void> ) => void | Promise<void>
export type EventContext = { export type EventContext = {
wit: { wit: Wit
train(text: string, label: string): Promise<void>
message(text: string): Promise<WitMessageResponse>
}
tesseract: TesseractWorker tesseract: TesseractWorker
logger: Logger logger: Logger
config: Config config: Config

View File

@@ -10,7 +10,7 @@ const trainMessageEventHandler: EventHandler<ClientOperation.TrainMessage> = asy
const nextSeq = client.currentSequence++ const nextSeq = client.currentSequence++
const actualText = text.slice(0, 279) const actualText = text.slice(0, 279)
logger.debug(`${client.id} requested to train label ${label} (${nextSeq}) with:`, actualText) logger.debug(`${client.id} requested to train label ${label ?? '<out of scope>'} (${nextSeq}) with:`, actualText)
try { try {
await wit.train(actualText, label) await wit.train(actualText, label)

View File

@@ -1,3 +1,59 @@
## @revanced/discord-bot [1.5.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.5.2...@revanced/discord-bot@1.5.3) (2026-01-17)
### Bug Fixes
* **bots/discord:** more logging during apply role preset on member join ([77626c9](https://github.com/revanced/revanced-bots/commit/77626c9aeddceb25c2f0d418bf764efc66669baf))
## @revanced/discord-bot [1.5.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.5.1...@revanced/discord-bot@1.5.2) (2025-09-25)
### Bug Fixes
* **bots/discord:** correct `respondToReply` logic ([6fe1530](https://github.com/revanced/revanced-bots/commit/6fe15301a21fdc196fded8d6fb13236a7bb826f5))
## @revanced/discord-bot [1.5.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.5.0...@revanced/discord-bot@1.5.1) (2025-09-13)
### Bug Fixes
* **bots/discord:** only fetch reference when it exists when `respondToReply` is set ([42038e6](https://github.com/revanced/revanced-bots/commit/42038e6b38983fefe79481359bad300dcb5e83b4))
# @revanced/discord-bot [1.5.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.4.1...@revanced/discord-bot@1.5.0) (2025-09-13)
### Features
* **bots/discord:** add additional options for `respondToReply` ([399c201](https://github.com/revanced/revanced-bots/commit/399c201f8c3e9e116050b49c2ffccdd79b02f39b))
## @revanced/discord-bot [1.4.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.4.0...@revanced/discord-bot@1.4.1) (2025-09-13)
### Bug Fixes
* **bots/discord/database:** use non-deprecated way to create indexes ([8cc2377](https://github.com/revanced/revanced-bots/commit/8cc2377cbfcc74c2c3228ed18da2495b4efd45aa))
* **bots/discord:** allow partial users ([dd3e7d2](https://github.com/revanced/revanced-bots/commit/dd3e7d2ee0cdf40e083af7f6db35ac3508ddf763))
* **bots/discord:** error in cache `keepOverLimit` comparison ([567c5d2](https://github.com/revanced/revanced-bots/commit/567c5d2c7f41dae7d7fec9946a7dd2ac1b10cc2a))
# @revanced/discord-bot [1.4.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.3.0...@revanced/discord-bot@1.4.0) (2025-07-11)
### Bug Fixes
* **bots/discord:** pass non-empty out of scope label to discord ([fbd9480](https://github.com/revanced/revanced-bots/commit/fbd948003631b48a1914eb7b2551ead4b05089b7))
### Features
* **bots/discord:** react to label classified response ([96a9b83](https://github.com/revanced/revanced-bots/commit/96a9b83c486fdc6e78f4c59e197fa6c1dab09161))
# @revanced/discord-bot [1.3.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.3...@revanced/discord-bot@1.3.0) (2025-07-11)
### Features
* **bots/discord:** support training without label ([c68cfd1](https://github.com/revanced/revanced-bots/commit/c68cfd1c01703fad17f233ad4e13cf81913afbb6))
## @revanced/discord-bot [1.2.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.2...@revanced/discord-bot@1.2.3) (2025-06-23) ## @revanced/discord-bot [1.2.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.2...@revanced/discord-bot@1.2.3) (2025-06-23)

View File

@@ -73,7 +73,7 @@ export type ConfigMessageScanResponse = {
} }
filterOverride?: NonNullable<Config['messageScan']>['filter'] filterOverride?: NonNullable<Config['messageScan']>['filter']
response: ConfigMessageScanResponseMessage | null response: ConfigMessageScanResponseMessage | null
respondToReply?: boolean respondToReply?: boolean | 'only_regex' | 'only_labeled'
} }
export type ConfigMessageScanResponseLabelConfig = { export type ConfigMessageScanResponseLabelConfig = {

View File

@@ -2,7 +2,7 @@
"name": "@revanced/discord-bot", "name": "@revanced/discord-bot",
"type": "module", "type": "module",
"private": true, "private": true,
"version": "1.2.3", "version": "1.5.3",
"description": "🤖 Discord bot assisting ReVanced", "description": "🤖 Discord bot assisting ReVanced",
"main": "src/index.ts", "main": "src/index.ts",
"scripts": { "scripts": {
@@ -28,19 +28,19 @@
}, },
"homepage": "https://github.com/revanced/revanced-bots#readme", "homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": { "dependencies": {
"@discordjs/builders": "^1.11.2", "@discordjs/builders": "^1.11.3",
"@discordjs/rest": "^2.5.1", "@discordjs/rest": "^2.6.0",
"@revanced/bot-api": "workspace:*", "@revanced/bot-api": "workspace:*",
"@revanced/bot-shared": "workspace:*", "@revanced/bot-shared": "workspace:*",
"@sapphire/duration": "^1.2.0", "@sapphire/duration": "^1.2.0",
"chalk": "^5.4.1", "chalk": "^5.6.2",
"decancer": "^3.3.2", "decancer": "^3.3.3",
"discord.js": "^14.20.0", "discord.js": "^14.22.1",
"drizzle-orm": "^0.44.2" "drizzle-orm": "^0.44.5"
}, },
"devDependencies": { "devDependencies": {
"@libsql/client": "^0.15.9", "@libsql/client": "^0.15.15",
"discord-api-types": "^0.38.13", "discord-api-types": "^0.38.24",
"drizzle-kit": "^0.31.2" "drizzle-kit": "^0.31.4"
} }
} }

View File

@@ -1,11 +1,11 @@
import { AdminCommand } from '$/classes/Command'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { parseDuration } from '$/utils/duration'
import { ApplicationCommandOptionType, MessageFlags } from 'discord.js' import { ApplicationCommandOptionType, MessageFlags } from 'discord.js'
import { unlinkSync, writeFileSync } from 'fs' import { unlinkSync, writeFileSync } from 'fs'
import { join } from 'path' import { join } from 'path'
import { inspect } from 'util' import { inspect } from 'util'
import { createContext, runInContext } from 'vm' import { createContext, runInContext } from 'vm'
import { AdminCommand } from '$/classes/Command'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { parseDuration } from '$/utils/duration'
export default new AdminCommand({ export default new AdminCommand({
name: 'eval', name: 'eval',

View File

@@ -1,6 +1,6 @@
import { ApplicationCommandOptionType } from 'discord.js'
import { AdminCommand } from '$/classes/Command' import { AdminCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { ApplicationCommandOptionType } from 'discord.js'
export default new AdminCommand({ export default new AdminCommand({
name: 'exception-test', name: 'exception-test',

View File

@@ -1,5 +1,5 @@
import { AdminCommand } from '$/classes/Command'
import { type CommandInteraction, MessageFlags } from 'discord.js' import { type CommandInteraction, MessageFlags } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
export default new AdminCommand({ export default new AdminCommand({
name: 'reload', name: 'reload',

View File

@@ -1,7 +1,7 @@
import { ApplicationCommandOptionType, Routes } from 'discord.js'
import { AdminCommand } from '$/classes/Command' import { AdminCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { createSuccessEmbed } from '$/utils/discord/embeds' import { createSuccessEmbed } from '$/utils/discord/embeds'
import { ApplicationCommandOptionType, Routes } from 'discord.js'
const SubcommandOptions = { const SubcommandOptions = {
where: { where: {

View File

@@ -1,5 +1,5 @@
import { AdminCommand } from '$/classes/Command'
import { MessageFlags } from 'discord.js' import { MessageFlags } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
export default new AdminCommand({ export default new AdminCommand({
name: 'stop', name: 'stop',

View File

@@ -1,6 +1,6 @@
import { EmbedBuilder, MessageFlags } from 'discord.js'
import Command from '$/classes/Command' import Command from '$/classes/Command'
import { applyCommonEmbedStyles } from '$/utils/discord/embeds' import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
import { EmbedBuilder, MessageFlags } from 'discord.js'
export default new Command({ export default new Command({
name: 'coinflip', name: 'coinflip',

View File

@@ -1,5 +1,5 @@
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { ApplicationCommandOptionType, Message, MessageFlags } from 'discord.js' import { ApplicationCommandOptionType, Message, MessageFlags } from 'discord.js'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { ModerationCommand } from '../../classes/Command' import { ModerationCommand } from '../../classes/Command'
export default new ModerationCommand({ export default new ModerationCommand({

View File

@@ -1,7 +1,7 @@
import { MessageFlags } from 'discord.js'
import { ModerationCommand } from '$/classes/Command' import { ModerationCommand } from '$/classes/Command'
import { createSuccessEmbed } from '$/utils/discord/embeds' import { createSuccessEmbed } from '$/utils/discord/embeds'
import { cureNickname } from '$/utils/discord/moderation' import { cureNickname } from '$/utils/discord/moderation'
import { MessageFlags } from 'discord.js'
export default new ModerationCommand({ export default new ModerationCommand({
name: 'cure', name: 'cure',

View File

@@ -1,7 +1,7 @@
import { EmbedBuilder } from 'discord.js'
import { ModerationCommand } from '$/classes/Command' import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { applyCommonEmbedStyles } from '$/utils/discord/embeds' import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
import { EmbedBuilder } from 'discord.js'
export default new ModerationCommand({ export default new ModerationCommand({
name: 'purge', name: 'purge',

View File

@@ -1,8 +1,8 @@
import { ChannelType } from 'discord.js'
import { ModerationCommand } from '$/classes/Command' import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { createSuccessEmbed } from '$/utils/discord/embeds' import { createSuccessEmbed } from '$/utils/discord/embeds'
import { durationToString, parseDuration } from '$/utils/duration' import { durationToString, parseDuration } from '$/utils/duration'
import { ChannelType } from 'discord.js'
export default new ModerationCommand({ export default new ModerationCommand({
name: 'slowmode', name: 'slowmode',

View File

@@ -1,10 +1,10 @@
import { and, eq } from 'drizzle-orm'
import { ModerationCommand } from '$/classes/Command' import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { appliedPresets } from '$/database/schemas' import { appliedPresets } from '$/database/schemas'
import { createModerationActionEmbed } from '$/utils/discord/embeds' 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'
export default new ModerationCommand({ export default new ModerationCommand({
name: 'unmute', name: 'unmute',

View File

@@ -1,7 +1,7 @@
import { type FetchMessageOptions, MessageFlags, type MessageResolvable } from 'discord.js'
import Command from '$/classes/Command' import Command from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { createSuccessEmbed } from '$/utils/discord/embeds' import { createSuccessEmbed } from '$/utils/discord/embeds'
import { type FetchMessageOptions, MessageFlags, type MessageResolvable } from 'discord.js'
import { config } from '../../../context' import { config } from '../../../context'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema' import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'

View File

@@ -1,6 +1,6 @@
import { type APIStringSelectComponent, ComponentType, MessageFlags } from 'discord.js'
import Command from '$/classes/Command' import Command from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { type APIStringSelectComponent, ComponentType, MessageFlags } from 'discord.js'
import { config } from '../../../context' import { config } from '../../../context'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema' import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'

View File

@@ -100,7 +100,7 @@ export const discord = {
GuildMessageManager: { GuildMessageManager: {
maxSize: 0, maxSize: 0,
// Always keep messages posted by the client in cache // Always keep messages posted by the client in cache
keepOverLimit: message => message.author.id === message.client.user.id, keepOverLimit: message => message.author?.id === message.client.user.id,
}, },
// Unneeded cache // Unneeded cache
MessageManager: 0, MessageManager: 0,
@@ -122,7 +122,7 @@ export const discord = {
BaseGuildEmojiManager: 0, BaseGuildEmojiManager: 0,
GuildInviteManager: 0, GuildInviteManager: 0,
}), }),
partials: [Partials.Message, Partials.Reaction, Partials.GuildMember], partials: [Partials.Message, Partials.Reaction, Partials.GuildMember, Partials.User],
}), }),
commands: Object.fromEntries(Object.values(commands).map(cmd => [cmd.name, cmd])) as Record< commands: Object.fromEntries(Object.values(commands).map(cmd => [cmd.name, cmd])) as Record<
string, string,

View File

@@ -20,9 +20,7 @@ export const appliedPresets = sqliteTable(
preset: text('preset').notNull(), preset: text('preset').notNull(),
until: integer('until'), until: integer('until'),
}, },
table => ({ table => [uniqueIndex('unique_composite').on(table.memberId, table.preset, table.guildId)],
uniqueComposite: uniqueIndex('unique_composite').on(table.memberId, table.preset, table.guildId),
}),
) )
export type Response = InferSelectModel<typeof responses> export type Response = InferSelectModel<typeof responses>

View File

@@ -1,5 +1,5 @@
import { on, withContext } from '$utils/api/events'
import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared' import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared'
import { on, withContext } from '$utils/api/events'
withContext(on, 'disconnect', ({ api, config, logger }, reason, msg) => { withContext(on, 'disconnect', ({ api, config, logger }, reason, msg) => {
if (reason === DisconnectReason.PlannedDisconnect && api.intentionallyDisconnecting) return if (reason === DisconnectReason.PlannedDisconnect && api.intentionallyDisconnecting) return

View File

@@ -1,7 +1,8 @@
import { and, eq, gt } from 'drizzle-orm'
import { appliedPresets } from '$/database/schemas' import { appliedPresets } from '$/database/schemas'
import { on, withContext } from '$/utils/discord/events' import { on, withContext } from '$/utils/discord/events'
import { applyRolesUsingPreset } from '$/utils/discord/rolePresets' import { applyRolesUsingPreset } from '$/utils/discord/rolePresets'
import { and, eq, gt } from 'drizzle-orm' import { logger } from '$/context'
withContext(on, 'guildMemberAdd', async ({ database }, member) => { withContext(on, 'guildMemberAdd', async ({ database }, member) => {
const applieds = await database.query.appliedPresets.findMany({ const applieds = await database.query.appliedPresets.findMany({
@@ -12,5 +13,12 @@ withContext(on, 'guildMemberAdd', async ({ database }, member) => {
), ),
}) })
if (!applieds.length) return
logger.info(
`Re-applying role presets for member ${member.id} in guild ${member.guild.id}:`,
applieds.map(x => x.preset),
)
for (const { preset } of applieds) await applyRolesUsingPreset(preset, member) for (const { preset } of applieds) await applyRolesUsingPreset(preset, member)
}) })

View File

@@ -1,7 +1,7 @@
import { MessageFlags } from 'discord.js'
import CommandError from '$/classes/CommandError' import CommandError from '$/classes/CommandError'
import { createStackTraceEmbed } from '$utils/discord/embeds' import { createStackTraceEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
import { MessageFlags } from 'discord.js'
withContext(on, 'interactionCreate', async (context, interaction) => { withContext(on, 'interactionCreate', async (context, interaction) => {
if (!interaction.isChatInputCommand()) return if (!interaction.isChatInputCommand()) return

View File

@@ -1,7 +1,7 @@
import { MessageFlags } from 'discord.js'
import CommandError from '$/classes/CommandError' import CommandError from '$/classes/CommandError'
import { createStackTraceEmbed } from '$utils/discord/embeds' import { createStackTraceEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
import { MessageFlags } from 'discord.js'
withContext(on, 'interactionCreate', async (context, interaction) => { withContext(on, 'interactionCreate', async (context, interaction) => {
if (!interaction.isContextMenuCommand()) return if (!interaction.isContextMenuCommand()) return

View File

@@ -1,7 +1,3 @@
import { responses } from '$/database/schemas'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
import { import {
type ButtonInteraction, type ButtonInteraction,
MessageFlags, MessageFlags,
@@ -9,6 +5,10 @@ import {
type TextBasedChannel, type TextBasedChannel,
} from 'discord.js' } from 'discord.js'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import { responses } from '$/database/schemas'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
// No permission check required as it is already done when the user reacts to a bot response // No permission check required as it is already done when the user reacts to a bot response
withContext(on, 'interactionCreate', async (context, interaction) => { withContext(on, 'interactionCreate', async (context, interaction) => {

View File

@@ -1,6 +1,6 @@
import { MessageFlags, type TextBasedChannel } from 'discord.js'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds' import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
import { MessageFlags, type TextBasedChannel } from 'discord.js'
withContext(on, 'interactionCreate', async (context, interaction) => { withContext(on, 'interactionCreate', async (context, interaction) => {
const { const {

View File

@@ -1,3 +1,4 @@
import { MessageScanLabeledResponseReactions } from '$/constants'
import { responses } from '$/database/schemas' import { responses } from '$/database/schemas'
import { getResponseFromText, messageMatchesFilter } from '$/utils/discord/messageScan' import { getResponseFromText, messageMatchesFilter } from '$/utils/discord/messageScan'
import { createMessageScanResponseEmbed } from '$utils/discord/embeds' import { createMessageScanResponseEmbed } from '$utils/discord/embeds'
@@ -32,7 +33,13 @@ withContext(on, 'messageCreate', async (context, msg) => {
if (response) { if (response) {
logger.debug('Response found') logger.debug('Response found')
const toReply = respondToReply ? (msg.reference?.messageId ? await msg.fetchReference() : msg) : msg const toReply =
msg.reference?.messageId &&
(respondToReply === true ||
(label === undefined ? respondToReply === 'only_regex' : respondToReply === 'only_labeled'))
? await msg.fetchReference()
: msg
const reply = await toReply.reply({ const reply = await toReply.reply({
...response, ...response,
embeds: response.embeds?.map(createMessageScanResponseEmbed), embeds: response.embeds?.map(createMessageScanResponseEmbed),
@@ -47,6 +54,8 @@ withContext(on, 'messageCreate', async (context, msg) => {
label, label,
content: msg.content, content: msg.content,
}) })
await Promise.all(Object.values(MessageScanLabeledResponseReactions).map(name => reply.react(name)))
} }
} }
} catch (e) { } catch (e) {

View File

@@ -1,9 +1,3 @@
import { MessageScanLabeledResponseReactions as Reactions } from '$/constants'
import { responses } from '$/database/schemas'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$/utils/discord/embeds'
import { on, withContext } from '$/utils/discord/events'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { isAdmin } from '$/utils/discord/permissions'
import { import {
ActionRowBuilder, ActionRowBuilder,
ButtonBuilder, ButtonBuilder,
@@ -12,6 +6,12 @@ import {
StringSelectMenuOptionBuilder, StringSelectMenuOptionBuilder,
} from 'discord.js' } from 'discord.js'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import { MessageScanLabeledResponseReactions as Reactions } from '$/constants'
import { responses } from '$/database/schemas'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$/utils/discord/embeds'
import { on, withContext } from '$/utils/discord/events'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { isAdmin } from '$/utils/discord/permissions'
import type { ConfigMessageScanResponseLabelConfig } from '$/../config.schema' import type { ConfigMessageScanResponseLabelConfig } from '$/../config.schema'
const PossibleReactions = Object.values(Reactions) as string[] const PossibleReactions = Object.values(Reactions) as string[]

View File

@@ -1,9 +1,9 @@
import { type Client, DiscordAPIError } from 'discord.js'
import { and, eq, lt } from 'drizzle-orm'
import { database, logger } from '$/context' import { database, logger } from '$/context'
import { appliedPresets } from '$/database/schemas' import { appliedPresets } from '$/database/schemas'
import { on, withContext } from '$/utils/discord/events' import { on, withContext } from '$/utils/discord/events'
import { removeRolePreset } from '$/utils/discord/rolePresets' import { removeRolePreset } from '$/utils/discord/rolePresets'
import { type Client, DiscordAPIError } from 'discord.js'
import { and, eq, lt } from 'drizzle-orm'
export default withContext(on, 'ready', async ({ config }, client) => { export default withContext(on, 'ready', async ({ config }, client) => {
if (config.rolePresets) { if (config.rolePresets) {

View File

@@ -1,5 +1,5 @@
import { api, discord, logger } from '$/context'
import { getMissingEnvironmentVariables } from '@revanced/bot-shared' import { getMissingEnvironmentVariables } from '@revanced/bot-shared'
import { api, discord, logger } from '$/context'
import './events/register' import './events/register'

View File

@@ -1,5 +1,5 @@
import { DefaultEmbedColor, ReVancedLogoURL } from '$/constants'
import { type APIEmbed, EmbedBuilder, type EmbedField, type JSONEncodable, type User } from 'discord.js' import { type APIEmbed, EmbedBuilder, type EmbedField, type JSONEncodable, type User } from 'discord.js'
import { DefaultEmbedColor, ReVancedLogoURL } from '$/constants'
import type { ConfigMessageScanResponseMessage } from '../../../config.schema' import type { ConfigMessageScanResponseMessage } from '../../../config.schema'
export const createErrorEmbed = (title: string | null, description?: string) => export const createErrorEmbed = (title: string | null, description?: string) =>

View File

@@ -1,6 +1,6 @@
import { type Response, responses } from '$/database/schemas'
import { ButtonStyle, ComponentType } from 'discord.js' import { ButtonStyle, ComponentType } from 'discord.js'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import { type Response, responses } from '$/database/schemas'
import { createMessageScanResponseEmbed } from './embeds' import { createMessageScanResponseEmbed } from './embeds'
import type { Config, ConfigMessageScanResponse, ConfigMessageScanResponseLabelConfig } from 'config.schema' import type { Config, ConfigMessageScanResponse, ConfigMessageScanResponseLabelConfig } from 'config.schema'
import type { APIActionRowComponent, APIButtonComponent, Message, PartialUser, User } from 'discord.js' import type { APIActionRowComponent, APIButtonComponent, Message, PartialUser, User } from 'discord.js'

View File

@@ -1,5 +1,5 @@
import { config, logger } from '$/context'
import decancer from 'decancer' import decancer from 'decancer'
import { config, logger } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from './embeds' import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from './embeds'
import type { CommandInteraction, EmbedBuilder, Guild, GuildMember, Message, User } from 'discord.js' import type { CommandInteraction, EmbedBuilder, Guild, GuildMember, Message, User } from 'discord.js'

View File

@@ -1,6 +1,6 @@
import { and, eq } from 'drizzle-orm'
import { config, database } from '$/context' import { config, database } from '$/context'
import { appliedPresets } from '$/database/schemas' import { appliedPresets } from '$/database/schemas'
import { and, eq } from 'drizzle-orm'
import type { GuildMember } from 'discord.js' import type { GuildMember } from 'discord.js'
// TODO: Fix this type // TODO: Fix this type

840
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -33,8 +33,8 @@
], ],
"packageManager": "bun@1.2.17", "packageManager": "bun@1.2.17",
"devDependencies": { "devDependencies": {
"@anolilab/multi-semantic-release": "^2.0.0", "@anolilab/multi-semantic-release": "^2.0.3",
"@biomejs/biome": "^2.0.5", "@biomejs/biome": "^2.2.4",
"@codedependant/semantic-release-docker": "^5.1.1", "@codedependant/semantic-release-docker": "^5.1.1",
"@commitlint/cli": "^19.8.1", "@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1", "@commitlint/config-conventional": "^19.8.1",
@@ -43,13 +43,13 @@
"@semantic-release/exec": "^7.1.0", "@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"@tsconfig/strictest": "^2.0.5", "@tsconfig/strictest": "^2.0.5",
"@types/bun": "^1.2.17", "@types/bun": "^1.2.21",
"conventional-changelog-conventionalcommits": "^9.0.0", "conventional-changelog-conventionalcommits": "^9.1.0",
"lefthook": "^1.11.14", "lefthook": "^1.13.0",
"portainer-service-webhook": "https://github.com/newarifrh/portainer-service-webhook#v1", "portainer-service-webhook": "https://github.com/newarifrh/portainer-service-webhook#v1",
"semantic-release": "^24.2.5", "semantic-release": "^24.2.8",
"turbo": "^2.5.4", "turbo": "^2.5.6",
"typescript": "^5.8.3" "typescript": "^5.9.2"
}, },
"trustedDependencies": [ "trustedDependencies": [
"@biomejs/biome", "@biomejs/biome",
@@ -58,9 +58,6 @@
"lefthook" "lefthook"
], ],
"patchedDependencies": { "patchedDependencies": {
"@semantic-release/npm@12.0.1": "patches/@semantic-release%2Fnpm@12.0.1.patch", "@semantic-release/npm@12.0.2": "patches/@semantic-release%2Fnpm@12.0.2.patch"
"drizzle-kit@0.22.8": "patches/drizzle-kit@0.22.8.patch",
"decancer@3.2.4": "patches/decancer@3.2.4.patch",
"discord.js@14.18.0": "patches/discord.js@14.18.0.patch"
} }
} }

View File

@@ -28,7 +28,7 @@
"homepage": "https://github.com/revanced/revanced-bots#readme", "homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": { "dependencies": {
"@revanced/bot-shared": "workspace:*", "@revanced/bot-shared": "workspace:*",
"ws": "^8.18.2" "ws": "^8.18.3"
}, },
"devDependencies": { "devDependencies": {
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",

View File

@@ -101,7 +101,7 @@ export default class Client {
return packet return packet
} }
async trainMessage(text: string, label: string) { async trainMessage(text: string, label?: string) {
this.#throwIfNotReady() this.#throwIfNotReady()
this.ws.send({ this.ws.send({

View File

@@ -31,7 +31,7 @@
"homepage": "https://github.com/revanced/revanced-bots#readme", "homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": { "dependencies": {
"bson": "^6.10.4", "bson": "^6.10.4",
"chalk": "^5.4.1", "chalk": "^5.6.2",
"tracer": "^1.3.0", "tracer": "^1.3.0",
"valibot": "^1.1.0" "valibot": "^1.1.0"
} }

View File

@@ -1,15 +1,12 @@
import { import {
type AnySchema,
array, array,
type BooleanSchema,
boolean, boolean,
custom, custom,
enum_, enum_,
type InferOutput, type InferOutput,
type NullSchema,
null_, null_,
type ObjectSchema,
object, object,
optional,
parse, parse,
pipe, pipe,
string, string,
@@ -75,13 +72,9 @@ export const PacketDataSchemas = {
}), }),
[ClientOperation.TrainMessage]: object({ [ClientOperation.TrainMessage]: object({
text: string(), text: string(),
label: string(), label: optional(string()),
}), }),
} as const satisfies Record< } as const
Operation,
// biome-ignore lint/suspicious/noExplicitAny: This is a schema, it's not possible to type it
ObjectSchema<any, any> | AnySchema | NullSchema<any> | BooleanSchema<any>
>
export type Packet<TOp extends Operation = Operation> = TOp extends ServerOperation export type Packet<TOp extends Operation = Operation> = TOp extends ServerOperation
? PacketWithSequenceNumber<TOp> ? PacketWithSequenceNumber<TOp>

View File

@@ -1,12 +1,3 @@
diff --git a/node_modules/@semantic-release/npm/.bun-tag-3853154e196b7721 b/.bun-tag-3853154e196b7721
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/@semantic-release/npm/.bun-tag-550461f23a8ec245 b/.bun-tag-550461f23a8ec245
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/@semantic-release/npm/.bun-tag-c9c8130945517add b/.bun-tag-c9c8130945517add
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/prepare.js b/lib/prepare.js diff --git a/lib/prepare.js b/lib/prepare.js
index 3e76bec44cf595a1b4141728336bed904d4d518d..4b25ca64879bbee2a600f2b23b738c86136ad9c6 100644 index 3e76bec44cf595a1b4141728336bed904d4d518d..4b25ca64879bbee2a600f2b23b738c86136ad9c6 100644
--- a/lib/prepare.js --- a/lib/prepare.js

View File

@@ -1,13 +0,0 @@
diff --git a/src/lib.js b/src/lib.js
index de45d7dbe82975b09eff3742d0718accae2107fc..0575daa03dfabdd5c96928458ff4270cb8f7188a 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -42,7 +42,7 @@ function isMusl() {
}
function getBinding(name) {
- const path = join(__dirname, '..', `decancer.${name}.node`)
+ const path = join(import.meta.dir, '..', `decancer.${name}.node`)
return require(existsSync(path) ? path : `@vierofernando/decancer-${name}`)
}

View File

@@ -1,17 +0,0 @@
# Make Message#reply work with { flags: MessageFlags.Ephemeral } in typings
# So our Command system doesn't break
diff --git a/typings/index.d.mts b/typings/index.d.mts
index 645b870..fa93158 100644
--- a/typings/index.d.mts
+++ b/typings/index.d.mts
@@ -6764,8 +6764,8 @@ export interface MessageCreateOptions extends BaseMessageOptionsWithPoll {
stickers?: readonly StickerResolvable[];
flags?:
| BitFieldResolvable<
- Extract<MessageFlagsString, 'SuppressEmbeds' | 'SuppressNotifications'>,
- MessageFlags.SuppressEmbeds | MessageFlags.SuppressNotifications
+ Extract<MessageFlagsString, 'SuppressEmbeds' | 'SuppressNotifications' | 'Ephemeral'>,
+ MessageFlags.SuppressEmbeds | MessageFlags.SuppressNotifications | MessageFlags.Ephemeral
>
| undefined;
}

View File

@@ -1,21 +0,0 @@
diff --git a/bin.cjs b/bin.cjs
index 142ed9c20f28dc1080bebfb52325fa308c6cb771..9d3bea0787f6c05df11567c6821bc85743286340 100644
--- a/bin.cjs
+++ b/bin.cjs
@@ -22053,7 +22053,7 @@ var init_sqliteImports = __esm({
const { unregister } = await safeRegister();
for (let i2 = 0; i2 < imports.length; i2++) {
const it = imports[i2];
- const i0 = require(`${it}`);
+ const i0 = await import(`${it}`);
const prepared = prepareFromExports3(i0);
tables.push(...prepared.tables);
}
@@ -129572,6 +129572,7 @@ var generateCommand = new Command("generate").option("--dialect <dialect>", "Dat
} else {
assertUnreachable(dialect7);
}
+ process.exit(0);
});
var migrateCommand = new Command("migrate").option(
"--config <config>",