mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-10 12:25:02 +00:00
Migrate ModActions to new Plugin structure !!mutes dont work!!
This commit is contained in:
parent
ebcb28261b
commit
fd56664984
29 changed files with 1213 additions and 16 deletions
90
backend/src/plugins/ModActions/commands/AddCaseCmd.ts
Normal file
90
backend/src/plugins/ModActions/commands/AddCaseCmd.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage, hasPermission, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember, stripObjectToScalars } from "../../../utils";
|
||||
import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments";
|
||||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import { CasesPlugin } from "src/plugins/Cases/CasesPlugin";
|
||||
import { Case } from "src/data/entities/Case";
|
||||
import { LogType } from "src/data/LogType";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
};
|
||||
|
||||
export const AddCaseCmd = modActionsCommand({
|
||||
trigger: "addcase",
|
||||
permission: "can_addcase",
|
||||
description: "Add an arbitrary case to the specified user without taking any action",
|
||||
|
||||
signature: [
|
||||
{
|
||||
type: ct.string(),
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
// 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 add case on this user: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
// The moderator who did the action is the message author or, if used, the specified -mod
|
||||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!hasPermission(pluginData, "can_act_as_other", { message: msg })) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No permission for -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
mod = args.mod;
|
||||
}
|
||||
|
||||
// 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");
|
||||
return;
|
||||
}
|
||||
|
||||
const reason = formatReasonWithAttachments(args.reason, msg.attachments);
|
||||
|
||||
// Create the case
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const theCase: Case = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: mod.id,
|
||||
type: CaseTypes[type],
|
||||
reason,
|
||||
ppId: mod.id !== msg.author.id ? msg.author.id : null,
|
||||
});
|
||||
|
||||
if (user) {
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`Case #${theCase.case_number} created for **${user.username}#${user.discriminator}**`,
|
||||
);
|
||||
} else {
|
||||
sendSuccessMessage(pluginData, msg.channel, `Case #${theCase.case_number} created`);
|
||||
}
|
||||
|
||||
// Log the action
|
||||
pluginData.state.serverLogs.log(LogType.CASE_CREATE, {
|
||||
mod: stripObjectToScalars(mod.user),
|
||||
userId: user.id,
|
||||
caseNum: theCase.case_number,
|
||||
caseType: type.toUpperCase(),
|
||||
reason,
|
||||
});
|
||||
},
|
||||
});
|
97
backend/src/plugins/ModActions/commands/BanCmd.ts
Normal file
97
backend/src/plugins/ModActions/commands/BanCmd.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { modActionsCommand, IgnoredEventType } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage, hasPermission, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember } from "../../../utils";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromArgs";
|
||||
import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments";
|
||||
import { banUserId } from "../functions/banUserId";
|
||||
import { ignoreEvent } from "../functions/ignoreEvent";
|
||||
import { LogType } from "src/data/LogType";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
notify: ct.string({ option: true }),
|
||||
"notify-channel": ct.textChannel({ option: true }),
|
||||
"delete-deays": ct.number({ option: true, shortcut: "d" }),
|
||||
};
|
||||
|
||||
export const BanCmd = modActionsCommand({
|
||||
trigger: "ban",
|
||||
permission: "can_ban",
|
||||
description: "Ban the specified member",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
|
||||
if (!memberToBan) {
|
||||
const banned = await isBanned(pluginData, user.id);
|
||||
if (banned) {
|
||||
sendErrorMessage(pluginData, msg.channel, `User is already banned`);
|
||||
} else {
|
||||
sendErrorMessage(pluginData, msg.channel, `User not found on the server`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're allowed to ban this member
|
||||
if (!canActOn(pluginData, msg.member, memberToBan)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot ban: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
// The moderator who did the action is the message author or, if used, the specified -mod
|
||||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!hasPermission(pluginData, "can_act_as_other", { message: msg, channelId: msg.channel.id })) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No permission for -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
mod = args.mod;
|
||||
}
|
||||
|
||||
let contactMethods;
|
||||
try {
|
||||
contactMethods = readContactMethodsFromArgs(args);
|
||||
} catch (e) {
|
||||
sendErrorMessage(pluginData, msg.channel, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteMessageDays = args["delete-days"] ?? pluginData.config.getForMessage(msg).ban_delete_message_days;
|
||||
const reason = formatReasonWithAttachments(args.reason, msg.attachments);
|
||||
const banResult = await banUserId(pluginData, memberToBan.id, reason, {
|
||||
contactMethods,
|
||||
caseArgs: {
|
||||
modId: mod.id,
|
||||
ppId: mod.id !== msg.author.id ? msg.author.id : null,
|
||||
},
|
||||
deleteMessageDays,
|
||||
});
|
||||
|
||||
if (banResult.status === "failed") {
|
||||
sendErrorMessage(pluginData, msg.channel, `Failed to ban member`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm the action to the moderator
|
||||
let response = `Banned **${memberToBan.user.username}#${memberToBan.user.discriminator}** (Case #${banResult.case.case_number})`;
|
||||
|
||||
if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`;
|
||||
sendSuccessMessage(pluginData, msg.channel, response);
|
||||
},
|
||||
});
|
29
backend/src/plugins/ModActions/commands/CaseCmd.ts
Normal file
29
backend/src/plugins/ModActions/commands/CaseCmd.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage } from "../../../pluginUtils";
|
||||
import { CasesPlugin } from "src/plugins/Cases/CasesPlugin";
|
||||
|
||||
export const CaseCmd = modActionsCommand({
|
||||
trigger: "case",
|
||||
permission: "can_view",
|
||||
description: "Show information about a specific case",
|
||||
|
||||
signature: [
|
||||
{
|
||||
caseNumber: ct.number(),
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(args.caseNumber);
|
||||
|
||||
if (!theCase) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Case not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const embed = await casesPlugin.getCaseEmbed(theCase.id);
|
||||
msg.channel.createMessage(embed);
|
||||
},
|
||||
});
|
43
backend/src/plugins/ModActions/commands/CasesModCmd.ts
Normal file
43
backend/src/plugins/ModActions/commands/CasesModCmd.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage } from "../../../pluginUtils";
|
||||
import { trimLines, createChunkedMessage } from "src/utils";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
};
|
||||
|
||||
export const CasesModCmd = modActionsCommand({
|
||||
trigger: "cases",
|
||||
permission: "can_view",
|
||||
description: "Show the most recent 5 cases by the specified -mod",
|
||||
|
||||
signature: [
|
||||
{
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const modId = args.mod ? args.mod.id : msg.author.id;
|
||||
const recentCases = await pluginData.state.cases.with("notes").getRecentByModId(modId, 5);
|
||||
|
||||
const mod = pluginData.client.users.get(modId);
|
||||
const modName = mod ? `${mod.username}#${mod.discriminator}` : modId;
|
||||
|
||||
if (recentCases.length === 0) {
|
||||
sendErrorMessage(pluginData, msg.channel, `No cases by **${modName}**`);
|
||||
} else {
|
||||
const lines = recentCases.map(c => pluginData.state.cases.getSummaryText(c));
|
||||
const finalMessage = trimLines(`
|
||||
Most recent 5 cases by **${modName}**:
|
||||
|
||||
${lines.join("\n")}
|
||||
|
||||
Use the \`case <num>\` command to see more info about individual cases
|
||||
Use the \`cases <user>\` command to see a specific user's cases
|
||||
`);
|
||||
createChunkedMessage(msg.channel, finalMessage);
|
||||
}
|
||||
},
|
||||
});
|
84
backend/src/plugins/ModActions/commands/CasesUserCmd.ts
Normal file
84
backend/src/plugins/ModActions/commands/CasesUserCmd.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage } from "../../../pluginUtils";
|
||||
import { CasesPlugin } from "src/plugins/Cases/CasesPlugin";
|
||||
import { UnknownUser, multiSorter, trimLines, createChunkedMessage, resolveUser } from "src/utils";
|
||||
|
||||
const opts = {
|
||||
expand: ct.bool({ option: true, isSwitch: true, shortcut: "e" }),
|
||||
hidden: ct.bool({ option: true, isSwitch: true, shortcut: "h" }),
|
||||
};
|
||||
|
||||
export const CasesUserCmd = modActionsCommand({
|
||||
trigger: "cases",
|
||||
permission: "can_view",
|
||||
description: "Show a list of cases the specified user has",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
const cases = await pluginData.state.cases.with("notes").getByUserId(user.id);
|
||||
const normalCases = cases.filter(c => !c.is_hidden);
|
||||
const hiddenCases = cases.filter(c => c.is_hidden);
|
||||
|
||||
const userName =
|
||||
user instanceof UnknownUser && cases.length
|
||||
? cases[cases.length - 1].user_name
|
||||
: `${user.username}#${user.discriminator}`;
|
||||
|
||||
if (cases.length === 0) {
|
||||
msg.channel.createMessage(`No cases found for **${userName}**`);
|
||||
} else {
|
||||
const casesToDisplay = args.hidden ? cases : normalCases;
|
||||
|
||||
if (args.expand) {
|
||||
if (casesToDisplay.length > 8) {
|
||||
msg.channel.createMessage("Too many cases for expanded view. Please use compact view instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Expanded view (= individual case embeds)
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
for (const theCase of casesToDisplay) {
|
||||
const embed = await casesPlugin.getCaseEmbed(theCase.id);
|
||||
msg.channel.createMessage(embed);
|
||||
}
|
||||
} else {
|
||||
// Compact view (= regular message with a preview of each case)
|
||||
const lines = [];
|
||||
for (const theCase of casesToDisplay) {
|
||||
theCase.notes.sort(multiSorter(["created_at", "id"]));
|
||||
const caseSummary = pluginData.state.cases.getSummaryText(theCase);
|
||||
lines.push(caseSummary);
|
||||
}
|
||||
|
||||
if (!args.hidden && hiddenCases.length) {
|
||||
if (hiddenCases.length === 1) {
|
||||
lines.push(`*+${hiddenCases.length} hidden case, use "-hidden" to show it*`);
|
||||
} else {
|
||||
lines.push(`*+${hiddenCases.length} hidden cases, use "-hidden" to show them*`);
|
||||
}
|
||||
}
|
||||
|
||||
const finalMessage = trimLines(`
|
||||
Cases for **${userName}**:
|
||||
|
||||
${lines.join("\n")}
|
||||
|
||||
Use the \`case <num>\` command to see more info about individual cases
|
||||
`);
|
||||
|
||||
createChunkedMessage(msg.channel, finalMessage);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
93
backend/src/plugins/ModActions/commands/ForcebanCmd.ts
Normal file
93
backend/src/plugins/ModActions/commands/ForcebanCmd.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { modActionsCommand, IgnoredEventType } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage, hasPermission, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember, stripObjectToScalars } from "../../../utils";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromArgs";
|
||||
import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments";
|
||||
import { banUserId } from "../functions/banUserId";
|
||||
import { ignoreEvent } from "../functions/ignoreEvent";
|
||||
import { LogType } from "src/data/LogType";
|
||||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import { CasesPlugin } from "src/plugins/Cases/CasesPlugin";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
};
|
||||
|
||||
export const ForcebanCmd = modActionsCommand({
|
||||
trigger: "forceban",
|
||||
permission: "can_ban",
|
||||
description: "Force-ban the specified user, even if they aren't on the server",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
// 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");
|
||||
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`);
|
||||
return;
|
||||
}
|
||||
|
||||
// The moderator who did the action is the message author or, if used, the specified -mod
|
||||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!hasPermission(pluginData, "can_act_as_other", { message: msg })) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No permission for -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
mod = args.mod;
|
||||
}
|
||||
|
||||
const reason = formatReasonWithAttachments(args.reason, msg.attachments);
|
||||
|
||||
ignoreEvent(pluginData, IgnoredEventType.Ban, user.id);
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, user.id);
|
||||
|
||||
try {
|
||||
await pluginData.guild.banMember(user.id, 1);
|
||||
} catch (e) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Failed to forceban member");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a case
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const createdCase = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: mod.id,
|
||||
type: CaseTypes.Ban,
|
||||
reason,
|
||||
ppId: mod.id !== msg.author.id ? msg.author.id : null,
|
||||
});
|
||||
|
||||
// Confirm the action
|
||||
sendSuccessMessage(pluginData, msg.channel, `Member forcebanned (Case #${createdCase.case_number})`);
|
||||
|
||||
// Log the action
|
||||
pluginData.state.serverLogs.log(LogType.MEMBER_FORCEBAN, {
|
||||
mod: stripObjectToScalars(mod.user),
|
||||
userId: user.id,
|
||||
reason,
|
||||
});
|
||||
},
|
||||
});
|
48
backend/src/plugins/ModActions/commands/ForcemuteCmd.ts
Normal file
48
backend/src/plugins/ModActions/commands/ForcemuteCmd.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveMember, resolveUser } from "../../../utils";
|
||||
import { actualMuteUserCmd } from "../functions/actualMuteUserCmd";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
notify: ct.string({ option: true }),
|
||||
"notify-channel": ct.textChannel({ option: true }),
|
||||
};
|
||||
|
||||
export const ForcemuteCmd = modActionsCommand({
|
||||
trigger: "forcemute",
|
||||
permission: "can_mute",
|
||||
description: "Force-mute the specified user, even if they're not on the server",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
time: ct.delay(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
const memberToMute = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
|
||||
// Make sure we're allowed to mute this user
|
||||
if (memberToMute && !canActOn(pluginData, msg.member, memberToMute)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot mute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
actualMuteUserCmd(pluginData, user, msg, args);
|
||||
},
|
||||
});
|
53
backend/src/plugins/ModActions/commands/ForceunmuteCmd.ts
Normal file
53
backend/src/plugins/ModActions/commands/ForceunmuteCmd.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember } from "../../../utils";
|
||||
import { actualUnmuteCmd } from "../functions/actualUnmuteUserCmd";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
};
|
||||
|
||||
export const ForceUnmuteCmd = modActionsCommand({
|
||||
trigger: "forceunmute",
|
||||
permission: "can_mute",
|
||||
description: "Force-unmute the specified user, even if they're not on the server",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
time: ct.delay(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
// 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");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the server member to unmute
|
||||
const memberToUnmute = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
|
||||
// Make sure we're allowed to unmute this member
|
||||
if (memberToUnmute && !canActOn(pluginData, msg.member, memberToUnmute)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
actualUnmuteCmd(pluginData, user, msg, args);
|
||||
},
|
||||
});
|
30
backend/src/plugins/ModActions/commands/HideCaseCmd.ts
Normal file
30
backend/src/plugins/ModActions/commands/HideCaseCmd.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
|
||||
export const HideCaseCmd = modActionsCommand({
|
||||
trigger: ["hide", "hidecase", "hide_case"],
|
||||
permission: "can_hidecase",
|
||||
description: "Hide the specified case so it doesn't appear in !cases or !info",
|
||||
|
||||
signature: [
|
||||
{
|
||||
caseNum: ct.number(),
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(args.caseNum);
|
||||
if (!theCase) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Case not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
await pluginData.state.cases.setHidden(theCase.id, true);
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`Case #${theCase.case_number} is now hidden! Use \`unhidecase\` to unhide it.`,
|
||||
);
|
||||
},
|
||||
});
|
35
backend/src/plugins/ModActions/commands/KickCmd.ts
Normal file
35
backend/src/plugins/ModActions/commands/KickCmd.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember } from "../../../utils";
|
||||
import { MutesPlugin } from "src/plugins/Mutes/MutesPlugin";
|
||||
import { actualUnmuteCmd } from "../functions/actualUnmuteUserCmd";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { plugin } from "knub";
|
||||
import { actualKickMemberCmd } from "../functions/actualKickMemberCmd";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
notify: ct.string({ option: true }),
|
||||
"notify-channel": ct.textChannel({ option: true }),
|
||||
clean: ct.bool({ option: true, isSwitch: true }),
|
||||
};
|
||||
|
||||
export const KickCmd = modActionsCommand({
|
||||
trigger: "kick",
|
||||
permission: "can_kick",
|
||||
description: "Kick the specified member",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
actualKickMemberCmd(pluginData, msg, args);
|
||||
},
|
||||
});
|
109
backend/src/plugins/ModActions/commands/MassBanCmd.ts
Normal file
109
backend/src/plugins/ModActions/commands/MassBanCmd.ts
Normal file
|
@ -0,0 +1,109 @@
|
|||
import { modActionsCommand, IgnoredEventType } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage, hasPermission, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember, stripObjectToScalars } from "../../../utils";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromArgs";
|
||||
import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments";
|
||||
import { banUserId } from "../functions/banUserId";
|
||||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import { TextChannel } from "eris";
|
||||
import { waitForReply } from "knub/dist/helpers";
|
||||
import { ignoreEvent } from "../functions/ignoreEvent";
|
||||
import { CasesPlugin } from "src/plugins/Cases/CasesPlugin";
|
||||
import { LogType } from "src/data/LogType";
|
||||
|
||||
export const MassbanCmd = modActionsCommand({
|
||||
trigger: "massban",
|
||||
permission: "can_massban",
|
||||
description: "Mass-ban a list of user IDs",
|
||||
|
||||
signature: [
|
||||
{
|
||||
userIds: ct.string({ rest: true }),
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
// Limit to 100 users at once (arbitrary?)
|
||||
if (args.userIds.length > 100) {
|
||||
sendErrorMessage(pluginData, msg.channel, `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)
|
||||
msg.channel.createMessage("Ban reason? `cancel` to cancel");
|
||||
const banReasonReply = await waitForReply(pluginData.client, msg.channel as TextChannel, msg.author.id);
|
||||
if (!banReasonReply || !banReasonReply.content || banReasonReply.content.toLowerCase().trim() === "cancel") {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
const banReason = formatReasonWithAttachments(banReasonReply.content, msg.attachments);
|
||||
|
||||
// Verify we can act on each of the users specified
|
||||
for (const userId of args.userIds) {
|
||||
const member = pluginData.guild.members.get(userId); // TODO: Get members on demand?
|
||||
if (member && !canActOn(pluginData, msg.member, member)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot massban one or more users: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore automatic ban cases and logs for these users
|
||||
// We'll create our own cases below and post a single "mass banned" log instead
|
||||
args.userIds.forEach(userId => {
|
||||
// Use longer timeouts since this can take a while
|
||||
ignoreEvent(pluginData, IgnoredEventType.Ban, userId, 120 * 1000);
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, userId, 120 * 1000);
|
||||
});
|
||||
|
||||
// Show a loading indicator since this can take a while
|
||||
const loadingMsg = await msg.channel.createMessage("Banning...");
|
||||
|
||||
// Ban each user and count failed bans (if any)
|
||||
const failedBans = [];
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
for (const userId of args.userIds) {
|
||||
try {
|
||||
await pluginData.guild.banMember(userId, 1);
|
||||
|
||||
await casesPlugin.createCase({
|
||||
userId,
|
||||
modId: msg.author.id,
|
||||
type: CaseTypes.Ban,
|
||||
reason: `Mass ban: ${banReason}`,
|
||||
postInCaseLogOverride: false,
|
||||
});
|
||||
} catch (e) {
|
||||
failedBans.push(userId);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear loading indicator
|
||||
loadingMsg.delete();
|
||||
|
||||
const successfulBanCount = args.userIds.length - failedBans.length;
|
||||
if (successfulBanCount === 0) {
|
||||
// All bans failed - don't create a log entry and notify the user
|
||||
sendErrorMessage(pluginData, msg.channel, "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.state.serverLogs.log(LogType.MASSBAN, {
|
||||
mod: stripObjectToScalars(msg.author),
|
||||
count: successfulBanCount,
|
||||
reason: banReason,
|
||||
});
|
||||
|
||||
if (failedBans.length) {
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`Banned ${successfulBanCount} users, ${failedBans.length} failed: ${failedBans.join(" ")}`,
|
||||
);
|
||||
} else {
|
||||
sendSuccessMessage(pluginData, msg.channel, `Banned ${successfulBanCount} users successfully`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
35
backend/src/plugins/ModActions/commands/SoftbanCommand.ts
Normal file
35
backend/src/plugins/ModActions/commands/SoftbanCommand.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { trimPluginDescription } from "../../../utils";
|
||||
import { actualKickMemberCmd } from "../functions/actualKickMemberCmd";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
notify: ct.string({ option: true }),
|
||||
"notify-channel": ct.textChannel({ option: true }),
|
||||
};
|
||||
|
||||
export const SoftbanCmd = modActionsCommand({
|
||||
trigger: "softban",
|
||||
permission: "can_kick",
|
||||
description: trimPluginDescription(`
|
||||
"Softban" the specified user by banning and immediately unbanning them. Effectively a kick with message deletions.
|
||||
This command will be removed in the future, please use kick with the \`- clean\` argument instead
|
||||
`),
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
await actualKickMemberCmd(pluginData, msg, { clean: true, ...args });
|
||||
await msg.channel.createMessage(
|
||||
"Softban will be removed in the future - please use the kick command with the `-clean` argument instead!",
|
||||
);
|
||||
},
|
||||
});
|
76
backend/src/plugins/ModActions/commands/UnbanCmd.ts
Normal file
76
backend/src/plugins/ModActions/commands/UnbanCmd.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { modActionsCommand, IgnoredEventType } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, hasPermission, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, stripObjectToScalars } from "../../../utils";
|
||||
import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments";
|
||||
import { LogType } from "src/data/LogType";
|
||||
import { ignoreEvent } from "../functions/ignoreEvent";
|
||||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import { CasesPlugin } from "src/plugins/Cases/CasesPlugin";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
};
|
||||
|
||||
export const UnbanCmd = modActionsCommand({
|
||||
trigger: "unban",
|
||||
permission: "can_ban",
|
||||
description: "Unban the specified member",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
|
||||
// The moderator who did the action is the message author or, if used, the specified -mod
|
||||
let mod = msg.member;
|
||||
if (args.mod) {
|
||||
if (!hasPermission(pluginData, "can_act_as_other", { message: msg, channelId: msg.channel.id })) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No permission for -mod");
|
||||
return;
|
||||
}
|
||||
|
||||
mod = args.mod;
|
||||
}
|
||||
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, user.id);
|
||||
|
||||
try {
|
||||
ignoreEvent(pluginData, IgnoredEventType.Unban, user.id);
|
||||
await pluginData.guild.unbanMember(user.id);
|
||||
} catch (e) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Failed to unban member; are you sure they're banned?");
|
||||
return;
|
||||
}
|
||||
|
||||
const reason = formatReasonWithAttachments(args.reason, msg.attachments);
|
||||
|
||||
// Create a case
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const createdCase = await casesPlugin.createCase({
|
||||
userId: user.id,
|
||||
modId: mod.id,
|
||||
type: CaseTypes.Unban,
|
||||
reason,
|
||||
ppId: mod.id !== msg.author.id ? msg.author.id : null,
|
||||
});
|
||||
|
||||
// Confirm the action
|
||||
sendSuccessMessage(pluginData, msg.channel, `Member unbanned (Case #${createdCase.case_number})`);
|
||||
|
||||
// Log the action
|
||||
pluginData.state.serverLogs.log(LogType.MEMBER_UNBAN, {
|
||||
mod: stripObjectToScalars(mod.user),
|
||||
userId: user.id,
|
||||
reason,
|
||||
});
|
||||
},
|
||||
});
|
26
backend/src/plugins/ModActions/commands/UnhideCaseCmd.ts
Normal file
26
backend/src/plugins/ModActions/commands/UnhideCaseCmd.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
|
||||
export const UnhideCaseCmd = modActionsCommand({
|
||||
trigger: ["unhide", "unhidecase", "unhide_case"],
|
||||
permission: "can_hidecase",
|
||||
description: "Un-hide the specified case, making it appear in !cases and !info again",
|
||||
|
||||
signature: [
|
||||
{
|
||||
caseNum: ct.number(),
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(args.caseNum);
|
||||
if (!theCase) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Case not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
await pluginData.state.cases.setHidden(theCase.id, false);
|
||||
sendSuccessMessage(pluginData, msg.channel, `Case #${theCase.case_number} is no longer hidden!`);
|
||||
},
|
||||
});
|
76
backend/src/plugins/ModActions/commands/UnmuteCmd.ts
Normal file
76
backend/src/plugins/ModActions/commands/UnmuteCmd.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { canActOn, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, resolveMember } from "../../../utils";
|
||||
import { MutesPlugin } from "src/plugins/Mutes/MutesPlugin";
|
||||
import { actualUnmuteCmd } from "../functions/actualUnmuteUserCmd";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { plugin } from "knub";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
};
|
||||
|
||||
export const UnmuteCmd = modActionsCommand({
|
||||
trigger: "unmute",
|
||||
permission: "can_mute",
|
||||
description: "Unmute the specified member",
|
||||
|
||||
signature: [
|
||||
{
|
||||
user: ct.string(),
|
||||
time: ct.delay(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
{
|
||||
user: ct.string(),
|
||||
reason: ct.string({ required: false, catchAll: true }),
|
||||
|
||||
...opts,
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
const user = await resolveUser(pluginData.client, args.user);
|
||||
if (!user) return sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||
const memberToUnmute = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
const mutesPlugin = pluginData.getPlugin(MutesPlugin);
|
||||
const hasMuteRole = memberToUnmute && mutesPlugin.hasMutedRole(memberToUnmute);
|
||||
|
||||
// Check if they're muted in the first place
|
||||
if (!(await pluginData.state.mutes.isMuted(args.user)) && !hasMuteRole) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: member is not muted");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!memberToUnmute) {
|
||||
const banned = await isBanned(pluginData, memberToUnmute.id);
|
||||
const prefix = pluginData.guildConfig.prefix;
|
||||
if (banned) {
|
||||
sendErrorMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`,
|
||||
);
|
||||
} else {
|
||||
sendErrorMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`User is not on the server. Use \`${prefix}forceunmute\` to unmute them anyway.`,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're allowed to unmute this member
|
||||
if (memberToUnmute && !canActOn(pluginData, msg.member, memberToUnmute)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot unmute: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
||||
actualUnmuteCmd(pluginData, user, msg, args);
|
||||
},
|
||||
});
|
|
@ -13,10 +13,15 @@ export const UpdateCmd = modActionsCommand({
|
|||
description:
|
||||
"Update the specified case (or, if case number is omitted, your latest case) by adding more notes/details to it",
|
||||
|
||||
signature: {
|
||||
caseNumber: ct.number(),
|
||||
note: ct.string({ required: false, catchAll: true }),
|
||||
},
|
||||
signature: [
|
||||
{
|
||||
caseNumber: ct.number(),
|
||||
note: ct.string({ required: false, catchAll: true }),
|
||||
},
|
||||
{
|
||||
note: ct.string({ catchAll: true }),
|
||||
},
|
||||
],
|
||||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
let theCase: Case;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue