diff --git a/backend/src/plugins/Automod/actions/availableActions.ts b/backend/src/plugins/Automod/actions/availableActions.ts index c37f3a39..3f253bc0 100644 --- a/backend/src/plugins/Automod/actions/availableActions.ts +++ b/backend/src/plugins/Automod/actions/availableActions.ts @@ -6,6 +6,7 @@ import { AlertAction } from "./alert"; import { ArchiveThreadAction } from "./archiveThread"; import { BanAction } from "./ban"; import { ChangeNicknameAction } from "./changeNickname"; +import { ChangePermsAction } from "./changePerms"; import { CleanAction } from "./clean"; import { KickAction } from "./kick"; import { LogAction } from "./log"; @@ -36,6 +37,7 @@ export const availableActions: Record> = { set_slowmode: SetSlowmodeAction, start_thread: StartThreadAction, archive_thread: ArchiveThreadAction, + change_perms: ChangePermsAction, }; export const AvailableActions = t.type({ @@ -56,4 +58,5 @@ export const AvailableActions = t.type({ set_slowmode: SetSlowmodeAction.configType, start_thread: StartThreadAction.configType, archive_thread: ArchiveThreadAction.configType, + change_perms: ChangePermsAction.configType, }); diff --git a/backend/src/plugins/Automod/actions/changePerms.ts b/backend/src/plugins/Automod/actions/changePerms.ts new file mode 100644 index 00000000..ad060879 --- /dev/null +++ b/backend/src/plugins/Automod/actions/changePerms.ts @@ -0,0 +1,96 @@ +import { Permissions, PermissionString } from "discord.js"; +import * as t from "io-ts"; +import { automodAction } from "../helpers"; +import { tNullable, isValidSnowflake, tPartialDictionary } from "../../../utils"; +import { noop } from "knub/dist/utils"; +import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; +import { + guildToTemplateSafeGuild, + savedMessageToTemplateSafeSavedMessage, + userToTemplateSafeUser, +} from "../../../utils/templateSafeObjects"; + +export const ChangePermsAction = automodAction({ + configType: t.type({ + target: t.string, + channel: tNullable(t.string), + perms: tPartialDictionary(t.keyof(Permissions.FLAGS), tNullable(t.boolean)), + }), + defaultConfig: {}, + + async apply({ pluginData, contexts, actionConfig, ruleName }) { + const user = contexts.find((c) => c.user)?.user; + const message = contexts.find((c) => c.message)?.message; + + const renderTarget = async (str: string) => + renderTemplate( + str, + new TemplateSafeValueContainer({ + user: user ? userToTemplateSafeUser(user) : null, + guild: guildToTemplateSafeGuild(pluginData.guild), + message: message ? savedMessageToTemplateSafeSavedMessage(message) : null, + }), + ); + const renderChannel = async (str: string) => + renderTemplate( + str, + new TemplateSafeValueContainer({ + user: user ? userToTemplateSafeUser(user) : null, + guild: guildToTemplateSafeGuild(pluginData.guild), + message: message ? savedMessageToTemplateSafeSavedMessage(message) : null, + }), + ); + const target = await renderTarget(actionConfig.target); + const channelId = actionConfig.channel ? await renderChannel(actionConfig.channel) : null; + const role = pluginData.guild.roles.resolve(target); + if (!role) { + const member = await pluginData.guild.members.fetch(target).catch(noop); + if (!member) return; + } + + if (channelId && isValidSnowflake(channelId)) { + const channel = pluginData.guild.channels.resolve(channelId); + if (!channel || channel.isThread()) return; + const overwrite = channel.permissionOverwrites.cache.find((pw) => pw.id === target); + const allow = new Permissions(overwrite?.allow ?? 0n).serialize(); + const deny = new Permissions(overwrite?.deny ?? 0n).serialize(); + const newPerms: Partial> = {}; + + for (const key in allow) { + if (typeof actionConfig.perms[key] !== "undefined") { + newPerms[key] = actionConfig.perms[key]; + continue; + } + if (allow[key]) { + newPerms[key] = true; + } else if (deny[key]) { + newPerms[key] = false; + } + } + + // takes more code lines but looks cleaner imo + let hasPerms = false; + for (const key in newPerms) { + if (typeof newPerms[key] === "boolean") { + hasPerms = true; + break; + } + } + if (overwrite && !hasPerms) { + await channel.permissionOverwrites.delete(target).catch(noop); + return; + } + await channel.permissionOverwrites.edit(target, newPerms).catch(noop); + return; + } + + if (!role) return; + + const perms = new Permissions(role.permissions).serialize(); + for (const key in actionConfig.perms) { + perms[key] = actionConfig.perms[key]; + } + const permsArray = Object.keys(perms).filter((key) => perms[key]); + await role.setPermissions(new Permissions(permsArray)).catch(noop); + }, +});