From 74c78f51ce31af885172b7de80bdee7009b8fc97 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 21 Jul 2019 21:15:52 +0300 Subject: [PATCH] Add io-ts config schemas to all guild plugins --- src/plugins/AutoReactionsPlugin.ts | 6 +- src/plugins/Cases.ts | 15 ++-- src/plugins/Censor.ts | 38 ++++----- src/plugins/CompanionChannels.ts | 30 ++++--- src/plugins/CustomEvents.ts | 109 +++++++++++++----------- src/plugins/LocateUser.ts | 17 ++-- src/plugins/Logs.ts | 50 +++++------ src/plugins/MessageSaver.ts | 13 +-- src/plugins/ModActions.ts | 58 +++++++------ src/plugins/Mutes.ts | 35 ++++---- src/plugins/NameHistory.ts | 13 +-- src/plugins/Persist.ts | 17 ++-- src/plugins/PingableRolesPlugin.ts | 15 ++-- src/plugins/Post.ts | 16 ++-- src/plugins/ReactionRoles.ts | 33 ++++--- src/plugins/Reminders.ts | 15 ++-- src/plugins/SelfGrantableRolesPlugin.ts | 17 ++-- src/plugins/Slowmode.ts | 21 +++-- src/plugins/Spam.ts | 56 ++++++------ src/plugins/Starboard.ts | 13 +-- src/plugins/Tags.ts | 21 +++-- src/plugins/Utility.ts | 42 ++++----- src/plugins/WelcomeMessage.ts | 17 ++-- 23 files changed, 373 insertions(+), 294 deletions(-) diff --git a/src/plugins/AutoReactionsPlugin.ts b/src/plugins/AutoReactionsPlugin.ts index 91ad1153..c8d07d9d 100644 --- a/src/plugins/AutoReactionsPlugin.ts +++ b/src/plugins/AutoReactionsPlugin.ts @@ -7,14 +7,14 @@ import { customEmojiRegex, errorMessage, isEmoji, successMessage } from "../util import { ZeppelinPlugin } from "./ZeppelinPlugin"; import * as t from "io-ts"; -const configSchema = t.type({ +const ConfigSchema = t.type({ can_manage: t.boolean, }); -type TConfigSchema = t.TypeOf; +type TConfigSchema = t.TypeOf; export class AutoReactionsPlugin extends ZeppelinPlugin { public static pluginName = "auto_reactions"; - protected static configSchema = configSchema; + protected static configSchema = ConfigSchema; protected savedMessages: GuildSavedMessages; protected autoReactions: GuildAutoReactions; diff --git a/src/plugins/Cases.ts b/src/plugins/Cases.ts index 742477e8..b51a339a 100644 --- a/src/plugins/Cases.ts +++ b/src/plugins/Cases.ts @@ -9,11 +9,13 @@ import { GuildArchives } from "../data/GuildArchives"; import { IPluginOptions } from "knub"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; +import * as t from "io-ts"; -interface ICasesPluginConfig { - log_automatic_actions: boolean; - case_log_channel: string; -} +const ConfigSchema = t.type({ + log_automatic_actions: t.boolean, + case_log_channel: t.string, +}); +type TConfigSchema = t.TypeOf; /** * Can also be used as a config object for functions that create cases @@ -40,14 +42,15 @@ export type CaseNoteArgs = { noteDetails?: string[]; }; -export class CasesPlugin extends ZeppelinPlugin { +export class CasesPlugin extends ZeppelinPlugin { public static pluginName = "cases"; + protected static configSchema = ConfigSchema; protected cases: GuildCases; protected archives: GuildArchives; protected logs: GuildLogs; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { log_automatic_actions: true, diff --git a/src/plugins/Censor.ts b/src/plugins/Censor.ts index 4a672e73..2dca48ce 100644 --- a/src/plugins/Censor.ts +++ b/src/plugins/Censor.ts @@ -15,26 +15,26 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { SavedMessage } from "../data/entities/SavedMessage"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import cloneDeep from "lodash.clonedeep"; +import * as t from "io-ts"; -interface ICensorPluginConfig { - filter_zalgo: boolean; - filter_invites: boolean; - invite_guild_whitelist: string[]; - invite_guild_blacklist: string[]; - invite_code_whitelist: string[]; - invite_code_blacklist: string[]; - allow_group_dm_invites: boolean; +const ConfigSchema = t.type({ + filter_zalgo: t.boolean, + filter_invites: t.boolean, + invite_guild_whitelist: t.array(t.string), + invite_guild_blacklist: t.array(t.string), + invite_code_whitelist: t.array(t.string), + invite_code_blacklist: t.array(t.string), + allow_group_dm_invites: t.boolean, + filter_domains: t.boolean, + domain_whitelist: t.array(t.string), + domain_blacklist: t.array(t.string), + blocked_tokens: t.array(t.string), + blocked_words: t.array(t.string), + blocked_regex: t.array(t.string), +}); +type TConfigSchema = t.TypeOf; - filter_domains: boolean; - domain_whitelist: string[]; - domain_blacklist: string[]; - - blocked_tokens: string[]; - blocked_words: string[]; - blocked_regex: string[]; -} - -export class CensorPlugin extends ZeppelinPlugin { +export class CensorPlugin extends ZeppelinPlugin { public static pluginName = "censor"; protected serverLogs: GuildLogs; @@ -43,7 +43,7 @@ export class CensorPlugin extends ZeppelinPlugin { private onMessageCreateFn; private onMessageUpdateFn; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { filter_zalgo: false, diff --git a/src/plugins/CompanionChannels.ts b/src/plugins/CompanionChannels.ts index bc93d9c1..4949f84c 100644 --- a/src/plugins/CompanionChannels.ts +++ b/src/plugins/CompanionChannels.ts @@ -1,29 +1,31 @@ import { decorators as d, IPluginOptions, logger } from "knub"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { Member, Channel, GuildChannel, PermissionOverwrite, Permission, Message } from "eris"; +import * as t from "io-ts"; // Permissions using these numbers: https://abal.moe/Eris/docs/reference (add all allowed/denied ones up) -interface ICompanionChannel { - channelIds: string[]; - permissions: number; -} +const CompanionChannel = t.type({ + channelIds: t.array(t.string), + permissions: t.number, +}); +type TCompanionChannel = t.TypeOf; + +const ConfigSchema = t.type({ + channels: t.record(t.string, CompanionChannel), +}); +type TConfigSchema = t.TypeOf; interface ICompanionChannelMap { - [channelId: string]: ICompanionChannel; + [channelId: string]: TCompanionChannel; } -interface ICompanionChannelPluginConfig { - channels: { - [key: string]: ICompanionChannel; - }; -} - -export class CompanionChannelPlugin extends ZeppelinPlugin { +export class CompanionChannelPlugin extends ZeppelinPlugin { public static pluginName = "companion_channels"; + protected static configSchema = ConfigSchema; - companionChannels: Map = new Map(); + companionChannels: Map = new Map(); - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { channels: {}, diff --git a/src/plugins/CustomEvents.ts b/src/plugins/CustomEvents.ts index e411e90b..5843d103 100644 --- a/src/plugins/CustomEvents.ts +++ b/src/plugins/CustomEvents.ts @@ -5,68 +5,77 @@ import { renderTemplate } from "../templateFormatter"; import { stripObjectToScalars } from "../utils"; import { CasesPlugin } from "./Cases"; import { CaseTypes } from "../data/CaseTypes"; +import * as t from "io-ts"; // Triggers -type CommandTrigger = { - type: "command"; - name: string; - params: string; - can_use: boolean; -}; +const CommandTrigger = t.type({ + type: t.literal("command"), + name: t.string, + params: t.string, + can_use: t.boolean, +}); +type TCommandTrigger = t.TypeOf; -type AnyTrigger = CommandTrigger; +const AnyTrigger = CommandTrigger; // TODO: Make into a union once we have more triggers +type TAnyTrigger = t.TypeOf; // Actions -type AddRoleAction = { - type: "add_role"; - target: string; - role: string | string[]; -}; +const AddRoleAction = t.type({ + type: t.literal("add_role"), + target: t.string, + role: t.union([t.string, t.array(t.string)]), +}); +type TAddRoleAction = t.TypeOf; -type CreateCaseAction = { - type: "create_case"; - case_type: string; - mod: string; - target: string; - reason: string; -}; +const CreateCaseAction = t.type({ + type: t.literal("create_case"), + case_type: t.string, + mod: t.string, + target: t.string, + reason: t.string, +}); +type TCreateCaseAction = t.TypeOf; -type MoveToVoiceChannelAction = { - type: "move_to_vc"; - target: string; - channel: string; -}; +const MoveToVoiceChannelAction = t.type({ + type: t.literal("move_to_vc"), + target: t.string, + channel: t.string, +}); +type TMoveToVoiceChannelAction = t.TypeOf; -type MessageAction = { - type: "message"; - channel: string; - content: string; -}; +const MessageAction = t.type({ + type: t.literal("message"), + channel: t.string, + content: t.string, +}); +type TMessageAction = t.TypeOf; -type AnyAction = AddRoleAction | CreateCaseAction | MoveToVoiceChannelAction | MessageAction; +const AnyAction = t.union([AddRoleAction, CreateCaseAction, MoveToVoiceChannelAction, MessageAction]); +type TAnyAction = t.TypeOf; -// Event -type CustomEvent = { - name: string; - trigger: AnyTrigger; - actions: AnyAction[]; -}; +// Full config schema +const CustomEvent = t.type({ + name: t.string, + trigger: AnyTrigger, + actions: t.array(AnyAction), +}); +type TCustomEvent = t.TypeOf; -interface ICustomEventsPluginConfig { - events: { - [key: string]: CustomEvent; - }; -} +const ConfigSchema = t.type({ + events: t.record(t.string, CustomEvent), +}); +type TConfigSchema = t.TypeOf; class ActionError extends Error {} -export class CustomEventsPlugin extends ZeppelinPlugin { +export class CustomEventsPlugin extends ZeppelinPlugin { public static pluginName = "custom_events"; + public static dependencies = ["cases"]; + protected static configSchema = ConfigSchema; + private clearTriggers: () => void; - public static dependencies = ["cases"]; - - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { events: {}, @@ -97,7 +106,7 @@ export class CustomEventsPlugin extends ZeppelinPlugin; const ALERT_LOOP_TIME = 30 * 1000; -interface ILocatePluginConfig { - can_where: boolean; - can_alert: boolean; -} - -export class LocatePlugin extends ZeppelinPlugin { +export class LocatePlugin extends ZeppelinPlugin { public static pluginName = "locate_user"; + protected static configSchema = ConfigSchema; private alerts: GuildVCAlerts; private outdatedAlertsTimeout; private usersWithAlerts: string[] = []; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_where: false, diff --git a/src/plugins/Logs.ts b/src/plugins/Logs.ts index 2570a6c7..6939f058 100644 --- a/src/plugins/Logs.ts +++ b/src/plugins/Logs.ts @@ -26,33 +26,35 @@ import { GuildCases } from "../data/GuildCases"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { renderTemplate, TemplateParseError } from "../templateFormatter"; import cloneDeep from "lodash.clonedeep"; +import * as t from "io-ts"; -interface ILogChannel { - include?: string[]; - exclude?: string[]; - batched?: boolean; - batch_time?: number; - excluded_users?: string[]; -} +const LogChannel = t.partial({ + include: t.array(t.string), + exclude: t.array(t.string), + batched: t.boolean, + batch_time: t.number, + excluded_users: t.array(t.string), +}); +type TLogChannel = t.TypeOf; -interface ILogChannelMap { - [channelId: string]: ILogChannel; -} +const LogChannelMap = t.record(t.string, LogChannel); +type TLogChannelMap = t.TypeOf; -interface ILogsPluginConfig { - channels: { - [key: string]: ILogChannel; - }; - format: { - [key: string]: string; - timestamp: string; - }; +const ConfigSchema = t.type({ + channels: LogChannelMap, + format: t.intersection([ + t.record(t.string, t.string), + t.type({ + timestamp: t.string, + }), + ]), + ping_user: t.boolean, +}); +type TConfigSchema = t.TypeOf; - ping_user: boolean; -} - -export class LogsPlugin extends ZeppelinPlugin { +export class LogsPlugin extends ZeppelinPlugin { public static pluginName = "logs"; + protected static configSchema = ConfigSchema; protected guildLogs: GuildLogs; protected savedMessages: GuildSavedMessages; @@ -69,7 +71,7 @@ export class LogsPlugin extends ZeppelinPlugin { private excludedUserProps = ["user", "member", "mod"]; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { channels: {}, @@ -121,7 +123,7 @@ export class LogsPlugin extends ZeppelinPlugin { } async log(type, data) { - const logChannels: ILogChannelMap = this.getConfig().channels; + const logChannels: TLogChannelMap = this.getConfig().channels; const typeStr = LogType[type]; logChannelLoop: for (const [channelId, opts] of Object.entries(logChannels)) { diff --git a/src/plugins/MessageSaver.ts b/src/plugins/MessageSaver.ts index 78685a0a..4939ff8f 100644 --- a/src/plugins/MessageSaver.ts +++ b/src/plugins/MessageSaver.ts @@ -3,17 +3,20 @@ import { GuildChannel, Message, TextChannel } from "eris"; import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { successMessage } from "../utils"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; +import * as t from "io-ts"; -interface IMessageSaverPluginConfig { - can_manage: boolean; -} +const ConfigSchema = t.type({ + can_manage: t.boolean, +}); +type TConfigSchema = t.TypeOf; -export class MessageSaverPlugin extends ZeppelinPlugin { +export class MessageSaverPlugin extends ZeppelinPlugin { public static pluginName = "message_saver"; + protected static configSchema = ConfigSchema; protected savedMessages: GuildSavedMessages; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/ModActions.ts b/src/plugins/ModActions.ts index b50bd0d0..7a97b27e 100644 --- a/src/plugins/ModActions.ts +++ b/src/plugins/ModActions.ts @@ -26,6 +26,33 @@ import { Case } from "../data/entities/Case"; import { renderTemplate } from "../templateFormatter"; import { CasesPlugin } from "./Cases"; import { MuteResult, MutesPlugin } from "./Mutes"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + dm_on_warn: t.boolean, + dm_on_kick: t.boolean, + dm_on_ban: t.boolean, + message_on_warn: t.boolean, + message_on_kick: t.boolean, + message_on_ban: t.boolean, + message_channel: t.string, + warn_message: t.string, + kick_message: t.string, + ban_message: t.string, + alert_on_rejoin: t.boolean, + alert_channel: t.string, + can_note: t.boolean, + can_warn: t.boolean, + can_mute: t.boolean, + can_kick: t.boolean, + can_ban: t.boolean, + can_view: t.boolean, + can_addcase: t.boolean, + can_massban: t.boolean, + can_hidecase: t.boolean, + can_act_as_other: t.boolean, +}); +type TConfigSchema = t.TypeOf; enum IgnoredEventType { Ban = 1, @@ -38,35 +65,10 @@ interface IIgnoredEvent { userId: string; } -interface IModActionsPluginConfig { - dm_on_warn: boolean; - dm_on_kick: boolean; - dm_on_ban: boolean; - message_on_warn: boolean; - message_on_kick: boolean; - message_on_ban: boolean; - message_channel: string; - warn_message: string; - kick_message: string; - ban_message: string; - alert_on_rejoin: boolean; - alert_channel: string; - - can_note: boolean; - can_warn: boolean; - can_mute: boolean; - can_kick: boolean; - can_ban: boolean; - can_view: boolean; - can_addcase: boolean; - can_massban: boolean; - can_hidecase: boolean; - can_act_as_other: boolean; -} - -export class ModActionsPlugin extends ZeppelinPlugin { +export class ModActionsPlugin extends ZeppelinPlugin { public static pluginName = "mod_actions"; public static dependencies = ["cases", "mutes"]; + protected static configSchema = ConfigSchema; protected mutes: GuildMutes; protected cases: GuildCases; @@ -82,7 +84,7 @@ export class ModActionsPlugin extends ZeppelinPlugin { this.ignoredEvents = []; } - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { dm_on_warn: true, diff --git a/src/plugins/Mutes.ts b/src/plugins/Mutes.ts index 0b76994b..8e0f3772 100644 --- a/src/plugins/Mutes.ts +++ b/src/plugins/Mutes.ts @@ -25,26 +25,28 @@ import { renderTemplate } from "../templateFormatter"; import { CaseTypes } from "../data/CaseTypes"; import { CaseArgs, CasesPlugin } from "./Cases"; import { Case } from "../data/entities/Case"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + mute_role: t.string, + move_to_voice_channel: t.string, + + dm_on_mute: t.boolean, + message_on_mute: t.boolean, + message_channel: t.string, + mute_message: t.string, + timed_mute_message: t.string, + + can_view_list: t.boolean, + can_cleanup: t.boolean, +}); +type TConfigSchema = t.TypeOf; interface IMuteWithDetails extends Mute { member?: Member; banned?: boolean; } -interface IMutesPluginConfig { - mute_role: string; - move_to_voice_channel: string; - - dm_on_mute: boolean; - message_on_mute: boolean; - message_channel: string; - mute_message: string; - timed_mute_message: string; - - can_view_list: boolean; - can_cleanup: boolean; -} - export type MuteResult = { case: Case; notifyResult: INotifyUserResult; @@ -59,15 +61,16 @@ const EXPIRED_MUTE_CHECK_INTERVAL = 60 * 1000; let FIRST_CHECK_TIME = Date.now(); const FIRST_CHECK_INCREMENT = 5 * 1000; -export class MutesPlugin extends ZeppelinPlugin { +export class MutesPlugin extends ZeppelinPlugin { public static pluginName = "mutes"; + protected static configSchema = ConfigSchema; protected mutes: GuildMutes; protected cases: GuildCases; protected serverLogs: GuildLogs; private muteClearIntervalId: NodeJS.Timer; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { mute_role: null, diff --git a/src/plugins/NameHistory.ts b/src/plugins/NameHistory.ts index 72f8defa..aeb2ed36 100644 --- a/src/plugins/NameHistory.ts +++ b/src/plugins/NameHistory.ts @@ -4,18 +4,21 @@ import { Member, Message } from "eris"; import { createChunkedMessage, disableCodeBlocks } from "../utils"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { MAX_USERNAME_ENTRIES_PER_USER, UsernameHistory } from "../data/UsernameHistory"; +import * as t from "io-ts"; -interface INameHistoryPluginConfig { - can_view: boolean; -} +const ConfigSchema = t.type({ + can_view: t.boolean, +}); +type TConfigSchema = t.TypeOf; -export class NameHistoryPlugin extends ZeppelinPlugin { +export class NameHistoryPlugin extends ZeppelinPlugin { public static pluginName = "name_history"; + protected static configSchema = ConfigSchema; protected nicknameHistory: GuildNicknameHistory; protected usernameHistory: UsernameHistory; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_view: false, diff --git a/src/plugins/Persist.ts b/src/plugins/Persist.ts index 4fa9b544..24628874 100644 --- a/src/plugins/Persist.ts +++ b/src/plugins/Persist.ts @@ -6,20 +6,23 @@ import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; import { stripObjectToScalars } from "../utils"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; +import * as t from "io-ts"; -interface IPersistPluginConfig { - persisted_roles: string[]; - persist_nicknames: boolean; - persist_voice_mutes: boolean; -} +const ConfigSchema = t.type({ + persisted_roles: t.array(t.string), + persist_nicknames: t.boolean, + persist_voice_mutes: t.boolean, +}); +type TConfigSchema = t.TypeOf; -export class PersistPlugin extends ZeppelinPlugin { +export class PersistPlugin extends ZeppelinPlugin { public static pluginName = "persist"; + protected static configSchema = ConfigSchema; protected persistedData: GuildPersistedData; protected logs: GuildLogs; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { persisted_roles: [], diff --git a/src/plugins/PingableRolesPlugin.ts b/src/plugins/PingableRolesPlugin.ts index 62d90b9d..d4d6f1be 100644 --- a/src/plugins/PingableRolesPlugin.ts +++ b/src/plugins/PingableRolesPlugin.ts @@ -4,21 +4,24 @@ import { GuildPingableRoles } from "../data/GuildPingableRoles"; import { PingableRole } from "../data/entities/PingableRole"; import { errorMessage, successMessage } from "../utils"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + can_manage: t.boolean, +}); +type TConfigSchema = t.TypeOf; const TIMEOUT = 10 * 1000; -interface IPingableRolesPluginConfig { - can_manage: boolean; -} - -export class PingableRolesPlugin extends ZeppelinPlugin { +export class PingableRolesPlugin extends ZeppelinPlugin { public static pluginName = "pingable_roles"; + protected static configSchema = ConfigSchema; protected pingableRoles: GuildPingableRoles; protected cache: Map; protected timeouts: Map; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Post.ts b/src/plugins/Post.ts index d6b7f953..8750a56f 100644 --- a/src/plugins/Post.ts +++ b/src/plugins/Post.ts @@ -22,6 +22,13 @@ import { GuildScheduledPosts } from "../data/GuildScheduledPosts"; import moment, { Moment } from "moment-timezone"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + can_post: t.boolean, +}); +type TConfigSchema = t.TypeOf; + const fsp = fs.promises; const COLOR_MATCH_REGEX = /^#?([0-9a-f]{6})$/; @@ -29,12 +36,9 @@ const COLOR_MATCH_REGEX = /^#?([0-9a-f]{6})$/; const SCHEDULED_POST_CHECK_INTERVAL = 15 * SECONDS; const SCHEDULED_POST_PREVIEW_TEXT_LENGTH = 50; -interface IPostPluginConfig { - can_post: boolean; -} - -export class PostPlugin extends ZeppelinPlugin { +export class PostPlugin extends ZeppelinPlugin { public static pluginName = "post"; + protected static configSchema = ConfigSchema; protected savedMessages: GuildSavedMessages; protected scheduledPosts: GuildScheduledPosts; @@ -54,7 +58,7 @@ export class PostPlugin extends ZeppelinPlugin { clearTimeout(this.scheduledPostLoopTimeout); } - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_post: false, diff --git a/src/plugins/ReactionRoles.ts b/src/plugins/ReactionRoles.ts index c6904b4b..899a7d17 100644 --- a/src/plugins/ReactionRoles.ts +++ b/src/plugins/ReactionRoles.ts @@ -8,6 +8,22 @@ import { Queue } from "../Queue"; import { ReactionRole } from "../data/entities/ReactionRole"; import Timeout = NodeJS.Timeout; import DiscordRESTError from "eris/lib/errors/DiscordRESTError"; // tslint:disable-line +import * as t from "io-ts"; + +/** + * Either of: + * [emojiId, roleId] + * [emojiId, roleId, emojiName] + * Where emojiId is either the snowflake of a custom emoji, or the actual unicode emoji + */ +const ReactionRolePair = t.union([t.tuple([t.string, t.string, t.string]), t.tuple([t.string, t.string])]); +type TReactionRolePair = t.TypeOf; + +const ConfigSchema = t.type({ + auto_refresh_interval: t.number, + can_manage: t.boolean, +}); +type TConfigSchema = t.TypeOf; type ReactionRolePair = [string, string, string?]; @@ -26,14 +42,9 @@ type PendingMemberRoleChanges = { }>; }; -interface IReactionRolesPluginConfig { - auto_refresh_interval: number; - - can_manage: boolean; -} - -export class ReactionRolesPlugin extends ZeppelinPlugin { +export class ReactionRolesPlugin extends ZeppelinPlugin { public static pluginName = "reaction_roles"; + protected static configSchema = ConfigSchema; protected reactionRoles: GuildReactionRoles; protected savedMessages: GuildSavedMessages; @@ -44,7 +55,7 @@ export class ReactionRolesPlugin extends ZeppelinPlugin { + getDefaultOptions(): IPluginOptions { return { config: { auto_refresh_interval: null, @@ -279,17 +290,17 @@ export class ReactionRolesPlugin extends ZeppelinPlugin v.split("=").map(v => v.trim())) // tslint:disable-line .map( - (pair): ReactionRolePair => { + (pair): TReactionRolePair => { const customEmojiMatch = pair[0].match(/^$/); if (customEmojiMatch) { return [customEmojiMatch[2], pair[1], customEmojiMatch[1]]; } else { - return pair as ReactionRolePair; + return pair as TReactionRolePair; } }, ); diff --git a/src/plugins/Reminders.ts b/src/plugins/Reminders.ts index 4449144f..e91598a4 100644 --- a/src/plugins/Reminders.ts +++ b/src/plugins/Reminders.ts @@ -12,16 +12,19 @@ import { sorter, successMessage, } from "../utils"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + can_use: t.boolean, +}); +type TConfigSchema = t.TypeOf; const REMINDER_LOOP_TIME = 10 * 1000; const MAX_TRIES = 3; -interface IRemindersPluginConfig { - can_use: boolean; -} - -export class RemindersPlugin extends ZeppelinPlugin { +export class RemindersPlugin extends ZeppelinPlugin { public static pluginName = "reminders"; + protected static configSchema = ConfigSchema; protected reminders: GuildReminders; protected tries: Map; @@ -29,7 +32,7 @@ export class RemindersPlugin extends ZeppelinPlugin { private postRemindersTimeout; private unloaded = false; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_use: false, diff --git a/src/plugins/SelfGrantableRolesPlugin.ts b/src/plugins/SelfGrantableRolesPlugin.ts index 4a26bff4..adf72f3f 100644 --- a/src/plugins/SelfGrantableRolesPlugin.ts +++ b/src/plugins/SelfGrantableRolesPlugin.ts @@ -3,19 +3,22 @@ import { GuildSelfGrantableRoles } from "../data/GuildSelfGrantableRoles"; import { GuildChannel, Message, Role, TextChannel } from "eris"; import { asSingleLine, chunkArray, errorMessage, sorter, successMessage, trimLines } from "../utils"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; +import * as t from "io-ts"; -interface ISelfGrantableRolesPluginConfig { - can_manage: boolean; - can_use: boolean; - can_ignore_cooldown: boolean; -} +const ConfigSchema = t.type({ + can_manage: t.boolean, + can_use: t.boolean, + can_ignore_cooldown: t.boolean, +}); +type TConfigSchema = t.TypeOf; -export class SelfGrantableRolesPlugin extends ZeppelinPlugin { +export class SelfGrantableRolesPlugin extends ZeppelinPlugin { public static pluginName = "self_grantable_roles"; + protected static configSchema = ConfigSchema; protected selfGrantableRoles: GuildSelfGrantableRoles; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Slowmode.ts b/src/plugins/Slowmode.ts index 1fbc30f3..e0809a50 100644 --- a/src/plugins/Slowmode.ts +++ b/src/plugins/Slowmode.ts @@ -17,20 +17,23 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages"; import DiscordRESTError from "eris/lib/errors/DiscordRESTError"; // tslint:disable-line import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + use_native_slowmode: t.boolean, + + can_manage: t.boolean, + is_affected: t.boolean, +}); +type TConfigSchema = t.TypeOf; const NATIVE_SLOWMODE_LIMIT = 6 * 60 * 60; // 6 hours const MAX_SLOWMODE = 60 * 60 * 24 * 365 * 100; // 100 years const BOT_SLOWMODE_CLEAR_INTERVAL = 60 * 1000; -interface ISlowmodePluginConfig { - use_native_slowmode: boolean; - - can_manage: boolean; - is_affected: boolean; -} - -export class SlowmodePlugin extends ZeppelinPlugin { +export class SlowmodePlugin extends ZeppelinPlugin { public static pluginName = "slowmode"; + protected static configSchema = ConfigSchema; protected slowmodes: GuildSlowmodes; protected savedMessages: GuildSavedMessages; @@ -39,7 +42,7 @@ export class SlowmodePlugin extends ZeppelinPlugin { private onMessageCreateFn; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { use_native_slowmode: true, diff --git a/src/plugins/Spam.ts b/src/plugins/Spam.ts index 65b084f4..cdd32399 100644 --- a/src/plugins/Spam.ts +++ b/src/plugins/Spam.ts @@ -21,6 +21,34 @@ import { GuildMutes } from "../data/GuildMutes"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { MuteResult, MutesPlugin } from "./Mutes"; import { CasesPlugin } from "./Cases"; +import * as t from "io-ts"; + +const BaseSingleSpamConfig = t.intersection([ + t.type({ + interval: t.number, + count: t.number, + }), + t.partial({ + mute: t.boolean, + mute_time: t.number, + clean: t.boolean, + }), +]); +type TBaseSingleSpamConfig = t.TypeOf; + +const ConfigSchema = t.type({ + max_censor: BaseSingleSpamConfig, + max_messages: BaseSingleSpamConfig, + max_mentions: BaseSingleSpamConfig, + max_links: BaseSingleSpamConfig, + max_attachments: BaseSingleSpamConfig, + max_emojis: BaseSingleSpamConfig, + max_newlines: BaseSingleSpamConfig, + max_duplicates: BaseSingleSpamConfig, + max_characters: BaseSingleSpamConfig, + max_voice_moves: BaseSingleSpamConfig, +}); +type TConfigSchema = t.TypeOf; enum RecentActionType { Message = 1, @@ -47,29 +75,9 @@ const MAX_INTERVAL = 300; const SPAM_ARCHIVE_EXPIRY_DAYS = 90; -interface IBaseSingleSpamConfig { - interval: number; - count: number; - mute?: boolean; - mute_time?: number; - clean?: boolean; -} - -interface ISpamPluginConfig { - max_censor: IBaseSingleSpamConfig; - max_messages: IBaseSingleSpamConfig; - max_mentions: IBaseSingleSpamConfig; - max_links: IBaseSingleSpamConfig; - max_attachments: IBaseSingleSpamConfig; - max_emojis: IBaseSingleSpamConfig; - max_newlines: IBaseSingleSpamConfig; - max_duplicates: IBaseSingleSpamConfig; - max_characters: IBaseSingleSpamConfig; - max_voice_moves: IBaseSingleSpamConfig; -} - -export class SpamPlugin extends ZeppelinPlugin { +export class SpamPlugin extends ZeppelinPlugin { public static pluginName = "spam"; + protected static configSchema = ConfigSchema; protected logs: GuildLogs; protected archives: GuildArchives; @@ -91,7 +99,7 @@ export class SpamPlugin extends ZeppelinPlugin { private expiryInterval; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { max_censor: null, @@ -201,7 +209,7 @@ export class SpamPlugin extends ZeppelinPlugin { async logAndDetectMessageSpam( savedMessage: SavedMessage, type: RecentActionType, - spamConfig: IBaseSingleSpamConfig, + spamConfig: TBaseSingleSpamConfig, actionCount: number, description: string, ) { diff --git a/src/plugins/Starboard.ts b/src/plugins/Starboard.ts index 9ad11901..032217e1 100644 --- a/src/plugins/Starboard.ts +++ b/src/plugins/Starboard.ts @@ -16,20 +16,23 @@ import path from "path"; import moment from "moment-timezone"; import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { SavedMessage } from "../data/entities/SavedMessage"; +import * as t from "io-ts"; -interface IStarboardPluginConfig { - can_manage: boolean; -} +const ConfigSchema = t.type({ + can_manage: t.boolean, +}); +type TConfigSchema = t.TypeOf; -export class StarboardPlugin extends ZeppelinPlugin { +export class StarboardPlugin extends ZeppelinPlugin { public static pluginName = "starboard"; + protected static configSchema = ConfigSchema; protected starboards: GuildStarboards; protected savedMessages: GuildSavedMessages; private onMessageDeleteFn; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Tags.ts b/src/plugins/Tags.ts index dad4e1b7..8ee095d7 100644 --- a/src/plugins/Tags.ts +++ b/src/plugins/Tags.ts @@ -9,18 +9,21 @@ import humanizeDuration from "humanize-duration"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { parseTemplate, renderTemplate, TemplateParseError } from "../templateFormatter"; import { GuildArchives } from "../data/GuildArchives"; +import * as t from "io-ts"; -interface ITagsPluginConfig { - prefix: string; - delete_with_command: boolean; +const ConfigSchema = t.type({ + prefix: t.string, + delete_with_command: t.boolean, - can_create: boolean; - can_use: boolean; - can_list: boolean; -} + can_create: t.boolean, + can_use: t.boolean, + can_list: t.boolean, +}); +type TConfigSchema = t.TypeOf; -export class TagsPlugin extends ZeppelinPlugin { +export class TagsPlugin extends ZeppelinPlugin { public static pluginName = "tags"; + protected static configSchema = ConfigSchema; protected archives: GuildArchives; protected tags: GuildTags; @@ -31,7 +34,7 @@ export class TagsPlugin extends ZeppelinPlugin { protected tagFunctions; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { prefix: "!!", diff --git a/src/plugins/Utility.ts b/src/plugins/Utility.ts index 5958e94d..d86a02e4 100644 --- a/src/plugins/Utility.ts +++ b/src/plugins/Utility.ts @@ -43,8 +43,25 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildArchives } from "../data/GuildArchives"; import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { getCurrentUptime } from "../uptime"; - import LCL from "last-commit-log"; +import * as t from "io-ts"; + +const ConfigSchema = t.type({ + can_roles: t.boolean, + can_level: t.boolean, + can_search: t.boolean, + can_clean: t.boolean, + can_info: t.boolean, + can_server: t.boolean, + can_reload_guild: t.boolean, + can_nickname: t.boolean, + can_ping: t.boolean, + can_source: t.boolean, + can_vcmove: t.boolean, + can_help: t.boolean, + can_about: t.boolean, +}); +type TConfigSchema = t.TypeOf; const { performance } = require("perf_hooks"); @@ -63,24 +80,9 @@ type MemberSearchParams = { "case-sensitive"?: boolean; }; -interface IUtilityPluginConfig { - can_roles: boolean; - can_level: boolean; - can_search: boolean; - can_clean: boolean; - can_info: boolean; - can_server: boolean; - can_reload_guild: boolean; - can_nickname: boolean; - can_ping: boolean; - can_source: boolean; - can_vcmove: boolean; - can_help: boolean; - can_about: boolean; -} - -export class UtilityPlugin extends ZeppelinPlugin { +export class UtilityPlugin extends ZeppelinPlugin { public static pluginName = "utility"; + protected static configSchema = ConfigSchema; protected logs: GuildLogs; protected cases: GuildCases; @@ -89,7 +91,7 @@ export class UtilityPlugin extends ZeppelinPlugin { protected lastFullMemberRefresh = 0; - getDefaultOptions(): IPluginOptions { + getDefaultOptions(): IPluginOptions { return { config: { can_roles: false, @@ -834,7 +836,7 @@ export class UtilityPlugin extends ZeppelinPlugin { const highest = Math.round(Math.max(...times)); const lowest = Math.round(Math.min(...times)); - const mean = Math.round(times.reduce((t, v) => t + v, 0) / times.length); + const mean = Math.round(times.reduce((total, ms) => total + ms, 0) / times.length); const shard = this.bot.shards.get(this.bot.guildShardMap[this.guildId]); diff --git a/src/plugins/WelcomeMessage.ts b/src/plugins/WelcomeMessage.ts index 82de2a60..9a27ff14 100644 --- a/src/plugins/WelcomeMessage.ts +++ b/src/plugins/WelcomeMessage.ts @@ -5,19 +5,22 @@ import { renderTemplate } from "../templateFormatter"; import { createChunkedMessage, stripObjectToScalars } from "../utils"; import { LogType } from "../data/LogType"; import { GuildLogs } from "../data/GuildLogs"; +import * as t from "io-ts"; -interface IWelcomeMessageConfig { - send_dm: boolean; - send_to_channel: string; - message: string; -} +const ConfigSchema = t.type({ + send_dm: t.boolean, + send_to_channel: t.string, + message: t.string, +}); +type TConfigSchema = t.TypeOf; -export class WelcomeMessagePlugin extends ZeppelinPlugin { +export class WelcomeMessagePlugin extends ZeppelinPlugin { public static pluginName = "welcome_message"; + protected static configSchema = ConfigSchema; protected logs: GuildLogs; - protected getDefaultOptions(): IPluginOptions { + protected getDefaultOptions(): IPluginOptions { return { config: { send_dm: false,