From 514e93aa23f8d462e1e2be642ae8487afe363c06 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sat, 24 Jun 2023 09:53:06 +0000 Subject: [PATCH] feat(persist): reapply persisted data after delay This enables better interoperability with other bots that apply roles on join. This is because bots that apply roles on join often run into a race condition where they're both setting the member's roles at the same time without knowing about the other, which results in one bot's roles overriding the other one's. Reapplying Zeppelin's roles after a delay should ensure that persisted roles get applied properly even in these cases. --- .../src/plugins/Persist/events/LoadDataEvt.ts | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/backend/src/plugins/Persist/events/LoadDataEvt.ts b/backend/src/plugins/Persist/events/LoadDataEvt.ts index e5b880cc..3fb1dcdb 100644 --- a/backend/src/plugins/Persist/events/LoadDataEvt.ts +++ b/backend/src/plugins/Persist/events/LoadDataEvt.ts @@ -1,14 +1,51 @@ -import { PermissionFlagsBits } from "discord.js"; +import { GuildMember, PermissionFlagsBits } from "discord.js"; +import { GuildPluginData } from "knub"; import intersection from "lodash.intersection"; +import { PersistedData } from "../../../data/entities/PersistedData"; +import { SECONDS } from "../../../utils"; import { canAssignRole } from "../../../utils/canAssignRole"; import { getMissingPermissions } from "../../../utils/getMissingPermissions"; import { missingPermissionError } from "../../../utils/missingPermissionError"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin"; -import { persistEvt } from "../types"; +import { PersistPluginType, persistEvt } from "../types"; const p = PermissionFlagsBits; +async function applyPersistedData( + pluginData: GuildPluginData, + persistedData: PersistedData, + member: GuildMember, +): Promise { + const config = await pluginData.config.getForMember(member); + const guildRoles = Array.from(pluginData.guild.roles.cache.keys()); + const restoredData: string[] = []; + + const persistedRoles = config.persisted_roles; + if (persistedRoles.length) { + const roleManager = pluginData.getPlugin(RoleManagerPlugin); + const rolesToRestore = intersection(persistedRoles, persistedData.roles, guildRoles).filter( + (roleId) => !member.roles.cache.has(roleId), + ); + + if (rolesToRestore.length) { + restoredData.push("roles"); + for (const roleId of rolesToRestore) { + roleManager.addRole(member.id, roleId); + } + } + } + + if (config.persist_nicknames && persistedData.nickname && member.nickname !== persistedData.nickname) { + restoredData.push("nickname"); + await member.edit({ + nick: persistedData.nickname, + }); + } + + return restoredData; +} + export const LoadDataEvt = persistEvt({ event: "guildMemberAdd", @@ -23,7 +60,6 @@ export const LoadDataEvt = persistEvt({ await pluginData.state.persistedData.clear(member.id); const config = await pluginData.config.getForMember(member); - const restoredData: string[] = []; // Check permissions const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; @@ -52,25 +88,11 @@ export const LoadDataEvt = persistEvt({ } } - const persistedRoles = config.persisted_roles; - if (persistedRoles.length) { - const roleManager = pluginData.getPlugin(RoleManagerPlugin); - const rolesToRestore = intersection(persistedRoles, persistedData.roles, guildRoles); - - if (rolesToRestore.length) { - restoredData.push("roles"); - for (const roleId of rolesToRestore) { - roleManager.addRole(member.id, roleId); - } - } - } - - if (config.persist_nicknames && persistedData.nickname) { - restoredData.push("nickname"); - await member.edit({ - nick: persistedData.nickname, - }); - } + const restoredData = await applyPersistedData(pluginData, persistedData, member); + setTimeout(() => { + // Reapply persisted data after a while for better interop with other bots that restore roles + void applyPersistedData(pluginData, persistedData, member); + }, 5 * SECONDS); if (restoredData.length) { pluginData.getPlugin(LogsPlugin).logMemberRestore({