mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-07-07 19:17:19 +00:00
Fixes, refactoring and PR feedback
This commit is contained in:
parent
0be54912c4
commit
893a77d562
202 changed files with 1037 additions and 1069 deletions
|
@ -2,8 +2,7 @@ import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
|||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualAddCaseCmd } from "../../functions/actualCommands/actualAddCaseCmd";
|
||||
import { actualAddCaseCmd } from "./actualAddCaseCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
const opts = {
|
||||
|
@ -28,7 +27,7 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -36,7 +35,7 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
|
|||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
|
|||
// Verify the case type is valid
|
||||
const type: string = args.type[0].toUpperCase() + args.type.slice(1).toLowerCase();
|
||||
if (!CaseTypes[type]) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot add case: invalid case type");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot add case: invalid case type");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ import { CaseTypes } from "../../../../data/CaseTypes";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualAddCaseCmd } from "../../functions/actualCommands/actualAddCaseCmd";
|
||||
import { actualAddCaseCmd } from "./actualAddCaseCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -49,9 +48,10 @@ export const AddCaseSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { Case } from "../../../../data/entities/Case";
|
||||
import { canActOn } from "../../../../pluginUtils";
|
||||
import { UnknownUser, renderUsername, resolveMember } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
|
||||
export async function actualAddCaseCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
author: GuildMember,
|
||||
mod: GuildMember,
|
||||
attachments: Array<Attachment>,
|
||||
user: User | UnknownUser,
|
||||
type: keyof CaseTypes,
|
||||
reason: string,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user exists as a guild member, make sure we can act on them first
|
||||
const member = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
if (member && !canActOn(pluginData, author, member)) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"Cannot add case on this user: insufficient permissions"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
|
||||
// Create the case
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const theCase: Case = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: mod.id,
|
||||
type: CaseTypes[type],
|
||||
reason: formattedReason,
|
||||
ppId: mod.id !== author.id ? author.id : undefined,
|
||||
});
|
||||
|
||||
if (user) {
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Case #${theCase.case_number} created for **${renderUsername(user)}**`
|
||||
);
|
||||
} else {
|
||||
pluginData.state.common.sendSuccessMessage(context, `Case #${theCase.case_number} created`);
|
||||
}
|
||||
|
||||
// Log the action
|
||||
pluginData.getPlugin(LogsPlugin).logCaseCreate({
|
||||
mod: mod.user,
|
||||
userId: user.id,
|
||||
caseNum: theCase.case_number,
|
||||
caseType: type.toUpperCase(),
|
||||
reason: formattedReason,
|
||||
});
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { UserNotificationMethod, resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualBanCmd } from "../../functions/actualCommands/actualBanCmd";
|
||||
import { actualBanCmd } from "./actualBanCmd";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
|
@ -38,7 +37,7 @@ export const BanMsgCmd = modActionsMsgCmd({
|
|||
const user = await resolveUser(pluginData.client, args.user);
|
||||
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ export const BanMsgCmd = modActionsMsgCmd({
|
|||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,7 +56,7 @@ export const BanMsgCmd = modActionsMsgCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(args) ?? undefined;
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
|
||||
pluginData.state.common.sendErrorMessage(msg, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { UserNotificationMethod, convertDelayStringToMS, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualBanCmd } from "../../functions/actualCommands/actualBanCmd";
|
||||
import { actualBanCmd } from "./actualBanCmd";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
@ -52,9 +51,13 @@ export const BanSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -67,9 +70,10 @@ export const BanSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -80,13 +84,13 @@ export const BanSlashCmd = modActionsSlashCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
|
||||
pluginData.state.common.sendErrorMessage(interaction, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
|
||||
if (options.time && !convertedTime) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
pluginData.state.common.sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
190
backend/src/plugins/ModActions/commands/ban/actualBanCmd.ts
Normal file
190
backend/src/plugins/ModActions/commands/ban/actualBanCmd.ts
Normal file
|
@ -0,0 +1,190 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { getMemberLevel } from "knub/helpers";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { clearExpiringTempban, registerExpiringTempban } from "../../../../data/loops/expiringTempbansLoop";
|
||||
import { canActOn, getContextChannel } from "../../../../pluginUtils";
|
||||
import { UnknownUser, UserNotificationMethod, renderUsername, resolveMember } from "../../../../utils";
|
||||
import { banLock } from "../../../../utils/lockNameHelpers";
|
||||
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { banUserId } from "../../functions/banUserId";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
|
||||
export async function actualBanCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
user: User | UnknownUser,
|
||||
time: number | null,
|
||||
reason: string,
|
||||
attachments: Attachment[],
|
||||
author: GuildMember,
|
||||
mod: GuildMember,
|
||||
contactMethods?: UserNotificationMethod[],
|
||||
deleteDays?: number,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
const formattedReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
|
||||
|
||||
// acquire a lock because of the needed user-inputs below (if banned/not on server)
|
||||
const lock = await pluginData.locks.acquire(banLock(user));
|
||||
let forceban = false;
|
||||
const existingTempban = await pluginData.state.tempbans.findExistingTempbanForUserId(user.id);
|
||||
|
||||
if (!memberToBan) {
|
||||
const banned = await isBanned(pluginData, user.id);
|
||||
|
||||
if (!banned) {
|
||||
// Ask the mod if we should upgrade to a forceban as the user is not on the server
|
||||
const reply = await waitForButtonConfirm(
|
||||
context,
|
||||
{ content: "User not on server, forceban instead?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: author.id },
|
||||
);
|
||||
|
||||
if (!reply) {
|
||||
pluginData.state.common.sendErrorMessage(context, "User not on server, ban cancelled by moderator");
|
||||
lock.unlock();
|
||||
return;
|
||||
} else {
|
||||
forceban = true;
|
||||
}
|
||||
} else {
|
||||
// Abort if trying to ban user indefinitely if they are already banned indefinitely
|
||||
if (!existingTempban && !time) {
|
||||
pluginData.state.common.sendErrorMessage(context, `User is already banned indefinitely.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask the mod if we should update the existing ban
|
||||
const reply = await waitForButtonConfirm(
|
||||
context,
|
||||
{ content: "Failed to message the user. Log the warning anyway?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: author.id },
|
||||
);
|
||||
|
||||
if (!reply) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"User already banned, update cancelled by moderator"
|
||||
);
|
||||
lock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update or add new tempban / remove old tempban
|
||||
if (time && time > 0) {
|
||||
if (existingTempban) {
|
||||
await pluginData.state.tempbans.updateExpiryTime(user.id, time, mod.id);
|
||||
} else {
|
||||
await pluginData.state.tempbans.addTempban(user.id, time, mod.id);
|
||||
}
|
||||
const tempban = (await pluginData.state.tempbans.findExistingTempbanForUserId(user.id))!;
|
||||
registerExpiringTempban(tempban);
|
||||
} else if (existingTempban) {
|
||||
clearExpiringTempban(existingTempban);
|
||||
pluginData.state.tempbans.clear(user.id);
|
||||
}
|
||||
|
||||
// Create a new case for the updated ban since we never stored the old case id and log the action
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const createdCase = await casesPlugin.createCase({
|
||||
modId: mod.id,
|
||||
type: CaseTypes.Ban,
|
||||
userId: user.id,
|
||||
reason: formattedReason,
|
||||
noteDetails: [`Ban updated to ${time ? humanizeDuration(time) : "indefinite"}`],
|
||||
});
|
||||
if (time) {
|
||||
pluginData.getPlugin(LogsPlugin).logMemberTimedBan({
|
||||
mod: mod.user,
|
||||
user,
|
||||
caseNumber: createdCase.case_number,
|
||||
reason: formattedReason,
|
||||
banTime: humanizeDuration(time),
|
||||
});
|
||||
} else {
|
||||
pluginData.getPlugin(LogsPlugin).logMemberBan({
|
||||
mod: mod.user,
|
||||
user,
|
||||
caseNumber: createdCase.case_number,
|
||||
reason: formattedReason,
|
||||
});
|
||||
}
|
||||
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Ban updated to ${time ? "expire in " + humanizeDuration(time) + " from now" : "indefinite"}`,
|
||||
);
|
||||
lock.unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we're allowed to ban this member if they are on the server
|
||||
if (!forceban && !canActOn(pluginData, author, memberToBan!)) {
|
||||
const ourLevel = getMemberLevel(pluginData, author);
|
||||
const targetLevel = getMemberLevel(pluginData, memberToBan!);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
`Cannot ban: target permission level is equal or higher to yours, ${targetLevel} >= ${ourLevel}`,
|
||||
);
|
||||
lock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
const matchingConfig = await pluginData.config.getMatchingConfig({
|
||||
member: author,
|
||||
channel: await getContextChannel(context),
|
||||
});
|
||||
const deleteMessageDays = deleteDays ?? matchingConfig.ban_delete_message_days;
|
||||
const banResult = await banUserId(
|
||||
pluginData,
|
||||
user.id,
|
||||
formattedReason,
|
||||
formattedReasonWithAttachments,
|
||||
{
|
||||
contactMethods,
|
||||
caseArgs: {
|
||||
modId: mod.id,
|
||||
ppId: mod.id !== author.id ? author.id : undefined,
|
||||
},
|
||||
deleteMessageDays,
|
||||
modId: mod.id,
|
||||
},
|
||||
time ?? undefined,
|
||||
);
|
||||
|
||||
if (banResult.status === "failed") {
|
||||
pluginData.state.common.sendErrorMessage(context, `Failed to ban member: ${banResult.error}`);
|
||||
lock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
let forTime = "";
|
||||
if (time && time > 0) {
|
||||
forTime = `for ${humanizeDuration(time)} `;
|
||||
}
|
||||
|
||||
// Confirm the action to the moderator
|
||||
let response = "";
|
||||
if (!forceban) {
|
||||
response = `Banned **${renderUsername(user)}** ${forTime}(Case #${banResult.case.case_number})`;
|
||||
if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`;
|
||||
} else {
|
||||
response = `Member forcebanned ${forTime}(Case #${banResult.case.case_number})`;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
pluginData.state.common.sendSuccessMessage(context, response);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { actualCaseCmd } from "../../functions/actualCommands/actualCaseCmd";
|
||||
import { actualCaseCmd } from "./actualCaseCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
const opts = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { slashOptions } from "knub";
|
||||
import { actualCaseCmd } from "../../functions/actualCommands/actualCaseCmd";
|
||||
import { actualCaseCmd } from "./actualCaseCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
||||
const opts = [
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { ChatInputCommandInteraction, Message } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { sendContextResponse } from "../../../../pluginUtils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
|
||||
export async function actualCaseCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
authorId: string,
|
||||
caseNumber: number,
|
||||
show: boolean | null,
|
||||
) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(caseNumber);
|
||||
|
||||
if (!theCase) {
|
||||
void pluginData.state.common.sendErrorMessage(context, "Case not found", undefined, undefined, show !== true);
|
||||
return;
|
||||
}
|
||||
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const embed = await casesPlugin.getCaseEmbed(theCase.id, authorId);
|
||||
|
||||
void sendContextResponse(context, { ...embed, ephemeral: show !== true });
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { actualCasesCmd } from "../../functions/actualCommands/actualCasesCmd";
|
||||
import { actualCasesCmd } from "./actualCasesCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
const opts = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { slashOptions } from "knub";
|
||||
import { actualCasesCmd } from "../../functions/actualCommands/actualCasesCmd";
|
||||
import { actualCasesCmd } from "./actualCasesCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
||||
const opts = [
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { resolveMember, resolveUser, UnknownUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualCasesCmd } from "../../functions/actualCommands/actualCasesCmd";
|
||||
import { actualCasesCmd } from "./actualCasesCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
const opts = {
|
||||
|
@ -38,7 +37,7 @@ export const CasesUserMsgCmd = modActionsMsgCmd({
|
|||
(await resolveUser(pluginData.client, args.user));
|
||||
|
||||
if (user instanceof UnknownUser) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
301
backend/src/plugins/ModActions/commands/cases/actualCasesCmd.ts
Normal file
301
backend/src/plugins/ModActions/commands/cases/actualCasesCmd.ts
Normal file
|
@ -0,0 +1,301 @@
|
|||
import { APIEmbed, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { In } from "typeorm";
|
||||
import { FindOptionsWhere } from "typeorm/find-options/FindOptionsWhere";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { Case } from "../../../../data/entities/Case";
|
||||
import { sendContextResponse } from "../../../../pluginUtils";
|
||||
import {
|
||||
UnknownUser,
|
||||
chunkArray,
|
||||
emptyEmbedValue,
|
||||
renderUsername,
|
||||
resolveMember,
|
||||
resolveUser,
|
||||
trimLines,
|
||||
} from "../../../../utils";
|
||||
import { asyncMap } from "../../../../utils/async";
|
||||
import { createPaginatedMessage } from "../../../../utils/createPaginatedMessage";
|
||||
import { getGuildPrefix } from "../../../../utils/getGuildPrefix";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
|
||||
const casesPerPage = 5;
|
||||
const maxExpandedCases = 8;
|
||||
|
||||
async function sendExpandedCases(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
casesCount: number,
|
||||
cases: Case[],
|
||||
show: boolean | null,
|
||||
) {
|
||||
if (casesCount > maxExpandedCases) {
|
||||
await sendContextResponse(context, {
|
||||
content: "Too many cases for expanded view. Please use compact view instead.",
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
|
||||
for (const theCase of cases) {
|
||||
const embed = await casesPlugin.getCaseEmbed(theCase.id);
|
||||
await sendContextResponse(context, { ...embed, ephemeral: !show });
|
||||
}
|
||||
}
|
||||
|
||||
async function casesUserCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
author: User,
|
||||
modId: string | null,
|
||||
user: GuildMember | User | UnknownUser,
|
||||
modName: string,
|
||||
typesToShow: CaseTypes[],
|
||||
hidden: boolean | null,
|
||||
expand: boolean | null,
|
||||
show: boolean | null,
|
||||
) {
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const casesFilters: Omit<FindOptionsWhere<Case>, "guild_id" | "user_id"> = { type: In(typesToShow) };
|
||||
|
||||
if (modId) {
|
||||
casesFilters.mod_id = modId;
|
||||
}
|
||||
|
||||
const cases = await pluginData.state.cases.with("notes").getByUserId(user.id, casesFilters);
|
||||
const normalCases = cases.filter((c) => !c.is_hidden);
|
||||
const hiddenCases = cases.filter((c) => c.is_hidden);
|
||||
|
||||
const userName =
|
||||
user instanceof UnknownUser && cases.length ? cases[cases.length - 1].user_name : renderUsername(user);
|
||||
|
||||
if (cases.length === 0) {
|
||||
await sendContextResponse(context, {
|
||||
content: `No cases found for **${userName}**${modId ? ` by ${modName}` : ""}.`,
|
||||
ephemeral: !show,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const casesToDisplay = hidden ? cases : normalCases;
|
||||
|
||||
if (!casesToDisplay.length) {
|
||||
await sendContextResponse(context, {
|
||||
content: `No normal cases found for **${userName}**. Use "-hidden" to show ${cases.length} hidden cases.`,
|
||||
ephemeral: !show,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (expand) {
|
||||
await sendExpandedCases(pluginData, context, casesToDisplay.length, casesToDisplay, show);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compact view (= regular message with a preview of each case)
|
||||
const lines = await asyncMap(casesToDisplay, (c) => casesPlugin.getCaseSummary(c, true, author.id));
|
||||
const prefix = getGuildPrefix(pluginData);
|
||||
const linesPerChunk = 10;
|
||||
const lineChunks = chunkArray(lines, linesPerChunk);
|
||||
|
||||
const footerField = {
|
||||
name: emptyEmbedValue,
|
||||
value: trimLines(`
|
||||
Use \`${prefix}case <num>\` to see more information about an individual case
|
||||
`),
|
||||
};
|
||||
|
||||
for (const [i, linesInChunk] of lineChunks.entries()) {
|
||||
const isLastChunk = i === lineChunks.length - 1;
|
||||
|
||||
if (isLastChunk && !hidden && hiddenCases.length) {
|
||||
if (hiddenCases.length === 1) {
|
||||
linesInChunk.push(`*+${hiddenCases.length} hidden case, use "-hidden" to show it*`);
|
||||
} else {
|
||||
linesInChunk.push(`*+${hiddenCases.length} hidden cases, use "-hidden" to show them*`);
|
||||
}
|
||||
}
|
||||
|
||||
const chunkStart = i * linesPerChunk + 1;
|
||||
const chunkEnd = Math.min((i + 1) * linesPerChunk, lines.length);
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name:
|
||||
lineChunks.length === 1
|
||||
? `Cases for ${userName}${modId ? ` by ${modName}` : ""} (${lines.length} total)`
|
||||
: `Cases ${chunkStart}–${chunkEnd} of ${lines.length} for ${userName}`,
|
||||
icon_url: user instanceof UnknownUser ? undefined : user.displayAvatarURL(),
|
||||
},
|
||||
description: linesInChunk.join("\n"),
|
||||
fields: [...(isLastChunk ? [footerField] : [])],
|
||||
} satisfies APIEmbed;
|
||||
|
||||
await sendContextResponse(context, { embeds: [embed], ephemeral: !show });
|
||||
}
|
||||
}
|
||||
|
||||
async function casesModCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
author: User,
|
||||
modId: string | null,
|
||||
mod: GuildMember | User | UnknownUser,
|
||||
modName: string,
|
||||
typesToShow: CaseTypes[],
|
||||
hidden: boolean | null,
|
||||
expand: boolean | null,
|
||||
show: boolean | null,
|
||||
) {
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const casesFilters = { type: In(typesToShow), is_hidden: !!hidden };
|
||||
|
||||
const totalCases = await casesPlugin.getTotalCasesByMod(modId ?? author.id, casesFilters);
|
||||
|
||||
if (totalCases === 0) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
`No cases by **${modName}**`,
|
||||
undefined,
|
||||
undefined,
|
||||
!show
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const totalPages = Math.max(Math.ceil(totalCases / casesPerPage), 1);
|
||||
const prefix = getGuildPrefix(pluginData);
|
||||
|
||||
if (expand) {
|
||||
// Expanded view (= individual case embeds)
|
||||
const cases = totalCases > 8 ? [] : await casesPlugin.getRecentCasesByMod(modId ?? author.id, 8, 0, casesFilters);
|
||||
|
||||
await sendExpandedCases(pluginData, context, totalCases, cases, show);
|
||||
return;
|
||||
}
|
||||
|
||||
await createPaginatedMessage(
|
||||
pluginData.client,
|
||||
context,
|
||||
totalPages,
|
||||
async (page) => {
|
||||
const cases = await casesPlugin.getRecentCasesByMod(
|
||||
modId ?? author.id,
|
||||
casesPerPage,
|
||||
(page - 1) * casesPerPage,
|
||||
casesFilters,
|
||||
);
|
||||
|
||||
const lines = await asyncMap(cases, (c) => casesPlugin.getCaseSummary(c, true, author.id));
|
||||
const firstCaseNum = (page - 1) * casesPerPage + 1;
|
||||
const lastCaseNum = firstCaseNum - 1 + Math.min(cases.length, casesPerPage);
|
||||
const title = `Most recent cases ${firstCaseNum}-${lastCaseNum} of ${totalCases} by ${modName}`;
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: title,
|
||||
icon_url: mod instanceof UnknownUser ? undefined : mod.displayAvatarURL(),
|
||||
},
|
||||
description: lines.join("\n"),
|
||||
fields: [
|
||||
{
|
||||
name: emptyEmbedValue,
|
||||
value: trimLines(`
|
||||
Use \`${prefix}case <num>\` to see more information about an individual case
|
||||
Use \`${prefix}cases <user>\` to see a specific user's cases
|
||||
`),
|
||||
},
|
||||
],
|
||||
} satisfies APIEmbed;
|
||||
|
||||
return { embeds: [embed], ephemeral: !show };
|
||||
},
|
||||
{
|
||||
limitToUserId: author.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function actualCasesCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
modId: string | null,
|
||||
user: GuildMember | User | UnknownUser | null,
|
||||
author: GuildMember,
|
||||
notes: boolean | null,
|
||||
warns: boolean | null,
|
||||
mutes: boolean | null,
|
||||
unmutes: boolean | null,
|
||||
kicks: boolean | null,
|
||||
bans: boolean | null,
|
||||
unbans: boolean | null,
|
||||
reverseFilters: boolean | null,
|
||||
hidden: boolean | null,
|
||||
expand: boolean | null,
|
||||
show: boolean | null,
|
||||
) {
|
||||
const mod = modId
|
||||
? (await resolveMember(pluginData.client, pluginData.guild, modId)) || (await resolveUser(pluginData.client, modId))
|
||||
: null;
|
||||
const modName = modId ? (mod instanceof UnknownUser ? modId : renderUsername(mod!)) : renderUsername(author);
|
||||
|
||||
const allTypes = [
|
||||
CaseTypes.Note,
|
||||
CaseTypes.Warn,
|
||||
CaseTypes.Mute,
|
||||
CaseTypes.Unmute,
|
||||
CaseTypes.Kick,
|
||||
CaseTypes.Ban,
|
||||
CaseTypes.Unban,
|
||||
];
|
||||
let typesToShow: CaseTypes[] = [];
|
||||
|
||||
if (notes) typesToShow.push(CaseTypes.Note);
|
||||
if (warns) typesToShow.push(CaseTypes.Warn);
|
||||
if (mutes) typesToShow.push(CaseTypes.Mute);
|
||||
if (unmutes) typesToShow.push(CaseTypes.Unmute);
|
||||
if (kicks) typesToShow.push(CaseTypes.Kick);
|
||||
if (bans) typesToShow.push(CaseTypes.Ban);
|
||||
if (unbans) typesToShow.push(CaseTypes.Unban);
|
||||
|
||||
if (typesToShow.length === 0) {
|
||||
typesToShow = allTypes;
|
||||
} else {
|
||||
if (reverseFilters) {
|
||||
typesToShow = allTypes.filter((t) => !typesToShow.includes(t));
|
||||
}
|
||||
}
|
||||
|
||||
user
|
||||
? await casesUserCmd(
|
||||
pluginData,
|
||||
context,
|
||||
author.user,
|
||||
modId!,
|
||||
user,
|
||||
modName,
|
||||
typesToShow,
|
||||
hidden,
|
||||
expand,
|
||||
show === true,
|
||||
)
|
||||
: await casesModCmd(
|
||||
pluginData,
|
||||
context,
|
||||
author.user,
|
||||
modId!,
|
||||
mod ?? author,
|
||||
modName,
|
||||
typesToShow,
|
||||
hidden,
|
||||
expand,
|
||||
show === true,
|
||||
);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { trimLines } from "../../../../utils";
|
||||
import { actualDeleteCaseCmd } from "../../functions/actualCommands/actualDeleteCaseCmd";
|
||||
import { actualDeleteCaseCmd } from "./actualDeleteCaseCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const DeleteCaseMsgCmd = modActionsMsgCmd({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { slashOptions } from "knub";
|
||||
import { actualDeleteCaseCmd } from "../../functions/actualCommands/actualDeleteCaseCmd";
|
||||
import { actualDeleteCaseCmd } from "./actualDeleteCaseCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
||||
const opts = [slashOptions.boolean({ name: "force", description: "Whether or not to force delete", required: false })];
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
import { ChatInputCommandInteraction, GuildMember, Message } from "discord.js";
|
||||
import { GuildPluginData, helpers } from "knub";
|
||||
import { Case } from "../../../../data/entities/Case";
|
||||
import { getContextChannel, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { SECONDS, renderUsername } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { TimeAndDatePlugin } from "../../../TimeAndDate/TimeAndDatePlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
|
||||
export async function actualDeleteCaseCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
author: GuildMember,
|
||||
caseNumbers: number[],
|
||||
force: boolean,
|
||||
) {
|
||||
const failed: number[] = [];
|
||||
const validCases: Case[] = [];
|
||||
let cancelled = 0;
|
||||
|
||||
for (const num of caseNumbers) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(num);
|
||||
if (!theCase) {
|
||||
failed.push(num);
|
||||
continue;
|
||||
}
|
||||
|
||||
validCases.push(theCase);
|
||||
}
|
||||
|
||||
if (failed.length === caseNumbers.length) {
|
||||
pluginData.state.common.sendErrorMessage(context, "None of the cases were found!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const theCase of validCases) {
|
||||
if (!force) {
|
||||
const cases = pluginData.getPlugin(CasesPlugin);
|
||||
const embedContent = await cases.getCaseEmbed(theCase);
|
||||
sendContextResponse(context, {
|
||||
...embedContent,
|
||||
content: "Delete the following case? Answer 'Yes' to continue, 'No' to cancel.",
|
||||
});
|
||||
|
||||
const reply = await helpers.waitForReply(
|
||||
pluginData.client,
|
||||
await getContextChannel(context),
|
||||
author.id,
|
||||
15 * SECONDS,
|
||||
);
|
||||
const normalizedReply = (reply?.content || "").toLowerCase().trim();
|
||||
if (normalizedReply !== "yes" && normalizedReply !== "y") {
|
||||
sendContextResponse(context, "Cancelled. Case was not deleted.");
|
||||
cancelled++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const deletedByName = renderUsername(author);
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const deletedAt = timeAndDate.inGuildTz().format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
|
||||
await pluginData.state.cases.softDelete(
|
||||
theCase.id,
|
||||
author.id,
|
||||
deletedByName,
|
||||
`Case deleted by **${deletedByName}** (\`${author.id}\`) on ${deletedAt}`,
|
||||
);
|
||||
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
logs.logCaseDelete({
|
||||
mod: author,
|
||||
case: theCase,
|
||||
});
|
||||
}
|
||||
|
||||
const failedAddendum =
|
||||
failed.length > 0
|
||||
? `\nThe following cases were not found: ${failed.toString().replace(new RegExp(",", "g"), ", ")}`
|
||||
: "";
|
||||
const amt = validCases.length - cancelled;
|
||||
if (amt === 0) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"All deletions were cancelled, no cases were deleted."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`${amt} case${amt === 1 ? " was" : "s were"} deleted!${failedAddendum}`
|
||||
);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember, resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualForceBanCmd } from "../../functions/actualCommands/actualForceBanCmd";
|
||||
import { actualForceBanCmd } from "./actualForceBanCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
|
@ -27,21 +26,21 @@ export const ForceBanMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user exists as a guild member, make sure we can act on them first
|
||||
const member = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
if (member && !canActOn(pluginData, msg.member, member)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot forceban this user: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot forceban this user: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the user isn't already banned
|
||||
const banned = await isBanned(pluginData, user.id);
|
||||
if (banned) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User is already banned`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User is already banned`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,7 +48,7 @@ export const ForceBanMsgCmd = modActionsMsgCmd({
|
|||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { convertDelayStringToMS, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualForceBanCmd } from "../../functions/actualCommands/actualForceBanCmd";
|
||||
import { actualForceBanCmd } from "./actualForceBanCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -30,9 +29,13 @@ export const ForceBanSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -45,9 +48,10 @@ export const ForceBanSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,7 @@ export const ForceBanSlashCmd = modActionsSlashCmd({
|
|||
|
||||
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
|
||||
if (options.time && !convertedTime) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
pluginData.state.common.sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, Snowflake, User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { LogType } from "../../../../data/LogType";
|
||||
import { DAYS, MINUTES, UnknownUser } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { IgnoredEventType, ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { ignoreEvent } from "../../functions/ignoreEvent";
|
||||
|
||||
export async function actualForceBanCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
authorId: string,
|
||||
user: User | UnknownUser,
|
||||
reason: string,
|
||||
attachments: Array<Attachment>,
|
||||
mod: GuildMember,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
const formattedReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
|
||||
|
||||
ignoreEvent(pluginData, IgnoredEventType.Ban, user.id);
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, user.id);
|
||||
|
||||
try {
|
||||
// FIXME: Use banUserId()?
|
||||
await pluginData.guild.bans.create(user.id as Snowflake, {
|
||||
deleteMessageSeconds: (1 * DAYS) / MINUTES,
|
||||
reason: formattedReasonWithAttachments ?? undefined,
|
||||
});
|
||||
} catch {
|
||||
pluginData.state.common.sendErrorMessage(context, "Failed to forceban member");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a case
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const createdCase = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: mod.id,
|
||||
type: CaseTypes.Ban,
|
||||
reason: formattedReason,
|
||||
ppId: mod.id !== authorId ? authorId : undefined,
|
||||
});
|
||||
|
||||
// Confirm the action
|
||||
pluginData.state.common.sendSuccessMessage(context, `Member forcebanned (Case #${createdCase.case_number})`);
|
||||
|
||||
// Log the action
|
||||
pluginData.getPlugin(LogsPlugin).logMemberForceban({
|
||||
mod,
|
||||
userId: user.id,
|
||||
caseNumber: createdCase.case_number,
|
||||
reason: formattedReason,
|
||||
});
|
||||
|
||||
pluginData.state.events.emit("ban", user.id, formattedReason);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember, resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMuteCmd } from "../../functions/actualCommands/actualMuteCmd";
|
||||
import { actualMuteCmd } from "../mute/actualMuteCmd";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
|
@ -36,7 +35,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -44,7 +43,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
// Make sure we're allowed to mute this user
|
||||
if (memberToMute && !canActOn(pluginData, msg.member, memberToMute)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot mute: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot mute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -54,7 +53,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -66,7 +65,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(args);
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
|
||||
pluginData.state.common.sendErrorMessage(msg, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { UserNotificationMethod, convertDelayStringToMS, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMuteCmd } from "../../functions/actualCommands/actualMuteCmd";
|
||||
import { actualMuteCmd } from "../mute/actualMuteCmd";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
@ -47,9 +46,13 @@ export const ForceMuteSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -63,9 +66,10 @@ export const ForceMuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -75,7 +79,7 @@ export const ForceMuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
const convertedTime = options.time ? convertDelayStringToMS(options.time) ?? undefined : undefined;
|
||||
if (options.time && !convertedTime) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
pluginData.state.common.sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,7 +87,7 @@ export const ForceMuteSlashCmd = modActionsSlashCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
|
||||
pluginData.state.common.sendErrorMessage(interaction, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember, resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualUnmuteCmd } from "../../functions/actualCommands/actualUnmuteCmd";
|
||||
import { actualUnmuteCmd } from "../unmute/actualUnmuteCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
const opts = {
|
||||
|
@ -33,13 +32,13 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if they're muted in the first place
|
||||
if (!(await pluginData.state.mutes.isMuted(user.id))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: member is not muted");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot unmute: member is not muted");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -48,7 +47,7 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
// Make sure we're allowed to unmute this member
|
||||
if (memberToUnmute && !canActOn(pluginData, msg.member, memberToUnmute)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot unmute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,7 +57,7 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { convertDelayStringToMS, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualUnmuteCmd } from "../../functions/actualCommands/actualUnmuteCmd";
|
||||
import { actualUnmuteCmd } from "../unmute/actualUnmuteCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -31,9 +30,13 @@ export const ForceUnmuteSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -47,9 +50,10 @@ export const ForceUnmuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,7 +63,7 @@ export const ForceUnmuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
const convertedTime = options.time ? convertDelayStringToMS(options.time) ?? undefined : undefined;
|
||||
if (options.time && !convertedTime) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
pluginData.state.common.sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { actualHideCaseCmd } from "../../functions/actualCommands/actualHideCaseCmd";
|
||||
import { actualHideCaseCmd } from "./actualHideCaseCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const HideCaseMsgCmd = modActionsMsgCmd({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { slashOptions } from "knub";
|
||||
import { actualHideCaseCmd } from "../../functions/actualCommands/actualHideCaseCmd";
|
||||
import { actualHideCaseCmd } from "./actualHideCaseCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
||||
export const HideCaseSlashCmd = modActionsSlashCmd({
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { ChatInputCommandInteraction, Message } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
|
||||
export async function actualHideCaseCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
caseNumbers: number[],
|
||||
) {
|
||||
const failed: number[] = [];
|
||||
|
||||
for (const num of caseNumbers) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(num);
|
||||
if (!theCase) {
|
||||
failed.push(num);
|
||||
continue;
|
||||
}
|
||||
|
||||
await pluginData.state.cases.setHidden(theCase.id, true);
|
||||
}
|
||||
|
||||
if (failed.length === caseNumbers.length) {
|
||||
pluginData.state.common.sendErrorMessage(context, "None of the cases were found!");
|
||||
return;
|
||||
}
|
||||
const failedAddendum =
|
||||
failed.length > 0
|
||||
? `\nThe following cases were not found: ${failed.toString().replace(new RegExp(",", "g"), ", ")}`
|
||||
: "";
|
||||
|
||||
const amt = caseNumbers.length - failed.length;
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`${amt} case${amt === 1 ? " is" : "s are"} now hidden! Use \`unhidecase\` to unhide them.${failedAddendum}`,
|
||||
);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { hasPermission } from "knub/helpers";
|
||||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualKickCmd } from "../../functions/actualCommands/actualKickCmd";
|
||||
import { actualKickCmd } from "./actualKickCmd";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
|
@ -30,7 +29,7 @@ export const KickMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -38,7 +37,7 @@ export const KickMsgCmd = modActionsMsgCmd({
|
|||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(await pluginData.config.getForMessage(msg), "can_act_as_other"))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,7 +48,7 @@ export const KickMsgCmd = modActionsMsgCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(args);
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
|
||||
pluginData.state.common.sendErrorMessage(msg, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { UserNotificationMethod, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualKickCmd } from "../../functions/actualCommands/actualKickCmd";
|
||||
import { actualKickCmd } from "./actualKickCmd";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
@ -51,9 +50,13 @@ export const KickSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -66,9 +69,10 @@ export const KickSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -79,7 +83,7 @@ export const KickSlashCmd = modActionsSlashCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
|
||||
pluginData.state.common.sendErrorMessage(interaction, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { LogType } from "../../../../data/LogType";
|
||||
import { canActOn } from "../../../../pluginUtils";
|
||||
import { DAYS, SECONDS, UnknownUser, UserNotificationMethod, renderUsername, resolveMember } from "../../../../utils";
|
||||
import { IgnoredEventType, ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { ignoreEvent } from "../../functions/ignoreEvent";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { kickMember } from "../../functions/kickMember";
|
||||
|
||||
export async function actualKickCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
author: GuildMember,
|
||||
user: User | UnknownUser,
|
||||
reason: string,
|
||||
attachments: Attachment[],
|
||||
mod: GuildMember,
|
||||
contactMethods?: UserNotificationMethod[],
|
||||
clean?: boolean | null,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const memberToKick = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
|
||||
if (!memberToKick) {
|
||||
const banned = await isBanned(pluginData, user.id);
|
||||
if (banned) {
|
||||
pluginData.state.common.sendErrorMessage(context, `User is banned`);
|
||||
} else {
|
||||
pluginData.state.common.sendErrorMessage(context, `User not found on the server`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're allowed to kick this member
|
||||
if (!canActOn(pluginData, author, memberToKick)) {
|
||||
pluginData.state.common.sendErrorMessage(context, "Cannot kick: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
const formattedReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
|
||||
|
||||
const kickResult = await kickMember(pluginData, memberToKick, formattedReason, formattedReasonWithAttachments, {
|
||||
contactMethods,
|
||||
caseArgs: {
|
||||
modId: mod.id,
|
||||
ppId: mod.id !== author.id ? author.id : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
if (clean) {
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, memberToKick.id);
|
||||
ignoreEvent(pluginData, IgnoredEventType.Ban, memberToKick.id);
|
||||
|
||||
try {
|
||||
await memberToKick.ban({ deleteMessageSeconds: (1 * DAYS) / SECONDS, reason: "kick -clean" });
|
||||
} catch {
|
||||
pluginData.state.common.sendErrorMessage(context, "Failed to ban the user to clean messages (-clean)");
|
||||
}
|
||||
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, memberToKick.id);
|
||||
ignoreEvent(pluginData, IgnoredEventType.Unban, memberToKick.id);
|
||||
|
||||
try {
|
||||
await pluginData.guild.bans.remove(memberToKick.id, "kick -clean");
|
||||
} catch {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"Failed to unban the user after banning them (-clean)");
|
||||
}
|
||||
}
|
||||
|
||||
if (kickResult.status === "failed") {
|
||||
pluginData.state.common.sendErrorMessage(context, `Failed to kick user`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm the action to the moderator
|
||||
let response = `Kicked **${renderUsername(memberToKick.user)}** (Case #${kickResult.case.case_number})`;
|
||||
|
||||
if (kickResult.notifyResult.text) response += ` (${kickResult.notifyResult.text})`;
|
||||
pluginData.state.common.sendSuccessMessage(context, response);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { waitForReply } from "knub/helpers";
|
||||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { getContextChannel, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMassBanCmd } from "../../functions/actualCommands/actualMassBanCmd";
|
||||
import { actualMassBanCmd } from "./actualMassBanCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const MassBanMsgCmd = modActionsMsgCmd({
|
||||
|
@ -22,7 +21,7 @@ export const MassBanMsgCmd = modActionsMsgCmd({
|
|||
const banReasonReply = await waitForReply(pluginData.client, await getContextChannel(msg), msg.author.id);
|
||||
|
||||
if (!banReasonReply || !banReasonReply.content || banReasonReply.content.toLowerCase().trim() === "cancel") {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cancelled");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { slashOptions } from "knub";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMassBanCmd } from "../../functions/actualCommands/actualMassBanCmd";
|
||||
import { actualMassBanCmd } from "./actualMassBanCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -31,9 +30,13 @@ export const MassBanSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { LogType } from "../../../../data/LogType";
|
||||
import { humanizeDurationShort } from "../../../../humanizeDurationShort";
|
||||
import { canActOn, getContextChannel, isContextInteraction, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { DAYS, MINUTES, SECONDS, noop } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { IgnoredEventType, ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { ignoreEvent } from "../../functions/ignoreEvent";
|
||||
|
||||
export async function actualMassBanCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
userIds: string[],
|
||||
author: GuildMember,
|
||||
reason: string,
|
||||
attachments: Attachment[],
|
||||
) {
|
||||
// Limit to 100 users at once (arbitrary?)
|
||||
if (userIds.length > 100) {
|
||||
pluginData.state.common.sendErrorMessage(context, `Can only massban max 100 users at once`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const banReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
const banReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
|
||||
|
||||
// Verify we can act on each of the users specified
|
||||
for (const userId of userIds) {
|
||||
const member = pluginData.guild.members.cache.get(userId as Snowflake); // TODO: Get members on demand?
|
||||
if (member && !canActOn(pluginData, author, member)) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"Cannot massban one or more users: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Show a loading indicator since this can take a while
|
||||
const maxWaitTime = pluginData.state.massbanQueue.timeout * pluginData.state.massbanQueue.length;
|
||||
const maxWaitTimeFormatted = humanizeDurationShort(maxWaitTime, { round: true });
|
||||
const initialLoadingText =
|
||||
pluginData.state.massbanQueue.length === 0
|
||||
? "Banning..."
|
||||
: `Massban queued. Waiting for previous massban to finish (max wait ${maxWaitTimeFormatted}).`;
|
||||
const loadingMsg = await sendContextResponse(context, { content: initialLoadingText, ephemeral: true });
|
||||
|
||||
const waitTimeStart = performance.now();
|
||||
const waitingInterval = setInterval(() => {
|
||||
const waitTime = humanizeDurationShort(performance.now() - waitTimeStart, { round: true });
|
||||
const waitMessageContent = `Massban queued. Still waiting for previous massban to finish (waited ${waitTime}).`;
|
||||
|
||||
if (isContextInteraction(context)) {
|
||||
context.editReply(waitMessageContent).catch(() => clearInterval(waitingInterval));
|
||||
} else {
|
||||
loadingMsg.edit(waitMessageContent).catch(() => clearInterval(waitingInterval));
|
||||
}
|
||||
}, 1 * MINUTES);
|
||||
|
||||
pluginData.state.massbanQueue.add(async () => {
|
||||
clearInterval(waitingInterval);
|
||||
|
||||
if (pluginData.state.unloaded) {
|
||||
if (isContextInteraction(context)) {
|
||||
void context.deleteReply().catch(noop);
|
||||
} else {
|
||||
void loadingMsg.delete().catch(noop);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isContextInteraction(context)) {
|
||||
void context.editReply("Banning...").catch(noop);
|
||||
} else {
|
||||
void loadingMsg.edit("Banning...").catch(noop);
|
||||
}
|
||||
|
||||
// Ban each user and count failed bans (if any)
|
||||
const startTime = performance.now();
|
||||
const failedBans: string[] = [];
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const messageConfig = isContextInteraction(context)
|
||||
? await pluginData.config.getForInteraction(context)
|
||||
: await pluginData.config.getForChannel(await getContextChannel(context));
|
||||
const deleteDays = messageConfig.ban_delete_message_days;
|
||||
|
||||
for (const [i, userId] of userIds.entries()) {
|
||||
if (pluginData.state.unloaded) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
// Ignore automatic ban cases and logs
|
||||
// We create our own cases below and post a single "mass banned" log instead
|
||||
ignoreEvent(pluginData, IgnoredEventType.Ban, userId, 30 * MINUTES);
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, userId, 30 * MINUTES);
|
||||
|
||||
await pluginData.guild.bans.create(userId as Snowflake, {
|
||||
deleteMessageSeconds: (deleteDays * DAYS) / SECONDS,
|
||||
reason: banReasonWithAttachments,
|
||||
});
|
||||
|
||||
await casesPlugin.createCase({
|
||||
userId,
|
||||
modId: author.id,
|
||||
type: CaseTypes.Ban,
|
||||
reason: `Mass ban: ${banReason}`,
|
||||
postInCaseLogOverride: false,
|
||||
});
|
||||
|
||||
pluginData.state.events.emit("ban", userId, banReason);
|
||||
} catch {
|
||||
failedBans.push(userId);
|
||||
}
|
||||
|
||||
// Send a status update every 10 bans
|
||||
if ((i + 1) % 10 === 0) {
|
||||
const newLoadingMessageContent = `Banning... ${i + 1}/${userIds.length}`;
|
||||
|
||||
if (isContextInteraction(context)) {
|
||||
void context.editReply(newLoadingMessageContent).catch(noop);
|
||||
} else {
|
||||
loadingMsg.edit(newLoadingMessageContent).catch(noop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const totalTime = performance.now() - startTime;
|
||||
const formattedTimeTaken = humanizeDurationShort(totalTime, { round: true });
|
||||
|
||||
if (!isContextInteraction(context)) {
|
||||
// Clear loading indicator
|
||||
loadingMsg.delete().catch(noop);
|
||||
}
|
||||
|
||||
const successfulBanCount = userIds.length - failedBans.length;
|
||||
if (successfulBanCount === 0) {
|
||||
// All bans failed - don't create a log entry and notify the user
|
||||
pluginData.state.common.sendErrorMessage(context, "All bans failed. Make sure the IDs are valid.");
|
||||
} else {
|
||||
// Some or all bans were successful. Create a log entry for the mass ban and notify the user.
|
||||
pluginData.getPlugin(LogsPlugin).logMassBan({
|
||||
mod: author.user,
|
||||
count: successfulBanCount,
|
||||
reason: banReason,
|
||||
});
|
||||
|
||||
if (failedBans.length) {
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Banned ${successfulBanCount} users in ${formattedTimeTaken}, ${
|
||||
failedBans.length
|
||||
} failed: ${failedBans.join(" ")}`,
|
||||
);
|
||||
} else {
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Banned ${successfulBanCount} users successfully in ${formattedTimeTaken}`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { waitForReply } from "knub/helpers";
|
||||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { getContextChannel, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMassMuteCmd } from "../../functions/actualCommands/actualMassMuteCmd";
|
||||
import { actualMassMuteCmd } from "./actualMassMuteCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const MassMuteMsgCmd = modActionsMsgCmd({
|
||||
|
@ -25,7 +24,7 @@ export const MassMuteMsgCmd = modActionsMsgCmd({
|
|||
!muteReasonReceived.content ||
|
||||
muteReasonReceived.content.toLowerCase().trim() === "cancel"
|
||||
) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cancelled");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { slashOptions } from "knub";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMassMuteCmd } from "../../functions/actualCommands/actualMassMuteCmd";
|
||||
import { actualMassMuteCmd } from "./actualMassMuteCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -31,9 +30,13 @@ export const MassMuteSlashSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { LogType } from "../../../../data/LogType";
|
||||
import { logger } from "../../../../logger";
|
||||
import { canActOn, isContextInteraction, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
|
||||
export async function actualMassMuteCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
userIds: string[],
|
||||
author: GuildMember,
|
||||
reason: string,
|
||||
attachments: Attachment[],
|
||||
) {
|
||||
// Limit to 100 users at once (arbitrary?)
|
||||
if (userIds.length > 100) {
|
||||
pluginData.state.common.sendErrorMessage(context, `Can only massmute max 100 users at once`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const muteReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
const muteReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
|
||||
|
||||
// Verify we can act upon all users
|
||||
for (const userId of userIds) {
|
||||
const member = pluginData.guild.members.cache.get(userId as Snowflake);
|
||||
if (member && !canActOn(pluginData, author, member)) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"Cannot massmute one or more users: insufficient permissions"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore automatic mute cases and logs for these users
|
||||
// We'll create our own cases below and post a single "mass muted" log instead
|
||||
userIds.forEach((userId) => {
|
||||
// Use longer timeouts since this can take a while
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_MUTE, userId, 120 * 1000);
|
||||
});
|
||||
|
||||
// Show loading indicator
|
||||
const loadingMsg = await sendContextResponse(context, { content: "Muting...", ephemeral: true });
|
||||
|
||||
// Mute everyone and count fails
|
||||
const modId = author.id;
|
||||
const failedMutes: string[] = [];
|
||||
const mutesPlugin = pluginData.getPlugin(MutesPlugin);
|
||||
for (const userId of userIds) {
|
||||
try {
|
||||
await mutesPlugin.muteUser(userId, 0, `Mass mute: ${muteReason}`, `Mass mute: ${muteReasonWithAttachments}`, {
|
||||
caseArgs: {
|
||||
modId,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
logger.info(e);
|
||||
failedMutes.push(userId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isContextInteraction(context)) {
|
||||
// Clear loading indicator
|
||||
loadingMsg.delete();
|
||||
}
|
||||
|
||||
const successfulMuteCount = userIds.length - failedMutes.length;
|
||||
if (successfulMuteCount === 0) {
|
||||
// All mutes failed
|
||||
pluginData.state.common.sendErrorMessage(context, "All mutes failed. Make sure the IDs are valid.");
|
||||
} else {
|
||||
// Success on all or some mutes
|
||||
pluginData.getPlugin(LogsPlugin).logMassMute({
|
||||
mod: author.user,
|
||||
count: successfulMuteCount,
|
||||
});
|
||||
|
||||
if (failedMutes.length) {
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Muted ${successfulMuteCount} users, ${failedMutes.length} failed: ${failedMutes.join(" ")}`,
|
||||
);
|
||||
} else {
|
||||
pluginData.state.common.sendSuccessMessage(context, `Muted ${successfulMuteCount} users successfully`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { waitForReply } from "knub/helpers";
|
||||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { getContextChannel, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMassUnbanCmd } from "../../functions/actualCommands/actualMassUnbanCmd";
|
||||
import { actualMassUnbanCmd } from "./actualMassUnbanCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const MassUnbanMsgCmd = modActionsMsgCmd({
|
||||
|
@ -21,7 +20,7 @@ export const MassUnbanMsgCmd = modActionsMsgCmd({
|
|||
sendContextResponse(msg, "Unban reason? `cancel` to cancel");
|
||||
const unbanReasonReply = await waitForReply(pluginData.client, await getContextChannel(msg), msg.author.id);
|
||||
if (!unbanReasonReply || !unbanReasonReply.content || unbanReasonReply.content.toLowerCase().trim() === "cancel") {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cancelled");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { slashOptions } from "knub";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMassUnbanCmd } from "../../functions/actualCommands/actualMassUnbanCmd";
|
||||
import { actualMassUnbanCmd } from "./actualMassUnbanCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -31,9 +30,13 @@ export const MassUnbanSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { LogType } from "../../../../data/LogType";
|
||||
import { isContextInteraction, sendContextResponse } from "../../../../pluginUtils";
|
||||
import { MINUTES, noop } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { IgnoredEventType, ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { ignoreEvent } from "../../functions/ignoreEvent";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
|
||||
export async function actualMassUnbanCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
userIds: string[],
|
||||
author: GuildMember,
|
||||
reason: string,
|
||||
attachments: Attachment[],
|
||||
) {
|
||||
// Limit to 100 users at once (arbitrary?)
|
||||
if (userIds.length > 100) {
|
||||
pluginData.state.common.sendErrorMessage(context, `Can only mass-unban max 100 users at once`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unbanReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
|
||||
// Ignore automatic unban cases and logs for these users
|
||||
// We'll create our own cases below and post a single "mass unbanned" log instead
|
||||
userIds.forEach((userId) => {
|
||||
// Use longer timeouts since this can take a while
|
||||
ignoreEvent(pluginData, IgnoredEventType.Unban, userId, 2 * MINUTES);
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, userId, 2 * MINUTES);
|
||||
});
|
||||
|
||||
// Show a loading indicator since this can take a while
|
||||
const loadingMsg = await sendContextResponse(context, { content: "Unbanning...", ephemeral: true });
|
||||
|
||||
// Unban each user and count failed unbans (if any)
|
||||
const failedUnbans: Array<{ userId: string; reason: UnbanFailReasons }> = [];
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
for (const userId of userIds) {
|
||||
if (!(await isBanned(pluginData, userId))) {
|
||||
failedUnbans.push({ userId, reason: UnbanFailReasons.NOT_BANNED });
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await pluginData.guild.bans.remove(userId as Snowflake, unbanReason ?? undefined);
|
||||
|
||||
await casesPlugin.createCase({
|
||||
userId,
|
||||
modId: author.id,
|
||||
type: CaseTypes.Unban,
|
||||
reason: `Mass unban: ${unbanReason}`,
|
||||
postInCaseLogOverride: false,
|
||||
});
|
||||
} catch {
|
||||
failedUnbans.push({ userId, reason: UnbanFailReasons.UNBAN_FAILED });
|
||||
}
|
||||
}
|
||||
|
||||
if (!isContextInteraction(context)) {
|
||||
// Clear loading indicator
|
||||
loadingMsg.delete().catch(noop);
|
||||
}
|
||||
|
||||
const successfulUnbanCount = userIds.length - failedUnbans.length;
|
||||
if (successfulUnbanCount === 0) {
|
||||
// All unbans failed - don't create a log entry and notify the user
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"All unbans failed. Make sure the IDs are valid and banned."
|
||||
);
|
||||
} else {
|
||||
// Some or all unbans were successful. Create a log entry for the mass unban and notify the user.
|
||||
pluginData.getPlugin(LogsPlugin).logMassUnban({
|
||||
mod: author.user,
|
||||
count: successfulUnbanCount,
|
||||
reason: unbanReason,
|
||||
});
|
||||
|
||||
if (failedUnbans.length) {
|
||||
const notBanned = failedUnbans.filter((x) => x.reason === UnbanFailReasons.NOT_BANNED);
|
||||
const unbanFailed = failedUnbans.filter((x) => x.reason === UnbanFailReasons.UNBAN_FAILED);
|
||||
|
||||
let failedMsg = "";
|
||||
if (notBanned.length > 0) {
|
||||
failedMsg += `${notBanned.length}x ${UnbanFailReasons.NOT_BANNED}:`;
|
||||
notBanned.forEach((fail) => {
|
||||
failedMsg += " " + fail.userId;
|
||||
});
|
||||
}
|
||||
if (unbanFailed.length > 0) {
|
||||
failedMsg += `\n${unbanFailed.length}x ${UnbanFailReasons.UNBAN_FAILED}:`;
|
||||
unbanFailed.forEach((fail) => {
|
||||
failedMsg += " " + fail.userId;
|
||||
});
|
||||
}
|
||||
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Unbanned ${successfulUnbanCount} users, ${failedUnbans.length} failed:\n${failedMsg}`,
|
||||
);
|
||||
} else {
|
||||
pluginData.state.common.sendSuccessMessage(context, `Unbanned ${successfulUnbanCount} users successfully`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum UnbanFailReasons {
|
||||
NOT_BANNED = "Not banned",
|
||||
UNBAN_FAILED = "Unban failed",
|
||||
}
|
|
@ -2,8 +2,7 @@ import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
|||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember, resolveUser } from "../../../../utils";
|
||||
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMuteCmd } from "../../functions/actualCommands/actualMuteCmd";
|
||||
import { actualMuteCmd } from "./actualMuteCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
@ -38,7 +37,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -48,9 +47,10 @@ export const MuteMsgCmd = modActionsMsgCmd({
|
|||
const _isBanned = await isBanned(pluginData, user.id);
|
||||
const prefix = pluginData.fullConfig.prefix;
|
||||
if (_isBanned) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(msg, `User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
msg,
|
||||
`User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forcemute as the user is not on the server
|
||||
|
@ -61,7 +61,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
|
|||
);
|
||||
|
||||
if (!reply) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "User not on server, mute cancelled by moderator");
|
||||
pluginData.state.common.sendErrorMessage(msg, "User not on server, mute cancelled by moderator");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
// Make sure we're allowed to mute this member
|
||||
if (memberToMute && !canActOn(pluginData, msg.member, memberToMute)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot mute: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot mute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(args);
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
|
||||
pluginData.state.common.sendErrorMessage(msg, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ import { canActOn, hasPermission } from "../../../../pluginUtils";
|
|||
import { UserNotificationMethod, convertDelayStringToMS, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualMuteCmd } from "../../functions/actualCommands/actualMuteCmd";
|
||||
import { actualMuteCmd } from "./actualMuteCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
@ -53,9 +52,10 @@ export const MuteSlashCmd = modActionsSlashCmd({
|
|||
const _isBanned = await isBanned(pluginData, options.user.id);
|
||||
const prefix = pluginData.fullConfig.prefix;
|
||||
if (_isBanned) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, `User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
`User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forcemute as the user is not on the server
|
||||
|
@ -66,9 +66,10 @@ export const MuteSlashCmd = modActionsSlashCmd({
|
|||
);
|
||||
|
||||
if (!reply) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "User not on server, mute cancelled by moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"User not on server, mute cancelled by moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ export const MuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
// Make sure we're allowed to mute this member
|
||||
if (memberToMute && !canActOn(pluginData, interaction.member as GuildMember, memberToMute)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot mute: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(interaction, "Cannot mute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,9 +90,10 @@ export const MuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -101,7 +103,7 @@ export const MuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
const convertedTime = options.time ? convertDelayStringToMS(options.time) ?? undefined : undefined;
|
||||
if (options.time && !convertedTime) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
pluginData.state.common.sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -109,7 +111,7 @@ export const MuteSlashCmd = modActionsSlashCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
|
||||
} catch (e) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
|
||||
pluginData.state.common.sendErrorMessage(interaction, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
108
backend/src/plugins/ModActions/commands/mute/actualMuteCmd.ts
Normal file
108
backend/src/plugins/ModActions/commands/mute/actualMuteCmd.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../../RecoverablePluginError";
|
||||
import { logger } from "../../../../logger";
|
||||
import {
|
||||
UnknownUser,
|
||||
UserNotificationMethod,
|
||||
asSingleLine,
|
||||
isDiscordAPIError,
|
||||
renderUsername,
|
||||
} from "../../../../utils";
|
||||
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
|
||||
import { MuteResult } from "../../../Mutes/types";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
|
||||
/**
|
||||
* The actual function run by both !mute and !forcemute.
|
||||
* The only difference between the two commands is in target member validation.
|
||||
*/
|
||||
export async function actualMuteCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
user: User | UnknownUser,
|
||||
attachments: Attachment[],
|
||||
mod: GuildMember,
|
||||
ppId?: string,
|
||||
time?: number,
|
||||
reason?: string | null,
|
||||
contactMethods?: UserNotificationMethod[],
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeUntilUnmute = time && humanizeDuration(time);
|
||||
const formattedReason =
|
||||
reason || attachments.length > 0
|
||||
? await formatReasonWithMessageLinkForAttachments(pluginData, reason ?? "", context, attachments)
|
||||
: undefined;
|
||||
const formattedReasonWithAttachments =
|
||||
reason || attachments.length > 0 ? formatReasonWithAttachments(reason ?? "", attachments) : undefined;
|
||||
|
||||
let muteResult: MuteResult;
|
||||
const mutesPlugin = pluginData.getPlugin(MutesPlugin);
|
||||
|
||||
try {
|
||||
muteResult = await mutesPlugin.muteUser(user.id, time, formattedReason, formattedReasonWithAttachments, {
|
||||
contactMethods,
|
||||
caseArgs: {
|
||||
modId: mod.id,
|
||||
ppId,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"Could not mute the user: no mute role set in config"
|
||||
);
|
||||
} else if (isDiscordAPIError(e) && e.code === 10007) {
|
||||
pluginData.state.common.sendErrorMessage(context, "Could not mute the user: unknown member");
|
||||
} else {
|
||||
logger.error(`Failed to mute user ${user.id}: ${e.stack}`);
|
||||
if (user.id == null) {
|
||||
// FIXME: Debug
|
||||
// tslint:disable-next-line:no-console
|
||||
console.trace("[DEBUG] Null user.id for mute");
|
||||
}
|
||||
pluginData.state.common.sendErrorMessage(context, "Could not mute the user");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm the action to the moderator
|
||||
let response: string;
|
||||
if (time) {
|
||||
if (muteResult.updatedExistingMute) {
|
||||
response = asSingleLine(`
|
||||
Updated **${renderUsername(user)}**'s
|
||||
mute to ${timeUntilUnmute} (Case #${muteResult.case.case_number})
|
||||
`);
|
||||
} else {
|
||||
response = asSingleLine(`
|
||||
Muted **${renderUsername(user)}**
|
||||
for ${timeUntilUnmute} (Case #${muteResult.case.case_number})
|
||||
`);
|
||||
}
|
||||
} else {
|
||||
if (muteResult.updatedExistingMute) {
|
||||
response = asSingleLine(`
|
||||
Updated **${renderUsername(user)}**'s
|
||||
mute to indefinite (Case #${muteResult.case.case_number})
|
||||
`);
|
||||
} else {
|
||||
response = asSingleLine(`
|
||||
Muted **${renderUsername(user)}**
|
||||
indefinitely (Case #${muteResult.case.case_number})
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
if (muteResult.notifyResult.text) response += ` (${muteResult.notifyResult.text})`;
|
||||
pluginData.state.common.sendSuccessMessage(context, response);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualNoteCmd } from "../../functions/actualCommands/actualNoteCmd";
|
||||
import { actualNoteCmd } from "./actualNoteCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const NoteMsgCmd = modActionsMsgCmd({
|
||||
|
@ -17,12 +16,12 @@ export const NoteMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.note && msg.attachments.size === 0) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Text or attachment required");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Text or attachment required");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { slashOptions } from "knub";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualNoteCmd } from "../../functions/actualCommands/actualNoteCmd";
|
||||
import { actualNoteCmd } from "./actualNoteCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -26,9 +25,13 @@ export const NoteSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.note || options.note.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import { Attachment, ChatInputCommandInteraction, Message, User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { UnknownUser, renderUsername } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
|
||||
export async function actualNoteCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
author: User,
|
||||
attachments: Array<Attachment>,
|
||||
user: User | UnknownUser,
|
||||
note: string,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, note)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userName = renderUsername(user);
|
||||
const reason = await formatReasonWithMessageLinkForAttachments(pluginData, note, context, attachments);
|
||||
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const createdCase = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: author.id,
|
||||
type: CaseTypes.Note,
|
||||
reason,
|
||||
});
|
||||
|
||||
pluginData.getPlugin(LogsPlugin).logMemberNote({
|
||||
mod: author,
|
||||
user,
|
||||
caseNumber: createdCase.case_number,
|
||||
reason,
|
||||
});
|
||||
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Note added on **${userName}** (Case #${createdCase.case_number})`,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
pluginData.state.events.emit("note", user.id, reason);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualUnbanCmd } from "../../functions/actualCommands/actualUnbanCmd";
|
||||
import { actualUnbanCmd } from "./actualUnbanCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
const opts = {
|
||||
|
@ -26,7 +25,7 @@ export const UnbanMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -34,7 +33,7 @@ export const UnbanMsgCmd = modActionsMsgCmd({
|
|||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg, channelId: msg.channel.id }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualUnbanCmd } from "../../functions/actualCommands/actualUnbanCmd";
|
||||
import { actualUnbanCmd } from "./actualUnbanCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
||||
|
@ -30,9 +29,13 @@ export const UnbanSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -45,9 +48,10 @@ export const UnbanSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, Snowflake, User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { LogType } from "../../../../data/LogType";
|
||||
import { clearExpiringTempban } from "../../../../data/loops/expiringTempbansLoop";
|
||||
import { UnknownUser } from "../../../../utils";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { LogsPlugin } from "../../../Logs/LogsPlugin";
|
||||
import { IgnoredEventType, ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { ignoreEvent } from "../../functions/ignoreEvent";
|
||||
|
||||
export async function actualUnbanCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
authorId: string,
|
||||
user: User | UnknownUser,
|
||||
reason: string,
|
||||
attachments: Array<Attachment>,
|
||||
mod: GuildMember,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, user.id);
|
||||
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
|
||||
try {
|
||||
ignoreEvent(pluginData, IgnoredEventType.Unban, user.id);
|
||||
await pluginData.guild.bans.remove(user.id as Snowflake, formattedReason ?? undefined);
|
||||
} catch {
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
context,
|
||||
"Failed to unban member; are you sure they're banned?"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a case
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const createdCase = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: mod.id,
|
||||
type: CaseTypes.Unban,
|
||||
reason: formattedReason,
|
||||
ppId: mod.id !== authorId ? authorId : undefined,
|
||||
});
|
||||
|
||||
// Delete the tempban, if one exists
|
||||
const tempban = await pluginData.state.tempbans.findExistingTempbanForUserId(user.id);
|
||||
if (tempban) {
|
||||
clearExpiringTempban(tempban);
|
||||
await pluginData.state.tempbans.clear(user.id);
|
||||
}
|
||||
|
||||
// Confirm the action
|
||||
pluginData.state.common.sendSuccessMessage(context, `Member unbanned (Case #${createdCase.case_number})`);
|
||||
|
||||
// Log the action
|
||||
pluginData.getPlugin(LogsPlugin).logMemberUnban({
|
||||
mod: mod.user,
|
||||
userId: user.id,
|
||||
caseNumber: createdCase.case_number,
|
||||
reason: formattedReason ?? "",
|
||||
});
|
||||
|
||||
pluginData.state.events.emit("unban", user.id);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { actualHideCaseCmd } from "../../functions/actualCommands/actualHideCaseCmd";
|
||||
import { actualHideCaseCmd } from "../hidecase/actualHideCaseCmd";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
export const UnhideCaseMsgCmd = modActionsMsgCmd({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { slashOptions } from "knub";
|
||||
import { actualUnhideCaseCmd } from "../../functions/actualCommands/actualUnhideCaseCmd";
|
||||
import { actualUnhideCaseCmd } from "./actualUnhideCaseCmd";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
||||
export const UnhideCaseSlashCmd = modActionsSlashCmd({
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { ChatInputCommandInteraction, Message } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
|
||||
export async function actualUnhideCaseCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
caseNumbers: number[],
|
||||
) {
|
||||
const failed: number[] = [];
|
||||
|
||||
for (const num of caseNumbers) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(num);
|
||||
if (!theCase) {
|
||||
failed.push(num);
|
||||
continue;
|
||||
}
|
||||
|
||||
await pluginData.state.cases.setHidden(theCase.id, false);
|
||||
}
|
||||
|
||||
if (failed.length === caseNumbers.length) {
|
||||
pluginData.state.common.sendErrorMessage(context, "None of the cases were found!");
|
||||
return;
|
||||
}
|
||||
|
||||
const failedAddendum =
|
||||
failed.length > 0
|
||||
? `\nThe following cases were not found: ${failed.toString().replace(new RegExp(",", "g"), ", ")}`
|
||||
: "";
|
||||
|
||||
const amt = caseNumbers.length - failed.length;
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`${amt} case${amt === 1 ? " is" : "s are"} no longer hidden!${failedAddendum}`
|
||||
);
|
||||
}
|
|
@ -2,9 +2,8 @@ import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
|||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { resolveMember, resolveUser } from "../../../../utils";
|
||||
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
|
||||
import { actualUnmuteCmd } from "../../functions/actualCommands/actualUnmuteCmd";
|
||||
import { actualUnmuteCmd } from "./actualUnmuteCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
||||
|
@ -36,7 +35,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -50,7 +49,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
|
|||
!hasMuteRole &&
|
||||
!memberToUnmute?.isCommunicationDisabled()
|
||||
) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: member is not muted");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot unmute: member is not muted");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,9 +57,10 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
|
|||
const banned = await isBanned(pluginData, user.id);
|
||||
const prefix = pluginData.fullConfig.prefix;
|
||||
if (banned) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(msg, `User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
msg,
|
||||
`User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forceunmute as the user is not on the server
|
||||
|
@ -71,7 +71,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
|
|||
);
|
||||
|
||||
if (!reply) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "User not on server, unmute cancelled by moderator");
|
||||
pluginData.state.common.sendErrorMessage(msg, "User not on server, unmute cancelled by moderator");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
// Make sure we're allowed to unmute this member
|
||||
if (memberToUnmute && !canActOn(pluginData, msg.member, memberToUnmute)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(msg, "Cannot unmute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
|
|||
|
||||
if (args.mod) {
|
||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,8 @@ import { canActOn, hasPermission } from "../../../../pluginUtils";
|
|||
import { convertDelayStringToMS, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
|
||||
import { actualUnmuteCmd } from "../../functions/actualCommands/actualUnmuteCmd";
|
||||
import { actualUnmuteCmd } from "./actualUnmuteCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
|
||||
|
@ -34,9 +33,13 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -51,7 +54,7 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
!hasMuteRole &&
|
||||
!memberToUnmute?.isCommunicationDisabled()
|
||||
) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot unmute: member is not muted");
|
||||
pluginData.state.common.sendErrorMessage(interaction, "Cannot unmute: member is not muted");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,9 +62,10 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
const banned = await isBanned(pluginData, options.user.id);
|
||||
const prefix = pluginData.fullConfig.prefix;
|
||||
if (banned) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, `User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`);
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
`User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forceunmute as the user is not on the server
|
||||
|
@ -72,9 +76,10 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
);
|
||||
|
||||
if (!reply) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "User not on server, unmute cancelled by moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"User not on server, unmute cancelled by moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +87,7 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
// Make sure we're allowed to unmute this member
|
||||
if (memberToUnmute && !canActOn(pluginData, interaction.member as GuildMember, memberToUnmute)) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot unmute: insufficient permissions");
|
||||
pluginData.state.common.sendErrorMessage(interaction, "Cannot unmute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -95,9 +100,10 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -107,7 +113,7 @@ export const UnmuteSlashCmd = modActionsSlashCmd({
|
|||
|
||||
const convertedTime = options.time ? convertDelayStringToMS(options.time) ?? undefined : undefined;
|
||||
if (options.time && !convertedTime) {
|
||||
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
pluginData.state.common.sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { UnknownUser, asSingleLine, renderUsername } from "../../../../utils";
|
||||
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
|
||||
export async function actualUnmuteCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
user: User | UnknownUser,
|
||||
attachments: Array<Attachment>,
|
||||
mod: GuildMember,
|
||||
ppId?: string,
|
||||
time?: number,
|
||||
reason?: string | null,
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedReason =
|
||||
reason || attachments.length > 0
|
||||
? await formatReasonWithMessageLinkForAttachments(pluginData, reason ?? "", context, attachments)
|
||||
: undefined;
|
||||
|
||||
const mutesPlugin = pluginData.getPlugin(MutesPlugin);
|
||||
const result = await mutesPlugin.unmuteUser(user.id, time, {
|
||||
modId: mod.id,
|
||||
ppId: ppId ?? undefined,
|
||||
reason: formattedReason,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
pluginData.state.common.sendErrorMessage(context, "User is not muted!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm the action to the moderator
|
||||
if (time) {
|
||||
const timeUntilUnmute = time && humanizeDuration(time);
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
asSingleLine(`
|
||||
Unmuting **${renderUsername(user)}**
|
||||
in ${timeUntilUnmute} (Case #${result.case.case_number})
|
||||
`),
|
||||
);
|
||||
} else {
|
||||
pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
asSingleLine(`
|
||||
Unmuted **${renderUsername(user)}**
|
||||
(Case #${result.case.case_number})
|
||||
`),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { commandTypeHelpers as ct } from "../../../../commandTypes";
|
||||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { errorMessage, resolveMember, resolveUser } from "../../../../utils";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualWarnCmd } from "../../functions/actualCommands/actualWarnCmd";
|
||||
import { actualWarnCmd } from "./actualWarnCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsMsgCmd } from "../../types";
|
||||
|
@ -24,7 +23,7 @@ export const WarnMsgCmd = modActionsMsgCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user.id) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
|
||||
await pluginData.state.common.sendErrorMessage(msg, `User not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -33,9 +32,9 @@ export const WarnMsgCmd = modActionsMsgCmd({
|
|||
if (!memberToWarn) {
|
||||
const _isBanned = await isBanned(pluginData, user.id);
|
||||
if (_isBanned) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User is banned`);
|
||||
await pluginData.state.common.sendErrorMessage(msg, `User is banned`);
|
||||
} else {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found on the server`);
|
||||
await pluginData.state.common.sendErrorMessage(msg, `User not found on the server`);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -43,7 +42,7 @@ export const WarnMsgCmd = modActionsMsgCmd({
|
|||
|
||||
// Make sure we're allowed to warn this member
|
||||
if (!canActOn(pluginData, msg.member, memberToWarn)) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot warn: insufficient permissions");
|
||||
await pluginData.state.common.sendErrorMessage(msg, "Cannot warn: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -62,7 +61,7 @@ export const WarnMsgCmd = modActionsMsgCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(args);
|
||||
} catch (e) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
|
||||
await pluginData.state.common.sendErrorMessage(msg, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import { slashOptions } from "knub";
|
|||
import { canActOn, hasPermission } from "../../../../pluginUtils";
|
||||
import { UserNotificationMethod, resolveMember } from "../../../../utils";
|
||||
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
|
||||
import { CommonPlugin } from "../../../Common/CommonPlugin";
|
||||
import { actualWarnCmd } from "../../functions/actualCommands/actualWarnCmd";
|
||||
import { actualWarnCmd } from "./actualWarnCmd";
|
||||
import { isBanned } from "../../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
|
||||
import { modActionsSlashCmd } from "../../types";
|
||||
|
@ -47,9 +46,13 @@ export const WarnSlashCmd = modActionsSlashCmd({
|
|||
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
|
||||
|
||||
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
|
||||
await pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
|
||||
await pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"Text or attachment required",
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -59,9 +62,9 @@ export const WarnSlashCmd = modActionsSlashCmd({
|
|||
if (!memberToWarn) {
|
||||
const _isBanned = await isBanned(pluginData, options.user.id);
|
||||
if (_isBanned) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User is banned`);
|
||||
await pluginData.state.common.sendErrorMessage(interaction, `User is banned`);
|
||||
} else {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User not found on the server`);
|
||||
await pluginData.state.common.sendErrorMessage(interaction, `User not found on the server`);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -69,7 +72,7 @@ export const WarnSlashCmd = modActionsSlashCmd({
|
|||
|
||||
// Make sure we're allowed to warn this member
|
||||
if (!canActOn(pluginData, interaction.member as GuildMember, memberToWarn)) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot warn: insufficient permissions");
|
||||
await pluginData.state.common.sendErrorMessage(interaction, "Cannot warn: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,9 +84,10 @@ export const WarnSlashCmd = modActionsSlashCmd({
|
|||
|
||||
if (options.mod) {
|
||||
if (!canActAsOther) {
|
||||
await pluginData
|
||||
.getPlugin(CommonPlugin)
|
||||
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
|
||||
await pluginData.state.common.sendErrorMessage(
|
||||
interaction,
|
||||
"You don't have permission to act as another moderator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,7 +98,7 @@ export const WarnSlashCmd = modActionsSlashCmd({
|
|||
try {
|
||||
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
|
||||
} catch (e) {
|
||||
await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
|
||||
await pluginData.state.common.sendErrorMessage(interaction, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { Attachment, ChatInputCommandInteraction, GuildMember, Message } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { CaseTypes } from "../../../../data/CaseTypes";
|
||||
import { UserNotificationMethod, renderUsername } from "../../../../utils";
|
||||
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
|
||||
import { CasesPlugin } from "../../../Cases/CasesPlugin";
|
||||
import { ModActionsPluginType } from "../../types";
|
||||
import { handleAttachmentLinkDetectionAndGetRestriction } from "../../functions/attachmentLinkReaction";
|
||||
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../../functions/formatReasonForAttachments";
|
||||
import { warnMember } from "../../functions/warnMember";
|
||||
|
||||
export async function actualWarnCmd(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
context: Message | ChatInputCommandInteraction,
|
||||
authorId: string,
|
||||
mod: GuildMember,
|
||||
memberToWarn: GuildMember,
|
||||
reason: string,
|
||||
attachments: Attachment[],
|
||||
contactMethods?: UserNotificationMethod[],
|
||||
) {
|
||||
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = pluginData.config.get();
|
||||
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
|
||||
const formattedReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
|
||||
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const priorWarnAmount = await casesPlugin.getCaseTypeAmountForUserId(memberToWarn.id, CaseTypes.Warn);
|
||||
if (config.warn_notify_enabled && priorWarnAmount >= config.warn_notify_threshold) {
|
||||
const reply = await waitForButtonConfirm(
|
||||
context,
|
||||
{ content: config.warn_notify_message.replace("{priorWarnings}", `${priorWarnAmount}`) },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: authorId },
|
||||
);
|
||||
if (!reply) {
|
||||
await pluginData.state.common.sendErrorMessage(context, "Warn cancelled by moderator");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const warnResult = await warnMember(pluginData, memberToWarn, formattedReason, formattedReasonWithAttachments, {
|
||||
contactMethods,
|
||||
caseArgs: {
|
||||
modId: mod.id,
|
||||
ppId: mod.id !== authorId ? authorId : undefined,
|
||||
reason: formattedReason,
|
||||
},
|
||||
retryPromptContext: context,
|
||||
});
|
||||
|
||||
if (warnResult.status === "failed") {
|
||||
const failReason = warnResult.error ? `: ${warnResult.error}` : "";
|
||||
|
||||
await pluginData.state.common.sendErrorMessage(context, `Failed to warn user${failReason}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const messageResultText = warnResult.notifyResult.text ? ` (${warnResult.notifyResult.text})` : "";
|
||||
|
||||
await pluginData.state.common.sendSuccessMessage(
|
||||
context,
|
||||
`Warned **${renderUsername(memberToWarn.user)}** (Case #${warnResult.case.case_number})${messageResultText}`,
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue