Added Command System
This commit is contained in:
parent
623645090d
commit
a96da2066e
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
node_modules
|
node_modules
|
||||||
docs
|
docs
|
||||||
storage
|
storage
|
||||||
|
test/
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "linkup-bot-lib",
|
"name": "linkup-bot-lib",
|
||||||
"version": "1.1.0",
|
"version": "1.2",
|
||||||
"main": "lib/Client.js",
|
"main": "lib/Client.js",
|
||||||
"types": "lib/*.ts",
|
"types": "lib/*.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -18,11 +18,13 @@
|
|||||||
"corestore": "^6.18.2",
|
"corestore": "^6.18.2",
|
||||||
"hyperdrive": "^11.8.1",
|
"hyperdrive": "^11.8.1",
|
||||||
"hyperswarm": "^4.7.15",
|
"hyperswarm": "^4.7.15",
|
||||||
"serve-drive": "^5.0.8"
|
"serve-drive": "^5.0.8",
|
||||||
|
"glob": "7.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/b4a": "^1.6.4",
|
"@types/b4a": "^1.6.4",
|
||||||
"@types/node": "^20.14.2",
|
"@types/node": "^20.14.2",
|
||||||
"typedoc-plugin-merge-modules": "^5.1.0"
|
"typedoc-plugin-merge-modules": "^5.1.0",
|
||||||
|
"@types/glob": "^8.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,17 @@ import ServeDrive from 'serve-drive';
|
|||||||
import {IconMessage} from "./message/IconMessage";
|
import {IconMessage} from "./message/IconMessage";
|
||||||
import {TypedEventEmitter} from "./util/TypedEventEmitter";
|
import {TypedEventEmitter} from "./util/TypedEventEmitter";
|
||||||
import {LinkUpEvents} from "./LinkUpEvents";
|
import {LinkUpEvents} from "./LinkUpEvents";
|
||||||
|
import {Command} from "./Command";
|
||||||
|
import { glob } from "glob";
|
||||||
|
import { normalize } from "path";
|
||||||
|
import { promisify } from "util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the core component of the bot system. It handles connections to the Hyperswarm network, manages message sending and receiving, and emits events for various actions.
|
* This class is the core component of the bot system. It handles connections to the Hyperswarm network, manages message sending and receiving, and emits events for various actions.
|
||||||
*/
|
*/
|
||||||
export class Client extends TypedEventEmitter<LinkUpEvents> {
|
export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||||
|
public static _INSTANCE: Client;
|
||||||
|
|
||||||
public botName: string = "";
|
public botName: string = "";
|
||||||
public servePort: number | null = 0;
|
public servePort: number | null = 0;
|
||||||
public storagePath: string | undefined;
|
public storagePath: string | undefined;
|
||||||
@ -28,20 +34,21 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
|||||||
public botAvatar: string = "";
|
public botAvatar: string = "";
|
||||||
public iconMessage: IconMessage | undefined;
|
public iconMessage: IconMessage | undefined;
|
||||||
public discovery: PeerDiscovery | undefined;
|
public discovery: PeerDiscovery | undefined;
|
||||||
|
public commands: Command[] = []
|
||||||
|
private commandPrefix: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param botName The name of the bot.
|
* @param botName The name of the bot.
|
||||||
|
* @param commandPrefix Prefix for bot commands
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @constructor
|
* @constructor
|
||||||
* @author snxraven
|
* @author snxraven
|
||||||
*/
|
*/
|
||||||
constructor(botName: string) {
|
constructor(botName: string, commandPrefix: string) {
|
||||||
super();
|
super();
|
||||||
if (!botName) {
|
Client._INSTANCE = this;
|
||||||
console.error("Bot Name is not defined!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.botName = botName;
|
this.botName = botName;
|
||||||
|
this.commandPrefix = commandPrefix;
|
||||||
this.swarm = new Hyperswarm();
|
this.swarm = new Hyperswarm();
|
||||||
this.joinedRooms = new Set(); // Track the rooms the bot has joined
|
this.joinedRooms = new Set(); // Track the rooms the bot has joined
|
||||||
this.currentTopic = null; // Track the current topic
|
this.currentTopic = null; // Track the current topic
|
||||||
@ -75,6 +82,20 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
|||||||
console.log('HyperSwarm was shut down. Exiting the process with exit code 0.');
|
console.log('HyperSwarm was shut down. Exiting the process with exit code 0.');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.on("onMessage", (msg: TextMessage) => {
|
||||||
|
const message = msg.message;
|
||||||
|
if(!message.startsWith(this.commandPrefix)) return;
|
||||||
|
const [commandName, ...args] = message.slice(this.commandPrefix.length).split(' ');
|
||||||
|
|
||||||
|
const command = this.commands.find(c => c.options.name === commandName || c.options.aliases?.indexOf(commandName) !== -1);
|
||||||
|
if(command) {
|
||||||
|
console.log(`Executing command: ${command.options.name} (${command.options.aliases?.join(", ")}) with arguments: [${args.join(", ")}]`);
|
||||||
|
command.handler(this, msg, args);
|
||||||
|
} else {
|
||||||
|
console.warn(`Command not found: ${command}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -326,4 +347,62 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
|||||||
await this.swarm?.destroy();
|
await this.swarm?.destroy();
|
||||||
console.log(`Bot ${this.botName} disconnected.`);
|
console.log(`Bot ${this.botName} disconnected.`);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* @description Adds command to the bot Commands array
|
||||||
|
* @param command Command to register
|
||||||
|
* @since 1.2
|
||||||
|
* @author MiTask
|
||||||
|
*/
|
||||||
|
registerCommand(command: Command) {
|
||||||
|
console.log(`Registering command "${command.options.name}" with aliases: [${command.options.aliases?.join(", ")}]`)
|
||||||
|
this.commands.push(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Removes command from the bot Commands array
|
||||||
|
* @param command Command to unregister
|
||||||
|
* @since 1.2
|
||||||
|
* @author MiTask
|
||||||
|
*/
|
||||||
|
unregisterCommand(command: Command) {
|
||||||
|
console.log(`Unregistering command "${command.options.name}"`)
|
||||||
|
this.commands = this.commands.filter(cmd => cmd.options.name !== command.options.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Registers all classes that extend Command class on specified path
|
||||||
|
* @param path Path to search for commands (Must be full path. For example using __dirname)
|
||||||
|
* @since 1.2
|
||||||
|
* @author MiTask
|
||||||
|
*/
|
||||||
|
public async registerCommands(path: String) {
|
||||||
|
const commands = await promisify(glob)(normalize(path + "/**/*.{ts,js}"));
|
||||||
|
for (const commandPath of commands) {
|
||||||
|
try {
|
||||||
|
let command: MaybeCommand = await import(commandPath);
|
||||||
|
if ('default' in command) command = command.default;
|
||||||
|
if (command.constructor.name === 'Object') command = Object.values(command)[0];
|
||||||
|
|
||||||
|
const instance = new (command as Constructor<Command>)();
|
||||||
|
if (!instance.options || !instance.options.name) {
|
||||||
|
console.log(`Invalid command class (Missing options or options.name) at ${commandPath}`)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerCommand(instance)
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof TypeError) {
|
||||||
|
console.warn(`Invalid command class at ${commandPath}`)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = (e instanceof Error) ? e.message : String(e)
|
||||||
|
console.log(`Error during loading the command ${commandPath}:\n${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Constructor<T extends {} = {}> = new (...args: any[]) => T;
|
||||||
|
export type MaybeCommand = Constructor<Command> | {default: Constructor<Command>} | {[k: string]: Constructor<Command>};
|
16
src/Command.ts
Normal file
16
src/Command.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {Client} from "./Client"
|
||||||
|
import {TextMessage} from "./message/TextMessage";
|
||||||
|
|
||||||
|
export class Command {
|
||||||
|
constructor(public options: ICommandOption) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(bot: Client, message: TextMessage, args: string[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICommandOption {
|
||||||
|
name: string,
|
||||||
|
description?: string,
|
||||||
|
aliases?: string[]
|
||||||
|
}
|
12
test/Bot.ts
Normal file
12
test/Bot.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {Client} from "../src/Client";
|
||||||
|
|
||||||
|
const bot = new Client("testBot", ">>");
|
||||||
|
|
||||||
|
bot.registerCommands(__dirname).then(() => {
|
||||||
|
bot.on('onBotJoinRoom', () => {
|
||||||
|
console.log("Bot is ready!");
|
||||||
|
bot.sendTextMessage("Bot is ready!");
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.joinChatRoom("fdc8aad933cde0d88f15cb395dfe2e24e1731d7622c890828d8eef9608e52437");
|
||||||
|
})
|
23
tsconfig.json
Normal file
23
tsconfig.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "ESNext",
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationDir": "./lib/",
|
||||||
|
"outDir": "./lib/",
|
||||||
|
"allowJs": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"typeRoots": ["./types", "./node_modules/@types"],
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user