From cdb600195520dba33110c40841629259e317055e Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Sun, 28 Jul 2024 20:10:47 +0700 Subject: [PATCH] feat(bots/discord): blacklist and whitelist for filters --- bots/discord/config.js | 28 ++++++++++++++++--- bots/discord/config.schema.ts | 16 +++++++---- bots/discord/src/context.ts | 16 +++-------- bots/discord/src/events/api/disconnect.ts | 2 +- .../discord/messageCreate/scanMessage.ts | 7 +++-- .../messageReactionAdd/correctResponse.ts | 2 +- bots/discord/src/utils/discord/messageScan.ts | 20 +++++-------- bots/discord/src/utils/discord/permissions.ts | 10 +++---- 8 files changed, 58 insertions(+), 43 deletions(-) diff --git a/bots/discord/config.js b/bots/discord/config.js index 9415baa..5b7eedd 100644 --- a/bots/discord/config.js +++ b/bots/discord/config.js @@ -37,11 +37,19 @@ export default { checkExpiredEvery: 3600, }, messageScan: { + scanBots: false, + scanOutsideGuilds: false, filter: { - channels: ['CHANNEL_ID_HERE'], - roles: ['ROLE_ID_HERE'], - users: ['USER_ID_HERE'], - whitelist: false, + whitelist: { + channels: ['CHANNEL_ID_HERE'], + roles: ['ROLE_ID_HERE'], + users: ['USER_ID_HERE'], + }, + blacklist: { + channels: ['CHANNEL_ID_HERE'], + roles: ['ROLE_ID_HERE'], + users: ['USER_ID_HERE'], + }, }, humanCorrections: { falsePositiveLabel: 'false_positive', @@ -55,6 +63,18 @@ export default { allowedAttachmentMimeTypes: ['image/jpeg', 'image/png', 'image/webp'], responses: [ { + filterOverride: { + whitelist: { + channels: ['CHANNEL_ID_HERE'], + roles: ['ROLE_ID_HERE'], + users: ['USER_ID_HERE'], + }, + blacklist: { + channels: ['CHANNEL_ID_HERE'], + roles: ['ROLE_ID_HERE'], + users: ['USER_ID_HERE'], + }, + }, triggers: { text: [/^regexp?$/, { label: 'label', threshold: 0.85 }], }, diff --git a/bots/discord/config.schema.ts b/bots/discord/config.schema.ts index 7498d48..fb05df1 100644 --- a/bots/discord/config.schema.ts +++ b/bots/discord/config.schema.ts @@ -20,12 +20,12 @@ export type Config = { guilds: Record> } messageScan?: { + scanBots?: boolean + scanOutsideGuilds?: boolean allowedAttachmentMimeTypes: string[] - filter: { - roles?: string[] - users?: string[] - channels?: string[] - whitelist: boolean + filter?: { + whitelist?: Filter + blacklist?: Filter } humanCorrections: { falsePositiveLabel: string @@ -73,4 +73,10 @@ export type ConfigMessageScanResponseLabelConfig = { threshold: number } +export type Filter = { + roles?: string[] + users?: string[] + channels?: string[] +} + export type ConfigMessageScanResponseMessage = BaseMessageOptions diff --git a/bots/discord/src/context.ts b/bots/discord/src/context.ts index a855887..915df97 100644 --- a/bots/discord/src/context.ts +++ b/bots/discord/src/context.ts @@ -3,7 +3,7 @@ import { existsSync, readFileSync, readdirSync } from 'fs' import { join } from 'path' import { Client as APIClient } from '@revanced/bot-api' import { createLogger } from '@revanced/bot-shared' -import { ActivityType, Client as DiscordClient, Partials } from 'discord.js' +import { Client as DiscordClient, Partials } from 'discord.js' import { drizzle } from 'drizzle-orm/bun-sqlite' // Export config first, as commands require them @@ -13,7 +13,7 @@ export { config } import * as commands from './commands' import * as schemas from './database/schemas' -import type { Command } from './commands/types' +import type Command from './classes/Command' export const logger = createLogger({ level: config.logLevel === 'none' ? Number.MAX_SAFE_INTEGER : config.logLevel, @@ -27,7 +27,7 @@ export const api = { }, }, }), - isStopping: false, + intentionallyDisconnecting: false, disconnectCount: 0, } @@ -80,16 +80,8 @@ export const discord = { repliedUser: true, }, partials: [Partials.Message, Partials.Reaction], - presence: { - activities: [ - { - type: ActivityType.Watching, - name: 'cat videos', - }, - ], - }, }), - commands: Object.fromEntries(Object.values(commands).map(cmd => [cmd.data.name, cmd])) as Record< + commands: Object.fromEntries(Object.values(commands).map(cmd => [cmd.name, cmd])) as Record< string, Command >, diff --git a/bots/discord/src/events/api/disconnect.ts b/bots/discord/src/events/api/disconnect.ts index 7af62eb..75b9340 100644 --- a/bots/discord/src/events/api/disconnect.ts +++ b/bots/discord/src/events/api/disconnect.ts @@ -2,7 +2,7 @@ import { on, withContext } from '$utils/api/events' import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared' withContext(on, 'disconnect', ({ api, config, logger }, reason, msg) => { - if (reason === DisconnectReason.PlannedDisconnect && api.isStopping) return + if (reason === DisconnectReason.PlannedDisconnect && api.intentionallyDisconnecting) return const ws = api.client.ws if (!ws.disconnected) ws.disconnect() diff --git a/bots/discord/src/events/discord/messageCreate/scanMessage.ts b/bots/discord/src/events/discord/messageCreate/scanMessage.ts index 508737b..1009808 100644 --- a/bots/discord/src/events/discord/messageCreate/scanMessage.ts +++ b/bots/discord/src/events/discord/messageCreate/scanMessage.ts @@ -1,6 +1,6 @@ import { MessageScanLabeledResponseReactions } from '$/constants' import { responses } from '$/database/schemas' -import { getResponseFromText, shouldScanMessage } from '$/utils/discord/messageScan' +import { getResponseFromText, messageMatchesFilter } from '$/utils/discord/messageScan' import { createMessageScanResponseEmbed } from '$utils/discord/embeds' import { on, withContext } from '$utils/discord/events' @@ -13,8 +13,11 @@ withContext(on, 'messageCreate', async (context, msg) => { } = context if (!config || !config.responses) return + if (msg.author.bot && !config.scanBots) + if (!msg.inGuild() && !config.scanOutsideGuilds) return + if (msg.inGuild() && msg.member?.partial) await msg.member.fetch() - const filteredResponses = config.responses.filter(x => shouldScanMessage(msg, x.filterOverride ?? config.filter)) + const filteredResponses = config.responses.filter(x => messageMatchesFilter(msg, x.filterOverride ?? config.filter)) if (!filteredResponses.length) return if (msg.content.length) { diff --git a/bots/discord/src/events/discord/messageReactionAdd/correctResponse.ts b/bots/discord/src/events/discord/messageReactionAdd/correctResponse.ts index ccc6fe8..4aa8a30 100644 --- a/bots/discord/src/events/discord/messageReactionAdd/correctResponse.ts +++ b/bots/discord/src/events/discord/messageReactionAdd/correctResponse.ts @@ -33,7 +33,7 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => { if (reactionMessage.author.id !== reaction.client.user!.id) return if (!PossibleReactions.includes(reaction.emoji.name!)) return - if (!isAdmin(reactionMessage.member || reactionMessage.author, config.admin)) { + if (!isAdmin(reactionMessage.member || reactionMessage.author)) { // User is in guild, and config has member requirements if ( reactionMessage.inGuild() && diff --git a/bots/discord/src/utils/discord/messageScan.ts b/bots/discord/src/utils/discord/messageScan.ts index 7672c0f..5352901 100644 --- a/bots/discord/src/utils/discord/messageScan.ts +++ b/bots/discord/src/utils/discord/messageScan.ts @@ -113,24 +113,18 @@ export const getResponseFromText = async ( return responseConfig } -export const shouldScanMessage = ( +export const messageMatchesFilter = ( message: Message, filter: NonNullable['filter'], -): message is Message => { - if (message.author.bot) return false - if (!message.guild) return false +) => { if (!filter) return true - const filters = [ - filter.users?.includes(message.author.id), - message.member?.roles.cache.some(x => filter.roles?.includes(x.id)), - filter.channels?.includes(message.channel.id), - ] + const memberRoles = new Set(message.member?.roles.cache.keys()) + const blFilter = filter.blacklist - if (filter.whitelist && filters.every(x => !x)) return false - if (!filter.whitelist && filters.some(x => x)) return false - - return true + // If matches blacklist, will return false + // Any other case, will return true + return !(blFilter && (blFilter.channels?.includes(message.channelId) || blFilter.roles?.some(role => memberRoles.has(role)) || blFilter.users?.includes(message.author.id))) } export const handleUserResponseCorrection = async ( diff --git a/bots/discord/src/utils/discord/permissions.ts b/bots/discord/src/utils/discord/permissions.ts index 6bb2540..576e4c7 100644 --- a/bots/discord/src/utils/discord/permissions.ts +++ b/bots/discord/src/utils/discord/permissions.ts @@ -1,11 +1,11 @@ import { GuildMember, type User } from 'discord.js' -import type { Config } from 'config.schema' +import config from '../../../config' -export const isAdmin = (userOrMember: User | GuildMember, adminConfig: Config['admin']) => { - return adminConfig?.users?.includes(userOrMember.id) || (userOrMember instanceof GuildMember && isMemberAdmin(userOrMember, adminConfig)) +export const isAdmin = (userOrMember: User | GuildMember) => { + return config.admin?.users?.includes(userOrMember.id) || (userOrMember instanceof GuildMember && isMemberAdmin(userOrMember)) } -export const isMemberAdmin = (member: GuildMember, adminConfig: Config['admin']) => { +export const isMemberAdmin = (member: GuildMember) => { const roles = new Set(member.roles.cache.keys()) - return Boolean(adminConfig?.roles?.[member.guild.id]?.some(role => roles.has(role))) + return Boolean(config?.admin?.roles?.[member.guild.id]?.some(role => roles.has(role))) } \ No newline at end of file