import { Snowflake } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import { CaseTypes } from "../../../data/CaseTypes"; import { Mute } from "../../../data/entities/Mute"; import { AddMuteParams } from "../../../data/GuildMutes"; import { MuteTypes } from "../../../data/MuteTypes"; import { resolveMember, resolveUser } from "../../../utils"; import { CasesPlugin } from "../../Cases/CasesPlugin"; import { CaseArgs } from "../../Cases/types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { MutesPluginType, UnmuteResult } from "../types"; import { clearMute } from "./clearMute"; import { getDefaultMuteType } from "./getDefaultMuteType"; import { getTimeoutExpiryTime } from "./getTimeoutExpiryTime"; import { memberHasMutedRole } from "./memberHasMutedRole"; export async function unmuteUser( pluginData: GuildPluginData, userId: string, unmuteTime?: number, caseArgs: Partial = {}, ): Promise { const existingMute = await pluginData.state.mutes.findExistingMuteForUserId(userId); const user = await resolveUser(pluginData.client, userId); const member = await resolveMember(pluginData.client, pluginData.guild, userId, true); // Grab the fresh member so we don't have stale role info const modId = caseArgs.modId || pluginData.client.user!.id; if ( !existingMute && member && !memberHasMutedRole(pluginData, member) && !member?.communicationDisabledUntilTimestamp ) { return null; } if (unmuteTime) { // Schedule timed unmute (= just update the mute's duration) const muteExpiresAt = Date.now() + unmuteTime; const timeoutExpiresAt = getTimeoutExpiryTime(muteExpiresAt); let createdMute: Mute | null = null; if (!existingMute) { const defaultMuteType = getDefaultMuteType(pluginData); const muteParams: AddMuteParams = { userId, type: defaultMuteType, expiresAt: muteExpiresAt, }; if (defaultMuteType === MuteTypes.Role) { muteParams.muteRole = pluginData.config.get().mute_role; } else { muteParams.timeoutExpiresAt = timeoutExpiresAt; } createdMute = await pluginData.state.mutes.addMute(muteParams); } else { await pluginData.state.mutes.updateExpiryTime(userId, unmuteTime); } // Update timeout if (existingMute?.type === MuteTypes.Timeout || createdMute?.type === MuteTypes.Timeout) { await member?.disableCommunicationUntil(timeoutExpiresAt); await pluginData.state.mutes.updateTimeoutExpiresAt(userId, timeoutExpiresAt); } } else { // Unmute immediately clearMute(pluginData, existingMute); } const timeUntilUnmute = unmuteTime && humanizeDuration(unmuteTime); // Create a case const noteDetails: string[] = []; if (unmuteTime) { noteDetails.push(`Scheduled unmute in ${timeUntilUnmute}`); } else { noteDetails.push(`Unmuted immediately`); } if (!existingMute) { noteDetails.push(`Removed external mute`); } const casesPlugin = pluginData.getPlugin(CasesPlugin); const createdCase = await casesPlugin.createCase({ ...caseArgs, userId, modId, type: CaseTypes.Unmute, noteDetails, }); // Log the action const mod = await pluginData.client.users.fetch(modId as Snowflake); if (unmuteTime) { pluginData.getPlugin(LogsPlugin).logMemberTimedUnmute({ mod, user, caseNumber: createdCase.case_number, time: timeUntilUnmute, reason: caseArgs.reason ?? "", }); } else { pluginData.getPlugin(LogsPlugin).logMemberUnmute({ mod, user, caseNumber: createdCase.case_number, reason: caseArgs.reason ?? "", }); } if (!unmuteTime) { // If the member was unmuted, not just scheduled to be unmuted, fire the unmute event as well // Scheduled unmutes have their event fired in clearExpiredMutes() pluginData.state.events.emit("unmute", user.id, caseArgs.reason); } return { case: createdCase, }; }