2021-10-04 19:27:00 +03:00
|
|
|
import { Client, Constants, Intents, Options, TextChannel, ThreadChannel } from "discord.js";
|
2020-07-22 22:56:21 +03:00
|
|
|
import { Knub, PluginError } from "knub";
|
2021-06-06 23:51:32 +02:00
|
|
|
import { PluginLoadError } from "knub/dist/plugins/PluginLoadError";
|
2020-07-22 22:56:21 +03:00
|
|
|
// Always use UTC internally
|
|
|
|
// This is also enforced for the database in data/db.ts
|
|
|
|
import moment from "moment-timezone";
|
|
|
|
import { AllowedGuilds } from "./data/AllowedGuilds";
|
2021-06-06 23:51:32 +02:00
|
|
|
import { Configs } from "./data/Configs";
|
|
|
|
import { connect } from "./data/db";
|
2020-07-22 22:56:21 +03:00
|
|
|
import { GuildLogs } from "./data/GuildLogs";
|
|
|
|
import { LogType } from "./data/LogType";
|
2021-07-28 16:43:31 +01:00
|
|
|
import { DiscordJSError } from "./DiscordJSError";
|
2021-06-06 23:51:32 +02:00
|
|
|
import "./loadEnv";
|
|
|
|
import { logger } from "./logger";
|
|
|
|
import { baseGuildPlugins, globalPlugins, guildPlugins } from "./plugins/availablePlugins";
|
|
|
|
import { RecoverablePluginError } from "./RecoverablePluginError";
|
|
|
|
import { SimpleError } from "./SimpleError";
|
|
|
|
import { ZeppelinGlobalConfig, ZeppelinGuildConfig } from "./types";
|
|
|
|
import { startUptimeCounter } from "./uptime";
|
2021-11-02 20:54:05 +02:00
|
|
|
import { errorMessage, isDiscordAPIError, isDiscordHTTPError, MINUTES, SECONDS, sleep, successMessage } from "./utils";
|
2021-08-14 18:22:29 +03:00
|
|
|
import { loadYamlSafely } from "./utils/loadYamlSafely";
|
2021-08-18 23:57:44 +03:00
|
|
|
import { DecayingCounter } from "./utils/DecayingCounter";
|
2021-08-20 22:13:02 +03:00
|
|
|
import { PluginNotLoadedError } from "knub/dist/plugins/PluginNotLoadedError";
|
2021-09-11 21:14:47 +03:00
|
|
|
import { logRestCall } from "./restCallStats";
|
2021-09-12 00:17:26 +03:00
|
|
|
import { logRateLimit } from "./rateLimitStats";
|
2021-09-25 21:33:59 +03:00
|
|
|
import { runExpiringMutesLoop } from "./data/loops/expiringMutesLoop";
|
|
|
|
import { runUpcomingRemindersLoop } from "./data/loops/upcomingRemindersLoop";
|
|
|
|
import { runUpcomingScheduledPostsLoop } from "./data/loops/upcomingScheduledPostsLoop";
|
|
|
|
import { runExpiringTempbansLoop } from "./data/loops/expiringTempbansLoop";
|
|
|
|
import { runExpiringVCAlertsLoop } from "./data/loops/expiringVCAlertsLoop";
|
2021-09-25 21:53:48 +03:00
|
|
|
import { runExpiredArchiveDeletionLoop } from "./data/loops/expiredArchiveDeletionLoop";
|
2021-10-09 12:18:19 +03:00
|
|
|
import { runSavedMessageCleanupLoop } from "./data/loops/savedMessageCleanupLoop";
|
2021-10-25 22:39:20 +03:00
|
|
|
import { performance } from "perf_hooks";
|
2021-10-26 22:40:31 +03:00
|
|
|
import { setProfiler } from "./profiler";
|
2021-10-28 17:11:56 +03:00
|
|
|
import { enableProfiling } from "./utils/easyProfiler";
|
2021-10-31 17:17:31 +02:00
|
|
|
import { runPhishermanCacheCleanupLoop, runPhishermanReportingLoop } from "./data/loops/phishermanLoops";
|
|
|
|
import { hasPhishermanMasterAPIKey } from "./data/Phisherman";
|
2021-11-02 20:54:05 +02:00
|
|
|
import { consumeQueryStats } from "./data/queryLogger";
|
2021-06-06 23:51:32 +02:00
|
|
|
|
2020-09-16 22:32:43 +03:00
|
|
|
if (!process.env.KEY) {
|
|
|
|
// tslint:disable-next-line:no-console
|
|
|
|
console.error("Project root .env with KEY is required!");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2018-07-01 03:35:51 +03:00
|
|
|
|
2019-05-07 19:54:16 +03:00
|
|
|
// Error handling
|
2019-01-15 01:05:12 +02:00
|
|
|
let recentPluginErrors = 0;
|
2019-04-23 05:29:53 +03:00
|
|
|
const RECENT_PLUGIN_ERROR_EXIT_THRESHOLD = 5;
|
2019-01-13 16:52:00 +02:00
|
|
|
|
2019-04-23 05:29:53 +03:00
|
|
|
let recentDiscordErrors = 0;
|
|
|
|
const RECENT_DISCORD_ERROR_EXIT_THRESHOLD = 5;
|
2019-01-15 01:05:12 +02:00
|
|
|
|
2020-12-18 05:08:51 +02:00
|
|
|
setInterval(() => (recentPluginErrors = Math.max(0, recentPluginErrors - 1)), 2000);
|
|
|
|
setInterval(() => (recentDiscordErrors = Math.max(0, recentDiscordErrors - 1)), 2000);
|
2018-07-01 03:35:51 +03:00
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
// Eris handles these internally, so we don't need to panic if we get one of them
|
|
|
|
const SAFE_TO_IGNORE_ERIS_ERROR_CODES = [
|
|
|
|
1001, // "CloudFlare WebSocket proxy restarting"
|
|
|
|
1006, // "Connection reset by peer"
|
|
|
|
"ECONNRESET", // Pretty much the same as above
|
|
|
|
];
|
2020-09-13 22:46:59 +03:00
|
|
|
|
2020-12-19 03:07:41 +02:00
|
|
|
const SAFE_TO_IGNORE_ERIS_ERROR_MESSAGES = ["Server didn't acknowledge previous heartbeat, possible lost connection"];
|
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
function errorHandler(err) {
|
|
|
|
const guildName = err.guild?.name || "Global";
|
|
|
|
const guildId = err.guild?.id || "0";
|
2020-01-29 02:44:11 +02:00
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
if (err instanceof RecoverablePluginError) {
|
|
|
|
// Recoverable plugin errors can be, well, recovered from.
|
|
|
|
// Log it in the console as a warning and post a warning to the guild's log.
|
2020-01-29 02:44:11 +02:00
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
// tslint:disable:no-console
|
2021-10-17 19:19:53 +03:00
|
|
|
console.warn(`${guildId} ${guildName}: [${err.code}] ${err.message}`);
|
2020-01-29 02:44:11 +02:00
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
if (err.guild) {
|
|
|
|
const logs = new GuildLogs(err.guild.id);
|
|
|
|
logs.log(LogType.BOT_ALERT, { body: `\`[${err.code}]\` ${err.message}` });
|
2020-01-29 02:44:11 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err instanceof PluginLoadError) {
|
|
|
|
// tslint:disable:no-console
|
|
|
|
console.warn(`${guildName} (${guildId}): Failed to load plugin '${err.pluginName}': ${err.message}`);
|
|
|
|
return;
|
|
|
|
}
|
2020-07-30 20:40:00 +03:00
|
|
|
|
2021-07-28 16:43:31 +01:00
|
|
|
if (err instanceof DiscordJSError) {
|
2020-12-18 05:24:07 +02:00
|
|
|
if (err.code && SAFE_TO_IGNORE_ERIS_ERROR_CODES.includes(err.code)) {
|
2020-12-17 03:51:59 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-12-19 03:07:41 +02:00
|
|
|
|
|
|
|
if (err.message && SAFE_TO_IGNORE_ERIS_ERROR_MESSAGES.includes(err.message)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-12-18 05:24:07 +02:00
|
|
|
}
|
|
|
|
|
2021-05-31 03:30:55 +02:00
|
|
|
if (isDiscordHTTPError(err) && err.code >= 500) {
|
2020-12-18 05:24:07 +02:00
|
|
|
// Don't need stack traces on HTTP 500 errors
|
|
|
|
// These also shouldn't count towards RECENT_DISCORD_ERROR_EXIT_THRESHOLD because they don't indicate an error in our code
|
|
|
|
console.error(err.message);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-19 03:09:24 +02:00
|
|
|
if (err.message && err.message.startsWith("Request timed out")) {
|
|
|
|
// These are very noisy, so just print the message without stack. The stack trace doesn't really help here anyway.
|
|
|
|
console.error(err.message);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-18 22:24:23 +03:00
|
|
|
// FIXME: Hotfix
|
|
|
|
if (err.message && err.message.startsWith("Unknown custom override criteria")) {
|
2021-08-20 21:40:07 +03:00
|
|
|
// console.warn(err.message);
|
2021-08-18 22:24:23 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-18 22:35:51 +03:00
|
|
|
// FIXME: Hotfix
|
|
|
|
if (err.message && err.message.startsWith("Unknown override criteria")) {
|
2021-08-20 21:40:07 +03:00
|
|
|
// console.warn(err.message);
|
2021-08-18 22:35:51 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-20 22:13:02 +03:00
|
|
|
if (err instanceof PluginNotLoadedError) {
|
|
|
|
// We don't want to crash the bot here, although this *should not happen*
|
|
|
|
// TODO: Proper system for preventing plugin load/unload race conditions
|
|
|
|
console.error(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
// tslint:disable:no-console
|
|
|
|
console.error(err);
|
|
|
|
|
|
|
|
if (err instanceof PluginError) {
|
|
|
|
// Tolerate a few recent plugin errors before crashing
|
|
|
|
if (++recentPluginErrors >= RECENT_PLUGIN_ERROR_EXIT_THRESHOLD) {
|
|
|
|
console.error(`Exiting after ${RECENT_PLUGIN_ERROR_EXIT_THRESHOLD} plugin errors`);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2021-06-30 23:06:02 +02:00
|
|
|
} else if (isDiscordAPIError(err) || isDiscordHTTPError(err)) {
|
2020-12-18 05:24:07 +02:00
|
|
|
// Discord API errors, usually safe to just log instead of crash
|
|
|
|
// We still bail if we get a ton of them in a short amount of time
|
|
|
|
if (++recentDiscordErrors >= RECENT_DISCORD_ERROR_EXIT_THRESHOLD) {
|
|
|
|
console.error(`Exiting after ${RECENT_DISCORD_ERROR_EXIT_THRESHOLD} API errors`);
|
2019-04-23 05:38:48 +03:00
|
|
|
process.exit(1);
|
|
|
|
}
|
2020-12-18 05:24:07 +02:00
|
|
|
} else {
|
|
|
|
// On other errors, crash immediately
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
// tslint:enable:no-console
|
|
|
|
}
|
2019-04-23 05:29:53 +03:00
|
|
|
|
2020-12-18 05:24:07 +02:00
|
|
|
if (process.env.NODE_ENV === "production") {
|
2020-01-12 10:28:38 +02:00
|
|
|
process.on("uncaughtException", errorHandler);
|
2020-12-18 05:07:55 +02:00
|
|
|
process.on("unhandledRejection", errorHandler);
|
2020-01-12 10:28:38 +02:00
|
|
|
}
|
2018-08-03 19:25:00 +03:00
|
|
|
|
2018-12-14 06:27:41 +02:00
|
|
|
// Verify required Node.js version
|
2020-07-05 05:00:54 +03:00
|
|
|
const REQUIRED_NODE_VERSION = "14.0.0";
|
2021-09-11 19:06:51 +03:00
|
|
|
const requiredParts = REQUIRED_NODE_VERSION.split(".").map((v) => parseInt(v, 10));
|
|
|
|
const actualVersionParts = process.versions.node.split(".").map((v) => parseInt(v, 10));
|
2018-12-14 06:27:41 +02:00
|
|
|
for (const [i, part] of actualVersionParts.entries()) {
|
2019-01-03 03:47:52 +02:00
|
|
|
if (part > requiredParts[i]) break;
|
|
|
|
if (part === requiredParts[i]) continue;
|
|
|
|
throw new SimpleError(`Unsupported Node.js version! Must be at least ${REQUIRED_NODE_VERSION}`);
|
2018-12-14 06:27:41 +02:00
|
|
|
}
|
|
|
|
|
2018-07-07 15:39:56 +03:00
|
|
|
moment.tz.setDefault("UTC");
|
|
|
|
|
2021-10-25 22:39:20 +03:00
|
|
|
// Blocking check
|
|
|
|
let avgTotal = 0;
|
|
|
|
let avgCount = 0;
|
|
|
|
let lastCheck = performance.now();
|
|
|
|
setInterval(() => {
|
|
|
|
const now = performance.now();
|
|
|
|
let diff = Math.max(0, now - lastCheck);
|
|
|
|
if (diff < 5) diff = 0;
|
|
|
|
avgTotal += diff;
|
|
|
|
avgCount++;
|
|
|
|
lastCheck = now;
|
|
|
|
}, 500);
|
|
|
|
setInterval(() => {
|
|
|
|
const avgBlocking = avgTotal / (avgCount || 1);
|
|
|
|
console.log(`Average blocking in the last 5min: ${avgBlocking / avgTotal}ms`);
|
|
|
|
avgTotal = 0;
|
|
|
|
avgCount = 0;
|
|
|
|
}, 5 * 60 * 1000);
|
|
|
|
|
2019-05-25 21:23:09 +03:00
|
|
|
logger.info("Connecting to database");
|
2020-06-30 17:48:18 +03:00
|
|
|
connect().then(async () => {
|
2021-05-31 03:30:55 +02:00
|
|
|
const client = new Client({
|
|
|
|
partials: ["USER", "CHANNEL", "GUILD_MEMBER", "MESSAGE", "REACTION"],
|
2021-08-18 23:58:25 +03:00
|
|
|
|
2021-10-04 19:27:00 +03:00
|
|
|
makeCache: Options.cacheWithLimits({
|
|
|
|
...Options.defaultMakeCacheSettings,
|
2021-10-26 13:15:04 +03:00
|
|
|
MessageManager: 1,
|
2021-10-17 21:02:44 +03:00
|
|
|
// GuildMemberManager: 15000,
|
2021-10-04 19:27:00 +03:00
|
|
|
GuildInviteManager: 0,
|
|
|
|
}),
|
|
|
|
|
2021-08-19 21:15:31 +03:00
|
|
|
restGlobalRateLimit: 50,
|
|
|
|
// restTimeOffset: 1000,
|
2021-08-18 23:58:25 +03:00
|
|
|
|
2021-04-10 23:37:55 +03:00
|
|
|
// Disable mentions by default
|
|
|
|
allowedMentions: {
|
2021-05-31 03:30:55 +02:00
|
|
|
parse: [],
|
|
|
|
users: [],
|
|
|
|
roles: [],
|
2021-04-10 23:37:55 +03:00
|
|
|
repliedUser: false,
|
|
|
|
},
|
2020-07-30 22:10:17 +03:00
|
|
|
intents: [
|
|
|
|
// Privileged
|
2021-05-31 03:30:55 +02:00
|
|
|
Intents.FLAGS.GUILD_MEMBERS,
|
2021-06-06 02:41:06 +02:00
|
|
|
// Intents.FLAGS.GUILD_PRESENCES,
|
2021-05-31 03:30:55 +02:00
|
|
|
Intents.FLAGS.GUILD_MESSAGE_TYPING,
|
2020-07-30 22:10:17 +03:00
|
|
|
|
|
|
|
// Regular
|
2021-05-31 03:30:55 +02:00
|
|
|
Intents.FLAGS.DIRECT_MESSAGES,
|
|
|
|
Intents.FLAGS.GUILD_BANS,
|
2021-07-21 22:14:09 +02:00
|
|
|
Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS,
|
2021-05-31 03:30:55 +02:00
|
|
|
Intents.FLAGS.GUILD_INVITES,
|
|
|
|
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
|
|
|
|
Intents.FLAGS.GUILD_MESSAGES,
|
|
|
|
Intents.FLAGS.GUILDS,
|
|
|
|
Intents.FLAGS.GUILD_VOICE_STATES,
|
2020-07-30 22:10:17 +03:00
|
|
|
],
|
2018-07-31 20:58:48 +03:00
|
|
|
});
|
2020-07-30 13:51:36 +03:00
|
|
|
client.setMaxListeners(200);
|
2018-07-01 03:35:51 +03:00
|
|
|
|
2021-09-11 19:06:51 +03:00
|
|
|
client.on(Constants.Events.RATE_LIMIT, (data) => {
|
2021-08-19 00:00:16 +03:00
|
|
|
// tslint:disable-next-line:no-console
|
2021-08-19 01:44:06 +03:00
|
|
|
// console.log(`[DEBUG] [RATE_LIMIT] ${JSON.stringify(data)}`);
|
2021-08-19 00:00:16 +03:00
|
|
|
});
|
|
|
|
|
2021-08-18 23:57:44 +03:00
|
|
|
const safe429DecayInterval = 5 * SECONDS;
|
|
|
|
const safe429MaxCount = 5;
|
|
|
|
const safe429Counter = new DecayingCounter(safe429DecayInterval);
|
2021-09-11 19:06:51 +03:00
|
|
|
client.on(Constants.Events.DEBUG, (errorText) => {
|
2021-08-19 00:01:29 +03:00
|
|
|
if (!errorText.includes("429")) {
|
2021-08-18 23:20:31 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// tslint:disable-next-line:no-console
|
2021-08-19 00:00:16 +03:00
|
|
|
console.warn(`[DEBUG] [WARN] [429] ${errorText}`);
|
2021-08-18 23:57:44 +03:00
|
|
|
|
|
|
|
const value = safe429Counter.add(1);
|
|
|
|
if (value > safe429MaxCount) {
|
|
|
|
// tslint:disable-next-line:no-console
|
|
|
|
console.error(`Too many 429s (over ${safe429MaxCount} in ${safe429MaxCount * safe429DecayInterval}ms), exiting`);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2019-02-07 20:44:26 +02:00
|
|
|
});
|
|
|
|
|
2021-09-11 19:06:51 +03:00
|
|
|
client.on("error", (err) => {
|
2021-07-28 16:43:31 +01:00
|
|
|
errorHandler(new DiscordJSError(err.message, (err as any).code, 0));
|
2020-10-13 19:49:31 +03:00
|
|
|
});
|
|
|
|
|
2019-06-22 18:52:24 +03:00
|
|
|
const allowedGuilds = new AllowedGuilds();
|
|
|
|
const guildConfigs = new Configs();
|
|
|
|
|
2020-08-10 00:24:06 +03:00
|
|
|
const bot = new Knub<ZeppelinGuildConfig, ZeppelinGlobalConfig>(client, {
|
2020-07-06 01:53:58 +03:00
|
|
|
guildPlugins,
|
|
|
|
globalPlugins,
|
2018-07-31 19:00:17 +03:00
|
|
|
|
|
|
|
options: {
|
2019-06-22 18:52:24 +03:00
|
|
|
canLoadGuild(guildId): Promise<boolean> {
|
|
|
|
return allowedGuilds.isAllowed(guildId);
|
|
|
|
},
|
|
|
|
|
2019-05-07 19:54:16 +03:00
|
|
|
/**
|
|
|
|
* Plugins are enabled if they...
|
|
|
|
* - are base plugins, i.e. always enabled, or
|
|
|
|
* - are explicitly enabled in the guild config
|
2020-07-05 05:00:54 +03:00
|
|
|
* Dependencies are also automatically loaded by Knub.
|
2019-05-07 19:54:16 +03:00
|
|
|
*/
|
2020-07-30 22:00:08 +03:00
|
|
|
async getEnabledGuildPlugins(ctx, plugins): Promise<string[]> {
|
2020-08-28 02:01:35 +03:00
|
|
|
if (!ctx.config || !ctx.config.plugins) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
const configuredPlugins = ctx.config.plugins;
|
2021-09-11 19:06:51 +03:00
|
|
|
const basePluginNames = baseGuildPlugins.map((p) => p.name);
|
2019-04-20 17:36:28 +03:00
|
|
|
|
2021-09-11 19:06:51 +03:00
|
|
|
return Array.from(plugins.keys()).filter((pluginName) => {
|
2020-07-30 22:23:18 +03:00
|
|
|
if (basePluginNames.includes(pluginName)) return true;
|
2019-04-20 17:36:28 +03:00
|
|
|
return configuredPlugins[pluginName] && configuredPlugins[pluginName].enabled !== false;
|
2018-07-31 19:00:17 +03:00
|
|
|
});
|
2018-10-26 06:41:20 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
async getConfig(id) {
|
2019-06-22 18:52:24 +03:00
|
|
|
const key = id === "global" ? "global" : `guild-${id}`;
|
2021-09-05 17:07:50 +03:00
|
|
|
if (id !== "global") {
|
|
|
|
const allowedGuild = await allowedGuilds.find(id);
|
|
|
|
if (!allowedGuild) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 18:52:24 +03:00
|
|
|
const row = await guildConfigs.getActiveByKey(key);
|
|
|
|
if (row) {
|
2021-08-14 18:22:29 +03:00
|
|
|
try {
|
|
|
|
return loadYamlSafely(row.config);
|
|
|
|
} catch (err) {
|
|
|
|
logger.error(`Error while loading config "${key}": ${err.message}`);
|
|
|
|
return {};
|
|
|
|
}
|
2018-10-26 06:41:20 +03:00
|
|
|
}
|
|
|
|
|
2019-06-22 18:52:24 +03:00
|
|
|
logger.warn(`No config with key "${key}"`);
|
|
|
|
return {};
|
2018-10-26 06:41:20 +03:00
|
|
|
},
|
|
|
|
|
2019-05-04 10:58:57 +03:00
|
|
|
logFn: (level, msg) => {
|
|
|
|
if (level === "debug") return;
|
2020-07-22 22:56:21 +03:00
|
|
|
|
|
|
|
if (logger[level]) {
|
|
|
|
logger[level](msg);
|
|
|
|
} else {
|
|
|
|
logger.log(`[${level.toUpperCase()}] ${msg}`);
|
|
|
|
}
|
2019-05-04 10:58:57 +03:00
|
|
|
},
|
|
|
|
|
2019-02-08 21:04:04 +02:00
|
|
|
performanceDebug: {
|
2019-05-04 10:52:59 +03:00
|
|
|
enabled: false,
|
2019-02-08 21:04:04 +02:00
|
|
|
size: 30,
|
2019-02-19 00:02:46 +02:00
|
|
|
threshold: 200,
|
|
|
|
},
|
2019-04-05 20:05:37 +03:00
|
|
|
|
2020-01-12 22:19:10 +11:00
|
|
|
sendSuccessMessageFn(channel, body) {
|
2021-08-18 20:47:24 +03:00
|
|
|
const guildId =
|
|
|
|
channel instanceof TextChannel || channel instanceof ThreadChannel ? channel.guild.id : undefined;
|
2020-11-09 20:03:57 +02:00
|
|
|
const emoji = guildId ? bot.getLoadedGuild(guildId)!.config.success_emoji : undefined;
|
2021-05-31 03:30:55 +02:00
|
|
|
channel.send(successMessage(body, emoji));
|
2019-04-19 12:25:25 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
sendErrorMessageFn(channel, body) {
|
2021-08-18 20:47:24 +03:00
|
|
|
const guildId =
|
|
|
|
channel instanceof TextChannel || channel instanceof ThreadChannel ? channel.guild.id : undefined;
|
2020-11-09 20:03:57 +02:00
|
|
|
const emoji = guildId ? bot.getLoadedGuild(guildId)!.config.error_emoji : undefined;
|
2021-05-31 03:30:55 +02:00
|
|
|
channel.send(errorMessage(body, emoji));
|
2019-04-19 12:25:25 +03:00
|
|
|
},
|
2019-02-19 00:02:46 +02:00
|
|
|
},
|
2018-07-01 03:35:51 +03:00
|
|
|
});
|
|
|
|
|
2019-04-23 05:58:50 +03:00
|
|
|
client.once("ready", () => {
|
|
|
|
startUptimeCounter();
|
|
|
|
});
|
|
|
|
|
2021-09-12 00:17:26 +03:00
|
|
|
client.on(Constants.Events.RATE_LIMIT, (data) => {
|
|
|
|
logRateLimit(data);
|
|
|
|
});
|
|
|
|
|
2021-09-25 21:53:48 +03:00
|
|
|
bot.on("loadingFinished", async () => {
|
2021-09-25 21:33:59 +03:00
|
|
|
runExpiringMutesLoop();
|
2021-09-25 21:53:48 +03:00
|
|
|
await sleep(10 * SECONDS);
|
2021-09-25 21:33:59 +03:00
|
|
|
runExpiringTempbansLoop();
|
2021-09-25 21:53:48 +03:00
|
|
|
await sleep(10 * SECONDS);
|
2021-09-25 21:33:59 +03:00
|
|
|
runUpcomingScheduledPostsLoop();
|
2021-09-25 21:53:48 +03:00
|
|
|
await sleep(10 * SECONDS);
|
|
|
|
runUpcomingRemindersLoop();
|
|
|
|
await sleep(10 * SECONDS);
|
|
|
|
runExpiringVCAlertsLoop();
|
|
|
|
await sleep(10 * SECONDS);
|
|
|
|
runExpiredArchiveDeletionLoop();
|
2021-10-09 12:18:19 +03:00
|
|
|
await sleep(10 * SECONDS);
|
|
|
|
runSavedMessageCleanupLoop();
|
2021-10-31 17:17:31 +02:00
|
|
|
|
|
|
|
if (hasPhishermanMasterAPIKey()) {
|
|
|
|
await sleep(10 * SECONDS);
|
|
|
|
runPhishermanCacheCleanupLoop();
|
|
|
|
await sleep(10 * SECONDS);
|
|
|
|
runPhishermanReportingLoop();
|
|
|
|
}
|
2021-09-25 21:33:59 +03:00
|
|
|
});
|
|
|
|
|
2021-10-26 22:40:31 +03:00
|
|
|
setProfiler(bot.profiler);
|
2021-10-28 17:11:56 +03:00
|
|
|
if (process.env.PROFILING === "true") {
|
|
|
|
enableProfiling();
|
|
|
|
}
|
2021-10-26 22:40:31 +03:00
|
|
|
|
2021-10-28 17:19:07 +03:00
|
|
|
let lowestGlobalRemaining = Infinity;
|
|
|
|
setInterval(() => {
|
|
|
|
lowestGlobalRemaining = Math.min(lowestGlobalRemaining, (client as any).rest.globalRemaining);
|
|
|
|
}, 100);
|
|
|
|
setInterval(() => {
|
2021-11-02 20:14:19 +02:00
|
|
|
console.log("Lowest global remaining in the past 15 seconds:", lowestGlobalRemaining);
|
2021-10-28 17:19:07 +03:00
|
|
|
lowestGlobalRemaining = Infinity;
|
2021-11-02 20:14:19 +02:00
|
|
|
}, 15000);
|
2021-10-28 17:19:07 +03:00
|
|
|
|
2021-11-02 20:54:05 +02:00
|
|
|
setInterval(() => {
|
|
|
|
const queryStatsMap = consumeQueryStats();
|
|
|
|
const entries = Array.from(queryStatsMap.entries());
|
|
|
|
entries.sort((a, b) => b[1] - a[1]);
|
|
|
|
const topEntriesStr = entries
|
|
|
|
.slice(0, 5)
|
|
|
|
.map(([key, count]) => `${count}x ${key}`)
|
|
|
|
.join("\n");
|
|
|
|
console.log(`Top query entries in the past 5 minutes:\n${topEntriesStr}`);
|
|
|
|
}, 5 * MINUTES);
|
|
|
|
|
2021-07-04 17:41:44 +02:00
|
|
|
bot.initialize();
|
|
|
|
logger.info("Bot Initialized");
|
2021-06-02 19:35:44 +02:00
|
|
|
logger.info("Logging in...");
|
2021-07-28 16:43:31 +01:00
|
|
|
await client.login(process.env.TOKEN);
|
2018-07-01 03:35:51 +03:00
|
|
|
});
|