From f4b13529c639927c9105c82ea4a0ae920a582b44 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Thu, 24 Dec 2020 00:48:04 +0200 Subject: [PATCH 01/54] logs: fix excluded_categories not working --- backend/src/plugins/Logs/util/log.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/plugins/Logs/util/log.ts b/backend/src/plugins/Logs/util/log.ts index c38d8dda..a2bccba5 100644 --- a/backend/src/plugins/Logs/util/log.ts +++ b/backend/src/plugins/Logs/util/log.ts @@ -62,7 +62,7 @@ export async function log(pluginData: GuildPluginData, type: Log type === LogType.CENSOR || type === LogType.CLEAN ) { - if (data.channel.parent_id && opts.excluded_categories.includes(data.channel.parent_id)) { + if (data.channel.parentID && opts.excluded_categories.includes(data.channel.parentID)) { continue logChannelLoop; } } From 506565b20bf6ab933b4536afc1b6998ca6abddf2 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 20:51:27 +0200 Subject: [PATCH 02/54] Don't bind to a specific ip when debugging --- backend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index 281f6253..f91124d3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,10 +7,10 @@ "watch": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node start-dev.js\"", "watch-yaml-parse-test": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node dist/backend/src/yamlParseTest.js\"", "build": "rimraf dist && tsc", - "start-bot-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --inspect=127.0.0.1:9229 dist/backend/src/index.js", + "start-bot-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --inspect=0.0.0.0:9229 dist/backend/src/index.js", "start-bot-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js --unhandled-rejections=strict dist/backend/src/index.js", "watch-bot": "cross-env NODE_ENV=development tsc-watch --onSuccess \"npm run start-bot-dev\"", - "start-api-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --inspect=127.0.0.1:9239 dist/backend/src/api/index.js", + "start-api-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --inspect=0.0.0.0:9239 dist/backend/src/api/index.js", "start-api-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js --unhandled-rejections=strict dist/backend/src/api/index.js", "watch-api": "cross-env NODE_ENV=development tsc-watch --onSuccess \"npm run start-api-dev\"", "typeorm": "node -r ./register-tsconfig-paths.js ./node_modules/typeorm/cli.js", From 5f5113ee84f49c172b4276b07d94a68060733cab Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 20:52:25 +0200 Subject: [PATCH 03/54] Allow specifying first user's ID in !addserver --- .../plugins/BotControl/commands/AllowServerCmd.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/backend/src/plugins/BotControl/commands/AllowServerCmd.ts b/backend/src/plugins/BotControl/commands/AllowServerCmd.ts index 5e7f8888..7d969f8f 100644 --- a/backend/src/plugins/BotControl/commands/AllowServerCmd.ts +++ b/backend/src/plugins/BotControl/commands/AllowServerCmd.ts @@ -2,6 +2,7 @@ import { botControlCmd } from "../types"; import { isOwnerPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isSnowflake } from "../../../utils"; +import { ApiPermissions } from "@shared/apiPermissions"; export const AllowServerCmd = botControlCmd({ trigger: ["allow_server", "allowserver", "add_server", "addserver"], @@ -12,6 +13,7 @@ export const AllowServerCmd = botControlCmd({ signature: { guildId: ct.string(), + userId: ct.string({ required: false }), }, async run({ pluginData, message: msg, args }) { @@ -26,8 +28,18 @@ export const AllowServerCmd = botControlCmd({ return; } + if (args.userId && !isSnowflake(args.userId)) { + sendErrorMessage(pluginData, msg.channel, "Invalid user ID!"); + return; + } + await pluginData.state.allowedGuilds.add(args.guildId); await pluginData.state.configs.saveNewRevision(`guild-${args.guildId}`, "plugins: {}", msg.author.id); + + if (args.userId) { + await pluginData.state.apiPermissionAssignments.addUser(args.guildId, args.userId, [ApiPermissions.EditConfig]); + } + sendSuccessMessage(pluginData, msg.channel, "Server is now allowed to use Zeppelin!"); }, }); From 1a5fe949afbb649ae91fb13c07a277f6198490e3 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 20:58:09 +0200 Subject: [PATCH 04/54] Add !list_dashboard_users --- .../plugins/BotControl/BotControlPlugin.ts | 2 + .../commands/ListDashboardUsersCmd.ts | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts diff --git a/backend/src/plugins/BotControl/BotControlPlugin.ts b/backend/src/plugins/BotControl/BotControlPlugin.ts index 5e871612..e37df650 100644 --- a/backend/src/plugins/BotControl/BotControlPlugin.ts +++ b/backend/src/plugins/BotControl/BotControlPlugin.ts @@ -15,6 +15,7 @@ import { AddDashboardUserCmd } from "./commands/AddDashboardUserCmd"; import { RemoveDashboardUserCmd } from "./commands/RemoveDashboardUserCmd"; import { Configs } from "../../data/Configs"; import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments"; +import { ListDashboardUsersCmd } from "./commands/ListDashboardUsersCmd"; const defaultOptions = { config: { @@ -37,6 +38,7 @@ export const BotControlPlugin = zeppelinGlobalPlugin()("bo DisallowServerCmd, AddDashboardUserCmd, RemoveDashboardUserCmd, + ListDashboardUsersCmd, ], onLoad(pluginData) { diff --git a/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts b/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts new file mode 100644 index 00000000..35f8318f --- /dev/null +++ b/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts @@ -0,0 +1,37 @@ +import { botControlCmd } from "../types"; +import { isOwnerPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; +import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { ApiPermissions } from "@shared/apiPermissions"; +import { resolveUser } from "../../../utils"; + +export const ListDashboardUsersCmd = botControlCmd({ + trigger: ["list_dashboard_users"], + permission: null, + config: { + preFilters: [isOwnerPreFilter], + }, + + signature: { + guildId: ct.string(), + }, + + async run({ pluginData, message: msg, args }) { + const guild = await pluginData.state.allowedGuilds.find(args.guildId); + if (!guild) { + sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin"); + return; + } + + const dashboardUsers = await pluginData.state.apiPermissionAssignments.getByGuildId(guild.id); + const users = await Promise.all(dashboardUsers.map(perm => resolveUser(pluginData.client, perm.target_id))); + const userNameList = users.map( + user => `<@!${user.id}> (**${user.username}#${user.discriminator}**, \`${user.id}\`)`, + ); + + sendSuccessMessage( + pluginData, + msg.channel, + `The following users have dashboard access for **${guild.name}**:\n\n${userNameList}`, + ); + }, +}); From 6896afebfa662cc9146878268670c72a22792e45 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 21:05:11 +0200 Subject: [PATCH 05/54] Add proper types to sendSuccessMessage() --- backend/src/pluginUtils.ts | 21 +++++++++++++++---- backend/src/plugins/Utility/UtilityPlugin.ts | 2 +- .../src/plugins/Utility/commands/CleanCmd.ts | 4 ++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/backend/src/pluginUtils.ts b/backend/src/pluginUtils.ts index 9b48f809..fe6c973f 100644 --- a/backend/src/pluginUtils.ts +++ b/backend/src/pluginUtils.ts @@ -2,7 +2,7 @@ * @file Utility functions that are plugin-instance-specific (i.e. use PluginData) */ -import { Member } from "eris"; +import { GuildTextableChannel, Member, Message, TextableChannel } from "eris"; import { CommandContext, configUtils, ConfigValidationError, GuildPluginData, helpers, PluginOptions } from "knub"; import { decodeAndValidateStrict, StrictValidationError, validate } from "./validatorUtils"; import { deepKeyIntersect, errorMessage, successMessage, tDeepPartial, tNullable } from "./utils"; @@ -137,17 +137,30 @@ export function getPluginConfigPreprocessor( }; } -export function sendSuccessMessage(pluginData: AnyPluginData, channel, body) { +export function sendSuccessMessage( + pluginData: AnyPluginData, + channel: TextableChannel, + body: string, +): Promise { const emoji = pluginData.fullConfig.success_emoji || undefined; return channel.createMessage(successMessage(body, emoji)).catch(err => { - logger.warn(`Failed to send success message to ${channel.id} (${channel.guild?.id}): ${err.code} ${err.message}`); + logger.warn( + `Failed to send success message to ${channel.id} (${(channel as GuildTextableChannel).guild?.id}): ${err.code} ${ + err.message + }`, + ); + return undefined; }); } export function sendErrorMessage(pluginData: AnyPluginData, channel, body) { const emoji = pluginData.fullConfig.error_emoji || undefined; return channel.createMessage(errorMessage(body, emoji)).catch(err => { - logger.warn(`Failed to send error message to ${channel.id} (${channel.guild?.id}): ${err.code} ${err.message}`); + logger.warn( + `Failed to send error message to ${channel.id} (${(channel as GuildTextableChannel).guild?.id}): ${err.code} ${ + err.message + }`, + ); }); } diff --git a/backend/src/plugins/Utility/UtilityPlugin.ts b/backend/src/plugins/Utility/UtilityPlugin.ts index bfc08657..41a81d0e 100644 --- a/backend/src/plugins/Utility/UtilityPlugin.ts +++ b/backend/src/plugins/Utility/UtilityPlugin.ts @@ -148,7 +148,7 @@ export const UtilityPlugin = zeppelinGuildPlugin()("utility", state.lastReload = Date.now(); if (activeReloads.has(guild.id)) { - sendSuccessMessage(pluginData, activeReloads.get(guild.id), "Reloaded!"); + sendSuccessMessage(pluginData, activeReloads.get(guild.id)!, "Reloaded!"); activeReloads.delete(guild.id); } }, diff --git a/backend/src/plugins/Utility/commands/CleanCmd.ts b/backend/src/plugins/Utility/commands/CleanCmd.ts index a54964f1..4bb51b42 100644 --- a/backend/src/plugins/Utility/commands/CleanCmd.ts +++ b/backend/src/plugins/Utility/commands/CleanCmd.ts @@ -136,7 +136,7 @@ export const CleanCmd = utilityCmd({ } } - let responseMsg: Message; + let responseMsg: Message | undefined; if (messagesToClean.length > 0) { const cleanResult = await cleanMessages(pluginData, targetChannel, messagesToClean, msg.author); @@ -157,7 +157,7 @@ export const CleanCmd = utilityCmd({ // (so as not to spam the cleaned channel with the command itself) setTimeout(() => { msg.delete().catch(noop); - responseMsg.delete().catch(noop); + responseMsg?.delete().catch(noop); }, CLEAN_COMMAND_DELETE_DELAY); } }, From edaeb7ef0e6fd3a5c5fc4f7b4b76524cfd2569c9 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 21:21:18 +0200 Subject: [PATCH 06/54] Add proper types to sendErrorMessage() --- backend/src/pluginUtils.ts | 39 +++++++++++-------- .../plugins/ModActions/commands/AddCaseCmd.ts | 3 +- .../src/plugins/ModActions/commands/BanCmd.ts | 3 +- .../ModActions/commands/CasesUserCmd.ts | 3 +- .../ModActions/commands/ForcebanCmd.ts | 3 +- .../ModActions/commands/ForcemuteCmd.ts | 3 +- .../ModActions/commands/ForceunmuteCmd.ts | 3 +- .../plugins/ModActions/commands/MuteCmd.ts | 3 +- .../plugins/ModActions/commands/NoteCmd.ts | 3 +- .../plugins/ModActions/commands/UnbanCmd.ts | 3 +- .../plugins/ModActions/commands/UnmuteCmd.ts | 3 +- .../plugins/ModActions/commands/WarnCmd.ts | 3 +- .../functions/actualKickMemberCmd.ts | 3 +- .../plugins/NameHistory/commands/NamesCmd.ts | 3 +- .../Post/commands/SchedluedPostsDeleteCmd.ts | 3 +- .../Post/commands/ScheduledPostsShowCmd.ts | 3 +- .../src/plugins/Post/util/actualPostCmd.ts | 38 +++++++++--------- .../src/plugins/Roles/commands/AddRoleCmd.ts | 15 ++++--- .../plugins/Roles/commands/MassAddRoleCmd.ts | 12 ++++-- .../Roles/commands/MassRemoveRoleCmd.ts | 12 ++++-- .../plugins/Roles/commands/RemoveRoleCmd.ts | 15 ++++--- .../Slowmode/commands/SlowmodeSetCmd.ts | 7 +--- backend/src/plugins/Utility/search.ts | 18 ++++++--- 23 files changed, 123 insertions(+), 78 deletions(-) diff --git a/backend/src/pluginUtils.ts b/backend/src/pluginUtils.ts index fe6c973f..0b87ca25 100644 --- a/backend/src/pluginUtils.ts +++ b/backend/src/pluginUtils.ts @@ -143,25 +143,32 @@ export function sendSuccessMessage( body: string, ): Promise { const emoji = pluginData.fullConfig.success_emoji || undefined; - return channel.createMessage(successMessage(body, emoji)).catch(err => { - logger.warn( - `Failed to send success message to ${channel.id} (${(channel as GuildTextableChannel).guild?.id}): ${err.code} ${ - err.message - }`, - ); - return undefined; - }); + return channel + .createMessage(successMessage(body, emoji)) // Force line break + .catch(err => { + const channelInfo = (channel as GuildTextableChannel).guild + ? `${channel.id} (${(channel as GuildTextableChannel).guild.id})` + : `${channel.id}`; + logger.warn(`Failed to send success message to ${channelInfo}): ${err.code} ${err.message}`); + return undefined; + }); } -export function sendErrorMessage(pluginData: AnyPluginData, channel, body) { +export function sendErrorMessage( + pluginData: AnyPluginData, + channel: TextableChannel, + body: string, +): Promise { const emoji = pluginData.fullConfig.error_emoji || undefined; - return channel.createMessage(errorMessage(body, emoji)).catch(err => { - logger.warn( - `Failed to send error message to ${channel.id} (${(channel as GuildTextableChannel).guild?.id}): ${err.code} ${ - err.message - }`, - ); - }); + return channel + .createMessage(errorMessage(body, emoji)) // Force line break + .catch(err => { + const channelInfo = (channel as GuildTextableChannel).guild + ? `${channel.id} (${(channel as GuildTextableChannel).guild.id})` + : `${channel.id}`; + logger.warn(`Failed to send error message to ${channelInfo}): ${err.code} ${err.message}`); + return undefined; + }); } export function getBaseUrl(pluginData: AnyPluginData) { diff --git a/backend/src/plugins/ModActions/commands/AddCaseCmd.ts b/backend/src/plugins/ModActions/commands/AddCaseCmd.ts index 9c965933..7bd06bf8 100644 --- a/backend/src/plugins/ModActions/commands/AddCaseCmd.ts +++ b/backend/src/plugins/ModActions/commands/AddCaseCmd.ts @@ -30,7 +30,8 @@ export const AddCaseCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } // If the user exists as a guild member, make sure we can act on them first diff --git a/backend/src/plugins/ModActions/commands/BanCmd.ts b/backend/src/plugins/ModActions/commands/BanCmd.ts index 4196aded..197a4672 100644 --- a/backend/src/plugins/ModActions/commands/BanCmd.ts +++ b/backend/src/plugins/ModActions/commands/BanCmd.ts @@ -34,7 +34,8 @@ export const BanCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id); diff --git a/backend/src/plugins/ModActions/commands/CasesUserCmd.ts b/backend/src/plugins/ModActions/commands/CasesUserCmd.ts index 916ac3a2..12fdf125 100644 --- a/backend/src/plugins/ModActions/commands/CasesUserCmd.ts +++ b/backend/src/plugins/ModActions/commands/CasesUserCmd.ts @@ -37,7 +37,8 @@ export const CasesUserCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const cases = await pluginData.state.cases.with("notes").getByUserId(user.id); diff --git a/backend/src/plugins/ModActions/commands/ForcebanCmd.ts b/backend/src/plugins/ModActions/commands/ForcebanCmd.ts index c7f4e52c..d586cbf8 100644 --- a/backend/src/plugins/ModActions/commands/ForcebanCmd.ts +++ b/backend/src/plugins/ModActions/commands/ForcebanCmd.ts @@ -32,7 +32,8 @@ export const ForcebanCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } // If the user exists as a guild member, make sure we can act on them first diff --git a/backend/src/plugins/ModActions/commands/ForcemuteCmd.ts b/backend/src/plugins/ModActions/commands/ForcemuteCmd.ts index 7c3e68b5..af680374 100644 --- a/backend/src/plugins/ModActions/commands/ForcemuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/ForcemuteCmd.ts @@ -34,7 +34,8 @@ export const ForcemuteCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const memberToMute = await resolveMember(pluginData.client, pluginData.guild, user.id); diff --git a/backend/src/plugins/ModActions/commands/ForceunmuteCmd.ts b/backend/src/plugins/ModActions/commands/ForceunmuteCmd.ts index 177676ab..e86a6408 100644 --- a/backend/src/plugins/ModActions/commands/ForceunmuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/ForceunmuteCmd.ts @@ -32,7 +32,8 @@ export const ForceUnmuteCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } // Check if they're muted in the first place diff --git a/backend/src/plugins/ModActions/commands/MuteCmd.ts b/backend/src/plugins/ModActions/commands/MuteCmd.ts index 709dbbb7..ada23e2f 100644 --- a/backend/src/plugins/ModActions/commands/MuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/MuteCmd.ts @@ -44,7 +44,8 @@ export const MuteCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const memberToMute = await resolveMember(pluginData.client, pluginData.guild, user.id); diff --git a/backend/src/plugins/ModActions/commands/NoteCmd.ts b/backend/src/plugins/ModActions/commands/NoteCmd.ts index 1c8b3478..5e203328 100644 --- a/backend/src/plugins/ModActions/commands/NoteCmd.ts +++ b/backend/src/plugins/ModActions/commands/NoteCmd.ts @@ -21,7 +21,8 @@ export const NoteCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const userName = `${user.username}#${user.discriminator}`; diff --git a/backend/src/plugins/ModActions/commands/UnbanCmd.ts b/backend/src/plugins/ModActions/commands/UnbanCmd.ts index 9db1f85d..07a3edf8 100644 --- a/backend/src/plugins/ModActions/commands/UnbanCmd.ts +++ b/backend/src/plugins/ModActions/commands/UnbanCmd.ts @@ -29,7 +29,8 @@ export const UnbanCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } // The moderator who did the action is the message author or, if used, the specified -mod diff --git a/backend/src/plugins/ModActions/commands/UnmuteCmd.ts b/backend/src/plugins/ModActions/commands/UnmuteCmd.ts index eac1bdb8..f6efdd39 100644 --- a/backend/src/plugins/ModActions/commands/UnmuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/UnmuteCmd.ts @@ -34,7 +34,8 @@ export const UnmuteCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const memberToUnmute = await resolveMember(pluginData.client, pluginData.guild, user.id); diff --git a/backend/src/plugins/ModActions/commands/WarnCmd.ts b/backend/src/plugins/ModActions/commands/WarnCmd.ts index 0329103b..617b0f05 100644 --- a/backend/src/plugins/ModActions/commands/WarnCmd.ts +++ b/backend/src/plugins/ModActions/commands/WarnCmd.ts @@ -30,7 +30,8 @@ export const WarnCmd = modActionsCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const memberToWarn = await resolveMember(pluginData.client, pluginData.guild, user.id); diff --git a/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts b/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts index 1df90eef..18ee2137 100644 --- a/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts @@ -25,7 +25,8 @@ export async function actualKickMemberCmd( ) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - return sendErrorMessage(pluginData, msg.channel, `User not found`); + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; } const memberToKick = await resolveMember(pluginData.client, pluginData.guild, user.id); diff --git a/backend/src/plugins/NameHistory/commands/NamesCmd.ts b/backend/src/plugins/NameHistory/commands/NamesCmd.ts index dfb1f3cb..78456820 100644 --- a/backend/src/plugins/NameHistory/commands/NamesCmd.ts +++ b/backend/src/plugins/NameHistory/commands/NamesCmd.ts @@ -20,7 +20,8 @@ export const NamesCmd = nameHistoryCmd({ const usernames = await pluginData.state.usernameHistory.getByUserId(args.userId); if (nicknames.length === 0 && usernames.length === 0) { - return sendErrorMessage(pluginData, msg.channel, "No name history found"); + sendErrorMessage(pluginData, msg.channel, "No name history found"); + return; } const nicknameRows = nicknames.map( diff --git a/backend/src/plugins/Post/commands/SchedluedPostsDeleteCmd.ts b/backend/src/plugins/Post/commands/SchedluedPostsDeleteCmd.ts index cc5222ec..98810fc1 100644 --- a/backend/src/plugins/Post/commands/SchedluedPostsDeleteCmd.ts +++ b/backend/src/plugins/Post/commands/SchedluedPostsDeleteCmd.ts @@ -16,7 +16,8 @@ export const ScheduledPostsDeleteCmd = postCmd({ scheduledPosts.sort(sorter("post_at")); const post = scheduledPosts[args.num - 1]; if (!post) { - return sendErrorMessage(pluginData, msg.channel, "Scheduled post not found"); + sendErrorMessage(pluginData, msg.channel, "Scheduled post not found"); + return; } await pluginData.state.scheduledPosts.delete(post.id); diff --git a/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts b/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts index 0e76f134..25ecdede 100644 --- a/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts +++ b/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts @@ -18,7 +18,8 @@ export const ScheduledPostsShowCmd = postCmd({ scheduledPosts.sort(sorter("post_at")); const post = scheduledPosts[args.num - 1]; if (!post) { - return sendErrorMessage(pluginData, msg.channel, "Scheduled post not found"); + sendErrorMessage(pluginData, msg.channel, "Scheduled post not found"); + return; } postMessage(pluginData, msg.channel as TextChannel, post.content, post.attachments, post.enable_mentions); diff --git a/backend/src/plugins/Post/util/actualPostCmd.ts b/backend/src/plugins/Post/util/actualPostCmd.ts index 5c74aa7f..b8c8cbc8 100644 --- a/backend/src/plugins/Post/util/actualPostCmd.ts +++ b/backend/src/plugins/Post/util/actualPostCmd.ts @@ -39,14 +39,12 @@ export async function actualPostCmd( if (opts.repeat) { if (opts.repeat < MIN_REPEAT_TIME) { - return sendErrorMessage( - pluginData, - msg.channel, - `Minimum time for -repeat is ${humanizeDuration(MIN_REPEAT_TIME)}`, - ); + sendErrorMessage(pluginData, msg.channel, `Minimum time for -repeat is ${humanizeDuration(MIN_REPEAT_TIME)}`); + return; } if (opts.repeat > MAX_REPEAT_TIME) { - return sendErrorMessage(pluginData, msg.channel, `Max time for -repeat is ${humanizeDuration(MAX_REPEAT_TIME)}`); + sendErrorMessage(pluginData, msg.channel, `Max time for -repeat is ${humanizeDuration(MAX_REPEAT_TIME)}`); + return; } } @@ -56,7 +54,8 @@ export async function actualPostCmd( // Schedule the post to be posted later postAt = await parseScheduleTime(pluginData, msg.author.id, opts.schedule); if (!postAt) { - return sendErrorMessage(pluginData, msg.channel, "Invalid schedule time"); + sendErrorMessage(pluginData, msg.channel, "Invalid schedule time"); + return; } } else if (opts.repeat) { postAt = moment.utc().add(opts.repeat, "ms"); @@ -72,35 +71,37 @@ export async function actualPostCmd( // Invalid time if (!repeatUntil) { - return sendErrorMessage(pluginData, msg.channel, "Invalid time specified for -repeat-until"); + sendErrorMessage(pluginData, msg.channel, "Invalid time specified for -repeat-until"); + return; } if (repeatUntil.isBefore(moment.utc())) { - return sendErrorMessage(pluginData, msg.channel, "You can't set -repeat-until in the past"); + sendErrorMessage(pluginData, msg.channel, "You can't set -repeat-until in the past"); + return; } if (repeatUntil.isAfter(MAX_REPEAT_UNTIL)) { - return sendErrorMessage( + sendErrorMessage( pluginData, msg.channel, "Unfortunately, -repeat-until can only be at most 100 years into the future. Maybe 99 years would be enough?", ); + return; } } else if (opts["repeat-times"]) { repeatTimes = opts["repeat-times"]; if (repeatTimes <= 0) { - return sendErrorMessage(pluginData, msg.channel, "-repeat-times must be 1 or more"); + sendErrorMessage(pluginData, msg.channel, "-repeat-times must be 1 or more"); + return; } } if (repeatUntil && repeatTimes) { - return sendErrorMessage(pluginData, msg.channel, "You can only use one of -repeat-until or -repeat-times at once"); + sendErrorMessage(pluginData, msg.channel, "You can only use one of -repeat-until or -repeat-times at once"); + return; } if (opts.repeat && !repeatUntil && !repeatTimes) { - return sendErrorMessage( - pluginData, - msg.channel, - "You must specify -repeat-until or -repeat-times for repeated messages", - ); + sendErrorMessage(pluginData, msg.channel, "You must specify -repeat-until or -repeat-times for repeated messages"); + return; } if (opts.repeat) { @@ -114,7 +115,8 @@ export async function actualPostCmd( // Save schedule/repeat information in DB if (postAt) { if (postAt < moment.utc()) { - return sendErrorMessage(pluginData, msg.channel, "Post can't be scheduled to be posted in the past"); + sendErrorMessage(pluginData, msg.channel, "Post can't be scheduled to be posted in the past"); + return; } await pluginData.state.scheduledPosts.create({ diff --git a/backend/src/plugins/Roles/commands/AddRoleCmd.ts b/backend/src/plugins/Roles/commands/AddRoleCmd.ts index a17042c9..20395f27 100644 --- a/backend/src/plugins/Roles/commands/AddRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/AddRoleCmd.ts @@ -17,17 +17,20 @@ export const AddRoleCmd = rolesCmd({ async run({ message: msg, args, pluginData }) { if (!canActOn(pluginData, msg.member, args.member, true)) { - return sendErrorMessage(pluginData, msg.channel, "Cannot add roles to this user: insufficient permissions"); + sendErrorMessage(pluginData, msg.channel, "Cannot add roles to this user: insufficient permissions"); + return; } const roleId = await resolveRoleId(pluginData.client, pluginData.guild.id, args.role); if (!roleId) { - return sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + return; } const config = pluginData.config.getForMessage(msg); if (!config.assignable_roles.includes(roleId)) { - return sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + return; } // Sanity check: make sure the role is configured properly @@ -36,11 +39,13 @@ export const AddRoleCmd = rolesCmd({ pluginData.state.logs.log(LogType.BOT_ALERT, { body: `Unknown role configured for 'roles' plugin: ${roleId}`, }); - return sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + return; } if (args.member.roles.includes(roleId)) { - return sendErrorMessage(pluginData, msg.channel, "Member already has that role"); + sendErrorMessage(pluginData, msg.channel, "Member already has that role"); + return; } pluginData.state.logs.ignoreLog(LogType.MEMBER_ROLE_ADD, args.member.id); diff --git a/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts b/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts index 4f0f458c..b2e3000f 100644 --- a/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts @@ -28,22 +28,25 @@ export const MassAddRoleCmd = rolesCmd({ for (const member of members) { if (!canActOn(pluginData, msg.member, member, true)) { - return sendErrorMessage( + sendErrorMessage( pluginData, msg.channel, "Cannot add roles to 1 or more specified members: insufficient permissions", ); + return; } } const roleId = await resolveRoleId(pluginData.client, pluginData.guild.id, args.role); if (!roleId) { - return sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + return; } const config = pluginData.config.getForMessage(msg); if (!config.assignable_roles.includes(roleId)) { - return sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + return; } const role = pluginData.guild.roles.get(roleId); @@ -51,7 +54,8 @@ export const MassAddRoleCmd = rolesCmd({ pluginData.state.logs.log(LogType.BOT_ALERT, { body: `Unknown role configured for 'roles' plugin: ${roleId}`, }); - return sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot assign that role"); + return; } const membersWithoutTheRole = members.filter(m => !m.roles.includes(roleId)); diff --git a/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts b/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts index 593e4c07..966f2e4a 100644 --- a/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts @@ -28,22 +28,25 @@ export const MassRemoveRoleCmd = rolesCmd({ for (const member of members) { if (!canActOn(pluginData, msg.member, member, true)) { - return sendErrorMessage( + sendErrorMessage( pluginData, msg.channel, "Cannot add roles to 1 or more specified members: insufficient permissions", ); + return; } } const roleId = await resolveRoleId(pluginData.client, pluginData.guild.id, args.role); if (!roleId) { - return sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + return; } const config = pluginData.config.getForMessage(msg); if (!config.assignable_roles.includes(roleId)) { - return sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + return; } const role = pluginData.guild.roles.get(roleId); @@ -51,7 +54,8 @@ export const MassRemoveRoleCmd = rolesCmd({ pluginData.state.logs.log(LogType.BOT_ALERT, { body: `Unknown role configured for 'roles' plugin: ${roleId}`, }); - return sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + return; } const membersWithTheRole = members.filter(m => m.roles.includes(roleId)); diff --git a/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts b/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts index f3af983b..d078e1f6 100644 --- a/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts @@ -17,17 +17,20 @@ export const RemoveRoleCmd = rolesCmd({ async run({ message: msg, args, pluginData }) { if (!canActOn(pluginData, msg.member, args.member, true)) { - return sendErrorMessage(pluginData, msg.channel, "Cannot remove roles from this user: insufficient permissions"); + sendErrorMessage(pluginData, msg.channel, "Cannot remove roles from this user: insufficient permissions"); + return; } const roleId = await resolveRoleId(pluginData.client, pluginData.guild.id, args.role); if (!roleId) { - return sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + sendErrorMessage(pluginData, msg.channel, "Invalid role id"); + return; } const config = pluginData.config.getForMessage(msg); if (!config.assignable_roles.includes(roleId)) { - return sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + return; } // Sanity check: make sure the role is configured properly @@ -36,11 +39,13 @@ export const RemoveRoleCmd = rolesCmd({ pluginData.state.logs.log(LogType.BOT_ALERT, { body: `Unknown role configured for 'roles' plugin: ${roleId}`, }); - return sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + sendErrorMessage(pluginData, msg.channel, "You cannot remove that role"); + return; } if (!args.member.roles.includes(roleId)) { - return sendErrorMessage(pluginData, msg.channel, "Member doesn't have that role"); + sendErrorMessage(pluginData, msg.channel, "Member doesn't have that role"); + return; } pluginData.state.logs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, args.member.id); diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts index 1da1dc29..f237bc97 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts @@ -126,11 +126,8 @@ export const SlowmodeSetCmd = slowmodeCmd({ rateLimitPerUser: rateLimitSeconds, }); } catch (e) { - return sendErrorMessage( - pluginData, - msg.channel, - `Failed to set native slowmode: ${disableInlineCode(e.message)}`, - ); + sendErrorMessage(pluginData, msg.channel, `Failed to set native slowmode: ${disableInlineCode(e.message)}`); + return; } } else { // If there is an existing native slowmode, disable that first diff --git a/backend/src/plugins/Utility/search.ts b/backend/src/plugins/Utility/search.ts index ca3829dd..c369e7ba 100644 --- a/backend/src/plugins/Utility/search.ts +++ b/backend/src/plugins/Utility/search.ts @@ -83,18 +83,21 @@ export async function displaySearch( } } catch (e) { if (e instanceof SearchError) { - return sendErrorMessage(pluginData, msg.channel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); + return; } if (e instanceof InvalidRegexError) { - return sendErrorMessage(pluginData, msg.channel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); + return; } throw e; } if (searchResult.totalResults === 0) { - return sendErrorMessage(pluginData, msg.channel, "No results found"); + sendErrorMessage(pluginData, msg.channel, "No results found"); + return; } const resultWord = searchResult.totalResults === 1 ? "matching member" : "matching members"; @@ -203,18 +206,21 @@ export async function archiveSearch( } } catch (e) { if (e instanceof SearchError) { - return sendErrorMessage(pluginData, msg.channel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); + return; } if (e instanceof InvalidRegexError) { - return sendErrorMessage(pluginData, msg.channel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); + return; } throw e; } if (results.totalResults === 0) { - return sendErrorMessage(pluginData, msg.channel, "No results found"); + sendErrorMessage(pluginData, msg.channel, "No results found"); + return; } const resultList = args.ids ? formatSearchResultIdList(results.results) : formatSearchResultList(results.results); From 564aaaded0395eded21313a62ab4eccd7c3572db Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 21:24:23 +0200 Subject: [PATCH 07/54] Add allowedMentions parameter to sendSuccessMessage()/sendErrorMessage() --- backend/src/pluginUtils.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/backend/src/pluginUtils.ts b/backend/src/pluginUtils.ts index 0b87ca25..cae48714 100644 --- a/backend/src/pluginUtils.ts +++ b/backend/src/pluginUtils.ts @@ -2,7 +2,7 @@ * @file Utility functions that are plugin-instance-specific (i.e. use PluginData) */ -import { GuildTextableChannel, Member, Message, TextableChannel } from "eris"; +import { AdvancedMessageContent, AllowedMentions, GuildTextableChannel, Member, Message, TextableChannel } from "eris"; import { CommandContext, configUtils, ConfigValidationError, GuildPluginData, helpers, PluginOptions } from "knub"; import { decodeAndValidateStrict, StrictValidationError, validate } from "./validatorUtils"; import { deepKeyIntersect, errorMessage, successMessage, tDeepPartial, tNullable } from "./utils"; @@ -141,10 +141,15 @@ export function sendSuccessMessage( pluginData: AnyPluginData, channel: TextableChannel, body: string, + allowedMentions?: AllowedMentions, ): Promise { const emoji = pluginData.fullConfig.success_emoji || undefined; + const formattedBody = successMessage(body, emoji); + const content: AdvancedMessageContent = allowedMentions + ? { content: formattedBody, allowedMentions } + : { content: formattedBody }; return channel - .createMessage(successMessage(body, emoji)) // Force line break + .createMessage(content) // Force line break .catch(err => { const channelInfo = (channel as GuildTextableChannel).guild ? `${channel.id} (${(channel as GuildTextableChannel).guild.id})` @@ -158,10 +163,15 @@ export function sendErrorMessage( pluginData: AnyPluginData, channel: TextableChannel, body: string, + allowedMentions?: AllowedMentions, ): Promise { const emoji = pluginData.fullConfig.error_emoji || undefined; + const formattedBody = errorMessage(body, emoji); + const content: AdvancedMessageContent = allowedMentions + ? { content: formattedBody, allowedMentions } + : { content: formattedBody }; return channel - .createMessage(errorMessage(body, emoji)) // Force line break + .createMessage(content) // Force line break .catch(err => { const channelInfo = (channel as GuildTextableChannel).guild ? `${channel.id} (${(channel as GuildTextableChannel).guild.id})` From b74c7fae43bac2b612991aed0b82752857ef8176 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 17 Jan 2021 21:24:45 +0200 Subject: [PATCH 08/54] Disable mentions in !list_dashboard_users --- backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts b/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts index 35f8318f..5c856275 100644 --- a/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts +++ b/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts @@ -32,6 +32,7 @@ export const ListDashboardUsersCmd = botControlCmd({ pluginData, msg.channel, `The following users have dashboard access for **${guild.name}**:\n\n${userNameList}`, + {}, ); }, }); From d4d89327fd2aa9b581b4f27ea78bbbf3a3d77633 Mon Sep 17 00:00:00 2001 From: Nils <7890309+DarkView@users.noreply.github.com> Date: Sun, 17 Jan 2021 20:30:59 +0100 Subject: [PATCH 09/54] Fixed note command not allowing only attachments (#144) --- backend/src/plugins/ModActions/commands/NoteCmd.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/src/plugins/ModActions/commands/NoteCmd.ts b/backend/src/plugins/ModActions/commands/NoteCmd.ts index 5e203328..b0dd89f7 100644 --- a/backend/src/plugins/ModActions/commands/NoteCmd.ts +++ b/backend/src/plugins/ModActions/commands/NoteCmd.ts @@ -15,7 +15,7 @@ export const NoteCmd = modActionsCmd({ signature: { user: ct.string(), - note: ct.string({ catchAll: true }), + note: ct.string({ required: false, catchAll: true }), }, async run({ pluginData, message: msg, args }) { @@ -25,6 +25,11 @@ export const NoteCmd = modActionsCmd({ return; } + if (!args.note && msg.attachments.length === 0) { + sendErrorMessage(pluginData, msg.channel, "Text or attachment required"); + return; + } + const userName = `${user.username}#${user.discriminator}`; const reason = formatReasonWithAttachments(args.note, msg.attachments); From 8c11349a8bfcd427241e1fe1f061ff9a771d3121 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Mon, 18 Jan 2021 22:44:54 +0200 Subject: [PATCH 10/54] Add presetup-configurator to the repo --- presetup-configurator/package-lock.json | 305 +++++++++++++++++++++ presetup-configurator/package.json | 21 ++ presetup-configurator/snowpack.config.js | 8 + presetup-configurator/src/App.css | 8 + presetup-configurator/src/App.tsx | 11 + presetup-configurator/src/Configurator.css | 48 ++++ presetup-configurator/src/Configurator.tsx | 152 ++++++++++ presetup-configurator/src/Levels.tsx | 46 ++++ presetup-configurator/src/LogChannels.css | 12 + presetup-configurator/src/LogChannels.tsx | 152 ++++++++++ presetup-configurator/src/index.css | 14 + presetup-configurator/src/index.html | 14 + presetup-configurator/src/index.tsx | 15 + presetup-configurator/tsconfig.json | 23 ++ 14 files changed, 829 insertions(+) create mode 100644 presetup-configurator/package-lock.json create mode 100644 presetup-configurator/package.json create mode 100644 presetup-configurator/snowpack.config.js create mode 100644 presetup-configurator/src/App.css create mode 100644 presetup-configurator/src/App.tsx create mode 100644 presetup-configurator/src/Configurator.css create mode 100644 presetup-configurator/src/Configurator.tsx create mode 100644 presetup-configurator/src/Levels.tsx create mode 100644 presetup-configurator/src/LogChannels.css create mode 100644 presetup-configurator/src/LogChannels.tsx create mode 100644 presetup-configurator/src/index.css create mode 100644 presetup-configurator/src/index.html create mode 100644 presetup-configurator/src/index.tsx create mode 100644 presetup-configurator/tsconfig.json diff --git a/presetup-configurator/package-lock.json b/presetup-configurator/package-lock.json new file mode 100644 index 00000000..37cd3f41 --- /dev/null +++ b/presetup-configurator/package-lock.json @@ -0,0 +1,305 @@ +{ + "name": "zeppelin-presetup-configurator", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@snowpack/plugin-typescript": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@snowpack/plugin-typescript/-/plugin-typescript-1.2.1.tgz", + "integrity": "sha512-wU+JNaMVkqGsqTaUY7TnEMhGt/3URTgA9dpMCtZX6wn/ceA7Gwlmue/sOLynf0OTNLygHPvjiQECQYkEi3LTtg==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "npm-run-path": "^4.0.1" + } + }, + "@types/node": { + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + }, + "esbuild": { + "version": "0.8.32", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.8.32.tgz", + "integrity": "sha512-5IzQapMW/wFy5oxziHCJzawk26K3xeyrIAQPnPN3c0Q84hqRw6IfGDGfGWOdJNw5tAx77yvwqZ4r1QMpo6emJA==", + "dev": true + }, + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "fsevents": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", + "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "dev": true, + "optional": true + }, + "get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "requires": { + "argparse": "^2.0.1" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.1.tgz", + "integrity": "sha512-f2wt9DCBKKjlFbjzGb8MOAW8LH8F0mrs1zc7KTjAJ9PZNQbfenzWbNP1VZJvw6ICMG9r14Ah6yfwPn7T7i646A==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "react": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", + "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-dom": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", + "integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.1" + } + }, + "rollup": { + "version": "2.36.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.36.2.tgz", + "integrity": "sha512-qjjiuJKb+/8n0EZyQYVW+gFU4bNRBcZaXVzUgSVrGw0HlQBlK2aWyaOMMs1Ufic1jV69b9kW3u3i9B+hISDm3A==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + }, + "dependencies": { + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + } + } + }, + "scheduler": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz", + "integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "snowpack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.0.11.tgz", + "integrity": "sha512-lBxgkvWTgdg0szE31JUt01wQkA9Lnmm+6lxqeV9rxDfflpx7ASnldVHFvu7Se70QJmPTQB0UJjfKI+xmYGwiiQ==", + "dev": true, + "requires": { + "esbuild": "^0.8.7", + "fsevents": "^2.2.0", + "open": "^7.0.4", + "rollup": "^2.34.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } +} diff --git a/presetup-configurator/package.json b/presetup-configurator/package.json new file mode 100644 index 00000000..837348c2 --- /dev/null +++ b/presetup-configurator/package.json @@ -0,0 +1,21 @@ +{ + "name": "zeppelin-presetup-configurator", + "private": true, + "scripts": { + "watch": "snowpack dev", + "build": "snowpack build", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "@snowpack/plugin-typescript": "^1.2.1", + "@types/node": "^14.14.21", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "snowpack": "^3.0.11" + }, + "dependencies": { + "js-yaml": "^4.0.0", + "react": "^17.0.1", + "react-dom": "^17.0.1" + } +} diff --git a/presetup-configurator/snowpack.config.js b/presetup-configurator/snowpack.config.js new file mode 100644 index 00000000..0125dd6f --- /dev/null +++ b/presetup-configurator/snowpack.config.js @@ -0,0 +1,8 @@ +module.exports = { + mount: { + "src": "/", + }, + plugins: [ + '@snowpack/plugin-typescript', + ], +}; diff --git a/presetup-configurator/src/App.css b/presetup-configurator/src/App.css new file mode 100644 index 00000000..2b17506e --- /dev/null +++ b/presetup-configurator/src/App.css @@ -0,0 +1,8 @@ +.App { + display: flex; + justify-content: center; +} + +.App .wrapper { + flex: 0 1 800px; +} diff --git a/presetup-configurator/src/App.tsx b/presetup-configurator/src/App.tsx new file mode 100644 index 00000000..5201bdf8 --- /dev/null +++ b/presetup-configurator/src/App.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import { Configurator } from "./Configurator"; +import "./App.css"; + +export function App() { + return
+
+ +
+
; +} diff --git a/presetup-configurator/src/Configurator.css b/presetup-configurator/src/Configurator.css new file mode 100644 index 00000000..83d737cb --- /dev/null +++ b/presetup-configurator/src/Configurator.css @@ -0,0 +1,48 @@ +.Configurator { + +} + +.Configurator .options { + display: grid; + grid-auto-columns: min-content auto; + grid-gap: 1px; + + overflow: hidden; + border: 1px solid #444; + border-radius: 4px; + background-color: #fff; +} + +.Configurator .options > h2 { + grid-column: 1; + + margin: 0; + padding: 8px 24px 8px 8px; + + white-space: nowrap; + text-align: right; + font-size: 16px; + font-weight: 600; + + box-shadow: 0 0 0 1px #444; +} + +.Configurator .options > .control { + grid-column: 2; + + padding: 8px; + box-shadow: 0 0 0 1px #444; +} + +.Configurator label { + display: block; + padding: 0 0 8px; +} + +.Configurator .result { + background-color: #eee; + padding: 8px; + border: 1px solid #444; + border-radius: 4px; + box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2); +} diff --git a/presetup-configurator/src/Configurator.tsx b/presetup-configurator/src/Configurator.tsx new file mode 100644 index 00000000..437b4af7 --- /dev/null +++ b/presetup-configurator/src/Configurator.tsx @@ -0,0 +1,152 @@ +import React, { useEffect, useState } from "react"; +import { LevelEntry, Levels } from "./Levels"; +import { LogChannel, LogChannels } from "./LogChannels"; +import yaml from "js-yaml"; +import "./Configurator.css"; + +export function Configurator() { + const [prefix, setPrefix] = useState('!'); + const [levels, setLevels] = useState([]); + + const [withModCommands, setWithModCommands] = useState(false); + const [muteRoleId, setMuteRoleId] = useState(""); + const [caseChannelId, setCaseChannelId] = useState(""); + const [dmModActionReasons, setDmModActionReasons] = useState(false); + + const [withLogs, setWithLogs] = useState(false); + const [logChannels, setLogChannels] = useState([]); + + const [result, setResult] = useState({}); + useEffect(() => { + const resultObj: any = { + prefix, + levels: levels.reduce((obj, entry) => { + obj[entry[0]] = entry[1]; + return obj; + }, {}), + plugins: { + utility: {}, + }, + }; + + if (withModCommands) { + resultObj.plugins.cases = { + config: { + case_log_channel: caseChannelId, + }, + }; + + resultObj.plugins.mod_actions = {}; + + if (muteRoleId) { + resultObj.plugins.mutes = { + config: { + mute_role: muteRoleId, + } + }; + + if (dmModActionReasons) { + resultObj.plugins.mutes.config.dm_on_mute = true; + } + } + + if (dmModActionReasons) { + resultObj.plugins.mod_actions = { + config: { + dm_on_warn: true, + dm_on_kick: true, + dm_on_ban: true, + }, + }; + } + } + + if (withLogs) { + resultObj.plugins.logs = { + config: { + channels: logChannels.reduce((obj, logChannel) => { + if (logChannel.includeExclude === "include") { + obj[logChannel.id] = { + include: Array.from(logChannel.logTypes.values()), + }; + } else { + obj[logChannel.id] = { + exclude: Array.from(logChannel.logTypes.values()), + }; + } + return obj; + }, {}), + }, + }; + } + + setResult(resultObj); + }, [prefix, levels, withModCommands, muteRoleId, caseChannelId, dmModActionReasons, withLogs, logChannels]); + + const [formattedResult, setFormattedResult] = useState(""); + useEffect(() => { + setFormattedResult(yaml.dump(result)); + }, [result]); + + return ( +
+ {/* Options */} +
+

Prefix

+
+ +
+ +

Levels

+
+ +
+ +

Mod commands

+
+ + + {withModCommands && ( +
+ + + + + +
+ )} +
+ +

Logs

+
+ + + {withLogs && ( + + )} +
+
+ + {/* Result */} +
{formattedResult}
+
+ ); +} diff --git a/presetup-configurator/src/Levels.tsx b/presetup-configurator/src/Levels.tsx new file mode 100644 index 00000000..5211770d --- /dev/null +++ b/presetup-configurator/src/Levels.tsx @@ -0,0 +1,46 @@ +import React, { useState } from "react"; + +const LEVEL_ADMIN = 100; +const LEVEL_MODERATOR = 50; + +export type LevelEntry = [string, number]; // id, level + +export function Levels({ levels, setLevels }) { + function addLevel() { + setLevels(arr => [...arr, ["", LEVEL_MODERATOR]]); + } + + function removeLevel(index) { + setLevels(arr => [...arr].splice(index, 1)); + } + + function updateLevelId(index, id) { + const validId = id.replace(/[^0-9]/g, ""); + setLevels(arr => { + arr[index][0] = validId; + return [...arr]; + }); + } + + function updateLevelLevel(index, level) { + setLevels(arr => { + arr[index][1] = parseInt(level, 10); + return [...arr]; + }); + } + + return ( +
+ {levels.map(([id, level], index) => ( +
+ updateLevelId(index, e.target.value)} /> + +
+ ))} + +
+ ); +} diff --git a/presetup-configurator/src/LogChannels.css b/presetup-configurator/src/LogChannels.css new file mode 100644 index 00000000..fcbe3466 --- /dev/null +++ b/presetup-configurator/src/LogChannels.css @@ -0,0 +1,12 @@ +.LogChannels .log-channel { + margin: 16px 0; +} + +.LogChannels .log-types { + height: 200px; + overflow-y: scroll; + border: 1px solid #aaa; + border-radius: 3px; + padding: 4px; + margin-top: 8px; +} diff --git a/presetup-configurator/src/LogChannels.tsx b/presetup-configurator/src/LogChannels.tsx new file mode 100644 index 00000000..a645faa2 --- /dev/null +++ b/presetup-configurator/src/LogChannels.tsx @@ -0,0 +1,152 @@ +import React, { SetStateAction, useState } from "react"; +import "./LogChannels.css"; + +const LOG_TYPES = { + "MEMBER_WARN": "Warned", + "MEMBER_MUTE": "Muted", + "MEMBER_UNMUTE": "Unmuted", + "MEMBER_MUTE_EXPIRED": "Mute expired", + "MEMBER_KICK": "Kicked", + "MEMBER_BAN": "Banned", + "MEMBER_UNBAN": "Unbanned", + "MEMBER_FORCEBAN": "Forcebanned", + "MEMBER_SOFTBAN": "Softbanned", + "MEMBER_JOIN": "Member joined", + "MEMBER_LEAVE": "Member left", + "MEMBER_ROLE_ADD": "Role added to member", + "MEMBER_ROLE_REMOVE": "Role removed from member", + "MEMBER_NICK_CHANGE": "Nickname changed", + "MEMBER_USERNAME_CHANGE": "Username changed", + "MEMBER_RESTORE": "Member roles restored", + "CHANNEL_CREATE": "Channel created", + "CHANNEL_DELETE": "Channel deleted", + "ROLE_CREATE": "Role created", + "ROLE_DELETE": "Role deleted", + "MESSAGE_EDIT": "Message edited", + "MESSAGE_DELETE": "Message deleted", + "MESSAGE_DELETE_BULK": "Messages deleted in bulk", + "MESSAGE_DELETE_BARE": "Message deleted (bare)", + "VOICE_CHANNEL_JOIN": "Voice channel join", + "VOICE_CHANNEL_LEAVE": "Voice channel leave", + "VOICE_CHANNEL_MOVE": "Voice channel move", + "COMMAND": "Command used", + "MESSAGE_SPAM_DETECTED": "Message spam detected", + "CENSOR": "Message censored", + "CLEAN": "Messages cleaned", + "CASE_CREATE": "Case created", + "MASSBAN": "Massbanned", + "MASSMUTE": "Massmuted", + "MEMBER_TIMED_MUTE": "Temporarily muted", + "MEMBER_TIMED_UNMUTE": "Scheduled unmute", + "MEMBER_JOIN_WITH_PRIOR_RECORDS": "Member joined with prior records", + "OTHER_SPAM_DETECTED": "Non-message spam detected", + "MEMBER_ROLE_CHANGES": "Member roles changed", + "VOICE_CHANNEL_FORCE_MOVE": "Force-moved to a voice channel", + "CASE_UPDATE": "Case updated", + "MEMBER_MUTE_REJOIN": "Muted member rejoined", + "SCHEDULED_MESSAGE": "Scheduled message to be posted", + "POSTED_SCHEDULED_MESSAGE": "Posted scheduled message", + "BOT_ALERT": "Bot alert", + "AUTOMOD_ACTION": "Automod action", + "SCHEDULED_REPEATED_MESSAGE": "Scheduled message to be posted repeatedly", + "REPEATED_MESSAGE": "Set a message to be posted repeatedly", + "MESSAGE_DELETE_AUTO": "Message deleted (auto)", + "SET_ANTIRAID_USER": "Set antiraid (user)", + "SET_ANTIRAID_AUTO": "Set antiraid (auto)", + "MASS_ASSIGN_ROLES": "Mass-assigned roles", + "MASS_UNASSIGN_ROLES": "Mass-unassigned roles", + "MEMBER_NOTE": "Added note on member", + "CASE_DELETE": "Case deleted", + "DM_FAILED": "Failed to DM member", +}; + +type LOG_TYPE = keyof typeof LOG_TYPES; + +export interface LogChannel { + id: string; + includeExclude: "include" | "exclude"; + logTypes: Set; +} + +interface Props { + logChannels: LogChannel[]; + setLogChannels: React.Dispatch>; +} + +export function LogChannels({ logChannels, setLogChannels }: Props) { + function addLogChannel() { + setLogChannels(_logChannels => { + return [..._logChannels, { + id: "", + includeExclude: "include", + logTypes: new Set(), + }]; + }); + } + + function deleteLogChannel(index) { + setLogChannels(_logChannels => { + const newArr = [..._logChannels]; + newArr.splice(index, 1); + return newArr; + }); + } + + function setId(index: number, id: string) { + setLogChannels(_logChannels => { + _logChannels[index].id = id; + return [..._logChannels]; + }); + } + + function setIncludeExclude(index: number, includeExclude: LogChannel["includeExclude"]) { + setLogChannels(_logChannels => { + _logChannels[index].includeExclude = includeExclude; + return [..._logChannels]; + }); + } + + function toggleLogType(index: number, logType: LOG_TYPE, enabled: boolean) { + setLogChannels(_logChannels => { + if (enabled) { + _logChannels[index].logTypes.add(logType); + } else { + _logChannels[index].logTypes.delete(logType); + } + + return [..._logChannels]; + }); + } + + return ( +
+ {logChannels.map((logChannel, index) => ( +
+ + +
+ {Object.entries(LOG_TYPES).map(([logType, description]) => ( + + ))} +
+
+ ))} + +
+ ); +} diff --git a/presetup-configurator/src/index.css b/presetup-configurator/src/index.css new file mode 100644 index 00000000..047342ea --- /dev/null +++ b/presetup-configurator/src/index.css @@ -0,0 +1,14 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-family: Arial, sans-serif; +} + +body { + background-color: #f8f8f8; + color: #222; +} diff --git a/presetup-configurator/src/index.html b/presetup-configurator/src/index.html new file mode 100644 index 00000000..a50d603e --- /dev/null +++ b/presetup-configurator/src/index.html @@ -0,0 +1,14 @@ + + + + + + + Document + + +
+ + + diff --git a/presetup-configurator/src/index.tsx b/presetup-configurator/src/index.tsx new file mode 100644 index 00000000..0aaeed55 --- /dev/null +++ b/presetup-configurator/src/index.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import { App } from "./App"; +import "./index.css"; + +ReactDOM.render( + + + , + document.getElementById("root") +); + +if ((import.meta as any).hot) { + (import.meta as any).hot.accept(); +} diff --git a/presetup-configurator/tsconfig.json b/presetup-configurator/tsconfig.json new file mode 100644 index 00000000..75e45666 --- /dev/null +++ b/presetup-configurator/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "esnext", + "target": "es2018", + "sourceMap": true, + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "alwaysStrict": true, + "noImplicitThis": true, + "strictPropertyInitialization": false, + "lib": [ + "esnext", + "dom" + ], + "baseUrl": ".", + "resolveJsonModule": true, + "esModuleInterop": true, + "allowJs": true, + "jsx": "react" + } +} From be06985ad757e42befdd0d7743e64c46c226f589 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Wed, 20 Jan 2021 22:19:10 +0200 Subject: [PATCH 11/54] presetup: improve log type sorting --- presetup-configurator/src/LogChannels.tsx | 43 ++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/presetup-configurator/src/LogChannels.tsx b/presetup-configurator/src/LogChannels.tsx index a645faa2..e7505bcf 100644 --- a/presetup-configurator/src/LogChannels.tsx +++ b/presetup-configurator/src/LogChannels.tsx @@ -2,21 +2,21 @@ import React, { SetStateAction, useState } from "react"; import "./LogChannels.css"; const LOG_TYPES = { - "MEMBER_WARN": "Warned", - "MEMBER_MUTE": "Muted", - "MEMBER_UNMUTE": "Unmuted", + "MEMBER_WARN": "Member warned", + "MEMBER_MUTE": "Member muted", + "MEMBER_UNMUTE": "Member unmuted", "MEMBER_MUTE_EXPIRED": "Mute expired", - "MEMBER_KICK": "Kicked", - "MEMBER_BAN": "Banned", - "MEMBER_UNBAN": "Unbanned", - "MEMBER_FORCEBAN": "Forcebanned", - "MEMBER_SOFTBAN": "Softbanned", + "MEMBER_KICK": "Member kicked", + "MEMBER_BAN": "Member banned", + "MEMBER_UNBAN": "Member unbanned", + "MEMBER_FORCEBAN": "Member forcebanned", + "MEMBER_SOFTBAN": "Member softbanned", "MEMBER_JOIN": "Member joined", "MEMBER_LEAVE": "Member left", - "MEMBER_ROLE_ADD": "Role added to member", - "MEMBER_ROLE_REMOVE": "Role removed from member", - "MEMBER_NICK_CHANGE": "Nickname changed", - "MEMBER_USERNAME_CHANGE": "Username changed", + "MEMBER_ROLE_ADD": "Member, role added", + "MEMBER_ROLE_REMOVE": "Member, role removed", + "MEMBER_NICK_CHANGE": "Member nickname changed", + "MEMBER_USERNAME_CHANGE": "Member username changed", "MEMBER_RESTORE": "Member roles restored", "CHANNEL_CREATE": "Channel created", "CHANNEL_DELETE": "Channel deleted", @@ -36,8 +36,8 @@ const LOG_TYPES = { "CASE_CREATE": "Case created", "MASSBAN": "Massbanned", "MASSMUTE": "Massmuted", - "MEMBER_TIMED_MUTE": "Temporarily muted", - "MEMBER_TIMED_UNMUTE": "Scheduled unmute", + "MEMBER_TIMED_MUTE": "Member temporarily muted", + "MEMBER_TIMED_UNMUTE": "Member, scheduled unmute", "MEMBER_JOIN_WITH_PRIOR_RECORDS": "Member joined with prior records", "OTHER_SPAM_DETECTED": "Non-message spam detected", "MEMBER_ROLE_CHANGES": "Member roles changed", @@ -55,11 +55,22 @@ const LOG_TYPES = { "SET_ANTIRAID_AUTO": "Set antiraid (auto)", "MASS_ASSIGN_ROLES": "Mass-assigned roles", "MASS_UNASSIGN_ROLES": "Mass-unassigned roles", - "MEMBER_NOTE": "Added note on member", + "MEMBER_NOTE": "Member noted", "CASE_DELETE": "Case deleted", "DM_FAILED": "Failed to DM member", }; +const sortedLogTypes = Object.fromEntries( + Object.entries(LOG_TYPES) + .sort((a, b) => { + if (a[1].toLowerCase() > b[1].toLowerCase()) return 1; + if (a[1].toLowerCase() < b[1].toLowerCase()) return -1; + if (a[0].toLowerCase() > b[0].toLowerCase()) return 1; + if (a[0].toLowerCase() < b[0].toLowerCase()) return -1; + return 0; + }) +) as typeof LOG_TYPES; + type LOG_TYPE = keyof typeof LOG_TYPES; export interface LogChannel { @@ -133,7 +144,7 @@ export function LogChannels({ logChannels, setLogChannels }: Props) {
- {Object.entries(LOG_TYPES).map(([logType, description]) => ( + {Object.entries(sortedLogTypes).map(([logType, description]) => (