3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-10 04:25:01 +00:00

refactor: debounce and batch simultaneous audit log requests

This commit is contained in:
Dragory 2021-10-27 00:09:29 +03:00
parent d7c6e34695
commit 8f17a835f9
No known key found for this signature in database
GPG key ID: 5F387BA66DF8AAC1
8 changed files with 77 additions and 117 deletions

View file

@ -0,0 +1,68 @@
import { Guild, GuildAuditLogsAction, GuildAuditLogsEntry } from "discord.js";
import { SECONDS, sleep } from "../utils";
const BATCH_DEBOUNCE_TIME = 2 * SECONDS;
const BATCH_FETCH_COUNT_INCREMENT = 10;
type Batch = {
_waitUntil: number;
_fetchCount: number;
_promise: Promise<GuildAuditLogsEntry[]>;
join: () => Promise<GuildAuditLogsEntry[]>;
};
const batches = new Map<string, Batch>();
/**
* Find a recent audit log entry matching the given criteria.
* This function will debounce and batch simultaneous calls into one audit log request.
*/
export async function findMatchingAuditLogEntry(
guild: Guild,
action?: GuildAuditLogsAction,
targetId?: string,
): Promise<GuildAuditLogsEntry | undefined> {
let candidates: GuildAuditLogsEntry[];
if (batches.has(guild.id)) {
candidates = await batches.get(guild.id)!.join();
} else {
const batch: Batch = {
_waitUntil: Date.now(),
_fetchCount: 0,
_promise: new Promise(async (resolve) => {
await sleep(BATCH_DEBOUNCE_TIME);
do {
await sleep(Math.max(0, batch._waitUntil - Date.now()));
} while (Date.now() < batch._waitUntil);
const result = await guild
.fetchAuditLogs({
limit: batch._fetchCount,
})
.catch((err) => {
// tslint:disable-next-line:no-console
console.warn(`[DEBUG] Audit log error in ${guild.id} (${guild.name}): ${err.message}`);
return null;
});
const _candidates = Array.from(result?.entries.values() ?? []);
batches.delete(guild.id);
resolve(_candidates);
}),
join() {
batch._waitUntil = Date.now() + BATCH_DEBOUNCE_TIME;
batch._fetchCount = Math.min(100, batch._fetchCount + BATCH_FETCH_COUNT_INCREMENT);
return batch._promise;
},
};
batches.set(guild.id, batch);
candidates = await batch.join();
}
return candidates.find(
(entry) =>
(action == null || entry.action === action) && (targetId == null || (entry.target as any)?.id === targetId),
);
}

View file

@ -1,30 +0,0 @@
import { GuildPluginData } from "knub";
import { LogType } from "../data/LogType";
import { LogsPlugin } from "../plugins/Logs/LogsPlugin";
import { findRelevantAuditLogEntry, isDiscordAPIError } from "../utils";
/**
* Wrapper for findRelevantAuditLogEntry() that handles permission errors gracefully.
* Calling plugin must have LogsPlugin as a dependency (or be LogsPlugin itself).
*/
export async function safeFindRelevantAuditLogEntry(
pluginData: GuildPluginData<any>,
actionType: number,
userId: string,
attempts?: number,
attemptDelay?: number,
) {
try {
return await findRelevantAuditLogEntry(pluginData.guild, actionType, userId, attempts, attemptDelay);
} catch (e) {
if (isDiscordAPIError(e) && e.code === 50013) {
const logs = pluginData.getPlugin(LogsPlugin);
logs.logBotAlert({
body: "Missing permissions to read audit log",
});
return;
}
throw e;
}
}