From 8600cc4195504a6b540718dcf6195b89e394ea1c Mon Sep 17 00:00:00 2001
From: Dark <7890309+DarkView@users.noreply.github.com>
Date: Wed, 29 Jul 2020 02:32:01 +0200
Subject: [PATCH] Update Massmute to newest version... again

---
 backend/src/data/DefaultLogMessages.json      |   1 +
 backend/src/data/LogType.ts                   |   1 +
 .../plugins/ModActions/ModActionsPlugin.ts    |   4 +
 .../ModActions/commands/MassmuteCmd.ts        | 105 ++++++++++++++++++
 backend/src/plugins/ModActions/types.ts       |   1 +
 5 files changed, 112 insertions(+)
 create mode 100644 backend/src/plugins/ModActions/commands/MassmuteCmd.ts

diff --git a/backend/src/data/DefaultLogMessages.json b/backend/src/data/DefaultLogMessages.json
index 62703878..3fa918a2 100644
--- a/backend/src/data/DefaultLogMessages.json
+++ b/backend/src/data/DefaultLogMessages.json
@@ -49,6 +49,7 @@
   "CASE_CREATE": "✏ {userMention(mod)} manually created new **{caseType}** case (#{caseNum})",
 
   "MASSBAN": "⚒ {userMention(mod)} massbanned {count} users",
+  "MASSMUTE": "📢🚫 {userMention(mod)} massmuted {count} users",
 
   "MEMBER_JOIN_WITH_PRIOR_RECORDS": "⚠ {userMention(member)} joined with prior records. Recent cases:\n{recentCaseSummary}",
 
