More documentation work

This commit is contained in:
Dragory 2019-08-22 02:58:32 +03:00
parent 5bbc318c68
commit 1681a45069
33 changed files with 286 additions and 57 deletions

View file

@ -1,16 +1,17 @@
<template>
<pre class="codeblock" v-highlightjs><code v-bind:class="lang" v-trim-code="trim"><slot></slot></code></pre>
<pre class="codeblock" v-highlightjs><code :class="lang" v-trim-code="trim"><slot></slot></code></pre>
</template>
<style scoped>
.codeblock {
padding: 0;
border-radius: 3px;
padding: 16px;
max-width: 970px; /* FIXME: temp fix for overflowing code blocks, look into properly later */
}
.hljs {
background: transparent;
padding: 16px;
padding: 0;
}
</style>

View file

@ -105,8 +105,9 @@
}
.docs-sidebar-content {
position: sticky;
top: 20px;
/* can't scroll with a long list before reaching the end of the page, figure out */
/*position: sticky;*/
/*top: 20px;*/
}
.docs-sidebar .menu {

View file

@ -8,16 +8,28 @@
Name in config: <code>{{ data.name }}</code>
</p>
<div v-if="data.info.description">
<h2 class="z-title is-2 mt-2 mb-1">Description</h2>
<div class="content" v-html="renderMarkdown(data.info.description)"></div>
</div>
<div v-if="data.info.description" class="content" v-html="renderMarkdown(data.info.description)"></div>
<h2 class="z-title is-2 mt-2 mb-1">Default configuration</h2>
<p class="mt-1 mb-1">
To enable this plugin with default configuration, add <code>{{ data.name }}: {}</code> to the <code>plugins</code> list in config
</p>
<h2 id="default-configuration" class="z-title is-2 mt-2 mb-1">Default configuration</h2>
<CodeBlock lang="yaml">{{ renderConfiguration(data.options) }}</CodeBlock>
<b-collapse :open="false" class="card mt-1 mb-1">
<div slot="trigger" slot-scope="props" class="card-header" role="button">
<p class="card-header-title">Config schema</p>
<a class="card-header-icon">
<b-icon :icon="props.open ? 'menu-down' : 'menu-up'"></b-icon>
</a>
</div>
<div class="card-content">
<CodeBlock lang="plain">{{ data.configSchema }}</CodeBlock>
</div>
</b-collapse>
<div v-if="data.commands.length">
<h2 class="z-title is-2 mt-2 mb-1">Commands</h2>
<h2 id="commands" class="z-title is-2 mt-2 mb-1">Commands</h2>
<div v-for="command in data.commands">
<h3 class="z-title is-3 mt-2 mb-1">!{{ command.trigger }}</h3>
<div v-if="command.config.requiredPermission">
@ -30,6 +42,40 @@
Shortcut:
<code style="margin-right: 4px" v-for="alias in command.config.aliases">!{{ alias }}</code>
</div>
<div v-if="command.config.info && command.config.info.description" class="content mt-1 mb-1" v-html="renderMarkdown(command.config.info.description)"></div>
<b-collapse :open="false" class="card mt-1 mb-1">
<div slot="trigger" slot-scope="props" class="card-header" role="button">
<p class="card-header-title">Additional information</p>
<a class="card-header-icon">
<b-icon :icon="props.open ? 'menu-down' : 'menu-up'"></b-icon>
</a>
</div>
<div class="card-content">
Signatures:
<ul class="z-list z-ul">
<li>
<code>
!{{ command.trigger }}
<span v-for="param in command.parameters">{{ renderParameter(param) }} </span>
</code>
</li>
</ul>
<div class="mt-2" v-if="command.parameters.length">
Command arguments:
<ul class="z-list z-ul">
<li v-for="param in command.parameters">
<code>{{ renderParameter(param) }}</code>
<router-link :to="'/docs/descriptions/argument-types#' + (param.type || 'string')">{{ param.type || 'string' }}</router-link>
<div v-if="command.config.info && command.config.info.parameterDescriptions && command.config.info.parameterDescriptions[param.name]" class="content">
{{ renderMarkdown(command.config.info.parameterDescriptions[param.name]) }}
</div>
</li>
</ul>
</div>
</div>
</b-collapse>
</div>
</div>
</div>
@ -59,6 +105,15 @@
[this.pluginName]: options,
});
},
renderParameter(param) {
let str = `${param.name}`;
if (param.rest) str += '...';
if (param.required) {
return `<${str}>`;
} else {
return `[${str}]`;
}
},
},
data() {
return {

View file

@ -3,9 +3,58 @@ import { availablePlugins } from "../plugins/availablePlugins";
import { ZeppelinPlugin } from "../plugins/ZeppelinPlugin";
import { notFound } from "./responses";
import { CommandManager, ICommandConfig } from "knub/dist/CommandManager";
import { dropPropertiesByName, indentLines } from "../utils";
const commandManager = new CommandManager();
function formatConfigSchema(schema) {
if (schema._tag === "InterfaceType" || schema._tag === "PartialType") {
return (
`{\n` +
Object.entries(schema.props)
.map(([k, value]) => indentLines(`${k}: ${formatConfigSchema(value)}`, 2))
.join("\n") +
"\n}"
);
} else if (schema._tag === "DictionaryType") {
return "{\n" + indentLines(`[string]: ${formatConfigSchema(schema.codomain)}`, 2) + "\n}";
} else {
return schema.name;
}
}
function formatTypeName(typeName) {
let result = "";
let indent = 0;
let skip = false;
for (const char of [...typeName]) {
if (skip) {
skip = false;
continue;
}
if (char === "}") {
result += "\n";
indent--;
skip = true;
}
result += char;
if (char === "{") {
result += "\n";
indent++;
skip = true;
}
if (char === ",") {
result += "\n";
skip = true;
}
}
return result;
}
export function initDocs(app: express.Express) {
const docsPlugins = availablePlugins.filter(pluginClass => pluginClass.showInDocs);
@ -60,9 +109,12 @@ export function initDocs(app: express.Express) {
const options = (pluginClass as typeof ZeppelinPlugin).getStaticDefaultOptions();
const configSchema = pluginClass.configSchema && formatConfigSchema(pluginClass.configSchema);
res.json({
name: pluginClass.pluginName,
info: pluginClass.pluginInfo || {},
configSchema,
options,
commands,
});

View file

@ -4,7 +4,7 @@ import { SavedMessage } from "../data/entities/SavedMessage";
import { GuildAutoReactions } from "../data/GuildAutoReactions";
import { Message } from "eris";
import { customEmojiRegex, errorMessage, isEmoji, successMessage } from "../utils";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import * as t from "io-ts";
const ConfigSchema = t.type({
@ -14,7 +14,14 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class AutoReactionsPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "auto_reactions";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Auto-reactions",
description: trimPluginDescription(`
Allows setting up automatic reactions to all new messages on a channel
`),
};
protected savedMessages: GuildSavedMessages;
protected autoReactions: GuildAutoReactions;

View file

@ -309,7 +309,7 @@ const inviteCache = new SimpleCache(10 * MINUTES);
export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "automod";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static dependencies = ["mod_actions", "mutes"];
protected unloaded = false;

View file

@ -23,7 +23,7 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
*/
export class BotControlPlugin extends GlobalZeppelinPlugin<TConfigSchema> {
public static pluginName = "bot_control";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
protected archives: GuildArchives;

View file

@ -4,7 +4,7 @@ import { CaseTypes } from "../data/CaseTypes";
import { Case } from "../data/entities/Case";
import moment from "moment-timezone";
import { CaseTypeColors } from "../data/CaseTypeColors";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildArchives } from "../data/GuildArchives";
import { IPluginOptions } from "knub";
import { GuildLogs } from "../data/GuildLogs";
@ -45,7 +45,14 @@ export type CaseNoteArgs = {
export class CasesPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "cases";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Cases",
description: trimPluginDescription(`
This plugin contains basic configuration for cases created by other plugins
`),
};
protected cases: GuildCases;
protected archives: GuildArchives;

View file

@ -14,7 +14,7 @@ import {
import { ZalgoRegex } from "../data/Zalgo";
import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import cloneDeep from "lodash.clonedeep";
import * as t from "io-ts";
import { TSafeRegex } from "../validatorUtils";
@ -38,7 +38,14 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class CensorPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "censor";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Censor",
description: trimPluginDescription(`
Censor words, tokens, links, regex, etc.
`),
};
protected serverLogs: GuildLogs;
protected savedMessages: GuildSavedMessages;

View file

@ -15,6 +15,7 @@ const MAX_ATTACHMENT_REHOST_SIZE = 1024 * 1024 * 8;
export class ChannelArchiverPlugin extends ZeppelinPlugin {
public static pluginName = "channel_archiver";
public static showInDocs = false;
protected isOwner(userId) {
const owners = this.knub.getGlobalConfig().owners || [];

View file

@ -1,5 +1,5 @@
import { decorators as d, IPluginOptions, logger } from "knub";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import { Member, Channel, GuildChannel, PermissionOverwrite, Permission, Message, TextChannel } from "eris";
import * as t from "io-ts";
import { tNullable } from "../utils";
@ -28,7 +28,16 @@ const defaultCompanionChannelOpts: Partial<TCompanionChannelOpts> = {
export class CompanionChannelPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "companion_channels";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Companion channels",
description: trimPluginDescription(`
Set up 'companion channels' between text and voice channels.
Once set up, any time a user joins one of the specified voice channels,
they'll get channel permissions applied to them for the text channels.
`),
};
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
return {

View file

@ -72,7 +72,7 @@ export class CustomEventsPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "custom_events";
public static showInDocs = false;
public static dependencies = ["cases"];
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
private clearTriggers: () => void;

View file

@ -13,7 +13,7 @@ import { mergeConfig } from "knub/dist/configUtils";
const SLOW_RESOLVE_THRESHOLD = 1500;
export class GlobalZeppelinPlugin<TConfig extends {} = IBasePluginConfig> extends GlobalPlugin<TConfig> {
protected static configSchema: t.TypeC<any>;
public static configSchema: t.TypeC<any>;
public static dependencies = [];
/**

View file

@ -1,5 +1,5 @@
import { decorators as d, IPluginOptions, getInviteLink, logger } from "knub";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import humanizeDuration from "humanize-duration";
import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User } from "eris";
import { GuildVCAlerts } from "../data/GuildVCAlerts";
@ -17,7 +17,16 @@ const ALERT_LOOP_TIME = 30 * 1000;
export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "locate_user";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Locate user",
description: trimPluginDescription(`
This plugin allows users with access to the commands the following:
* Instantly receive an invite to the voice channel of a user
* Be notified as soon as a user switches or joins a voice channel
`),
};
private alerts: GuildVCAlerts;
private outdatedAlertsTimeout;
@ -68,7 +77,11 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
});
}
@d.command("where", "<member:resolvedMember>", {})
@d.command("where", "<member:resolvedMember>", {
info: {
description: "Posts an instant invite to the voice channel that `<member>` is in",
},
})
@d.permission("can_where")
async whereCmd(msg: Message, args: { member: Member; time?: number; reminder?: string }) {
const member = await resolveMember(this.bot, this.guild, args.member.id);
@ -77,6 +90,9 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
@d.command("vcalert", "<member:resolvedMember> [duration:delay] [reminder:string$]", {
aliases: ["vca"],
info: {
description: "Sets up an alert that notifies you any time `<member>` switches or joins voice channels",
},
})
@d.permission("can_alert")
async vcalertCmd(msg: Message, args: { member: Member; duration?: number; reminder?: string }) {

View file

@ -22,7 +22,7 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage";
import { GuildArchives } from "../data/GuildArchives";
import { GuildCases } from "../data/GuildCases";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import { renderTemplate, TemplateParseError } from "../templateFormatter";
import cloneDeep from "lodash.clonedeep";
import * as t from "io-ts";
@ -53,7 +53,11 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class LogsPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "logs";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Logs",
};
protected guildLogs: GuildLogs;
protected savedMessages: GuildSavedMessages;

View file

@ -13,7 +13,7 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class MessageSaverPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "message_saver";
public static showInDocs = false;
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
protected savedMessages: GuildSavedMessages;

View file

@ -26,7 +26,7 @@ import { GuildMutes } from "../data/GuildMutes";
import { CaseTypes } from "../data/CaseTypes";
import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin";
import { Case } from "../data/entities/Case";
import { renderTemplate } from "../templateFormatter";
import { CaseArgs, CasesPlugin } from "./Cases";
@ -108,18 +108,13 @@ type WarnMemberNotifyRetryCallback = () => boolean | Promise<boolean>;
export class ModActionsPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "mod_actions";
public static dependencies = ["cases", "mutes"];
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Mod actions",
description: trimIndents(
trimEmptyStartEndLines(`
Testing **things**
Multiline haHAA
description: trimPluginDescription(`
This plugin contains the 'typical' mod actions such as warning, muting, kicking, banning, etc.
`),
6,
),
};
protected mutes: GuildMutes;

View file

@ -64,7 +64,11 @@ const FIRST_CHECK_INCREMENT = 5 * 1000;
export class MutesPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "mutes";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Mutes",
};
protected mutes: GuildMutes;
protected cases: GuildCases;

View file

@ -14,7 +14,7 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class NameHistoryPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "name_history";
public static showInDocs = false;
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
protected nicknameHistory: GuildNicknameHistory;
protected usernameHistory: UsernameHistory;

View file

@ -17,7 +17,11 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class PersistPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "persist";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Persist",
};
protected persistedData: GuildPersistedData;
protected logs: GuildLogs;

View file

@ -15,7 +15,11 @@ const TIMEOUT = 10 * 1000;
export class PingableRolesPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "pingable_roles";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Pingable roles",
};
protected pingableRoles: GuildPingableRoles;
protected cache: Map<string, PingableRole[]>;

View file

@ -38,7 +38,11 @@ const SCHEDULED_POST_PREVIEW_TEXT_LENGTH = 50;
export class PostPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "post";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Post",
};
protected savedMessages: GuildSavedMessages;
protected scheduledPosts: GuildScheduledPosts;

View file

@ -44,7 +44,11 @@ type PendingMemberRoleChanges = {
export class ReactionRolesPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "reaction_roles";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Reaction roles",
};
protected reactionRoles: GuildReactionRoles;
protected savedMessages: GuildSavedMessages;

View file

@ -24,7 +24,11 @@ const MAX_TRIES = 3;
export class RemindersPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "reminders";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Reminders",
};
protected reminders: GuildReminders;
protected tries: Map<number, number>;

View file

@ -15,7 +15,7 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class SelfGrantableRolesPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "self_grantable_roles";
public static showInDocs = false;
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
protected selfGrantableRoles: GuildSelfGrantableRoles;

View file

@ -33,7 +33,11 @@ const BOT_SLOWMODE_CLEAR_INTERVAL = 60 * 1000;
export class SlowmodePlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "slowmode";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Slowmode",
};
protected slowmodes: GuildSlowmodes;
protected savedMessages: GuildSavedMessages;

View file

@ -74,7 +74,11 @@ const SPAM_ARCHIVE_EXPIRY_DAYS = 90;
export class SpamPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "spam";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Spam protection",
};
protected logs: GuildLogs;
protected archives: GuildArchives;

View file

@ -26,7 +26,7 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "starboard";
public static showInDocs = false;
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
protected starboards: GuildStarboards;
protected savedMessages: GuildSavedMessages;

View file

@ -23,7 +23,11 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class TagsPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "tags";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Tags",
};
protected archives: GuildArchives;
protected tags: GuildTags;

View file

@ -82,7 +82,11 @@ type MemberSearchParams = {
export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "utility";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Utility",
};
protected logs: GuildLogs;
protected cases: GuildCases;
@ -698,7 +702,9 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
msg.channel.createMessage({ embed });
}
@d.command(/(?:nickname|nick) reset/, "<member:resolvedMember>")
@d.command("nickname reset", "<member:resolvedMember>", {
aliases: ["nick reset"],
})
@d.permission("can_nickname")
async nicknameResetCmd(msg: Message, args: { member: Member }) {
if (msg.member.id !== args.member.id && !this.canActOn(msg.member, args.member)) {
@ -718,7 +724,9 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
msg.channel.createMessage(successMessage(`The nickname of <@!${args.member.id}> has been reset`));
}
@d.command(/nickname|nick/, "<member:resolvedMember> <nickname:string$>")
@d.command("nickname", "<member:resolvedMember> <nickname:string$>", {
aliases: ["nick"],
})
@d.permission("can_nickname")
async nicknameCmd(msg: Message, args: { member: Member; nickname: string }) {
if (msg.member.id !== args.member.id && !this.canActOn(msg.member, args.member)) {

View file

@ -16,11 +16,15 @@ type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
export class WelcomeMessagePlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "welcome_message";
protected static configSchema = ConfigSchema;
public static configSchema = ConfigSchema;
public static pluginInfo = {
prettyName: "Welcome message",
};
protected logs: GuildLogs;
protected getDefaultOptions(): IPluginOptions<TConfigSchema> {
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
return {
config: {
send_dm: false,

View file

@ -11,6 +11,8 @@ import {
resolveMember,
resolveUser,
resolveUserId,
trimEmptyStartEndLines,
trimIndents,
UnknownUser,
} from "../utils";
import { Member, User } from "eris";
@ -34,11 +36,15 @@ export interface CommandInfo {
};
}
export function trimPluginDescription(str) {
return trimIndents(trimEmptyStartEndLines(str), 6);
}
export class ZeppelinPlugin<TConfig extends {} = IBasePluginConfig> extends Plugin<TConfig> {
public static pluginInfo: PluginInfo;
public static showInDocs: boolean = true;
protected static configSchema: t.TypeC<any>;
public static configSchema: t.TypeC<any>;
public static dependencies = [];
protected throwPluginRuntimeError(message: string) {

View file

@ -39,7 +39,16 @@ export const HOURS = 60 * MINUTES;
export const DAYS = 24 * HOURS;
export function tNullable<T extends t.Type<any, any, unknown>>(type: T) {
return t.union([type, t.undefined, t.null]);
return t.union([type, t.undefined, t.null], type.name);
}
export function dropPropertiesByName(obj, propName) {
if (obj.hasOwnProperty(propName)) delete obj[propName];
for (const value of Object.values(obj)) {
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
dropPropertiesByName(value, propName);
}
}
}
/**
@ -275,6 +284,17 @@ export function trimIndents(str: string, indentLength: number) {
.join("\n");
}
export function indentLine(str: string, indentLength: number) {
return " ".repeat(indentLength) + str;
}
export function indentLines(str: string, indentLength: number) {
return str
.split("\n")
.map(line => indentLine(line, indentLength))
.join("\n");
}
export const emptyEmbedValue = "\u200b";
export const embedPadding = "\n" + emptyEmbedValue;