From fb4979ff00dfcc094429b457625ce3351216f676 Mon Sep 17 00:00:00 2001 From: Jonathan <54381371+yaboyaxis@users.noreply.github.com> Date: Fri, 2 Apr 2021 09:38:24 -0400 Subject: [PATCH 1/8] Add !reason alias for !update (#141) --- backend/src/plugins/ModActions/commands/UpdateCmd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 2fc8cffd80a8a17a003f0932d54b4d5ce28b884e Mon Sep 17 00:00:00 2001 From: Nils <7890309+DarkView@users.noreply.github.com> Date: Fri, 2 Apr 2021 15:39:22 +0200 Subject: [PATCH 2/8] Allow kicking the user from VC on mute (#156) * Allow kicking the user from VC on mute If any non-id string is entered in `move_to_voice_channel`, the user is kicked from the VC instead of being moved. We do not automatically kick if the option is set to null in order to not make this a breaking change for old, intended behavior * Add explicit config option for kicking instead of kicking on any non-id Kicking takes precedent in this case and will take effect instead of moving to voice id --- backend/src/plugins/Mutes/MutesPlugin.ts | 1 + backend/src/plugins/Mutes/functions/muteUser.ts | 7 ++++--- backend/src/plugins/Mutes/types.ts | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) 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/functions/muteUser.ts b/backend/src/plugins/Mutes/functions/muteUser.ts index 2f8e4e28..393a00e1 100644 --- a/backend/src/plugins/Mutes/functions/muteUser.ts +++ b/backend/src/plugins/Mutes/functions/muteUser.ts @@ -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, From 2e50fa763078a07d27847d5b72c80118cfbf8dde Mon Sep 17 00:00:00 2001 From: Nils <7890309+DarkView@users.noreply.github.com> Date: Fri, 2 Apr 2021 15:40:20 +0200 Subject: [PATCH 3/8] Add allow_mentions option to enable tags mentioning someone (#160) Checked when a tag is posted - so a tag can have for example pings enabled if a mod executes that tag, but not if anyone below mod does it. --- backend/src/plugins/Tags/TagsPlugin.ts | 1 + backend/src/plugins/Tags/types.ts | 2 ++ backend/src/plugins/Tags/util/onMessageCreate.ts | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) 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<TagsPluginType> = { 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<TagsPluginType return; } - const responseMsg = await channel.createMessage(tagResult.renderedContent); + const allowMentions = tagResult.category?.allow_mentions ?? config.allow_mentions; + const responseMsg = await channel.createMessage({ + ...tagResult.renderedContent, + allowedMentions: { roles: allowMentions, users: allowMentions }, + }); // Save the command-response message pair once the message is in our database const deleteWithCommand = tagResult.category?.delete_with_command ?? config.delete_with_command; From fcbb25b7fff22e6193e3b2006bc86cbeb7a9b5e2 Mon Sep 17 00:00:00 2001 From: vcokltfre <vcokltfre@gmail.com> Date: Fri, 2 Apr 2021 14:42:25 +0100 Subject: [PATCH 4/8] feat: add color option to starboard (#163) Co-authored-by: Almeida <42935195+almeidx@users.noreply.github.com> --- backend/src/plugins/Starboard/StarboardPlugin.ts | 13 +++++++++++++ backend/src/plugins/Starboard/types.ts | 2 ++ .../util/createStarboardEmbedFromMessage.ts | 3 ++- .../Starboard/util/saveMessageToStarboard.ts | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/src/plugins/Starboard/StarboardPlugin.ts b/backend/src/plugins/Starboard/StarboardPlugin.ts index c3374e56..8767d7e9 100644 --- a/backend/src/plugins/Starboard/StarboardPlugin.ts +++ b/backend/src/plugins/Starboard/StarboardPlugin.ts @@ -57,6 +57,19 @@ export const StarboardPlugin = zeppelinGuildPlugin<StarboardPluginType>()("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/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<typeof StarboardOpts>; @@ -27,6 +28,7 @@ export const defaultStarboardOpts: Partial<TStarboardOpts> = { 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 }); From c5d68650a3ba52b22f5b8235d5589bd364744f44 Mon Sep 17 00:00:00 2001 From: Nils <7890309+DarkView@users.noreply.github.com> Date: Fri, 2 Apr 2021 15:43:13 +0200 Subject: [PATCH 5/8] Enforce unified lock names by using functions to generate lock keys (#165) --- .../src/plugins/Automod/actions/addRoles.ts | 5 ++-- .../plugins/Automod/actions/removeRoles.ts | 5 ++-- .../plugins/Censor/util/onMessageCreate.ts | 3 ++- .../plugins/Censor/util/onMessageUpdate.ts | 3 ++- .../Counters/functions/changeCounterValue.ts | 3 ++- .../Counters/functions/decayCounter.ts | 3 ++- .../Counters/functions/setCounterValue.ts | 3 ++- .../src/plugins/ModActions/commands/BanCmd.ts | 9 ++++--- .../events/ReapplyActiveMuteOnJoinEvt.ts | 5 ++-- .../src/plugins/Mutes/functions/muteUser.ts | 6 ++--- .../src/plugins/Persist/events/LoadDataEvt.ts | 7 ++--- .../util/addMemberPendingRoleChange.ts | 3 ++- .../SelfGrantableRoles/commands/RoleAddCmd.ts | 3 ++- .../commands/RoleRemoveCmd.ts | 3 ++- .../plugins/Slowmode/util/onMessageCreate.ts | 3 ++- .../events/StarboardReactionAddEvt.ts | 3 ++- .../events/StarboardReactionRemoveEvts.ts | 5 ++-- backend/src/utils/lockNameHelpers.ts | 26 +++++++++++++++++++ 18 files changed, 70 insertions(+), 28 deletions(-) create mode 100644 backend/src/utils/lockNameHelpers.ts 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<CensorPluginType>, 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<CensorPluginType>, 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<CountersPluginType>, @@ -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/BanCmd.ts b/backend/src/plugins/ModActions/commands/BanCmd.ts index 4fe9561e..1f943087 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 }), @@ -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/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 393a00e1..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<MutesPluginType>, @@ -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) { 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<SlowmodePluginType>, msg: SavedMessage) { if (msg.is_bot) return; @@ -18,7 +19,7 @@ export async function onMessageCreate(pluginData: GuildPluginData<SlowmodePlugin if (!channel) return; // Don't apply slowmode if the lock was interrupted earlier (e.g. the message was caught by word filters) - const thisMsgLock = await pluginData.locks.acquire(`message-${msg.id}`); + const thisMsgLock = await pluginData.locks.acquire(messageLock(msg)); if (thisMsgLock.interrupted) return; // Check if this channel even *has* a bot-maintained slowmode 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/utils/lockNameHelpers.ts b/backend/src/utils/lockNameHelpers.ts new file mode 100644 index 00000000..4a16bd33 --- /dev/null +++ b/backend/src/utils/lockNameHelpers.ts @@ -0,0 +1,26 @@ +import { Member, Message, User } from "eris"; +import { SavedMessage } from "../data/entities/SavedMessage"; + +export function allStarboardsLock() { + return `starboards`; +} + +export function banLock(user: Member | User | { id: string }) { + return `ban-${user.id}`; +} + +export function counterIdLock(counterId: number | string) { + return `counter-${counterId}`; +} + +export function memberRolesLock(member: Member | User | { id: string }) { + return `member-roles-${member.id}`; +} + +export function messageLock(message: Message | SavedMessage | { id: string }) { + return `message-${message.id}`; +} + +export function muteLock(user: Member | User | { id: string }) { + return `mute-${user.id}`; +} From 2af168b8e3a617b52fa6f4e7cb43d9a55bc29e26 Mon Sep 17 00:00:00 2001 From: vcokltfre <vcokltfre@gmail.com> Date: Fri, 2 Apr 2021 14:43:52 +0100 Subject: [PATCH 6/8] chore: update wording of permission error for -mod (#172) --- backend/src/plugins/ModActions/commands/AddCaseCmd.ts | 2 +- backend/src/plugins/ModActions/commands/BanCmd.ts | 2 +- backend/src/plugins/ModActions/commands/ForcebanCmd.ts | 2 +- backend/src/plugins/ModActions/commands/UnbanCmd.ts | 2 +- backend/src/plugins/ModActions/commands/WarnCmd.ts | 2 +- backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts | 2 +- backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts | 2 +- backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) 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 1f943087..da7156c2 100644 --- a/backend/src/plugins/ModActions/commands/BanCmd.ts +++ b/backend/src/plugins/ModActions/commands/BanCmd.ts @@ -55,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; } 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/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/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; } From c4a8c3014e430583dc8cbdc7ee63e80a7d95dd17 Mon Sep 17 00:00:00 2001 From: DEX <zodpixel@gmail.com> Date: Fri, 2 Apr 2021 17:45:00 +0400 Subject: [PATCH 7/8] Adding av as an alias for the avatar command (#169) --- backend/src/plugins/Utility/commands/AvatarCmd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/plugins/Utility/commands/AvatarCmd.ts b/backend/src/plugins/Utility/commands/AvatarCmd.ts index 9e413a3d..7ab1fac8 100644 --- a/backend/src/plugins/Utility/commands/AvatarCmd.ts +++ b/backend/src/plugins/Utility/commands/AvatarCmd.ts @@ -5,7 +5,7 @@ import { sendErrorMessage } from "../../../pluginUtils"; import { EmbedOptions } from "eris"; export const AvatarCmd = utilityCmd({ - trigger: "avatar", + trigger: ["avatar", "av"], description: "Retrieves a user's profile picture", permission: "can_avatar", From 56ade239dcd535bcccbfc68700955476233d3dbe Mon Sep 17 00:00:00 2001 From: Nils <7890309+DarkView@users.noreply.github.com> Date: Fri, 2 Apr 2021 15:53:09 +0200 Subject: [PATCH 8/8] Allow mute and unmute to be upgraded to their force variant (#174) --- .../plugins/ModActions/commands/MuteCmd.ts | 19 ++++++++++-------- .../plugins/ModActions/commands/UnmuteCmd.ts | 20 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) 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/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