Compare commits

...

13 Commits

Author SHA1 Message Date
semantic-release-bot
4e889d4991 chore(release): 1.0.0-dev.18 [skip ci]
# @revanced/discord-bot [1.0.0-dev.18](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.17...@revanced/discord-bot@1.0.0-dev.18) (2024-08-03)

### Bug Fixes

* **bots/discord:** set the `label` property correctly for message scans ([6d463df](6d463df586))
2024-08-03 15:24:18 +00:00
PalmDevs
6d463df586 fix(bots/discord): set the label property correctly for message scans 2024-08-03 22:22:42 +07:00
semantic-release-bot
2d8688bd4c chore(release): 1.0.0-dev.17 [skip ci]
# @revanced/discord-bot [1.0.0-dev.17](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.16...@revanced/discord-bot@1.0.0-dev.17) (2024-08-02)

### Bug Fixes

* **bots/discord/commands/eval:** evaluate in current context ([5925d90](5925d90209))
* **bots/discord:** send right response for after regexes ([a7688fa](a7688fa9b9))

### Features

* **bots/discord:** update example config file ([bc9951c](bc9951c9b5))
2024-08-02 18:40:39 +00:00
PalmDevs
bc9951c9b5 feat(bots/discord): update example config file 2024-08-03 01:36:19 +07:00
PalmDevs
a7688fa9b9 fix(bots/discord): send right response for after regexes 2024-08-03 01:31:45 +07:00
PalmDevs
5925d90209 fix(bots/discord/commands/eval): evaluate in current context 2024-08-03 01:31:44 +07:00
semantic-release-bot
5506518635 chore(release): 1.0.0-dev.16 [skip ci]
# @revanced/discord-bot [1.0.0-dev.16](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.15...@revanced/discord-bot@1.0.0-dev.16) (2024-08-02)

### Bug Fixes

* **bots/discord:** open database as read-write ([c366840](c36684091d))
* **bots/discord:** remove bad text channel checks ([f5939e2](f5939e2528))
* **bots/discord:** remove redundant footer for response embeds ([412e003](412e00317d))

### Features

* **bots/discord/commands:** add `reload` command ([6875b32](6875b32fd0))
2024-08-02 12:31:21 +00:00
PalmDevs
b9d08fff64 feat(bots/discord)!: add more attachment scan options 2024-08-02 19:26:20 +07:00
PalmDevs
6875b32fd0 feat(bots/discord/commands): add reload command 2024-08-02 19:26:20 +07:00
PalmDevs
c36684091d fix(bots/discord): open database as read-write 2024-08-02 19:26:19 +07:00
PalmDevs
f5939e2528 fix(bots/discord): remove bad text channel checks 2024-08-02 19:26:18 +07:00
PalmDevs
412e00317d fix(bots/discord): remove redundant footer for response embeds 2024-08-02 19:26:17 +07:00
PalmDevs
8fe78e424e fix(bots/discord)!: rename config replyToReplied to respondToReply 2024-08-02 19:26:12 +07:00
15 changed files with 115 additions and 46 deletions

View File

@@ -174,9 +174,6 @@ dist
# Finder (MacOS) folder config
.DS_Store
# Config
config.ts
# DB
*.db
*.sqlite

View File

