mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-17 07:05:03 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
792a65c26c
22 changed files with 265 additions and 68 deletions
|
@ -11,6 +11,7 @@ import { CleanAction } from "./clean";
|
|||
import { KickAction } from "./kick";
|
||||
import { LogAction } from "./log";
|
||||
import { MuteAction } from "./mute";
|
||||
import { PauseInvitesAction } from "./pauseInvites";
|
||||
import { RemoveRolesAction } from "./removeRoles";
|
||||
import { ReplyAction } from "./reply";
|
||||
import { SetAntiraidLevelAction } from "./setAntiraidLevel";
|
||||
|
@ -38,6 +39,7 @@ export const availableActions: Record<string, AutomodActionBlueprint<any>> = {
|
|||
start_thread: StartThreadAction,
|
||||
archive_thread: ArchiveThreadAction,
|
||||
change_perms: ChangePermsAction,
|
||||
pause_invites: PauseInvitesAction,
|
||||
};
|
||||
|
||||
export const AvailableActions = t.type({
|
||||
|
@ -59,4 +61,5 @@ export const AvailableActions = t.type({
|
|||
start_thread: StartThreadAction.configType,
|
||||
archive_thread: ArchiveThreadAction.configType,
|
||||
change_perms: ChangePermsAction.configType,
|
||||
pause_invites: PauseInvitesAction.configType,
|
||||
});
|
||||
|
|
19
backend/src/plugins/Automod/actions/pauseInvites.ts
Normal file
19
backend/src/plugins/Automod/actions/pauseInvites.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { GuildFeature } from "discord.js";
|
||||
import * as t from "io-ts";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
export const PauseInvitesAction = automodAction({
|
||||
configType: t.type({
|
||||
paused: t.boolean,
|
||||
}),
|
||||
|
||||
defaultConfig: {},
|
||||
|
||||
async apply({ pluginData, actionConfig }) {
|
||||
const hasInvitesDisabled = pluginData.guild.features.includes(GuildFeature.InvitesDisabled);
|
||||
|
||||
if (actionConfig.paused !== hasInvitesDisabled) {
|
||||
await pluginData.guild.disableInvites(actionConfig.paused);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -5,13 +5,15 @@ import { AutomodContext, AutomodPluginType } from "../types";
|
|||
|
||||
export async function runAutomodOnAntiraidLevel(
|
||||
pluginData: GuildPluginData<AutomodPluginType>,
|
||||
level: string | null,
|
||||
newLevel: string | null,
|
||||
oldLevel: string | null,
|
||||
user?: User,
|
||||
) {
|
||||
const context: AutomodContext = {
|
||||
timestamp: Date.now(),
|
||||
antiraid: {
|
||||
level,
|
||||
level: newLevel,
|
||||
oldLevel,
|
||||
},
|
||||
user,
|
||||
};
|
||||
|
|
10
backend/src/plugins/Automod/functions/applyCooldown.ts
Normal file
10
backend/src/plugins/Automod/functions/applyCooldown.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { convertDelayStringToMS } from "../../../utils";
|
||||
import { AutomodContext, AutomodPluginType, TRule } from "../types";
|
||||
|
||||
export function applyCooldown(pluginData: GuildPluginData<AutomodPluginType>, rule: TRule, context: AutomodContext) {
|
||||
const cooldownKey = `${rule.name}-${context.user?.id}`;
|
||||
|
||||
const cooldownTime = convertDelayStringToMS(rule.cooldown, "s");
|
||||
if (cooldownTime) pluginData.state.cooldownManager.setCooldown(cooldownKey, cooldownTime);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { convertDelayStringToMS } from "../../../utils";
|
||||
import { AutomodContext, AutomodPluginType, TRule } from "../types";
|
||||
|
||||
export function checkAndUpdateCooldown(
|
||||
pluginData: GuildPluginData<AutomodPluginType>,
|
||||
rule: TRule,
|
||||
context: AutomodContext,
|
||||
) {
|
||||
const cooldownKey = `${rule.name}-${context.user?.id}`;
|
||||
|
||||
if (cooldownKey) {
|
||||
if (pluginData.state.cooldownManager.isOnCooldown(cooldownKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const cooldownTime = convertDelayStringToMS(rule.cooldown, "s");
|
||||
if (cooldownTime) {
|
||||
pluginData.state.cooldownManager.setCooldown(cooldownKey, cooldownTime);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
8
backend/src/plugins/Automod/functions/checkCooldown.ts
Normal file
8
backend/src/plugins/Automod/functions/checkCooldown.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { AutomodContext, AutomodPluginType, TRule } from "../types";
|
||||
|
||||
export function checkCooldown(pluginData: GuildPluginData<AutomodPluginType>, rule: TRule, context: AutomodContext) {
|
||||
const cooldownKey = `${rule.name}-${context.user?.id}`;
|
||||
|
||||
return pluginData.state.cooldownManager.isOnCooldown(cooldownKey);
|
||||
}
|
|
@ -7,7 +7,8 @@ import { CleanAction } from "../actions/clean";
|
|||
import { AutomodTriggerMatchResult } from "../helpers";
|
||||
import { availableTriggers } from "../triggers/availableTriggers";
|
||||
import { AutomodContext, AutomodPluginType } from "../types";
|
||||
import { checkAndUpdateCooldown } from "./checkAndUpdateCooldown";
|
||||
import { applyCooldown } from "./applyCooldown";
|
||||
import { checkCooldown } from "./checkCooldown";
|
||||
|
||||
export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
|
||||
const userId = context.user?.id || context.member?.id || context.message?.user_id;
|
||||
|
@ -46,7 +47,7 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
|
|||
}
|
||||
if (!rule.affects_self && userId && userId === pluginData.client.user?.id) continue;
|
||||
|
||||
if (rule.cooldown && checkAndUpdateCooldown(pluginData, rule, context)) {
|
||||
if (rule.cooldown && checkCooldown(pluginData, rule, context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -84,6 +85,8 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
|
|||
}
|
||||
|
||||
if (matchResult) {
|
||||
if (rule.cooldown) applyCooldown(pluginData, rule, context);
|
||||
|
||||
contexts = [context, ...(matchResult.extraContexts || [])];
|
||||
|
||||
for (const _context of contexts) {
|
||||
|
|
|
@ -9,10 +9,11 @@ export async function setAntiraidLevel(
|
|||
newLevel: string | null,
|
||||
user?: User,
|
||||
) {
|
||||
const oldLevel = pluginData.state.cachedAntiraidLevel;
|
||||
pluginData.state.cachedAntiraidLevel = newLevel;
|
||||
await pluginData.state.antiraidLevels.set(newLevel);
|
||||
|
||||
runAutomodOnAntiraidLevel(pluginData, newLevel, user);
|
||||
runAutomodOnAntiraidLevel(pluginData, newLevel, oldLevel, user);
|
||||
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ interface AntiraidLevelTriggerResult {}
|
|||
export const AntiraidLevelTrigger = automodTrigger<AntiraidLevelTriggerResult>()({
|
||||
configType: t.type({
|
||||
level: tNullable(t.string),
|
||||
only_on_change: tNullable(t.boolean),
|
||||
}),
|
||||
|
||||
defaultConfig: {},
|
||||
|
@ -20,6 +21,14 @@ export const AntiraidLevelTrigger = automodTrigger<AntiraidLevelTriggerResult>()
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
triggerConfig.only_on_change &&
|
||||
context.antiraid.oldLevel !== undefined &&
|
||||
context.antiraid.level === context.antiraid.oldLevel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
extra: {},
|
||||
};
|
||||
|
|
|
@ -54,7 +54,7 @@ export const MatchWordsTrigger = automodTrigger<MatchResultType>()({
|
|||
const looseMatchingThreshold = Math.min(Math.max(trigger.loose_matching_threshold, 1), 64);
|
||||
const patterns = trigger.words.map((word) => {
|
||||
let pattern = trigger.loose_matching
|
||||
? [...word].map((c) => escapeStringRegexp(c)).join(`(?:\\s*|.{0,${looseMatchingThreshold})`)
|
||||
? [...word].map((c) => escapeStringRegexp(c)).join(`(?:\\s*|.{0,${looseMatchingThreshold}})`)
|
||||
: escapeStringRegexp(word);
|
||||
|
||||
if (trigger.only_full_words) {
|
||||
|
|
|
@ -130,6 +130,7 @@ export interface AutomodContext {
|
|||
};
|
||||
antiraid?: {
|
||||
level: string | null;
|
||||
oldLevel?: string | null;
|
||||
};
|
||||
threadChange?: {
|
||||
created?: ThreadChannel;
|
||||
|
|
|
@ -57,9 +57,10 @@ export async function clearMute(
|
|||
await member.timeout(null);
|
||||
}
|
||||
}
|
||||
pluginData.getPlugin(LogsPlugin).logMemberMuteExpired({ member });
|
||||
} catch {
|
||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||
body: `Failed to remove mute role from ${verboseUserMention(member.user)}`,
|
||||
body: `Failed to clear mute from ${verboseUserMention(member.user)}`,
|
||||
});
|
||||
} finally {
|
||||
lock.unlock();
|
||||
|
|
|
@ -3,7 +3,6 @@ import { GuildLogs } from "../../data/GuildLogs";
|
|||
import { GuildPersistedData } from "../../data/GuildPersistedData";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { GuildMemberCachePlugin } from "../GuildMemberCache/GuildMemberCachePlugin";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
|
@ -31,7 +30,7 @@ export const PersistPlugin = zeppelinGuildPlugin<PersistPluginType>()({
|
|||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: () => [LogsPlugin, RoleManagerPlugin, GuildMemberCachePlugin],
|
||||
dependencies: () => [LogsPlugin, RoleManagerPlugin],
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { PersistedData } from "../../../data/entities/PersistedData";
|
||||
import { GuildMemberCachePlugin } from "../../GuildMemberCache/GuildMemberCachePlugin";
|
||||
import { persistEvt } from "../types";
|
||||
|
||||
export const StoreDataEvt = persistEvt({
|
||||
|
@ -9,8 +8,11 @@ export const StoreDataEvt = persistEvt({
|
|||
const config = await pluginData.config.getForUser(member.user);
|
||||
const persistData: Partial<PersistedData> = {};
|
||||
|
||||
// FIXME: New caching thing, or fix deadlocks with this plugin
|
||||
if (member.partial) {
|
||||
return;
|
||||
// Djs hasn't cached member data => use db cache
|
||||
/*
|
||||
const data = await pluginData.getPlugin(GuildMemberCachePlugin).getCachedMemberData(member.id);
|
||||
if (!data) {
|
||||
return;
|
||||
|
@ -22,7 +24,7 @@ export const StoreDataEvt = persistEvt({
|
|||
}
|
||||
if (config.persist_nicknames && data.nickname) {
|
||||
persistData.nickname = data.nickname;
|
||||
}
|
||||
}*/
|
||||
} else {
|
||||
// Djs has cached member data => use that
|
||||
const memberRoles = Array.from(member.roles.cache.keys());
|
||||
|
|
|
@ -86,6 +86,12 @@ export async function applyReactionRoleReactionsToMessage(
|
|||
body: `Error ${e.code} while applying reaction role reactions to ${channelId}/${messageId}: ${e.message}`,
|
||||
});
|
||||
break;
|
||||
} else if (e.code === 30010) {
|
||||
errors.push(`Maximum number of reactions reached (20)`);
|
||||
logs.logBotAlert({
|
||||
body: `Error ${e.code} while applying reaction role reactions to ${channelId}/${messageId}: ${e.message}`,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import { CustomEventsPlugin } from "./CustomEvents/CustomEventsPlugin";
|
|||
import { GuildAccessMonitorPlugin } from "./GuildAccessMonitor/GuildAccessMonitorPlugin";
|
||||
import { GuildConfigReloaderPlugin } from "./GuildConfigReloader/GuildConfigReloaderPlugin";
|
||||
import { GuildInfoSaverPlugin } from "./GuildInfoSaver/GuildInfoSaverPlugin";
|
||||
import { GuildMemberCachePlugin } from "./GuildMemberCache/GuildMemberCachePlugin";
|
||||
import { InternalPosterPlugin } from "./InternalPoster/InternalPosterPlugin";
|
||||
import { LocateUserPlugin } from "./LocateUser/LocateUserPlugin";
|
||||
import { LogsPlugin } from "./Logs/LogsPlugin";
|
||||
|
@ -54,7 +53,7 @@ export const guildPlugins: Array<ZeppelinGuildPluginBlueprint<any>> = [
|
|||
PostPlugin,
|
||||
ReactionRolesPlugin,
|
||||
MessageSaverPlugin,
|
||||
GuildMemberCachePlugin,
|
||||
// GuildMemberCachePlugin, // FIXME: New caching thing, or fix deadlocks with this plugin
|
||||
ModActionsPlugin,
|
||||
NameHistoryPlugin,
|
||||
RemindersPlugin,
|
||||
|
@ -93,7 +92,7 @@ export const baseGuildPlugins: Array<ZeppelinGuildPluginBlueprint<any>> = [
|
|||
GuildInfoSaverPlugin,
|
||||
MessageSaverPlugin,
|
||||
NameHistoryPlugin,
|
||||
GuildMemberCachePlugin,
|
||||
// GuildMemberCachePlugin, // FIXME: New caching thing, or fix deadlocks with this plugin
|
||||
CasesPlugin,
|
||||
MutesPlugin,
|
||||
TimeAndDatePlugin,
|
||||
|
|
|
@ -135,7 +135,7 @@ export interface TDeepPartialProps<P extends t.Props>
|
|||
{
|
||||
[K in keyof P]?: TDeepPartial<t.OutputOf<P[K]>>;
|
||||
}
|
||||
> { }
|
||||
> {}
|
||||
|
||||
export function tDeepPartial<T>(type: T): TDeepPartial<T> {
|
||||
if (type instanceof t.InterfaceType || type instanceof t.PartialType) {
|
||||
|
@ -220,7 +220,7 @@ export interface PartialDictionaryC<D extends t.Mixed, C extends t.Mixed>
|
|||
[K in t.OutputOf<D>]?: t.OutputOf<C>;
|
||||
},
|
||||
unknown
|
||||
> { }
|
||||
> {}
|
||||
|
||||
export const tPartialDictionary = <D extends t.Mixed, C extends t.Mixed>(
|
||||
domain: D,
|
||||
|
@ -652,7 +652,13 @@ export function getUrlsInString(str: string, onlyUnique = false): MatchedURL[] {
|
|||
return urls;
|
||||
}
|
||||
|
||||
const hostnameParts = matchUrl.hostname.split(".");
|
||||
let hostname = matchUrl.hostname.toLowerCase();
|
||||
|
||||
if (hostname.length > 3) {
|
||||
hostname = hostname.replace(/[^a-z]+$/, "");
|
||||
}
|
||||
|
||||
const hostnameParts = hostname.split(".");
|
||||
const tld = hostnameParts[hostnameParts.length - 1];
|
||||
if (tlds.includes(tld)) {
|
||||
urls.push(matchUrl);
|
||||
|
@ -1617,4 +1623,4 @@ export function renderUsername(username: string, discriminator: string): string
|
|||
|
||||
export function renderUserUsername(user: User | UnknownUser): string {
|
||||
return renderUsername(user.username, user.discriminator);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue