From e1323687a7f7540381c39b4c863d7d3177d2e119 Mon Sep 17 00:00:00 2001
From: Dark <7890309+DarkView@users.noreply.github.com>
Date: Tue, 21 Jul 2020 01:02:42 +0200
Subject: [PATCH] Migrated Persist to new Plugin structure

---
 backend/src/plugins/Persist/PersistPlugin.ts  | 33 ++++++++++++
 .../src/plugins/Persist/events/LoadDataEvt.ts | 52 +++++++++++++++++++
 .../plugins/Persist/events/StoreDataEvt.ts    | 35 +++++++++++++
 backend/src/plugins/Persist/types.ts          | 22 ++++++++
 backend/src/plugins/availablePlugins.ts       |  2 +
 5 files changed, 144 insertions(+)
 create mode 100644 backend/src/plugins/Persist/PersistPlugin.ts
 create mode 100644 backend/src/plugins/Persist/events/LoadDataEvt.ts
 create mode 100644 backend/src/plugins/Persist/events/StoreDataEvt.ts
 create mode 100644 backend/src/plugins/Persist/types.ts

diff --git a/backend/src/plugins/Persist/PersistPlugin.ts b/backend/src/plugins/Persist/PersistPlugin.ts
new file mode 100644
index 00000000..6233d696
--- /dev/null
+++ b/backend/src/plugins/Persist/PersistPlugin.ts
@@ -0,0 +1,33 @@
+import { PluginOptions } from "knub";
+import { zeppelinPlugin } from "../ZeppelinPluginBlueprint";
+import { PersistPluginType, ConfigSchema } from "./types";
+import { GuildPersistedData } from "src/data/GuildPersistedData";
+import { GuildLogs } from "src/data/GuildLogs";
+import { StoreDataEvt } from "./events/StoreDataEvt";
+import { LoadDataEvt } from "./events/LoadDataEvt";
+
+const defaultOptions: PluginOptions<PersistPluginType> = {
+  config: {
+    persisted_roles: [],
+    persist_nicknames: false,
+    persist_voice_mutes: false,
+  },
+};
+
+export const PersistPlugin = zeppelinPlugin<PersistPluginType>()("persist", {
+  configSchema: ConfigSchema,
+  defaultOptions,
+
+  // prettier-ignore
+  events: [
+    StoreDataEvt,
+    LoadDataEvt,
+  ],
+
+  onLoad(pluginData) {
+    const { state, guild } = pluginData;
+
+    state.persistedData = GuildPersistedData.getGuildInstance(guild.id);
+    state.logs = new GuildLogs(guild.id);
+  },
+});
diff --git a/backend/src/plugins/Persist/events/LoadDataEvt.ts b/backend/src/plugins/Persist/events/LoadDataEvt.ts
new file mode 100644
index 00000000..63404921
--- /dev/null
+++ b/backend/src/plugins/Persist/events/LoadDataEvt.ts
@@ -0,0 +1,52 @@
+import { persistEvent } from "../types";
+import { MemberOptions } from "eris";
+import intersection from "lodash.intersection";
+import { LogType } from "src/data/LogType";
+import { stripObjectToScalars } from "src/utils";
+
+export const LoadDataEvt = persistEvent({
+  event: "guildMemberAdd",
+
+  async listener(meta) {
+    const member = meta.args.member;
+    const pluginData = meta.pluginData;
+
+    const memberRolesLock = await pluginData.locks.acquire(`member-roles-${member.id}`);
+
+    const persistedData = await pluginData.state.persistedData.find(member.id);
+    if (!persistedData) {
+      memberRolesLock.unlock();
+      return;
+    }
+
+    const toRestore: MemberOptions = {};
+    const config = pluginData.config.getForMember(member);
+    const restoredData = [];
+
+    const persistedRoles = config.persisted_roles;
+    if (persistedRoles.length) {
+      const rolesToRestore = intersection(persistedRoles, persistedData.roles);
+      if (rolesToRestore.length) {
+        restoredData.push("roles");
+        toRestore.roles = Array.from(new Set([...rolesToRestore, ...member.roles]));
+      }
+    }
+
+    if (config.persist_nicknames && persistedData.nickname) {
+      restoredData.push("nickname");
+      toRestore.nick = persistedData.nickname;
+    }
+
+    if (restoredData.length) {
+      await member.edit(toRestore, "Restored upon rejoin");
+      await pluginData.state.persistedData.clear(member.id);
+
+      pluginData.state.logs.log(LogType.MEMBER_RESTORE, {
+        member: stripObjectToScalars(member, ["user", "roles"]),
+        restoredData: restoredData.join(", "),
+      });
+    }
+
+    memberRolesLock.unlock();
+  },
+});
diff --git a/backend/src/plugins/Persist/events/StoreDataEvt.ts b/backend/src/plugins/Persist/events/StoreDataEvt.ts
new file mode 100644
index 00000000..5de3bc8a
--- /dev/null
+++ b/backend/src/plugins/Persist/events/StoreDataEvt.ts
@@ -0,0 +1,35 @@
+import { persistEvent } from "../types";
+import { IPartialPersistData } from "src/data/GuildPersistedData";
+import { Member } from "eris";
+import intersection from "lodash.intersection";
+
+export const StoreDataEvt = persistEvent({
+  event: "guildMemberRemove",
+
+  async listener(meta) {
+    const member = meta.args.member as Member;
+    const pluginData = meta.pluginData;
+
+    let persist = false;
+    const persistData: IPartialPersistData = {};
+    const config = pluginData.config.getForUser(member.user);
+
+    const persistedRoles = config.persisted_roles;
+    if (persistedRoles.length && member.roles) {
+      const rolesToPersist = intersection(persistedRoles, member.roles);
+      if (rolesToPersist.length) {
+        persist = true;
+        persistData.roles = rolesToPersist;
+      }
+    }
+
+    if (config.persist_nicknames && member.nick) {
+      persist = true;
+      persistData.nickname = member.nick;
+    }
+
+    if (persist) {
+      pluginData.state.persistedData.set(member.id, persistData);
+    }
+  },
+});
diff --git a/backend/src/plugins/Persist/types.ts b/backend/src/plugins/Persist/types.ts
new file mode 100644
index 00000000..d7900cbd
--- /dev/null
+++ b/backend/src/plugins/Persist/types.ts
@@ -0,0 +1,22 @@
+import * as t from "io-ts";
+import { BasePluginType, eventListener } from "knub";
+import { GuildPersistedData } from "src/data/GuildPersistedData";
+import { GuildLogs } from "src/data/GuildLogs";
+
+export const ConfigSchema = t.type({
+  persisted_roles: t.array(t.string),
+  persist_nicknames: t.boolean,
+  persist_voice_mutes: t.boolean, // Deprecated, here to not break old configs
+});
+export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
+
+export interface PersistPluginType extends BasePluginType {
+  config: TConfigSchema;
+
+  state: {
+    persistedData: GuildPersistedData;
+    logs: GuildLogs;
+  };
+}
+
+export const persistEvent = eventListener<PersistPluginType>();
diff --git a/backend/src/plugins/availablePlugins.ts b/backend/src/plugins/availablePlugins.ts
index 0dce5a2c..06705dba 100644
--- a/backend/src/plugins/availablePlugins.ts
+++ b/backend/src/plugins/availablePlugins.ts
@@ -1,10 +1,12 @@
 import { UtilityPlugin } from "./Utility/UtilityPlugin";
 import { LocateUserPlugin } from "./LocateUser/LocateUserPlugin";
 import { ZeppelinPluginBlueprint } from "./ZeppelinPluginBlueprint";
+import { PersistPlugin } from "./Persist/PersistPlugin";
 
 // prettier-ignore
 export const guildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
   LocateUserPlugin,
+  PersistPlugin,
   UtilityPlugin,
 ];