ban reaction

This commit is contained in:
404invalid-user 2025-03-20 21:30:17 +00:00
parent b51e508357
commit 0529a9d4d7
13 changed files with 305 additions and 79 deletions

View file

@ -1,6 +1,6 @@
{
"name": "knightrider",
"version": "4.8.434",
"version": "4.9.000",
"description": "a bot for a private discord server",
"main": "./dist/index.js",
"scripts": {

View file

@ -0,0 +1,53 @@
import { Client, ChatInputCommandInteraction, PermissionFlagsBits, SlashCommandBuilder } from "discord.js";
import YALAS from 'mcstatusbot-logger';
import * as AddBanReaction from './banreaction/add';
import * as RemoveBanReaction from './banreaction/remove';
import { GuildInstance } from "../../database/schemas/Guild";
import { UserInstance } from "../../database/schemas/User";
const data = {
allowSuspendedUserAccess: false,
command: new SlashCommandBuilder()
.setName('banreaction')
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
.setDescription("setup reaction to ban members from your server")
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription("Add a ban reaction to your channel")
.addChannelOption(option =>
option.setName('channel')
.setDescription('The channel in which you want to add a reaction')
.setRequired(true)
)
)
.addSubcommand(subcommand =>
subcommand
.setName('remove')
.setDescription("remove a ban reaction")
.addChannelOption(option =>
option.setName('channel')
.setDescription('The channel in which you want to remove a reaction')
.setRequired(true)
)
)
}
export { data };
export async function chatInputCommand(client: Client, interaction: ChatInputCommandInteraction, guild: GuildInstance, user: UserInstance) {
switch (interaction.options.getSubcommand()) {
case 'add':
AddBanReaction.chatInputCommand(client, interaction, guild, user)
break;
case 'remove':
RemoveBanReaction.chatInputCommand(client, interaction, guild, user)
break;
}
}

View file

@ -0,0 +1,116 @@
import { Client, ChatInputCommandInteraction, GuildChannelResolvable, PermissionsBitField, ChannelType, EmbedBuilder } from "discord.js";
import { schemas } from "../../../database";
import YALAS from 'mcstatusbot-logger';
import { GuildInstance } from "../../../database/schemas/Guild";
import { UserInstance } from "../../../database/schemas/User";
export async function chatInputCommand(client: Client, interaction: ChatInputCommandInteraction, guild: GuildInstance, user: UserInstance) {
if (!interaction.guild) return interaction.reply("must be done in discord server");
const botMember = interaction.guild.members.me;
if (!botMember) {
YALAS.error("reactionrole add: bot in guild null");
return;
}
//get and validate channel
const channel = interaction.options.getChannel('channel');
if (channel == undefined) {
return interaction.reply({
embeds: [{
title: "Channel Not Found",
description: "The selected channel could not be found please try again."
}]
});
}
if (channel.type !== ChannelType.GuildText) {
return interaction.reply({
embeds: [{
title: "That Will Not Work",
description: "The selected channel is not a text channel please select a text channel."
}]
});
}
const botPermSM = botMember.permissionsIn((channel as GuildChannelResolvable)).toArray();
if (botPermSM && !botPermSM.includes('SendMessages')) {
return interaction.reply({
embeds: [{
title: "Missing permission",
description: "you need to give me the send message permission in the selected channel."
}]
});
}
//check bot has manage roles perm
const botPermMR = botMember.permissions.has(PermissionsBitField.Flags.BanMembers);
if (!botPermMR) {
return interaction.reply({
embeds: [{
title: "Missing permission",
description: "you need to give me the Ban Members permission in this server."
}]
});
}
await interaction.deferReply();
//check emoji does net exist for reaction in channel
try {
const BanReaction = await schemas['BanReaction'].findOne({
where: {
guild: guild.id,
channel: channel.id
}
});
if (BanReaction !== null) {
return interaction.editReply({
embeds: [{
title: "Ban Reaction Exists",
description: "This is already setup in selected channel."
}]
});
}
} catch (err: any) {
YALAS.error(err)
YALAS.error(err.stack || err);
return interaction.editReply("error when checking for existing ban reaction in database.");
}
try {
await schemas['BanReaction'].create({
guild: guild.id,
channel: channel.id
});
} catch (err: any) {
YALAS.error(err.stack || err);
return interaction.editReply("error when saving ban reaction to database.");
}
try {
const textChannel = interaction.guild?.channels.cache.get(channel.id);
if (textChannel === undefined || textChannel.type !== 0) return;
const embed = new EmbedBuilder()
.setDescription("React with a ❌")
.setFooter({ text: 'to get banned this is no joke' });
const BanReactionMsg = await textChannel.send({ embeds: [embed] });
await BanReactionMsg.react('❌');
return interaction.editReply("ban reaction added to channel <#" + textChannel.id + ">.");
} catch (err: any) {
YALAS.error(err.stack || err);
return interaction.editReply("error when making ban reaction embed.");
}
}

View file

@ -0,0 +1,67 @@
import { GuildInstance } from "../../../database/schemas/Guild";
import { UserInstance } from "../../../database/schemas/User";
import { Client, ChatInputCommandInteraction, Role, GuildChannelResolvable, TextChannel, ChannelType } from "discord.js";
import parseEmoji from "../../functions/parseEmoji";
import { schemas } from "../../../database";
import YALAS from 'mcstatusbot-logger';
import SendReactionRoleEmbed from "../../functions/SendReactionRoleEmbed";
export async function chatInputCommand(client: Client, interaction: ChatInputCommandInteraction, guild: GuildInstance, user: UserInstance) {
if (!interaction.guild) return interaction.reply("must be done in discord server");
//get and validate channel
const channel = interaction.options.getChannel('channel');
if (channel == undefined) {
return interaction.reply({
embeds: [{
title: "Channel Not Found",
description: "The selected channel could not be found please try again."
}]
});
}
if (channel.type !== ChannelType.GuildText) {
return interaction.reply({
embeds: [{
title: "That Will Not Work",
description: "The selected channel is not a text channel please select a text channel."
}]
});
}
await interaction.deferReply();
try {
const BanReaction = await schemas['BanReaction'].findOne({
where: {
guild: guild.id,
channel: channel.id,
},
raw: true
});
if (BanReaction == null) {
return interaction.editReply({
embeds: [{
title: "Ban Role Doesn't Exists",
description: "This ban reaction could not be found in the selected channel."
}]
});
}
await schemas['BanReaction'].destroy({ where: { id: BanReaction.id } });
return interaction.editReply("ban reaction deleted.");
} catch (err: any) {
YALAS.error(err)
YALAS.error(err.stack || err);
return interaction.editReply("error when getting and deleting reaction role in database.");
}
}

View file

@ -1,4 +1,4 @@
import { Client, ChatInputCommandInteraction, Role, GuildChannelResolvable, PermissionsBitField } from "discord.js";
import { Client, ChatInputCommandInteraction, Role, GuildChannelResolvable, PermissionsBitField, ChannelType } from "discord.js";
import parseEmoji from "../../functions/parseEmoji";
import { schemas } from "../../../database";
@ -31,7 +31,7 @@ export async function chatInputCommand(client: Client, interaction: ChatInputCom
});
}
if (channel.type !== 0) {
if (channel.type !== ChannelType.GuildText) {
return interaction.reply({
embeds: [{
title: "That Will Not Work",
@ -75,6 +75,15 @@ export async function chatInputCommand(client: Client, interaction: ChatInputCom
});
}
if (cleanEmoji == '❌') {
return interaction.reply({
embeds: [{
title: "Invalid Emoji",
description: "The selected emoji is used for the ban reaction to avoid confusion you can not use it."
}]
});
}
//get and validate role
const role = interaction.options.getMentionable('role');
@ -146,6 +155,7 @@ export async function chatInputCommand(client: Client, interaction: ChatInputCom
const textChannel = interaction.guild?.channels.cache.get(channel.id);
if (textChannel === undefined || textChannel.type !== 0) return;
const embed = await SendReactionRoleEmbed(textChannel, guild.id, channel.id);
return interaction.editReply({
embeds: [{

View file

@ -0,0 +1,41 @@
import { Client, MessageReaction, PartialMessageReaction, User, PartialUser, Guild, Role, GuildMember } from "discord.js";
import { schemas } from "../../database";
import * as YALAS from 'mcstatusbot-logger';
export default async function ReactionRoleAddHandler(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser) {
const emoji: string | null = reaction.emoji.id ?? reaction.emoji.name;
const guild: Guild | null = reaction.message.guild;
const channelId: string = reaction.message.channelId;
if (guild === null || emoji === null) {
YALAS.error("failed to find guild");
return false;
}
if (emoji!=='❌') return false;
//TODO: implement redis cache or better table lookup
const banReaction = await schemas['BanReaction'].findOne({
where: {
guild: guild.id,
channel: channelId
},
raw: true
});
if (banReaction===null) return false;
try {
const member: GuildMember | null = guild.members.cache.get(user.id) || (await guild.members.fetch(user.id).catch(() => null));
if (!member) return YALAS.error("Ban Reaction Handler: Member not found in the guild '" + guild.id + "'");
await member.ban({reason: "ban reaction"});
return true;
} catch (error){
YALAS.error("Ban Reaction Handler: Error banning user '" + user.id + "' for guild '" + guild.id + "'");
YALAS.error(error);
return true;
}
}

View file

@ -1,59 +0,0 @@
import { Client, MessageReaction, PartialMessageReaction, User, PartialUser, Guild, Role, GuildMember } from "discord.js";
import { schemas } from "../../database";
import * as YALAS from 'mcstatusbot-logger';
export default async function ReactionRoleAddHandler(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser) {
const emoji: string | null = reaction.emoji.id ?? reaction.emoji.name;
const guild: Guild | null = reaction.message.guild;
const channelId: string = reaction.message.channelId;
if (guild === null || emoji === null) return YALAS.error("failed to find guild");
async function getRole(roleId: string) {
if (!guild) return null;
let role: Role | null | undefined = guild.roles.cache.get(roleId);
if (role) return role
try {
role = await guild.roles.fetch(roleId);
} catch (error) {
YALAS.error('Error fetching role:');
YALAS.error(error);
return null;
}
if (role) return role
return null;
}
const reactionRoles = await schemas['ReactionRole'].findAll({
where: {
guild: guild.id,
channel: channelId,
reaction: emoji,
},
raw: true
});
for (const rRole of reactionRoles) {
let role = await getRole(rRole.role);
if (role == null) return YALAS.error("failed to find role '" + rRole.role + "' in guild '" + guild.id + "' ")
// Fetch role if not cached
// Get the member from the user
const member: GuildMember | null = guild.members.cache.get(user.id) || (await guild.members.fetch(user.id).catch(() => null));
if (!member) return YALAS.error("Member not found in the guild '" + guild.id + "'");
// Add the role to the member
try {
await member.roles.add(role);
if (process.env.DEBUG === 'true') YALAS.info(`Added role "${role.name}" to user "${user.tag}".`);
} catch (error) {
YALAS.error("Error adding role '" + role.id + "' to user '" + user.id + "' for guild '" + guild.id + "'");
YALAS.error(error);
}
}
}

View file

@ -1,3 +1,4 @@
//TODO: move to redis cache?
export default class PrefixCache {
private cache = new Map<string, string>();

View file

@ -44,14 +44,14 @@ export default async function ReactionRoleAddHandler(reaction: MessageReaction |
let role = await getRole(rRole.role);
if (role == null) return YALAS.error("failed to find role '" + rRole.role + "' in guild '" + guild.id + "' ")
//TODO: mod channel logs ad role error etc
//TODO: implement audit log
// Add the role to the member
try {
await member.roles.add(role);
if (process.env.DEBUG === 'true') YALAS.info(`Added role "${role.name}" to user "${user.tag}".`);
} catch (error) {
//TODO: implement ignore reaction reomve when using this
//TODO: implement ignoring this reaction remove to avoid errors with msg reaction remove handler
//reaction.users.remove(user.id);
const errUsrMsg = await (channel as TextChannel).send(`Sorry <@!${user.id}>, that did not work please try again later or ask a moderator.`);

View file

@ -9,6 +9,7 @@ export default async function ReactionRoleRemoveHandler(reaction: MessageReactio
const channel = reaction.message.channel;
if (guild === null || emoji === null) return YALAS.error("failed to find guild");
if (emoji=='❌') return;
async function getRole(roleId: string) {
@ -46,13 +47,14 @@ export default async function ReactionRoleRemoveHandler(reaction: MessageReactio
if (rRole.dataValues.reactionRemove === false) continue;
//TODO: implement audit log
// Add the role to the member
try {
await member.roles.remove(role);
if (process.env.DEBUG === 'true') YALAS.info(`Added role "${role.name}" to user "${user.tag}".`);
} catch (error) {
reaction.users.remove(user.id);
const errUsrMsg = await (channel as TextChannel).send(`Sorry <@!${user.id}>, that did not work please try again later or ask a moderator.`);
setTimeout(() => {

View file

@ -1,5 +1,6 @@
import { Client, MessageReaction, PartialMessageReaction, User, PartialUser } from "discord.js";
import ReactionRoleAddHandler from "../functions/ReactionRoleAddHandler";
import BanReactionHandler from "../functions/BanReactionHandler";
import * as YALAS from 'mcstatusbot-logger';
export default async function MessageReactionAdd(client: Client, reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser) {
@ -13,7 +14,7 @@ export default async function MessageReactionAdd(client: Client, reaction: Messa
}
}
if (!reaction.message.guild) return;
if (user.partial) {
try {
@ -23,12 +24,12 @@ export default async function MessageReactionAdd(client: Client, reaction: Messa
return;
}
}
if (user.id===client.user?.id) return;
if (user.id === client.user?.id) return;
const memberBanned = await BanReactionHandler(reaction, user);
//non need to try and run reactions roles if member banned
if (memberBanned === true) return;
await ReactionRoleAddHandler(reaction, user);
//TODO: ban role hndler
//TODO: kick role handler
return;
}

View file

@ -23,7 +23,7 @@ const sequelize = new Sequelize(process.env.DB_URI, {
});
const schemas = {
BanRole: BanReaction(sequelize),
BanReaction: BanReaction(sequelize),
Guild: Guild(sequelize),
KickRole: KickReaction(sequelize),
MessageMacro: MessageMacro(sequelize),
@ -34,7 +34,6 @@ const schemas = {
export async function connect() {
YALAS.info("attempting to connect to db");
try {
await sequelize.authenticate();

View file

@ -4,7 +4,6 @@ export interface BanReactionAttributes {
id: number;
guild: string;
channel: string;
reaction: string;
}
export interface BanReactionCreationAttributes extends Optional<BanReactionAttributes, 'id'> { }
@ -28,11 +27,7 @@ export default function BanRole(sequelize: Sequelize) {
channel: {
type: DataTypes.STRING,
allowNull: false,
},
reaction: {
type: DataTypes.STRING,
allowNull: false,
},
}
});
// Sync the model with the database