diff --git a/backend/src/data/LogType.ts b/backend/src/data/LogType.ts
index 2a6d6f70..8ae42cd7 100644
--- a/backend/src/data/LogType.ts
+++ b/backend/src/data/LogType.ts
@@ -40,6 +40,7 @@ export enum LogType {
   CASE_CREATE,
 
   MASSBAN,
+  MASSMUTE,
 
   MEMBER_TIMED_MUTE,
   MEMBER_TIMED_UNMUTE,
diff --git a/backend/src/plugins/ModActions/ModActionsPlugin.ts b/backend/src/plugins/ModActions/ModActionsPlugin.ts
index 76fc260f..fea68069 100644
--- a/backend/src/plugins/ModActions/ModActionsPlugin.ts
+++ b/backend/src/plugins/ModActions/ModActionsPlugin.ts
@@ -32,6 +32,7 @@ import { warnMember } from "./functions/warnMember";
 import { Member } from "eris";
 import { kickMember } from "./functions/kickMember";
 import { banUserId } from "./functions/banUserId";
+import { MassmuteCmd } from "./commands/MassmuteCmd";
 import { trimPluginDescription } from "../../utils";
 
 const defaultOptions = {
@@ -62,6 +63,7 @@ const defaultOptions = {
     can_view: false,
     can_addcase: false,
     can_massban: false,
+    can_massmute: false,
     can_hidecase: false,
     can_act_as_other: false,
   },
@@ -82,6 +84,7 @@ const defaultOptions = {
       level: ">=100",
       config: {
         can_massban: true,
+        can_massmute: true,
         can_hidecase: true,
         can_act_as_other: true,
       },
@@ -124,6 +127,7 @@ export const ModActionsPlugin = zeppelinPlugin<ModActionsPluginType>()("mod_acti
     UnbanCmd,
     ForcebanCmd,
     MassbanCmd,
+    MassmuteCmd,
     AddCaseCmd,
     CaseCmd,
     CasesUserCmd,
diff --git a/backend/src/plugins/ModActions/commands/MassmuteCmd.ts b/backend/src/plugins/ModActions/commands/MassmuteCmd.ts
new file mode 100644
index 00000000..5b446c03
--- /dev/null
+++ b/backend/src/plugins/ModActions/commands/MassmuteCmd.ts
@@ -0,0 +1,105 @@
+import { modActionsCommand } from "../types";
+import { commandTypeHelpers as ct } from "../../../commandTypes";
+import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
+import { stripObjectToScalars } from "../../../utils";
+import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments";
+import { TextChannel } from "eris";
+import { waitForReply } from "knub/dist/helpers";
+import { LogType } from "src/data/LogType";
+import { logger } from "src/logger";
+import { MutesPlugin } from "src/plugins/Mutes/MutesPlugin";
+
+export const MassmuteCmd = modActionsCommand({
+  trigger: "massmute",
+  permission: "can_massmute",
+  description: "Mass-mute 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 massmute max 100 users at once`);
+      return;
+    }
+
+    // Ask for mute reason
+    msg.channel.createMessage("Mute reason? `cancel` to cancel");
+    const muteReasonReceived = await waitForReply(pluginData.client, msg.channel as TextChannel, msg.author.id);
+    if (
+      !muteReasonReceived ||
+      !muteReasonReceived.content ||
+      muteReasonReceived.content.toLowerCase().trim() === "cancel"
+    ) {
+      sendErrorMessage(pluginData, msg.channel, "Cancelled");
+      return;
+    }
+
+    const muteReason = formatReasonWithAttachments(muteReasonReceived.content, msg.attachments);
+
+    // Verify we can act upon all users
+    for (const userId of args.userIds) {
+      const member = pluginData.guild.members.get(userId);
+      if (member && !canActOn(pluginData, msg.member, member)) {
+        sendErrorMessage(pluginData, msg.channel, "Cannot massmute one or more users: insufficient permissions");
+        return;
+      }
+    }
+
+    // Ignore automatic mute cases and logs for these users
+    // We'll create our own cases below and post a single "mass muted" log instead
+    args.userIds.forEach(userId => {
+      // Use longer timeouts since this can take a while
+      pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_MUTE, userId, 120 * 1000);
+    });
+
+    // Show loading indicator
+    const loadingMsg = await msg.channel.createMessage("Muting...");
+
+    // Mute everyone and count fails
+    const modId = msg.author.id;
+    const failedMutes = [];
+    const mutesPlugin = pluginData.getPlugin(MutesPlugin);
+    for (const userId of args.userIds) {
+      try {
+        await mutesPlugin.muteUser(userId, 0, `Mass mute: ${muteReason}`, {
+          caseArgs: {
+            modId,
+          },
+        });
+      } catch (e) {
+        logger.info(e);
+        failedMutes.push(userId);
+      }
+    }
+
+    // Clear loading indicator
+    loadingMsg.delete();
+
+    const successfulMuteCount = args.userIds.length - failedMutes.length;
+    if (successfulMuteCount === 0) {
+      // All mutes failed
+      sendErrorMessage(pluginData, msg.channel, "All mutes failed. Make sure the IDs are valid.");
+    } else {
+      // Success on all or some mutes
+      pluginData.state.serverLogs.log(LogType.MASSMUTE, {
+        mod: stripObjectToScalars(msg.author),
+        count: successfulMuteCount,
+      });
+
+      if (failedMutes.length) {
+        sendSuccessMessage(
+          pluginData,
+          msg.channel,
+          `Muted ${successfulMuteCount} users, ${failedMutes.length} failed: ${failedMutes.join(" ")}`,
+        );
+      } else {
+        sendSuccessMessage(pluginData, msg.channel, `Muted ${successfulMuteCount} users successfully`);
+      }
+    }
+  },
+});
diff --git a/backend/src/plugins/ModActions/types.ts b/backend/src/plugins/ModActions/types.ts
index 34ef8669..74546de6 100644
--- a/backend/src/plugins/ModActions/types.ts
+++ b/backend/src/plugins/ModActions/types.ts
@@ -33,6 +33,7 @@ export const ConfigSchema = t.type({
   can_view: t.boolean,
   can_addcase: t.boolean,
   can_massban: t.boolean,
+  can_massmute: t.boolean,
   can_hidecase: t.boolean,
   can_act_as_other: t.boolean,
 });