mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-10 12:25:02 +00:00
Update djs & knub (#395)
* update pkgs
Signed-off-by: GitHub <noreply@github.com>
* new knub typings
Signed-off-by: GitHub <noreply@github.com>
* more pkg updates
Signed-off-by: GitHub <noreply@github.com>
* more fixes
Signed-off-by: GitHub <noreply@github.com>
* channel typings
Signed-off-by: GitHub <noreply@github.com>
* more message utils typings fixes
Signed-off-by: GitHub <noreply@github.com>
* migrate permissions
Signed-off-by: GitHub <noreply@github.com>
* fix: InternalPoster webhookables
Signed-off-by: GitHub <noreply@github.com>
* djs typings: Attachment & Util
Signed-off-by: GitHub <noreply@github.com>
* more typings
Signed-off-by: GitHub <noreply@github.com>
* fix: rename permissionNames
Signed-off-by: GitHub <noreply@github.com>
* more fixes
Signed-off-by: GitHub <noreply@github.com>
* half the number of errors
* knub commands => messageCommands
Signed-off-by: GitHub <noreply@github.com>
* configPreprocessor => configParser
Signed-off-by: GitHub <noreply@github.com>
* fix channel.messages
Signed-off-by: GitHub <noreply@github.com>
* revert automod any typing
Signed-off-by: GitHub <noreply@github.com>
* more configParser typings
Signed-off-by: GitHub <noreply@github.com>
* revert
Signed-off-by: GitHub <noreply@github.com>
* remove knub type params
Signed-off-by: GitHub <noreply@github.com>
* fix more MessageEmbed / MessageOptions
Signed-off-by: GitHub <noreply@github.com>
* dumb commit for @almeidx to see why this is stupid
Signed-off-by: GitHub <noreply@github.com>
* temp disable custom_events
Signed-off-by: GitHub <noreply@github.com>
* more minor typings fixes - 23 err left
Signed-off-by: GitHub <noreply@github.com>
* update djs dep
* +debug build method (revert this)
Signed-off-by: GitHub <noreply@github.com>
* Revert "+debug build method (revert this)"
This reverts commit a80af1e729
.
* Redo +debug build (Revert this)
Signed-off-by: GitHub <noreply@github.com>
* uniform before/after Load shorthands
Signed-off-by: GitHub <noreply@github.com>
* remove unused imports & add prettier plugin
Signed-off-by: GitHub <noreply@github.com>
* env fixes for web platform hosting
Signed-off-by: GitHub <noreply@github.com>
* feat: knub v32-next; related fixes
* fix: allow legacy keys in change_perms action
* fix: request Message Content intent
* fix: use Knub's config validation logic in API
* fix(dashboard): fix error when there are no message and/or slash commands in a plugin
* fix(automod): start_thread action thread options
* fix(CustomEvents): message command types
* chore: remove unneeded type annotation
* feat: add forum channel icon; use thread icon for news threads
* chore: make tslint happy
* chore: fix formatting
---------
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: almeidx <almeidx@pm.me>
Co-authored-by: Dragory <2606411+Dragory@users.noreply.github.com>
This commit is contained in:
parent
293115af22
commit
06877e90cc
476 changed files with 2965 additions and 3251 deletions
|
@ -1,6 +1,7 @@
|
|||
import { PluginOptions } from "knub";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
|
@ -23,10 +24,11 @@ export const AutoDeletePlugin = zeppelinGuildPlugin<AutoDeletePluginType>()({
|
|||
prettyName: "Auto-delete",
|
||||
description: "Allows Zeppelin to auto-delete messages from a channel after a delay",
|
||||
configurationGuide: "Maximum deletion delay is currently 5 minutes",
|
||||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: () => [TimeAndDatePlugin, LogsPlugin],
|
||||
configSchema: ConfigSchema,
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
beforeLoad(pluginData) {
|
||||
|
@ -56,8 +58,10 @@ export const AutoDeletePlugin = zeppelinGuildPlugin<AutoDeletePluginType>()({
|
|||
},
|
||||
|
||||
beforeUnload(pluginData) {
|
||||
pluginData.state.guildSavedMessages.events.off("create", pluginData.state.onMessageCreateFn);
|
||||
pluginData.state.guildSavedMessages.events.off("delete", pluginData.state.onMessageDeleteFn);
|
||||
pluginData.state.guildSavedMessages.events.off("deleteBulk", pluginData.state.onMessageDeleteBulkFn);
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
state.guildSavedMessages.events.off("create", state.onMessageCreateFn);
|
||||
state.guildSavedMessages.events.off("delete", state.onMessageDeleteFn);
|
||||
state.guildSavedMessages.events.off("deleteBulk", state.onMessageDeleteBulkFn);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Permissions, Snowflake, TextChannel } from "discord.js";
|
||||
import { ChannelType, PermissionsBitField, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import moment from "moment-timezone";
|
||||
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { logger } from "../../../logger";
|
||||
import { resolveUser, verboseChannelMention } from "../../../utils";
|
||||
|
@ -17,8 +16,8 @@ export async function deleteNextItem(pluginData: GuildPluginData<AutoDeletePlugi
|
|||
|
||||
scheduleNextDeletion(pluginData);
|
||||
|
||||
const channel = pluginData.guild.channels.cache.get(itemToDelete.message.channel_id as Snowflake) as TextChannel;
|
||||
if (!channel) {
|
||||
const channel = pluginData.guild.channels.cache.get(itemToDelete.message.channel_id as Snowflake);
|
||||
if (!channel || channel.type === ChannelType.GuildCategory) {
|
||||
// Channel was deleted, ignore
|
||||
return;
|
||||
}
|
||||
|
@ -26,7 +25,9 @@ export async function deleteNextItem(pluginData: GuildPluginData<AutoDeletePlugi
|
|||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
const perms = channel.permissionsFor(pluginData.client.user!.id);
|
||||
|
||||
if (!hasDiscordPermissions(perms, Permissions.FLAGS.VIEW_CHANNEL | Permissions.FLAGS.READ_MESSAGE_HISTORY)) {
|
||||
if (
|
||||
!hasDiscordPermissions(perms, PermissionsBitField.Flags.ViewChannel | PermissionsBitField.Flags.ReadMessageHistory)
|
||||
) {
|
||||
logs.logBotAlert({
|
||||
body: `Missing permissions to read messages or message history in auto-delete channel ${verboseChannelMention(
|
||||
channel,
|
||||
|
@ -35,7 +36,7 @@ export async function deleteNextItem(pluginData: GuildPluginData<AutoDeletePlugi
|
|||
return;
|
||||
}
|
||||
|
||||
if (!hasDiscordPermissions(perms, Permissions.FLAGS.MANAGE_MESSAGES)) {
|
||||
if (!hasDiscordPermissions(perms, PermissionsBitField.Flags.ManageMessages)) {
|
||||
logs.logBotAlert({
|
||||
body: `Missing permissions to delete messages in auto-delete channel ${verboseChannelMention(channel)}`,
|
||||
});
|
||||
|
@ -45,7 +46,7 @@ export async function deleteNextItem(pluginData: GuildPluginData<AutoDeletePlugi
|
|||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
pluginData.state.guildLogs.ignoreLog(LogType.MESSAGE_DELETE, itemToDelete.message.id);
|
||||
(channel as TextChannel).messages.delete(itemToDelete.message.id as Snowflake).catch((err) => {
|
||||
channel.messages.delete(itemToDelete.message.id as Snowflake).catch((err) => {
|
||||
if (err.code === 10008) {
|
||||
// "Unknown Message", probably already deleted by automod or another bot, ignore
|
||||
return;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { convertDelayStringToMS, resolveMember } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { AutoDeletePluginType, MAX_DELAY } from "../types";
|
||||
import { addMessageToDeletionQueue } from "./addMessageToDeletionQueue";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
|
||||
export async function onMessageCreate(pluginData: GuildPluginData<AutoDeletePluginType>, msg: SavedMessage) {
|
||||
const member = await resolveMember(pluginData.client, pluginData.guild, msg.user_id);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PluginOptions } from "knub";
|
||||
import { GuildAutoReactions } from "../../data/GuildAutoReactions";
|
||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
|
@ -31,6 +32,7 @@ export const AutoReactionsPlugin = zeppelinGuildPlugin<AutoReactionsPluginType>(
|
|||
description: trimPluginDescription(`
|
||||
Allows setting up automatic reactions to all new messages on a channel
|
||||
`),
|
||||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
// prettier-ignore
|
||||
|
@ -38,11 +40,11 @@ export const AutoReactionsPlugin = zeppelinGuildPlugin<AutoReactionsPluginType>(
|
|||
LogsPlugin,
|
||||
],
|
||||
|
||||
configSchema: ConfigSchema,
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
// prettier-ignore
|
||||
commands: [
|
||||
messageCommands: [
|
||||
NewAutoReactionsCmd,
|
||||
DisableAutoReactionsCmd,
|
||||
],
|
||||
|
@ -53,8 +55,10 @@ export const AutoReactionsPlugin = zeppelinGuildPlugin<AutoReactionsPluginType>(
|
|||
],
|
||||
|
||||
beforeLoad(pluginData) {
|
||||
pluginData.state.savedMessages = GuildSavedMessages.getGuildInstance(pluginData.guild.id);
|
||||
pluginData.state.autoReactions = GuildAutoReactions.getGuildInstance(pluginData.guild.id);
|
||||
pluginData.state.cache = new Map();
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id);
|
||||
state.autoReactions = GuildAutoReactions.getGuildInstance(guild.id);
|
||||
state.cache = new Map();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { GuildChannel, Permissions } from "discord.js";
|
||||
import { PermissionsBitField } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { canUseEmoji, customEmojiRegex, isEmoji } from "../../../utils";
|
||||
|
@ -7,7 +7,7 @@ import { missingPermissionError } from "../../../utils/missingPermissionError";
|
|||
import { readChannelPermissions } from "../../../utils/readChannelPermissions";
|
||||
import { autoReactionsCmd } from "../types";
|
||||
|
||||
const requiredPermissions = readChannelPermissions | Permissions.FLAGS.ADD_REACTIONS;
|
||||
const requiredPermissions = readChannelPermissions | PermissionsBitField.Flags.AddReactions;
|
||||
|
||||
export const NewAutoReactionsCmd = autoReactionsCmd({
|
||||
trigger: "auto_reactions",
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { GuildChannel, GuildTextBasedChannel, Permissions } from "discord.js";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { GuildTextBasedChannel, PermissionsBitField } from "discord.js";
|
||||
import { AutoReaction } from "../../../data/entities/AutoReaction";
|
||||
import { isDiscordAPIError } from "../../../utils";
|
||||
import { getMissingChannelPermissions } from "../../../utils/getMissingChannelPermissions";
|
||||
import { missingPermissionError } from "../../../utils/missingPermissionError";
|
||||
import { readChannelPermissions } from "../../../utils/readChannelPermissions";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { autoReactionsEvt } from "../types";
|
||||
import { AutoReaction } from "../../../data/entities/AutoReaction";
|
||||
|
||||
const p = Permissions.FLAGS;
|
||||
const p = PermissionsBitField.Flags;
|
||||
|
||||
export const AddReactionsEvt = autoReactionsEvt({
|
||||
event: "messageCreate",
|
||||
|
@ -40,7 +39,7 @@ export const AddReactionsEvt = autoReactionsEvt({
|
|||
|
||||
const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!;
|
||||
if (me) {
|
||||
const missingPermissions = getMissingChannelPermissions(me, channel, readChannelPermissions | p.ADD_REACTIONS);
|
||||
const missingPermissions = getMissingChannelPermissions(me, channel, readChannelPermissions | p.AddReactions);
|
||||
if (missingPermissions) {
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
logs.logBotAlert({
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub";
|
||||
import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub";
|
||||
import { AutoReaction } from "../../data/entities/AutoReaction";
|
||||
import { GuildAutoReactions } from "../../data/GuildAutoReactions";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||
import { AutoReaction } from "../../data/entities/AutoReaction";
|
||||
|
||||
export const ConfigSchema = t.type({
|
||||
can_manage: t.boolean,
|
||||
|
@ -20,5 +20,5 @@ export interface AutoReactionsPluginType extends BasePluginType {
|
|||
};
|
||||
}
|
||||
|
||||
export const autoReactionsCmd = typedGuildCommand<AutoReactionsPluginType>();
|
||||
export const autoReactionsEvt = typedGuildEventListener<AutoReactionsPluginType>();
|
||||
export const autoReactionsCmd = guildPluginMessageCommand<AutoReactionsPluginType>();
|
||||
export const autoReactionsEvt = guildPluginEventListener<AutoReactionsPluginType>();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as t from "io-ts";
|
||||
import { configUtils, CooldownManager } from "knub";
|
||||
import { ConfigPreprocessorFn } from "knub/dist/config/configTypes";
|
||||
import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels";
|
||||
import { GuildArchives } from "../../data/GuildArchives";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
|
@ -9,11 +9,13 @@ import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
|||
import { MINUTES, SECONDS } from "../../utils";
|
||||
import { registerEventListenersFromMap } from "../../utils/registerEventListenersFromMap";
|
||||
import { unregisterEventListenersFromMap } from "../../utils/unregisterEventListenersFromMap";
|
||||
import { StrictValidationError } from "../../validatorUtils";
|
||||
import { StrictValidationError, validate } from "../../validatorUtils";
|
||||
import { CountersPlugin } from "../Counters/CountersPlugin";
|
||||
import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { ModActionsPlugin } from "../ModActions/ModActionsPlugin";
|
||||
import { MutesPlugin } from "../Mutes/MutesPlugin";
|
||||
import { PhishermanPlugin } from "../Phisherman/PhishermanPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { availableActions } from "./actions/availableActions";
|
||||
import { AntiraidClearCmd } from "./commands/AntiraidClearCmd";
|
||||
|
@ -35,8 +37,6 @@ import { clearOldRecentSpam } from "./functions/clearOldRecentSpam";
|
|||
import { pluginInfo } from "./info";
|
||||
import { availableTriggers } from "./triggers/availableTriggers";
|
||||
import { AutomodPluginType, ConfigSchema } from "./types";
|
||||
import { PhishermanPlugin } from "../Phisherman/PhishermanPlugin";
|
||||
import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin";
|
||||
|
||||
const defaultOptions = {
|
||||
config: {
|
||||
|
@ -63,13 +63,15 @@ const defaultOptions = {
|
|||
|
||||
/**
|
||||
* Config preprocessor to set default values for triggers and perform extra validation
|
||||
* TODO: Separate input and output types
|
||||
*/
|
||||
const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) => {
|
||||
if (options.config?.rules) {
|
||||
const configParser = (input: unknown) => {
|
||||
const rules = (input as any).rules;
|
||||
if (rules) {
|
||||
// Loop through each rule
|
||||
for (const [name, rule] of Object.entries(options.config.rules)) {
|
||||
for (const [name, rule] of Object.entries(rules)) {
|
||||
if (rule == null) {
|
||||
delete options.config.rules[name];
|
||||
delete rules[name];
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -97,7 +99,7 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) =>
|
|||
for (const triggerObj of rule["triggers"]) {
|
||||
for (const triggerName in triggerObj) {
|
||||
if (!availableTriggers[triggerName]) {
|
||||
throw new StrictValidationError([`Unknown trigger '${triggerName}' in rule '${rule.name}'`]);
|
||||
throw new StrictValidationError([`Unknown trigger '${triggerName}' in rule '${rule["name"]}'`]);
|
||||
}
|
||||
|
||||
const triggerBlueprint = availableTriggers[triggerName];
|
||||
|
@ -117,11 +119,11 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) =>
|
|||
|
||||
if (white && black) {
|
||||
throw new StrictValidationError([
|
||||
`Cannot have both blacklist and whitelist enabled at rule <${rule.name}/match_attachment_type>`,
|
||||
`Cannot have both blacklist and whitelist enabled at rule <${rule["name"]}/match_attachment_type>`,
|
||||
]);
|
||||
} else if (!white && !black) {
|
||||
throw new StrictValidationError([
|
||||
`Must have either blacklist or whitelist enabled at rule <${rule.name}/match_attachment_type>`,
|
||||
`Must have either blacklist or whitelist enabled at rule <${rule["name"]}/match_attachment_type>`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -132,11 +134,11 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) =>
|
|||
|
||||
if (white && black) {
|
||||
throw new StrictValidationError([
|
||||
`Cannot have both blacklist and whitelist enabled at rule <${rule.name}/match_mime_type>`,
|
||||
`Cannot have both blacklist and whitelist enabled at rule <${rule["name"]}/match_mime_type>`,
|
||||
]);
|
||||
} else if (!white && !black) {
|
||||
throw new StrictValidationError([
|
||||
`Must have either blacklist or whitelist enabled at rule <${rule.name}/match_mime_type>`,
|
||||
`Must have either blacklist or whitelist enabled at rule <${rule["name"]}/match_mime_type>`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +149,7 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) =>
|
|||
if (rule["actions"]) {
|
||||
for (const actionName in rule["actions"]) {
|
||||
if (!availableActions[actionName]) {
|
||||
throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule.name}'`]);
|
||||
throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule["name"]}'`]);
|
||||
}
|
||||
|
||||
const actionBlueprint = availableActions[actionName];
|
||||
|
@ -163,9 +165,9 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) =>
|
|||
|
||||
// Enable logging of automod actions by default
|
||||
if (rule["actions"]) {
|
||||
for (const actionName in rule.actions) {
|
||||
for (const actionName in rule["actions"]) {
|
||||
if (!availableActions[actionName]) {
|
||||
throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule.name}'`]);
|
||||
throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule["name"]}'`]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,13 +175,18 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = (options) =>
|
|||
rule["actions"]["log"] = true;
|
||||
}
|
||||
if (rule["actions"]["clean"] && rule["actions"]["start_thread"]) {
|
||||
throw new StrictValidationError([`Cannot have both clean and start_thread at rule '${rule.name}'`]);
|
||||
throw new StrictValidationError([`Cannot have both clean and start_thread at rule '${rule["name"]}'`]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
const error = validate(ConfigSchema, input);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return input as t.TypeOf<typeof ConfigSchema>;
|
||||
};
|
||||
|
||||
export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()({
|
||||
|
@ -197,9 +204,8 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()({
|
|||
InternalPosterPlugin,
|
||||
],
|
||||
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
configPreprocessor,
|
||||
configParser,
|
||||
|
||||
customOverrideCriteriaFunctions: {
|
||||
antiraid_level: (pluginData, matchParams, value) => {
|
||||
|
@ -218,136 +224,126 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()({
|
|||
// Messages use message events from SavedMessages, see onLoad below
|
||||
],
|
||||
|
||||
commands: [AntiraidClearCmd, SetAntiraidCmd, ViewAntiraidCmd],
|
||||
messageCommands: [AntiraidClearCmd, SetAntiraidCmd, ViewAntiraidCmd],
|
||||
|
||||
async beforeLoad(pluginData) {
|
||||
pluginData.state.queue = new Queue();
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
pluginData.state.regexRunner = getRegExpRunner(`guild-${pluginData.guild.id}`);
|
||||
state.queue = new Queue();
|
||||
|
||||
pluginData.state.recentActions = [];
|
||||
state.regexRunner = getRegExpRunner(`guild-${guild.id}`);
|
||||
|
||||
pluginData.state.recentSpam = [];
|
||||
state.recentActions = [];
|
||||
|
||||
pluginData.state.recentNicknameChanges = new Map();
|
||||
state.recentSpam = [];
|
||||
|
||||
pluginData.state.ignoredRoleChanges = new Set();
|
||||
state.recentNicknameChanges = new Map();
|
||||
|
||||
pluginData.state.cooldownManager = new CooldownManager();
|
||||
state.ignoredRoleChanges = new Set();
|
||||
|
||||
pluginData.state.logs = new GuildLogs(pluginData.guild.id);
|
||||
pluginData.state.savedMessages = GuildSavedMessages.getGuildInstance(pluginData.guild.id);
|
||||
pluginData.state.antiraidLevels = GuildAntiraidLevels.getGuildInstance(pluginData.guild.id);
|
||||
pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id);
|
||||
state.cooldownManager = new CooldownManager();
|
||||
|
||||
pluginData.state.cachedAntiraidLevel = await pluginData.state.antiraidLevels.get();
|
||||
state.logs = new GuildLogs(guild.id);
|
||||
state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id);
|
||||
state.antiraidLevels = GuildAntiraidLevels.getGuildInstance(guild.id);
|
||||
state.archives = GuildArchives.getGuildInstance(guild.id);
|
||||
|
||||
state.cachedAntiraidLevel = await state.antiraidLevels.get();
|
||||
},
|
||||
|
||||
async afterLoad(pluginData) {
|
||||
pluginData.state.clearRecentActionsInterval = setInterval(() => clearOldRecentActions(pluginData), 1 * MINUTES);
|
||||
pluginData.state.clearRecentSpamInterval = setInterval(() => clearOldRecentSpam(pluginData), 1 * SECONDS);
|
||||
pluginData.state.clearRecentNicknameChangesInterval = setInterval(
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
state.clearRecentActionsInterval = setInterval(() => clearOldRecentActions(pluginData), 1 * MINUTES);
|
||||
state.clearRecentSpamInterval = setInterval(() => clearOldRecentSpam(pluginData), 1 * SECONDS);
|
||||
state.clearRecentNicknameChangesInterval = setInterval(
|
||||
() => clearOldRecentNicknameChanges(pluginData),
|
||||
30 * SECONDS,
|
||||
);
|
||||
|
||||
pluginData.state.onMessageCreateFn = (message) => runAutomodOnMessage(pluginData, message, false);
|
||||
pluginData.state.savedMessages.events.on("create", pluginData.state.onMessageCreateFn);
|
||||
|
||||
pluginData.state.onMessageUpdateFn = (message) => runAutomodOnMessage(pluginData, message, true);
|
||||
pluginData.state.savedMessages.events.on("update", pluginData.state.onMessageUpdateFn);
|
||||
state.onMessageCreateFn = (message) => runAutomodOnMessage(pluginData, message, false);
|
||||
state.savedMessages.events.on("create", state.onMessageCreateFn);
|
||||
|
||||
state.onMessageUpdateFn = (message) => runAutomodOnMessage(pluginData, message, true);
|
||||
state.savedMessages.events.on("update", state.onMessageUpdateFn);
|
||||
const countersPlugin = pluginData.getPlugin(CountersPlugin);
|
||||
|
||||
pluginData.state.onCounterTrigger = (name, triggerName, channelId, userId) => {
|
||||
state.onCounterTrigger = (name, triggerName, channelId, userId) => {
|
||||
runAutomodOnCounterTrigger(pluginData, name, triggerName, channelId, userId, false);
|
||||
};
|
||||
|
||||
pluginData.state.onCounterReverseTrigger = (name, triggerName, channelId, userId) => {
|
||||
state.onCounterReverseTrigger = (name, triggerName, channelId, userId) => {
|
||||
runAutomodOnCounterTrigger(pluginData, name, triggerName, channelId, userId, true);
|
||||
};
|
||||
|
||||
countersPlugin.onCounterEvent("trigger", pluginData.state.onCounterTrigger);
|
||||
countersPlugin.onCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
|
||||
countersPlugin.onCounterEvent("trigger", state.onCounterTrigger);
|
||||
countersPlugin.onCounterEvent("reverseTrigger", state.onCounterReverseTrigger);
|
||||
|
||||
const modActionsEvents = pluginData.getPlugin(ModActionsPlugin).getEventEmitter();
|
||||
pluginData.state.modActionsListeners = new Map();
|
||||
pluginData.state.modActionsListeners.set("note", (userId: string) =>
|
||||
runAutomodOnModAction(pluginData, "note", userId),
|
||||
state.modActionsListeners = new Map();
|
||||
state.modActionsListeners.set("note", (userId: string) => runAutomodOnModAction(pluginData, "note", userId));
|
||||
state.modActionsListeners.set("warn", (userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "warn", userId, reason, isAutomodAction),
|
||||
);
|
||||
pluginData.state.modActionsListeners.set(
|
||||
"warn",
|
||||
(userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "warn", userId, reason, isAutomodAction),
|
||||
state.modActionsListeners.set("kick", (userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "kick", userId, reason, isAutomodAction),
|
||||
);
|
||||
pluginData.state.modActionsListeners.set(
|
||||
"kick",
|
||||
(userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "kick", userId, reason, isAutomodAction),
|
||||
state.modActionsListeners.set("ban", (userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "ban", userId, reason, isAutomodAction),
|
||||
);
|
||||
pluginData.state.modActionsListeners.set(
|
||||
"ban",
|
||||
(userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "ban", userId, reason, isAutomodAction),
|
||||
);
|
||||
pluginData.state.modActionsListeners.set("unban", (userId: string) =>
|
||||
runAutomodOnModAction(pluginData, "unban", userId),
|
||||
);
|
||||
registerEventListenersFromMap(modActionsEvents, pluginData.state.modActionsListeners);
|
||||
state.modActionsListeners.set("unban", (userId: string) => runAutomodOnModAction(pluginData, "unban", userId));
|
||||
registerEventListenersFromMap(modActionsEvents, state.modActionsListeners);
|
||||
|
||||
const mutesEvents = pluginData.getPlugin(MutesPlugin).getEventEmitter();
|
||||
pluginData.state.mutesListeners = new Map();
|
||||
pluginData.state.mutesListeners.set(
|
||||
"mute",
|
||||
(userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "mute", userId, reason, isAutomodAction),
|
||||
state.mutesListeners = new Map();
|
||||
state.mutesListeners.set("mute", (userId: string, reason: string | undefined, isAutomodAction: boolean) =>
|
||||
runAutomodOnModAction(pluginData, "mute", userId, reason, isAutomodAction),
|
||||
);
|
||||
pluginData.state.mutesListeners.set("unmute", (userId: string) =>
|
||||
runAutomodOnModAction(pluginData, "unmute", userId),
|
||||
);
|
||||
registerEventListenersFromMap(mutesEvents, pluginData.state.mutesListeners);
|
||||
state.mutesListeners.set("unmute", (userId: string) => runAutomodOnModAction(pluginData, "unmute", userId));
|
||||
registerEventListenersFromMap(mutesEvents, state.mutesListeners);
|
||||
},
|
||||
|
||||
async beforeUnload(pluginData) {
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
const countersPlugin = pluginData.getPlugin(CountersPlugin);
|
||||
if (pluginData.state.onCounterTrigger) {
|
||||
countersPlugin.offCounterEvent("trigger", pluginData.state.onCounterTrigger);
|
||||
if (state.onCounterTrigger) {
|
||||
countersPlugin.offCounterEvent("trigger", state.onCounterTrigger);
|
||||
}
|
||||
if (pluginData.state.onCounterReverseTrigger) {
|
||||
countersPlugin.offCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
|
||||
if (state.onCounterReverseTrigger) {
|
||||
countersPlugin.offCounterEvent("reverseTrigger", state.onCounterReverseTrigger);
|
||||
}
|
||||
|
||||
const modActionsEvents = pluginData.getPlugin(ModActionsPlugin).getEventEmitter();
|
||||
if (pluginData.state.modActionsListeners) {
|
||||
unregisterEventListenersFromMap(modActionsEvents, pluginData.state.modActionsListeners);
|
||||
if (state.modActionsListeners) {
|
||||
unregisterEventListenersFromMap(modActionsEvents, state.modActionsListeners);
|
||||
}
|
||||
|
||||
const mutesEvents = pluginData.getPlugin(MutesPlugin).getEventEmitter();
|
||||
if (pluginData.state.mutesListeners) {
|
||||
unregisterEventListenersFromMap(mutesEvents, pluginData.state.mutesListeners);
|
||||
if (state.mutesListeners) {
|
||||
unregisterEventListenersFromMap(mutesEvents, state.mutesListeners);
|
||||
}
|
||||
|
||||
pluginData.state.queue.clear();
|
||||
state.queue.clear();
|
||||
|
||||
discardRegExpRunner(`guild-${pluginData.guild.id}`);
|
||||
discardRegExpRunner(`guild-${guild.id}`);
|
||||
|
||||
if (pluginData.state.clearRecentActionsInterval) {
|
||||
clearInterval(pluginData.state.clearRecentActionsInterval);
|
||||
if (state.clearRecentActionsInterval) {
|
||||
clearInterval(state.clearRecentActionsInterval);
|
||||
}
|
||||
|
||||
if (pluginData.state.clearRecentSpamInterval) {
|
||||
clearInterval(pluginData.state.clearRecentSpamInterval);
|
||||
if (state.clearRecentSpamInterval) {
|
||||
clearInterval(state.clearRecentSpamInterval);
|
||||
}
|
||||
|
||||
if (pluginData.state.clearRecentNicknameChangesInterval) {
|
||||
clearInterval(pluginData.state.clearRecentNicknameChangesInterval);
|
||||
if (state.clearRecentNicknameChangesInterval) {
|
||||
clearInterval(state.clearRecentNicknameChangesInterval);
|
||||
}
|
||||
|
||||
if (pluginData.state.onMessageCreateFn) {
|
||||
pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn);
|
||||
if (state.onMessageCreateFn) {
|
||||
state.savedMessages.events.off("create", state.onMessageCreateFn);
|
||||
}
|
||||
if (pluginData.state.onMessageUpdateFn) {
|
||||
pluginData.state.savedMessages.events.off("update", pluginData.state.onMessageUpdateFn);
|
||||
if (state.onMessageUpdateFn) {
|
||||
state.savedMessages.events.off("update", state.onMessageUpdateFn);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Permissions, Snowflake } from "discord.js";
|
||||
import { PermissionFlagsBits, Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { nonNullish, unique } from "../../../utils";
|
||||
import { canAssignRole } from "../../../utils/canAssignRole";
|
||||
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
|
||||
|
@ -10,7 +9,7 @@ import { LogsPlugin } from "../../Logs/LogsPlugin";
|
|||
import { ignoreRoleChange } from "../functions/ignoredRoleChanges";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
const p = Permissions.FLAGS;
|
||||
const p = PermissionFlagsBits;
|
||||
|
||||
export const AddRolesAction = automodAction({
|
||||
configType: t.array(t.string),
|
||||
|
@ -20,7 +19,7 @@ export const AddRolesAction = automodAction({
|
|||
const members = unique(contexts.map((c) => c.member).filter(nonNullish));
|
||||
const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!;
|
||||
|
||||
const missingPermissions = getMissingPermissions(me.permissions, p.MANAGE_ROLES);
|
||||
const missingPermissions = getMissingPermissions(me.permissions, p.ManageRoles);
|
||||
if (missingPermissions) {
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
logs.logBotAlert({
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { CountersPlugin } from "../../Counters/CountersPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
export const AddToCounterAction = automodAction({
|
||||
configType: t.type({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel, ThreadChannel } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { erisAllowedMentionsToDjsMentionOptions } from "src/utils/erisAllowedMentionsToDjsMentionOptions";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
|
@ -9,21 +9,19 @@ import {
|
|||
TemplateSafeValueContainer,
|
||||
} from "../../../templateFormatter";
|
||||
import {
|
||||
createChunkedMessage,
|
||||
chunkMessageLines,
|
||||
isTruthy,
|
||||
messageLink,
|
||||
stripObjectToScalars,
|
||||
tAllowedMentions,
|
||||
tNormalizedNullOptional,
|
||||
isTruthy,
|
||||
verboseChannelMention,
|
||||
validateAndParseMessageContent,
|
||||
chunkMessageLines,
|
||||
verboseChannelMention,
|
||||
} from "../../../utils";
|
||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
import { TemplateSafeUser, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||
import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin";
|
||||
|
||||
export const AlertAction = automodAction({
|
||||
configType: t.type({
|
||||
|
@ -38,7 +36,7 @@ export const AlertAction = automodAction({
|
|||
const channel = pluginData.guild.channels.cache.get(actionConfig.channel as Snowflake);
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
|
||||
if (channel?.isText()) {
|
||||
if (channel?.isTextBased()) {
|
||||
const text = actionConfig.text;
|
||||
const theMessageLink =
|
||||
contexts[0].message && messageLink(pluginData.guild.id, contexts[0].message.channel_id, contexts[0].message.id);
|
||||
|
@ -96,7 +94,7 @@ export const AlertAction = automodAction({
|
|||
const chunks = chunkMessageLines(rendered);
|
||||
for (const chunk of chunks) {
|
||||
await poster.sendMessage(channel, {
|
||||
content: rendered,
|
||||
content: chunk,
|
||||
allowedMentions: erisAllowedMentionsToDjsMentionOptions(actionConfig.allowed_mentions),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ThreadChannel } from "discord.js";
|
||||
import { AnyThreadChannel } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { noop } from "../../../utils";
|
||||
import { automodAction } from "../helpers";
|
||||
|
@ -11,7 +11,7 @@ export const ArchiveThreadAction = automodAction({
|
|||
const threads = contexts
|
||||
.filter((c) => c.message?.channel_id)
|
||||
.map((c) => pluginData.guild.channels.cache.get(c.message!.channel_id))
|
||||
.filter((c): c is ThreadChannel => c?.isThread() ?? false);
|
||||
.filter((c): c is AnyThreadChannel => c?.isThread() ?? false);
|
||||
|
||||
for (const thread of threads) {
|
||||
await thread.setArchived().catch(noop);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { nonNullish, unique } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
|
|
|
@ -1,20 +1,72 @@
|
|||
import { Permissions, PermissionString } from "discord.js";
|
||||
import { PermissionsBitField, PermissionsString } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { automodAction } from "../helpers";
|
||||
import { tNullable, isValidSnowflake, tPartialDictionary } from "../../../utils";
|
||||
import { noop } from "knub/dist/utils";
|
||||
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter";
|
||||
import { isValidSnowflake, noop, tNullable, tPartialDictionary } from "../../../utils";
|
||||
import {
|
||||
guildToTemplateSafeGuild,
|
||||
savedMessageToTemplateSafeSavedMessage,
|
||||
userToTemplateSafeUser,
|
||||
} from "../../../utils/templateSafeObjects";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
type LegacyPermMap = Record<string, keyof (typeof PermissionsBitField)["Flags"]>;
|
||||
const legacyPermMap = {
|
||||
CREATE_INSTANT_INVITE: "CreateInstantInvite",
|
||||
KICK_MEMBERS: "KickMembers",
|
||||
BAN_MEMBERS: "BanMembers",
|
||||
ADMINISTRATOR: "Administrator",
|
||||
MANAGE_CHANNELS: "ManageChannels",
|
||||
MANAGE_GUILD: "ManageGuild",
|
||||
ADD_REACTIONS: "AddReactions",
|
||||
VIEW_AUDIT_LOG: "ViewAuditLog",
|
||||
PRIORITY_SPEAKER: "PrioritySpeaker",
|
||||
STREAM: "Stream",
|
||||
VIEW_CHANNEL: "ViewChannel",
|
||||
SEND_MESSAGES: "SendMessages",
|
||||
SEND_TTSMESSAGES: "SendTTSMessages",
|
||||
MANAGE_MESSAGES: "ManageMessages",
|
||||
EMBED_LINKS: "EmbedLinks",
|
||||
ATTACH_FILES: "AttachFiles",
|
||||
READ_MESSAGE_HISTORY: "ReadMessageHistory",
|
||||
MENTION_EVERYONE: "MentionEveryone",
|
||||
USE_EXTERNAL_EMOJIS: "UseExternalEmojis",
|
||||
VIEW_GUILD_INSIGHTS: "ViewGuildInsights",
|
||||
CONNECT: "Connect",
|
||||
SPEAK: "Speak",
|
||||
MUTE_MEMBERS: "MuteMembers",
|
||||
DEAFEN_MEMBERS: "DeafenMembers",
|
||||
MOVE_MEMBERS: "MoveMembers",
|
||||
USE_VAD: "UseVAD",
|
||||
CHANGE_NICKNAME: "ChangeNickname",
|
||||
MANAGE_NICKNAMES: "ManageNicknames",
|
||||
MANAGE_ROLES: "ManageRoles",
|
||||
MANAGE_WEBHOOKS: "ManageWebhooks",
|
||||
MANAGE_EMOJIS_AND_STICKERS: "ManageEmojisAndStickers",
|
||||
USE_APPLICATION_COMMANDS: "UseApplicationCommands",
|
||||
REQUEST_TO_SPEAK: "RequestToSpeak",
|
||||
MANAGE_EVENTS: "ManageEvents",
|
||||
MANAGE_THREADS: "ManageThreads",
|
||||
CREATE_PUBLIC_THREADS: "CreatePublicThreads",
|
||||
CREATE_PRIVATE_THREADS: "CreatePrivateThreads",
|
||||
USE_EXTERNAL_STICKERS: "UseExternalStickers",
|
||||
SEND_MESSAGES_IN_THREADS: "SendMessagesInThreads",
|
||||
USE_EMBEDDED_ACTIVITIES: "UseEmbeddedActivities",
|
||||
MODERATE_MEMBERS: "ModerateMembers",
|
||||
} satisfies LegacyPermMap;
|
||||
|
||||
const realToLegacyMap = Object.entries(legacyPermMap).reduce((map, pair) => {
|
||||
map[pair[1]] = pair[0];
|
||||
return map;
|
||||
}, {}) as Record<keyof typeof PermissionsBitField.Flags, keyof typeof legacyPermMap>;
|
||||
|
||||
export const ChangePermsAction = automodAction({
|
||||
configType: t.type({
|
||||
target: t.string,
|
||||
channel: tNullable(t.string),
|
||||
perms: tPartialDictionary(t.keyof(Permissions.FLAGS), tNullable(t.boolean)),
|
||||
perms: tPartialDictionary(
|
||||
t.union([t.keyof(PermissionsBitField.Flags), t.keyof(legacyPermMap)]),
|
||||
tNullable(t.boolean),
|
||||
),
|
||||
}),
|
||||
defaultConfig: {},
|
||||
|
||||
|
@ -52,13 +104,15 @@ export const ChangePermsAction = automodAction({
|
|||
const channel = pluginData.guild.channels.resolve(channelId);
|
||||
if (!channel || channel.isThread()) return;
|
||||
const overwrite = channel.permissionOverwrites.cache.find((pw) => pw.id === target);
|
||||
const allow = new Permissions(overwrite?.allow ?? 0n).serialize();
|
||||
const deny = new Permissions(overwrite?.deny ?? 0n).serialize();
|
||||
const newPerms: Partial<Record<PermissionString, boolean | null>> = {};
|
||||
const allow = new PermissionsBitField(overwrite?.allow ?? 0n).serialize();
|
||||
const deny = new PermissionsBitField(overwrite?.deny ?? 0n).serialize();
|
||||
const newPerms: Partial<Record<PermissionsString, boolean | null>> = {};
|
||||
|
||||
for (const key in allow) {
|
||||
if (typeof actionConfig.perms[key] !== "undefined") {
|
||||
newPerms[key] = actionConfig.perms[key];
|
||||
const legacyKey = realToLegacyMap[key];
|
||||
const configEntry = actionConfig.perms[key] ?? actionConfig.perms[legacyKey];
|
||||
if (typeof configEntry !== "undefined") {
|
||||
newPerms[key] = configEntry;
|
||||
continue;
|
||||
}
|
||||
if (allow[key]) {
|
||||
|
@ -86,11 +140,12 @@ export const ChangePermsAction = automodAction({
|
|||
|
||||
if (!role) return;
|
||||
|
||||
const perms = new Permissions(role.permissions).serialize();
|
||||
const perms = new PermissionsBitField(role.permissions).serialize();
|
||||
for (const key in actionConfig.perms) {
|
||||
perms[key] = actionConfig.perms[key];
|
||||
const realKey = legacyPermMap[key] ?? key;
|
||||
perms[realKey] = actionConfig.perms[key];
|
||||
}
|
||||
const permsArray = <PermissionString[]>Object.keys(perms).filter((key) => perms[key]);
|
||||
await role.setPermissions(new Permissions(permsArray)).catch(noop);
|
||||
const permsArray = <PermissionsString[]>Object.keys(perms).filter((key) => perms[key]);
|
||||
await role.setPermissions(new PermissionsBitField(permsArray)).catch(noop);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { GuildTextBasedChannel, Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { noop } from "../../../utils";
|
||||
|
@ -32,7 +32,7 @@ export const CleanAction = automodAction({
|
|||
pluginData.state.logs.ignoreLog(LogType.MESSAGE_DELETE, id);
|
||||
}
|
||||
|
||||
const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as GuildTextBasedChannel;
|
||||
await channel.bulkDelete(messageIds as Snowflake[]).catch(noop);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { isTruthy, stripObjectToScalars, unique } from "../../../utils";
|
||||
import { isTruthy, unique } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
|
||||
export const LogAction = automodAction({
|
||||
configType: t.boolean,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||
import { convertDelayStringToMS, nonNullish, tDelayString, tNullable, unique } from "../../../utils";
|
||||
import { CaseArgs } from "../../Cases/types";
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Permissions, Snowflake } from "discord.js";
|
||||
import { PermissionFlagsBits, Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { nonNullish, unique } from "../../../utils";
|
||||
import { canAssignRole } from "../../../utils/canAssignRole";
|
||||
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
|
||||
|
@ -10,7 +9,7 @@ import { LogsPlugin } from "../../Logs/LogsPlugin";
|
|||
import { ignoreRoleChange } from "../functions/ignoredRoleChanges";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
const p = Permissions.FLAGS;
|
||||
const p = PermissionFlagsBits;
|
||||
|
||||
export const RemoveRolesAction = automodAction({
|
||||
configType: t.array(t.string),
|
||||
|
@ -21,7 +20,7 @@ export const RemoveRolesAction = automodAction({
|
|||
const members = unique(contexts.map((c) => c.member).filter(nonNullish));
|
||||
const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!;
|
||||
|
||||
const missingPermissions = getMissingPermissions(me.permissions, p.MANAGE_ROLES);
|
||||
const missingPermissions = getMissingPermissions(me.permissions, p.ManageRoles);
|
||||
if (missingPermissions) {
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
logs.logBotAlert({
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { MessageOptions, Permissions, Snowflake, TextChannel, ThreadChannel, User } from "discord.js";
|
||||
import { GuildTextBasedChannel, MessageCreateOptions, PermissionsBitField, Snowflake, User } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter";
|
||||
import {
|
||||
convertDelayStringToMS,
|
||||
|
@ -14,10 +13,11 @@ import {
|
|||
verboseChannelMention,
|
||||
} from "../../../utils";
|
||||
import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions";
|
||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
import { AutomodContext } from "../types";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||
|
||||
export const ReplyAction = automodAction({
|
||||
configType: t.union([
|
||||
|
@ -36,7 +36,7 @@ export const ReplyAction = automodAction({
|
|||
.filter((c) => c.message?.channel_id)
|
||||
.filter((c) => {
|
||||
const channel = pluginData.guild.channels.cache.get(c.message!.channel_id as Snowflake);
|
||||
return channel?.isText();
|
||||
return channel?.isTextBased();
|
||||
});
|
||||
|
||||
const contextsByChannelId = contextsWithTextChannels.reduce((map: Map<string, AutomodContext[]>, context) => {
|
||||
|
@ -63,16 +63,16 @@ export const ReplyAction = automodAction({
|
|||
const formatted =
|
||||
typeof actionConfig === "string"
|
||||
? await renderReplyText(actionConfig)
|
||||
: ((await renderRecursively(actionConfig.text, renderReplyText)) as MessageOptions);
|
||||
: ((await renderRecursively(actionConfig.text, renderReplyText)) as MessageCreateOptions);
|
||||
|
||||
if (formatted) {
|
||||
const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as GuildTextBasedChannel;
|
||||
|
||||
// Check for basic Send Messages and View Channel permissions
|
||||
if (
|
||||
!hasDiscordPermissions(
|
||||
channel.permissionsFor(pluginData.client.user!.id),
|
||||
Permissions.FLAGS.SEND_MESSAGES | Permissions.FLAGS.VIEW_CHANNEL,
|
||||
PermissionsBitField.Flags.SendMessages | PermissionsBitField.Flags.ViewChannel,
|
||||
)
|
||||
) {
|
||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||
|
@ -84,7 +84,10 @@ export const ReplyAction = automodAction({
|
|||
// If the message is an embed, check for embed permissions
|
||||
if (
|
||||
typeof formatted !== "string" &&
|
||||
!hasDiscordPermissions(channel.permissionsFor(pluginData.client.user!.id), Permissions.FLAGS.EMBED_LINKS)
|
||||
!hasDiscordPermissions(
|
||||
channel.permissionsFor(pluginData.client.user!.id),
|
||||
PermissionsBitField.Flags.EmbedLinks,
|
||||
)
|
||||
) {
|
||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||
body: `Missing permissions to reply **with an embed** in ${verboseChannelMention(
|
||||
|
@ -96,7 +99,7 @@ export const ReplyAction = automodAction({
|
|||
|
||||
const messageContent = validateAndParseMessageContent(formatted);
|
||||
|
||||
const messageOpts: MessageOptions = {
|
||||
const messageOpts: MessageCreateOptions = {
|
||||
...messageContent,
|
||||
allowedMentions: {
|
||||
users: [user.id],
|
||||
|
@ -118,7 +121,7 @@ export const ReplyAction = automodAction({
|
|||
|
||||
if (typeof actionConfig === "object" && actionConfig.auto_delete) {
|
||||
const delay = convertDelayStringToMS(String(actionConfig.auto_delete))!;
|
||||
setTimeout(() => !replyMsg.deleted && replyMsg.delete().catch(noop), delay);
|
||||
setTimeout(() => replyMsg.deletable && replyMsg.delete().catch(noop), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as t from "io-ts";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { CountersPlugin } from "../../Counters/CountersPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
export const SetCounterAction = automodAction({
|
||||
configType: t.type({
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { ChannelType, GuildTextBasedChannel, Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { ChannelTypeStrings } from "src/types";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { convertDelayStringToMS, isDiscordAPIError, tDelayString, tNullable } from "../../../utils";
|
||||
import { automodAction } from "../helpers";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
export const SetSlowmodeAction = automodAction({
|
||||
configType: t.type({
|
||||
|
@ -23,29 +21,27 @@ export const SetSlowmodeAction = automodAction({
|
|||
const channel = pluginData.guild.channels.cache.get(channelId as Snowflake);
|
||||
|
||||
// Only text channels and text channels within categories support slowmodes
|
||||
if (!channel || !(channel.type === ChannelTypeStrings.TEXT || ChannelTypeStrings.CATEGORY)) {
|
||||
if (!channel || (!channel.isTextBased() && channel.type !== ChannelType.GuildCategory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const channelsToSlowmode: TextChannel[] = [];
|
||||
if (channel.type === ChannelTypeStrings.CATEGORY) {
|
||||
const channelsToSlowmode: GuildTextBasedChannel[] = [];
|
||||
if (channel.type === ChannelType.GuildCategory) {
|
||||
// Find all text channels within the category
|
||||
for (const ch of pluginData.guild.channels.cache.values()) {
|
||||
if (ch.parentId === channel.id && ch.type === ChannelTypeStrings.TEXT) {
|
||||
channelsToSlowmode.push(ch as TextChannel);
|
||||
if (ch.parentId === channel.id && ch.type === ChannelType.GuildText) {
|
||||
channelsToSlowmode.push(ch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
channelsToSlowmode.push(channel as TextChannel);
|
||||
channelsToSlowmode.push(channel);
|
||||
}
|
||||
|
||||
const slowmodeSeconds = Math.ceil(slowmodeMs / 1000);
|
||||
|
||||
try {
|
||||
for (const chan of channelsToSlowmode) {
|
||||
await chan.edit({
|
||||
rateLimitPerUser: slowmodeSeconds,
|
||||
});
|
||||
await chan.setRateLimitPerUser(slowmodeSeconds);
|
||||
}
|
||||
} catch (e) {
|
||||
// Check for invalid form body -> indicates duration was too large
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { GuildFeature, ThreadAutoArchiveDuration } from "discord-api-types/v9";
|
||||
import { TextChannel } from "discord.js";
|
||||
import {
|
||||
ChannelType,
|
||||
GuildFeature,
|
||||
GuildTextThreadCreateOptions,
|
||||
ThreadAutoArchiveDuration,
|
||||
ThreadChannel,
|
||||
} from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter";
|
||||
import { ChannelTypeStrings } from "../../../types";
|
||||
import { convertDelayStringToMS, MINUTES, noop, tDelayString, tNullable } from "../../../utils";
|
||||
import { savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { automodAction } from "../helpers";
|
||||
|
@ -32,12 +36,11 @@ export const StartThreadAction = automodAction({
|
|||
const threads = contexts.filter((c) => {
|
||||
if (!c.message || !c.user) return false;
|
||||
const channel = pluginData.guild.channels.cache.get(c.message.channel_id);
|
||||
if (channel?.type !== ChannelTypeStrings.TEXT || !channel.isText()) return false; // for some reason the typing here for channel.type defaults to ThreadChannelTypes (?)
|
||||
if (channel?.type !== ChannelType.GuildText || !channel.isTextBased()) return false; // for some reason the typing here for channel.type defaults to ThreadChannelTypes (?)
|
||||
// check against max threads per channel
|
||||
if (actionConfig.limit_per_channel && actionConfig.limit_per_channel > 0) {
|
||||
const threadCount = channel.threads.cache.filter(
|
||||
(tr) =>
|
||||
tr.ownerId === pluginData.client.user!.id && !tr.deleted && !tr.archived && tr.parentId === channel.id,
|
||||
(tr) => tr.ownerId === pluginData.client.user!.id && !tr.archived && tr.parentId === channel.id,
|
||||
).size;
|
||||
if (threadCount >= actionConfig.limit_per_channel) return false;
|
||||
}
|
||||
|
@ -53,7 +56,9 @@ export const StartThreadAction = automodAction({
|
|||
: ThreadAutoArchiveDuration.OneHour;
|
||||
|
||||
for (const threadContext of threads) {
|
||||
const channel = pluginData.guild.channels.cache.get(threadContext.message!.channel_id) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(threadContext.message!.channel_id);
|
||||
if (!channel || !("threads" in channel) || channel.type === ChannelType.GuildForum) continue;
|
||||
|
||||
const renderThreadName = async (str: string) =>
|
||||
renderTemplate(
|
||||
str,
|
||||
|
@ -63,20 +68,35 @@ export const StartThreadAction = automodAction({
|
|||
}),
|
||||
);
|
||||
const threadName = await renderThreadName(actionConfig.name ?? "{user.tag}s thread");
|
||||
const thread = await channel.threads
|
||||
.create({
|
||||
name: threadName,
|
||||
autoArchiveDuration: autoArchive,
|
||||
type:
|
||||
actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads)
|
||||
? ChannelTypeStrings.PRIVATE_THREAD
|
||||
: ChannelTypeStrings.PUBLIC_THREAD,
|
||||
startMessage:
|
||||
!actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads)
|
||||
? threadContext.message!.id
|
||||
: undefined,
|
||||
})
|
||||
.catch(noop);
|
||||
const threadOptions: GuildTextThreadCreateOptions<unknown> = {
|
||||
name: threadName,
|
||||
autoArchiveDuration: autoArchive,
|
||||
startMessage:
|
||||
!actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads)
|
||||
? threadContext.message!.id
|
||||
: undefined,
|
||||
};
|
||||
|
||||
let thread: ThreadChannel | undefined;
|
||||
if (channel.type === ChannelType.GuildNews) {
|
||||
thread = await channel.threads
|
||||
.create({
|
||||
...threadOptions,
|
||||
type: ChannelType.AnnouncementThread,
|
||||
})
|
||||
.catch(() => undefined);
|
||||
} else {
|
||||
thread = await channel.threads
|
||||
.create({
|
||||
...threadOptions,
|
||||
type: actionConfig.private ? ChannelType.PrivateThread : ChannelType.PublicThread,
|
||||
startMessage:
|
||||
!actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads)
|
||||
? threadContext.message!.id
|
||||
: undefined,
|
||||
})
|
||||
.catch(() => undefined);
|
||||
}
|
||||
if (actionConfig.slowmode && thread) {
|
||||
const dur = Math.ceil(Math.max(convertDelayStringToMS(actionConfig.slowmode) ?? 0, 0) / 1000);
|
||||
if (dur > 0) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { typedGuildCommand } from "knub";
|
||||
import { guildPluginMessageCommand } from "knub";
|
||||
import { sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { setAntiraidLevel } from "../functions/setAntiraidLevel";
|
||||
import { AutomodPluginType } from "../types";
|
||||
|
||||
export const AntiraidClearCmd = typedGuildCommand<AutomodPluginType>()({
|
||||
export const AntiraidClearCmd = guildPluginMessageCommand<AutomodPluginType>()({
|
||||
trigger: ["antiraid clear", "antiraid reset", "antiraid none", "antiraid off"],
|
||||
permission: "can_set_antiraid",
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { typedGuildCommand } from "knub";
|
||||
import { guildPluginMessageCommand } from "knub";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { setAntiraidLevel } from "../functions/setAntiraidLevel";
|
||||
import { AutomodPluginType } from "../types";
|
||||
|
||||
export const SetAntiraidCmd = typedGuildCommand<AutomodPluginType>()({
|
||||
export const SetAntiraidCmd = guildPluginMessageCommand<AutomodPluginType>()({
|
||||
trigger: "antiraid",
|
||||
permission: "can_set_antiraid",
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { typedGuildCommand } from "knub";
|
||||
import { guildPluginMessageCommand } from "knub";
|
||||
import { AutomodPluginType } from "../types";
|
||||
|
||||
export const ViewAntiraidCmd = typedGuildCommand<AutomodPluginType>()({
|
||||
export const ViewAntiraidCmd = guildPluginMessageCommand<AutomodPluginType>()({
|
||||
trigger: "antiraid",
|
||||
permission: "can_view_antiraid",
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { typedGuildEventListener } from "knub";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import { RecentActionType } from "../constants";
|
||||
import { runAutomod } from "../functions/runAutomod";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
|
||||
export const RunAutomodOnJoinEvt = typedGuildEventListener<AutomodPluginType>()({
|
||||
export const RunAutomodOnJoinEvt = guildPluginEventListener<AutomodPluginType>()({
|
||||
event: "guildMemberAdd",
|
||||
listener({ pluginData, args: { member } }) {
|
||||
const context: AutomodContext = {
|
||||
|
@ -26,7 +26,7 @@ export const RunAutomodOnJoinEvt = typedGuildEventListener<AutomodPluginType>()(
|
|||
},
|
||||
});
|
||||
|
||||
export const RunAutomodOnLeaveEvt = typedGuildEventListener<AutomodPluginType>()({
|
||||
export const RunAutomodOnLeaveEvt = guildPluginEventListener<AutomodPluginType>()({
|
||||
event: "guildMemberRemove",
|
||||
listener({ pluginData, args: { member } }) {
|
||||
const context: AutomodContext = {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { typedGuildEventListener } from "knub";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import diff from "lodash.difference";
|
||||
import isEqual from "lodash.isequal";
|
||||
import { runAutomod } from "../functions/runAutomod";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
|
||||
export const RunAutomodOnMemberUpdate = typedGuildEventListener<AutomodPluginType>()({
|
||||
export const RunAutomodOnMemberUpdate = guildPluginEventListener<AutomodPluginType>()({
|
||||
event: "guildMemberUpdate",
|
||||
listener({ pluginData, args: { oldMember, newMember } }) {
|
||||
if (!oldMember) return;
|
||||
|
|
|
@ -14,7 +14,6 @@ export async function runAutomodOnCounterTrigger(
|
|||
) {
|
||||
const user = userId ? await resolveUser(pluginData.client, userId) : undefined;
|
||||
const member = (userId && (await resolveMember(pluginData.client, pluginData.guild, userId))) || undefined;
|
||||
|
||||
const prettyCounterName = pluginData.getPlugin(CountersPlugin).getPrettyNameForCounter(counterName);
|
||||
const prettyTriggerName = pluginData
|
||||
.getPlugin(CountersPlugin)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import moment from "moment-timezone";
|
||||
import { performance } from "perf_hooks";
|
||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||
import { profilingEnabled } from "../../../utils/easyProfiler";
|
||||
import { addRecentActionsFromMessage } from "../functions/addRecentActionsFromMessage";
|
||||
import { clearRecentActionsForMessage } from "../functions/clearRecentActionsForMessage";
|
||||
import { runAutomod } from "../functions/runAutomod";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
import { performance } from "perf_hooks";
|
||||
import { profilingEnabled } from "../../../utils/easyProfiler";
|
||||
|
||||
export async function runAutomodOnMessage(
|
||||
pluginData: GuildPluginData<AutomodPluginType>,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { typedGuildEventListener } from "knub";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import { RecentActionType } from "../constants";
|
||||
import { runAutomod } from "../functions/runAutomod";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
|
||||
export const RunAutomodOnThreadCreate = typedGuildEventListener<AutomodPluginType>()({
|
||||
export const RunAutomodOnThreadCreate = guildPluginEventListener<AutomodPluginType>()({
|
||||
event: "threadCreate",
|
||||
async listener({ pluginData, args: { thread } }) {
|
||||
const user = thread.ownerId
|
||||
|
@ -32,7 +32,7 @@ export const RunAutomodOnThreadCreate = typedGuildEventListener<AutomodPluginTyp
|
|||
},
|
||||
});
|
||||
|
||||
export const RunAutomodOnThreadDelete = typedGuildEventListener<AutomodPluginType>()({
|
||||
export const RunAutomodOnThreadDelete = guildPluginEventListener<AutomodPluginType>()({
|
||||
event: "threadDelete",
|
||||
async listener({ pluginData, args: { thread } }) {
|
||||
const user = thread.ownerId
|
||||
|
@ -54,7 +54,7 @@ export const RunAutomodOnThreadDelete = typedGuildEventListener<AutomodPluginTyp
|
|||
},
|
||||
});
|
||||
|
||||
export const RunAutomodOnThreadUpdate = typedGuildEventListener<AutomodPluginType>()({
|
||||
export const RunAutomodOnThreadUpdate = guildPluginEventListener<AutomodPluginType>()({
|
||||
event: "threadUpdate",
|
||||
async listener({ pluginData, args: { oldThread, newThread: thread } }) {
|
||||
const user = thread.ownerId
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
import { RECENT_ACTION_EXPIRY_TIME } from "../constants";
|
||||
import { AutomodPluginType } from "../types";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
|
||||
export function clearOldRecentActions(pluginData: GuildPluginData<AutomodPluginType>) {
|
||||
const stopProfiling = startProfiling(pluginData.getKnubInstance().profiler, "automod:fns:clearOldRecentActions");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
import { RECENT_SPAM_EXPIRY_TIME } from "../constants";
|
||||
import { AutomodPluginType } from "../types";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
|
||||
export function clearOldRecentSpam(pluginData: GuildPluginData<AutomodPluginType>) {
|
||||
const stopProfiling = startProfiling(pluginData.getKnubInstance().profiler, "automod:fns:clearOldRecentSpam");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
|
||||
export function clearRecentActionsForMessage(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
|
||||
const stopProfiling = startProfiling(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
import { RecentActionType } from "../constants";
|
||||
import { AutomodPluginType } from "../types";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
|
||||
export function findRecentSpam(
|
||||
pluginData: GuildPluginData<AutomodPluginType>,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import moment from "moment-timezone";
|
||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
import { RecentActionType } from "../constants";
|
||||
import { AutomodPluginType } from "../types";
|
||||
import { getMatchingRecentActions } from "./getMatchingRecentActions";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
|
||||
export function getMatchingMessageRecentActions(
|
||||
pluginData: GuildPluginData<AutomodPluginType>,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
import { RecentActionType } from "../constants";
|
||||
import { AutomodPluginType } from "../types";
|
||||
import { startProfiling } from "../../../utils/easyProfiler";
|
||||
|
||||
export function getMatchingRecentActions(
|
||||
pluginData: GuildPluginData<AutomodPluginType>,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { ActivityType, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { messageSummary, verboseChannelMention } from "../../../utils";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
|
@ -11,13 +11,13 @@ export function getTextMatchPartialSummary(
|
|||
) {
|
||||
if (type === "message") {
|
||||
const message = context.message!;
|
||||
const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake);
|
||||
const channelMention = channel ? verboseChannelMention(channel) : `\`#${message.channel_id}\``;
|
||||
|
||||
return `message in ${channelMention}:\n${messageSummary(message)}`;
|
||||
} else if (type === "embed") {
|
||||
const message = context.message!;
|
||||
const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake);
|
||||
const channelMention = channel ? verboseChannelMention(channel) : `\`#${message.channel_id}\``;
|
||||
|
||||
return `message embed in ${channelMention}:\n${messageSummary(message)}`;
|
||||
|
@ -29,6 +29,6 @@ export function getTextMatchPartialSummary(
|
|||
const visibleName = context.member?.nickname || context.user!.username;
|
||||
return `visible name: ${visibleName}`;
|
||||
} else if (type === "customstatus") {
|
||||
return `custom status: ${context.member!.presence?.activities.find((a) => a.type === "CUSTOM")?.name}`;
|
||||
return `custom status: ${context.member!.presence?.activities.find((a) => a.type === ActivityType.Custom)?.name}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Constants, MessageEmbed } from "discord.js";
|
||||
import { ActivityType, Embed } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||
import { resolveMember } from "../../../utils";
|
||||
import { DeepMutable } from "../../../utils/typeUtils.js";
|
||||
import { AutomodPluginType } from "../types";
|
||||
|
||||
type TextTriggerWithMultipleMatchTypes = {
|
||||
|
@ -33,7 +34,7 @@ export async function* matchMultipleTextTypesOnMessage(
|
|||
}
|
||||
|
||||
if (trigger.match_embeds && msg.data.embeds?.length) {
|
||||
const copiedEmbed: MessageEmbed = JSON.parse(JSON.stringify(msg.data.embeds[0]));
|
||||
const copiedEmbed: DeepMutable<Embed> = JSON.parse(JSON.stringify(msg.data.embeds[0]));
|
||||
if (copiedEmbed.video) {
|
||||
copiedEmbed.description = ""; // The description is not rendered, hence it doesn't need to be matched
|
||||
}
|
||||
|
@ -53,7 +54,7 @@ export async function* matchMultipleTextTypesOnMessage(
|
|||
}
|
||||
|
||||
for (const activity of member.presence?.activities ?? []) {
|
||||
if (activity.type === Constants.ActivityTypes[4]) {
|
||||
if (activity.type === ActivityType.Custom) {
|
||||
yield ["customstatus", `${activity.emoji} ${activity.name}`];
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel, ThreadChannel } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||
import { disableUserNotificationStrings, UserNotificationMethod } from "../../../utils";
|
||||
|
@ -19,7 +19,7 @@ export function resolveActionContactMethods(
|
|||
}
|
||||
|
||||
const channel = pluginData.guild.channels.cache.get(actionConfig.notifyChannel as Snowflake);
|
||||
if (!channel?.isText()) {
|
||||
if (!channel?.isTextBased()) {
|
||||
throw new RecoverablePluginError(ERRORS.INVALID_USER_NOTIFICATION_CHANNEL);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { Snowflake, TextChannel, ThreadChannel } from "discord.js";
|
||||
import { GuildTextBasedChannel, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { performance } from "perf_hooks";
|
||||
import { calculateBlocking, profilingEnabled } from "../../../utils/easyProfiler";
|
||||
import { availableActions } from "../actions/availableActions";
|
||||
import { CleanAction } from "../actions/clean";
|
||||
import { AutomodTriggerMatchResult } from "../helpers";
|
||||
import { availableTriggers } from "../triggers/availableTriggers";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
import { checkAndUpdateCooldown } from "./checkAndUpdateCooldown";
|
||||
import { performance } from "perf_hooks";
|
||||
import { calculateBlocking, profilingEnabled } from "../../../utils/easyProfiler";
|
||||
|
||||
export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
|
||||
const userId = context.user?.id || context.member?.id || context.message?.user_id;
|
||||
|
@ -18,7 +18,7 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
|
|||
const channelOrThread =
|
||||
context.channel ??
|
||||
(channelIdOrThreadId
|
||||
? (pluginData.guild.channels.cache.get(channelIdOrThreadId as Snowflake) as TextChannel | ThreadChannel)
|
||||
? (pluginData.guild.channels.cache.get(channelIdOrThreadId as Snowflake) as GuildTextBasedChannel)
|
||||
: null);
|
||||
const channelId = channelOrThread?.isThread() ? channelOrThread.parent?.id : channelIdOrThreadId;
|
||||
const threadId = channelOrThread?.isThread() ? channelOrThread.id : null;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { User } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { runAutomodOnAntiraidLevel } from "../events/runAutomodOnAntiraidLevel";
|
||||
import { AutomodPluginType } from "../types";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as t from "io-ts";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { Awaitable } from "knub/dist/utils";
|
||||
import { Awaitable } from "../../utils/typeUtils";
|
||||
import { AutomodContext, AutomodPluginType } from "./types";
|
||||
|
||||
interface BaseAutomodTriggerMatchResult {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { trimPluginDescription } from "../../utils";
|
||||
import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint";
|
||||
import { ConfigSchema } from "./types";
|
||||
|
||||
export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
|
||||
prettyName: "Automod",
|
||||
|
@ -99,4 +100,5 @@ export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
|
|||
{matchSummary}
|
||||
~~~
|
||||
`),
|
||||
configSchema: ConfigSchema,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { verboseChannelMention } from "../../../utils";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
@ -22,7 +22,7 @@ export const AnyMessageTrigger = automodTrigger<AnyMessageResultType>()({
|
|||
},
|
||||
|
||||
renderMatchInformation({ pluginData, contexts, matchResult }) {
|
||||
const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake);
|
||||
return `Matched message (\`${contexts[0].message!.id}\`) in ${
|
||||
channel ? verboseChannelMention(channel) : "Unknown Channel"
|
||||
}`;
|
||||
|
|
|
@ -11,9 +11,9 @@ import { KickTrigger } from "./kick";
|
|||
import { LineSpamTrigger } from "./lineSpam";
|
||||
import { LinkSpamTrigger } from "./linkSpam";
|
||||
import { MatchAttachmentTypeTrigger } from "./matchAttachmentType";
|
||||
import { MatchMimeTypeTrigger } from "./matchMimeType";
|
||||
import { MatchInvitesTrigger } from "./matchInvites";
|
||||
import { MatchLinksTrigger } from "./matchLinks";
|
||||
import { MatchMimeTypeTrigger } from "./matchMimeType";
|
||||
import { MatchRegexTrigger } from "./matchRegex";
|
||||
import { MatchWordsTrigger } from "./matchWords";
|
||||
import { MemberJoinTrigger } from "./memberJoin";
|
||||
|
@ -26,14 +26,14 @@ import { NoteTrigger } from "./note";
|
|||
import { RoleAddedTrigger } from "./roleAdded";
|
||||
import { RoleRemovedTrigger } from "./roleRemoved";
|
||||
import { StickerSpamTrigger } from "./stickerSpam";
|
||||
import { ThreadArchiveTrigger } from "./threadArchive";
|
||||
import { ThreadCreateTrigger } from "./threadCreate";
|
||||
import { ThreadCreateSpamTrigger } from "./threadCreateSpam";
|
||||
import { ThreadDeleteTrigger } from "./threadDelete";
|
||||
import { ThreadUnarchiveTrigger } from "./threadUnarchive";
|
||||
import { UnbanTrigger } from "./unban";
|
||||
import { UnmuteTrigger } from "./unmute";
|
||||
import { WarnTrigger } from "./warn";
|
||||
import { ThreadArchiveTrigger } from "./threadArchive";
|
||||
import { ThreadUnarchiveTrigger } from "./threadUnarchive";
|
||||
|
||||
export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>> = {
|
||||
any_message: AnyMessageTrigger,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel, Util } from "discord.js";
|
||||
import { escapeInlineCode, Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
@ -66,12 +66,12 @@ export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({
|
|||
},
|
||||
|
||||
renderMatchInformation({ pluginData, contexts, matchResult }) {
|
||||
const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake) as TextChannel;
|
||||
const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake)!;
|
||||
const prettyChannel = verboseChannelMention(channel);
|
||||
|
||||
return (
|
||||
asSingleLine(`
|
||||
Matched attachment type \`${Util.escapeInlineCode(matchResult.extra.matchedType)}\`
|
||||
Matched attachment type \`${escapeInlineCode(matchResult.extra.matchedType)}\`
|
||||
(${matchResult.extra.mode === "blacklist" ? "blacklisted" : "not in whitelist"})
|
||||
in message (\`${contexts[0].message!.id}\`) in ${prettyChannel}:
|
||||
`) + messageSummary(contexts[0].message!)
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import { Util } from "discord.js";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import { escapeInlineCode } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { phishermanDomainIsSafe } from "../../../data/Phisherman";
|
||||
import { allowTimeout } from "../../../RegExpRunner";
|
||||
import { getUrlsInString, tNullable } from "../../../utils";
|
||||
import { mergeRegexes } from "../../../utils/mergeRegexes";
|
||||
import { mergeWordsIntoRegex } from "../../../utils/mergeWordsIntoRegex";
|
||||
import { TRegex } from "../../../validatorUtils";
|
||||
import { PhishermanPlugin } from "../../Phisherman/PhishermanPlugin";
|
||||
import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary";
|
||||
import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage";
|
||||
import { automodTrigger } from "../helpers";
|
||||
import { mergeRegexes } from "../../../utils/mergeRegexes";
|
||||
import { mergeWordsIntoRegex } from "../../../utils/mergeWordsIntoRegex";
|
||||
import { PhishermanPlugin } from "../../Phisherman/PhishermanPlugin";
|
||||
import { phishermanDomainIsSafe } from "../../../data/Phisherman";
|
||||
|
||||
interface MatchResultType {
|
||||
type: MatchableTextType;
|
||||
|
@ -186,7 +185,7 @@ export const MatchLinksTrigger = automodTrigger<MatchResultType>()({
|
|||
|
||||
renderMatchInformation({ pluginData, contexts, matchResult }) {
|
||||
const partialSummary = getTextMatchPartialSummary(pluginData, matchResult.extra.type, contexts[0]);
|
||||
let information = `Matched link \`${Util.escapeInlineCode(matchResult.extra.link)}\``;
|
||||
let information = `Matched link \`${escapeInlineCode(matchResult.extra.link)}\``;
|
||||
if (matchResult.extra.details) {
|
||||
information += ` ${matchResult.extra.details}`;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { automodTrigger } from "../helpers";
|
||||
import { escapeInlineCode } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils";
|
||||
import { GuildChannel, Util } from "discord.js";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
||||
interface MatchResultType {
|
||||
matchedType: string;
|
||||
|
@ -65,13 +65,13 @@ export const MatchMimeTypeTrigger = automodTrigger<MatchResultType>()({
|
|||
|
||||
renderMatchInformation({ pluginData, contexts, matchResult }) {
|
||||
const { message } = contexts[0];
|
||||
const channel = pluginData.guild.channels.resolve(message!.channel_id);
|
||||
const prettyChannel = verboseChannelMention(channel as GuildChannel);
|
||||
const channel = pluginData.guild.channels.resolve(message!.channel_id)!;
|
||||
const prettyChannel = verboseChannelMention(channel);
|
||||
const { matchedType, mode } = matchResult.extra;
|
||||
|
||||
return (
|
||||
asSingleLine(`
|
||||
Matched MIME type \`${Util.escapeInlineCode(matchedType)}\`
|
||||
Matched MIME type \`${escapeInlineCode(matchedType)}\`
|
||||
(${mode === "blacklist" ? "blacklisted" : "not in whitelist"})
|
||||
in message (\`${message!.id}\`) in ${prettyChannel}
|
||||
`) + messageSummary(message!)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import * as t from "io-ts";
|
||||
import { allowTimeout } from "../../../RegExpRunner";
|
||||
import { mergeRegexes } from "../../../utils/mergeRegexes";
|
||||
import { normalizeText } from "../../../utils/normalizeText";
|
||||
import { stripMarkdown } from "../../../utils/stripMarkdown";
|
||||
import { TRegex } from "../../../validatorUtils";
|
||||
import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary";
|
||||
import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage";
|
||||
import { automodTrigger } from "../helpers";
|
||||
import { mergeRegexes } from "../../../utils/mergeRegexes";
|
||||
|
||||
interface MatchResultType {
|
||||
pattern: string;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Util } from "discord.js";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import * as t from "io-ts";
|
||||
import { normalizeText } from "../../../utils/normalizeText";
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Snowflake } from "discord-api-types/v9";
|
||||
import { User, Util } from "discord.js";
|
||||
import { escapeBold, User, type Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { tNullable } from "../../../utils";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
@ -49,7 +48,7 @@ export const ThreadArchiveTrigger = automodTrigger<ThreadArchiveResult>()({
|
|||
const parentName = matchResult.extra.matchedThreadParentName;
|
||||
const base = `Thread **#${threadName}** (\`${threadId}\`) has been archived in the **#${parentName}** (\`${parentId}\`) channel`;
|
||||
if (threadOwner) {
|
||||
return `${base} by **${Util.escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`;
|
||||
return `${base} by **${escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`;
|
||||
}
|
||||
return base;
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Snowflake } from "discord-api-types/v9";
|
||||
import { User, Util } from "discord.js";
|
||||
import { escapeBold, User, type Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
||||
|
@ -41,7 +40,7 @@ export const ThreadCreateTrigger = automodTrigger<ThreadCreateResult>()({
|
|||
const parentName = matchResult.extra.matchedThreadParentName;
|
||||
const base = `Thread **#${threadName}** (\`${threadId}\`) has been created in the **#${parentName}** (\`${parentId}\`) channel`;
|
||||
if (threadOwner) {
|
||||
return `${base} by **${Util.escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`;
|
||||
return `${base} by **${escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`;
|
||||
}
|
||||
return base;
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Snowflake } from "discord-api-types/v9";
|
||||
import { User, Util } from "discord.js";
|
||||
import { escapeBold, User, type Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
||||
|
@ -40,7 +39,7 @@ export const ThreadDeleteTrigger = automodTrigger<ThreadDeleteResult>()({
|
|||
const parentId = matchResult.extra.matchedThreadParentId;
|
||||
const parentName = matchResult.extra.matchedThreadParentName;
|
||||
if (threadOwner) {
|
||||
return `Thread **#${threadName ?? "Unknown"}** (\`${threadId}\`) created by **${Util.escapeBold(
|
||||
return `Thread **#${threadName ?? "Unknown"}** (\`${threadId}\`) created by **${escapeBold(
|
||||
threadOwner.tag,
|
||||
)}** (\`${threadOwner.id}\`) in the **#${parentName}** (\`${parentId}\`) channel has been deleted`;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Snowflake } from "discord-api-types/v9";
|
||||
import { User, Util } from "discord.js";
|
||||
import { escapeBold, User, type Snowflake } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { tNullable } from "../../../utils";
|
||||
import { automodTrigger } from "../helpers";
|
||||
|
@ -49,7 +48,7 @@ export const ThreadUnarchiveTrigger = automodTrigger<ThreadUnarchiveResult>()({
|
|||
const parentName = matchResult.extra.matchedThreadParentName;
|
||||
const base = `Thread **#${threadName}** (\`${threadId}\`) has been unarchived in the **#${parentName}** (\`${parentId}\`) channel`;
|
||||
if (threadOwner) {
|
||||
return `${base} by **${Util.escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`;
|
||||
return `${base} by **${escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`;
|
||||
}
|
||||
return base;
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { GuildMember, PartialGuildMember, TextChannel, ThreadChannel, User } from "discord.js";
|
||||
import { GuildMember, GuildTextBasedChannel, PartialGuildMember, ThreadChannel, User } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { BasePluginType, CooldownManager } from "knub";
|
||||
import { SavedMessage } from "../../data/entities/SavedMessage";
|
||||
|
@ -139,7 +139,7 @@ export interface AutomodContext {
|
|||
locked?: ThreadChannel;
|
||||
unlocked?: ThreadChannel;
|
||||
};
|
||||
channel?: TextChannel | ThreadChannel;
|
||||
channel?: GuildTextBasedChannel;
|
||||
}
|
||||
|
||||
export interface RecentAction {
|
||||
|
|
|
@ -3,26 +3,26 @@ import { AllowedGuilds } from "../../data/AllowedGuilds";
|
|||
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments";
|
||||
import { Configs } from "../../data/Configs";
|
||||
import { GuildArchives } from "../../data/GuildArchives";
|
||||
import { sendSuccessMessage } from "../../pluginUtils";
|
||||
import { makeIoTsConfigParser, sendSuccessMessage } from "../../pluginUtils";
|
||||
import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { getActiveReload, resetActiveReload } from "./activeReload";
|
||||
import { AddDashboardUserCmd } from "./commands/AddDashboardUserCmd";
|
||||
import { AddServerFromInviteCmd } from "./commands/AddServerFromInviteCmd";
|
||||
import { AllowServerCmd } from "./commands/AllowServerCmd";
|
||||
import { ChannelToServerCmd } from "./commands/ChannelToServerCmd";
|
||||
import { DisallowServerCmd } from "./commands/DisallowServerCmd";
|
||||
import { EligibleCmd } from "./commands/EligibleCmd";
|
||||
import { LeaveServerCmd } from "./commands/LeaveServerCmd";
|
||||
import { ListDashboardPermsCmd } from "./commands/ListDashboardPermsCmd";
|
||||
import { ListDashboardUsersCmd } from "./commands/ListDashboardUsersCmd";
|
||||
import { ProfilerDataCmd } from "./commands/ProfilerDataCmd";
|
||||
import { RateLimitPerformanceCmd } from "./commands/RateLimitPerformanceCmd";
|
||||
import { ReloadGlobalPluginsCmd } from "./commands/ReloadGlobalPluginsCmd";
|
||||
import { ReloadServerCmd } from "./commands/ReloadServerCmd";
|
||||
import { RemoveDashboardUserCmd } from "./commands/RemoveDashboardUserCmd";
|
||||
import { RestPerformanceCmd } from "./commands/RestPerformanceCmd";
|
||||
import { ServersCmd } from "./commands/ServersCmd";
|
||||
import { BotControlPluginType, ConfigSchema } from "./types";
|
||||
import { ProfilerDataCmd } from "./commands/ProfilerDataCmd";
|
||||
import { AddServerFromInviteCmd } from "./commands/AddServerFromInviteCmd";
|
||||
import { ChannelToServerCmd } from "./commands/ChannelToServerCmd";
|
||||
import { RestPerformanceCmd } from "./commands/RestPerformanceCmd";
|
||||
import { RateLimitPerformanceCmd } from "./commands/RateLimitPerformanceCmd";
|
||||
|
||||
const defaultOptions = {
|
||||
config: {
|
||||
|
@ -37,11 +37,11 @@ const defaultOptions = {
|
|||
|
||||
export const BotControlPlugin = zeppelinGlobalPlugin<BotControlPluginType>()({
|
||||
name: "bot_control",
|
||||
configSchema: ConfigSchema,
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
// prettier-ignore
|
||||
commands: [
|
||||
messageCommands: [
|
||||
ReloadGlobalPluginsCmd,
|
||||
ServersCmd,
|
||||
LeaveServerCmd,
|
||||
|
@ -61,17 +61,19 @@ export const BotControlPlugin = zeppelinGlobalPlugin<BotControlPluginType>()({
|
|||
],
|
||||
|
||||
async afterLoad(pluginData) {
|
||||
pluginData.state.archives = new GuildArchives(0);
|
||||
pluginData.state.allowedGuilds = new AllowedGuilds();
|
||||
pluginData.state.configs = new Configs();
|
||||
pluginData.state.apiPermissionAssignments = new ApiPermissionAssignments();
|
||||
const { state, client } = pluginData;
|
||||
|
||||
state.archives = new GuildArchives(0);
|
||||
state.allowedGuilds = new AllowedGuilds();
|
||||
state.configs = new Configs();
|
||||
state.apiPermissionAssignments = new ApiPermissionAssignments();
|
||||
|
||||
const activeReload = getActiveReload();
|
||||
if (activeReload) {
|
||||
const [guildId, channelId] = activeReload;
|
||||
resetActiveReload();
|
||||
|
||||
const guild = await pluginData.client.guilds.fetch(guildId as Snowflake);
|
||||
const guild = await client.guilds.fetch(guildId as Snowflake);
|
||||
if (guild) {
|
||||
const channel = guild.channels.cache.get(channelId as Snowflake);
|
||||
if (channel instanceof TextChannel) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { ApiPermissions } from "@shared/apiPermissions";
|
||||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
@ -19,7 +18,7 @@ export const AddDashboardUserCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const guild = await pluginData.state.allowedGuilds.find(args.guildId);
|
||||
if (!guild) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin");
|
||||
sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -38,7 +37,7 @@ export const AddDashboardUserCmd = botControlCmd({
|
|||
const userNameList = args.users.map((user) => `<@!${user.id}> (**${user.tag}**, \`${user.id}\`)`);
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
msg.channel,
|
||||
`The following users were given dashboard access for **${guild.name}**:\n\n${userNameList}`,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { ApiPermissions } from "@shared/apiPermissions";
|
||||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { DBDateFormat, isGuildInvite, isSnowflake, resolveInvite } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
import moment from "moment-timezone";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { DBDateFormat, isGuildInvite, resolveInvite } from "../../../utils";
|
||||
import { isEligible } from "../functions/isEligible";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
export const AddServerFromInviteCmd = botControlCmd({
|
||||
trigger: ["add_server_from_invite", "allow_server_from_invite", "adv"],
|
||||
|
@ -19,23 +18,19 @@ export const AddServerFromInviteCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const invite = await resolveInvite(pluginData.client, args.inviteCode, true);
|
||||
if (!invite || !isGuildInvite(invite)) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not resolve invite"); // :D
|
||||
sendErrorMessage(pluginData, msg.channel, "Could not resolve invite"); // :D
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = await pluginData.state.allowedGuilds.find(invite.guild.id);
|
||||
if (existing) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is already allowed!");
|
||||
sendErrorMessage(pluginData, msg.channel, "Server is already allowed!");
|
||||
return;
|
||||
}
|
||||
|
||||
const { result, explanation } = await isEligible(pluginData, args.user, invite);
|
||||
if (!result) {
|
||||
sendErrorMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
`Could not add server because it's not eligible: ${explanation}`,
|
||||
);
|
||||
sendErrorMessage(pluginData, msg.channel, `Could not add server because it's not eligible: ${explanation}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -56,10 +51,6 @@ export const AddServerFromInviteCmd = botControlCmd({
|
|||
);
|
||||
}
|
||||
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
"Server was eligible and is now allowed to use Zeppelin!",
|
||||
);
|
||||
sendSuccessMessage(pluginData, msg.channel, "Server was eligible and is now allowed to use Zeppelin!");
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { ApiPermissions } from "@shared/apiPermissions";
|
||||
import { TextChannel } from "discord.js";
|
||||
import moment from "moment-timezone";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { DBDateFormat, isSnowflake } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
export const AllowServerCmd = botControlCmd({
|
||||
trigger: ["allow_server", "allowserver", "add_server", "addserver"],
|
||||
|
@ -21,17 +20,17 @@ export const AllowServerCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const existing = await pluginData.state.allowedGuilds.find(args.guildId);
|
||||
if (existing) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is already allowed!");
|
||||
sendErrorMessage(pluginData, msg.channel, "Server is already allowed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSnowflake(args.guildId)) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Invalid server ID!");
|
||||
sendErrorMessage(pluginData, msg.channel, "Invalid server ID!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.userId && !isSnowflake(args.userId)) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Invalid user ID!");
|
||||
sendErrorMessage(pluginData, msg.channel, "Invalid user ID!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -52,6 +51,6 @@ export const AllowServerCmd = botControlCmd({
|
|||
);
|
||||
}
|
||||
|
||||
sendSuccessMessage(pluginData, msg.channel as TextChannel, "Server is now allowed to use Zeppelin!");
|
||||
sendSuccessMessage(pluginData, msg.channel, "Server is now allowed to use Zeppelin!");
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { Guild, GuildChannel, TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { GuildInvite, isGuildInvite, resolveInvite, verboseUserMention } from "../../../utils";
|
||||
import { isStaffPreFilter, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { botControlCmd } from "../types";
|
||||
import { isEligible } from "../functions/isEligible";
|
||||
|
||||
export const ChannelToServerCmd = botControlCmd({
|
||||
trigger: ["channel_to_server", "channel2server"],
|
||||
|
@ -19,13 +16,13 @@ export const ChannelToServerCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const channel = pluginData.client.channels.cache.get(args.channelId);
|
||||
if (!channel) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Channel not found in cache!");
|
||||
sendErrorMessage(pluginData, msg.channel, "Channel not found in cache!");
|
||||
return;
|
||||
}
|
||||
|
||||
const channelName = channel.isVoice() ? channel.name : `#${(channel as TextChannel).name}`;
|
||||
const channelName = channel.isVoiceBased() ? channel.name : `#${"name" in channel ? channel.name : channel.id}`;
|
||||
|
||||
const guild: Guild | null = (channel as GuildChannel).guild ?? null;
|
||||
const guild = "guild" in channel ? channel.guild : null;
|
||||
const guildInfo = guild ? `${guild.name} (\`${guild.id}\`)` : "Not a server";
|
||||
|
||||
msg.channel.send(`**Channel:** ${channelName} (\`${channel.type}\`) (<#${channel.id}>)\n**Server:** ${guildInfo}`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { noop } from "../../../utils";
|
||||
|
@ -18,7 +18,7 @@ export const DisallowServerCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const existing = await pluginData.state.allowedGuilds.find(args.guildId);
|
||||
if (!existing) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "That server is not allowed in the first place!");
|
||||
sendErrorMessage(pluginData, msg.channel, "That server is not allowed in the first place!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,6 @@ export const DisallowServerCmd = botControlCmd({
|
|||
.get(args.guildId as Snowflake)
|
||||
?.leave()
|
||||
.catch(noop);
|
||||
sendSuccessMessage(pluginData, msg.channel as TextChannel, "Server removed!");
|
||||
sendSuccessMessage(pluginData, msg.channel, "Server removed!");
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { Guild, TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { GuildInvite, isGuildInvite, resolveInvite, verboseUserMention } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
import { isGuildInvite, resolveInvite } from "../../../utils";
|
||||
import { isEligible } from "../functions/isEligible";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
export const EligibleCmd = botControlCmd({
|
||||
trigger: ["eligible", "is_eligible", "iseligible"],
|
||||
|
@ -17,17 +16,17 @@ export const EligibleCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const invite = await resolveInvite(pluginData.client, args.inviteCode, true);
|
||||
if (!invite || !isGuildInvite(invite)) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not resolve invite");
|
||||
sendErrorMessage(pluginData, msg.channel, "Could not resolve invite");
|
||||
return;
|
||||
}
|
||||
|
||||
const { result, explanation } = await isEligible(pluginData, args.user, invite);
|
||||
|
||||
if (result) {
|
||||
sendSuccessMessage(pluginData, msg.channel as TextChannel, `Server is eligible: ${explanation}`);
|
||||
sendSuccessMessage(pluginData, msg.channel, `Server is eligible: ${explanation}`);
|
||||
return;
|
||||
}
|
||||
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, `Server is **NOT** eligible: ${explanation}`);
|
||||
sendErrorMessage(pluginData, msg.channel, `Server is **NOT** eligible: ${explanation}`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
@ -16,7 +16,7 @@ export const LeaveServerCmd = botControlCmd({
|
|||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
if (!pluginData.client.guilds.cache.has(args.guildId as Snowflake)) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "I am not in that guild");
|
||||
sendErrorMessage(pluginData, msg.channel, "I am not in that guild");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -26,10 +26,10 @@ export const LeaveServerCmd = botControlCmd({
|
|||
try {
|
||||
await pluginData.client.guilds.cache.get(args.guildId as Snowflake)?.leave();
|
||||
} catch (e) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, `Failed to leave guild: ${e.message}`);
|
||||
sendErrorMessage(pluginData, msg.channel, `Failed to leave guild: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
sendSuccessMessage(pluginData, msg.channel as TextChannel, `Left guild **${guildName}**`);
|
||||
sendSuccessMessage(pluginData, msg.channel, `Left guild **${guildName}**`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { AllowedGuild } from "../../../data/entities/AllowedGuild";
|
||||
import { ApiPermissionAssignment } from "../../../data/entities/ApiPermissionAssignment";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
|
@ -17,7 +16,7 @@ export const ListDashboardPermsCmd = botControlCmd({
|
|||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
if (!args.user && !args.guildId) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Must specify at least guildId, user, or both.");
|
||||
sendErrorMessage(pluginData, msg.channel, "Must specify at least guildId, user, or both.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -25,7 +24,7 @@ export const ListDashboardPermsCmd = botControlCmd({
|
|||
if (args.guildId) {
|
||||
guild = await pluginData.state.allowedGuilds.find(args.guildId);
|
||||
if (!guild) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin");
|
||||
sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +33,7 @@ export const ListDashboardPermsCmd = botControlCmd({
|
|||
if (args.user) {
|
||||
existingUserAssignment = await pluginData.state.apiPermissionAssignments.getByUserId(args.user.id);
|
||||
if (existingUserAssignment.length === 0) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "The user has no assigned permissions.");
|
||||
sendErrorMessage(pluginData, msg.channel, "The user has no assigned permissions.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +56,7 @@ export const ListDashboardPermsCmd = botControlCmd({
|
|||
if (finalMessage === "") {
|
||||
sendErrorMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
msg.channel,
|
||||
`The user ${userInfo} has no assigned permissions on the specified server.`,
|
||||
);
|
||||
return;
|
||||
|
@ -68,11 +67,7 @@ export const ListDashboardPermsCmd = botControlCmd({
|
|||
|
||||
const existingGuildAssignment = await pluginData.state.apiPermissionAssignments.getByGuildId(guild.id);
|
||||
if (existingGuildAssignment.length === 0) {
|
||||
sendErrorMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
`The server ${guildInfo} has no assigned permissions.`,
|
||||
);
|
||||
sendErrorMessage(pluginData, msg.channel, `The server ${guildInfo} has no assigned permissions.`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,6 +78,6 @@ export const ListDashboardPermsCmd = botControlCmd({
|
|||
}
|
||||
}
|
||||
|
||||
await sendSuccessMessage(pluginData, msg.channel as TextChannel, finalMessage.trim(), {});
|
||||
await sendSuccessMessage(pluginData, msg.channel, finalMessage.trim(), {});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveUser } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
|
@ -15,7 +14,7 @@ export const ListDashboardUsersCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const guild = await pluginData.state.allowedGuilds.find(args.guildId);
|
||||
if (!guild) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin");
|
||||
sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -33,7 +32,7 @@ export const ListDashboardUsersCmd = botControlCmd({
|
|||
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
msg.channel,
|
||||
`The following users have dashboard access for **${guild.name}**:\n\n${userNameList.join("\n")}`,
|
||||
{},
|
||||
);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { getBaseUrl, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { createChunkedMessage, formatNumber, resolveInvite, sorter, verboseUserMention } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
import { GuildArchives } from "../../../data/GuildArchives";
|
||||
import moment from "moment-timezone";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { GuildArchives } from "../../../data/GuildArchives";
|
||||
import { getBaseUrl } from "../../../pluginUtils";
|
||||
import { sorter } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
const sortProps = {
|
||||
totalTime: "TOTAL TIME",
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { botControlCmd } from "../types";
|
||||
import { getRateLimitStats } from "../../../rateLimitStats";
|
||||
import moment from "moment-timezone";
|
||||
import { GuildArchives } from "../../../data/GuildArchives";
|
||||
import { getBaseUrl, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { TextChannel } from "discord.js";
|
||||
import { getBaseUrl, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { getRateLimitStats } from "../../../rateLimitStats";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
export const RateLimitPerformanceCmd = botControlCmd({
|
||||
trigger: ["rate_limit_performance"],
|
||||
|
@ -14,7 +13,7 @@ export const RateLimitPerformanceCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const logItems = getRateLimitStats();
|
||||
if (logItems.length === 0) {
|
||||
sendSuccessMessage(pluginData, msg.channel as TextChannel, `No rate limits hit`);
|
||||
sendSuccessMessage(pluginData, msg.channel, `No rate limits hit`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -25,7 +24,7 @@ export const RateLimitPerformanceCmd = botControlCmd({
|
|||
if (item.data.global) items.push("GLOBAL");
|
||||
items.push(item.data.method.toUpperCase());
|
||||
items.push(item.data.route);
|
||||
items.push(`stalled for ${item.data.timeout}ms`);
|
||||
items.push(`stalled for ${item.data.timeToReset}ms`);
|
||||
items.push(`(max requests ${item.data.limit})`);
|
||||
return items.join(" ");
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import { isStaffPreFilter } from "../../../pluginUtils";
|
||||
import { isStaffPreFilter, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { getActiveReload, setActiveReload } from "../activeReload";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
|
@ -13,7 +12,13 @@ export const ReloadGlobalPluginsCmd = botControlCmd({
|
|||
async run({ pluginData, message }) {
|
||||
if (getActiveReload()) return;
|
||||
|
||||
setActiveReload((message.channel as TextChannel).guild?.id, message.channel.id);
|
||||
const guildId = "guild" in message.channel ? message.channel.guild.id : null;
|
||||
if (!guildId) {
|
||||
sendErrorMessage(pluginData, message.channel, "This command can only be used in a server");
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveReload(guildId, message.channel.id);
|
||||
await message.channel.send("Reloading global plugins...");
|
||||
|
||||
pluginData.getKnubInstance().reloadGlobalContext();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
@ -16,18 +16,18 @@ export const ReloadServerCmd = botControlCmd({
|
|||
|
||||
async run({ pluginData, message: msg, args }) {
|
||||
if (!pluginData.client.guilds.cache.has(args.guildId as Snowflake)) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "I am not in that guild");
|
||||
sendErrorMessage(pluginData, msg.channel, "I am not in that guild");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await pluginData.getKnubInstance().reloadGuild(args.guildId);
|
||||
} catch (e) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, `Failed to reload guild: ${e.message}`);
|
||||
sendErrorMessage(pluginData, msg.channel, `Failed to reload guild: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const guild = await pluginData.client.guilds.fetch(args.guildId as Snowflake);
|
||||
sendSuccessMessage(pluginData, msg.channel as TextChannel, `Reloaded guild **${guild?.name || "???"}**`);
|
||||
sendSuccessMessage(pluginData, msg.channel, `Reloaded guild **${guild?.name || "???"}**`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
@ -18,7 +17,7 @@ export const RemoveDashboardUserCmd = botControlCmd({
|
|||
async run({ pluginData, message: msg, args }) {
|
||||
const guild = await pluginData.state.allowedGuilds.find(args.guildId);
|
||||
if (!guild) {
|
||||
sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin");
|
||||
sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -37,7 +36,7 @@ export const RemoveDashboardUserCmd = botControlCmd({
|
|||
const userNameList = args.users.map((user) => `<@!${user.id}> (**${user.tag}**, \`${user.id}\`)`);
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel as TextChannel,
|
||||
msg.channel,
|
||||
`The following users were removed from the dashboard for **${guild.name}**:\n\n${userNameList}`,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { createChunkedMessage, formatNumber, resolveInvite, sorter, verboseUserMention } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
import { getTopRestCallStats } from "../../../restCallStats";
|
||||
import { createChunkedMessage } from "../../../utils";
|
||||
import { botControlCmd } from "../types";
|
||||
|
||||
const leadingPathRegex = /(?<=\().+\/backend\//g;
|
||||
|
||||
|
@ -22,6 +20,6 @@ export const RestPerformanceCmd = botControlCmd({
|
|||
const cleanSource = callStats.source.replace(leadingPathRegex, "");
|
||||
return `**${callStats.count} calls**\n${callStats.method.toUpperCase()} ${callStats.path}\n${cleanSource}`;
|
||||
});
|
||||
createChunkedMessage(msg.channel as TextChannel, `Top rest calls:\n\n${formatted.join("\n")}`);
|
||||
createChunkedMessage(msg.channel, `Top rest calls:\n\n${formatted.join("\n")}`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { TextChannel } from "discord.js";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isStaffPreFilter } from "../../../pluginUtils";
|
||||
|
@ -51,7 +50,7 @@ export const ServersCmd = botControlCmd({
|
|||
const owner = getUser(pluginData.client, g.ownerId);
|
||||
return `\`${paddedId}\` **${g.name}** (${g.memberCount} members) (owner **${owner.tag}** \`${owner.id}\`)`;
|
||||
});
|
||||
createChunkedMessage(msg.channel as TextChannel, lines.join("\n"));
|
||||
createChunkedMessage(msg.channel, lines.join("\n"));
|
||||
} else {
|
||||
msg.channel.send("No servers matched the filters");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { User } from "discord.js";
|
||||
import { BotControlPluginType } from "../types";
|
||||
import { GlobalPluginData } from "knub";
|
||||
import { GuildInvite } from "../../../utils";
|
||||
import { BotControlPluginType } from "../types";
|
||||
|
||||
const REQUIRED_MEMBER_COUNT = 5000;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, typedGlobalCommand, typedGlobalEventListener } from "knub";
|
||||
import { BasePluginType, globalPluginEventListener, globalPluginMessageCommand } from "knub";
|
||||
import { AllowedGuilds } from "../../data/AllowedGuilds";
|
||||
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments";
|
||||
import { Configs } from "../../data/Configs";
|
||||
|
@ -26,5 +26,5 @@ export interface BotControlPluginType extends BasePluginType {
|
|||
};
|
||||
}
|
||||
|
||||
export const botControlCmd = typedGlobalCommand<BotControlPluginType>();
|
||||
export const botControlEvt = typedGlobalEventListener<BotControlPluginType>();
|
||||
export const botControlCmd = globalPluginMessageCommand<BotControlPluginType>();
|
||||
export const botControlEvt = globalPluginEventListener<BotControlPluginType>();
|
||||
|
|
|
@ -3,8 +3,9 @@ import { Case } from "../../data/entities/Case";
|
|||
import { GuildArchives } from "../../data/GuildArchives";
|
||||
import { GuildCases } from "../../data/GuildCases";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { mapToPublicFn } from "../../pluginUtils";
|
||||
import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { createCase } from "./functions/createCase";
|
||||
|
@ -16,7 +17,11 @@ import { getRecentCasesByMod } from "./functions/getRecentCasesByMod";
|
|||
import { getTotalCasesByMod } from "./functions/getTotalCasesByMod";
|
||||
import { postCaseToCaseLogChannel } from "./functions/postToCaseLogChannel";
|
||||
import { CaseArgs, CaseNoteArgs, CasesPluginType, ConfigSchema } from "./types";
|
||||
import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin";
|
||||
|
||||
// The `any` cast here is to prevent TypeScript from locking up from the circular dependency
|
||||
function getLogsPlugin(): Promise<any> {
|
||||
return import("../Logs/LogsPlugin.js") as Promise<any>;
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
config: {
|
||||
|
@ -37,15 +42,11 @@ export const CasesPlugin = zeppelinGuildPlugin<CasesPluginType>()({
|
|||
description: trimPluginDescription(`
|
||||
This plugin contains basic configuration for cases created by other plugins
|
||||
`),
|
||||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: async () => [
|
||||
TimeAndDatePlugin,
|
||||
InternalPosterPlugin,
|
||||
// The `as any` cast here is to prevent TypeScript from locking up from the circular dependency
|
||||
((await import("../Logs/LogsPlugin")) as any).LogsPlugin,
|
||||
],
|
||||
configSchema: ConfigSchema,
|
||||
dependencies: async () => [TimeAndDatePlugin, InternalPosterPlugin, (await getLogsPlugin()).LogsPlugin],
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
public: {
|
||||
|
@ -81,8 +82,10 @@ export const CasesPlugin = zeppelinGuildPlugin<CasesPluginType>()({
|
|||
},
|
||||
|
||||
afterLoad(pluginData) {
|
||||
pluginData.state.logs = new GuildLogs(pluginData.guild.id);
|
||||
pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id);
|
||||
pluginData.state.cases = GuildCases.getGuildInstance(pluginData.guild.id);
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
state.logs = new GuildLogs(pluginData.guild.id);
|
||||
state.archives = GuildArchives.getGuildInstance(guild.id);
|
||||
state.cases = GuildCases.getGuildInstance(guild.id);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Snowflake } from "discord-api-types/globals";
|
||||
import type { Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { logger } from "../../../logger";
|
||||
import { resolveUser } from "../../../utils";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { MessageEditOptions, MessageOptions, Util } from "discord.js";
|
||||
import { escapeCodeBlock, MessageCreateOptions, MessageEditOptions } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import moment from "moment-timezone";
|
||||
import { CaseTypes } from "../../../data/CaseTypes";
|
||||
|
@ -14,7 +14,7 @@ export async function getCaseEmbed(
|
|||
caseOrCaseId: Case | number,
|
||||
requestMemberId?: string,
|
||||
noOriginalCaseLink?: boolean,
|
||||
): Promise<MessageOptions & MessageEditOptions> {
|
||||
): Promise<MessageCreateOptions & MessageEditOptions> {
|
||||
const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId));
|
||||
if (!theCase) {
|
||||
throw new Error("Unknown case");
|
||||
|
@ -67,7 +67,7 @@ export async function getCaseEmbed(
|
|||
if (theCase.notes.length) {
|
||||
for (const note of theCase.notes) {
|
||||
const noteDate = moment.utc(note.created_at);
|
||||
let noteBody = Util.escapeCodeBlock(note.body.trim());
|
||||
let noteBody = escapeCodeBlock(note.body.trim());
|
||||
if (noteBody === "") {
|
||||
noteBody = emptyEmbedValue;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { splitMessageIntoChunks } from "knub/dist/helpers";
|
||||
import { splitMessageIntoChunks } from "knub/helpers";
|
||||
import moment from "moment-timezone";
|
||||
import { Case } from "../../../data/entities/Case";
|
||||
import { convertDelayStringToMS, DAYS, DBDateFormat, disableLinkPreviews, messageLink } from "../../../utils";
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { FileOptions, MessageOptions, NewsChannel, Snowflake, TextChannel } from "discord.js";
|
||||
import { MessageCreateOptions, NewsChannel, RESTJSONErrorCodes, Snowflake, TextChannel } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { Case } from "../../../data/entities/Case";
|
||||
import { isDiscordAPIError } from "../../../utils";
|
||||
import { InternalPosterMessageResult } from "../../InternalPoster/functions/sendMessage";
|
||||
import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { CasesPluginType } from "../types";
|
||||
import { getCaseEmbed } from "./getCaseEmbed";
|
||||
import { resolveCaseId } from "./resolveCaseId";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin";
|
||||
import { InternalPosterMessageResult } from "../../InternalPoster/functions/sendMessage";
|
||||
|
||||
export async function postToCaseLogChannel(
|
||||
pluginData: GuildPluginData<CasesPluginType>,
|
||||
content: MessageOptions,
|
||||
file?: FileOptions[],
|
||||
content: MessageCreateOptions,
|
||||
files?: MessageCreateOptions["files"],
|
||||
): Promise<InternalPosterMessageResult | null> {
|
||||
const caseLogChannelId = pluginData.config.get().case_log_channel;
|
||||
if (!caseLogChannelId) return null;
|
||||
|
@ -23,13 +23,16 @@ export async function postToCaseLogChannel(
|
|||
|
||||
let result: InternalPosterMessageResult | null = null;
|
||||
try {
|
||||
if (file != null) {
|
||||
content.files = file;
|
||||
if (files != null) {
|
||||
content.files = files;
|
||||
}
|
||||
const poster = pluginData.getPlugin(InternalPosterPlugin);
|
||||
result = await poster.sendMessage(caseLogChannel, { ...content });
|
||||
} catch (e) {
|
||||
if (isDiscordAPIError(e) && (e.code === 50013 || e.code === 50001)) {
|
||||
if (
|
||||
isDiscordAPIError(e) &&
|
||||
(e.code === RESTJSONErrorCodes.MissingPermissions || e.code === RESTJSONErrorCodes.MissingAccess)
|
||||
) {
|
||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||
body: `Missing permissions to post mod cases in <#${caseLogChannel.id}>`,
|
||||
});
|
||||
|
@ -57,10 +60,12 @@ export async function postCaseToCaseLogChannel(
|
|||
|
||||
try {
|
||||
const poster = pluginData.getPlugin(InternalPosterPlugin);
|
||||
const channel = pluginData.guild.channels.resolve(channelId as Snowflake) as TextChannel;
|
||||
const message = await channel.messages.fetch(messageId);
|
||||
if (message) {
|
||||
await poster.editMessage(message, caseEmbed);
|
||||
const channel = pluginData.guild.channels.resolve(channelId as Snowflake);
|
||||
if (channel?.isTextBased()) {
|
||||
const message = await channel.messages.fetch(messageId);
|
||||
if (message) {
|
||||
await poster.editMessage(message, caseEmbed);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch {} // tslint:disable-line:no-empty
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PluginOptions } from "knub";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
|
@ -53,10 +54,11 @@ export const CensorPlugin = zeppelinGuildPlugin<CensorPluginType>()({
|
|||
For more advanced filtering, check out the Automod plugin!
|
||||
`),
|
||||
legacy: true,
|
||||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: () => [LogsPlugin],
|
||||
configSchema: ConfigSchema,
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
beforeLoad(pluginData) {
|
||||
|
@ -69,7 +71,7 @@ export const CensorPlugin = zeppelinGuildPlugin<CensorPluginType>()({
|
|||
},
|
||||
|
||||
afterLoad(pluginData) {
|
||||
const { state, guild } = pluginData;
|
||||
const { state } = pluginData;
|
||||
|
||||
state.onMessageCreateFn = (msg) => onMessageCreate(pluginData, msg);
|
||||
state.savedMessages.events.on("create", state.onMessageCreateFn);
|
||||
|
@ -79,9 +81,11 @@ export const CensorPlugin = zeppelinGuildPlugin<CensorPluginType>()({
|
|||
},
|
||||
|
||||
beforeUnload(pluginData) {
|
||||
discardRegExpRunner(`guild-${pluginData.guild.id}`);
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn);
|
||||
pluginData.state.savedMessages.events.off("update", pluginData.state.onMessageUpdateFn);
|
||||
discardRegExpRunner(`guild-${guild.id}`);
|
||||
|
||||
state.savedMessages.events.off("create", state.onMessageCreateFn);
|
||||
state.savedMessages.events.off("update", state.onMessageUpdateFn);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Invite, MessageEmbed } from "discord.js";
|
||||
import { Embed, Invite } from "discord.js";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import { GuildPluginData } from "knub";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
|
@ -19,7 +19,7 @@ export async function applyFiltersToMsg(
|
|||
let messageContent = savedMessage.data.content || "";
|
||||
if (savedMessage.data.attachments) messageContent += " " + JSON.stringify(savedMessage.data.attachments);
|
||||
if (savedMessage.data.embeds) {
|
||||
const embeds = (savedMessage.data.embeds as MessageEmbed[]).map((e) => cloneDeep(e));
|
||||
const embeds = (savedMessage.data.embeds as Embed[]).map((e) => cloneDeep(e));
|
||||
for (const embed of embeds) {
|
||||
if (embed.type === "video") {
|
||||
// Ignore video descriptions as they're not actually shown on the embed
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { BaseGuildTextChannel, GuildTextBasedChannel, Snowflake, TextChannel, ThreadChannel } from "discord.js";
|
||||
import { GuildTextBasedChannel, Snowflake } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { resolveUser } from "../../../utils";
|
||||
import { CensorPluginType } from "../types";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { CensorPluginType } from "../types";
|
||||
|
||||
export async function censorMessage(
|
||||
pluginData: GuildPluginData<CensorPluginType>,
|
||||
|
@ -15,8 +14,8 @@ export async function censorMessage(
|
|||
pluginData.state.serverLogs.ignoreLog(LogType.MESSAGE_DELETE, savedMessage.id);
|
||||
|
||||
try {
|
||||
const resolvedChannel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake) as TextChannel;
|
||||
await resolvedChannel.messages.delete(savedMessage.id as Snowflake);
|
||||
const resolvedChannel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake);
|
||||
if (resolvedChannel?.isTextBased()) await resolvedChannel.messages.delete(savedMessage.id as Snowflake);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import * as t from "io-ts";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { ArchiveChannelCmd } from "./commands/ArchiveChannelCmd";
|
||||
import { ChannelArchiverPluginType } from "./types";
|
||||
|
||||
const ConfigSchema = t.type({});
|
||||
|
||||
export const ChannelArchiverPlugin = zeppelinGuildPlugin<ChannelArchiverPluginType>()({
|
||||
name: "channel_archiver",
|
||||
showInDocs: false,
|
||||
|
||||
dependencies: () => [TimeAndDatePlugin],
|
||||
configSchema: t.type({}),
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
|
||||
// prettier-ignore
|
||||
commands: [
|
||||
messageCommands: [
|
||||
ArchiveChannelCmd,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Collection, Message, Snowflake } from "discord.js";
|
||||
import { Snowflake } from "discord.js";
|
||||
import moment from "moment-timezone";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isOwner, sendErrorMessage } from "../../../pluginUtils";
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import { MessageAttachment, MessageOptions, TextChannel, ThreadChannel } from "discord.js";
|
||||
import { Attachment, GuildTextBasedChannel, MessageCreateOptions } from "discord.js";
|
||||
import fs from "fs";
|
||||
import { downloadFile } from "../../utils";
|
||||
const fsp = fs.promises;
|
||||
|
||||
const MAX_ATTACHMENT_REHOST_SIZE = 1024 * 1024 * 8;
|
||||
|
||||
export async function rehostAttachment(
|
||||
attachment: MessageAttachment,
|
||||
targetChannel: TextChannel | ThreadChannel,
|
||||
): Promise<string> {
|
||||
export async function rehostAttachment(attachment: Attachment, targetChannel: GuildTextBasedChannel): Promise<string> {
|
||||
if (attachment.size > MAX_ATTACHMENT_REHOST_SIZE) {
|
||||
return "Attachment too big to rehost";
|
||||
}
|
||||
|
@ -21,7 +18,7 @@ export async function rehostAttachment(
|
|||
}
|
||||
|
||||
try {
|
||||
const content: MessageOptions = {
|
||||
const content: MessageCreateOptions = {
|
||||
content: `Rehost of attachment ${attachment.id}`,
|
||||
files: [{ name: attachment.name ? attachment.name : undefined, attachment: await fsp.readFile(downloaded.path) }],
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { BasePluginType, typedGuildCommand } from "knub";
|
||||
import { BasePluginType, guildPluginMessageCommand } from "knub";
|
||||
|
||||
export interface ChannelArchiverPluginType extends BasePluginType {
|
||||
state: {};
|
||||
}
|
||||
|
||||
export const channelArchiverCmd = typedGuildCommand<ChannelArchiverPluginType>();
|
||||
export const channelArchiverCmd = guildPluginMessageCommand<ChannelArchiverPluginType>();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { CooldownManager } from "knub";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
|
@ -22,10 +23,11 @@ export const CompanionChannelsPlugin = zeppelinGuildPlugin<CompanionChannelsPlug
|
|||
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.
|
||||
`),
|
||||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: () => [LogsPlugin],
|
||||
configSchema: ConfigSchema,
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
events: [VoiceStateUpdateEvt],
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Permissions, Snowflake, StageChannel, TextChannel, VoiceChannel } from "discord.js";
|
||||
import { PermissionsBitField, Snowflake, StageChannel, TextChannel, VoiceChannel } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { isDiscordAPIError, MINUTES } from "../../../utils";
|
||||
import { filterObject } from "../../../utils/filterObject";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { CompanionChannelsPluginType, TCompanionChannelOpts } from "../types";
|
||||
import { getCompanionChannelOptsForVoiceChannelId } from "./getCompanionChannelOptsForVoiceChannelId";
|
||||
import { filterObject } from "../../../utils/filterObject";
|
||||
|
||||
const ERROR_COOLDOWN_KEY = "errorCooldown";
|
||||
const ERROR_COOLDOWN = 5 * MINUTES;
|
||||
|
@ -64,7 +64,7 @@ export async function handleCompanionPermissions(
|
|||
const channel = pluginData.guild.channels.cache.get(channelId as Snowflake);
|
||||
if (!channel || !(channel instanceof TextChannel)) continue;
|
||||
pluginData.state.serverLogs.ignoreLog(LogType.CHANNEL_UPDATE, channelId, 3 * 1000);
|
||||
const fullSerialized = new Permissions(BigInt(permissions)).serialize();
|
||||
const fullSerialized = new PermissionsBitField(BigInt(permissions)).serialize();
|
||||
const onlyAllowed = filterObject(fullSerialized, (v) => v === true);
|
||||
await channel.permissionOverwrites.create(userId, onlyAllowed, {
|
||||
reason: `Companion Channel for ${voiceChannel!.id} | User Joined`,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, CooldownManager, typedGuildEventListener } from "knub";
|
||||
import { BasePluginType, CooldownManager, guildPluginEventListener } from "knub";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { tNullable } from "../../utils";
|
||||
|
||||
|
@ -29,4 +29,4 @@ export interface CompanionChannelsPluginType extends BasePluginType {
|
|||
};
|
||||
}
|
||||
|
||||
export const companionChannelsEvt = typedGuildEventListener<CompanionChannelsPluginType>();
|
||||
export const companionChannelsEvt = guildPluginEventListener<CompanionChannelsPluginType>();
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { PluginOptions } from "knub";
|
||||
import { GuildContextMenuLinks } from "../../data/GuildContextMenuLinks";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { MutesPlugin } from "../Mutes/MutesPlugin";
|
||||
import { UtilityPlugin } from "../Utility/UtilityPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { ContextClickedEvt } from "./events/ContextClickedEvt";
|
||||
import { ConfigSchema, ContextMenuPluginType } from "./types";
|
||||
import { loadAllCommands } from "./utils/loadAllCommands";
|
||||
import { UtilityPlugin } from "../Utility/UtilityPlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<ContextMenuPluginType> = {
|
||||
config: {
|
||||
|
@ -35,8 +36,8 @@ export const ContextMenuPlugin = zeppelinGuildPlugin<ContextMenuPluginType>()({
|
|||
name: "context_menu",
|
||||
showInDocs: false,
|
||||
|
||||
configSchema: ConfigSchema,
|
||||
dependencies: () => [MutesPlugin, LogsPlugin, UtilityPlugin],
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
// prettier-ignore
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { ContextMenuInteraction, TextChannel } from "discord.js";
|
||||
import { ContextMenuCommandInteraction, TextChannel } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { UtilityPlugin } from "../../../plugins/Utility/UtilityPlugin";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
|
@ -9,7 +8,7 @@ import { ContextMenuPluginType } from "../types";
|
|||
export async function cleanAction(
|
||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||
amount: number,
|
||||
interaction: ContextMenuInteraction,
|
||||
interaction: ContextMenuCommandInteraction,
|
||||
) {
|
||||
interaction.deferReply({ ephemeral: true });
|
||||
const executingMember = await pluginData.guild.members.fetch(interaction.user.id);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { ContextMenuInteraction } from "discord.js";
|
||||
import { ContextMenuCommandInteraction } from "discord.js";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ModActionsPlugin } from "src/plugins/ModActions/ModActionsPlugin";
|
||||
import { canActOn } from "src/pluginUtils";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||
import { convertDelayStringToMS } from "../../../utils";
|
||||
import { CaseArgs } from "../../Cases/types";
|
||||
|
@ -14,7 +13,7 @@ import { ContextMenuPluginType } from "../types";
|
|||
export async function muteAction(
|
||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||
duration: string | undefined,
|
||||
interaction: ContextMenuInteraction,
|
||||
interaction: ContextMenuCommandInteraction,
|
||||
) {
|
||||
interaction.deferReply({ ephemeral: true });
|
||||
const executingMember = await pluginData.guild.members.fetch(interaction.user.id);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { ContextMenuInteraction } from "discord.js";
|
||||
import { ContextMenuCommandInteraction } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { UtilityPlugin } from "../../../plugins/Utility/UtilityPlugin";
|
||||
import { ContextMenuPluginType } from "../types";
|
||||
|
||||
export async function userInfoAction(
|
||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||
interaction: ContextMenuInteraction,
|
||||
interaction: ContextMenuCommandInteraction,
|
||||
) {
|
||||
interaction.deferReply({ ephemeral: true });
|
||||
const executingMember = await pluginData.guild.members.fetch(interaction.user.id);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { ContextMenuInteraction } from "discord.js";
|
||||
import { contextMenuEvt } from "../types";
|
||||
import { routeContextAction } from "../utils/contextRouter";
|
||||
|
||||
|
@ -6,8 +5,8 @@ export const ContextClickedEvt = contextMenuEvt({
|
|||
event: "interactionCreate",
|
||||
|
||||
async listener(meta) {
|
||||
if (!meta.args.interaction.isContextMenu) return;
|
||||
const inter = meta.args.interaction as ContextMenuInteraction;
|
||||
if (!meta.args.interaction.isContextMenuCommand()) return;
|
||||
const inter = meta.args.interaction;
|
||||
await routeContextAction(meta.pluginData, inter);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, typedGuildEventListener } from "knub";
|
||||
import { BasePluginType, guildPluginEventListener } from "knub";
|
||||
import { GuildContextMenuLinks } from "../../data/GuildContextMenuLinks";
|
||||
|
||||
export const ConfigSchema = t.type({
|
||||
|
@ -22,4 +22,4 @@ export interface ContextMenuPluginType extends BasePluginType {
|
|||
};
|
||||
}
|
||||
|
||||
export const contextMenuEvt = typedGuildEventListener<ContextMenuPluginType>();
|
||||
export const contextMenuEvt = guildPluginEventListener<ContextMenuPluginType>();
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { ContextMenuInteraction } from "discord.js";
|
||||
import { ContextMenuCommandInteraction } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ContextMenuPluginType } from "../types";
|
||||
import { hardcodedActions } from "./hardcodedContextOptions";
|
||||
|
||||
export async function routeContextAction(
|
||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||
interaction: ContextMenuInteraction,
|
||||
interaction: ContextMenuCommandInteraction,
|
||||
) {
|
||||
const contextLink = await pluginData.state.contextMenuLinks.get(interaction.commandId);
|
||||
if (!contextLink) return;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { ApplicationCommandData, Constants } from "discord.js";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin";
|
||||
import { ApplicationCommandData, ApplicationCommandType } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin";
|
||||
import { ContextMenuPluginType } from "../types";
|
||||
import { hardcodedContext } from "./hardcodedContextOptions";
|
||||
|
||||
|
@ -14,9 +13,7 @@ export async function loadAllCommands(pluginData: GuildPluginData<ContextMenuPlu
|
|||
for (const [name, label] of Object.entries(hardcodedContext)) {
|
||||
if (!cfg[name]) continue;
|
||||
|
||||
const type = name.startsWith("user")
|
||||
? Constants.ApplicationCommandTypes.USER
|
||||
: Constants.ApplicationCommandTypes.MESSAGE;
|
||||
const type = name.startsWith("user") ? ApplicationCommandType.User : ApplicationCommandType.Message;
|
||||
const data: ApplicationCommandData = {
|
||||
type,
|
||||
name: label,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { EventEmitter } from "events";
|
||||
import * as t from "io-ts";
|
||||
import { PluginOptions } from "knub";
|
||||
import { ConfigPreprocessorFn } from "knub/dist/config/configTypes";
|
||||
import {
|
||||
buildCounterConditionString,
|
||||
CounterTrigger,
|
||||
|
@ -10,7 +10,7 @@ import {
|
|||
import { GuildCounters } from "../../data/GuildCounters";
|
||||
import { mapToPublicFn } from "../../pluginUtils";
|
||||
import { convertDelayStringToMS, MINUTES } from "../../utils";
|
||||
import { StrictValidationError } from "../../validatorUtils";
|
||||
import { StrictValidationError, validate } from "../../validatorUtils";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { AddCounterCmd } from "./commands/AddCounterCmd";
|
||||
import { CountersListCmd } from "./commands/CountersListCmd";
|
||||
|
@ -55,46 +55,6 @@ const defaultOptions: PluginOptions<CountersPluginType> = {
|
|||
],
|
||||
};
|
||||
|
||||
const configPreprocessor: ConfigPreprocessorFn<CountersPluginType> = (options) => {
|
||||
for (const [counterName, counter] of Object.entries(options.config?.counters || {})) {
|
||||
counter.name = counterName;
|
||||
counter.per_user = counter.per_user ?? false;
|
||||
counter.per_channel = counter.per_channel ?? false;
|
||||
counter.initial_value = counter.initial_value ?? 0;
|
||||
counter.triggers = counter.triggers || {};
|
||||
|
||||
if (Object.values(counter.triggers).length > MAX_TRIGGERS_PER_COUNTER) {
|
||||
throw new StrictValidationError([`You can only have at most ${MAX_TRIGGERS_PER_COUNTER} triggers per counter`]);
|
||||
}
|
||||
|
||||
// Normalize triggers
|
||||
for (const [triggerName, trigger] of Object.entries(counter.triggers)) {
|
||||
const triggerObj: Partial<TTrigger> = typeof trigger === "string" ? { condition: trigger } : trigger;
|
||||
|
||||
triggerObj.name = triggerName;
|
||||
const parsedCondition = parseCounterConditionString(triggerObj.condition || "");
|
||||
if (!parsedCondition) {
|
||||
throw new StrictValidationError([
|
||||
`Invalid comparison in counter trigger ${counterName}/${triggerName}: "${triggerObj.condition}"`,
|
||||
]);
|
||||
}
|
||||
|
||||
triggerObj.condition = buildCounterConditionString(parsedCondition[0], parsedCondition[1]);
|
||||
triggerObj.reverse_condition =
|
||||
triggerObj.reverse_condition ||
|
||||
buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]);
|
||||
|
||||
counter.triggers[triggerName] = triggerObj as TTrigger;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.values(options.config?.counters || {}).length > MAX_COUNTERS) {
|
||||
throw new StrictValidationError([`You can only have at most ${MAX_COUNTERS} counters`]);
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Counters plugin keeps track of simple integer values that are tied to a user, channel, both, or neither — "counters".
|
||||
* These values can be changed using the functions in the plugin's public interface.
|
||||
|
@ -113,11 +73,55 @@ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
|
|||
description:
|
||||
"Keep track of per-user, per-channel, or global numbers and trigger specific actions based on this number",
|
||||
configurationGuide: "See <a href='/docs/setup-guides/counters'>Counters setup guide</a>",
|
||||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
configPreprocessor,
|
||||
// TODO: Separate input and output types
|
||||
configParser: (input) => {
|
||||
for (const [counterName, counter] of Object.entries<any>((input as any).counters || {})) {
|
||||
counter.name = counterName;
|
||||
counter.per_user = counter.per_user ?? false;
|
||||
counter.per_channel = counter.per_channel ?? false;
|
||||
counter.initial_value = counter.initial_value ?? 0;
|
||||
counter.triggers = counter.triggers || {};
|
||||
|
||||
if (Object.values(counter.triggers).length > MAX_TRIGGERS_PER_COUNTER) {
|
||||
throw new StrictValidationError([`You can only have at most ${MAX_TRIGGERS_PER_COUNTER} triggers per counter`]);
|
||||
}
|
||||
|
||||
// Normalize triggers
|
||||
for (const [triggerName, trigger] of Object.entries(counter.triggers)) {
|
||||
const triggerObj = (typeof trigger === "string" ? { condition: trigger } : trigger) as Partial<TTrigger>;
|
||||
|
||||
triggerObj.name = triggerName;
|
||||
const parsedCondition = parseCounterConditionString(triggerObj.condition || "");
|
||||
if (!parsedCondition) {
|
||||
throw new StrictValidationError([
|
||||
`Invalid comparison in counter trigger ${counterName}/${triggerName}: "${triggerObj.condition}"`,
|
||||
]);
|
||||
}
|
||||
|
||||
triggerObj.condition = buildCounterConditionString(parsedCondition[0], parsedCondition[1]);
|
||||
triggerObj.reverse_condition =
|
||||
triggerObj.reverse_condition ||
|
||||
buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]);
|
||||
|
||||
counter.triggers[triggerName] = triggerObj as TTrigger;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.values((input as any).counters || {}).length > MAX_COUNTERS) {
|
||||
throw new StrictValidationError([`You can only have at most ${MAX_COUNTERS} counters`]);
|
||||
}
|
||||
|
||||
const error = validate(ConfigSchema, input);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return input as t.TypeOf<typeof ConfigSchema>;
|
||||
},
|
||||
|
||||
public: {
|
||||
counterExists: mapToPublicFn(counterExists),
|
||||
|
@ -136,7 +140,7 @@ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
|
|||
},
|
||||
|
||||
// prettier-ignore
|
||||
commands: [
|
||||
messageCommands: [
|
||||
CountersListCmd,
|
||||
ViewCounterCmd,
|
||||
AddCounterCmd,
|
||||
|
@ -146,32 +150,30 @@ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
|
|||
],
|
||||
|
||||
async beforeLoad(pluginData) {
|
||||
pluginData.state.counters = new GuildCounters(pluginData.guild.id);
|
||||
pluginData.state.events = new EventEmitter();
|
||||
pluginData.state.counterTriggersByCounterId = new Map();
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
state.counters = new GuildCounters(guild.id);
|
||||
state.events = new EventEmitter();
|
||||
state.counterTriggersByCounterId = new Map();
|
||||
|
||||
const activeTriggerIds: number[] = [];
|
||||
|
||||
// Initialize and store the IDs of each of the counters internally
|
||||
pluginData.state.counterIds = {};
|
||||
state.counterIds = {};
|
||||
const config = pluginData.config.get();
|
||||
for (const counter of Object.values(config.counters)) {
|
||||
const dbCounter = await pluginData.state.counters.findOrCreateCounter(
|
||||
counter.name,
|
||||
counter.per_channel,
|
||||
counter.per_user,
|
||||
);
|
||||
pluginData.state.counterIds[counter.name] = dbCounter.id;
|
||||
const dbCounter = await state.counters.findOrCreateCounter(counter.name, counter.per_channel, counter.per_user);
|
||||
state.counterIds[counter.name] = dbCounter.id;
|
||||
|
||||
const thisCounterTriggers: CounterTrigger[] = [];
|
||||
pluginData.state.counterTriggersByCounterId.set(dbCounter.id, thisCounterTriggers);
|
||||
state.counterTriggersByCounterId.set(dbCounter.id, thisCounterTriggers);
|
||||
|
||||
// Initialize triggers
|
||||
for (const trigger of Object.values(counter.triggers)) {
|
||||
const theTrigger = trigger as TTrigger;
|
||||
const parsedCondition = parseCounterConditionString(theTrigger.condition)!;
|
||||
const parsedReverseCondition = parseCounterConditionString(theTrigger.reverse_condition)!;
|
||||
const counterTrigger = await pluginData.state.counters.initCounterTrigger(
|
||||
const counterTrigger = await state.counters.initCounterTrigger(
|
||||
dbCounter.id,
|
||||
theTrigger.name,
|
||||
parsedCondition[0],
|
||||
|
@ -185,17 +187,19 @@ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
|
|||
}
|
||||
|
||||
// Mark old/unused counters to be deleted later
|
||||
await pluginData.state.counters.markUnusedCountersToBeDeleted([...Object.values(pluginData.state.counterIds)]);
|
||||
await state.counters.markUnusedCountersToBeDeleted([...Object.values(state.counterIds)]);
|
||||
|
||||
// Mark old/unused triggers to be deleted later
|
||||
await pluginData.state.counters.markUnusedTriggersToBeDeleted(activeTriggerIds);
|
||||
await state.counters.markUnusedTriggersToBeDeleted(activeTriggerIds);
|
||||
},
|
||||
|
||||
async afterLoad(pluginData) {
|
||||
const { state } = pluginData;
|
||||
|
||||
const config = pluginData.config.get();
|
||||
|
||||
// Start decay timers
|
||||
pluginData.state.decayTimers = [];
|
||||
state.decayTimers = [];
|
||||
for (const [counterName, counter] of Object.entries(config.counters)) {
|
||||
if (!counter.decay) {
|
||||
continue;
|
||||
|
@ -207,7 +211,7 @@ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
|
|||
continue;
|
||||
}
|
||||
|
||||
pluginData.state.decayTimers.push(
|
||||
state.decayTimers.push(
|
||||
setInterval(() => {
|
||||
decayCounter(pluginData, counterName, decayPeriodMs, decay.amount);
|
||||
}, DECAY_APPLY_INTERVAL),
|
||||
|
@ -216,12 +220,14 @@ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
|
|||
},
|
||||
|
||||
beforeUnload(pluginData) {
|
||||
if (pluginData.state.decayTimers) {
|
||||
for (const interval of pluginData.state.decayTimers) {
|
||||
const { state } = pluginData;
|
||||
|
||||
if (state.decayTimers) {
|
||||
for (const interval of state.decayTimers) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}
|
||||
|
||||
pluginData.state.events.removeAllListeners();
|
||||
state.events.removeAllListeners();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { typedGuildCommand } from "knub";
|
||||
import { waitForReply } from "knub/dist/helpers";
|
||||
import { guildPluginMessageCommand } from "knub";
|
||||
import { waitForReply } from "knub/helpers";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveUser, UnknownUser } from "../../../utils";
|
||||
import { changeCounterValue } from "../functions/changeCounterValue";
|
||||
import { CountersPluginType } from "../types";
|
||||
|
||||
export const AddCounterCmd = typedGuildCommand<CountersPluginType>()({
|
||||
export const AddCounterCmd = guildPluginMessageCommand<CountersPluginType>()({
|
||||
trigger: ["counters add", "counter add", "addcounter"],
|
||||
permission: "can_edit",
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { typedGuildCommand } from "knub";
|
||||
import { guildPluginMessageCommand } from "knub";
|
||||
import { sendErrorMessage } from "../../../pluginUtils";
|
||||
import { trimMultilineString, ucfirst } from "../../../utils";
|
||||
import { getGuildPrefix } from "../../../utils/getGuildPrefix";
|
||||
import { CountersPluginType } from "../types";
|
||||
|
||||
export const CountersListCmd = typedGuildCommand<CountersPluginType>()({
|
||||
export const CountersListCmd = guildPluginMessageCommand<CountersPluginType>()({
|
||||
trigger: ["counters list", "counter list", "counters"],
|
||||
permission: "can_view",
|
||||
|
||||
|
@ -44,7 +44,7 @@ export const CountersListCmd = typedGuildCommand<CountersPluginType>()({
|
|||
message.channel.send(
|
||||
trimMultilineString(`
|
||||
${counterLines.join("\n\n")}
|
||||
|
||||
|
||||
${hintLines.join("\n")}
|
||||
`),
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue