3
0
Fork 0
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:
Dark 2020-07-24 02:25:33 +02:00
parent ebcb28261b
commit fd56664984
29 changed files with 1213 additions and 16 deletions

View 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,
});
},
});

View 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);
},
});

View 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);
},
});

View 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);
}
},
});

View 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);
}
}
},
});

View 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,
});
},
});

View 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);
},
});

View 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);
},
});

View 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.`,
);
},
});

View 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);
},
});

View 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`);
}
}
},
});

View 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!",
);
},
});

View 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,
});
},
});

View 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!`);
},
});

View 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);
},
});

View file

@ -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;