Compare commits

...

14 Commits

Author SHA1 Message Date
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
semantic-release-bot
b4ac8433f4 chore(release): 1.2.3 [skip ci]
## @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)

### Bug Fixes

* **bots/discord:** disable unneeded cache, enable message cache sweeping ([3a0f0fe](3a0f0fe786))
2025-06-23 13:33:41 +00:00
46 changed files with 792 additions and 257 deletions

View File

@@ -20,11 +20,6 @@ jobs:
steps:
- name: Checkout
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
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.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",
"type": "module",
"private": true,
"version": "1.0.2",
"version": "1.1.0",
"description": "🧦 WebSocket API server for bots assisting ReVanced",
"main": "dist/index.js",
"scripts": {
@@ -31,10 +31,10 @@
"@sapphire/async-queue": "^1.5.5",
"chalk": "^5.4.1",
"tesseract.js": "^6.0.1",
"ws": "^8.18.2"
"ws": "^8.18.3"
},
"devDependencies": {
"@types/ws": "^8.18.1",
"typed-emitter": "^2.1.0"
}
}
}

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})`)
return await res.json()
return (await res.json()) as WitMessageResponse
},
message(text: string) {
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', {
body: JSON.stringify([
{
@@ -41,7 +41,14 @@ export const wit = {
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 {
text: string

View File

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

View File

@@ -10,7 +10,7 @@ const trainMessageEventHandler: EventHandler<ClientOperation.TrainMessage> = asy
const nextSeq = client.currentSequence++
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 {
await wit.train(actualText, label)

View File

@@ -1,3 +1,29 @@
# @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)
### Bug Fixes
* **bots/discord:** disable unneeded cache, enable message cache sweeping ([3a0f0fe](https://github.com/revanced/revanced-bots/commit/3a0f0fe7861d73a4d81ecaba0e12bd60c06f8eb8))
## @revanced/discord-bot [1.2.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.1...@revanced/discord-bot@1.2.2) (2025-06-10)

View File

@@ -66,7 +66,6 @@ export default {
},
},
humanCorrections: {
falsePositiveLabel: 'false_positive',
allow: {
members: {
permissions: 8n,

View File

@@ -36,7 +36,6 @@ export type Config = {
blacklist?: Filter
}
humanCorrections: {
falsePositiveLabel: string
allow?: {
users?: string[]
members?: {
@@ -72,7 +71,7 @@ export type ConfigMessageScanResponse = {
image?: Array<RegExp>
}
filterOverride?: NonNullable<Config['messageScan']>['filter']
response: ConfigMessageScanResponseMessage | null
response: ConfigMessageScanResponseMessage
respondToReply?: boolean
}

View File

@@ -2,7 +2,7 @@
"name": "@revanced/discord-bot",
"type": "module",
"private": true,
"version": "1.2.2",
"version": "1.4.0",
"description": "🤖 Discord bot assisting ReVanced",
"main": "src/index.ts",
"scripts": {
@@ -35,12 +35,12 @@
"@sapphire/duration": "^1.2.0",
"chalk": "^5.4.1",
"decancer": "^3.3.2",
"discord.js": "^14.20.0",
"discord.js": "^14.21.0",
"drizzle-orm": "^0.44.2"
},
"devDependencies": {
"@libsql/client": "^0.15.9",
"discord-api-types": "^0.38.13",
"drizzle-kit": "^0.31.2"
"discord-api-types": "^0.38.15",
"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 { unlinkSync, writeFileSync } from 'fs'
import { join } from 'path'
import { inspect } from 'util'
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({
name: 'eval',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
import { and, eq } from 'drizzle-orm'
import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { appliedPresets } from '$/database/schemas'
import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
import { removeRolePreset } from '$/utils/discord/rolePresets'
import { and, eq } from 'drizzle-orm'
export default new ModerationCommand({
name: 'unmute',

View File

@@ -1,7 +1,7 @@
import { type FetchMessageOptions, MessageFlags, type MessageResolvable } from 'discord.js'
import Command from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { type FetchMessageOptions, MessageFlags, type MessageResolvable } from 'discord.js'
import { config } from '../../../context'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'
@@ -9,7 +9,7 @@ const msRcConfig = config.messageScan?.humanCorrections?.allow
export default new Command({
name: 'train',
description: 'Train a specific message or text to a specific label',
description: 'Train a specific message to a specific label',
type: Command.Type.ChatGuild,
requirements: {
users: msRcConfig?.users,
@@ -26,9 +26,9 @@ export default new Command({
required: true,
},
label: {
description: 'The label to train the message as',
description: 'The label to train the message as (leave empty for out of scope)',
type: Command.OptionType.String,
required: true,
required: false,
},
},
allowMessageCommand: true,
@@ -49,7 +49,7 @@ export default new Command({
'This command can only be used in or on text channels',
)
if (!labels.includes(label))
if (label && !labels.includes(label))
throw new CommandError(
CommandErrorType.InvalidArgument,
`The provided label is invalid.\nValid labels are:${labels.map(l => `\n- \`${l}\``).join('')}`,
@@ -60,14 +60,14 @@ export default new Command({
)
if (!refMsg) throw new CommandError(CommandErrorType.InvalidArgument, 'The provided message does not exist.')
logger.debug(`User ${context.executor.id} is training message ${refMsg?.id} as ${label}`)
logger.debug(`User ${context.executor.id} is training message ${refMsg?.id} as ${label ?? 'out of scope'}`)
await context.api.client.trainMessage(refMsg.content, label)
await trigger.reply({
embeds: [
createSuccessEmbed(
'Message trained',
`The provided message has been trained as \`${label}\`. Thank you for your contribution!`,
`The provided message has been trained as ${label ? `\`${label}\`` : 'out of scope'}. Thank you for your contribution!`,
),
],
flags: MessageFlags.Ephemeral,

View File

@@ -1,6 +1,6 @@
import { type APIStringSelectComponent, ComponentType, MessageFlags } from 'discord.js'
import Command from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { type APIStringSelectComponent, ComponentType, MessageFlags } from 'discord.js'
import { config } from '../../../context'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'
@@ -37,7 +37,10 @@ export default new Command({
components: [
{
custom_id: `tr_${trigger.targetMessage.channelId}_${trigger.targetId}`,
options: labels.map(label => ({ label, value: label })),
options: [
...labels.map(label => ({ label, value: label })),
{ label: 'Out of scope', value: OutOfScopeLabel, emoji: { name: '❌' } },
],
type: ComponentType.StringSelect,
} satisfies APIStringSelectComponent,
],
@@ -48,3 +51,5 @@ export default new Command({
})
},
})
export const OutOfScopeLabel = '<out of scope>'

View File

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

View File

@@ -1,7 +1,7 @@
import { and, eq, gt } from 'drizzle-orm'
import { appliedPresets } from '$/database/schemas'
import { on, withContext } from '$/utils/discord/events'
import { applyRolesUsingPreset } from '$/utils/discord/rolePresets'
import { and, eq, gt } from 'drizzle-orm'
withContext(on, 'guildMemberAdd', async ({ database }, member) => {
const applieds = await database.query.appliedPresets.findMany({

View File

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

View File

@@ -1,7 +1,7 @@
import { MessageFlags } from 'discord.js'
import CommandError from '$/classes/CommandError'
import { createStackTraceEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
import { MessageFlags } from 'discord.js'
withContext(on, 'interactionCreate', async (context, interaction) => {
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 {
type ButtonInteraction,
MessageFlags,
@@ -9,6 +5,10 @@ import {
type TextBasedChannel,
} from 'discord.js'
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
withContext(on, 'interactionCreate', async (context, interaction) => {
@@ -58,8 +58,8 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
const editMessage = (content: string, description?: string) =>
editInteractionMessage(interaction, msg.url, content, description)
const handleCorrection = (label: string) =>
handleUserResponseCorrection(context, response, msg, label, interaction.user)
const handleCorrection = (label?: string) =>
handleUserResponseCorrection(context, response, msg, interaction.user, label)
if (response.correctedById)
return await editMessage(
@@ -82,7 +82,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
await editMessage('Canceled', 'You canceled this interaction. 😞')
break
case 'delete':
await handleCorrection(msConfig.humanCorrections.falsePositiveLabel)
await handleCorrection()
await editMessage(
'Marked as false positive',
'The response has been deleted and marked as a false positive. Thank you for your feedback. 🎉',

View File

@@ -1,6 +1,7 @@
import { MessageFlags, type TextBasedChannel } from 'discord.js'
import { OutOfScopeLabel } from '$/commands/support/train/context-menu'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
import { MessageFlags, type TextBasedChannel } from 'discord.js'
withContext(on, 'interactionCreate', async (context, interaction) => {
const {
@@ -30,8 +31,11 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
flags: MessageFlags.Ephemeral,
}))
const selectedLabel = interaction.values[0]!
await context.api.client.trainMessage(msg.content, selectedLabel)
const selectedLabel = interaction.values[0]
await context.api.client.trainMessage(
msg.content,
selectedLabel === OutOfScopeLabel ? undefined : selectedLabel,
)
await interaction.reply({
embeds: [
createSuccessEmbed(

View File

@@ -1,3 +1,4 @@
import { MessageScanLabeledResponseReactions } from '$/constants'
import { responses } from '$/database/schemas'
import { getResponseFromText, messageMatchesFilter } from '$/utils/discord/messageScan'
import { createMessageScanResponseEmbed } from '$utils/discord/embeds'
@@ -47,6 +48,8 @@ withContext(on, 'messageCreate', async (context, msg) => {
label,
content: msg.content,
})
await Promise.all(Object.values(MessageScanLabeledResponseReactions).map(name => reply.react(name)))
}
}
} 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 {
ActionRowBuilder,
ButtonBuilder,
@@ -12,6 +6,12 @@ import {
StringSelectMenuOptionBuilder,
} from 'discord.js'
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'
const PossibleReactions = Object.values(Reactions) as string[]
@@ -63,8 +63,8 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
logger.debug(`User ${user.id} is trying to correct the response ${rct.message.id}`)
const handleCorrection = (label: string) =>
handleUserResponseCorrection(context, response, reactionMessage, label, user)
const handleCorrection = (label?: string) =>
handleUserResponseCorrection(context, response, reactionMessage, user, label)
try {
if (reaction.emoji.name === Reactions.train) {
@@ -106,7 +106,7 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
.setCustomId(`${componentPrefix}_cancel`),
new ButtonBuilder()
.setEmoji(Reactions.delete)
.setLabel('Delete (mark as false positive)')
.setLabel('Delete (mark as out of scope)')
.setStyle(ButtonStyle.Danger)
.setCustomId(`${componentPrefix}_delete`),
),
@@ -117,8 +117,8 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
components: rows,
})
} else if (reaction.emoji.name === Reactions.delete) {
await handleCorrection(msConfig.humanCorrections.falsePositiveLabel)
await user.send({ content: 'The response has been deleted and marked as a false positive.' })
await handleCorrection()
await user.send({ content: 'The response has been deleted and marked as out of scope.' })
}
} catch (e) {
logger.error('Failed to correct response:', e)

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 { appliedPresets } from '$/database/schemas'
import { on, withContext } from '$/utils/discord/events'
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) => {
if (config.rolePresets) {

View File

@@ -1,5 +1,5 @@
import { api, discord, logger } from '$/context'
import { getMissingEnvironmentVariables } from '@revanced/bot-shared'
import { api, discord, logger } from '$/context'
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 { DefaultEmbedColor, ReVancedLogoURL } from '$/constants'
import type { ConfigMessageScanResponseMessage } from '../../../config.schema'
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 { eq } from 'drizzle-orm'
import { type Response, responses } from '$/database/schemas'
import { createMessageScanResponseEmbed } from './embeds'
import type { Config, ConfigMessageScanResponse, ConfigMessageScanResponseLabelConfig } from 'config.schema'
import type { APIActionRowComponent, APIButtonComponent, Message, PartialUser, User } from 'discord.js'
@@ -17,7 +17,7 @@ export const getResponseFromText = async (
type ResponseConfig = Awaited<ReturnType<typeof getResponseFromText>>
let responseConfig: Omit<ResponseConfig, 'triggers'> & { triggers?: ResponseConfig['triggers'] } = {
triggers: undefined,
response: null,
response: null!,
}
const firstLabelIndexes: number[] = []
@@ -143,15 +143,22 @@ export const handleUserResponseCorrection = async (
{ api, database: db, config: { messageScan: msConfig }, logger }: typeof import('$/context'),
response: Response,
reply: Message,
label: string,
user: User | PartialUser,
label?: string,
) => {
if (!label) {
await Promise.all([reply.delete(), api.client.trainMessage(response.content, label)]).finally(() =>
logger.debug(`User ${user.id} trained message ${response.replyId} as out of scope`),
)
return
}
const correctLabelResponse = msConfig!.responses!.find(r =>
r.triggers.text!.some(t => 'label' in t && t.label === label),
)
if (!correctLabelResponse) throw new Error('Cannot find label config for the selected label')
if (!correctLabelResponse.response) return void (await reply.delete())
if (response.label !== label) {
db.update(responses)
@@ -168,12 +175,12 @@ export const handleUserResponseCorrection = async (
}))
}
await api.client.trainMessage(response.content, label)
logger.debug(`User ${user.id} trained message ${response.replyId} as ${label} (positive)`)
await reply.edit({
components: [],
})
await Promise.all([
api.client.trainMessage(response.content, label),
reply.edit({
components: [],
}),
]).finally(() => logger.debug(`User ${user.id} trained message ${response.replyId} as ${label}`))
}
export const createMessageScanResponseComponents = (reply: Message<true>) => [

View File

@@ -1,5 +1,5 @@
import { config, logger } from '$/context'
import decancer from 'decancer'
import { config, logger } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from './embeds'
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 { appliedPresets } from '$/database/schemas'
import { and, eq } from 'drizzle-orm'
import type { GuildMember } from 'discord.js'
// TODO: Fix this type

730
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -33,8 +33,8 @@
],
"packageManager": "bun@1.2.17",
"devDependencies": {
"@anolilab/multi-semantic-release": "^2.0.0",
"@biomejs/biome": "^2.0.5",
"@anolilab/multi-semantic-release": "^2.0.2",
"@biomejs/biome": "^2.1.1",
"@codedependant/semantic-release-docker": "^5.1.1",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
@@ -43,11 +43,11 @@
"@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1",
"@tsconfig/strictest": "^2.0.5",
"@types/bun": "^1.2.17",
"conventional-changelog-conventionalcommits": "^9.0.0",
"lefthook": "^1.11.14",
"@types/bun": "^1.2.18",
"conventional-changelog-conventionalcommits": "^9.1.0",
"lefthook": "^1.12.2",
"portainer-service-webhook": "https://github.com/newarifrh/portainer-service-webhook#v1",
"semantic-release": "^24.2.5",
"semantic-release": "^24.2.6",
"turbo": "^2.5.4",
"typescript": "^5.8.3"
},
@@ -58,9 +58,6 @@
"lefthook"
],
"patchedDependencies": {
"@semantic-release/npm@12.0.1": "patches/@semantic-release%2Fnpm@12.0.1.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"
"@semantic-release/npm@12.0.2": "patches/@semantic-release%2Fnpm@12.0.2.patch"
}
}

View File

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

View File

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

View File

@@ -1,15 +1,12 @@
import {
type AnySchema,
array,
type BooleanSchema,
boolean,
custom,
enum_,
type InferOutput,
type NullSchema,
null_,
type ObjectSchema,
object,
optional,
parse,
pipe,
string,
@@ -75,13 +72,9 @@ export const PacketDataSchemas = {
}),
[ClientOperation.TrainMessage]: object({
text: string(),
label: string(),
label: optional(string()),
}),
} as const satisfies Record<
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>
>
} as const
export type Packet<TOp extends Operation = Operation> = TOp extends ServerOperation
? 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
index 3e76bec44cf595a1b4141728336bed904d4d518d..4b25ca64879bbee2a600f2b23b738c86136ad9c6 100644
--- 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>",