@@ -1,3 +1,37 @@
# @revanced/discord-bot [1.0.0-dev.18](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.17...@revanced/discord-bot@1.0.0-dev.18) (2024-08-03)
### Bug Fixes
* **bots/discord:** set the `label` property correctly for message scans ([6d463df](https://github.com/revanced/revanced-helper/commit/6d463df586dee5dd8fe8d6cff1c5316f7809b32a))
# @revanced/discord-bot [1.0.0-dev.17](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.16...@revanced/discord-bot@1.0.0-dev.17) (2024-08-02)
### Bug Fixes
* **bots/discord/commands/eval:** evaluate in current context ([5925d90](https://github.com/revanced/revanced-helper/commit/5925d902095acef5f6396ca03583a9cbb0862498))
* **bots/discord:** send right response for after regexes ([a7688fa](https://github.com/revanced/revanced-helper/commit/a7688fa9b91919a87f74071b502cd0a87cd1c1fa))
### Features
* **bots/discord:** update example config file ([bc9951c](https://github.com/revanced/revanced-helper/commit/bc9951c9b5e007c3e1b3076aa0966ccf29bb18bc))
# @revanced/discord-bot [1.0.0-dev.16](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.15...@revanced/discord-bot@1.0.0-dev.16) (2024-08-02)
### Bug Fixes
* **bots/discord:** open database as read-write ([c366840](https://github.com/revanced/revanced-helper/commit/c36684091dddf67880505dc459e4334a8a5492f4))
* **bots/discord:** remove bad text channel checks ([f5939e2](https://github.com/revanced/revanced-helper/commit/f5939e25288fea2022fdeec9085ecb9ffada6111))
* **bots/discord:** remove redundant footer for response embeds ([412e003](https://github.com/revanced/revanced-helper/commit/412e00317d1eaca23e9c1375e16f94a5f2fa8d86))
### Features
* **bots/discord/commands:** add `reload` command ([6875b32](https://github.com/revanced/revanced-helper/commit/6875b32fd0c6ce3034da9dc6c704d425afb26f2e))
# @revanced/discord-bot [1.0.0-dev.15](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.14...@revanced/discord-bot@1.0.0-dev.15) (2024-07-31)

View File

@@ -11,8 +11,21 @@ export default {
GUILD_ID_HERE: ['ROLE_ID_HERE'],
},
},
stickyMessages: {
GUILD_ID_HERE: {
CHANNEL_ID_HERE: {
message: {
content: 'This is a sticky message!',
},
timeout: 60000,
forceSendTimeout: 300000,
}
}
},
moderation: {
cure: {
minimumNameLength: 3,
removeCharactersRegex: /[^a-zA-Z0-9 \-_]/g,
defaultName: 'Server member',
},
roles: ['ROLE_ID_HERE'],
@@ -61,7 +74,11 @@ export default {
},
},
},
allowedAttachmentMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
attachments: {
scanAttachments: true,
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp', 'text/plain'],
maxTextFileSize: 512000
},
responses: [
{
filterOverride: {

View File

@@ -26,7 +26,11 @@ export type Config = {
messageScan?: {
scanBots?: boolean
scanOutsideGuilds?: boolean
allowedAttachmentMimeTypes: string[]
attachments?: {
scanAttachments?: boolean
allowedMimeTypes?: string[]
maxTextFileSize?: number
}
filter?: {
whitelist?: Filter
blacklist?: Filter
@@ -69,7 +73,7 @@ export type ConfigMessageScanResponse = {
}
filterOverride?: NonNullable<Config['messageScan']>['filter']
response: ConfigMessageScanResponseMessage | null
replyToReplied?: boolean
respondToReply?: boolean
}
export type ConfigMessageScanResponseLabelConfig = {

View File

@@ -2,7 +2,7 @@
"name": "@revanced/discord-bot",
"type": "module",
"private": true,
"version": "1.0.0-dev.15",
"version": "1.0.0-dev.18",
"description": "🤖 Discord bot assisting ReVanced",
"main": "src/index.ts",
"scripts": {

View File

@@ -1,5 +1,5 @@
import { inspect } from 'util'
import { runInNewContext } from 'vm'
import { runInThisContext } from 'vm'
import { ApplicationCommandOptionType } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
@@ -20,13 +20,13 @@ export default new AdminCommand({
required: false,
},
},
async execute(context, trigger, { code, 'show-hidden': showHidden }) {
async execute(_, trigger, { code, 'show-hidden': showHidden }) {
await trigger.reply({
ephemeral: true,
embeds: [
createSuccessEmbed('Evaluate', `\`\`\`js\n${code}\`\`\``).addFields({
name: 'Result',
value: `\`\`\`js\n${inspect(runInNewContext(code, { client: trigger.client, context, trigger }), { depth: 1, showHidden, getters: true, numericSeparator: true, showProxy: true })}\`\`\``,
value: `\`\`\`js\n${inspect(runInThisContext(code), { depth: 1, showHidden, getters: true, numericSeparator: true, showProxy: true })}\`\`\``,
}),
],
})

View File

@@ -0,0 +1,17 @@
import { AdminCommand } from '$/classes/Command'
import { join, dirname } from 'path'
import type { Config } from 'config.schema'
export default new AdminCommand({
name: 'reload',
description: 'Reload configuration',
async execute(context, trigger) {
context.config = ((await import(join(dirname(Bun.main), '..', 'config.js'))) as { default: Config }).default
await trigger.reply({
content: 'Reloaded configuration',
ephemeral: true,
})
},
})

View File

@@ -1,4 +1,4 @@
import { EmbedBuilder, GuildChannel } from 'discord.js'
import { EmbedBuilder } from 'discord.js'
import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
@@ -31,7 +31,7 @@ export default new ModerationCommand({
throw new CommandError(CommandErrorType.MissingArgument, 'Either `amount` or `until` must be provided.')
const channel = interaction.channel!
if (!(channel.isTextBased() && channel instanceof GuildChannel))
if (!channel.isTextBased())
throw new CommandError(CommandErrorType.InvalidChannel, 'The supplied channel is not a text channel.')
const embed = applyCommonEmbedStyles(

View File

@@ -28,7 +28,7 @@ export default new ModerationCommand({
if (!channel?.isTextBased() || channel.isDMBased())
throw new CommandError(
CommandErrorType.InvalidChannel,
'The supplied channel is not a text channel or does not exist.',
'The supplied channel is not a text channel.',
)
if (Number.isNaN(duration)) throw new CommandError(CommandErrorType.InvalidDuration, 'Invalid duration.')

View File

@@ -4,12 +4,6 @@ export const MessageScanLabeledResponseReactions = {
delete: '❌',
} as const
export const MessageScanHumanizedMode = {
ocr: 'image recognition',
nlp: 'text analysis',
match: 'pattern matching',
} as const
export const DefaultEmbedColor = '#4E98F0'
export const ReVancedLogoURL =
'https://media.discordapp.net/attachments/1095487869923119144/1115436493050224660/revanced-logo.png'

View File

@@ -56,7 +56,7 @@ if (DatabasePath && !existsSync(DatabasePath)) {
}
}
const db = new Database(DatabasePath)
const db = new Database(DatabasePath, { readwrite: true, create: true })
if (dbSchemaFileName) db.run(readFileSync(join(DatabaseSchemaDir, dbSchemaFileName)).toString())
export const database = drizzle(db, {

View File

@@ -24,7 +24,7 @@ withContext(on, 'messageCreate', async (context, msg) => {
try {
logger.debug(`Classifying message ${msg.id}`)
const { response, label, replyToReplied } = await getResponseFromText(
const { response, label, respondToReply } = await getResponseFromText(
msg.content,
filteredResponses,
context,
@@ -33,13 +33,13 @@ withContext(on, 'messageCreate', async (context, msg) => {
if (response) {
logger.debug('Response found')
const toReply = replyToReplied ? (msg.reference?.messageId ? await msg.fetchReference() : msg) : msg
const toReply = respondToReply ? (msg.reference?.messageId ? await msg.fetchReference() : msg) : msg
const reply = await toReply.reply({
...response,
embeds: response.embeds?.map(it => createMessageScanResponseEmbed(it, label ? 'nlp' : 'match')),
embeds: response.embeds?.map(createMessageScanResponseEmbed),
})
if (label)
if (label) {
db.insert(responses).values({
replyId: reply.id,
channelId: reply.channel.id,
@@ -49,7 +49,6 @@ withContext(on, 'messageCreate', async (context, msg) => {
content: msg.content,
})
if (label) {
for (const reaction of Object.values(MessageScanLabeledResponseReactions)) {
await reply.react(reaction)
}
@@ -60,11 +59,22 @@ withContext(on, 'messageCreate', async (context, msg) => {
}
}
if (msg.attachments.size > 0) {
if (msg.attachments.size > 0 && config.attachments?.scanAttachments) {
logger.debug(`Classifying message attachments for ${msg.id}`)
for (const attachment of msg.attachments.values()) {
if (attachment.contentType && !config.allowedAttachmentMimeTypes.includes(attachment.contentType)) continue
if (
config.attachments.allowedMimeTypes &&
!config.attachments.allowedMimeTypes.includes(attachment.contentType!)
) {
logger.debug(`Disallowed MIME type for attachment: ${attachment.url}, ${attachment.contentType}`)
continue
}
if (attachment.contentType?.startsWith('text/') && attachment.size > (config.attachments.maxTextFileSize ?? 512 * 1000)) {
logger.debug(`Attachment ${attachment.url} is too large be to scanned, size is ${attachment.size}`)
continue
}
try {
const { text: content } = await api.client.parseImage(attachment.url)
@@ -74,7 +84,7 @@ withContext(on, 'messageCreate', async (context, msg) => {
logger.debug(`Response found for attachment: ${attachment.url}`)
await msg.reply({
...response,
embeds: response.embeds?.map(it => createMessageScanResponseEmbed(it, 'ocr')),
embeds: response.embeds?.map(createMessageScanResponseEmbed),
})
break

View File

@@ -1,4 +1,4 @@
import { DefaultEmbedColor, MessageScanHumanizedMode, ReVancedLogoURL } from '$/constants'
import { DefaultEmbedColor, ReVancedLogoURL } from '$/constants'
import { type APIEmbed, EmbedBuilder, type EmbedField, type JSONEncodable, type User } from 'discord.js'
import type { ConfigMessageScanResponseMessage } from '../../../config.schema'
@@ -25,12 +25,7 @@ export const createSuccessEmbed = (title: string | null, description?: string) =
export const createMessageScanResponseEmbed = (
response: NonNullable<ConfigMessageScanResponseMessage['embeds']>[number],
mode: 'ocr' | 'nlp' | 'match',
) =>
applyCommonEmbedStyles(response, true, true, true).setFooter({
text: `ReVanced • Via ${MessageScanHumanizedMode[mode]}`,
iconURL: ReVancedLogoURL,
})
) => applyCommonEmbedStyles(response, true, true, true)
export const createModerationActionEmbed = (
action: string,

View File

@@ -64,23 +64,24 @@ export const getResponseFromText = async (
const matchedLabel = scan.labels[0]!
logger.debug(`Message matched label with confidence: ${matchedLabel.name}, ${matchedLabel.confidence}`)
let triggerConfig: ConfigMessageScanResponseLabelConfig | undefined
const labelConfig = responses.find(x => {
let trigger: ConfigMessageScanResponseLabelConfig | undefined
const response = responses.find(x => {
const config = x.triggers.text!.find(
(x): x is ConfigMessageScanResponseLabelConfig => 'label' in x && x.label === matchedLabel.name,
)
if (config) triggerConfig = config
if (config) trigger = config
return config
})
if (!labelConfig) {
logger.warn(`No label config found for label ${matchedLabel.name}`)
if (!response) {
logger.warn(`No response config found for label ${matchedLabel.name}`)
// This returns the default value set in line 17, which means no response matched
return responseConfig
}
if (matchedLabel.confidence >= triggerConfig!.threshold) {
if (matchedLabel.confidence >= trigger!.threshold) {
logger.debug('Label confidence is enough')
responseConfig = labelConfig
responseConfig = { ...responseConfig, ...response, label: trigger!.label }
}
}
}
@@ -94,8 +95,8 @@ export const getResponseFromText = async (
} = responses[i]!
const firstLabelIndex = firstLabelIndexes[i] ?? -1
for (let i = firstLabelIndex + 1; i < textTriggers!.length; i++) {
const trigger = textTriggers![i]!
for (let j = firstLabelIndex + 1; j < textTriggers!.length; j++) {
const trigger = textTriggers![j]!
if (trigger instanceof RegExp) {
if (trigger.test(content)) {
@@ -158,7 +159,7 @@ export const handleUserResponseCorrection = async (
await reply.edit({
...correctLabelResponse.response,
embeds: correctLabelResponse.response.embeds?.map(it => createMessageScanResponseEmbed(it, 'nlp')),
embeds: correctLabelResponse.response.embeds?.map(createMessageScanResponseEmbed),
})
}

View File

@@ -38,7 +38,7 @@ export const getLogChannel = async (guild: Guild) => {
try {
const channel = await guild.channels.fetch(logConfig.thread ?? logConfig.channel)
if (!channel || !channel.isTextBased())
if (!channel?.isTextBased())
return void logger.warn('The moderation log channel does not exist, skipping logging')
return channel