zyno-bot-addons
Version:
Create easily addons for Zyno Bot
452 lines (429 loc) • 20.1 kB
JavaScript
const { ValueSaver } = require('valuesaver');
const CommandHandler = require('./commandHandler.js');
const Command = require('../command.js');
const CommandBuilder = require('../builders/commandBuilder.js');
const { generateId } = require('../../utils/functions.js');
const { getClientParser } = require('../../utils/parser.js');
const { createStructures } = require('./eventHandler.js');
const structureHandler = require('./structureHandler.js');
const { addons, addonCreate, commandListeners, eventListeners, builtStructures, structureStatus, structureListener } = require('../../utils/saves.js');
const EventEmitter = require('events');
const fs = require('fs/promises');
const { existsSync } = require('fs');
const path = require('path');
const registeredAddons = new ValueSaver();
var clientParser = getClientParser();
function validateRegistrant(addon, registrant){
if(!registrant) return false;
if(addon.name !== registrant.name) return false;
if(addon.permissions !== registrant.bitfield) return false;
if(addon.resolveablePath !== registrant.resolveablePath) return false;
return true;
}
class AddonRegistar{
constructor(){}
disableAddon(addonName, force = true, restart = false){
return new Promise(async (resolve, reject) => {
await new Promise(resolve => {
if(clientParser.ready === false){
clientParser.once('ready', () => {
resolve();
});
} else {
resolve();
}
});
const addon = addons.get(addonName);
if(!addon) return reject(`The addon was not found in the registar`);
if(addon.verified === false || addon.allowed === false) return reject(`The addon has already been disabled`);
const baseName = `${addon.addon.name}@${addon.addon.version}-${addon.addon.author}`;
const registeredAddon = registeredAddons.get(baseName);
if(registeredAddon){
registeredAddons.delete(baseName);
}
delete builtStructures[addonName];
addons.set(addonName, {
...addon,
verified: false,
allowed: false,
restarting: restart,
stopping: true,
starting: false
});
try{
let addonValuesaver = registeredAddons.toReadableArray();
await fs.writeFile(path.join(__dirname, `../../addons.json`), JSON.stringify(addonValuesaver), {encoding: 'utf-8'});
} catch {}
const registeredCommands = commandRegistrant.addonCommands.filter(c => c.addon === addonName);
const keys = registeredCommands.toReadableArray().map(c => c.key);
for(let i = 0; i < keys.length; i++){
let key = keys[i];
commandRegistrant.addonCommands.delete(key);
}
clientParser.getClient().addons.writeValueSaver(commandRegistrant.addonCommands.toReadableArray());
delete require.cache[require.resolve(addon.resolveablePath)];
addon.addon.channels.clear();
addon.addon.guilds.clear();
addon.addon.commands.clear();
addon.addon.removeAllListeners();
const _commandListeners = commandListeners.filter(c => c.addonName === addonName);
for(let i = 0; i < _commandListeners.length; i++){
_commandListeners[i].listener.removeAllListeners();
commandListeners.splice(commandListeners.indexOf(_commandListeners[i]), 1);
}
const _eventListeners = eventListeners.filter(e => e.addonName === addonName);
for(let i = 0; i < _eventListeners.length; i++){
_eventListeners[i].listener.removeAllListeners();
eventListeners.splice(eventListeners.indexOf(_eventListeners[i]), 1);
}
const commandHandlers = registeredCommands.toReadableArray().map(a => a.value.passedClass);
for(let i = 0; i < commandHandlers.length; i++){
commandHandlers[i].removeAllListeners();
}
if(force === true){
let commands = [...registeredCommands.toReadableArray().map(a => new CommandBuilder(a.value.command).toJSON())];
for(var i = 0; i < commands.length; i++){
delete commands[i]['overwrite'];
}
await clientParser.getClient().updateCommands(commands);
}
addons.set(addonName, {
...addon,
verified: false,
allowed: false,
restarting: restart,
stopping: false,
starting: false
});
resolve();
});
}
enableAddon(addonName){
return new Promise(async (resolve, reject) => {
const addon = addons.get(addonName);
if(!addon) return reject(`The addon was not found in the registar`);
const baseName = `${addon.addon.name}@${addon.addon.version}-${addon.addon.author}`;
registeredAddons.set(baseName, {
name: addon.addon.name,
bitfield: addon.permissions,
resolveablePath: addon.resolveablePath
});
addons.set(addonName, {
...addon,
verified: true,
allowed: true,
restarting: false,
stopping: false,
starting: true
});
try{
let addonValuesaver = registeredAddons.toReadableArray();
await fs.writeFile(path.join(__dirname, `../../addons.json`), JSON.stringify(addonValuesaver), {encoding: 'utf-8'});
} catch {}
try{
require(require.resolve(addon.resolveablePath));
} catch (err){
return reject(`There was an error while registering the addon '${addon}': ${err}`);
}
resolve();
});
}
restartAddon(addonName){
return new Promise((resolve, reject) => {
this.disableAddon(addonName, true, true).then(() => {
this.enableAddon(addonName).then(resolve).catch(reject);
}).catch(reject);
});
}
}
const addonRegistar = new AddonRegistar();
clientParser.parseAddonRegistar(addonRegistar);
class CommandRegister extends EventEmitter{
constructor(){
super();
if(clientParser.ready === false){
clientParser.once('ready', () => {
this.registeredCommands.writeValueSaver(clientParser.getClient().commands.toReadableArray());
this.emit('ready');
this.ready = true;
});
} else {
this.registeredCommands.writeValueSaver(clientParser.getClient().commands.toReadableArray());
this.emit('ready');
this.ready = true;
}
this.once('ready', () => {
for(var i = 0; i < this.queue.length; i++){
var commandQueue = this.queue[i].command;
if((this.registeredCommands.get(commandQueue.name) || this.addonCommands.get(commandQueue.name)) && commandQueue.overwrite === false){
this.queue[i].functions.reject('There has already been another command registered with this name');
return;
} else if(this.registeredCommands.get(commandQueue.name) || this.addonCommands.get(commandQueue.name)){
this.addonCommands.delete(commandQueue.name);
}
}
if(this.queue.length > 0) this.registerCommands();
});
}
register(commandJSON, addonName){
return new Promise(async (resolve, reject) => {
var addon = addons.get(addonName);
Object.defineProperty(commandJSON, 'creator', {
value: addonName,
writable: false
});
if(!addon){
var abortTooLong = setTimeout(function(){
reject('The owner of the bot took too long to response');
}, 3*6e4);
addonCreate.once(addonName, (allowed) => {
clearTimeout(abortTooLong);
if(allowed === true){
this.register(commandJSON, addonName).then(resolve).catch(reject);
} else {
reject(`The addon was disabled by the bot's owner`);
return;
}
});
} else if(addon.verified === false){
if(addon.allowed === false){
reject(`The addon was disabled by the bot's owner`);
return;
}
var abortTooLong = setTimeout(function(){
reject('The owner of the bot took too long to response');
}, 3*6e4);
addonCreate.once(addonName, (allowed) => {
clearTimeout(abortTooLong);
if(allowed === true){
this.register(commandJSON, addonName).then(resolve).catch(reject);
} else {
reject(`The addon was disabled by the bot's owner`);
return;
}
});
} else if(this.ready === false){
if(!!this.queue.filter(c => c.command.name === commandJSON.name).length && commandJSON.overwrite === false){
reject('There has already been another command registered with this name');
return;
} else {
if(!!this.queue.filter(c => c.command.name === commandJSON.name).length){
var getCommand = this.queue.filter(c => c.command.name === commandJSON.name)[0];
var getIndex = this.queue.indexOf(getCommand);
this.queue.splice(getIndex, 1);
}
this.queue.push({
command: commandJSON,
addon: addonName,
functions: {
resolve: resolve,
reject: reject
}
});
}
} else {
if((this.registeredCommands.get(commandJSON.name) || this.addonCommands.get(commandJSON.name)) && commandJSON.overwrite === false){
reject('There has already been another command registered with this name');
return;
} else {
if(this.registeredCommands.get(commandJSON.name) || this.addonCommands.get(commandJSON.name)){
this.addonCommands.delete(commandJSON.name);
}
if(!!this.queue.filter(c => c.command.name === commandJSON.name).length && commandJSON.overwrite === false){
reject('There has already been another command registered with this name');
return;
} else {
if(!!this.queue.filter(c => c.command.name === commandJSON.name).length){
var getCommand = this.queue.filter(c => c.command.name === commandJSON.name)[0];
var getIndex = this.queue.indexOf(getCommand);
this.queue.splice(getIndex, 1);
}
this.queue.push({
command: commandJSON,
addon: addonName,
functions: {
resolve: resolve,
reject: reject
}
});
this.registerCommands();
}
}
}
});
}
registerCommands(){
if(this.timeout !== undefined){
clearTimeout(this.timeout);
}
this.timeout = setTimeout(async () => {
this.timeout = undefined;
var commands = [...this.queue.map(q => {
return {...q.command}
})];
commands.push(...this.addonCommands.toReadableArray().map(a => new CommandBuilder(a.value.command).toJSON()));
for(var i = 0; i < commands.length; i++){
delete commands[i]['overwrite'];
delete commands[i]['category'];
}
try{
await clientParser.getClient().updateCommands(commands);
var commandIds = this.addonCommands.toReadableArray().map(c => c.value.command.id);
for(var i = 0; i < this.queue.length; i++){
var commandQueue = this.queue[i];
var cmdId = generateId();
while(commandIds.indexOf(cmdId) >= 0){
cmdId = generateId();
}
commandQueue.command['id'] = cmdId
var cmd = new CommandHandler(commandQueue.command);
commandQueue.functions.resolve(cmd);
delete commandQueue.functions;
commandQueue.command['passedClass'] = cmd;
this.addonCommands.set(commandQueue.command.name, commandQueue);
addons.get(commandQueue.addon).addon.commands.set(commandQueue.command.name, cmd);
}
clientParser.getClient().addons.writeValueSaver(this.addonCommands.toReadableArray());
} catch(err) {
for(var i = 0; i < this.queue.length; i++){
var commandQueue = this.queue[i];
commandQueue.functions.reject(err);
}
}
this.queue = [];
}, 2000);
}
removeCommand(commandName, addonName){
return new Promise((resolve, reject) => {
let addonCommand = this.addonCommands.get(commandName);
if(!addonCommand) return reject(`There was no such command found`);
if(addonCommand.command.creator !== addonName) return reject(`The command was not created by this addon`);
this.addonCommands.delete(commandName);
this.registerCommands();
resolve();
});
}
timeout = undefined;
ready = false;
queue = [];
registeredCommands = new ValueSaver();
addonCommands = new ValueSaver();
}
var commandRegistrant = new CommandRegister();
class InteractionHandler extends EventEmitter{
constructor(commandRegistrant){
super();
this.on('execute', (commandData, interaction) => {
var cmdName = interaction === true ? commandData.commandName : (commandData.content || '').split(' ')[0].slice(clientParser.getClient().config.prefix.length);
var cmd = commandRegistrant.addonCommands.get(cmdName.toLowerCase());
if(!cmd) return;
if(!cmd.command.passedClass) return;
var addon = addons.get(cmd.addon);
if(!addon) return;
if(typeof cmd.command.default_member_permissions === 'string'){
if(!commandData.member.permissions.has(cmd.command.default_member_permissions)) return;
}
var getExecuteableCommand = new Command(commandData, interaction, cmd.command, addon, structureHandler);
cmd.command.passedClass.emit('execute', getExecuteableCommand);
});
}
}
var interactionHandler = new InteractionHandler(commandRegistrant);
clientParser.parseInteractionHandler(interactionHandler);
function createStructureListener(addon, client){
return new Promise(async resolve => {
if(structureStatus['building'] === true){
structureListener.once('created', () => {
if(Array.isArray(builtStructures[addon.name])){
resolve();
} else {
createStructureListener(addon);
}
});
} else {
await createStructures(client);
resolve();
}
})
}
function registerAddon(addon){
return new Promise(async (resolve) => {
if(registeredAddons.size === 0){
if(existsSync(path.join(__dirname, `../../addons.json`))){
try{
let addonContent = await fs.readFile(path.join(__dirname, `../../addons.json`), {encoding: 'utf-8'});
try{
addonContent = JSON.parse(addonContent);
} catch {}
registeredAddons.writeValueSaver(addonContent);
} catch {}
}
}
const baseName = `${addon.name}@${addon.version}-${addon.author}`;
if(typeof addon.name === 'string'){
if(!addons.get(addon.name)){
var getRegistrant = registeredAddons.get(baseName);
if(validateRegistrant(addon, getRegistrant)){
addons.set(addon.name, {
baseName: baseName,
addon: addon,
permissions: addon.permissions,
verified: true,
allowed: true,
resolveablePath: addon.resolveablePath,
restarting: false,
stopping: false,
starting: false
});
addonCreate.emit(addon.name, true);
if(clientParser.ready === true){
await createStructureListener(addon, clientParser.getClient());
resolve(true);
} else {
clientParser.once('ready', async () => {
await createStructureListener(addon, clientParser.getClient());
resolve(true);
});
}
} else {
addons.set(addon.name, {
baseName: baseName,
addon: addon,
permissions: addon.permissions,
verified: true,
allowed: true,
resolveablePath: addon.resolveablePath,
restarting: false,
stopping: false,
starting: false
});
addonRegistar.disableAddon(addon.name, false, false).then(() => {
resolve(false);
}).catch(err => {
resolve({error: err});
});
}
} else {
if(typeof addon.resolveablePath === 'string') throw new Error(`Another addon with the name '${addon.name}' has already been registered`);
const addonInfo = addons.get(addon.name);
addons.set(addon.name, {
baseName: addonInfo.baseName,
addon: addon,
permissions: addon.permissions,
verified: true,
allowed: true,
resolveablePath: addonInfo.resolveablePath,
restarting: false,
stopping: false,
starting: false
});
await createStructureListener(addon, clientParser.getClient());
addonCreate.emit(addon.name, true);
resolve(true);
}
} else {
resolve({error: 'Invalid addon: The addon has no name or connection'});
}
});
}
module.exports = { registerAddon, commandRegistrant, InteractionHandler };