scriptman.js

const fs = require("fs");
const path = require("path");
const EngineScript = require("./enginescript");

const manifest = {
	version: '0.1b',
	name: 'org.js.diddle.engine.script'
}
/**
 * @class ScriptManager
 * @extends EngineScript
 * @param {DiddleEngine} diddle
 */
class ScriptManager extends EngineScript{

	/**
	 * Fetch All Valid scripts in the specifiyed directory in {DiddleEngine.config.cache}
	 */
	_fetchScripts() {
		var config = this.diddle.pacman.get("org.js.diddle.engine.config").get();

		var timestamp_start = Date.now();

		this.log.info(`fetching scripts...`);

		var scriptsdirectory = path.resolve(config.scripts);

		if (!fs.existsSync(scriptsdirectory)) {
			try {
				fs.mkdirSync(scriptsdirectory)
			} catch(e) {
				this.log.error(`cannot create scripts directory\n`,e);
				process.exit(1);
			}
			this.log.info(`created scripts directory`);
		}

		// Get Script Files
		var scripts_filenames,scripts_filtered = [];

		try {
			scripts_filenames = fs.readdirSync(scriptsdirectory).filter(f => f.endsWith(".js"));
		} catch(e) {
			this.log.error(`cannot fetch scripts directory\n`,e);
			process.exit(1);
		}

		var ScriptScheme = {
			manifest: {
				author: 'r',
				license: 'r',
				source: 'o',
				type: 'r',
				filename: 'r',
			},
			event: {},
		}
		for ( let j = 0; j < scripts_filenames.length; j++ ) {
			try {
				let scriptlocation = path.join(path.resolve(config.scripts),scripts_filenames[j])
				let currentscript_fname = scripts_filenames[j];
				let currentscript = require(scriptlocation);


				var ValidScript = { };

				var doSkipScript = false;
				var skipReason = null;

				// Check Event Object
				if (currentscript.event != undefined) {
					ValidScript.event = {};
					var eventobjs = Object.entries(currentscript.event)
					for (let i = 0; i < eventobjs.length; i++) {
						if (typeof eventobjs[i][1] == "function") {
							ValidScript.event[eventobjs[i][0]] = eventobjs[i][1];
						} else {
							skipReason = `event '${eventobjs[i][0]}' for script '${scripts_filenames[j]}' has typeof '${typeof eventobjs[i][1]}'. expected typeof 'function'. skipping script...`
							this.log.error(skipReason);
							doSkipScript = true;
						}
					}
				} else {
					this.log.warn(`no events found for script '${scripts_filenames[j]}'`);
				}

				// Check Script Manifest
				if (currentscript.manifest != undefined && doSkipScript == false) {
					ValidScript.manifest = {};

					currentscript.manifest.filename = path.join(scriptsdirectory,scripts_filenames[j]);

					var manifestobjs = Object.entries(ScriptScheme.manifest);

					for (let i = 0; i < manifestobjs.length; i++) {
						var c_manifestobj = manifestobjs[i];

						// Check if Manifest Entry Exists
						if (currentscript.manifest[c_manifestobj[0]] == undefined) {
							switch (c_manifestobj[1]) {
								case 'r':
								case 1:
									doSkipScript = true;
									skipReason = `missing required script manifest entry '${c_manifestobj[0]}' for script '${currentscript_fname}'. skipping...`
									this.log.error(skipReason);
									break;
								case 'o':
								case 0:
									this.log.warn(`missing optional script manifest entry '${c_manifestobj[0]}' for script '${currentscript_fname}'`);
									break;
							}
						} 
						// Check Manifest Entry Type
						else if (typeof currentscript.manifest[c_manifestobj[0]] != typeof c_manifestobj[1]) {
							switch (c_manifestobj[1]) {
								case 'r':
									doSkipScript = true;
									skipReason = `invalid type manifest entry '${c_manifestobj[0]}' for script '${currentscript_fname}'. skipping...`;
									this.log.error(skipReason);
									break;
								case 'o':
									this.log.warn(`invalid type manifest entry '${c_manifestobj[0]}' for script '${currentscript_fname}'`);
									break;
							}
						} 
						// Manifest Entry Exists and is matching type of scheme
						else {
							this.log.debug(`valid manifest entry '${c_manifestobj[0]}' for script '${currentscript_fname}'`);
							ValidScript.manifest[c_manifestobj[0]] = currentscript.manifest[c_manifestobj[0]];
						}
					}

					if (currentscript.manifest.type == undefined && doSkipScript == false) {
						this.log.warn(`manifest type is undefined, assuming as 'library'`);
						ValidScript.manifest.type = ValidScript.manifest.type || 'library';
					}
				} else if (currentscript.manifest == undefined) {
					skipReason = `manifest for script '${currentscript_fname}' is undefined`;
					doSkipScript = true;
					this.log.error(skipReason);
				}

				if (doSkipScript == false) {
					scripts_filtered.push(ValidScript);
				}
			} catch(e) {
				this.log.error(`cannot get script\n`,e);
				console.error(e);
				process.exit(1);
			}
		}
		if (scripts_filtered.length < 1) {
			this.log.error(`no valid scripts found, aborting!`);
			process.exit(1);
		} else {
			this.log.info(`${scripts_filtered.length} script${scripts_filtered.length == 1 ? "":"s"} found. took ${Date.now() - timestamp_start}ms`);
		}

		this.scripts = scripts_filtered;

		this._parseEvents();
	}

	async _parseEvents() {
		for ( let i = 0; i < this.scripts.length; i++ ) {
			var c_script = this.scripts[i];

			this._parseEventScript(c_script);
		}

		this.event.call('scripts-ready');
	}

	/**
	 * @param {EngineScript} script Script to Parse
	 */
	_parseEventScript(script) {
		if (script.manifest.type != 'event') return;
		if (script.event == undefined) return this.log.error(this.diddle.locale.get("placeholder.scriptEventUndefined").replace("%s",script.manifest.filename));

		var scriptEvents = Object.entries(script.event);

		for (let i = 0; i < scriptEvents.length; i++) {
			let event = scriptEvents[i];
			if (event[0] == 'ready') {
				this.event.on('scripts-ready',event[1]);
			} else if (event[0].startsWith("discord")) {
				this.diddle.pacman.get("org.js.diddle.engine.discord").event.on(event[0],event[1]);
			} else {
				this.event.on(event[0],event[1]);
			}
		}
	}

	_ready() {
		this._fetchScripts();
	}

	constructor(diddle) {
		super(diddle,manifest);
		this.log.info(`Running ${this.manifest.name}@${this.manifest.version}`);
	}
}
module.exports = ScriptManager;