261 lines
9.2 KiB
TypeScript
261 lines
9.2 KiB
TypeScript
import { User } from "revolt.js";
|
|
import { SendableEmbed } from "revolt-api";
|
|
import { ulid } from "ulid";
|
|
import { client } from "../../../";
|
|
import Infraction from "automod/dist/types/antispam/Infraction";
|
|
import InfractionType from "automod/dist/types/antispam/InfractionType";
|
|
import CommandCategory from "../../../struct/commands/CommandCategory";
|
|
import SimpleCommand from "../../../struct/commands/SimpleCommand";
|
|
import logger from "../../logger";
|
|
import { fetchUsername, logModAction } from "../../modules/mod_logs";
|
|
import {
|
|
dedupeArray,
|
|
embed,
|
|
EmbedColor,
|
|
generateInfractionDMEmbed,
|
|
getDmChannel,
|
|
getMembers,
|
|
isModerator,
|
|
NO_MANAGER_MSG,
|
|
parseUser,
|
|
parseUserOrId,
|
|
sanitizeMessageContent,
|
|
storeInfraction,
|
|
yesNoMessage,
|
|
} from "../../util";
|
|
|
|
export default {
|
|
name: "kick",
|
|
aliases: ["yeet", "vent"],
|
|
description: "Kick a member from the server",
|
|
syntax: "/kick @username [reason?]",
|
|
removeEmptyArgs: true,
|
|
category: CommandCategory.Moderation,
|
|
run: async (message, args, serverConfig) => {
|
|
if (!(await isModerator(message))) return message.reply(NO_MANAGER_MSG);
|
|
if (!message.serverContext.havePermission("KickMembers")) {
|
|
return await message.reply(
|
|
`Sorry, I do not have \`KickMembers\` permission.`
|
|
);
|
|
}
|
|
|
|
const userInput = !message.replyIds?.length
|
|
? args.shift() || ""
|
|
: undefined;
|
|
if (!userInput && !message.replyIds?.length)
|
|
return message.reply({
|
|
embeds: [
|
|
embed(
|
|
`Please specify one or more users by replying to their message while running this command or ` +
|
|
`by specifying a comma-separated list of usernames.`,
|
|
"No target user specified",
|
|
EmbedColor.SoftError
|
|
),
|
|
],
|
|
});
|
|
|
|
let reason = args
|
|
.join(" ")
|
|
?.replace(new RegExp("`", "g"), "'")
|
|
?.replace(new RegExp("\n", "g"), " ");
|
|
|
|
if (reason.length > 500)
|
|
return message.reply({
|
|
embeds: [
|
|
embed(
|
|
"Kick reason may not be longer than 500 characters.",
|
|
null,
|
|
EmbedColor.SoftError
|
|
),
|
|
],
|
|
});
|
|
|
|
const embeds: SendableEmbed[] = [];
|
|
const handledUsers: string[] = [];
|
|
const targetUsers: User | { id: string }[] = [];
|
|
|
|
const targetInput = dedupeArray(
|
|
message.replyIds?.length
|
|
? (
|
|
await Promise.allSettled(
|
|
message.replyIds.map((msg) =>
|
|
message.channel?.fetchMessage(msg)
|
|
)
|
|
)
|
|
)
|
|
.filter((m) => m.status == "fulfilled")
|
|
.map((m) => (m as any).value.authorId)
|
|
: userInput!.split(",")
|
|
);
|
|
|
|
for (const userStr of targetInput) {
|
|
try {
|
|
let user = await parseUserOrId(userStr);
|
|
if (!user) {
|
|
embeds.push(
|
|
embed(
|
|
`I can't resolve \`${sanitizeMessageContent(
|
|
userStr
|
|
).trim()}\` to a user.`,
|
|
null,
|
|
EmbedColor.SoftError
|
|
)
|
|
);
|
|
continue;
|
|
}
|
|
|
|
// Silently ignore duplicates
|
|
if (handledUsers.includes(user.id)) continue;
|
|
handledUsers.push(user.id);
|
|
|
|
if (user.id == message.authorId) {
|
|
embeds.push(
|
|
embed(
|
|
"You might want to avoid kicking yourself...",
|
|
null,
|
|
EmbedColor.Warning
|
|
)
|
|
);
|
|
continue;
|
|
}
|
|
|
|
if (user.id == client.user!.id) {
|
|
embeds.push(
|
|
embed(
|
|
"I won't allow you to get rid of me this easily :trol:",
|
|
null,
|
|
EmbedColor.Warning
|
|
)
|
|
);
|
|
continue;
|
|
}
|
|
|
|
targetUsers.push(user);
|
|
} catch (e) {
|
|
console.error(e);
|
|
embeds.push(
|
|
embed(
|
|
`Failed to kick target \`${sanitizeMessageContent(
|
|
userStr
|
|
).trim()}\`: ${e}`,
|
|
`Failed to kick: An error has occurred`,
|
|
EmbedColor.Error
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (message.replyIds?.length && targetUsers.length) {
|
|
let res = await yesNoMessage(
|
|
message.channel!,
|
|
message.authorId!,
|
|
`This will kick the author${targetUsers.length > 1 ? 's' : ''} `
|
|
+ `of the message${message.replyIds.length > 1 ? 's' : ''} you replied to.\n`
|
|
+ `The following user${targetUsers.length > 1 ? 's' : ''} will be affected: `
|
|
+ `${targetUsers.map(u => `<@${u.id}>`).join(', ')}.\n`
|
|
+ `Are you sure?`,
|
|
'Confirm action'
|
|
);
|
|
if (!res) return;
|
|
}
|
|
|
|
const members = getMembers(message.serverContext.id);
|
|
|
|
for (const user of targetUsers) {
|
|
try {
|
|
const member = members.find((m) => m.id.user == user.id)
|
|
|| await message.serverContext.fetchMember(user.id);
|
|
|
|
let infId = ulid();
|
|
let infraction: Infraction = {
|
|
_id: infId,
|
|
createdBy: message.authorId!,
|
|
date: Date.now(),
|
|
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
|
|
),
|
|
member.kick(),
|
|
]);
|
|
|
|
embeds.push({
|
|
title: `User kicked`,
|
|
icon_url:
|
|
user instanceof User
|
|
? user.avatarURL
|
|
: undefined,
|
|
colour: EmbedColor.Success,
|
|
description:
|
|
`This is ${
|
|
userWarnCount == 1
|
|
? "**the first infraction**"
|
|
: `infraction number **${userWarnCount}**`
|
|
}` +
|
|
` for ${await fetchUsername(user.id)}.\n` +
|
|
`**User ID:** \`${user.id}\`\n` +
|
|
`**Infraction ID:** \`${infraction._id}\`\n` +
|
|
`**Reason:** \`${infraction.reason}\``,
|
|
});
|
|
} catch (e) {
|
|
embeds.push(
|
|
embed(
|
|
`Failed to kick user ${await fetchUsername(
|
|
user.id
|
|
)}: ${e}`,
|
|
"Failed to kick user",
|
|
EmbedColor.Error
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
let firstMsg = true;
|
|
while (embeds.length > 0) {
|
|
const targetEmbeds = embeds.splice(0, 10);
|
|
|
|
if (firstMsg) {
|
|
await message.reply(
|
|
{ embeds: targetEmbeds, content: "Operation completed." },
|
|
false
|
|
);
|
|
} else {
|
|
await message.channel?.sendMessage({ embeds: targetEmbeds });
|
|
}
|
|
firstMsg = false;
|
|
}
|
|
},
|
|
} as SimpleCommand;
|