mirror of
https://gitlab.edgegamers.io/discord/ds-bot.git
synced 2025-12-06 07:42:40 -08:00
Basic commands working
This commit is contained in:
@@ -40,7 +40,6 @@
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"no-use-before-define": "warn",
|
||||
"camelcase": "warn",
|
||||
"block-scoped-var": "error",
|
||||
"arrow-body-style": [
|
||||
@@ -168,7 +167,6 @@
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"no-console": "warn"
|
||||
]
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.js
|
||||
@@ -1,2 +1,3 @@
|
||||
{
|
||||
"TOKEN": ""
|
||||
}
|
||||
8
guilds/1056498302205579324.json
Normal file
8
guilds/1056498302205579324.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"commands": [
|
||||
{
|
||||
"name": "foo",
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -14,7 +14,7 @@
|
||||
"@types/node": "^18.15.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint-plugin-jsdoc": "^40.1.1",
|
||||
"eslint-plugin-jsdoc": "^40.1.2",
|
||||
"typescript": "^5.0.3"
|
||||
}
|
||||
},
|
||||
@@ -840,9 +840,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jsdoc": {
|
||||
"version": "40.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.1.1.tgz",
|
||||
"integrity": "sha512-KxrQCq9pPt7LNeDBlLlnuJMpDFZnEQTs4e25NrT4u5cWmPw2P7F03F2qwPz0GMdlRZTyMOofuPAdiWytvPubvA==",
|
||||
"version": "40.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.1.2.tgz",
|
||||
"integrity": "sha512-U4Kt42OVjF0EXOWPEc8pjanT8O1ULvILwgA5k87CnhrCKG4xaJ8Sjsb6CWgDtaemOywN06u86duKU1yMaBp7IQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@es-joy/jsdoccomment": "~0.37.0",
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"start": "npm run build && node dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint-plugin-jsdoc": "^40.1.1",
|
||||
"eslint-plugin-jsdoc": "^40.1.2",
|
||||
"typescript": "^5.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
14
src/Bot.ts
14
src/Bot.ts
@@ -8,8 +8,10 @@ import { TokenEntry } from "./data/configs/Token";
|
||||
import { Client } from "discord.js";
|
||||
import { MaulConfig, MaulEntry } from "./data/configs/MAUL";
|
||||
import { CommandManager } from "./modules/Commands";
|
||||
import { PingCommand } from "./modules/commands/PingCommand";
|
||||
import { FooCommand } from "./modules/commands/FooCommand";
|
||||
|
||||
class Bot implements DSBot {
|
||||
export class Bot implements DSBot {
|
||||
maul: MaulAPI | undefined;
|
||||
modules: AbstractModuleManager;
|
||||
config: BotConfig;
|
||||
@@ -33,6 +35,11 @@ class Bot implements DSBot {
|
||||
this.maul = new MAUL(conf.TOKEN, conf.URL, conf.IP);
|
||||
});
|
||||
});
|
||||
|
||||
this.client.on("ready", () => {
|
||||
console.log(`Successfully logged in as ${this.client.user?.tag}!`);
|
||||
this.initModules();
|
||||
});
|
||||
}
|
||||
|
||||
initModules() {
|
||||
@@ -40,8 +47,9 @@ class Bot implements DSBot {
|
||||
|
||||
this.modules.loadModules();
|
||||
|
||||
cmds.registerCommand(new PingCommand());
|
||||
cmds.registerCommand(new FooCommand());
|
||||
|
||||
cmds.hookCommand();
|
||||
}
|
||||
}
|
||||
|
||||
new Bot();
|
||||
|
||||
@@ -3,6 +3,7 @@ import { GuildConfig } from "./GuildConfig";
|
||||
|
||||
export interface BotConfig extends Config {
|
||||
getGuildConfig(guildId: string): Promise<GuildConfig>;
|
||||
getGuildConfigs(): Promise<GuildConfig[]>;
|
||||
|
||||
addGuildConfig(config: GuildConfig): void;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ApplicationCommandDataResolvable, CommandInteraction, PermissionFlags, PermissionsBitField } from "discord.js";
|
||||
import { ApplicationCommandData, CommandInteraction, PermissionsBitField } from "discord.js";
|
||||
|
||||
export abstract class Command {
|
||||
abstract data: ApplicationCommandDataResolvable;
|
||||
abstract data: ApplicationCommandData;
|
||||
permissions: bigint = PermissionsBitField.All;
|
||||
global = false;
|
||||
|
||||
abstract handleCommand(interaction: CommandInteraction): boolean;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export interface ConfigEntry<T = unknown> {
|
||||
onUpdate(oldValue: T, newValue: T): void;
|
||||
}
|
||||
|
||||
export interface GuildedConfigEntry<T extends ConfigEntry> {
|
||||
export interface GuildedConfigEntry extends ConfigEntry {
|
||||
guildId: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Config } from "./Config";
|
||||
|
||||
export type GuildConfig = Config
|
||||
export interface GuildConfig extends Config {
|
||||
guildId: string;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as fs from "fs";
|
||||
import { BotConfig } from "../api/data/BotConfig";
|
||||
import { GuildConfig } from "../api/data/GuildConfig";
|
||||
import { ConfigEntry } from "../api/data/Config";
|
||||
import { GuildConfig } from "../api/data/GuildConfig";
|
||||
|
||||
export class FileConfig implements GuildConfig, BotConfig {
|
||||
export class FileConfig implements BotConfig {
|
||||
entries: ConfigEntry[] = [];
|
||||
file: string;
|
||||
|
||||
@@ -11,12 +11,23 @@ export class FileConfig implements GuildConfig, BotConfig {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
getGuildConfigs(): Promise<GuildConfig[]> {
|
||||
const files = fs.readdirSync("./guilds");
|
||||
const configs: GuildConfig[] = [];
|
||||
for (const file of files) {
|
||||
const config = new FileGuildConfig(`./guilds/${file}`, file.split(".")[0]);
|
||||
configs.push(config);
|
||||
}
|
||||
|
||||
return Promise.resolve(configs);
|
||||
}
|
||||
|
||||
addGuildConfig(config: GuildConfig): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
getGuildConfig(guildId: string): Promise<GuildConfig> {
|
||||
const config = new FileConfig(`./guilds/${guildId}.json`);
|
||||
const config = new FileGuildConfig(`./guilds/${guildId}.json`, guildId);
|
||||
|
||||
return Promise.resolve(config);
|
||||
}
|
||||
@@ -28,9 +39,10 @@ export class FileConfig implements GuildConfig, BotConfig {
|
||||
async getValue(id: string | ConfigEntry): Promise<unknown> {
|
||||
if (typeof id === "string")
|
||||
return this.getConfigEntry(id)?.value;
|
||||
for (const entry of this.entries)
|
||||
{if (entry.name === id.name)
|
||||
return entry.value;}
|
||||
for (const entry of this.entries) {
|
||||
if (entry.name === id.name)
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -77,9 +89,14 @@ export class FileConfig implements GuildConfig, BotConfig {
|
||||
this.entries.splice(this.entries.indexOf(entry), 1);
|
||||
}
|
||||
save(): Promise<void> {
|
||||
// this.entries.forEach(entry => {
|
||||
// entry.onUpdate();
|
||||
// });
|
||||
// JSON stringify entries and write to file
|
||||
const data: any = {};
|
||||
this.entries.forEach(entry => {
|
||||
data[entry.name] = entry.value;
|
||||
});
|
||||
|
||||
fs.writeFileSync(this.file, JSON.stringify(data));
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -87,3 +104,12 @@ export class FileConfig implements GuildConfig, BotConfig {
|
||||
return this.entries.find(entry => entry.name === name);
|
||||
}
|
||||
}
|
||||
|
||||
export class FileGuildConfig extends FileConfig implements GuildConfig {
|
||||
guildId: string;
|
||||
|
||||
constructor(file: string, guildId: string) {
|
||||
super(file);
|
||||
this.guildId = guildId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { BotConfig, ConfigEntry } from "../api/data/BotConfig";
|
||||
|
||||
export class SQLConfig implements BotConfig {
|
||||
entries: ConfigEntry[] = [];
|
||||
|
||||
constructor() {
|
||||
this.entries = [];
|
||||
}
|
||||
getConfigEntry(name: string): ConfigEntry | undefined {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getEntries(): Promise<ConfigEntry[]> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getValue(name: string): Promise<unknown> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
load(): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
removeEntry(entry: ConfigEntry): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
save(): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
registerEntry(entry: ConfigEntry): ConfigEntry {
|
||||
this.entries.push(entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Bot } from "./Bot";
|
||||
|
||||
new Bot();
|
||||
@@ -1,27 +1,40 @@
|
||||
import { DSBot } from "../api/DSBot";
|
||||
import { Module } from "../api/Module";
|
||||
import { Command } from "../api/data/Command";
|
||||
import { PingCommand } from "./commands/PingCommand";
|
||||
import { ConfigEntry, GuildedConfigEntry } from "../api/data/Config";
|
||||
|
||||
export class CommandManager extends Module {
|
||||
name = "Commands";
|
||||
commands: Map<string, Command>;
|
||||
configs: Map<string, Map<string, CommandConfigEntry>> = new Map();
|
||||
core = true;
|
||||
commandsConfigEntry: CommandsConfig;
|
||||
|
||||
constructor(main: DSBot) {
|
||||
super(main);
|
||||
this.commandsConfigEntry = new CommandsConfig();
|
||||
this.main = main;
|
||||
|
||||
this.commands = new Map();
|
||||
this.configs = new Map();
|
||||
|
||||
main.client.on("interactionCreate", async (interaction) => {
|
||||
if (!interaction.isCommand())
|
||||
if (!interaction.isCommand() || !interaction.guildId)
|
||||
return;
|
||||
|
||||
const command = this.commands.get(interaction.commandName);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
const config = this.configs.get(interaction.guildId)?.get(command.data.name);
|
||||
if (config) {
|
||||
if (!config.enabled) {
|
||||
interaction.reply({ content: "This command is disabled.", ephemeral: true });
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!interaction.memberPermissions?.has(command.permissions))
|
||||
return;
|
||||
|
||||
@@ -30,13 +43,95 @@ export class CommandManager extends Module {
|
||||
}
|
||||
|
||||
registerCommand(command: Command): void {
|
||||
console.log("Registering command: " + command.data.name);
|
||||
this.commands.set(command.data.name, command);
|
||||
}
|
||||
|
||||
onEnable(): void {
|
||||
this.commands.set("ping", new PingCommand());
|
||||
async hookCommand(...commands: Command[]): Promise<void> {
|
||||
if (!commands || commands.length === 0) {
|
||||
this.hookCommand(...this.commands.values());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const guildRegistrations = [];
|
||||
const guilds = await this.main.client.guilds.fetch();
|
||||
for (const weakGuild of guilds.values()) {
|
||||
console.log("Registering commands for guild: " + weakGuild.name);
|
||||
if (!this.configs.has(weakGuild.id))
|
||||
this.configs.set(weakGuild.id, new Map());
|
||||
|
||||
guildRegistrations.push(
|
||||
async () => {
|
||||
const guildConfig = this.configs.get(weakGuild.id);
|
||||
console.log(`${weakGuild.id} config: ${JSON.stringify(guildConfig)}`);
|
||||
const deepGuild = await weakGuild.fetch();
|
||||
for (const cmd of commands.filter(cmd => !cmd.global)) {
|
||||
if (guildConfig?.has(cmd.data.name)) {
|
||||
if (!guildConfig.get(cmd.data.name)?.enabled)
|
||||
continue;
|
||||
}
|
||||
console.log("Registering Guild Command: " + cmd.data.name);
|
||||
deepGuild.commands.create(cmd.data);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Awaiting completion of guild commands...");
|
||||
await Promise.all(guildRegistrations.map(fn => fn())).catch(console.error);
|
||||
console.log("Completed guild commands, registering global commands...");
|
||||
|
||||
for (const cmd of commands.filter(cmd => cmd.global)) {
|
||||
console.log("Registering global command: " + cmd.data.name);
|
||||
this.main.client.application?.commands.create(cmd.data);
|
||||
}
|
||||
|
||||
console.log("Completed global commands.");
|
||||
}
|
||||
|
||||
async onEnable(): Promise<void> {
|
||||
const guildConfigs = await this.main.config.getGuildConfigs();
|
||||
const processors = [];
|
||||
|
||||
for (const guildConfig of guildConfigs) {
|
||||
guildConfig.registerEntry(this.commandsConfigEntry);
|
||||
console.log("Registered guild config: " + guildConfig.guildId);
|
||||
|
||||
processors.push(async () => {
|
||||
const commandMap = this.configs.get(guildConfig.guildId) || new Map();
|
||||
await guildConfig.load();
|
||||
const commandEntries = await guildConfig.getValue(this.commandsConfigEntry) as CommandConfigEntry[];
|
||||
for (const entry of commandEntries)
|
||||
commandMap.set(entry.name, entry);
|
||||
|
||||
this.configs.set(guildConfig.guildId, commandMap);
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(processors.map(fn => fn())).catch(console.error);
|
||||
}
|
||||
|
||||
onDisable(): void {
|
||||
this.commands.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommandConfig extends GuildedConfigEntry {
|
||||
commands: CommandConfigEntry[];
|
||||
}
|
||||
|
||||
export interface CommandConfigEntry extends ConfigEntry {
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
overrides: unknown | undefined;
|
||||
}
|
||||
|
||||
export class CommandsConfig implements ConfigEntry<CommandConfigEntry[]> {
|
||||
name = "commands";
|
||||
value: CommandConfigEntry[] = [];
|
||||
|
||||
onUpdate(oldValue: CommandConfigEntry[], newValue: CommandConfigEntry[]): void {
|
||||
this.value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
16
src/modules/commands/FooCommand.ts
Normal file
16
src/modules/commands/FooCommand.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ApplicationCommandData, CommandInteraction, CacheType, ApplicationCommandType } from "discord.js";
|
||||
import { Command } from "../../api/data/Command";
|
||||
|
||||
export class FooCommand extends Command {
|
||||
data: ApplicationCommandData = {
|
||||
name: "foo",
|
||||
description: "Foobar",
|
||||
type: ApplicationCommandType.ChatInput
|
||||
};
|
||||
|
||||
handleCommand(interaction: CommandInteraction): boolean {
|
||||
interaction.reply("Bar");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { PermissionsBitField, CommandInteraction, CacheType, ApplicationCommandDataResolvable, ApplicationCommandType } from "discord.js";
|
||||
import { ApplicationCommandData, ApplicationCommandType, CommandInteraction, PermissionsBitField } from "discord.js";
|
||||
import { Command } from "../../api/data/Command";
|
||||
|
||||
export class PingCommand extends Command {
|
||||
data: ApplicationCommandDataResolvable = {
|
||||
data: ApplicationCommandData = {
|
||||
name: "ping",
|
||||
description: "Ping pong",
|
||||
description: "Ping!",
|
||||
type: ApplicationCommandType.ChatInput
|
||||
};
|
||||
global = true;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -15,6 +16,7 @@ export class PingCommand extends Command {
|
||||
|
||||
handleCommand(interaction: CommandInteraction): boolean {
|
||||
interaction.reply("Pong!");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user