diff --git a/backend/src/plugins/Automod/actions/addRoles.ts b/backend/src/plugins/Automod/actions/addRoles.ts index 4da915e7..976300a5 100644 --- a/backend/src/plugins/Automod/actions/addRoles.ts +++ b/backend/src/plugins/Automod/actions/addRoles.ts @@ -9,6 +9,7 @@ import { getMissingPermissions } from "../../../utils/getMissingPermissions"; import { canAssignRole } from "../../../utils/canAssignRole"; import { missingPermissionError } from "../../../utils/missingPermissionError"; import { ignoreRoleChange } from "../functions/ignoredRoleChanges"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; const p = Constants.Permissions; @@ -64,7 +65,7 @@ export const AddRolesAction = automodAction({ return; } - const memberRolesLock = await pluginData.locks.acquire(`member-roles-${member.id}`); + const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member)); const rolesArr = Array.from(memberRoles.values()); await member.edit({ @@ -72,7 +73,7 @@ export const AddRolesAction = automodAction({ }); member.roles = rolesArr; // Make sure we know of the new roles internally as well - memberRolesLock.unlock(); + memberRoleLock.unlock(); }), ); }, diff --git a/backend/src/plugins/Automod/actions/removeRoles.ts b/backend/src/plugins/Automod/actions/removeRoles.ts index 6049cbf2..c6c74e49 100644 --- a/backend/src/plugins/Automod/actions/removeRoles.ts +++ b/backend/src/plugins/Automod/actions/removeRoles.ts @@ -10,6 +10,7 @@ import { missingPermissionError } from "../../../utils/missingPermissionError"; import { canAssignRole } from "../../../utils/canAssignRole"; import { Constants } from "eris"; import { ignoreRoleChange } from "../functions/ignoredRoleChanges"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; const p = Constants.Permissions; @@ -66,7 +67,7 @@ export const RemoveRolesAction = automodAction({ return; } - const memberRolesLock = await pluginData.locks.acquire(`member-roles-${member.id}`); + const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member)); const rolesArr = Array.from(memberRoles.values()); await member.edit({ @@ -74,7 +75,7 @@ export const RemoveRolesAction = automodAction({ }); member.roles = rolesArr; // Make sure we know of the new roles internally as well - memberRolesLock.unlock(); + memberRoleLock.unlock(); }), ); }, diff --git a/backend/src/plugins/Censor/util/onMessageCreate.ts b/backend/src/plugins/Censor/util/onMessageCreate.ts index 2042126d..fb9cfaeb 100644 --- a/backend/src/plugins/Censor/util/onMessageCreate.ts +++ b/backend/src/plugins/Censor/util/onMessageCreate.ts @@ -2,10 +2,11 @@ import { GuildPluginData } from "knub"; import { CensorPluginType } from "../types"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { applyFiltersToMsg } from "./applyFiltersToMsg"; +import { messageLock } from "../../../utils/lockNameHelpers"; export async function onMessageCreate(pluginData: GuildPluginData, savedMessage: SavedMessage) { if (savedMessage.is_bot) return; - const lock = await pluginData.locks.acquire(`message-${savedMessage.id}`); + const lock = await pluginData.locks.acquire(messageLock(savedMessage)); const wasDeleted = await applyFiltersToMsg(pluginData, savedMessage); diff --git a/backend/src/plugins/Censor/util/onMessageUpdate.ts b/backend/src/plugins/Censor/util/onMessageUpdate.ts index 4c279caa..7afd4c17 100644 --- a/backend/src/plugins/Censor/util/onMessageUpdate.ts +++ b/backend/src/plugins/Censor/util/onMessageUpdate.ts @@ -2,10 +2,11 @@ import { GuildPluginData } from "knub"; import { CensorPluginType } from "../types"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { applyFiltersToMsg } from "./applyFiltersToMsg"; +import { messageLock } from "../../../utils/lockNameHelpers"; export async function onMessageUpdate(pluginData: GuildPluginData, savedMessage: SavedMessage) { if (savedMessage.is_bot) return; - const lock = await pluginData.locks.acquire(`message-${savedMessage.id}`); + const lock = await pluginData.locks.acquire(messageLock(savedMessage)); const wasDeleted = await applyFiltersToMsg(pluginData, savedMessage); diff --git a/backend/src/plugins/Counters/functions/changeCounterValue.ts b/backend/src/plugins/Counters/functions/changeCounterValue.ts index be214eb7..d5da6825 100644 --- a/backend/src/plugins/Counters/functions/changeCounterValue.ts +++ b/backend/src/plugins/Counters/functions/changeCounterValue.ts @@ -1,4 +1,5 @@ import { GuildPluginData } from "knub"; +import { counterIdLock } from "../../../utils/lockNameHelpers"; import { CountersPluginType } from "../types"; import { checkCounterTrigger } from "./checkCounterTrigger"; import { checkReverseCounterTrigger } from "./checkReverseCounterTrigger"; @@ -28,7 +29,7 @@ export async function changeCounterValue( userId = counter.per_user ? userId : null; const counterId = pluginData.state.counterIds[counterName]; - const lock = await pluginData.locks.acquire(counterId.toString()); + const lock = await pluginData.locks.acquire(counterIdLock(counterId)); await pluginData.state.counters.changeCounterValue(counterId, channelId, userId, change); diff --git a/backend/src/plugins/Counters/functions/decayCounter.ts b/backend/src/plugins/Counters/functions/decayCounter.ts index 175cb158..7db4cef3 100644 --- a/backend/src/plugins/Counters/functions/decayCounter.ts +++ b/backend/src/plugins/Counters/functions/decayCounter.ts @@ -2,6 +2,7 @@ import { GuildPluginData } from "knub"; import { CountersPluginType } from "../types"; import { checkAllValuesForTrigger } from "./checkAllValuesForTrigger"; import { checkAllValuesForReverseTrigger } from "./checkAllValuesForReverseTrigger"; +import { counterIdLock } from "../../../utils/lockNameHelpers"; export async function decayCounter( pluginData: GuildPluginData, @@ -16,7 +17,7 @@ export async function decayCounter( } const counterId = pluginData.state.counterIds[counterName]; - const lock = await pluginData.locks.acquire(counterId.toString()); + const lock = await pluginData.locks.acquire(counterIdLock(counterId)); await pluginData.state.counters.decay(counterId, decayPeriodMS, decayAmount); diff --git a/backend/src/plugins/Counters/functions/setCounterValue.ts b/backend/src/plugins/Counters/functions/setCounterValue.ts index 2eefed8f..697c8503 100644 --- a/backend/src/plugins/Counters/functions/setCounterValue.ts +++ b/backend/src/plugins/Counters/functions/setCounterValue.ts @@ -1,4 +1,5 @@ import { GuildPluginData } from "knub"; +import { counterIdLock } from "../../../utils/lockNameHelpers"; import { CountersPluginType } from "../types"; import { checkCounterTrigger } from "./checkCounterTrigger"; import { checkReverseCounterTrigger } from "./checkReverseCounterTrigger"; @@ -25,7 +26,7 @@ export async function setCounterValue( } const counterId = pluginData.state.counterIds[counterName]; - const lock = await pluginData.locks.acquire(counterId.toString()); + const lock = await pluginData.locks.acquire(counterIdLock(counterId)); await pluginData.state.counters.setCounterValue(counterId, channelId, userId, value); diff --git a/backend/src/plugins/ModActions/commands/AddCaseCmd.ts b/backend/src/plugins/ModActions/commands/AddCaseCmd.ts index 7bd06bf8..3bed2911 100644 --- a/backend/src/plugins/ModActions/commands/AddCaseCmd.ts +++ b/backend/src/plugins/ModActions/commands/AddCaseCmd.ts @@ -45,7 +45,7 @@ export const AddCaseCmd = modActionsCmd({ let mod = msg.member; if (args.mod) { if (!hasPermission(pluginData, "can_act_as_other", { message: msg })) { - sendErrorMessage(pluginData, msg.channel, "No permission for -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } diff --git a/backend/src/plugins/ModActions/commands/BanCmd.ts b/backend/src/plugins/ModActions/commands/BanCmd.ts index 4fe9561e..da7156c2 100644 --- a/backend/src/plugins/ModActions/commands/BanCmd.ts +++ b/backend/src/plugins/ModActions/commands/BanCmd.ts @@ -8,9 +8,10 @@ import { formatReasonWithAttachments } from "../functions/formatReasonWithAttach import { banUserId } from "../functions/banUserId"; import { getMemberLevel, waitForReaction } from "knub/dist/helpers"; import humanizeDuration from "humanize-duration"; -import { CasesPlugin } from "src/plugins/Cases/CasesPlugin"; -import { CaseTypes } from "src/data/CaseTypes"; -import { LogType } from "src/data/LogType"; +import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; +import { CaseTypes } from "../../../data/CaseTypes"; +import { LogType } from "../../../data/LogType"; +import { banLock } from "../../../utils/lockNameHelpers"; const opts = { mod: ct.member({ option: true }), @@ -54,7 +55,7 @@ export const BanCmd = modActionsCmd({ 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"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } @@ -62,7 +63,7 @@ export const BanCmd = modActionsCmd({ } // acquire a lock because of the needed user-inputs below (if banned/not on server) - const lock = await pluginData.locks.acquire(`ban-${user.id}`); + const lock = await pluginData.locks.acquire(banLock(user)); let forceban = false; const existingTempban = await pluginData.state.tempbans.findExistingTempbanForUserId(user.id); const banned = await isBanned(pluginData, user.id); diff --git a/backend/src/plugins/ModActions/commands/ForcebanCmd.ts b/backend/src/plugins/ModActions/commands/ForcebanCmd.ts index 907237ef..afda940e 100644 --- a/backend/src/plugins/ModActions/commands/ForcebanCmd.ts +++ b/backend/src/plugins/ModActions/commands/ForcebanCmd.ts @@ -54,7 +54,7 @@ export const ForcebanCmd = modActionsCmd({ let mod = msg.member; if (args.mod) { if (!hasPermission(pluginData, "can_act_as_other", { message: msg })) { - sendErrorMessage(pluginData, msg.channel, "No permission for -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } diff --git a/backend/src/plugins/ModActions/commands/MuteCmd.ts b/backend/src/plugins/ModActions/commands/MuteCmd.ts index ada23e2f..3aa96c38 100644 --- a/backend/src/plugins/ModActions/commands/MuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/MuteCmd.ts @@ -6,7 +6,7 @@ import { formatReasonWithAttachments } from "../functions/formatReasonWithAttach import { CasesPlugin } from "../../Cases/CasesPlugin"; import { LogType } from "../../../data/LogType"; import { CaseTypes } from "../../../data/CaseTypes"; -import { errorMessage, resolveMember, resolveUser, stripObjectToScalars } from "../../../utils"; +import { errorMessage, noop, resolveMember, resolveUser, stripObjectToScalars } from "../../../utils"; import { isBanned } from "../functions/isBanned"; import { waitForReaction } from "knub/dist/helpers"; import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromArgs"; @@ -59,15 +59,18 @@ export const MuteCmd = modActionsCmd({ msg.channel, `User is banned. Use \`${prefix}forcemute\` if you want to mute them anyway.`, ); + return; } else { - sendErrorMessage( - pluginData, - msg.channel, - `User is not on the server. Use \`${prefix}forcemute\` if you want to mute them anyway.`, - ); - } + // Ask the mod if we should upgrade to a forcemute as the user is not on the server + const notOnServerMsg = await msg.channel.createMessage("User not found on the server, forcemute instead?"); + const reply = await waitForReaction(pluginData.client, notOnServerMsg, ["✅", "❌"], msg.author.id); - return; + notOnServerMsg.delete().catch(noop); + if (!reply || reply.name === "❌") { + sendErrorMessage(pluginData, msg.channel, "User not on server, mute cancelled by moderator"); + return; + } + } } // Make sure we're allowed to mute this member diff --git a/backend/src/plugins/ModActions/commands/UnbanCmd.ts b/backend/src/plugins/ModActions/commands/UnbanCmd.ts index 80cac4f2..0d5abc1c 100644 --- a/backend/src/plugins/ModActions/commands/UnbanCmd.ts +++ b/backend/src/plugins/ModActions/commands/UnbanCmd.ts @@ -37,7 +37,7 @@ export const UnbanCmd = modActionsCmd({ 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"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } diff --git a/backend/src/plugins/ModActions/commands/UnmuteCmd.ts b/backend/src/plugins/ModActions/commands/UnmuteCmd.ts index f6efdd39..c294517c 100644 --- a/backend/src/plugins/ModActions/commands/UnmuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/UnmuteCmd.ts @@ -1,10 +1,11 @@ import { modActionsCmd } from "../types"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { canActOn, sendErrorMessage } from "../../../pluginUtils"; -import { resolveUser, resolveMember } from "../../../utils"; +import { resolveUser, resolveMember, noop } from "../../../utils"; import { MutesPlugin } from "../../../plugins/Mutes/MutesPlugin"; import { actualUnmuteCmd } from "../functions/actualUnmuteUserCmd"; import { isBanned } from "../functions/isBanned"; +import { waitForReaction } from "knub/dist/helpers"; const opts = { mod: ct.member({ option: true }), @@ -57,15 +58,18 @@ export const UnmuteCmd = modActionsCmd({ msg.channel, `User is banned. Use \`${prefix}forceunmute\` to unmute them anyway.`, ); + return; } else { - sendErrorMessage( - pluginData, - msg.channel, - `User is not on the server. Use \`${prefix}forceunmute\` to unmute them anyway.`, - ); - } + // Ask the mod if we should upgrade to a forceunmute as the user is not on the server + const notOnServerMsg = await msg.channel.createMessage("User not found on the server, forceunmute instead?"); + const reply = await waitForReaction(pluginData.client, notOnServerMsg, ["✅", "❌"], msg.author.id); - return; + notOnServerMsg.delete().catch(noop); + if (!reply || reply.name === "❌") { + sendErrorMessage(pluginData, msg.channel, "User not on server, unmute cancelled by moderator"); + return; + } + } } // Make sure we're allowed to unmute this member diff --git a/backend/src/plugins/ModActions/commands/UpdateCmd.ts b/backend/src/plugins/ModActions/commands/UpdateCmd.ts index 3a55463a..3a048b42 100644 --- a/backend/src/plugins/ModActions/commands/UpdateCmd.ts +++ b/backend/src/plugins/ModActions/commands/UpdateCmd.ts @@ -8,7 +8,7 @@ import { LogType } from "../../../data/LogType"; import { CaseTypes } from "../../../data/CaseTypes"; export const UpdateCmd = modActionsCmd({ - trigger: "update", + trigger: ["update", "reason"], permission: "can_note", description: "Update the specified case (or, if case number is omitted, your latest case) by adding more notes/details to it", diff --git a/backend/src/plugins/ModActions/commands/WarnCmd.ts b/backend/src/plugins/ModActions/commands/WarnCmd.ts index 8ac19baa..e36dbb86 100644 --- a/backend/src/plugins/ModActions/commands/WarnCmd.ts +++ b/backend/src/plugins/ModActions/commands/WarnCmd.ts @@ -57,7 +57,7 @@ export const WarnCmd = modActionsCmd({ let mod = msg.member; if (args.mod) { if (!hasPermission(pluginData, "can_act_as_other", { message: msg })) { - msg.channel.createMessage(errorMessage("No permission for -mod")); + msg.channel.createMessage(errorMessage("You don't have permission to use -mod")); return; } diff --git a/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts b/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts index 18ee2137..e467212c 100644 --- a/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts @@ -52,7 +52,7 @@ export async function actualKickMemberCmd( let mod = msg.member; if (args.mod) { if (!hasPermission(pluginData.config.getForMessage(msg), "can_act_as_other")) { - sendErrorMessage(pluginData, msg.channel, "No permission for -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } diff --git a/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts b/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts index 6d640ec6..19c13477 100644 --- a/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts @@ -28,7 +28,7 @@ export async function actualMuteUserCmd( if (args.mod) { if (!hasPermission(pluginData, "can_act_as_other", { message: msg })) { - sendErrorMessage(pluginData, msg.channel, "No permission for -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } diff --git a/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts b/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts index 0faee47c..7931aac1 100644 --- a/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts @@ -19,7 +19,7 @@ export async function actualUnmuteCmd( if (args.mod) { if (!hasPermission(pluginData, "can_act_as_other", { message: msg, channelId: msg.channel.id })) { - sendErrorMessage(pluginData, msg.channel, "No permission for -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } diff --git a/backend/src/plugins/Mutes/MutesPlugin.ts b/backend/src/plugins/Mutes/MutesPlugin.ts index ac55acc6..36fb425e 100644 --- a/backend/src/plugins/Mutes/MutesPlugin.ts +++ b/backend/src/plugins/Mutes/MutesPlugin.ts @@ -25,6 +25,7 @@ const defaultOptions = { config: { mute_role: null, move_to_voice_channel: null, + kick_from_voice_channel: false, dm_on_mute: false, dm_on_update: false, diff --git a/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts b/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts index 890636a6..2e57e8da 100644 --- a/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts +++ b/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts @@ -1,6 +1,7 @@ import { mutesEvt } from "../types"; import { LogType } from "../../../data/LogType"; import { stripObjectToScalars } from "../../../utils"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; /** * Reapply active mutes on join @@ -11,9 +12,9 @@ export const ReapplyActiveMuteOnJoinEvt = mutesEvt("guildMemberAdd", async ({ pl const muteRole = pluginData.config.get().mute_role; if (muteRole) { - const memberRolesLock = await pluginData.locks.acquire(`member-roles-${member.id}`); + const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member)); await member.addRole(muteRole); - memberRolesLock.unlock(); + memberRoleLock.unlock(); } pluginData.state.serverLogs.log(LogType.MEMBER_MUTE_REJOIN, { diff --git a/backend/src/plugins/Mutes/functions/muteUser.ts b/backend/src/plugins/Mutes/functions/muteUser.ts index 2f8e4e28..29ba54ab 100644 --- a/backend/src/plugins/Mutes/functions/muteUser.ts +++ b/backend/src/plugins/Mutes/functions/muteUser.ts @@ -17,8 +17,8 @@ import { CasesPlugin } from "../../Cases/CasesPlugin"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; import { Case } from "../../../data/entities/Case"; -import { sendErrorMessage } from "src/pluginUtils"; -import { LogsPlugin } from "src/plugins/Logs/LogsPlugin"; +import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin"; +import { muteLock } from "../../../utils/lockNameHelpers"; export async function muteUser( pluginData: GuildPluginData, @@ -29,7 +29,7 @@ export async function muteUser( removeRolesOnMuteOverride: boolean | string[] | null = null, restoreRolesOnMuteOverride: boolean | string[] | null = null, ) { - const lock = await pluginData.locks.acquire(`mute-${userId}`); + const lock = await pluginData.locks.acquire(muteLock({ id: userId })); const muteRole = pluginData.config.get().mute_role; if (!muteRole) { @@ -120,11 +120,12 @@ export async function muteUser( } // If enabled, move the user to the mute voice channel (e.g. afk - just to apply the voice perms from the mute role) - const moveToVoiceChannelId = pluginData.config.get().move_to_voice_channel; - if (moveToVoiceChannelId) { + const cfg = pluginData.config.get(); + const moveToVoiceChannel = cfg.kick_from_voice_channel ? null : cfg.move_to_voice_channel; + if (moveToVoiceChannel || cfg.kick_from_voice_channel) { // TODO: Add back the voiceState check once we figure out how to get voice state for guild members that are loaded on-demand try { - await member.edit({ channelID: moveToVoiceChannelId }); + await member.edit({ channelID: moveToVoiceChannel }); } catch (e) {} // tslint:disable-line } } diff --git a/backend/src/plugins/Mutes/types.ts b/backend/src/plugins/Mutes/types.ts index 72ea52c2..7da2cb0d 100644 --- a/backend/src/plugins/Mutes/types.ts +++ b/backend/src/plugins/Mutes/types.ts @@ -15,6 +15,7 @@ import { EventEmitter } from "events"; export const ConfigSchema = t.type({ mute_role: tNullable(t.string), move_to_voice_channel: tNullable(t.string), + kick_from_voice_channel: t.boolean, dm_on_mute: t.boolean, dm_on_update: t.boolean, diff --git a/backend/src/plugins/Persist/events/LoadDataEvt.ts b/backend/src/plugins/Persist/events/LoadDataEvt.ts index e88cb333..bd356acb 100644 --- a/backend/src/plugins/Persist/events/LoadDataEvt.ts +++ b/backend/src/plugins/Persist/events/LoadDataEvt.ts @@ -7,6 +7,7 @@ import { getMissingPermissions } from "../../../utils/getMissingPermissions"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { missingPermissionError } from "../../../utils/missingPermissionError"; import { canAssignRole } from "../../../utils/canAssignRole"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; const p = Constants.Permissions; @@ -17,11 +18,11 @@ export const LoadDataEvt = persistEvt({ const member = meta.args.member; const pluginData = meta.pluginData; - const memberRolesLock = await pluginData.locks.acquire(`member-roles-${member.id}`); + const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member)); const persistedData = await pluginData.state.persistedData.find(member.id); if (!persistedData) { - memberRolesLock.unlock(); + memberRoleLock.unlock(); return; } @@ -79,6 +80,6 @@ export const LoadDataEvt = persistEvt({ }); } - memberRolesLock.unlock(); + memberRoleLock.unlock(); }, }); diff --git a/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts b/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts index 2b2dfd0d..00ae6108 100644 --- a/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts +++ b/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts @@ -2,6 +2,7 @@ import { GuildPluginData } from "knub"; import { ReactionRolesPluginType, RoleChangeMode, PendingMemberRoleChanges } from "../types"; import { resolveMember } from "../../../utils"; import { logger } from "../../../logger"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; const ROLE_CHANGE_BATCH_DEBOUNCE_TIME = 1500; @@ -18,7 +19,7 @@ export async function addMemberPendingRoleChange( applyFn: async () => { pluginData.state.pendingRoleChanges.delete(memberId); - const lock = await pluginData.locks.acquire(`member-roles-${memberId}`); + const lock = await pluginData.locks.acquire(memberRolesLock({ id: memberId })); const member = await resolveMember(pluginData.client, pluginData.guild, memberId); if (member) { diff --git a/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts b/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts index 2c9a1d0f..8fe86f04 100644 --- a/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts +++ b/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts @@ -6,6 +6,7 @@ import { splitRoleNames } from "../util/splitRoleNames"; import { normalizeRoleNames } from "../util/normalizeRoleNames"; import { findMatchingRoles } from "../util/findMatchingRoles"; import { Role } from "eris"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; export const RoleAddCmd = selfGrantableRolesCmd({ trigger: ["role", "role add"], @@ -16,7 +17,7 @@ export const RoleAddCmd = selfGrantableRolesCmd({ }, async run({ message: msg, args, pluginData }) { - const lock = await pluginData.locks.acquire(`grantableRoles:${msg.author.id}`); + const lock = await pluginData.locks.acquire(memberRolesLock(msg.author)); const applyingEntries = getApplyingEntries(pluginData, msg); if (applyingEntries.length === 0) { diff --git a/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts b/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts index 7cabb3c4..c2011e05 100644 --- a/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts +++ b/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts @@ -5,6 +5,7 @@ import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { splitRoleNames } from "../util/splitRoleNames"; import { normalizeRoleNames } from "../util/normalizeRoleNames"; import { findMatchingRoles } from "../util/findMatchingRoles"; +import { memberRolesLock } from "../../../utils/lockNameHelpers"; export const RoleRemoveCmd = selfGrantableRolesCmd({ trigger: "role remove", @@ -15,7 +16,7 @@ export const RoleRemoveCmd = selfGrantableRolesCmd({ }, async run({ message: msg, args, pluginData }) { - const lock = await pluginData.locks.acquire(`grantableRoles:${msg.author.id}`); + const lock = await pluginData.locks.acquire(memberRolesLock(msg.author)); const applyingEntries = getApplyingEntries(pluginData, msg); if (applyingEntries.length === 0) { diff --git a/backend/src/plugins/Slowmode/util/onMessageCreate.ts b/backend/src/plugins/Slowmode/util/onMessageCreate.ts index 8e1b8564..4f0c3989 100644 --- a/backend/src/plugins/Slowmode/util/onMessageCreate.ts +++ b/backend/src/plugins/Slowmode/util/onMessageCreate.ts @@ -10,6 +10,7 @@ import { BOT_SLOWMODE_PERMISSIONS } from "../requiredPermissions"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { LogType } from "../../../data/LogType"; import { missingPermissionError } from "../../../utils/missingPermissionError"; +import { messageLock } from "../../../utils/lockNameHelpers"; export async function onMessageCreate(pluginData: GuildPluginData, msg: SavedMessage) { if (msg.is_bot) return; @@ -18,7 +19,7 @@ export async function onMessageCreate(pluginData: GuildPluginData()("starb stars_required: 5 ~~~ + ### Basic starboard with custom color + Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226), with the given color (0x87CEEB). + + ~~~yml + starboard: + config: + boards: + basic: + channel_id: "604342689038729226" + stars_required: 5 + color: 0x87CEEB + ~~~ + ### Custom star emoji This is identical to the basic starboard above, but accepts two emoji: the regular star and a custom :mrvnSmile: emoji diff --git a/backend/src/plugins/Starboard/events/StarboardReactionAddEvt.ts b/backend/src/plugins/Starboard/events/StarboardReactionAddEvt.ts index b6142655..6c6cf93a 100644 --- a/backend/src/plugins/Starboard/events/StarboardReactionAddEvt.ts +++ b/backend/src/plugins/Starboard/events/StarboardReactionAddEvt.ts @@ -3,6 +3,7 @@ import { Message, TextChannel } from "eris"; import { UnknownUser, resolveMember, noop, resolveUser } from "../../../utils"; import { saveMessageToStarboard } from "../util/saveMessageToStarboard"; import { updateStarboardMessageStarCount } from "../util/updateStarboardMessageStarCount"; +import { allStarboardsLock } from "../../../utils/lockNameHelpers"; export const StarboardReactionAddEvt = starboardEvt({ event: "messageReactionAdd", @@ -36,7 +37,7 @@ export const StarboardReactionAddEvt = starboardEvt({ categoryId: (msg.channel as TextChannel).parentID, }); - const boardLock = await pluginData.locks.acquire(`starboards`); + const boardLock = await pluginData.locks.acquire(allStarboardsLock()); const applicableStarboards = Object.values(config.boards) .filter(board => board.enabled) diff --git a/backend/src/plugins/Starboard/events/StarboardReactionRemoveEvts.ts b/backend/src/plugins/Starboard/events/StarboardReactionRemoveEvts.ts index 07f85670..90160a4a 100644 --- a/backend/src/plugins/Starboard/events/StarboardReactionRemoveEvts.ts +++ b/backend/src/plugins/Starboard/events/StarboardReactionRemoveEvts.ts @@ -1,10 +1,11 @@ +import { allStarboardsLock } from "../../../utils/lockNameHelpers"; import { starboardEvt } from "../types"; export const StarboardReactionRemoveEvt = starboardEvt({ event: "messageReactionRemove", async listener(meta) { - const boardLock = await meta.pluginData.locks.acquire(`starboards`); + const boardLock = await meta.pluginData.locks.acquire(allStarboardsLock()); await meta.pluginData.state.starboardReactions.deleteStarboardReaction(meta.args.message.id, meta.args.member.id); boardLock.unlock(); }, @@ -14,7 +15,7 @@ export const StarboardReactionRemoveAllEvt = starboardEvt({ event: "messageReactionRemoveAll", async listener(meta) { - const boardLock = await meta.pluginData.locks.acquire(`starboards`); + const boardLock = await meta.pluginData.locks.acquire(allStarboardsLock()); await meta.pluginData.state.starboardReactions.deleteAllStarboardReactionsForMessageId(meta.args.message.id); boardLock.unlock(); }, diff --git a/backend/src/plugins/Starboard/types.ts b/backend/src/plugins/Starboard/types.ts index 8c94f415..b2118f55 100644 --- a/backend/src/plugins/Starboard/types.ts +++ b/backend/src/plugins/Starboard/types.ts @@ -12,6 +12,7 @@ const StarboardOpts = t.type({ copy_full_embed: tNullable(t.boolean), enabled: tNullable(t.boolean), show_star_count: t.boolean, + color: t.number, }); export type TStarboardOpts = t.TypeOf; @@ -27,6 +28,7 @@ export const defaultStarboardOpts: Partial = { star_emoji: ["⭐"], enabled: true, show_star_count: true, + color: 0, }; export interface StarboardPluginType extends BasePluginType { diff --git a/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts b/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts index d63b519c..8ea900e2 100644 --- a/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts +++ b/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts @@ -8,7 +8,7 @@ const videoAttachmentExtensions = ["mp4", "mkv", "mov"]; type StarboardEmbed = EmbedWith<"footer" | "author" | "fields" | "timestamp">; -export function createStarboardEmbedFromMessage(msg: Message, copyFullEmbed: boolean): StarboardEmbed { +export function createStarboardEmbedFromMessage(msg: Message, copyFullEmbed: boolean, color: number): StarboardEmbed { const embed: StarboardEmbed = { footer: { text: `#${(msg.channel as GuildChannel).name}`, @@ -18,6 +18,7 @@ export function createStarboardEmbedFromMessage(msg: Message, copyFullEmbed: boo }, fields: [], timestamp: new Date(msg.timestamp).toISOString(), + color, }; if (msg.author.avatarURL) { diff --git a/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts b/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts index f30beffc..e7587abd 100644 --- a/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts +++ b/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts @@ -16,7 +16,7 @@ export async function saveMessageToStarboard( if (!channel) return; const starCount = (await pluginData.state.starboardReactions.getAllReactionsForMessageId(msg.id)).length; - const embed = createStarboardEmbedFromMessage(msg, Boolean(starboard.copy_full_embed)); + const embed = createStarboardEmbedFromMessage(msg, Boolean(starboard.copy_full_embed), starboard.color); embed.fields!.push(createStarboardPseudoFooterForMessage(starboard, msg, starboard.star_emoji![0], starCount)); const starboardMessage = await (channel as TextChannel).createMessage({ embed }); diff --git a/backend/src/plugins/Tags/TagsPlugin.ts b/backend/src/plugins/Tags/TagsPlugin.ts index e8bb8541..f6aa89e1 100644 --- a/backend/src/plugins/Tags/TagsPlugin.ts +++ b/backend/src/plugins/Tags/TagsPlugin.ts @@ -29,6 +29,7 @@ const defaultOptions: PluginOptions = { user_tag_cooldown: null, global_tag_cooldown: null, user_cooldown: null, + allow_mentions: false, global_cooldown: null, auto_delete_command: false, diff --git a/backend/src/plugins/Tags/types.ts b/backend/src/plugins/Tags/types.ts index 15b0ce5b..21324be0 100644 --- a/backend/src/plugins/Tags/types.ts +++ b/backend/src/plugins/Tags/types.ts @@ -15,6 +15,7 @@ export const TagCategory = t.type({ user_tag_cooldown: tNullable(t.union([t.string, t.number])), // Per user, per tag user_category_cooldown: tNullable(t.union([t.string, t.number])), // Per user, per tag category global_tag_cooldown: tNullable(t.union([t.string, t.number])), // Any user, per tag + allow_mentions: tNullable(t.boolean), // Per user, per category global_category_cooldown: tNullable(t.union([t.string, t.number])), // Any user, per category auto_delete_command: tNullable(t.boolean), // Any tag, per tag category @@ -31,6 +32,7 @@ export const ConfigSchema = t.type({ user_tag_cooldown: tNullable(t.union([t.string, t.number])), // Per user, per tag global_tag_cooldown: tNullable(t.union([t.string, t.number])), // Any user, per tag user_cooldown: tNullable(t.union([t.string, t.number])), // Per user + allow_mentions: t.boolean, // Per user global_cooldown: tNullable(t.union([t.string, t.number])), // Any tag use auto_delete_command: t.boolean, // Any tag diff --git a/backend/src/plugins/Tags/util/onMessageCreate.ts b/backend/src/plugins/Tags/util/onMessageCreate.ts index d7d8530b..c3e92a83 100644 --- a/backend/src/plugins/Tags/util/onMessageCreate.ts +++ b/backend/src/plugins/Tags/util/onMessageCreate.ts @@ -99,7 +99,11 @@ export async function onMessageCreate(pluginData: GuildPluginData