feat(bots/discord): blacklist and whitelist for filters

This commit is contained in:
PalmDevs
2024-07-28 20:10:47 +07:00
parent e748a4da92
commit cdb6001955
8 changed files with 58 additions and 43 deletions

View File

@@ -37,11 +37,19 @@ export default {
checkExpiredEvery: 3600, checkExpiredEvery: 3600,
}, },
messageScan: { messageScan: {
scanBots: false,
scanOutsideGuilds: false,
filter: { filter: {
channels: ['CHANNEL_ID_HERE'], whitelist: {
roles: ['ROLE_ID_HERE'], channels: ['CHANNEL_ID_HERE'],
users: ['USER_ID_HERE'], roles: ['ROLE_ID_HERE'],
whitelist: false, users: ['USER_ID_HERE'],
},
blacklist: {
channels: ['CHANNEL_ID_HERE'],
roles: ['ROLE_ID_HERE'],
users: ['USER_ID_HERE'],
},
}, },
humanCorrections: { humanCorrections: {
falsePositiveLabel: 'false_positive', falsePositiveLabel: 'false_positive',
@@ -55,6 +63,18 @@ export default {
allowedAttachmentMimeTypes: ['image/jpeg', 'image/png', 'image/webp'], allowedAttachmentMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
responses: [ 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: { triggers: {
text: [/^regexp?$/, { label: 'label', threshold: 0.85 }], text: [/^regexp?$/, { label: 'label', threshold: 0.85 }],
}, },

View File

@@ -20,12 +20,12 @@ export type Config = {
guilds: Record<string, Record<string, RolePresetConfig>> guilds: Record<string, Record<string, RolePresetConfig>>
} }
messageScan?: { messageScan?: {
scanBots?: boolean
scanOutsideGuilds?: boolean
allowedAttachmentMimeTypes: string[] allowedAttachmentMimeTypes: string[]
filter: { filter?: {
roles?: string[] whitelist?: Filter
users?: string[] blacklist?: Filter
channels?: string[]
whitelist: boolean
} }
humanCorrections: { humanCorrections: {
falsePositiveLabel: string falsePositiveLabel: string
@@ -73,4 +73,10 @@ export type ConfigMessageScanResponseLabelConfig = {
threshold: number threshold: number
} }
export type Filter = {
roles?: string[]
users?: string[]
channels?: string[]
}
export type ConfigMessageScanResponseMessage = BaseMessageOptions export type ConfigMessageScanResponseMessage = BaseMessageOptions

View File

@@ -3,7 +3,7 @@ import { existsSync, readFileSync, readdirSync } from 'fs'
import { join } from 'path' import { join } from 'path'
import { Client as APIClient } from '@revanced/bot-api' import { Client as APIClient } from '@revanced/bot-api'
import { createLogger } from '@revanced/bot-shared' 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' import { drizzle } from 'drizzle-orm/bun-sqlite'
// Export config first, as commands require them // Export config first, as commands require them
@@ -13,7 +13,7 @@ export { config }
import * as commands from './commands' import * as commands from './commands'
import * as schemas from './database/schemas' import * as schemas from './database/schemas'
import type { Command } from './commands/types' import type Command from './classes/Command'
export const logger = createLogger({ export const logger = createLogger({
level: config.logLevel === 'none' ? Number.MAX_SAFE_INTEGER : config.logLevel, level: config.logLevel === 'none' ? Number.MAX_SAFE_INTEGER : config.logLevel,
@@ -27,7 +27,7 @@ export const api = {
}, },
}, },
}), }),
isStopping: false, intentionallyDisconnecting: false,
disconnectCount: 0, disconnectCount: 0,
} }
@@ -80,16 +80,8 @@ export const discord = {
repliedUser: true, repliedUser: true,
}, },
partials: [Partials.Message, Partials.Reaction], partials: [Partials.Message, Partials.Reaction],
presence: {
activities: [
{
type: ActivityType.Watching,
name: 'cat videos',
},
],
},
}), }),
commands: Object.fromEntries(Object.values<Command>(commands).map(cmd => [cmd.data.name, cmd])) as Record< commands: Object.fromEntries(Object.values<Command>(commands).map(cmd => [cmd.name, cmd])) as Record<
string, string,
Command Command
>, >,

View File

@@ -2,7 +2,7 @@ import { on, withContext } from '$utils/api/events'
import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared' import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared'
withContext(on, 'disconnect', ({ api, config, logger }, reason, msg) => { 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 const ws = api.client.ws
if (!ws.disconnected) ws.disconnect() if (!ws.disconnected) ws.disconnect()

View File

@@ -1,6 +1,6 @@
import { MessageScanLabeledResponseReactions } from '$/constants' import { MessageScanLabeledResponseReactions } from '$/constants'
import { responses } from '$/database/schemas' 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 { createMessageScanResponseEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
@@ -13,8 +13,11 @@ withContext(on, 'messageCreate', async (context, msg) => {
} = context } = context
if (!config || !config.responses) return 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 (!filteredResponses.length) return
if (msg.content.length) { if (msg.content.length) {

View File

@@ -33,7 +33,7 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
if (reactionMessage.author.id !== reaction.client.user!.id) return if (reactionMessage.author.id !== reaction.client.user!.id) return
if (!PossibleReactions.includes(reaction.emoji.name!)) 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 // User is in guild, and config has member requirements
if ( if (
reactionMessage.inGuild() && reactionMessage.inGuild() &&

View File

@@ -113,24 +113,18 @@ export const getResponseFromText = async (
return responseConfig return responseConfig
} }
export const shouldScanMessage = ( export const messageMatchesFilter = (
message: Message, message: Message,
filter: NonNullable<Config['messageScan']>['filter'], filter: NonNullable<Config['messageScan']>['filter'],
): message is Message<true> => { ) => {
if (message.author.bot) return false
if (!message.guild) return false
if (!filter) return true if (!filter) return true
const filters = [ const memberRoles = new Set(message.member?.roles.cache.keys())
filter.users?.includes(message.author.id), const blFilter = filter.blacklist
message.member?.roles.cache.some(x => filter.roles?.includes(x.id)),
filter.channels?.includes(message.channel.id),
]
if (filter.whitelist && filters.every(x => !x)) return false // If matches blacklist, will return false
if (!filter.whitelist && filters.some(x => x)) 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)))
return true
} }
export const handleUserResponseCorrection = async ( export const handleUserResponseCorrection = async (

View File

@@ -1,11 +1,11 @@
import { GuildMember, type User } from 'discord.js' 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']) => { export const isAdmin = (userOrMember: User | GuildMember) => {
return adminConfig?.users?.includes(userOrMember.id) || (userOrMember instanceof GuildMember && isMemberAdmin(userOrMember, adminConfig)) 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()) 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)))
} }