Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion locales
6 changes: 5 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ type hubLogReports {
roleId String?
}

type userBan {
reason String
}

type hubBlacklist {
reason String
expires DateTime?
Expand Down Expand Up @@ -161,7 +165,7 @@ model userData {
username String?
locale String?
lastVoted DateTime?
banned Boolean? @default(false)
banMeta userBan?
blacklistedFrom hubBlacklist[]
// if user has seen the welcome message when they first use the network
viewedNetworkWelcome Boolean @default(false)
Expand Down
30 changes: 25 additions & 5 deletions src/commands/slash/Staff/ban.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import BaseCommand from '../../../core/BaseCommand.js';
import db from '../../../utils/Db.js';
import { simpleEmbed } from '../../../utils/Utils.js';
import { emojis } from '../../../utils/Constants.js';
import { DeveloperIds, emojis } from '../../../utils/Constants.js';

export default class Ban extends BaseCommand {
readonly staffOnly = true;
Expand All @@ -20,12 +20,30 @@ export default class Ban extends BaseCommand {
description: '🔨 The user to ban.',
required: true,
},
{
type: ApplicationCommandOptionType.String,
name: 'reason',
description: 'Reson for the ban',
required: true,
},
],
};
override async execute(interaction: ChatInputCommandInteraction): Promise<unknown> {
if (!DeveloperIds.includes(interaction.user.id)) return;

const user = interaction.options.getUser('user', true);
const reason = interaction.options.getString('reason', true);

if (user.id === interaction.user.id) {
await interaction.reply({
content: `Let's not go there. ${emojis.bruhcat}`,
ephemeral: true,
});
return;
}

const alreadyBanned = await db.userData.findFirst({
where: { userId: user.id, banned: true },
where: { userId: user.id, banMeta: { isNot: null } },
});

if (alreadyBanned) {
Expand All @@ -42,16 +60,18 @@ export default class Ban extends BaseCommand {
username: user.username,
viewedNetworkWelcome: false,
voteCount: 0,
banned: true,
banMeta: { reason },
},
update: {
banned: true,
banMeta: { reason },
},
});

await interaction.reply({
embeds: [
simpleEmbed(`${emojis.tick} Successfully banned **${user.username}**. They can no longer use the bot.`),
simpleEmbed(
`${emojis.tick} Successfully banned \`${user.username}\`. They can no longer use the bot.`,
),
],
});
}
Expand Down
5 changes: 4 additions & 1 deletion src/commands/slash/Staff/respawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ export default class Respawn extends BaseCommand {
return;
}

await interaction.reply(`${emojis.tick} Request to respawn shards received. I'll be back!`);
await interaction.reply({
content: `${emojis.tick} Request to respawn shards received. I'll be back!`,
ephemeral: true,
});

interaction.client.cluster.respawnAll();
}
Expand Down
10 changes: 6 additions & 4 deletions src/commands/slash/Staff/unban.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default class Unban extends BaseCommand {
override async execute(interaction: ChatInputCommandInteraction): Promise<unknown> {
const user = interaction.options.getUser('user', true);
const alreadyBanned = await db.userData.findFirst({
where: { userId: user.id, banned: true },
where: { userId: user.id, banMeta: { isSet: true } },
});

if (!alreadyBanned) {
Expand All @@ -42,16 +42,18 @@ export default class Unban extends BaseCommand {
username: user.username,
viewedNetworkWelcome: false,
voteCount: 0,
banned: false,
banMeta: { set: null },
},
update: {
banned: false,
banMeta: { set: null },
},
});

await interaction.reply({
embeds: [
simpleEmbed(`${emojis.tick} Successfully banned **${user.username}**. They can no longer use the bot.`),
simpleEmbed(
`${emojis.tick} Successfully unbanned \`${user.username}\`. They can use the bot again.`,
),
],
});
}
Expand Down
35 changes: 32 additions & 3 deletions src/managers/EventManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ import {
Interaction,
Client,
} from 'discord.js';
import {
checkIfStaff,
getAttachmentURL,
getUserLocale,
handleError,
simpleEmbed,
wait,
} from '../utils/Utils.js';
import db from '../utils/Db.js';
import Logger from '../utils/Logger.js';
import SuperClient from '../core/Client.js';
Expand All @@ -29,7 +37,6 @@ import { logGuildJoin, logGuildLeave } from '../scripts/guilds/goals.js';
import { channels, emojis, colors, LINKS } from '../utils/Constants.js';
import { getReferredMsgData, sendWelcomeMsg } from '../scripts/network/helpers.js';
import { HubSettingsBitField } from '../utils/BitFields.js';
import { getAttachmentURL, getUserLocale, handleError, simpleEmbed, wait } from '../utils/Utils.js';
import { addReaction, updateReactions } from '../scripts/reaction/actions.js';
import { checkBlacklists } from '../scripts/reaction/helpers.js';
import { CustomID } from '../utils/CustomID.js';
Expand Down Expand Up @@ -314,6 +321,23 @@ export default abstract class EventManager {
const userData = await db.userData.findFirst({ where: { userId: interaction.user.id } });
interaction.user.locale = getUserLocale(userData);

if (userData?.banMeta?.reason) {
if (interaction.isRepliable()) {
await interaction.reply({
content: t(
{ phrase: 'errors.banned', locale: interaction.user.locale },
{
emoji: emojis.no,
reason: userData.banMeta.reason,
support_invite: LINKS.SUPPORT_INVITE,
},
),
ephemeral: true,
});
}
return;
}

if (interaction.isMessageComponent() || interaction.isModalSubmit()) {
const ignoreList = ['page_', 'onboarding_'];
const customId = CustomID.parseCustomId(interaction.customId);
Expand Down Expand Up @@ -346,8 +370,13 @@ export default abstract class EventManager {
}

const command = commands.get(interaction.commandName);
if (!interaction.isAutocomplete()) await command?.execute(interaction); // slash commands
else if (command?.autocomplete) await command.autocomplete(interaction); // autocomplete

if (command?.staffOnly && !checkIfStaff(interaction.user.id)) return;

// slash commands
if (!interaction.isAutocomplete()) await command?.execute(interaction);
// autocompletes
else if (command?.autocomplete) await command.autocomplete(interaction);
}
catch (e) {
handleError(e, interaction);
Expand Down
5 changes: 2 additions & 3 deletions src/scripts/network/runChecks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,13 @@ export const runChecks = async (
const { locale } = message.author;
const { hasProfanity, hasSlurs } = checkProfanity(message.content);
const { settings, userData, attachmentURL } = opts;
const isUserBlacklisted = userData.blacklistedFrom.some((b) => b.hubId === hubId);

if (await isCaughtSpam(message, settings, hubId)) return false;
if (containsLinks(message, settings)) message.content = replaceLinks(message.content);

// banned / blacklisted
if (userData.banned || userData.blacklistedFrom.some((b) => b.hubId === hubId)) {
return false;
}
if (userData.banMeta?.reason || isUserBlacklisted) return false;

// send a log to the log channel set by the hub
if (hasProfanity || hasSlurs) {
Expand Down