3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-14 22:05:01 +00:00

Added Discord attachment link reaction, fixed emoji configuration and moved util functions

This commit is contained in:
Lily Bergonzat 2024-02-16 11:51:58 +01:00
parent a4c4b17a14
commit 592d037148
173 changed files with 1540 additions and 1170 deletions

View file

@ -72,6 +72,7 @@ import { onModActionsEvent } from "./functions/onModActionsEvent";
import { updateCase } from "./functions/updateCase";
import { warnMember } from "./functions/warnMember";
import {
AttachmentLinkReactionType,
BanOptions,
ConfigSchema,
KickOptions,
@ -100,6 +101,8 @@ const defaultOptions = {
warn_notify_message:
"The user already has **{priorWarnings}** warnings!\n Please check their prior cases and assess whether or not to warn anyways.\n Proceed with the warning?",
ban_delete_message_days: 1,
attachment_link_reaction: "warn" as AttachmentLinkReactionType,
attachment_storing_channel: null,
can_note: false,
can_warn: false,
@ -217,26 +220,32 @@ export const ModActionsPlugin = zeppelinGuildPlugin<ModActionsPluginType>()({
public: {
warnMember(pluginData) {
return (member: GuildMember, reason: string, warnOptions?: WarnOptions) => {
return warnMember(pluginData, member, reason, warnOptions);
return (member: GuildMember, reason: string, reasonWithAttachments: string, warnOptions?: WarnOptions) => {
return warnMember(pluginData, member, reason, reasonWithAttachments, warnOptions);
};
},
kickMember(pluginData) {
return (member: GuildMember, reason: string, kickOptions?: KickOptions) => {
kickMember(pluginData, member, reason, kickOptions);
return (member: GuildMember, reason: string, reasonWithAttachments: string, kickOptions?: KickOptions) => {
kickMember(pluginData, member, reason, reasonWithAttachments, kickOptions);
};
},
banUserId(pluginData) {
return (userId: string, reason?: string, banOptions?: BanOptions, banTime?: number) => {
return banUserId(pluginData, userId, reason, banOptions, banTime);
return (
userId: string,
reason?: string,
reasonWithAttachments?: string,
banOptions?: BanOptions,
banTime?: number,
) => {
return banUserId(pluginData, userId, reason, reasonWithAttachments, banOptions, banTime);
};
},
updateCase(pluginData) {
return (msg: Message, caseNumber: number | null, note: string) => {
updateCase(pluginData, msg.channel, msg.author, caseNumber ?? undefined, note, [...msg.attachments.values()]);
updateCase(pluginData, msg, msg.author, caseNumber ?? undefined, note, [...msg.attachments.values()]);
};
},

View file

@ -1,7 +1,8 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { CaseTypes } from "../../../../data/CaseTypes";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualAddCaseCmd } from "../../functions/actualCommands/actualAddCaseCmd";
import { modActionsMsgCmd } from "../../types";
@ -27,7 +28,7 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -35,7 +36,7 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
let mod = msg.member;
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -45,13 +46,13 @@ 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]) {
sendErrorMessage(pluginData, msg.channel, "Cannot add case: invalid case type");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot add case: invalid case type");
return;
}
actualAddCaseCmd(
pluginData,
msg.channel,
msg,
msg.member,
mod,
[...msg.attachments.values()],

View file

@ -1,7 +1,8 @@
import { slashOptions } from "knub";
import { CaseTypes } from "../../../../data/CaseTypes";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualAddCaseCmd } from "../../functions/actualCommands/actualAddCaseCmd";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -44,7 +45,9 @@ export const AddCaseSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}

View file

@ -1,6 +1,7 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { UserNotificationMethod, resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualBanCmd } from "../../functions/actualCommands/actualBanCmd";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
import { modActionsMsgCmd } from "../../types";
@ -37,7 +38,7 @@ export const BanMsgCmd = modActionsMsgCmd({
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -45,7 +46,7 @@ export const BanMsgCmd = modActionsMsgCmd({
let mod = msg.member;
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -56,13 +57,13 @@ export const BanMsgCmd = modActionsMsgCmd({
try {
contactMethods = readContactMethodsFromArgs(args) ?? undefined;
} catch (e) {
sendErrorMessage(pluginData, msg.channel, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
return;
}
actualBanCmd(
pluginData,
msg.channel,
msg,
user,
args["time"] ? args["time"] : null,
args.reason || "",

View file

@ -1,8 +1,9 @@
import { ChannelType } from "discord.js";
import { slashOptions } from "knub";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { UserNotificationMethod, convertDelayStringToMS } from "../../../../utils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualBanCmd } from "../../functions/actualCommands/actualBanCmd";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -49,7 +50,9 @@ export const BanSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -62,7 +65,9 @@ export const BanSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -73,13 +78,13 @@ export const BanSlashCmd = {
try {
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
} catch (e) {
sendErrorMessage(pluginData, interaction, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
return;
}
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
if (options.time && !convertedTime) {
sendErrorMessage(pluginData, interaction, `Could not convert ${options.time} to a delay`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
return;
}

View file

@ -14,6 +14,6 @@ export const CaseMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
actualCaseCmd(pluginData, msg.channel, msg.author.id, args.caseNumber);
actualCaseCmd(pluginData, msg, msg.author.id, args.caseNumber);
},
});

View file

@ -29,7 +29,7 @@ export const CasesModMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
return actualCasesCmd(
pluginData,
msg.channel,
msg,
args.mod,
null,
msg.author,

View file

@ -1,6 +1,6 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { sendErrorMessage } from "../../../../pluginUtils";
import { resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualCasesCmd } from "../../functions/actualCommands/actualCasesCmd";
import { modActionsMsgCmd } from "../../types";
@ -33,13 +33,13 @@ export const CasesUserMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
return actualCasesCmd(
pluginData,
msg.channel,
msg,
args.mod,
user,
msg.author,

View file

@ -18,6 +18,6 @@ export const DeleteCaseMsgCmd = modActionsMsgCmd({
},
async run({ pluginData, message, args }) {
actualDeleteCaseCmd(pluginData, message.channel, message.member, args.caseNumber, args.force);
actualDeleteCaseCmd(pluginData, message, message.member, args.caseNumber, args.force);
},
});

View file

@ -1,6 +1,7 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { canActOn, hasPermission } from "../../../../pluginUtils";
import { resolveMember, resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualForceBanCmd } from "../../functions/actualCommands/actualForceBanCmd";
import { isBanned } from "../../functions/isBanned";
import { modActionsMsgCmd } from "../../types";
@ -26,21 +27,21 @@ export const ForceBanMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).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)) {
sendErrorMessage(pluginData, msg.channel, "Cannot forceban this user: insufficient permissions");
pluginData.getPlugin(CommonPlugin).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) {
sendErrorMessage(pluginData, msg.channel, `User is already banned`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User is already banned`);
return;
}
@ -48,13 +49,13 @@ export const ForceBanMsgCmd = modActionsMsgCmd({
let mod = msg.member;
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
mod = args.mod;
}
actualForceBanCmd(pluginData, msg.channel, msg.author.id, user, args.reason, [...msg.attachments.values()], mod);
actualForceBanCmd(pluginData, msg, msg.author.id, user, args.reason, [...msg.attachments.values()], mod);
},
});

View file

@ -1,7 +1,8 @@
import { slashOptions } from "knub";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { convertDelayStringToMS } from "../../../../utils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualForceBanCmd } from "../../functions/actualCommands/actualForceBanCmd";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -26,7 +27,9 @@ export const ForceBanSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -39,7 +42,9 @@ export const ForceBanSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -48,7 +53,7 @@ export const ForceBanSlashCmd = {
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
if (options.time && !convertedTime) {
sendErrorMessage(pluginData, interaction, `Could not convert ${options.time} to a delay`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
return;
}

View file

@ -1,6 +1,7 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { canActOn, hasPermission } from "../../../../pluginUtils";
import { resolveMember, resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualMuteCmd } from "../../functions/actualCommands/actualMuteCmd";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
import { modActionsMsgCmd } from "../../types";
@ -35,7 +36,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -43,7 +44,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
// Make sure we're allowed to mute this user
if (memberToMute && !canActOn(pluginData, msg.member, memberToMute)) {
sendErrorMessage(pluginData, msg.channel, "Cannot mute: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot mute: insufficient permissions");
return;
}
@ -53,7 +54,7 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -65,13 +66,13 @@ export const ForceMuteMsgCmd = modActionsMsgCmd({
try {
contactMethods = readContactMethodsFromArgs(args);
} catch (e) {
sendErrorMessage(pluginData, msg.channel, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
return;
}
actualMuteCmd(
pluginData,
msg.channel,
msg,
user,
[...msg.attachments.values()],
mod,

View file

@ -1,8 +1,9 @@
import { ChannelType } from "discord.js";
import { slashOptions } from "knub";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { UserNotificationMethod, convertDelayStringToMS } from "../../../../utils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualMuteCmd } from "../../functions/actualCommands/actualMuteCmd";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -44,7 +45,9 @@ export const ForceMuteSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -58,7 +61,9 @@ export const ForceMuteSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -68,7 +73,7 @@ export const ForceMuteSlashCmd = {
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
if (options.time && !convertedTime) {
sendErrorMessage(pluginData, interaction, `Could not convert ${options.time} to a delay`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
return;
}
@ -76,7 +81,7 @@ export const ForceMuteSlashCmd = {
try {
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
} catch (e) {
sendErrorMessage(pluginData, interaction, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
return;
}

View file

@ -1,6 +1,7 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { canActOn, hasPermission } from "../../../../pluginUtils";
import { resolveMember, resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualUnmuteCmd } from "../../functions/actualCommands/actualUnmuteCmd";
import { modActionsMsgCmd } from "../../types";
@ -32,13 +33,13 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
// Check if they're muted in the first place
if (!(await pluginData.state.mutes.isMuted(user.id))) {
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: member is not muted");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: member is not muted");
return;
}
@ -47,7 +48,7 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
// Make sure we're allowed to unmute this member
if (memberToUnmute && !canActOn(pluginData, msg.member, memberToUnmute)) {
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: insufficient permissions");
return;
}
@ -57,7 +58,7 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -67,7 +68,7 @@ export const ForceUnmuteMsgCmd = modActionsMsgCmd({
actualUnmuteCmd(
pluginData,
msg.channel,
msg,
user,
[...msg.attachments.values()],
mod,

View file

@ -1,7 +1,8 @@
import { slashOptions } from "knub";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { convertDelayStringToMS } from "../../../../utils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualUnmuteCmd } from "../../functions/actualCommands/actualUnmuteCmd";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -27,7 +28,9 @@ export const ForceUnmuteSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -41,7 +44,9 @@ export const ForceUnmuteSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -51,7 +56,7 @@ export const ForceUnmuteSlashCmd = {
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
if (options.time && !convertedTime) {
sendErrorMessage(pluginData, interaction, `Could not convert ${options.time} to a delay`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
return;
}

View file

@ -14,6 +14,6 @@ export const HideCaseMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
actualHideCaseCmd(pluginData, msg.channel, args.caseNum);
actualHideCaseCmd(pluginData, msg, args.caseNum);
},
});

View file

@ -1,7 +1,7 @@
import { hasPermission } from "knub/helpers";
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { sendErrorMessage } from "../../../../pluginUtils";
import { resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualKickCmd } from "../../functions/actualCommands/actualKickCmd";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
import { modActionsMsgCmd } from "../../types";
@ -30,7 +30,7 @@ export const KickMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -38,7 +38,7 @@ export const KickMsgCmd = modActionsMsgCmd({
let mod = msg.member;
if (args.mod) {
if (!(await hasPermission(await pluginData.config.getForMessage(msg), "can_act_as_other"))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -49,13 +49,13 @@ export const KickMsgCmd = modActionsMsgCmd({
try {
contactMethods = readContactMethodsFromArgs(args);
} catch (e) {
sendErrorMessage(pluginData, msg.channel, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
return;
}
actualKickCmd(
pluginData,
msg.channel,
msg,
msg.member,
user,
args.reason,

View file

@ -1,8 +1,9 @@
import { ChannelType } from "discord.js";
import { slashOptions } from "knub";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { UserNotificationMethod } from "../../../../utils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualKickCmd } from "../../functions/actualCommands/actualKickCmd";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -48,7 +49,9 @@ export const KickSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -61,7 +64,9 @@ export const KickSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -72,7 +77,7 @@ export const KickSlashCmd = {
try {
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
} catch (e) {
sendErrorMessage(pluginData, interaction, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
return;
}

View file

@ -14,6 +14,6 @@ export const MassBanMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
actualMassBanCmd(pluginData, msg.channel, args.userIds, msg.member);
actualMassBanCmd(pluginData, msg, args.userIds, msg.member);
},
});

View file

@ -14,6 +14,6 @@ export const MassMuteMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
actualMassMuteCmd(pluginData, msg.channel, args.userIds, msg.member);
actualMassMuteCmd(pluginData, msg, args.userIds, msg.member);
},
});

View file

@ -14,6 +14,6 @@ export const MassUnbanMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
actualMassBanCmd(pluginData, msg.channel, args.userIds, msg.member);
actualMassBanCmd(pluginData, msg, args.userIds, msg.member);
},
});

View file

@ -1,7 +1,8 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
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 { isBanned } from "../../functions/isBanned";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
@ -37,7 +38,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -47,22 +48,20 @@ export const MuteMsgCmd = modActionsMsgCmd({
const _isBanned = await isBanned(pluginData, user.id);
const prefix = pluginData.fullConfig.prefix;
if (_isBanned) {
sendErrorMessage(
pluginData,
msg.channel,
`User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`,
);
pluginData
.getPlugin(CommonPlugin)
.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
const reply = await waitForButtonConfirm(
msg.channel,
msg,
{ content: "User not found on the server, forcemute instead?" },
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
);
if (!reply) {
sendErrorMessage(pluginData, msg.channel, "User not on server, mute cancelled by moderator");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "User not on server, mute cancelled by moderator");
return;
}
}
@ -70,7 +69,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
// Make sure we're allowed to mute this member
if (memberToMute && !canActOn(pluginData, msg.member, memberToMute)) {
sendErrorMessage(pluginData, msg.channel, "Cannot mute: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot mute: insufficient permissions");
return;
}
@ -80,7 +79,7 @@ export const MuteMsgCmd = modActionsMsgCmd({
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -92,13 +91,13 @@ export const MuteMsgCmd = modActionsMsgCmd({
try {
contactMethods = readContactMethodsFromArgs(args);
} catch (e) {
sendErrorMessage(pluginData, msg.channel, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
return;
}
actualMuteCmd(
pluginData,
msg.channel,
msg,
user,
[...msg.attachments.values()],
mod,

View file

@ -1,9 +1,10 @@
import { ChannelType } from "discord.js";
import { slashOptions } from "knub";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
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 { isBanned } from "../../functions/isBanned";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
@ -46,7 +47,9 @@ export const MuteSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -57,11 +60,9 @@ export const MuteSlashCmd = {
const _isBanned = await isBanned(pluginData, options.user.id);
const prefix = pluginData.fullConfig.prefix;
if (_isBanned) {
sendErrorMessage(
pluginData,
interaction,
`User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`,
);
pluginData
.getPlugin(CommonPlugin)
.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
@ -72,7 +73,9 @@ export const MuteSlashCmd = {
);
if (!reply) {
sendErrorMessage(pluginData, interaction, "User not on server, mute cancelled by moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "User not on server, mute cancelled by moderator");
return;
}
}
@ -80,7 +83,7 @@ export const MuteSlashCmd = {
// Make sure we're allowed to mute this member
if (memberToMute && !canActOn(pluginData, interaction.member, memberToMute)) {
sendErrorMessage(pluginData, interaction, "Cannot mute: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot mute: insufficient permissions");
return;
}
@ -93,7 +96,9 @@ export const MuteSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -103,7 +108,7 @@ export const MuteSlashCmd = {
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
if (options.time && !convertedTime) {
sendErrorMessage(pluginData, interaction, `Could not convert ${options.time} to a delay`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
return;
}
@ -111,7 +116,7 @@ export const MuteSlashCmd = {
try {
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
} catch (e) {
sendErrorMessage(pluginData, interaction, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
return;
}

View file

@ -1,6 +1,6 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { sendErrorMessage } from "../../../../pluginUtils";
import { resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualNoteCmd } from "../../functions/actualCommands/actualNoteCmd";
import { modActionsMsgCmd } from "../../types";
@ -17,15 +17,15 @@ export const NoteMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
if (!args.note && msg.attachments.size === 0) {
sendErrorMessage(pluginData, msg.channel, "Text or attachment required");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Text or attachment required");
return;
}
actualNoteCmd(pluginData, msg.channel, msg.author, [...msg.attachments.values()], user, args.note || "");
actualNoteCmd(pluginData, msg, msg.author, [...msg.attachments.values()], user, args.note || "");
},
});

View file

@ -1,6 +1,6 @@
import { slashOptions } from "knub";
import { sendErrorMessage } from "../../../../pluginUtils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualNoteCmd } from "../../functions/actualCommands/actualNoteCmd";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -24,7 +24,9 @@ export const NoteSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.note || options.note.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}

View file

@ -1,6 +1,7 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualUnbanCmd } from "../../functions/actualCommands/actualUnbanCmd";
import { modActionsMsgCmd } from "../../types";
@ -25,7 +26,7 @@ export const UnbanMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -33,13 +34,13 @@ 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 }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
mod = args.mod;
}
actualUnbanCmd(pluginData, msg.channel, msg.author.id, user, args.reason, [...msg.attachments.values()], mod);
actualUnbanCmd(pluginData, msg, msg.author.id, user, args.reason, [...msg.attachments.values()], mod);
},
});

View file

@ -1,6 +1,7 @@
import { slashOptions } from "knub";
import { hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { hasPermission } from "../../../../pluginUtils";
import { generateAttachmentSlashOptions, retrieveMultipleOptions } from "../../../../utils/multipleSlashOptions";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualUnbanCmd } from "../../functions/actualCommands/actualUnbanCmd";
import { NUMBER_ATTACHMENTS_CASE_CREATION } from "../constants";
@ -25,7 +26,9 @@ export const UnbanSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -38,7 +41,9 @@ export const UnbanSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}

View file

@ -14,6 +14,6 @@ export const UnhideCaseMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
actualHideCaseCmd(pluginData, msg.channel, args.caseNum);
actualHideCaseCmd(pluginData, msg, args.caseNum);
},
});

View file

@ -1,7 +1,8 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
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 { isBanned } from "../../functions/isBanned";
@ -35,7 +36,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -49,7 +50,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
!hasMuteRole &&
!memberToUnmute?.isCommunicationDisabled()
) {
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: member is not muted");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: member is not muted");
return;
}
@ -57,22 +58,20 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
const banned = await isBanned(pluginData, user.id);
const prefix = pluginData.fullConfig.prefix;
if (banned) {
sendErrorMessage(
pluginData,
msg.channel,
`User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`,
);
pluginData
.getPlugin(CommonPlugin)
.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
const reply = await waitForButtonConfirm(
msg.channel,
msg,
{ content: "User not on server, forceunmute instead?" },
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
);
if (!reply) {
sendErrorMessage(pluginData, msg.channel, "User not on server, unmute cancelled by moderator");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "User not on server, unmute cancelled by moderator");
return;
}
}
@ -80,7 +79,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
// Make sure we're allowed to unmute this member
if (memberToUnmute && !canActOn(pluginData, msg.member, memberToUnmute)) {
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot unmute: insufficient permissions");
return;
}
@ -90,7 +89,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "You don't have permission to use -mod");
return;
}
@ -100,7 +99,7 @@ export const UnmuteMsgCmd = modActionsMsgCmd({
actualUnmuteCmd(
pluginData,
msg.channel,
msg,
user,
[...msg.attachments.values()],
mod,

View file

@ -1,8 +1,9 @@
import { slashOptions } from "knub";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
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 { isBanned } from "../../functions/isBanned";
@ -30,7 +31,9 @@ export const UnmuteSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -45,7 +48,7 @@ export const UnmuteSlashCmd = {
!hasMuteRole &&
!memberToUnmute?.isCommunicationDisabled()
) {
sendErrorMessage(pluginData, interaction, "Cannot unmute: member is not muted");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot unmute: member is not muted");
return;
}
@ -53,11 +56,9 @@ export const UnmuteSlashCmd = {
const banned = await isBanned(pluginData, options.user.id);
const prefix = pluginData.fullConfig.prefix;
if (banned) {
sendErrorMessage(
pluginData,
interaction,
`User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`,
);
pluginData
.getPlugin(CommonPlugin)
.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
@ -68,7 +69,9 @@ export const UnmuteSlashCmd = {
);
if (!reply) {
sendErrorMessage(pluginData, interaction, "User not on server, unmute cancelled by moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "User not on server, unmute cancelled by moderator");
return;
}
}
@ -76,7 +79,7 @@ export const UnmuteSlashCmd = {
// Make sure we're allowed to unmute this member
if (memberToUnmute && !canActOn(pluginData, interaction.member, memberToUnmute)) {
sendErrorMessage(pluginData, interaction, "Cannot unmute: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot unmute: insufficient permissions");
return;
}
@ -89,7 +92,9 @@ export const UnmuteSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -99,7 +104,7 @@ export const UnmuteSlashCmd = {
const convertedTime = options.time ? convertDelayStringToMS(options.time) : null;
if (options.time && !convertedTime) {
sendErrorMessage(pluginData, interaction, `Could not convert ${options.time} to a delay`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `Could not convert ${options.time} to a delay`);
return;
}

View file

@ -19,6 +19,6 @@ export const UpdateMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
await updateCase(pluginData, msg.channel, msg.author, args.caseNumber, args.note, [...msg.attachments.values()]);
await updateCase(pluginData, msg, msg.author, args.caseNumber, args.note, [...msg.attachments.values()]);
},
});

View file

@ -1,6 +1,7 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
import { canActOn, hasPermission } from "../../../../pluginUtils";
import { errorMessage, resolveMember, resolveUser } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { actualWarnCmd } from "../../functions/actualCommands/actualWarnCmd";
import { isBanned } from "../../functions/isBanned";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
@ -23,7 +24,7 @@ export const WarnMsgCmd = modActionsMsgCmd({
async run({ pluginData, message: msg, args }) {
const user = await resolveUser(pluginData.client, args.user);
if (!user.id) {
sendErrorMessage(pluginData, msg.channel, `User not found`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`);
return;
}
@ -32,9 +33,9 @@ export const WarnMsgCmd = modActionsMsgCmd({
if (!memberToWarn) {
const _isBanned = await isBanned(pluginData, user.id);
if (_isBanned) {
sendErrorMessage(pluginData, msg.channel, `User is banned`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User is banned`);
} else {
sendErrorMessage(pluginData, msg.channel, `User not found on the server`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found on the server`);
}
return;
@ -42,7 +43,7 @@ export const WarnMsgCmd = modActionsMsgCmd({
// Make sure we're allowed to warn this member
if (!canActOn(pluginData, msg.member, memberToWarn)) {
sendErrorMessage(pluginData, msg.channel, "Cannot warn: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot warn: insufficient permissions");
return;
}
@ -61,13 +62,13 @@ export const WarnMsgCmd = modActionsMsgCmd({
try {
contactMethods = readContactMethodsFromArgs(args);
} catch (e) {
sendErrorMessage(pluginData, msg.channel, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message);
return;
}
actualWarnCmd(
pluginData,
msg.channel,
msg,
msg.author.id,
mod,
memberToWarn,

View file

@ -1,8 +1,9 @@
import { ChannelType } from "discord.js";
import { slashOptions } from "knub";
import { canActOn, hasPermission, sendErrorMessage } from "../../../../pluginUtils";
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 { isBanned } from "../../functions/isBanned";
import { readContactMethodsFromArgs } from "../../functions/readContactMethodsFromArgs";
@ -44,7 +45,9 @@ export const WarnSlashCmd = {
const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment");
if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) {
sendErrorMessage(pluginData, interaction, "Text or attachment required", undefined, undefined, true);
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true);
return;
}
@ -54,9 +57,9 @@ export const WarnSlashCmd = {
if (!memberToWarn) {
const _isBanned = await isBanned(pluginData, options.user.id);
if (_isBanned) {
sendErrorMessage(pluginData, interaction, `User is banned`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User is banned`);
} else {
sendErrorMessage(pluginData, interaction, `User not found on the server`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User not found on the server`);
}
return;
@ -64,7 +67,7 @@ export const WarnSlashCmd = {
// Make sure we're allowed to warn this member
if (!canActOn(pluginData, interaction.member, memberToWarn)) {
sendErrorMessage(pluginData, interaction, "Cannot warn: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot warn: insufficient permissions");
return;
}
@ -76,7 +79,9 @@ export const WarnSlashCmd = {
if (options.mod) {
if (!canActAsOther) {
sendErrorMessage(pluginData, interaction, "You don't have permission to act as another moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(interaction, "You don't have permission to act as another moderator");
return;
}
@ -87,7 +92,7 @@ export const WarnSlashCmd = {
try {
contactMethods = readContactMethodsFromArgs(options) ?? undefined;
} catch (e) {
sendErrorMessage(pluginData, interaction, e.message);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message);
return;
}

View file

@ -1,17 +1,19 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, TextBasedChannel, User } from "discord.js";
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, sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { canActOn } from "../../../../pluginUtils";
import { UnknownUser, renderUserUsername, resolveMember } from "../../../../utils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
export async function actualAddCaseCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: GuildMember,
mod: GuildMember,
attachments: Array<Attachment>,
@ -19,14 +21,20 @@ export async function actualAddCaseCmd(
type: keyof CaseTypes,
reason: string,
) {
// 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)) {
sendErrorMessage(pluginData, context, "Cannot add case on this user: insufficient permissions");
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
return;
}
const formattedReason = formatReasonWithAttachments(reason, attachments);
// 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
.getPlugin(CommonPlugin)
.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);
@ -39,9 +47,11 @@ export async function actualAddCaseCmd(
});
if (user) {
sendSuccessMessage(pluginData, context, `Case #${theCase.case_number} created for **${renderUserUsername(user)}**`);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(context, `Case #${theCase.case_number} created for **${renderUserUsername(user)}**`);
} else {
sendSuccessMessage(pluginData, context, `Case #${theCase.case_number} created`);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, `Case #${theCase.case_number} created`);
}
// Log the action

View file

@ -1,23 +1,25 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, TextBasedChannel, User } from "discord.js";
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, isContextInteraction, sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { canActOn, getContextChannel } from "../../../../pluginUtils";
import { UnknownUser, UserNotificationMethod, renderUserUsername, resolveMember } from "../../../../utils";
import { banLock } from "../../../../utils/lockNameHelpers";
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { ModActionsPluginType } from "../../types";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { banUserId } from "../banUserId";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { isBanned } from "../isBanned";
export async function actualBanCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
user: User | UnknownUser,
time: number | null,
reason: string,
@ -27,8 +29,13 @@ export async function actualBanCmd(
contactMethods?: UserNotificationMethod[],
deleteDays?: number,
) {
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
return;
}
const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id);
const formattedReason = formatReasonWithAttachments(reason, attachments);
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));
@ -47,7 +54,7 @@ export async function actualBanCmd(
);
if (!reply) {
sendErrorMessage(pluginData, context, "User not on server, ban cancelled by moderator");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "User not on server, ban cancelled by moderator");
lock.unlock();
return;
} else {
@ -57,7 +64,7 @@ export async function actualBanCmd(
// Abort if trying to ban user indefinitely if they are already banned indefinitely
if (!existingTempban && !time) {
sendErrorMessage(pluginData, context, `User is already banned indefinitely.`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `User is already banned indefinitely.`);
return;
}
@ -69,7 +76,9 @@ export async function actualBanCmd(
);
if (!reply) {
sendErrorMessage(pluginData, context, "User already banned, update cancelled by moderator");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "User already banned, update cancelled by moderator");
lock.unlock();
return;
}
@ -114,11 +123,12 @@ export async function actualBanCmd(
});
}
sendSuccessMessage(
pluginData,
context,
`Ban updated to ${time ? "expire in " + humanizeDuration(time) + " from now" : "indefinite"}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`Ban updated to ${time ? "expire in " + humanizeDuration(time) + " from now" : "indefinite"}`,
);
lock.unlock();
return;
}
@ -127,24 +137,26 @@ export async function actualBanCmd(
if (!forceban && !canActOn(pluginData, author, memberToBan!)) {
const ourLevel = getMemberLevel(pluginData, author);
const targetLevel = getMemberLevel(pluginData, memberToBan!);
sendErrorMessage(
pluginData,
context,
`Cannot ban: target permission level is equal or higher to yours, ${targetLevel} >= ${ourLevel}`,
);
pluginData
.getPlugin(CommonPlugin)
.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: isContextInteraction(context) ? context.channel : context,
channel: await getContextChannel(context),
});
const deleteMessageDays = deleteDays ?? matchingConfig.ban_delete_message_days;
const banResult = await banUserId(
pluginData,
user.id,
formattedReason,
formattedReasonWithAttachments,
{
contactMethods,
caseArgs: {
@ -158,7 +170,7 @@ export async function actualBanCmd(
);
if (banResult.status === "failed") {
sendErrorMessage(pluginData, context, `Failed to ban member: ${banResult.error}`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `Failed to ban member: ${banResult.error}`);
lock.unlock();
return;
}
@ -178,5 +190,5 @@ export async function actualBanCmd(
}
lock.unlock();
sendSuccessMessage(pluginData, context, response);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, response);
}

View file

@ -1,19 +1,20 @@
import { ChatInputCommandInteraction, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, Message } from "discord.js";
import { GuildPluginData } from "knub";
import { sendContextResponse, sendErrorMessage } from "../../../../pluginUtils";
import { sendContextResponse } from "../../../../pluginUtils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { ModActionsPluginType } from "../../types";
export async function actualCaseCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
authorId: string,
caseNumber: number,
) {
const theCase = await pluginData.state.cases.findByCaseNumber(caseNumber);
if (!theCase) {
sendErrorMessage(pluginData, context, "Case not found");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Case not found");
return;
}

View file

@ -1,10 +1,10 @@
import { APIEmbed, ChatInputCommandInteraction, TextBasedChannel, User } from "discord.js";
import { APIEmbed, ChatInputCommandInteraction, 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, sendErrorMessage } from "../../../../pluginUtils";
import { sendContextResponse } from "../../../../pluginUtils";
import {
UnknownUser,
chunkArray,
@ -18,6 +18,7 @@ import { createPaginatedMessage } from "../../../../utils/createPaginatedMessage
import { getChunkedEmbedFields } from "../../../../utils/getChunkedEmbedFields";
import { getGuildPrefix } from "../../../../utils/getGuildPrefix";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { ModActionsPluginType } from "../../types";
const casesPerPage = 5;
@ -25,7 +26,7 @@ const maxExpandedCases = 8;
async function sendExpandedCases(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
casesCount: number,
cases: Case[],
) {
@ -45,7 +46,7 @@ async function sendExpandedCases(
async function casesUserCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: User,
modId: string | null,
user: User | UnknownUser,
@ -137,7 +138,7 @@ async function casesUserCmd(
async function casesModCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: User,
modId: string | null,
mod: User | UnknownUser,
@ -152,7 +153,7 @@ async function casesModCmd(
const totalCases = await casesPlugin.getTotalCasesByMod(modId ?? author.id, caseFilters);
if (totalCases === 0) {
sendErrorMessage(pluginData, context, `No cases by **${modName}**`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `No cases by **${modName}**`);
return;
}
@ -211,7 +212,7 @@ async function casesModCmd(
export async function actualCasesCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
modId: string | null,
user: User | UnknownUser | null,
author: User,

View file

@ -1,21 +1,17 @@
import { ChatInputCommandInteraction, GuildMember, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, GuildMember, Message } from "discord.js";
import { GuildPluginData, helpers } from "knub";
import { Case } from "../../../../data/entities/Case";
import {
isContextInteraction,
sendContextResponse,
sendErrorMessage,
sendSuccessMessage,
} from "../../../../pluginUtils";
import { getContextChannel, sendContextResponse } from "../../../../pluginUtils";
import { SECONDS } from "../../../../utils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { TimeAndDatePlugin } from "../../../TimeAndDate/TimeAndDatePlugin";
import { ModActionsPluginType } from "../../types";
export async function actualDeleteCaseCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: GuildMember,
caseNumbers: number[],
force: boolean,
@ -35,7 +31,7 @@ export async function actualDeleteCaseCmd(
}
if (failed.length === caseNumbers.length) {
sendErrorMessage(pluginData, context, "None of the cases were found!");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "None of the cases were found!");
return;
}
@ -50,7 +46,7 @@ export async function actualDeleteCaseCmd(
const reply = await helpers.waitForReply(
pluginData.client,
isContextInteraction(context) ? context.channel! : context,
await getContextChannel(context),
author.id,
15 * SECONDS,
);
@ -87,9 +83,13 @@ export async function actualDeleteCaseCmd(
: "";
const amt = validCases.length - cancelled;
if (amt === 0) {
sendErrorMessage(pluginData, context, "All deletions were cancelled, no cases were deleted.");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "All deletions were cancelled, no cases were deleted.");
return;
}
sendSuccessMessage(pluginData, context, `${amt} case${amt === 1 ? " was" : "s were"} deleted!${failedAddendum}`);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(context, `${amt} case${amt === 1 ? " was" : "s were"} deleted!${failedAddendum}`);
}

View file

@ -1,25 +1,31 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, Snowflake, TextBasedChannel, User } from "discord.js";
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 { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { DAYS, MINUTES, UnknownUser } from "../../../../utils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { IgnoredEventType, ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { ignoreEvent } from "../ignoreEvent";
export async function actualForceBanCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
authorId: string,
user: User | UnknownUser,
reason: string,
attachments: Array<Attachment>,
mod: GuildMember,
) {
const formattedReason = formatReasonWithAttachments(reason, attachments);
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);
@ -28,10 +34,10 @@ export async function actualForceBanCmd(
// FIXME: Use banUserId()?
await pluginData.guild.bans.create(user.id as Snowflake, {
deleteMessageSeconds: (1 * DAYS) / MINUTES,
reason: formattedReason ?? undefined,
reason: formattedReasonWithAttachments ?? undefined,
});
} catch {
sendErrorMessage(pluginData, context, "Failed to forceban member");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Failed to forceban member");
return;
}
@ -46,7 +52,9 @@ export async function actualForceBanCmd(
});
// Confirm the action
sendSuccessMessage(pluginData, context, `Member forcebanned (Case #${createdCase.case_number})`);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(context, `Member forcebanned (Case #${createdCase.case_number})`);
// Log the action
pluginData.getPlugin(LogsPlugin).logMemberForceban({

View file

@ -1,11 +1,11 @@
import { ChatInputCommandInteraction, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, Message } from "discord.js";
import { GuildPluginData } from "knub";
import { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { ModActionsPluginType } from "../../types";
export async function actualHideCaseCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
caseNumbers: number[],
) {
const failed: number[] = [];
@ -21,7 +21,7 @@ export async function actualHideCaseCmd(
}
if (failed.length === caseNumbers.length) {
sendErrorMessage(pluginData, context, "None of the cases were found!");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "None of the cases were found!");
return;
}
const failedAddendum =
@ -30,9 +30,10 @@ export async function actualHideCaseCmd(
: "";
const amt = caseNumbers.length - failed.length;
sendSuccessMessage(
pluginData,
context,
`${amt} case${amt === 1 ? " is" : "s are"} now hidden! Use \`unhidecase\` to unhide them.${failedAddendum}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`${amt} case${amt === 1 ? " is" : "s are"} now hidden! Use \`unhidecase\` to unhide them.${failedAddendum}`,
);
}

View file

@ -1,7 +1,7 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, TextBasedChannel, User } from "discord.js";
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
import { GuildPluginData } from "knub";
import { LogType } from "../../../../data/LogType";
import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { canActOn } from "../../../../pluginUtils";
import {
DAYS,
SECONDS,
@ -10,15 +10,17 @@ import {
renderUserUsername,
resolveMember,
} from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { IgnoredEventType, ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { ignoreEvent } from "../ignoreEvent";
import { isBanned } from "../isBanned";
import { kickMember } from "../kickMember";
export async function actualKickCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: GuildMember,
user: User | UnknownUser,
reason: string,
@ -27,14 +29,18 @@ export async function actualKickCmd(
contactMethods?: UserNotificationMethod[],
clean?: boolean,
) {
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) {
sendErrorMessage(pluginData, context, `User is banned`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `User is banned`);
} else {
sendErrorMessage(pluginData, context, `User not found on the server`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `User not found on the server`);
}
return;
@ -42,13 +48,14 @@ export async function actualKickCmd(
// Make sure we're allowed to kick this member
if (!canActOn(pluginData, author, memberToKick)) {
sendErrorMessage(pluginData, context, "Cannot kick: insufficient permissions");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Cannot kick: insufficient permissions");
return;
}
const formattedReason = formatReasonWithAttachments(reason, attachments);
const formattedReason = await formatReasonWithMessageLinkForAttachments(pluginData, reason, context, attachments);
const formattedReasonWithAttachments = formatReasonWithAttachments(reason, attachments);
const kickResult = await kickMember(pluginData, memberToKick, formattedReason, {
const kickResult = await kickMember(pluginData, memberToKick, formattedReason, formattedReasonWithAttachments, {
contactMethods,
caseArgs: {
modId: mod.id,
@ -63,7 +70,7 @@ export async function actualKickCmd(
try {
await memberToKick.ban({ deleteMessageSeconds: (1 * DAYS) / SECONDS, reason: "kick -clean" });
} catch {
sendErrorMessage(pluginData, context, "Failed to ban the user to clean messages (-clean)");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Failed to ban the user to clean messages (-clean)");
}
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, memberToKick.id);
@ -72,12 +79,14 @@ export async function actualKickCmd(
try {
await pluginData.guild.bans.remove(memberToKick.id, "kick -clean");
} catch {
sendErrorMessage(pluginData, context, "Failed to unban the user after banning them (-clean)");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "Failed to unban the user after banning them (-clean)");
}
}
if (kickResult.status === "failed") {
sendErrorMessage(pluginData, context, `Failed to kick user`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `Failed to kick user`);
return;
}
@ -85,5 +94,5 @@ export async function actualKickCmd(
let response = `Kicked **${renderUserUsername(memberToKick.user)}** (Case #${kickResult.case.case_number})`;
if (kickResult.notifyResult.text) response += ` (${kickResult.notifyResult.text})`;
sendSuccessMessage(pluginData, context, response);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, response);
}

View file

@ -1,55 +1,58 @@
import { ChatInputCommandInteraction, GuildMember, Snowflake, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, GuildMember, Message, Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import { waitForReply } from "knub/helpers";
import { CaseTypes } from "../../../../data/CaseTypes";
import { LogType } from "../../../../data/LogType";
import { humanizeDurationShort } from "../../../../humanizeDurationShort";
import {
canActOn,
isContextInteraction,
sendContextResponse,
sendErrorMessage,
sendSuccessMessage,
} from "../../../../pluginUtils";
import { canActOn, getContextChannel, isContextInteraction, sendContextResponse } from "../../../../pluginUtils";
import { DAYS, MINUTES, SECONDS, noop } from "../../../../utils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { IgnoredEventType, ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { ignoreEvent } from "../ignoreEvent";
export async function actualMassBanCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
userIds: string[],
author: GuildMember,
) {
// Limit to 100 users at once (arbitrary?)
if (userIds.length > 100) {
sendErrorMessage(pluginData, context, `Can only massban max 100 users at once`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `Can only massban max 100 users at once`);
return;
}
// Ask for ban reason (cleaner this way instead of trying to cram it into the args)
sendContextResponse(context, "Ban reason? `cancel` to cancel");
const banReasonReply = await waitForReply(
pluginData.client,
isContextInteraction(context) ? context.channel! : context,
author.id,
);
const banReasonReply = await waitForReply(pluginData.client, await getContextChannel(context), author.id);
if (!banReasonReply || !banReasonReply.content || banReasonReply.content.toLowerCase().trim() === "cancel") {
sendErrorMessage(pluginData, context, "Cancelled");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Cancelled");
return;
}
const banReason = formatReasonWithAttachments(banReasonReply.content, [...banReasonReply.attachments.values()]);
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, banReasonReply.content)) {
return;
}
const banReason = await formatReasonWithMessageLinkForAttachments(pluginData, banReasonReply.content, context, [
...banReasonReply.attachments.values(),
]);
const banReasonWithAttachments = formatReasonWithAttachments(banReasonReply.content, [
...banReasonReply.attachments.values(),
]);
// 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)) {
sendErrorMessage(pluginData, context, "Cannot massban one or more users: insufficient permissions");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "Cannot massban one or more users: insufficient permissions");
return;
}
}
@ -87,7 +90,7 @@ export async function actualMassBanCmd(
const casesPlugin = pluginData.getPlugin(CasesPlugin);
const messageConfig = isContextInteraction(context)
? await pluginData.config.getForInteraction(context)
: await pluginData.config.getForChannel(context);
: await pluginData.config.getForChannel(await getContextChannel(context));
const deleteDays = messageConfig.ban_delete_message_days;
for (const [i, userId] of userIds.entries()) {
@ -103,7 +106,7 @@ export async function actualMassBanCmd(
await pluginData.guild.bans.create(userId as Snowflake, {
deleteMessageSeconds: (deleteDays * DAYS) / SECONDS,
reason: banReason,
reason: banReasonWithAttachments,
});
await casesPlugin.createCase({
@ -134,7 +137,7 @@ export async function actualMassBanCmd(
const successfulBanCount = userIds.length - failedBans.length;
if (successfulBanCount === 0) {
// All bans failed - don't create a log entry and notify the user
sendErrorMessage(pluginData, context, "All bans failed. Make sure the IDs are valid.");
pluginData.getPlugin(CommonPlugin).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({
@ -144,19 +147,18 @@ export async function actualMassBanCmd(
});
if (failedBans.length) {
sendSuccessMessage(
pluginData,
context,
`Banned ${successfulBanCount} users in ${formattedTimeTaken}, ${failedBans.length} failed: ${failedBans.join(
" ",
)}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`Banned ${successfulBanCount} users in ${formattedTimeTaken}, ${
failedBans.length
} failed: ${failedBans.join(" ")}`,
);
} else {
sendSuccessMessage(
pluginData,
context,
`Banned ${successfulBanCount} users successfully in ${formattedTimeTaken}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(context, `Banned ${successfulBanCount} users successfully in ${formattedTimeTaken}`);
}
}
});

View file

@ -1,49 +1,48 @@
import { ChatInputCommandInteraction, GuildMember, Snowflake, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, GuildMember, Message, Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import { waitForReply } from "knub/helpers";
import { LogType } from "../../../../data/LogType";
import { logger } from "../../../../logger";
import {
canActOn,
isContextInteraction,
sendContextResponse,
sendErrorMessage,
sendSuccessMessage,
} from "../../../../pluginUtils";
import { canActOn, getContextChannel, sendContextResponse } from "../../../../pluginUtils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
import { ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
export async function actualMassMuteCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
userIds: string[],
author: GuildMember,
) {
// Limit to 100 users at once (arbitrary?)
if (userIds.length > 100) {
sendErrorMessage(pluginData, context, `Can only massmute max 100 users at once`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `Can only massmute max 100 users at once`);
return;
}
// Ask for mute reason
sendContextResponse(context, "Mute reason? `cancel` to cancel");
const muteReasonReceived = await waitForReply(
pluginData.client,
isContextInteraction(context) ? context.channel! : context,
author.id,
);
const muteReasonReceived = await waitForReply(pluginData.client, await getContextChannel(context), author.id);
if (
!muteReasonReceived ||
!muteReasonReceived.content ||
muteReasonReceived.content.toLowerCase().trim() === "cancel"
) {
sendErrorMessage(pluginData, context, "Cancelled");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Cancelled");
return;
}
const muteReason = formatReasonWithAttachments(muteReasonReceived.content, [
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, muteReasonReceived.content)) {
return;
}
const muteReason = await formatReasonWithMessageLinkForAttachments(pluginData, muteReasonReceived.content, context, [
...muteReasonReceived.attachments.values(),
]);
const muteReasonWithAttachments = formatReasonWithAttachments(muteReasonReceived.content, [
...muteReasonReceived.attachments.values(),
]);
@ -51,7 +50,9 @@ export async function actualMassMuteCmd(
for (const userId of userIds) {
const member = pluginData.guild.members.cache.get(userId as Snowflake);
if (member && !canActOn(pluginData, author, member)) {
sendErrorMessage(pluginData, context, "Cannot massmute one or more users: insufficient permissions");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "Cannot massmute one or more users: insufficient permissions");
return;
}
}
@ -72,7 +73,7 @@ export async function actualMassMuteCmd(
const mutesPlugin = pluginData.getPlugin(MutesPlugin);
for (const userId of userIds) {
try {
await mutesPlugin.muteUser(userId, 0, `Mass mute: ${muteReason}`, {
await mutesPlugin.muteUser(userId, 0, `Mass mute: ${muteReason}`, `Mass mute: ${muteReasonWithAttachments}`, {
caseArgs: {
modId,
},
@ -89,7 +90,7 @@ export async function actualMassMuteCmd(
const successfulMuteCount = userIds.length - failedMutes.length;
if (successfulMuteCount === 0) {
// All mutes failed
sendErrorMessage(pluginData, context, "All mutes failed. Make sure the IDs are valid.");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "All mutes failed. Make sure the IDs are valid.");
} else {
// Success on all or some mutes
pluginData.getPlugin(LogsPlugin).logMassMute({
@ -98,13 +99,14 @@ export async function actualMassMuteCmd(
});
if (failedMutes.length) {
sendSuccessMessage(
pluginData,
context,
`Muted ${successfulMuteCount} users, ${failedMutes.length} failed: ${failedMutes.join(" ")}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`Muted ${successfulMuteCount} users, ${failedMutes.length} failed: ${failedMutes.join(" ")}`,
);
} else {
sendSuccessMessage(pluginData, context, `Muted ${successfulMuteCount} users successfully`);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, `Muted ${successfulMuteCount} users successfully`);
}
}
}

View file

@ -1,46 +1,45 @@
import { ChatInputCommandInteraction, GuildMember, Snowflake, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, GuildMember, Message, Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import { waitForReply } from "knub/helpers";
import { CaseTypes } from "../../../../data/CaseTypes";
import { LogType } from "../../../../data/LogType";
import {
isContextInteraction,
sendContextResponse,
sendErrorMessage,
sendSuccessMessage,
} from "../../../../pluginUtils";
import { getContextChannel, sendContextResponse } from "../../../../pluginUtils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { IgnoredEventType, ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { ignoreEvent } from "../ignoreEvent";
import { isBanned } from "../isBanned";
export async function actualMassUnbanCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
userIds: string[],
author: GuildMember,
) {
// Limit to 100 users at once (arbitrary?)
if (userIds.length > 100) {
sendErrorMessage(pluginData, context, `Can only mass-unban max 100 users at once`);
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `Can only mass-unban max 100 users at once`);
return;
}
// Ask for unban reason (cleaner this way instead of trying to cram it into the args)
sendContextResponse(context, "Unban reason? `cancel` to cancel");
const unbanReasonReply = await waitForReply(
pluginData.client,
isContextInteraction(context) ? context.channel! : context,
author.id,
);
const unbanReasonReply = await waitForReply(pluginData.client, await getContextChannel(context), author.id);
if (!unbanReasonReply || !unbanReasonReply.content || unbanReasonReply.content.toLowerCase().trim() === "cancel") {
sendErrorMessage(pluginData, context, "Cancelled");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Cancelled");
return;
}
const unbanReason = formatReasonWithAttachments(unbanReasonReply.content, [...unbanReasonReply.attachments.values()]);
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, unbanReasonReply.content)) {
return;
}
const unbanReason = await formatReasonWithMessageLinkForAttachments(pluginData, unbanReasonReply.content, context, [
...unbanReasonReply.attachments.values(),
]);
// Ignore automatic unban cases and logs for these users
// We'll create our own cases below and post a single "mass unbanned" log instead
@ -83,7 +82,9 @@ export async function actualMassUnbanCmd(
const successfulUnbanCount = userIds.length - failedUnbans.length;
if (successfulUnbanCount === 0) {
// All unbans failed - don't create a log entry and notify the user
sendErrorMessage(pluginData, context, "All unbans failed. Make sure the IDs are valid and banned.");
pluginData
.getPlugin(CommonPlugin)
.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({
@ -110,13 +111,16 @@ export async function actualMassUnbanCmd(
});
}
sendSuccessMessage(
pluginData,
context,
`Unbanned ${successfulUnbanCount} users, ${failedUnbans.length} failed:\n${failedMsg}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`Unbanned ${successfulUnbanCount} users, ${failedUnbans.length} failed:\n${failedMsg}`,
);
} else {
sendSuccessMessage(pluginData, context, `Unbanned ${successfulUnbanCount} users successfully`);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(context, `Unbanned ${successfulUnbanCount} users successfully`);
}
}
}

View file

@ -1,9 +1,8 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, TextBasedChannel, User } from "discord.js";
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 { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import {
UnknownUser,
UserNotificationMethod,
@ -11,10 +10,12 @@ import {
isDiscordAPIError,
renderUserUsername,
} from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
import { MuteResult } from "../../../Mutes/types";
import { ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
/**
* The actual function run by both !mute and !forcemute.
@ -22,23 +23,32 @@ import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
*/
export async function actualMuteCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
user: User | UnknownUser,
attachments: Array<Attachment>,
attachments: Attachment[],
mod: GuildMember,
ppId?: string,
time?: number,
reason?: string,
contactMethods?: UserNotificationMethod[],
) {
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
return;
}
const timeUntilUnmute = time && humanizeDuration(time);
const formattedReason = reason ? formatReasonWithAttachments(reason, attachments) : undefined;
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, {
muteResult = await mutesPlugin.muteUser(user.id, time, formattedReason, formattedReasonWithAttachments, {
contactMethods,
caseArgs: {
modId: mod.id,
@ -47,9 +57,11 @@ export async function actualMuteCmd(
});
} catch (e) {
if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) {
sendErrorMessage(pluginData, context, "Could not mute the user: no mute role set in config");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "Could not mute the user: no mute role set in config");
} else if (isDiscordAPIError(e) && e.code === 10007) {
sendErrorMessage(pluginData, context, "Could not mute the user: unknown member");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Could not mute the user: unknown member");
} else {
logger.error(`Failed to mute user ${user.id}: ${e.stack}`);
if (user.id == null) {
@ -57,7 +69,7 @@ export async function actualMuteCmd(
// tslint:disable-next-line:no-console
console.trace("[DEBUG] Null user.id for mute");
}
sendErrorMessage(pluginData, context, "Could not mute the user");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Could not mute the user");
}
return;
@ -92,5 +104,5 @@ export async function actualMuteCmd(
}
if (muteResult.notifyResult.text) response += ` (${muteResult.notifyResult.text})`;
sendSuccessMessage(pluginData, context, response);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, response);
}

View file

@ -1,23 +1,28 @@
import { Attachment, ChatInputCommandInteraction, TextBasedChannel, User } from "discord.js";
import { Attachment, ChatInputCommandInteraction, Message, User } from "discord.js";
import { GuildPluginData } from "knub";
import { CaseTypes } from "../../../../data/CaseTypes";
import { sendSuccessMessage } from "../../../../pluginUtils";
import { UnknownUser, renderUserUsername } from "../../../../utils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
export async function actualNoteCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: User,
attachments: Array<Attachment>,
user: User | UnknownUser,
note: string,
) {
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, note)) {
return;
}
const userName = renderUserUsername(user);
const reason = formatReasonWithAttachments(note, attachments);
const reason = await formatReasonWithMessageLinkForAttachments(pluginData, note, context, attachments);
const casesPlugin = pluginData.getPlugin(CasesPlugin);
const createdCase = await casesPlugin.createCase({
@ -34,14 +39,15 @@ export async function actualNoteCmd(
reason,
});
sendSuccessMessage(
pluginData,
context,
`Note added on **${userName}** (Case #${createdCase.case_number})`,
undefined,
undefined,
true,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`Note added on **${userName}** (Case #${createdCase.case_number})`,
undefined,
undefined,
true,
);
pluginData.state.events.emit("note", user.id, reason);
}

View file

@ -1,33 +1,40 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, Snowflake, TextBasedChannel, User } from "discord.js";
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 { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { UnknownUser } from "../../../../utils";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { LogsPlugin } from "../../../Logs/LogsPlugin";
import { IgnoredEventType, ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { ignoreEvent } from "../ignoreEvent";
export async function actualUnbanCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
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 = formatReasonWithAttachments(reason, attachments);
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 {
sendErrorMessage(pluginData, context, "Failed to unban member; are you sure they're banned?");
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(context, "Failed to unban member; are you sure they're banned?");
return;
}
@ -49,7 +56,7 @@ export async function actualUnbanCmd(
}
// Confirm the action
sendSuccessMessage(pluginData, context, `Member unbanned (Case #${createdCase.case_number})`);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, `Member unbanned (Case #${createdCase.case_number})`);
// Log the action
pluginData.getPlugin(LogsPlugin).logMemberUnban({

View file

@ -1,11 +1,11 @@
import { ChatInputCommandInteraction, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, Message } from "discord.js";
import { GuildPluginData } from "knub";
import { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { ModActionsPluginType } from "../../types";
export async function actualUnhideCaseCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
caseNumbers: number[],
) {
const failed: number[] = [];
@ -21,7 +21,7 @@ export async function actualUnhideCaseCmd(
}
if (failed.length === caseNumbers.length) {
sendErrorMessage(pluginData, context, "None of the cases were found!");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "None of the cases were found!");
return;
}
@ -31,9 +31,7 @@ export async function actualUnhideCaseCmd(
: "";
const amt = caseNumbers.length - failed.length;
sendSuccessMessage(
pluginData,
context,
`${amt} case${amt === 1 ? " is" : "s are"} no longer hidden!${failedAddendum}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(context, `${amt} case${amt === 1 ? " is" : "s are"} no longer hidden!${failedAddendum}`);
}

View file

@ -1,15 +1,16 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, TextBasedChannel, User } from "discord.js";
import { Attachment, ChatInputCommandInteraction, GuildMember, Message, User } from "discord.js";
import humanizeDuration from "humanize-duration";
import { GuildPluginData } from "knub";
import { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { UnknownUser, asSingleLine, renderUserUsername } from "../../../../utils";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { MutesPlugin } from "../../../Mutes/MutesPlugin";
import { ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
export async function actualUnmuteCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
user: User | UnknownUser,
attachments: Array<Attachment>,
mod: GuildMember,
@ -17,25 +18,31 @@ export async function actualUnmuteCmd(
time?: number,
reason?: string,
) {
const parsedReason = reason ? formatReasonWithAttachments(reason, attachments) : undefined;
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: parsedReason,
reason: formattedReason,
});
if (!result) {
sendErrorMessage(pluginData, context, "User is not muted!");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "User is not muted!");
return;
}
// Confirm the action to the moderator
if (time) {
const timeUntilUnmute = time && humanizeDuration(time);
sendSuccessMessage(
pluginData,
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(
context,
asSingleLine(`
Unmuting **${renderUserUsername(user)}**
@ -43,8 +50,7 @@ export async function actualUnmuteCmd(
`),
);
} else {
sendSuccessMessage(
pluginData,
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(
context,
asSingleLine(`
Unmuted **${renderUserUsername(user)}**

View file

@ -1,17 +1,18 @@
import { Attachment, ChatInputCommandInteraction, GuildMember, TextBasedChannel } from "discord.js";
import { Attachment, ChatInputCommandInteraction, GuildMember, Message } from "discord.js";
import { GuildPluginData } from "knub";
import { CaseTypes } from "../../../../data/CaseTypes";
import { sendErrorMessage, sendSuccessMessage } from "../../../../pluginUtils";
import { UserNotificationMethod, renderUserUsername } from "../../../../utils";
import { waitForButtonConfirm } from "../../../../utils/waitForInteraction";
import { CasesPlugin } from "../../../Cases/CasesPlugin";
import { CommonPlugin } from "../../../Common/CommonPlugin";
import { ModActionsPluginType } from "../../types";
import { formatReasonWithAttachments } from "../formatReasonWithAttachments";
import { handleAttachmentLinkDetectionAndGetRestriction } from "../attachmentLinkReaction";
import { formatReasonWithAttachments, formatReasonWithMessageLinkForAttachments } from "../formatReasonForAttachments";
import { warnMember } from "../warnMember";
export async function actualWarnCmd(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
authorId: string,
mod: GuildMember,
memberToWarn: GuildMember,
@ -19,8 +20,13 @@ export async function actualWarnCmd(
attachments: Attachment[],
contactMethods?: UserNotificationMethod[],
) {
if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, reason)) {
return;
}
const config = pluginData.config.get();
const formattedReason = formatReasonWithAttachments(reason, attachments);
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);
@ -31,12 +37,12 @@ export async function actualWarnCmd(
{ confirmText: "Yes", cancelText: "No", restrictToId: authorId },
);
if (!reply) {
sendErrorMessage(pluginData, context, "Warn cancelled by moderator");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Warn cancelled by moderator");
return;
}
}
const warnResult = await warnMember(pluginData, memberToWarn, formattedReason, {
const warnResult = await warnMember(pluginData, memberToWarn, formattedReason, formattedReasonWithAttachments, {
contactMethods,
caseArgs: {
modId: mod.id,
@ -47,15 +53,16 @@ export async function actualWarnCmd(
});
if (warnResult.status === "failed") {
sendErrorMessage(pluginData, context, "Failed to warn user");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Failed to warn user");
return;
}
const messageResultText = warnResult.notifyResult.text ? ` (${warnResult.notifyResult.text})` : "";
sendSuccessMessage(
pluginData,
context,
`Warned **${renderUserUsername(memberToWarn.user)}** (Case #${warnResult.case.case_number})${messageResultText}`,
);
pluginData
.getPlugin(CommonPlugin)
.sendSuccessMessage(
context,
`Warned **${renderUserUsername(memberToWarn.user)}** (Case #${warnResult.case.case_number})${messageResultText}`,
);
}

View file

@ -0,0 +1,51 @@
import { ChatInputCommandInteraction, Message, TextBasedChannel } from "discord.js";
import { AnyPluginData, GuildPluginData } from "knub";
import { CommonPlugin } from "../../Common/CommonPlugin";
import { ModActionsPluginType } from "../types";
export function shouldReactToAttachmentLink(pluginData: GuildPluginData<ModActionsPluginType>) {
const config = pluginData.config.get();
return !config.attachment_link_reaction || config.attachment_link_reaction !== "none";
}
export function attachmentLinkShouldRestrict(pluginData: GuildPluginData<ModActionsPluginType>) {
return pluginData.config.get().attachment_link_reaction === "restrict";
}
export function detectAttachmentLink(reason: string | null | undefined) {
return reason && /https:\/\/(cdn|media)\.discordapp\.(com|net)\/(ephemeral-)?attachments/gu.test(reason);
}
export function sendAttachmentLinkDetectionErrorMessage(
pluginData: AnyPluginData<any>,
context: TextBasedChannel | Message | ChatInputCommandInteraction,
restricted = false,
) {
const emoji = pluginData.getPlugin(CommonPlugin).getErrorEmoji();
pluginData
.getPlugin(CommonPlugin)
.sendErrorMessage(
context,
"You manually added a Discord attachment link to the reason. This link will only work for a limited time.\n" +
"You should instead **re-upload** the attachment with the command, in the same message.\n\n" +
(restricted ? `${emoji} **Command canceled.** ${emoji}` : "").trim(),
);
}
export async function handleAttachmentLinkDetectionAndGetRestriction(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | Message | ChatInputCommandInteraction,
reason: string | null | undefined,
) {
if (!shouldReactToAttachmentLink(pluginData) || !detectAttachmentLink(reason)) {
return false;
}
const restricted = attachmentLinkShouldRestrict(pluginData);
sendAttachmentLinkDetectionErrorMessage(pluginData, context, restricted);
return restricted;
}

View file

@ -30,6 +30,7 @@ export async function banUserId(
pluginData: GuildPluginData<ModActionsPluginType>,
userId: string,
reason?: string,
reasonWithAttachments?: string,
banOptions: BanOptions = {},
banTime?: number,
): Promise<BanResult> {
@ -45,7 +46,7 @@ export async function banUserId(
// Attempt to message the user *before* banning them, as doing it after may not be possible
const member = await resolveMember(pluginData.client, pluginData.guild, userId);
let notifyResult: UserNotificationResult = { method: null, success: true };
if (reason && member) {
if (reasonWithAttachments && member) {
const contactMethods = banOptions?.contactMethods
? banOptions.contactMethods
: getDefaultContactMethods(pluginData, "ban");
@ -56,7 +57,7 @@ export async function banUserId(
config.ban_message,
new TemplateSafeValueContainer({
guildName: pluginData.guild.name,
reason,
reason: reasonWithAttachments,
moderator: banOptions.caseArgs?.modId
? userToTemplateSafeUser(await resolveUser(pluginData.client, banOptions.caseArgs.modId))
: null,
@ -69,7 +70,7 @@ export async function banUserId(
config.tempban_message,
new TemplateSafeValueContainer({
guildName: pluginData.guild.name,
reason,
reason: reasonWithAttachments,
moderator: banOptions.caseArgs?.modId
? userToTemplateSafeUser(await resolveUser(pluginData.client, banOptions.caseArgs.modId))
: null,

View file

@ -10,7 +10,6 @@ import { resolveUser } from "../../../utils";
import { CasesPlugin } from "../../Cases/CasesPlugin";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { IgnoredEventType, ModActionsPluginType } from "../types";
import { formatReasonWithAttachments } from "./formatReasonWithAttachments";
import { ignoreEvent } from "./ignoreEvent";
import { isBanned } from "./isBanned";
@ -21,11 +20,9 @@ export async function clearTempban(pluginData: GuildPluginData<ModActionsPluginT
}
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, tempban.user_id);
const reason = formatReasonWithAttachments(
`Tempban timed out.
Tempbanned at: \`${tempban.created_at} UTC\``,
[],
);
const reason = `Tempban timed out.
Tempbanned at: \`${tempban.created_at} UTC\``;
try {
ignoreEvent(pluginData, IgnoredEventType.Unban, tempban.user_id);
await pluginData.guild.bans.remove(tempban.user_id as Snowflake, reason ?? undefined);

View file

@ -0,0 +1,36 @@
import { Attachment, ChatInputCommandInteraction, Message, TextBasedChannel } from "discord.js";
import { GuildPluginData } from "knub";
import { isContextMessage } from "../../../pluginUtils";
import { ModActionsPluginType } from "../types";
export async function formatReasonWithMessageLinkForAttachments(
pluginData: GuildPluginData<ModActionsPluginType>,
reason: string,
context: Message | ChatInputCommandInteraction,
attachments: Attachment[],
) {
if (isContextMessage(context)) {
return context.attachments.size > 0 ? ((reason || "") + " " + context.url).trim() : reason;
}
if (attachments.length < 1) {
return reason;
}
const attachmentChannelId = pluginData.config.get().attachment_storing_channel;
const channel = attachmentChannelId
? (pluginData.guild.channels.cache.get(attachmentChannelId) as TextBasedChannel) ?? context.channel
: context.channel;
const message = await channel!.send({
content: `Storing ${attachments.length} attachment${attachments.length === 1 ? "" : "s"}`,
files: attachments.map((a) => a.url),
});
return ((reason || "") + " " + message.url).trim();
}
export function formatReasonWithAttachments(reason: string, attachments: Attachment[]) {
const attachmentUrls = attachments.map((a) => a.url);
return ((reason || "") + " " + attachmentUrls.join(" ")).trim();
}

View file

@ -1,6 +0,0 @@
import { Attachment } from "discord.js";
export function formatReasonWithAttachments(reason: string, attachments: Attachment[]) {
const attachmentUrls = attachments.map((a) => a.url);
return ((reason || "") + " " + attachmentUrls.join(" ")).trim();
}

View file

@ -18,13 +18,14 @@ export async function kickMember(
pluginData: GuildPluginData<ModActionsPluginType>,
member: GuildMember,
reason?: string,
reasonWithAttachments?: string,
kickOptions: KickOptions = {},
): Promise<KickResult> {
const config = pluginData.config.get();
// Attempt to message the user *before* kicking them, as doing it after may not be possible
let notifyResult: UserNotificationResult = { method: null, success: true };
if (reason && member) {
if (reasonWithAttachments && member) {
const contactMethods = kickOptions?.contactMethods
? kickOptions.contactMethods
: getDefaultContactMethods(pluginData, "kick");
@ -35,7 +36,7 @@ export async function kickMember(
config.kick_message,
new TemplateSafeValueContainer({
guildName: pluginData.guild.name,
reason,
reason: reasonWithAttachments,
moderator: kickOptions.caseArgs?.modId
? userToTemplateSafeUser(await resolveUser(pluginData.client, kickOptions.caseArgs.modId))
: null,

View file

@ -1,19 +1,19 @@
import { Attachment, ChatInputCommandInteraction, TextBasedChannel, User } from "discord.js";
import { Attachment, ChatInputCommandInteraction, Message, User } from "discord.js";
import { GuildPluginData } from "knub";
import { CaseTypes } from "../../../data/CaseTypes";
import { Case } from "../../../data/entities/Case";
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
import { CasesPlugin } from "../../Cases/CasesPlugin";
import { CommonPlugin } from "../../Common/CommonPlugin";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { ModActionsPluginType } from "../types";
import { formatReasonWithAttachments } from "./formatReasonWithAttachments";
import { formatReasonWithMessageLinkForAttachments } from "./formatReasonForAttachments";
export async function updateCase(
pluginData: GuildPluginData<ModActionsPluginType>,
context: TextBasedChannel | ChatInputCommandInteraction,
context: Message | ChatInputCommandInteraction,
author: User,
caseNumber?: number,
note?: string,
note = "",
attachments: Attachment[] = [],
) {
let theCase: Case | null;
@ -24,16 +24,16 @@ export async function updateCase(
}
if (!theCase) {
sendErrorMessage(pluginData, context, "Case not found");
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Case not found");
return;
}
if (!note && attachments.length === 0) {
sendErrorMessage(pluginData, context, "Text or attachment required");
if (note.length === 0 && attachments.length === 0) {
pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Text or attachment required");
return;
}
const formattedNote = formatReasonWithAttachments(note ?? "", attachments);
const formattedNote = await formatReasonWithMessageLinkForAttachments(pluginData, note, context, attachments);
const casesPlugin = pluginData.getPlugin(CasesPlugin);
await casesPlugin.createCaseNote({
@ -49,5 +49,5 @@ export async function updateCase(
note: formattedNote,
});
sendSuccessMessage(pluginData, context, `Case \`#${theCase.case_number}\` updated`);
pluginData.getPlugin(CommonPlugin).sendSuccessMessage(context, `Case \`#${theCase.case_number}\` updated`);
}

View file

@ -1,7 +1,7 @@
import { GuildMember, Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import { CaseTypes } from "../../../data/CaseTypes";
import { isContextInteraction } from "../../../pluginUtils";
import { getContextChannel, isContextInteraction } from "../../../pluginUtils";
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter";
import { UserNotificationResult, createUserNotificationError, notifyUser, resolveUser, ucfirst } from "../../../utils";
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
@ -15,6 +15,7 @@ export async function warnMember(
pluginData: GuildPluginData<ModActionsPluginType>,
member: GuildMember,
reason: string,
reasonWithAttachments: string,
warnOptions: WarnOptions = {},
): Promise<WarnResult> {
const config = pluginData.config.get();
@ -25,7 +26,7 @@ export async function warnMember(
config.warn_message,
new TemplateSafeValueContainer({
guildName: pluginData.guild.name,
reason,
reason: reasonWithAttachments,
moderator: warnOptions.caseArgs?.modId
? userToTemplateSafeUser(await resolveUser(pluginData.client, warnOptions.caseArgs.modId))
: null,
@ -40,8 +41,10 @@ export async function warnMember(
}
if (!notifyResult.success) {
const contextIsChannel = warnOptions.retryPromptContext && !isContextInteraction(warnOptions.retryPromptContext);
const isValidChannel = contextIsChannel && pluginData.guild.channels.resolve(warnOptions.retryPromptContext!.id);
const contextIsNotInteraction =
warnOptions.retryPromptContext && !isContextInteraction(warnOptions.retryPromptContext);
const contextChannel = contextIsNotInteraction ? await getContextChannel(warnOptions.retryPromptContext!) : null;
const isValidChannel = contextIsNotInteraction && pluginData.guild.channels.resolve(contextChannel!.id);
if (!warnOptions.retryPromptContext || !isValidChannel) {
return {

View file

@ -1,4 +1,4 @@
import { ChatInputCommandInteraction, TextBasedChannel } from "discord.js";
import { ChatInputCommandInteraction, Message } from "discord.js";
import { EventEmitter } from "events";
import * as t from "io-ts";
import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand, guildPluginSlashGroup } from "knub";
@ -11,6 +11,8 @@ import { Case } from "../../data/entities/Case";
import { UserNotificationMethod, UserNotificationResult, tNullable } from "../../utils";
import { CaseArgs } from "../Cases/types";
export type AttachmentLinkReactionType = "none" | "warn" | "restrict" | null | undefined;
export const ConfigSchema = t.type({
dm_on_warn: t.boolean,
dm_on_kick: t.boolean,
@ -29,6 +31,8 @@ export const ConfigSchema = t.type({
warn_notify_threshold: t.number,
warn_notify_message: t.string,
ban_delete_message_days: t.number,
attachment_link_reaction: tNullable(t.union([t.literal("none"), t.literal("warn"), t.literal("restrict")])),
attachment_storing_channel: tNullable(t.string),
can_note: t.boolean,
can_warn: t.boolean,
can_mute: t.boolean,
@ -127,7 +131,7 @@ export type WarnMemberNotifyRetryCallback = () => boolean | Promise<boolean>;
export interface WarnOptions {
caseArgs?: Partial<CaseArgs> | null;
contactMethods?: UserNotificationMethod[] | null;
retryPromptContext?: TextBasedChannel | ChatInputCommandInteraction | null;
retryPromptContext?: Message | ChatInputCommandInteraction | null;
isAutomodAction?: boolean;
}