|
@@ -2,52 +2,25 @@
|
|
|
require("module-alias/register");
|
|
|
|
|
|
import "./environment";
|
|
|
-import * as fs from "fs";
|
|
|
import * as path from "path";
|
|
|
import { client } from "./client";
|
|
|
import * as mCmd from "./model/command";
|
|
|
-import "reflect-metadata";
|
|
|
import { createConnection, getConnectionOptions, getRepository } from "typeorm";
|
|
|
-import { getNumberEnums, formatString } from "./util";
|
|
|
+import { formatString, assertOk } from "./util";
|
|
|
import { DB_ENTITIES } from "@shared/db/entities";
|
|
|
import { logger } from "./logging";
|
|
|
import { GuildGreeting } from "@shared/db/entity/GuildGreeting";
|
|
|
import { TextChannel, GuildMember, PartialGuildMember } from "discord.js";
|
|
|
+import { CommandManager } from "./command_manager";
|
|
|
|
|
|
-const REACT_PROBABILITY = 0.3;
|
|
|
-
|
|
|
-async function trigger(type: mCmd.ActionType, ...params: unknown[]) {
|
|
|
- let actionDone = false;
|
|
|
- const actions = botEvents[type];
|
|
|
- for (let i = 0; i < actions.length; i++) {
|
|
|
- const action = actions[i] as (...args: unknown[]) => boolean | Promise<boolean>;
|
|
|
- const actionResult = action(actionDone, ...params);
|
|
|
- if (actionResult instanceof Promise)
|
|
|
- actionDone = (await actionResult) || actionDone;
|
|
|
- else
|
|
|
- actionDone = actionResult || actionDone;
|
|
|
- }
|
|
|
- return actionDone;
|
|
|
-}
|
|
|
+export const cmdMgr: CommandManager = new CommandManager(path.resolve(path.dirname(module.filename), "commands"));
|
|
|
|
|
|
-type BotEventCollection = { [event in mCmd.ActionType]: mCmd.BotAction[] };
|
|
|
-
|
|
|
-const commandSets: mCmd.ICommand[] = [];
|
|
|
-const botCommands: mCmd.IBotCommand[] = [];
|
|
|
-const botEvents: BotEventCollection = getNumberEnums(mCmd.ActionType).reduce((p, c) => { p[c as mCmd.ActionType] = []; return p; }, {} as BotEventCollection);
|
|
|
-const startActions: Array<() => void | Promise<void>> = [];
|
|
|
+const REACT_PROBABILITY = 0.3;
|
|
|
|
|
|
client.bot.on("ready", async () => {
|
|
|
logger.info("Starting up NoctBot");
|
|
|
- client.botUser.setActivity(process.env.NODE_ENV == "dev" ? "Maintenance" : "@NoctBot help", {
|
|
|
- type: "PLAYING"
|
|
|
- });
|
|
|
- for (let i = 0; i < startActions.length; i++) {
|
|
|
- const action = startActions[i];
|
|
|
- const val = action();
|
|
|
- if (val instanceof Promise)
|
|
|
- await val;
|
|
|
- }
|
|
|
+ await client.botUser.setActivity(`@${client.botUser.username} help`, { type: "PLAYING" });
|
|
|
+ await assertOk(cmdMgr.onStart());
|
|
|
logger.info("NoctBot is ready");
|
|
|
});
|
|
|
|
|
@@ -69,38 +42,27 @@ client.bot.on("message", async m => {
|
|
|
|
|
|
let content = m.cleanContent.trim();
|
|
|
|
|
|
- if (await trigger(mCmd.ActionType.MESSAGE, m, content))
|
|
|
+ if (await cmdMgr.trigger(mCmd.ActionType.MESSAGE, m, content))
|
|
|
return;
|
|
|
|
|
|
if (m.mentions.users.size > 0 && m.mentions.users.has(client.botUser.id)) {
|
|
|
const trimmedContent = m.content.trim();
|
|
|
if (trimmedContent.startsWith(client.nameMention) || trimmedContent.startsWith(client.usernameMention)) {
|
|
|
content = content.substring(`@${client.botUser.username}`.length).trim();
|
|
|
-
|
|
|
const lowerCaseContent = content.toLowerCase();
|
|
|
- for (const c of botCommands) {
|
|
|
- if (typeof (c.pattern) == "string" && lowerCaseContent.startsWith(c.pattern)) {
|
|
|
- c.action(m, content);
|
|
|
- return;
|
|
|
- }
|
|
|
- else if (c.pattern instanceof RegExp) {
|
|
|
- const result = c.pattern.exec(content);
|
|
|
- if (result != null) {
|
|
|
- c.action(m, content, result);
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (await trigger(mCmd.ActionType.DIRECT_MENTION, m, lowerCaseContent))
|
|
|
+
|
|
|
+ if (cmdMgr.runCommand(m, content))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (await cmdMgr.trigger(mCmd.ActionType.DIRECT_MENTION, m, lowerCaseContent))
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (await trigger(mCmd.ActionType.INDIRECT_MENTION, m))
|
|
|
+ if (await cmdMgr.trigger(mCmd.ActionType.INDIRECT_MENTION, m))
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- await trigger(mCmd.ActionType.POST_MESSAGE);
|
|
|
+ await cmdMgr.trigger(mCmd.ActionType.POST_MESSAGE);
|
|
|
});
|
|
|
|
|
|
client.bot.on("messageReactionAdd", (r, u) => {
|
|
@@ -110,7 +72,6 @@ client.bot.on("messageReactionAdd", (r, u) => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-
|
|
|
async function getGreeting(member: GuildMember | PartialGuildMember) {
|
|
|
const repo = getRepository(GuildGreeting);
|
|
|
|
|
@@ -160,66 +121,12 @@ client.bot.on("guildMemberRemove", async member => {
|
|
|
}));
|
|
|
});
|
|
|
|
|
|
-function loadCommand(mod: Record<string, unknown>) {
|
|
|
- for (const i in mod) {
|
|
|
- if (!Object.prototype.hasOwnProperty.call(mod, i))
|
|
|
- continue;
|
|
|
-
|
|
|
- const commandClass = mod[i] as unknown;
|
|
|
- // Ensure this is indeed a command class
|
|
|
- if (!mCmd.isCommandSet(commandClass))
|
|
|
- continue;
|
|
|
-
|
|
|
- const cmd = new commandClass();
|
|
|
- commandSets.push(cmd);
|
|
|
-
|
|
|
- if (cmd._botCommands)
|
|
|
- botCommands.push(...cmd._botCommands.map(c => ({ ...c, action: c.action.bind(cmd) })));
|
|
|
-
|
|
|
- if (cmd._botEvents)
|
|
|
- for (const [i, event] of Object.entries(cmd._botEvents)) {
|
|
|
- botEvents[+i as mCmd.ActionType].push((event as mCmd.BotAction).bind(cmd));
|
|
|
- }
|
|
|
-
|
|
|
- if (cmd.onStart)
|
|
|
- startActions.push(cmd.onStart.bind(cmd));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-interface IDocumentationData {
|
|
|
- name: string;
|
|
|
- doc?: string;
|
|
|
- example?: string;
|
|
|
- auth: boolean;
|
|
|
-}
|
|
|
-
|
|
|
-export function getDocumentation(): IDocumentationData[] {
|
|
|
- return botCommands.filter(m => m.documentation !== undefined).map(m => ({
|
|
|
- name: m.pattern.toString(),
|
|
|
- doc: m.documentation?.description,
|
|
|
- example: m.documentation?.example,
|
|
|
- auth: m.auth || false
|
|
|
- }));
|
|
|
-}
|
|
|
-
|
|
|
async function main() {
|
|
|
await createConnection({
|
|
|
...await getConnectionOptions(),
|
|
|
entities: DB_ENTITIES
|
|
|
});
|
|
|
|
|
|
- const commandsPath = path.resolve(path.dirname(module.filename), "commands");
|
|
|
- const files = fs.readdirSync(commandsPath);
|
|
|
-
|
|
|
- for (const file of files) {
|
|
|
- const ext = path.extname(file);
|
|
|
- if (ext != ".js")
|
|
|
- continue;
|
|
|
-
|
|
|
- // eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
|
- loadCommand(require(path.resolve(commandsPath, file)));
|
|
|
- }
|
|
|
-
|
|
|
client.bot.login(process.env.BOT_TOKEN);
|
|
|
}
|
|
|
|