Add types for all plugin configs and permissions. Update code to work with Knub 18.

This commit is contained in:
Dragory 2019-03-04 21:44:04 +02:00
parent c96e98fcef
commit b7b42705f9
22 changed files with 415 additions and 225 deletions

View file

@ -1,4 +1,4 @@
import { decorators as d } from "knub"; import { decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage"; import { SavedMessage } from "../data/entities/SavedMessage";
import { GuildAutoReactions } from "../data/GuildAutoReactions"; import { GuildAutoReactions } from "../data/GuildAutoReactions";
@ -6,7 +6,11 @@ import { Message } from "eris";
import { customEmojiRegex, errorMessage, isEmoji, successMessage } from "../utils"; import { customEmojiRegex, errorMessage, isEmoji, successMessage } from "../utils";
import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { ZeppelinPlugin } from "./ZeppelinPlugin";
export class AutoReactionsPlugin extends ZeppelinPlugin { interface IAutoReactionsPluginPermissions {
use: boolean;
}
export class AutoReactionsPlugin extends ZeppelinPlugin<IBasePluginConfig, IAutoReactionsPluginPermissions> {
public static pluginName = "auto_reactions"; public static pluginName = "auto_reactions";
protected savedMessages: GuildSavedMessages; protected savedMessages: GuildSavedMessages;
@ -14,8 +18,10 @@ export class AutoReactionsPlugin extends ZeppelinPlugin {
private onMessageCreateFn; private onMessageCreateFn;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IAutoReactionsPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
use: false, use: false,
}, },

View file

@ -1,4 +1,4 @@
import { decorators as d, GlobalPlugin } from "knub"; import { decorators as d, GlobalPlugin, IPluginOptions } from "knub";
import child_process from "child_process"; import child_process from "child_process";
import { GuildChannel, Message, TextChannel } from "eris"; import { GuildChannel, Message, TextChannel } from "eris";
import { createChunkedMessage, errorMessage, noop, sleep, sorter, successMessage } from "../utils"; import { createChunkedMessage, errorMessage, noop, sleep, sorter, successMessage } from "../utils";
@ -6,17 +6,25 @@ import { ReactionRolesPlugin } from "./ReactionRoles";
let activeReload: [string, string] = null; let activeReload: [string, string] = null;
interface IBotControlPluginConfig {
owners: string[];
update_cmd: string;
}
/** /**
* A global plugin that allows bot owners to control the bot * A global plugin that allows bot owners to control the bot
*/ */
export class BotControlPlugin extends GlobalPlugin { export class BotControlPlugin extends GlobalPlugin<IBotControlPluginConfig> {
public static pluginName = "bot_control"; public static pluginName = "bot_control";
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBotControlPluginConfig> {
return { return {
config: { config: {
owners: [], owners: [],
update_cmd: null,
}, },
permissions: {},
}; };
} }
@ -36,14 +44,14 @@ export class BotControlPlugin extends GlobalPlugin {
} }
isOwner(userId) { isOwner(userId) {
return this.configValue("owners").includes(userId); return this.getConfig().owners.includes(userId);
} }
@d.command("bot_full_update") @d.command("bot_full_update")
async fullUpdateCmd(msg: Message) { async fullUpdateCmd(msg: Message) {
if (!this.isOwner(msg.author.id)) return; if (!this.isOwner(msg.author.id)) return;
const updateCmd = this.configValue("update_cmd"); const updateCmd = this.getConfig().update_cmd;
if (!updateCmd) { if (!updateCmd) {
msg.channel.createMessage(errorMessage("Update command not specified!")); msg.channel.createMessage(errorMessage("Update command not specified!"));
return; return;

View file

@ -7,20 +7,28 @@ import { CaseTypeColors } from "../data/CaseTypeColors";
import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildActions } from "../data/GuildActions"; import { GuildActions } from "../data/GuildActions";
import { GuildArchives } from "../data/GuildArchives"; import { GuildArchives } from "../data/GuildArchives";
import { IPluginOptions } from "knub";
export class CasesPlugin extends ZeppelinPlugin { interface ICasesPluginConfig {
log_automatic_actions: boolean;
case_log_channel: string;
}
export class CasesPlugin extends ZeppelinPlugin<ICasesPluginConfig> {
public static pluginName = "cases"; public static pluginName = "cases";
protected actions: GuildActions; protected actions: GuildActions;
protected cases: GuildCases; protected cases: GuildCases;
protected archives: GuildArchives; protected archives: GuildArchives;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<ICasesPluginConfig> {
return { return {
config: { config: {
log_automatic_actions: true, log_automatic_actions: true,
case_log_channel: null, case_log_channel: null,
}, },
permissions: {},
}; };
} }
@ -103,11 +111,9 @@ export class CasesPlugin extends ZeppelinPlugin {
await this.createCaseNote(createdCase, modId, reason, automatic, false); await this.createCaseNote(createdCase, modId, reason, automatic, false);
} }
if ( const config = this.getConfig();
this.configValue("case_log_channel") &&
(!automatic || this.configValue("log_automatic_actions")) && if (config.case_log_channel && (!automatic || config.log_automatic_actions) && postInCaseLogOverride !== false) {
postInCaseLogOverride !== false
) {
try { try {
await this.postCaseToCaseLogChannel(createdCase); await this.postCaseToCaseLogChannel(createdCase);
} catch (e) {} // tslint:disable-line } catch (e) {} // tslint:disable-line
@ -154,7 +160,7 @@ export class CasesPlugin extends ZeppelinPlugin {
this.archives.makePermanent(archiveId); this.archives.makePermanent(archiveId);
} }
if ((!automatic || this.configValue("log_automatic_actions")) && postInCaseLogOverride !== false) { if ((!automatic || this.getConfig().log_automatic_actions) && postInCaseLogOverride !== false) {
try { try {
await this.postCaseToCaseLogChannel(theCase.id); await this.postCaseToCaseLogChannel(theCase.id);
} catch (e) {} // tslint:disable-line } catch (e) {} // tslint:disable-line
@ -225,7 +231,7 @@ export class CasesPlugin extends ZeppelinPlugin {
* Returns silently if the case log channel isn't specified or is invalid. * Returns silently if the case log channel isn't specified or is invalid.
*/ */
public postToCaseLogChannel(content: MessageContent, file: MessageFile = null): Promise<Message> { public postToCaseLogChannel(content: MessageContent, file: MessageFile = null): Promise<Message> {
const caseLogChannelId = this.configValue("case_log_channel"); const caseLogChannelId = this.getConfig().case_log_channel;
if (!caseLogChannelId) return; if (!caseLogChannelId) return;
const caseLogChannel = this.guild.channels.get(caseLogChannelId); const caseLogChannel = this.guild.channels.get(caseLogChannelId);

View file

@ -1,4 +1,4 @@
import { decorators as d, Plugin } from "knub"; import { decorators as d, IPluginOptions, Plugin } from "knub";
import { Invite, Message } from "eris"; import { Invite, Message } from "eris";
import escapeStringRegexp from "escape-string-regexp"; import escapeStringRegexp from "escape-string-regexp";
import { GuildLogs } from "../data/GuildLogs"; import { GuildLogs } from "../data/GuildLogs";
@ -13,8 +13,27 @@ import {
import { ZalgoRegex } from "../data/Zalgo"; import { ZalgoRegex } from "../data/Zalgo";
import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage"; import { SavedMessage } from "../data/entities/SavedMessage";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
export class CensorPlugin extends Plugin { 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;
filter_domains: boolean;
domain_whitelist: string[];
domain_blacklist: string[];
blocked_tokens: string[];
blocked_words: string[];
blocked_regex: string[];
}
export class CensorPlugin extends ZeppelinPlugin<ICensorPluginConfig> {
public static pluginName = "censor"; public static pluginName = "censor";
protected serverLogs: GuildLogs; protected serverLogs: GuildLogs;
@ -23,7 +42,7 @@ export class CensorPlugin extends Plugin {
private onMessageCreateFn; private onMessageCreateFn;
private onMessageUpdateFn; private onMessageUpdateFn;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<ICensorPluginConfig> {
return { return {
config: { config: {
filter_zalgo: false, filter_zalgo: false,
@ -43,6 +62,8 @@ export class CensorPlugin extends Plugin {
blocked_regex: null, blocked_regex: null,
}, },
permissions: {},
overrides: [ overrides: [
{ {
level: ">=50", level: ">=50",
@ -97,12 +118,10 @@ export class CensorPlugin extends Plugin {
async applyFiltersToMsg(savedMessage: SavedMessage) { async applyFiltersToMsg(savedMessage: SavedMessage) {
if (!savedMessage.data.content) return; if (!savedMessage.data.content) return;
const config = this.getConfigForMemberIdAndChannelId(savedMessage.user_id, savedMessage.channel_id);
// Filter zalgo // Filter zalgo
const filterZalgo = this.configValueForMemberIdAndChannelId( const filterZalgo = config.filter_zalgo;
savedMessage.user_id,
savedMessage.channel_id,
"filter_zalgo",
);
if (filterZalgo) { if (filterZalgo) {
const result = ZalgoRegex.exec(savedMessage.data.content); const result = ZalgoRegex.exec(savedMessage.data.content);
if (result) { if (result) {
@ -112,37 +131,13 @@ export class CensorPlugin extends Plugin {
} }
// Filter invites // Filter invites
const filterInvites = this.configValueForMemberIdAndChannelId( const filterInvites = config.filter_invites;
savedMessage.user_id,
savedMessage.channel_id,
"filter_invites",
);
if (filterInvites) { if (filterInvites) {
const inviteGuildWhitelist: string[] = this.configValueForMemberIdAndChannelId( const inviteGuildWhitelist = config.invite_guild_whitelist;
savedMessage.user_id, const inviteGuildBlacklist = config.invite_guild_blacklist;
savedMessage.channel_id, const inviteCodeWhitelist = config.invite_code_whitelist;
"invite_guild_whitelist", const inviteCodeBlacklist = config.invite_code_blacklist;
); const allowGroupDMInvites = config.allow_group_dm_invites;
const inviteGuildBlacklist: string[] = this.configValueForMemberIdAndChannelId(
savedMessage.user_id,
savedMessage.channel_id,
"invite_guild_blacklist",
);
const inviteCodeWhitelist: string[] = this.configValueForMemberIdAndChannelId(
savedMessage.user_id,
savedMessage.channel_id,
"invite_code_whitelist",
);
const inviteCodeBlacklist: string[] = this.configValueForMemberIdAndChannelId(
savedMessage.user_id,
savedMessage.channel_id,
"invite_code_blacklist",
);
const allowGroupDMInvites: boolean = this.configValueForMemberIdAndChannelId(
savedMessage.user_id,
savedMessage.channel_id,
"allow_group_dm_invites",
);
const inviteCodes = getInviteCodesInString(savedMessage.data.content); const inviteCodes = getInviteCodesInString(savedMessage.data.content);
@ -185,22 +180,10 @@ export class CensorPlugin extends Plugin {
} }
// Filter domains // Filter domains
const filterDomains = this.configValueForMemberIdAndChannelId( const filterDomains = config.filter_domains;
savedMessage.user_id,
savedMessage.channel_id,
"filter_domains",
);
if (filterDomains) { if (filterDomains) {
const domainWhitelist: string[] = this.configValueForMemberIdAndChannelId( const domainWhitelist = config.domain_whitelist;
savedMessage.user_id, const domainBlacklist = config.domain_blacklist;
savedMessage.channel_id,
"domain_whitelist",
);
const domainBlacklist: string[] = this.configValueForMemberIdAndChannelId(
savedMessage.user_id,
savedMessage.channel_id,
"domain_blacklist",
);
const urls = getUrlsInString(savedMessage.data.content); const urls = getUrlsInString(savedMessage.data.content);
for (const thisUrl of urls) { for (const thisUrl of urls) {
@ -217,8 +200,7 @@ export class CensorPlugin extends Plugin {
} }
// Filter tokens // Filter tokens
const blockedTokens = const blockedTokens = config.blocked_tokens || [];
this.configValueForMemberIdAndChannelId(savedMessage.user_id, savedMessage.channel_id, "blocked_tokens") || [];
for (const token of blockedTokens) { for (const token of blockedTokens) {
if (savedMessage.data.content.toLowerCase().includes(token.toLowerCase())) { if (savedMessage.data.content.toLowerCase().includes(token.toLowerCase())) {
this.censorMessage(savedMessage, `blocked token (\`${token}\`) found`); this.censorMessage(savedMessage, `blocked token (\`${token}\`) found`);
@ -227,8 +209,7 @@ export class CensorPlugin extends Plugin {
} }
// Filter words // Filter words
const blockedWords = const blockedWords = config.blocked_words || [];
this.configValueForMemberIdAndChannelId(savedMessage.user_id, savedMessage.channel_id, "blocked_words") || [];
for (const word of blockedWords) { for (const word of blockedWords) {
const regex = new RegExp(`\\b${escapeStringRegexp(word)}\\b`, "i"); const regex = new RegExp(`\\b${escapeStringRegexp(word)}\\b`, "i");
if (regex.test(savedMessage.data.content)) { if (regex.test(savedMessage.data.content)) {
@ -238,8 +219,7 @@ export class CensorPlugin extends Plugin {
} }
// Filter regex // Filter regex
const blockedRegex = const blockedRegex = config.blocked_regex || [];
this.configValueForMemberIdAndChannelId(savedMessage.user_id, savedMessage.channel_id, "blocked_regex") || [];
for (const regexStr of blockedRegex) { for (const regexStr of blockedRegex) {
const regex = new RegExp(regexStr, "i"); const regex = new RegExp(regexStr, "i");
if (regex.test(savedMessage.data.content)) { if (regex.test(savedMessage.data.content)) {

View file

@ -1,5 +1,5 @@
import http, { ServerResponse } from "http"; import http, { ServerResponse } from "http";
import { GlobalPlugin } from "knub"; import { GlobalPlugin, IPluginOptions } from "knub";
import { GuildArchives } from "../data/GuildArchives"; import { GuildArchives } from "../data/GuildArchives";
import { sleep } from "../utils"; import { sleep } from "../utils";
import moment from "moment-timezone"; import moment from "moment-timezone";
@ -12,12 +12,26 @@ function notFound(res: ServerResponse) {
res.end("Not Found"); res.end("Not Found");
} }
export class LogServerPlugin extends GlobalPlugin { interface ILogServerPluginConfig {
port: number;
}
export class LogServerPlugin extends GlobalPlugin<ILogServerPluginConfig> {
public static pluginName = "log_server"; public static pluginName = "log_server";
protected archives: GuildArchives; protected archives: GuildArchives;
protected server: http.Server; protected server: http.Server;
protected getDefaultOptions(): IPluginOptions<ILogServerPluginConfig> {
return {
config: {
port: DEFAULT_PORT,
},
permissions: {},
};
}
async onLoad() { async onLoad() {
this.archives = new GuildArchives(null); this.archives = new GuildArchives(null);
@ -55,6 +69,7 @@ export class LogServerPlugin extends GlobalPlugin {
} }
}); });
const port = this.getConfig().port;
let retried = false; let retried = false;
this.server.on("error", async (err: any) => { this.server.on("error", async (err: any) => {
@ -62,13 +77,13 @@ export class LogServerPlugin extends GlobalPlugin {
console.log("Got EADDRINUSE, retrying in 2 sec..."); console.log("Got EADDRINUSE, retrying in 2 sec...");
retried = true; retried = true;
await sleep(2000); await sleep(2000);
this.server.listen(this.configValue("port", DEFAULT_PORT)); this.server.listen(port);
} else { } else {
throw err; throw err;
} }
}); });
this.server.listen(this.configValue("port", DEFAULT_PORT)); this.server.listen(port);
} }
async onUnload() { async onUnload() {

View file

@ -1,4 +1,4 @@
import { decorators as d, Plugin } from "knub"; import { decorators as d, IPluginOptions, Plugin } from "knub";
import { GuildLogs } from "../data/GuildLogs"; import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType"; import { LogType } from "../data/LogType";
import { Channel, Constants as ErisConstants, Member, Message, TextChannel, User } from "eris"; import { Channel, Constants as ErisConstants, Member, Message, TextChannel, User } from "eris";
@ -22,6 +22,7 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage"; import { SavedMessage } from "../data/entities/SavedMessage";
import { GuildArchives } from "../data/GuildArchives"; import { GuildArchives } from "../data/GuildArchives";
import { GuildCases } from "../data/GuildCases"; import { GuildCases } from "../data/GuildCases";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
interface ILogChannel { interface ILogChannel {
include?: string[]; include?: string[];
@ -40,7 +41,24 @@ const unknownUser = {
discriminator: "0000", discriminator: "0000",
}; };
export class LogsPlugin extends Plugin { interface IChannelConfig {
include?: string[];
exclude?: string[];
batched?: boolean;
batch_time?: number;
}
interface ILogsPluginConfig {
channels: {
[key: string]: IChannelConfig;
};
format: {
[key: string]: string;
timestamp: string;
};
}
export class LogsPlugin extends ZeppelinPlugin<ILogsPluginConfig> {
public static pluginName = "logs"; public static pluginName = "logs";
protected guildLogs: GuildLogs; protected guildLogs: GuildLogs;
@ -56,7 +74,7 @@ export class LogsPlugin extends Plugin {
private onMessageDeleteBulkFn; private onMessageDeleteBulkFn;
private onMessageUpdateFn; private onMessageUpdateFn;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<ILogsPluginConfig> {
return { return {
config: { config: {
channels: {}, channels: {},
@ -65,6 +83,8 @@ export class LogsPlugin extends Plugin {
...DefaultLogMessages, ...DefaultLogMessages,
}, },
}, },
permissions: {},
}; };
} }
@ -98,7 +118,7 @@ export class LogsPlugin extends Plugin {
} }
async log(type, data) { async log(type, data) {
const logChannels: ILogChannelMap = this.configValue("channels"); const logChannels: ILogChannelMap = this.getConfig().channels;
const typeStr = LogType[type]; const typeStr = LogType[type];
for (const [channelId, opts] of Object.entries(logChannels)) { for (const [channelId, opts] of Object.entries(logChannels)) {
@ -130,12 +150,13 @@ export class LogsPlugin extends Plugin {
} }
getLogMessage(type, data): string { getLogMessage(type, data): string {
const format = this.configValue(`format.${LogType[type]}`, ""); const config = this.getConfig();
const format = config.format[LogType[type]] || "";
if (format === "") return; if (format === "") return;
const formatted = formatTemplateString(format, data); const formatted = formatTemplateString(format, data);
const timestampFormat = this.configValue("format.timestamp"); const timestampFormat = config.format.timestamp;
if (timestampFormat) { if (timestampFormat) {
const timestamp = moment().format(timestampFormat); const timestamp = moment().format(timestampFormat);
return `\`[${timestamp}]\` ${formatted}`; return `\`[${timestamp}]\` ${formatted}`;
@ -387,7 +408,7 @@ export class LogsPlugin extends Plugin {
member: stripObjectToScalars(member, ["user"]), member: stripObjectToScalars(member, ["user"]),
channel: stripObjectToScalars(channel), channel: stripObjectToScalars(channel),
messageText: disableCodeBlocks(deactivateMentions(savedMessage.data.content || "<no text content>")), messageText: disableCodeBlocks(deactivateMentions(savedMessage.data.content || "<no text content>")),
messageDate: moment(savedMessage.data.timestamp, "x").format(this.configValue("format.timestamp")), messageDate: moment(savedMessage.data.timestamp, "x").format(this.getConfig().format.timestamp),
attachments: disableLinkPreviews(useMediaUrls(attachments)), attachments: disableLinkPreviews(useMediaUrls(attachments)),
}, },
savedMessage.id, savedMessage.id,

View file

@ -1,15 +1,21 @@
import { Plugin, decorators as d } from "knub"; import { Plugin, decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { GuildChannel, Message, TextChannel } from "eris"; import { GuildChannel, Message, TextChannel } from "eris";
import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { successMessage } from "../utils"; import { successMessage } from "../utils";
export class MessageSaverPlugin extends Plugin { interface IMessageSaverPluginPermissions {
manage: boolean;
}
export class MessageSaverPlugin extends Plugin<IBasePluginConfig, IMessageSaverPluginPermissions> {
public static pluginName = "message_saver"; public static pluginName = "message_saver";
protected savedMessages: GuildSavedMessages; protected savedMessages: GuildSavedMessages;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IMessageSaverPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
manage: false, manage: false,
}, },

View file

@ -1,4 +1,4 @@
import { decorators as d, waitForReaction, waitForReply } from "knub"; import { decorators as d, IPluginOptions, waitForReaction, waitForReply } from "knub";
import { Attachment, Constants as ErisConstants, Guild, Member, Message, TextChannel, User } from "eris"; import { Attachment, Constants as ErisConstants, Guild, Member, Message, TextChannel, User } from "eris";
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
import { GuildCases } from "../data/GuildCases"; import { GuildCases } from "../data/GuildCases";
@ -48,7 +48,39 @@ const MessageResultText = {
[MessageResult.ChannelMessaged]: "user messaged with a ping", [MessageResult.ChannelMessaged]: "user messaged with a ping",
}; };
export class ModActionsPlugin extends ZeppelinPlugin { interface IModActionsPluginConfig {
dm_on_warn: boolean;
dm_on_mute: boolean;
dm_on_kick: boolean;
dm_on_ban: boolean;
message_on_warn: boolean;
message_on_mute: boolean;
message_on_kick: boolean;
message_on_ban: boolean;
message_channel: string;
warn_message: string;
mute_message: string;
timed_mute_message: string;
kick_message: string;
ban_message: string;
alert_on_rejoin: boolean;
alert_channel: string;
}
interface IModActionsPluginPermissions {
note: boolean;
warn: boolean;
mute: boolean;
kick: boolean;
ban: boolean;
view: boolean;
addcase: boolean;
massban: boolean;
hidecase: boolean;
act_as_other: boolean;
}
export class ModActionsPlugin extends ZeppelinPlugin<IModActionsPluginConfig, IModActionsPluginPermissions> {
public static pluginName = "mod_actions"; public static pluginName = "mod_actions";
protected actions: GuildActions; protected actions: GuildActions;
@ -67,7 +99,7 @@ export class ModActionsPlugin extends ZeppelinPlugin {
this.ignoredEvents = []; this.ignoredEvents = [];
} }
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IModActionsPluginConfig, IModActionsPluginPermissions> {
return { return {
config: { config: {
dm_on_warn: true, dm_on_warn: true,
@ -225,9 +257,11 @@ export class ModActionsPlugin extends ZeppelinPlugin {
*/ */
@d.event("guildMemberAdd") @d.event("guildMemberAdd")
async onGuildMemberAdd(_, member: Member) { async onGuildMemberAdd(_, member: Member) {
if (!this.configValue("alert_on_rejoin")) return; const config = this.getConfig();
const alertChannelId = this.configValue("alert_channel"); if (!config.alert_on_rejoin) return;
const alertChannelId = config.alert_channel;
if (!alertChannelId) return; if (!alertChannelId) return;
const actions = await this.cases.getByUserId(member.id); const actions = await this.cases.getByUserId(member.id);
@ -353,17 +387,16 @@ export class ModActionsPlugin extends ZeppelinPlugin {
mod = args.mod; mod = args.mod;
} }
const config = this.getConfig();
const reason = this.formatReasonWithAttachments(args.reason, msg.attachments); const reason = this.formatReasonWithAttachments(args.reason, msg.attachments);
const warnMessage = this.configValue("warn_message") const warnMessage = config.warn_message.replace("{guildName}", this.guild.name).replace("{reason}", reason);
.replace("{guildName}", this.guild.name)
.replace("{reason}", reason);
const userMessageResult = await this.tryToMessageUser( const userMessageResult = await this.tryToMessageUser(
args.member.user, args.member.user,
warnMessage, warnMessage,
this.configValue("dm_on_warn"), config.dm_on_warn,
this.configValue("message_on_warn"), config.message_on_warn,
); );
if (userMessageResult === MessageResult.Failed) { if (userMessageResult === MessageResult.Failed) {
@ -474,10 +507,12 @@ export class ModActionsPlugin extends ZeppelinPlugin {
await this.mutes.setCaseId(args.member.id, theCase.id); await this.mutes.setCaseId(args.member.id, theCase.id);
} }
const config = this.getConfig();
// Message the user informing them of the mute // Message the user informing them of the mute
// Don't message them if we're updating an old mute // Don't message them if we're updating an old mute
if (reason && !hasOldCase) { if (reason && !hasOldCase) {
const template = muteTime ? this.configValue("timed_mute_message") : this.configValue("mute_message"); const template = muteTime ? config.timed_mute_message : config.mute_message;
const muteMessage = formatTemplateString(template, { const muteMessage = formatTemplateString(template, {
guildName: this.guild.name, guildName: this.guild.name,
@ -488,8 +523,8 @@ export class ModActionsPlugin extends ZeppelinPlugin {
userMessageResult = await this.tryToMessageUser( userMessageResult = await this.tryToMessageUser(
args.member.user, args.member.user,
muteMessage, muteMessage,
this.configValue("dm_on_mute"), config.dm_on_mute,
this.configValue("message_on_mute"), config.message_on_mute,
); );
} }
@ -636,12 +671,13 @@ export class ModActionsPlugin extends ZeppelinPlugin {
mod = args.mod; mod = args.mod;
} }
const config = this.getConfig();
const reason = this.formatReasonWithAttachments(args.reason, msg.attachments); const reason = this.formatReasonWithAttachments(args.reason, msg.attachments);
// Attempt to message the user *before* kicking them, as doing it after may not be possible // Attempt to message the user *before* kicking them, as doing it after may not be possible
let userMessageResult: MessageResult = MessageResult.Ignored; let userMessageResult: MessageResult = MessageResult.Ignored;
if (args.reason) { if (args.reason) {
const kickMessage = formatTemplateString(this.configValue("kick_message"), { const kickMessage = formatTemplateString(config.kick_message, {
guildName: this.guild.name, guildName: this.guild.name,
reason, reason,
}); });
@ -649,8 +685,8 @@ export class ModActionsPlugin extends ZeppelinPlugin {
userMessageResult = await this.tryToMessageUser( userMessageResult = await this.tryToMessageUser(
args.member.user, args.member.user,
kickMessage, kickMessage,
this.configValue("dm_on_kick"), config.dm_on_kick,
this.configValue("message_on_kick"), config.message_on_kick,
); );
} }
@ -705,12 +741,13 @@ export class ModActionsPlugin extends ZeppelinPlugin {
mod = args.mod; mod = args.mod;
} }
const config = this.getConfig();
const reason = this.formatReasonWithAttachments(args.reason, msg.attachments); const reason = this.formatReasonWithAttachments(args.reason, msg.attachments);
// Attempt to message the user *before* banning them, as doing it after may not be possible // Attempt to message the user *before* banning them, as doing it after may not be possible
let userMessageResult: MessageResult = MessageResult.Ignored; let userMessageResult: MessageResult = MessageResult.Ignored;
if (reason) { if (reason) {
const banMessage = formatTemplateString(this.configValue("ban_message"), { const banMessage = formatTemplateString(config.ban_message, {
guildName: this.guild.name, guildName: this.guild.name,
reason, reason,
}); });
@ -718,8 +755,8 @@ export class ModActionsPlugin extends ZeppelinPlugin {
userMessageResult = await this.tryToMessageUser( userMessageResult = await this.tryToMessageUser(
args.member.user, args.member.user,
banMessage, banMessage,
this.configValue("dm_on_ban"), config.dm_on_ban,
this.configValue("message_on_ban"), config.message_on_ban,
); );
} }
@ -1215,9 +1252,11 @@ export class ModActionsPlugin extends ZeppelinPlugin {
} catch (e) {} // tslint:disable-line } catch (e) {} // tslint:disable-line
} }
if (useChannel && this.configValue("message_channel")) { const messageChannel = this.getConfig().message_channel;
if (useChannel && messageChannel) {
try { try {
const channel = this.guild.channels.get(this.configValue("message_channel")) as TextChannel; const channel = this.guild.channels.get(messageChannel) as TextChannel;
await channel.createMessage(`<@!${user.id}> ${str}`); await channel.createMessage(`<@!${user.id}> ${str}`);
return MessageResult.ChannelMessaged; return MessageResult.ChannelMessaged;
} catch (e) {} // tslint:disable-line } catch (e) {} // tslint:disable-line

View file

@ -1,4 +1,4 @@
import { Member, Message, TextableChannel, User } from "eris"; import { Member, Message, User } from "eris";
import { GuildCases } from "../data/GuildCases"; import { GuildCases } from "../data/GuildCases";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { ZeppelinPlugin } from "./ZeppelinPlugin";
@ -8,9 +8,18 @@ import { DBDateFormat, chunkMessageLines, stripObjectToScalars, successMessage,
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
import { LogType } from "../data/LogType"; import { LogType } from "../data/LogType";
import { GuildLogs } from "../data/GuildLogs"; import { GuildLogs } from "../data/GuildLogs";
import { decorators as d } from "knub"; import { decorators as d, IPluginOptions } from "knub";
export class MutesPlugin extends ZeppelinPlugin { interface IMutesPluginConfig {
mute_role: string;
}
interface IMutesPluginPermissions {
view_list: boolean;
cleanup: boolean;
}
export class MutesPlugin extends ZeppelinPlugin<IMutesPluginConfig, IMutesPluginPermissions> {
public static pluginName = "mutes"; public static pluginName = "mutes";
protected actions: GuildActions; protected actions: GuildActions;
@ -19,7 +28,7 @@ export class MutesPlugin extends ZeppelinPlugin {
protected serverLogs: GuildLogs; protected serverLogs: GuildLogs;
private muteClearIntervalId: NodeJS.Timer; private muteClearIntervalId: NodeJS.Timer;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IMutesPluginConfig, IMutesPluginPermissions> {
return { return {
config: { config: {
mute_role: null, mute_role: null,
@ -71,7 +80,7 @@ export class MutesPlugin extends ZeppelinPlugin {
} }
public async muteMember(member: Member, muteTime: number = null) { public async muteMember(member: Member, muteTime: number = null) {
const muteRole = this.configValue("mute_role"); const muteRole = this.getConfig().mute_role;
if (!muteRole) return; if (!muteRole) return;
await member.addRole(muteRole); await member.addRole(muteRole);
@ -82,9 +91,9 @@ export class MutesPlugin extends ZeppelinPlugin {
if (unmuteTime) { if (unmuteTime) {
await this.mutes.addOrUpdateMute(member.id, unmuteTime); await this.mutes.addOrUpdateMute(member.id, unmuteTime);
} else { } else {
const muteRole = this.configValue("mute_role"); const muteRole = this.getConfig().mute_role;
if (member.roles.includes(muteRole)) { if (member.roles.includes(muteRole)) {
await member.removeRole(this.configValue("mute_role")); await member.removeRole(muteRole);
} }
await this.mutes.clear(member.id); await this.mutes.clear(member.id);
@ -139,7 +148,7 @@ export class MutesPlugin extends ZeppelinPlugin {
// Manually added mute roles // Manually added mute roles
const muteUserIds = activeMutes.reduce((set, m) => set.add(m.user_id), new Set()); const muteUserIds = activeMutes.reduce((set, m) => set.add(m.user_id), new Set());
const manuallyMutedMembers = []; const manuallyMutedMembers = [];
const muteRole = this.configValue("mute_role"); const muteRole = this.getConfig().mute_role;
if (muteRole) { if (muteRole) {
this.guild.members.forEach(member => { this.guild.members.forEach(member => {
@ -206,7 +215,7 @@ export class MutesPlugin extends ZeppelinPlugin {
*/ */
@d.event("guildMemberUpdate") @d.event("guildMemberUpdate")
async onGuildMemberUpdate(_, member: Member) { async onGuildMemberUpdate(_, member: Member) {
const muteRole = this.configValue("mute_role"); const muteRole = this.getConfig().mute_role;
if (!muteRole) return; if (!muteRole) return;
const mute = await this.mutes.findExistingMuteForUserId(member.id); const mute = await this.mutes.findExistingMuteForUserId(member.id);
@ -224,7 +233,7 @@ export class MutesPlugin extends ZeppelinPlugin {
@d.permission("cleanup") @d.permission("cleanup")
async clearMutesWithoutRoleCmd(msg: Message) { async clearMutesWithoutRoleCmd(msg: Message) {
const activeMutes = await this.mutes.getActiveMutes(); const activeMutes = await this.mutes.getActiveMutes();
const muteRole = this.configValue("mute_role"); const muteRole = this.getConfig().mute_role;
if (!muteRole) return; if (!muteRole) return;
await msg.channel.createMessage("Clearing mutes from members that don't have the mute role..."); await msg.channel.createMessage("Clearing mutes from members that don't have the mute role...");
@ -264,7 +273,7 @@ export class MutesPlugin extends ZeppelinPlugin {
try { try {
this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, member.id); this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, member.id);
await member.removeRole(this.configValue("mute_role")); await member.removeRole(this.getConfig().mute_role);
} catch (e) {} // tslint:disable-line } catch (e) {} // tslint:disable-line
await this.mutes.clear(member.id); await this.mutes.clear(member.id);

View file

@ -1,16 +1,23 @@
import { Plugin, decorators as d } from "knub"; import { decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { GuildNameHistory } from "../data/GuildNameHistory"; import { GuildNameHistory } from "../data/GuildNameHistory";
import { Member, Message, Relationship, User } from "eris"; import { Member, Message, User } from "eris";
import { NameHistoryEntryTypes } from "../data/NameHistoryEntryTypes"; import { NameHistoryEntryTypes } from "../data/NameHistoryEntryTypes";
import { createChunkedMessage, errorMessage, trimLines } from "../utils"; import { createChunkedMessage, errorMessage, trimLines } from "../utils";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
export class NameHistoryPlugin extends Plugin { interface INameHistoryPluginPermissions {
view: boolean;
}
export class NameHistoryPlugin extends ZeppelinPlugin<IBasePluginConfig, INameHistoryPluginPermissions> {
public static pluginName = "name_history"; public static pluginName = "name_history";
protected nameHistory: GuildNameHistory; protected nameHistory: GuildNameHistory;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, INameHistoryPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
view: false, view: false,
}, },

View file

@ -1,24 +1,33 @@
import { Plugin, decorators as d } from "knub"; import { decorators as d, IPluginOptions } from "knub";
import { GuildPersistedData, IPartialPersistData } from "../data/GuildPersistedData"; import { GuildPersistedData, IPartialPersistData } from "../data/GuildPersistedData";
import intersection from "lodash.intersection"; import intersection from "lodash.intersection";
import { Member, MemberOptions } from "eris"; import { Member, MemberOptions } from "eris";
import { GuildLogs } from "../data/GuildLogs"; import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType"; import { LogType } from "../data/LogType";
import { stripObjectToScalars } from "../utils"; import { stripObjectToScalars } from "../utils";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
export class PersistPlugin extends Plugin { interface IPersistPluginConfig {
persisted_roles: string[];
persist_nicknames: boolean;
persist_voice_mutes: boolean;
}
export class PersistPlugin extends ZeppelinPlugin<IPersistPluginConfig> {
public static pluginName = "persist"; public static pluginName = "persist";
protected persistedData: GuildPersistedData; protected persistedData: GuildPersistedData;
protected logs: GuildLogs; protected logs: GuildLogs;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IPersistPluginConfig> {
return { return {
config: { config: {
persisted_roles: [], persisted_roles: [],
persist_nicknames: false, persist_nicknames: false,
persist_voice_mutes: false, persist_voice_mutes: false,
}, },
permissions: {},
}; };
} }
@ -31,8 +40,9 @@ export class PersistPlugin extends Plugin {
onGuildMemberRemove(_, member: Member) { onGuildMemberRemove(_, member: Member) {
let persist = false; let persist = false;
const persistData: IPartialPersistData = {}; const persistData: IPartialPersistData = {};
const config = this.getConfig();
const persistedRoles = this.configValue("persisted_roles"); const persistedRoles = config.persisted_roles;
if (persistedRoles.length && member.roles) { if (persistedRoles.length && member.roles) {
const rolesToPersist = intersection(persistedRoles, member.roles); const rolesToPersist = intersection(persistedRoles, member.roles);
if (rolesToPersist.length) { if (rolesToPersist.length) {
@ -41,12 +51,12 @@ export class PersistPlugin extends Plugin {
} }
} }
if (this.configValue("persist_nicknames") && member.nick) { if (config.persist_nicknames && member.nick) {
persist = true; persist = true;
persistData.nickname = member.nick; persistData.nickname = member.nick;
} }
if (this.configValue("persist_voice_mutes") && member.voiceState && member.voiceState.mute) { if (config.persist_voice_mutes && member.voiceState && member.voiceState.mute) {
persist = true; persist = true;
persistData.is_voice_muted = true; persistData.is_voice_muted = true;
} }
@ -63,8 +73,9 @@ export class PersistPlugin extends Plugin {
let restore = false; let restore = false;
const toRestore: MemberOptions = {}; const toRestore: MemberOptions = {};
const config = this.getConfig();
const persistedRoles = this.configValue("persisted_roles"); const persistedRoles = config.persisted_roles;
if (persistedRoles.length) { if (persistedRoles.length) {
const rolesToRestore = intersection(persistedRoles, persistedData.roles); const rolesToRestore = intersection(persistedRoles, persistedData.roles);
if (rolesToRestore.length) { if (rolesToRestore.length) {
@ -73,12 +84,12 @@ export class PersistPlugin extends Plugin {
} }
} }
if (this.configValue("persist_nicknames") && persistedData.nickname) { if (config.persist_nicknames && persistedData.nickname) {
restore = true; restore = true;
toRestore.nick = persistedData.nickname; toRestore.nick = persistedData.nickname;
} }
if (this.configValue("persist_voice_mutes") && persistedData.is_voice_muted) { if (config.persist_voice_mutes && persistedData.is_voice_muted) {
restore = true; restore = true;
toRestore.mute = true; toRestore.mute = true;
} }

View file

@ -1,20 +1,27 @@
import { Plugin, decorators as d } from "knub"; import { decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { Message, Role, TextableChannel, User } from "eris"; import { Message, Role, TextableChannel, User } from "eris";
import { GuildPingableRoles } from "../data/GuildPingableRoles"; import { GuildPingableRoles } from "../data/GuildPingableRoles";
import { PingableRole } from "../data/entities/PingableRole"; import { PingableRole } from "../data/entities/PingableRole";
import { errorMessage, successMessage } from "../utils"; import { errorMessage, successMessage } from "../utils";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
const TIMEOUT = 10 * 1000; const TIMEOUT = 10 * 1000;
export class PingableRolesPlugin extends Plugin { interface IPingableRolesPluginPermissions {
use: boolean;
}
export class PingableRolesPlugin extends ZeppelinPlugin<IBasePluginConfig, IPingableRolesPluginPermissions> {
public static pluginName = "pingable_roles"; public static pluginName = "pingable_roles";
protected pingableRoles: GuildPingableRoles; protected pingableRoles: GuildPingableRoles;
protected cache: Map<string, PingableRole[]>; protected cache: Map<string, PingableRole[]>;
protected timeouts: Map<string, any>; protected timeouts: Map<string, any>;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IPingableRolesPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
use: false, use: false,
}, },

View file

@ -1,14 +1,20 @@
import { Plugin, decorators as d } from "knub"; import { Plugin, decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { Channel, EmbedBase, Message, Role, TextChannel } from "eris"; import { Channel, EmbedBase, Message, Role, TextChannel } from "eris";
import { errorMessage, downloadFile, roleMentionRegex, getRoleMentions } from "../utils"; import { errorMessage, downloadFile, getRoleMentions } from "../utils";
import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import fs from "fs"; import fs from "fs";
const fsp = fs.promises; const fsp = fs.promises;
const COLOR_MATCH_REGEX = /^#?([0-9a-f]{6})$/; const COLOR_MATCH_REGEX = /^#?([0-9a-f]{6})$/;
export class PostPlugin extends Plugin { interface IPostPluginPermissions {
post: boolean;
edit: boolean;
}
export class PostPlugin extends ZeppelinPlugin<IBasePluginConfig, IPostPluginPermissions> {
public static pluginName = "post"; public static pluginName = "post";
protected savedMessages: GuildSavedMessages; protected savedMessages: GuildSavedMessages;
@ -17,8 +23,10 @@ export class PostPlugin extends Plugin {
this.savedMessages = GuildSavedMessages.getInstance(this.guildId); this.savedMessages = GuildSavedMessages.getInstance(this.guildId);
} }
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IPostPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
post: false, post: false,
edit: false, edit: false,

View file

@ -1,4 +1,4 @@
import { decorators as d, logger } from "knub"; import { decorators as d, IPluginOptions, logger } from "knub";
import { CustomEmoji, errorMessage, isSnowflake, noop, sleep, successMessage } from "../utils"; import { CustomEmoji, errorMessage, isSnowflake, noop, sleep, successMessage } from "../utils";
import { GuildReactionRoles } from "../data/GuildReactionRoles"; import { GuildReactionRoles } from "../data/GuildReactionRoles";
import { Message, TextChannel } from "eris"; import { Message, TextChannel } from "eris";
@ -25,7 +25,16 @@ type PendingMemberRoleChanges = {
}>; }>;
}; };
export class ReactionRolesPlugin extends ZeppelinPlugin { interface IReactionRolesPluginConfig {
auto_refresh_interval: number;
}
interface IReactionRolesPluginPermissions {
manage: boolean;
fallback_command: boolean;
}
export class ReactionRolesPlugin extends ZeppelinPlugin<IReactionRolesPluginConfig, IReactionRolesPluginPermissions> {
public static pluginName = "reaction_roles"; public static pluginName = "reaction_roles";
protected reactionRoles: GuildReactionRoles; protected reactionRoles: GuildReactionRoles;
@ -37,7 +46,7 @@ export class ReactionRolesPlugin extends ZeppelinPlugin {
private autoRefreshTimeout; private autoRefreshTimeout;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IReactionRolesPluginConfig, IReactionRolesPluginPermissions> {
return { return {
config: { config: {
auto_refresh_interval: null, auto_refresh_interval: null,
@ -66,7 +75,7 @@ export class ReactionRolesPlugin extends ZeppelinPlugin {
this.pendingRoleChanges = new Map(); this.pendingRoleChanges = new Map();
this.pendingRefreshes = new Set(); this.pendingRefreshes = new Set();
let autoRefreshInterval = this.configValue("auto_refresh_interval"); let autoRefreshInterval = this.getConfig().auto_refresh_interval;
if (autoRefreshInterval != null) { if (autoRefreshInterval != null) {
autoRefreshInterval = Math.max(MIN_AUTO_REFRESH, autoRefreshInterval); autoRefreshInterval = Math.max(MIN_AUTO_REFRESH, autoRefreshInterval);
this.autoRefreshLoop(autoRefreshInterval); this.autoRefreshLoop(autoRefreshInterval);

View file

@ -1,4 +1,4 @@
import { decorators as d } from "knub"; import { decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildReminders } from "../data/GuildReminders"; import { GuildReminders } from "../data/GuildReminders";
import { Message, TextChannel } from "eris"; import { Message, TextChannel } from "eris";
@ -9,7 +9,11 @@ import { convertDelayStringToMS, createChunkedMessage, errorMessage, sorter, suc
const REMINDER_LOOP_TIME = 10 * 1000; const REMINDER_LOOP_TIME = 10 * 1000;
const MAX_TRIES = 3; const MAX_TRIES = 3;
export class RemindersPlugin extends ZeppelinPlugin { interface IRemindersPluginPermissions {
use: boolean;
}
export class RemindersPlugin extends ZeppelinPlugin<IBasePluginConfig, IRemindersPluginPermissions> {
public static pluginName = "reminders"; public static pluginName = "reminders";
protected reminders: GuildReminders; protected reminders: GuildReminders;
@ -17,8 +21,10 @@ export class RemindersPlugin extends ZeppelinPlugin {
private postRemindersTimeout; private postRemindersTimeout;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IRemindersPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
use: false, use: false,
}, },

View file

@ -1,15 +1,24 @@
import { Plugin, decorators as d } from "knub"; import { Plugin, decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { GuildSelfGrantableRoles } from "../data/GuildSelfGrantableRoles"; import { GuildSelfGrantableRoles } from "../data/GuildSelfGrantableRoles";
import { GuildChannel, Message, Role, TextChannel } from "eris"; import { GuildChannel, Message, Role, TextChannel } from "eris";
import { chunkArray, errorMessage, sorter, successMessage } from "../utils"; import { chunkArray, errorMessage, sorter, successMessage } from "../utils";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
export class SelfGrantableRolesPlugin extends Plugin { interface ISelfGrantableRolesPluginPermissions {
manage: boolean;
use: boolean;
ignore_cooldown: boolean;
}
export class SelfGrantableRolesPlugin extends ZeppelinPlugin<IBasePluginConfig, ISelfGrantableRolesPluginPermissions> {
public static pluginName = "self_grantable_roles"; public static pluginName = "self_grantable_roles";
protected selfGrantableRoles: GuildSelfGrantableRoles; protected selfGrantableRoles: GuildSelfGrantableRoles;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, ISelfGrantableRolesPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
manage: false, manage: false,
use: false, use: false,

View file

@ -1,17 +1,25 @@
import { Plugin, decorators as d } from "knub"; import { Plugin, decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { GuildChannel, Message, TextChannel, Constants as ErisConstants, User } from "eris"; import { GuildChannel, Message, TextChannel, Constants as ErisConstants, User } from "eris";
import { convertDelayStringToMS, errorMessage, noop, successMessage } from "../utils"; import { convertDelayStringToMS, errorMessage, noop, successMessage } from "../utils";
import { GuildSlowmodes } from "../data/GuildSlowmodes"; import { GuildSlowmodes } from "../data/GuildSlowmodes";
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
export class SlowmodePlugin extends Plugin { interface ISlowmodePluginPermissions {
manage: boolean;
affected: boolean;
}
export class SlowmodePlugin extends ZeppelinPlugin<IBasePluginConfig, ISlowmodePluginPermissions> {
public static pluginName = "slowmode"; public static pluginName = "slowmode";
protected slowmodes: GuildSlowmodes; protected slowmodes: GuildSlowmodes;
protected clearInterval; protected clearInterval;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, ISlowmodePluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
manage: false, manage: false,
affected: true, affected: true,

View file

@ -1,4 +1,4 @@
import { decorators as d, Plugin } from "knub"; import { decorators as d, IPluginOptions, Plugin } from "knub";
import { Channel, Member, User } from "eris"; import { Channel, Member, User } from "eris";
import { import {
getEmojiInString, getEmojiInString,
@ -18,6 +18,7 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { GuildActions } from "../data/GuildActions"; import { GuildActions } from "../data/GuildActions";
import { Case } from "../data/entities/Case"; import { Case } from "../data/entities/Case";
import { GuildMutes } from "../data/GuildMutes"; import { GuildMutes } from "../data/GuildMutes";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
enum RecentActionType { enum RecentActionType {
Message = 1, Message = 1,
@ -44,7 +45,28 @@ const MAX_INTERVAL = 300;
const SPAM_ARCHIVE_EXPIRY_DAYS = 90; const SPAM_ARCHIVE_EXPIRY_DAYS = 90;
export class SpamPlugin extends Plugin { 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<ISpamPluginConfig> {
public static pluginName = "spam"; public static pluginName = "spam";
protected actions: GuildActions; protected actions: GuildActions;
@ -68,9 +90,10 @@ export class SpamPlugin extends Plugin {
private expiryInterval; private expiryInterval;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<ISpamPluginConfig> {
return { return {
config: { config: {
max_censor: null,
max_messages: null, max_messages: null,
max_mentions: null, max_mentions: null,
max_links: null, max_links: null,
@ -82,6 +105,8 @@ export class SpamPlugin extends Plugin {
max_voice_moves: null, max_voice_moves: null,
}, },
permissions: {},
// Default override to make mods immune to the spam filter // Default override to make mods immune to the spam filter
overrides: [ overrides: [
{ {
@ -178,7 +203,7 @@ export class SpamPlugin extends Plugin {
async logAndDetectMessageSpam( async logAndDetectMessageSpam(
savedMessage: SavedMessage, savedMessage: SavedMessage,
type: RecentActionType, type: RecentActionType,
spamConfig: any, spamConfig: IBaseSingleSpamConfig,
actionCount: number, actionCount: number,
description: string, description: string,
) { ) {
@ -360,11 +385,9 @@ export class SpamPlugin extends Plugin {
// For interoperability with the Censor plugin // For interoperability with the Censor plugin
async logCensor(savedMessage: SavedMessage) { async logCensor(savedMessage: SavedMessage) {
const spamConfig = this.configValueForMemberIdAndChannelId( const config = this.getConfigForMemberIdAndChannelId(savedMessage.user_id, savedMessage.channel_id);
savedMessage.user_id, const spamConfig = config.max_censor;
savedMessage.channel_id,
"max_censor",
);
if (spamConfig) { if (spamConfig) {
this.logAndDetectMessageSpam(savedMessage, RecentActionType.Censor, spamConfig, 1, "too many censored messages"); this.logAndDetectMessageSpam(savedMessage, RecentActionType.Censor, spamConfig, 1, "too many censored messages");
} }
@ -373,20 +396,14 @@ export class SpamPlugin extends Plugin {
async onMessageCreate(savedMessage: SavedMessage) { async onMessageCreate(savedMessage: SavedMessage) {
if (savedMessage.is_bot) return; if (savedMessage.is_bot) return;
const maxMessages = this.configValueForMemberIdAndChannelId( const config = this.getConfigForMemberIdAndChannelId(savedMessage.user_id, savedMessage.channel_id);
savedMessage.user_id,
savedMessage.channel_id, const maxMessages = config.max_messages;
"max_messages",
);
if (maxMessages) { if (maxMessages) {
this.logAndDetectMessageSpam(savedMessage, RecentActionType.Message, maxMessages, 1, "too many messages"); this.logAndDetectMessageSpam(savedMessage, RecentActionType.Message, maxMessages, 1, "too many messages");
} }
const maxMentions = this.configValueForMemberIdAndChannelId( const maxMentions = config.max_mentions;
savedMessage.user_id,
savedMessage.channel_id,
"max_mentions",
);
const mentions = savedMessage.data.content const mentions = savedMessage.data.content
? [...getUserMentions(savedMessage.data.content), ...getRoleMentions(savedMessage.data.content)] ? [...getUserMentions(savedMessage.data.content), ...getRoleMentions(savedMessage.data.content)]
: []; : [];
@ -400,21 +417,13 @@ export class SpamPlugin extends Plugin {
); );
} }
const maxLinks = this.configValueForMemberIdAndChannelId( const maxLinks = config.max_links;
savedMessage.user_id, if (maxLinks && savedMessage.data.content && typeof savedMessage.data.content === "string") {
savedMessage.channel_id,
"max_links",
);
if (maxLinks && savedMessage.data.content) {
const links = getUrlsInString(savedMessage.data.content); const links = getUrlsInString(savedMessage.data.content);
this.logAndDetectMessageSpam(savedMessage, RecentActionType.Link, maxLinks, links.length, "too many links"); this.logAndDetectMessageSpam(savedMessage, RecentActionType.Link, maxLinks, links.length, "too many links");
} }
const maxAttachments = this.configValueForMemberIdAndChannelId( const maxAttachments = config.max_attachments;
savedMessage.user_id,
savedMessage.channel_id,
"max_attachments",
);
if (maxAttachments && savedMessage.data.attachments) { if (maxAttachments && savedMessage.data.attachments) {
this.logAndDetectMessageSpam( this.logAndDetectMessageSpam(
savedMessage, savedMessage,
@ -425,21 +434,13 @@ export class SpamPlugin extends Plugin {
); );
} }
const maxEmoji = this.configValueForMemberIdAndChannelId( const maxEmoji = config.max_emojis;
savedMessage.user_id,
savedMessage.channel_id,
"max_emojis",
);
if (maxEmoji && savedMessage.data.content) { if (maxEmoji && savedMessage.data.content) {
const emojiCount = getEmojiInString(savedMessage.data.content).length; const emojiCount = getEmojiInString(savedMessage.data.content).length;
this.logAndDetectMessageSpam(savedMessage, RecentActionType.Emoji, maxEmoji, emojiCount, "too many emoji"); this.logAndDetectMessageSpam(savedMessage, RecentActionType.Emoji, maxEmoji, emojiCount, "too many emoji");
} }
const maxNewlines = this.configValueForMemberIdAndChannelId( const maxNewlines = config.max_newlines;
savedMessage.user_id,
savedMessage.channel_id,
"max_newlines",
);
if (maxNewlines && savedMessage.data.content) { if (maxNewlines && savedMessage.data.content) {
const newlineCount = (savedMessage.data.content.match(/\n/g) || []).length; const newlineCount = (savedMessage.data.content.match(/\n/g) || []).length;
this.logAndDetectMessageSpam( this.logAndDetectMessageSpam(
@ -451,11 +452,7 @@ export class SpamPlugin extends Plugin {
); );
} }
const maxCharacters = this.configValueForMemberIdAndChannelId( const maxCharacters = config.max_characters;
savedMessage.user_id,
savedMessage.channel_id,
"max_characters",
);
if (maxCharacters && savedMessage.data.content) { if (maxCharacters && savedMessage.data.content) {
const characterCount = [...savedMessage.data.content.trim()].length; const characterCount = [...savedMessage.data.content.trim()].length;
this.logAndDetectMessageSpam( this.logAndDetectMessageSpam(
@ -473,11 +470,12 @@ export class SpamPlugin extends Plugin {
@d.event("voiceChannelJoin") @d.event("voiceChannelJoin")
@d.event("voiceChannelSwitch") @d.event("voiceChannelSwitch")
onVoiceChannelSwitch(member: Member, channel: Channel) { onVoiceChannelSwitch(member: Member, channel: Channel) {
const spamConfig = this.configValueForMemberIdAndChannelId(member.id, channel.id, "max_voice_moves"); const config = this.getConfigForMemberIdAndChannelId(member.id, channel.id);
if (spamConfig) { const maxVoiceMoves = config.max_voice_moves;
if (maxVoiceMoves) {
this.logAndDetectOtherSpam( this.logAndDetectOtherSpam(
RecentActionType.VoiceChannelMove, RecentActionType.VoiceChannelMove,
spamConfig, maxVoiceMoves,
member.id, member.id,
1, 1,
"0", "0",

View file

@ -1,4 +1,4 @@
import { decorators as d, waitForReply, utils as knubUtils } from "knub"; import { decorators as d, waitForReply, utils as knubUtils, IBasePluginConfig, IPluginOptions } from "knub";
import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildStarboards } from "../data/GuildStarboards"; import { GuildStarboards } from "../data/GuildStarboards";
import { GuildChannel, Message, TextChannel } from "eris"; import { GuildChannel, Message, TextChannel } from "eris";
@ -17,7 +17,11 @@ import moment from "moment-timezone";
import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage"; import { SavedMessage } from "../data/entities/SavedMessage";
export class StarboardPlugin extends ZeppelinPlugin { interface IStarboardPluginPermissions {
manage: boolean;
}
export class StarboardPlugin extends ZeppelinPlugin<IBasePluginConfig, IStarboardPluginPermissions> {
public static pluginName = "starboard"; public static pluginName = "starboard";
protected starboards: GuildStarboards; protected starboards: GuildStarboards;
@ -25,8 +29,10 @@ export class StarboardPlugin extends ZeppelinPlugin {
private onMessageDeleteFn; private onMessageDeleteFn;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IStarboardPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
manage: false, manage: false,
}, },

View file

@ -1,4 +1,4 @@
import { Plugin, decorators as d } from "knub"; import { Plugin, decorators as d, IPluginOptions } from "knub";
import { Message, TextChannel } from "eris"; import { Message, TextChannel } from "eris";
import { errorMessage, successMessage } from "../utils"; import { errorMessage, successMessage } from "../utils";
import { GuildTags } from "../data/GuildTags"; import { GuildTags } from "../data/GuildTags";
@ -6,6 +6,7 @@ import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage"; import { SavedMessage } from "../data/entities/SavedMessage";
import moment from "moment-timezone"; import moment from "moment-timezone";
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
const TAG_FUNCTIONS = { const TAG_FUNCTIONS = {
countdown(toDate) { countdown(toDate) {
@ -14,10 +15,21 @@ const TAG_FUNCTIONS = {
const diff = target.diff(now); const diff = target.diff(now);
const result = humanizeDuration(diff, { largest: 2, round: true }); const result = humanizeDuration(diff, { largest: 2, round: true });
return diff >= 0 ? result : `${result} ago`; return diff >= 0 ? result : `${result} ago`;
} },
}; };
export class TagsPlugin extends Plugin { interface ITagsPluginConfig {
prefix: string;
delete_with_command: boolean;
}
interface ITagsPluginPermissions {
create: boolean;
use: boolean;
list: boolean;
}
export class TagsPlugin extends ZeppelinPlugin<ITagsPluginConfig, ITagsPluginPermissions> {
public static pluginName = "tags"; public static pluginName = "tags";
protected tags: GuildTags; protected tags: GuildTags;
@ -26,17 +38,17 @@ export class TagsPlugin extends Plugin {
private onMessageCreateFn; private onMessageCreateFn;
private onMessageDeleteFn; private onMessageDeleteFn;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<ITagsPluginConfig, ITagsPluginPermissions> {
return { return {
config: { config: {
prefix: "!!", prefix: "!!",
delete_with_command: true delete_with_command: true,
}, },
permissions: { permissions: {
create: false, create: false,
use: true, use: true,
list: false list: false,
}, },
overrides: [ overrides: [
@ -44,10 +56,10 @@ export class TagsPlugin extends Plugin {
level: ">=50", level: ">=50",
permissions: { permissions: {
create: true, create: true,
list: true list: true,
} },
} },
] ],
}; };
} }
@ -78,7 +90,7 @@ export class TagsPlugin extends Plugin {
return; return;
} }
const prefix = this.configValueForMsg(msg, "prefix"); const prefix = this.getConfigForMsg(msg).prefix;
const tagNames = tags.map(t => t.tag).sort(); const tagNames = tags.map(t => t.tag).sort();
msg.channel.createMessage(` msg.channel.createMessage(`
Available tags (use with ${prefix}tag): \`\`\`${tagNames.join(", ")}\`\`\` Available tags (use with ${prefix}tag): \`\`\`${tagNames.join(", ")}\`\`\`
@ -103,7 +115,7 @@ export class TagsPlugin extends Plugin {
async tagCmd(msg: Message, args: { tag: string; body: string }) { async tagCmd(msg: Message, args: { tag: string; body: string }) {
await this.tags.createOrUpdate(args.tag, args.body, msg.author.id); await this.tags.createOrUpdate(args.tag, args.body, msg.author.id);
const prefix = this.configValue("prefix"); const prefix = this.getConfig().prefix;
msg.channel.createMessage(successMessage(`Tag set! Use it with: \`${prefix}${args.tag}\``)); msg.channel.createMessage(successMessage(`Tag set! Use it with: \`${prefix}${args.tag}\``));
} }
@ -114,7 +126,7 @@ export class TagsPlugin extends Plugin {
if (!msg.data.content) return; if (!msg.data.content) return;
if (msg.is_bot) return; if (msg.is_bot) return;
const prefix = this.configValueForMemberIdAndChannelId(msg.user_id, msg.channel_id, "prefix"); const prefix = this.getConfigForMemberIdAndChannelId(msg.user_id, msg.channel_id).prefix;
if (!msg.data.content.startsWith(prefix)) return; if (!msg.data.content.startsWith(prefix)) return;
const tagNameMatch = msg.data.content.slice(prefix.length).match(/^\S+/); const tagNameMatch = msg.data.content.slice(prefix.length).match(/^\S+/);
@ -148,7 +160,7 @@ export class TagsPlugin extends Plugin {
const responseMsg = await channel.createMessage(body); const responseMsg = await channel.createMessage(body);
// Save the command-response message pair once the message is in our database // Save the command-response message pair once the message is in our database
if (this.configValueForMemberIdAndChannelId(msg.user_id, msg.channel_id, "delete_with_command")) { if (this.getConfigForMemberIdAndChannelId(msg.user_id, msg.channel_id).delete_with_command) {
this.savedMessages.onceMessageAvailable(responseMsg.id, async () => { this.savedMessages.onceMessageAvailable(responseMsg.id, async () => {
await this.tags.addResponse(msg.id, responseMsg.id); await this.tags.addResponse(msg.id, responseMsg.id);
}); });

View file

@ -1,4 +1,4 @@
import { decorators as d } from "knub"; import { decorators as d, IBasePluginConfig, IPluginOptions } from "knub";
import { CategoryChannel, Channel, EmbedOptions, Member, Message, Role, TextChannel, User, VoiceChannel } from "eris"; import { CategoryChannel, Channel, EmbedOptions, Member, Message, Role, TextChannel, User, VoiceChannel } from "eris";
import { import {
channelMentionRegex, channelMentionRegex,
@ -33,7 +33,21 @@ const CLEAN_COMMAND_DELETE_DELAY = 5000;
const activeReloads: Map<string, TextChannel> = new Map(); const activeReloads: Map<string, TextChannel> = new Map();
export class UtilityPlugin extends ZeppelinPlugin { interface IUtilityPluginPermissions {
roles: boolean;
level: boolean;
search: boolean;
clean: boolean;
info: boolean;
server: boolean;
reload_guild: boolean;
nickname: boolean;
ping: boolean;
source: boolean;
vcmove: boolean;
}
export class UtilityPlugin extends ZeppelinPlugin<IBasePluginConfig, IUtilityPluginPermissions> {
public static pluginName = "utility"; public static pluginName = "utility";
protected logs: GuildLogs; protected logs: GuildLogs;
@ -41,8 +55,10 @@ export class UtilityPlugin extends ZeppelinPlugin {
protected savedMessages: GuildSavedMessages; protected savedMessages: GuildSavedMessages;
protected archives: GuildArchives; protected archives: GuildArchives;
getDefaultOptions() { getDefaultOptions(): IPluginOptions<IBasePluginConfig, IUtilityPluginPermissions> {
return { return {
config: {},
permissions: { permissions: {
roles: false, roles: false,
level: false, level: false,

View file

@ -1,9 +1,12 @@
import { IPluginOptions, Plugin } from "knub"; import { IBasePluginConfig, IBasePluginPermissions, IPluginOptions, Plugin } from "knub";
import { PluginRuntimeError } from "../PluginRuntimeError"; import { PluginRuntimeError } from "../PluginRuntimeError";
import Ajv, { ErrorObject } from "ajv"; import Ajv, { ErrorObject } from "ajv";
import { isSnowflake, isUnicodeEmoji } from "../utils"; import { isSnowflake, isUnicodeEmoji } from "../utils";
export class ZeppelinPlugin extends Plugin { export class ZeppelinPlugin<
TConfig extends {} = IBasePluginConfig,
TPermissions extends {} = IBasePluginPermissions
> extends Plugin<TConfig, TPermissions> {
protected configSchema: any; protected configSchema: any;
protected permissionsSchema: any; protected permissionsSchema: any;