mirror of
https://github.com/ReVanced/revanced-bots.git
synced 2026-01-11 13:56:15 +00:00
feat(bots/discord)!: add admin config
This commit is contained in:
@@ -4,7 +4,18 @@
|
||||
* @type {import('./config.schema').Config}
|
||||
*/
|
||||
export default {
|
||||
owners: ['USER_ID_HERE'],
|
||||
/**
|
||||
* ? ADMIN CONFIGURATION
|
||||
* Bot administrators can run destructive commands like /stop, or /register.
|
||||
*
|
||||
* ! The match condition is `any`: If the user ID matches or the member has a specific role in the list, it considers that user as admin.
|
||||
*/
|
||||
admin: {
|
||||
users: ['USER_ID_HERE'],
|
||||
roles: {
|
||||
GUILD_ID_HERE: ['ROLE_ID_HERE'],
|
||||
},
|
||||
},
|
||||
guilds: ['GUILD_ID_HERE'],
|
||||
moderation: {
|
||||
cure: {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { BaseMessageOptions } from 'discord.js'
|
||||
|
||||
export type Config = {
|
||||
owners: string[]
|
||||
admin?: {
|
||||
users?: string[]
|
||||
roles?: Record<string, string[]>
|
||||
}
|
||||
guilds: string[]
|
||||
moderation?: {
|
||||
roles: string[]
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
.setDMPermission(true)
|
||||
.toJSON(),
|
||||
|
||||
ownerOnly: true,
|
||||
adminOnly: true,
|
||||
global: true,
|
||||
|
||||
async execute(_, interaction) {
|
||||
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
.setDMPermission(true)
|
||||
.toJSON(),
|
||||
|
||||
ownerOnly: true,
|
||||
adminOnly: true,
|
||||
global: true,
|
||||
|
||||
async execute(_, interaction) {
|
||||
|
||||
@@ -11,7 +11,7 @@ export default {
|
||||
.setDMPermission(true)
|
||||
.toJSON(),
|
||||
|
||||
ownerOnly: true,
|
||||
adminOnly: true,
|
||||
global: true,
|
||||
|
||||
async execute({ api, logger }, interaction) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
|
||||
global: false,
|
||||
|
||||
async execute({ logger }, interaction, { userIsOwner }) {
|
||||
async execute({ logger }, interaction, { isExecutorBotAdmin: isExecutorAdmin }) {
|
||||
const user = interaction.options.getUser('member', true)
|
||||
const reason = interaction.options.getString('reason') ?? 'No reason provided'
|
||||
const duration = interaction.options.getString('duration')
|
||||
@@ -48,7 +48,7 @@ export default {
|
||||
if (!member.manageable)
|
||||
throw new CommandError(CommandErrorType.Generic, 'This user cannot be managed by the bot.')
|
||||
|
||||
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0 && !userIsOwner)
|
||||
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0 && !isExecutorAdmin)
|
||||
throw new CommandError(
|
||||
CommandErrorType.InvalidUser,
|
||||
'You cannot mute a user with a role equal to or higher than yours.',
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
|
||||
global: false,
|
||||
|
||||
async execute({ logger }, interaction, { userIsOwner }) {
|
||||
async execute({ logger }, interaction, { isExecutorBotAdmin: isExecutorAdmin }) {
|
||||
const action = interaction.options.getString('action', true) as 'apply' | 'remove'
|
||||
const user = interaction.options.getUser('member', true)
|
||||
const preset = interaction.options.getString('preset', true)
|
||||
@@ -61,7 +61,7 @@ export default {
|
||||
'The duration must be at least 1 millisecond long.',
|
||||
)
|
||||
|
||||
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0 && !userIsOwner)
|
||||
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0 && !isExecutorAdmin)
|
||||
throw new CommandError(
|
||||
CommandErrorType.InvalidUser,
|
||||
'You cannot apply a role preset to a user with a role equal to or higher than yours.',
|
||||
|
||||
@@ -39,10 +39,10 @@ export type Command = {
|
||||
roles?: string[]
|
||||
}
|
||||
/**
|
||||
* Whether this command can only be used by bot owners.
|
||||
* Whether this command can only be used by bot admins.
|
||||
* @default false
|
||||
*/
|
||||
ownerOnly?: boolean
|
||||
adminOnly?: boolean
|
||||
/**
|
||||
* Whether to register this command as a global slash command.
|
||||
* This is set to `false` and commands will be registered in allowed guilds only by default.
|
||||
@@ -52,5 +52,5 @@ export type Command = {
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
userIsOwner: boolean
|
||||
isExecutorBotAdmin: boolean
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CommandError from '$/classes/CommandError'
|
||||
import { isAdmin } from '$/utils/discord/permissions'
|
||||
import { createErrorEmbed, createStackTraceEmbed } from '$utils/discord/embeds'
|
||||
import { on, withContext } from '$utils/discord/events'
|
||||
|
||||
@@ -11,14 +12,14 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
|
||||
logger.debug(`Command ${interaction.commandName} being invoked by ${interaction.user.tag}`)
|
||||
if (!command) return void logger.error(`Command ${interaction.commandName} not implemented but registered!!!`)
|
||||
|
||||
const isOwner = config.owners.includes(interaction.user.id)
|
||||
const isExecutorBotAdmin = isAdmin(await interaction.guild?.members.fetch(interaction.user.id) || interaction.user, config.admin)
|
||||
|
||||
/**
|
||||
* Owner check
|
||||
* Admin check
|
||||
*/
|
||||
if (command.ownerOnly && !isOwner)
|
||||
if (command.adminOnly && !isExecutorBotAdmin)
|
||||
return void (await interaction.reply({
|
||||
embeds: [createErrorEmbed('Massive skill issue', 'This command can only be used by the bot owners.')],
|
||||
embeds: [createErrorEmbed('Massive skill issue', 'This command can only be used by the bot admins.')],
|
||||
ephemeral: true,
|
||||
}))
|
||||
|
||||
@@ -39,7 +40,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
|
||||
*/
|
||||
if (interaction.inGuild()) {
|
||||
// Bot owners get bypass
|
||||
if (command.memberRequirements && !isOwner) {
|
||||
if (command.memberRequirements && !isExecutorBotAdmin) {
|
||||
const { permissions = 0n, roles = [], mode } = command.memberRequirements
|
||||
|
||||
const member = await interaction.guild!.members.fetch(interaction.user.id)
|
||||
@@ -69,7 +70,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
|
||||
|
||||
try {
|
||||
logger.debug(`Command ${interaction.commandName} being executed`)
|
||||
await command.execute(context, interaction, { userIsOwner: isOwner })
|
||||
await command.execute(context, interaction, { isExecutorBotAdmin })
|
||||
} catch (err) {
|
||||
logger.error(`Error while executing command ${interaction.commandName}:`, err)
|
||||
await interaction[interaction.replied ? 'followUp' : 'reply']({
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { ConfigMessageScanResponseLabelConfig } from '$/../config.schema'
|
||||
import { responses } from '$/database/schemas'
|
||||
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { isAdmin } from '$/utils/discord/permissions'
|
||||
|
||||
const PossibleReactions = Object.values(Reactions) as string[]
|
||||
|
||||
@@ -32,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 (!config.owners.includes(user.id)) {
|
||||
if (!isAdmin(reactionMessage.member || reactionMessage.author, config.admin)) {
|
||||
// User is in guild, and config has member requirements
|
||||
if (
|
||||
reactionMessage.inGuild() &&
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { Command } from '$commands/types'
|
||||
import { listAllFilesRecursive } from '$utils/fs'
|
||||
|
||||
export const loadCommands = async (dir: string) => {
|
||||
const commandsMap: Record<string, Command> = {}
|
||||
const files = listAllFilesRecursive(dir)
|
||||
const commands = await Promise.all(
|
||||
files.map(async file => {
|
||||
const command = await import(file)
|
||||
return command.default
|
||||
}),
|
||||
)
|
||||
|
||||
for (const command of commands) {
|
||||
if (command) commandsMap[command.data.name] = command
|
||||
}
|
||||
|
||||
return commandsMap
|
||||
}
|
||||
11
bots/discord/src/utils/discord/permissions.ts
Normal file
11
bots/discord/src/utils/discord/permissions.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { GuildMember, type User } from 'discord.js'
|
||||
import type { Config } from 'config.schema'
|
||||
|
||||
export const isAdmin = (userOrMember: User | GuildMember, adminConfig: Config['admin']) => {
|
||||
return adminConfig?.users?.includes(userOrMember.id) || (userOrMember instanceof GuildMember && isMemberAdmin(userOrMember, adminConfig))
|
||||
}
|
||||
|
||||
export const isMemberAdmin = (member: GuildMember, adminConfig: Config['admin']) => {
|
||||
const roles = new Set(member.roles.cache.keys())
|
||||
return Boolean(adminConfig?.roles?.[member.guild.id]?.some(role => roles.has(role)))
|
||||
}
|
||||
Reference in New Issue
Block a user