option to DM users on kick/ban/warn
This commit is contained in:
parent
7cee4638a9
commit
5546ec3c59
8 changed files with 140 additions and 22 deletions
|
@ -3,15 +3,15 @@ import { client } from "../../../index";
|
|||
import Infraction from "../../../struct/antispam/Infraction";
|
||||
import InfractionType from "../../../struct/antispam/InfractionType";
|
||||
import SimpleCommand from "../../../struct/commands/SimpleCommand";
|
||||
import MessageCommandContext from "../../../struct/MessageCommandContext";
|
||||
import { fetchUsername, logModAction } from "../../modules/mod_logs";
|
||||
import { storeTempBan } from "../../modules/tempbans";
|
||||
import { dedupeArray, embed, EmbedColor, isModerator, NO_MANAGER_MSG, parseUserOrId, sanitizeMessageContent, storeInfraction } from "../../util";
|
||||
import { dedupeArray, embed, EmbedColor, generateInfractionDMEmbed, getDmChannel, isModerator, NO_MANAGER_MSG, parseUserOrId, sanitizeMessageContent, storeInfraction } from "../../util";
|
||||
import Day from 'dayjs';
|
||||
import RelativeTime from 'dayjs/plugin/relativeTime';
|
||||
import CommandCategory from "../../../struct/commands/CommandCategory";
|
||||
import { SendableEmbed } from "@janderedev/revolt.js/node_modules/revolt-api";
|
||||
import { User } from "@janderedev/revolt.js";
|
||||
import logger from "../../logger";
|
||||
|
||||
Day.extend(RelativeTime);
|
||||
|
||||
|
@ -22,7 +22,7 @@ export default {
|
|||
syntax: '/ban @username [10m|1h|...?] [reason?]',
|
||||
removeEmptyArgs: true,
|
||||
category: CommandCategory.Moderation,
|
||||
run: async (message: MessageCommandContext, args: string[]) => {
|
||||
run: async (message, args, serverConfig) => {
|
||||
if (!await isModerator(message))
|
||||
return message.reply(NO_MANAGER_MSG);
|
||||
if (!message.serverContext.havePermission('BanMembers')) {
|
||||
|
@ -134,6 +134,7 @@ export default {
|
|||
type: InfractionType.Manual,
|
||||
user: user._id,
|
||||
actionType: 'ban',
|
||||
expires: Infinity,
|
||||
}
|
||||
const { userWarnCount } = await storeInfraction(infraction);
|
||||
|
||||
|
@ -157,6 +158,20 @@ export default {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (serverConfig?.dmOnKick) {
|
||||
try {
|
||||
const embed = generateInfractionDMEmbed(message.serverContext, serverConfig, infraction, message);
|
||||
const dmChannel = await getDmChannel(user);
|
||||
|
||||
if (dmChannel.havePermission('SendMessage') || dmChannel.havePermission('SendEmbeds')) {
|
||||
await dmChannel.sendMessage({ embeds: [ embed ] });
|
||||
}
|
||||
else logger.warn('Missing permission to DM user.');
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
await message.serverContext.banUser(user._id, {
|
||||
reason: reason + ` (by ${await fetchUsername(message.author_id)} ${message.author_id})`
|
||||
});
|
||||
|
@ -186,9 +201,24 @@ export default {
|
|||
type: InfractionType.Manual,
|
||||
user: user._id,
|
||||
actionType: 'ban',
|
||||
expires: banUntil,
|
||||
}
|
||||
const { userWarnCount } = await storeInfraction(infraction);
|
||||
|
||||
if (serverConfig?.dmOnKick) {
|
||||
try {
|
||||
const embed = generateInfractionDMEmbed(message.serverContext, serverConfig, infraction, message);
|
||||
const dmChannel = await getDmChannel(user);
|
||||
|
||||
if (dmChannel.havePermission('SendMessage') || dmChannel.havePermission('SendEmbeds')) {
|
||||
await dmChannel.sendMessage({ embeds: [ embed ] });
|
||||
}
|
||||
else logger.warn('Missing permission to DM user.');
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
await message.serverContext.banUser(user._id, {
|
||||
reason: reason + ` (by ${await fetchUsername(message.author_id)} ${message.author_id}) (${durationStr})`
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { User } from "@janderedev/revolt.js";
|
||||
import { Member } from "@janderedev/revolt.js/dist/maps/Members";
|
||||
import { SendableEmbed } from "revolt-api";
|
||||
import { ulid } from "ulid";
|
||||
import { client } from "../../../";
|
||||
|
@ -7,9 +6,9 @@ import Infraction from "../../../struct/antispam/Infraction";
|
|||
import InfractionType from "../../../struct/antispam/InfractionType";
|
||||
import CommandCategory from "../../../struct/commands/CommandCategory";
|
||||
import SimpleCommand from "../../../struct/commands/SimpleCommand";
|
||||
import MessageCommandContext from "../../../struct/MessageCommandContext";
|
||||
import logger from "../../logger";
|
||||
import { fetchUsername, logModAction } from "../../modules/mod_logs";
|
||||
import { dedupeArray, embed, EmbedColor, isModerator, NO_MANAGER_MSG, parseUser, parseUserOrId, sanitizeMessageContent, storeInfraction } from "../../util";
|
||||
import { dedupeArray, embed, EmbedColor, generateInfractionDMEmbed, getDmChannel, isModerator, NO_MANAGER_MSG, parseUser, parseUserOrId, sanitizeMessageContent, storeInfraction } from "../../util";
|
||||
|
||||
export default {
|
||||
name: 'kick',
|
||||
|
@ -18,7 +17,7 @@ export default {
|
|||
syntax: '/kick @username [reason?]',
|
||||
removeEmptyArgs: true,
|
||||
category: CommandCategory.Moderation,
|
||||
run: async (message: MessageCommandContext, args: string[]) => {
|
||||
run: async (message, args, serverConfig) => {
|
||||
if (!await isModerator(message))
|
||||
return message.reply(NO_MANAGER_MSG);
|
||||
if (!message.serverContext.havePermission('KickMembers')) {
|
||||
|
@ -104,13 +103,27 @@ export default {
|
|||
_id: infId,
|
||||
createdBy: message.author_id,
|
||||
date: Date.now(),
|
||||
reason: reason,
|
||||
reason: reason || 'No reason provided',
|
||||
server: message.serverContext._id,
|
||||
type: InfractionType.Manual,
|
||||
user: user._id,
|
||||
actionType: 'kick',
|
||||
}
|
||||
|
||||
if (serverConfig?.dmOnKick) {
|
||||
try {
|
||||
const embed = generateInfractionDMEmbed(message.serverContext, serverConfig, infraction, message);
|
||||
const dmChannel = await getDmChannel(user);
|
||||
|
||||
if (dmChannel.havePermission('SendMessage') || dmChannel.havePermission('SendEmbeds')) {
|
||||
await dmChannel.sendMessage({ embeds: [ embed ] });
|
||||
}
|
||||
else logger.warn('Missing permission to DM user.');
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
let [ { userWarnCount } ] = await Promise.all([
|
||||
storeInfraction(infraction),
|
||||
logModAction('kick', message.serverContext, message.member!, user._id, reason, infraction._id),
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import SimpleCommand from "../../../struct/commands/SimpleCommand";
|
||||
import { dedupeArray, embed, EmbedColor, isModerator, NO_MANAGER_MSG, parseUserOrId, sanitizeMessageContent, storeInfraction } from "../../util";
|
||||
import { dedupeArray, embed, EmbedColor, generateInfractionDMEmbed, getDmChannel, isModerator, NO_MANAGER_MSG, parseUserOrId, sanitizeMessageContent, storeInfraction } from "../../util";
|
||||
import Infraction from "../../../struct/antispam/Infraction";
|
||||
import { ulid } from "ulid";
|
||||
import InfractionType from "../../../struct/antispam/InfractionType";
|
||||
import { fetchUsername, logModAction } from "../../modules/mod_logs";
|
||||
import MessageCommandContext from "../../../struct/MessageCommandContext";
|
||||
import CommandCategory from "../../../struct/commands/CommandCategory";
|
||||
import { SendableEmbed } from "revolt-api";
|
||||
import { User } from "@janderedev/revolt.js";
|
||||
import logger from "../../logger";
|
||||
|
||||
export default {
|
||||
name: 'warn',
|
||||
|
@ -15,7 +15,7 @@ export default {
|
|||
removeEmptyArgs: false,
|
||||
description: 'add an infraction to an user\'s record',
|
||||
category: CommandCategory.Moderation,
|
||||
run: async (message: MessageCommandContext, args: string[]) => {
|
||||
run: async (message, args, serverConfig) => {
|
||||
if (!await isModerator(message)) return message.reply(NO_MANAGER_MSG);
|
||||
|
||||
const userInput = args.shift() || '';
|
||||
|
@ -94,15 +94,32 @@ export default {
|
|||
} as Infraction;
|
||||
|
||||
let { userWarnCount } = await storeInfraction(infraction);
|
||||
await logModAction(
|
||||
'warn',
|
||||
message.serverContext,
|
||||
message.member!,
|
||||
user._id,
|
||||
reason || 'No reason provided',
|
||||
infraction._id,
|
||||
`This is warn number ${userWarnCount} for this user.`
|
||||
);
|
||||
await Promise.all([
|
||||
logModAction(
|
||||
'warn',
|
||||
message.serverContext,
|
||||
message.member!,
|
||||
user._id,
|
||||
reason || 'No reason provided',
|
||||
infraction._id,
|
||||
`This is warn number ${userWarnCount} for this user.`
|
||||
),
|
||||
(async () => {
|
||||
if (serverConfig?.dmOnWarn) {
|
||||
try {
|
||||
const embed = generateInfractionDMEmbed(message.serverContext, serverConfig, infraction, message);
|
||||
const dmChannel = await getDmChannel(user);
|
||||
|
||||
if (dmChannel.havePermission('SendMessage') || dmChannel.havePermission('SendEmbeds')) {
|
||||
await dmChannel.sendMessage({ embeds: [ embed ] });
|
||||
}
|
||||
else logger.warn('Missing permission to DM user.');
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
})(),
|
||||
]);
|
||||
|
||||
embeds.push({
|
||||
title: `User warned`,
|
||||
|
|
|
@ -136,7 +136,7 @@ let commands: SimpleCommand[];
|
|||
}
|
||||
|
||||
try {
|
||||
await cmd.run(message, args);
|
||||
await cmd.run(message, args, config);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
message.reply(`### An error has occurred:\n\`\`\`js\n${e}\n\`\`\``);
|
||||
|
|
|
@ -15,11 +15,16 @@ import { Permission } from "@janderedev/revolt.js/dist/permissions/definitions";
|
|||
import { Message } from "@janderedev/revolt.js/dist/maps/Messages";
|
||||
import { isSudo } from "./commands/admin/botadm";
|
||||
import { SendableEmbed } from "revolt-api";
|
||||
import MessageCommandContext from "../struct/MessageCommandContext";
|
||||
import ServerConfig from "../struct/ServerConfig";
|
||||
|
||||
const NO_MANAGER_MSG = '🔒 Missing permission';
|
||||
const ULID_REGEX = /^[0-9A-HJ-KM-NP-TV-Z]{26}$/i;
|
||||
const USER_MENTION_REGEX = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i;
|
||||
const CHANNEL_MENTION_REGEX = /^<#[0-9A-HJ-KM-NP-TV-Z]{26}>$/i;
|
||||
const RE_HTTP_URI = /^http(s?):\/\//g;
|
||||
const RE_MAILTO_URI = /^mailto:/g;
|
||||
|
||||
let autumn_url: string|null = null;
|
||||
let apiConfig: any = axios.get(client.apiURL).then(res => {
|
||||
autumn_url = (res.data as any).features.autumn.url;
|
||||
|
@ -347,6 +352,52 @@ const awaitClient = () => new Promise<void>(async resolve => {
|
|||
else resolve();
|
||||
});
|
||||
|
||||
const getDmChannel = async (user: string|{_id: string}|User) => {
|
||||
if (typeof user == 'string') user = client.users.get(user) || await client.users.fetch(user);
|
||||
if (!(user instanceof User)) user = client.users.get(user._id) || await client.users.fetch(user._id);
|
||||
|
||||
return Array.from(client.channels).find(
|
||||
c => c[1].channel_type == 'DirectMessage' && c[1].recipient?._id == (user as User)._id
|
||||
)?.[1] || await (user as User).openDM();
|
||||
}
|
||||
|
||||
const generateInfractionDMEmbed = (server: Server, serverConfig: ServerConfig, infraction: Infraction, message: MessageCommandContext) => {
|
||||
const embed: SendableEmbed = {
|
||||
title: message.serverContext.name,
|
||||
icon_url: message.serverContext.generateIconURL({ max_side: 128 }),
|
||||
colour: '#ff9e2f',
|
||||
url: message.url,
|
||||
description: 'You have been ' +
|
||||
(infraction.actionType
|
||||
? `**${infraction.actionType == 'ban' ? 'banned' : 'kicked'}** from `
|
||||
: `**warned** in `) +
|
||||
`'${sanitizeMessageContent(message.serverContext.name).trim()}' <t:${Math.round(infraction.date / 1000)}:R>.\n` +
|
||||
`**Reason:** ${infraction.reason}\n` +
|
||||
`**Moderator:** [@${sanitizeMessageContent(message.author?.username || 'Unknown')}](/@${message.author_id})\n` +
|
||||
`**Infraction ID:** \`${infraction._id}\`` +
|
||||
(infraction.actionType == 'ban' && infraction.expires
|
||||
? (infraction.expires == Infinity
|
||||
? '\n**Ban duration:** Permanent'
|
||||
: `\n**Ban expires** <t:${Math.round(infraction.expires / 1000)}:R>`)
|
||||
: '')
|
||||
}
|
||||
|
||||
if (serverConfig.contact) {
|
||||
if (RE_MAILTO_URI.test(serverConfig.contact)) {
|
||||
embed.description += `\n\nIf you wish to appeal this decision, you may contact the server's moderation team at ` +
|
||||
`[${serverConfig.contact.replace(RE_MAILTO_URI, '')}](${serverConfig.contact}).`
|
||||
}
|
||||
else if (RE_HTTP_URI.test(serverConfig.contact)) {
|
||||
embed.description += `\n\nIf you wish to appeal this decision, you may do so [here](${serverConfig.contact}).`
|
||||
}
|
||||
else {
|
||||
embed.description += `\n\n${serverConfig.contact}`;
|
||||
}
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
export {
|
||||
getAutumnURL,
|
||||
hasPerm,
|
||||
|
@ -366,6 +417,8 @@ export {
|
|||
dedupeArray,
|
||||
awaitClient,
|
||||
getMutualServers,
|
||||
getDmChannel,
|
||||
generateInfractionDMEmbed,
|
||||
EmbedColor,
|
||||
NO_MANAGER_MSG,
|
||||
ULID_REGEX,
|
||||
|
|
|
@ -25,6 +25,9 @@ class ServerConfig {
|
|||
modAction?: LogConfig, // User warned, kicked or banned
|
||||
};
|
||||
allowBlacklistedUsers?: boolean; // Whether the server explicitly allows users that are globally blacklisted
|
||||
dmOnKick?: boolean; // Whether users should receive a DM when kicked/banned. Default false
|
||||
dmOnWarn?: boolean; // Whether users should receive a DM when warned. Default false
|
||||
contact?: string; // How to contact the server staff. Sent on kick/ban/warn DMs. http(s)/mailto link or normal text.
|
||||
}
|
||||
|
||||
export default ServerConfig;
|
||||
|
|
|
@ -12,6 +12,7 @@ class Infraction {
|
|||
targetMessages?: string[];
|
||||
reason: string;
|
||||
date: number;
|
||||
expires?: number; // Only applies to bans
|
||||
}
|
||||
|
||||
export default Infraction;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import CommandCategory from "./CommandCategory";
|
||||
import MessageCommandContext from "../MessageCommandContext";
|
||||
import ServerConfig from "../ServerConfig";
|
||||
|
||||
/**
|
||||
* A basic command, consisting of basic attributes
|
||||
|
@ -26,7 +27,7 @@ class SimpleCommand {
|
|||
removeEmptyArgs?: boolean | null;
|
||||
|
||||
// This is executed whenever the command is ran.
|
||||
run: (message: MessageCommandContext, args: string[]) => Promise<any>;
|
||||
run: (message: MessageCommandContext, args: string[], serverConfig?: ServerConfig|null) => Promise<any>;
|
||||
|
||||
// The category the command belongs to, used for /help.
|
||||
category: CommandCategory;
|
||||
|
|
Loading…
Reference in a new issue