UNPKG

adaptorex

Version:

Connect all your live interactive storytelling devices and software

315 lines (278 loc) 8.79 kB
/** * Organize access to adaptor:ex data collections * * data is a core plugin and will be included with every game * * @requires plugin * @requires file * * @module data/data * @copyright Lasse Marburg 2022 * @license MIT */ const plugin = require("../plugin.js") var schema = require("./schema.json") const file = require("../../file.js") const path = require("path") /** @typedef {import('../../types').AdaptorAction} Action */ /** * install request API for all custom collections the game contains */ class Data extends plugin.Plugin { constructor() { super(schema) this.name = "data" this.core = true this.collection_apis = [] } /** * create request API for each custom database * * collect files in file directory and watch for changes. */ setup(config, game) { return new Promise((resolve, reject) => { super .setup(config, game) .then((result) => { this.addTemplate("addItem", (payload, action) => { const title = `data ${action.name}` let subtitle = `Add new ${payload.collection} Item` const body = [] if (payload.reference) { subtitle += ` as ${payload.reference}` } if (payload.variables) { body.push({ text: JSON.stringify(payload.variables) }) } return { title, subtitle, body } }) this.addTemplate("getItem", (payload, action) => { const title = `data ${action.name}` let subtitle = `Get Item from ${payload.collection} as '${payload.reference}'` if (payload.multiple && payload.limit != 1) { subtitle = `Get Item(s) from ${payload.collection} as '${payload.reference}'` } const body = [{ text: payload.query }] if (payload.sort && payload.sort.length) { let txt = "" for (let s of payload.sort) { if (s.direction == 1) { txt += ` ${s.by} ⬆️` } else { txt += ` ${s.by} ⬇️` } } body.push({ text: `sort by:${txt}` }) } if (payload.limit && payload.limit > 1 && payload.multiple) { body.push({ text: `limit to ${payload.limit} Items` }) } if (payload.skip) { if (payload.skip == 1) { body.push({ text: `skip first Item` }) } else { body.push({ text: `skip first ${payload.skip} Items` }) } } return { title, subtitle, body } }) log.debug(this.name, "load assets filenames from " + this.game.files) this.collectFiles(this.game.files) file.watchDir(this.game.files, (e, file) => { this.collectFiles(this.game.files) log.debug(this.name, "change in file directory: " + e + " " + file) this.schemaUpdate(this.schema) }) for (let coll of this.game.setup.collections) { this.schema.definitions.collection.enum.push(coll) this.schema.definitions.collection.options.enum_titles.push(coll) this.schema.definitions.data_collection.enum.push(coll) } this.game.status.on("collection_added", (data) => { let updt = false if ( !this.schema.definitions.collection.enum.includes(data.collection) ) { this.schema.definitions.collection.enum.push(data.collection) if (data.plugin) { this.schema.definitions.collection.options.enum_titles.push( data.plugin + " " + data.name ) } else { this.schema.definitions.collection.options.enum_titles.push( data.name ) } updt = true } if ( data.type == "data" && !this.schema.definitions.data_collection.enum.includes( data.collection ) ) { this.schema.definitions.data_collection.enum.push(data.collection) updt = true } if (updt) { this.schemaUpdate(this.schema) } }) return resolve(this.schema) }) .catch((err) => { return reject(err) }) }) } /** * Collects all file paths from a directory and stores them in the data schema definitions under `files.anyOf[1].enum`. * * Clears the existing file enum array in the schema, walks through the specified directory to gather file paths, and then populates the enum array with those paths. * * @param {string} dir - The base directory to collect file paths from. All file paths will be relative to this directory. * @returns {string[]} - The updated `enum` array containing the relative file paths. * * @example * const fileList = this.collectFiles('./my-directory'); * log.info(fileList); * // Output: ['file1.png', 'sub-dir/file2.mp3'] */ collectFiles(dir) { this.schema.definitions.files.anyOf[1].enum = [] this.walkDirectory(dir, dir) return this.schema.definitions.files.anyOf[1].enum } /** * Recursively walks through a directory and returns a list of relative file paths in Unix-style format. * * @param {string} dir - The current directory being processed (absolute or relative path). * @param {string} baseDir - The base directory from which all file paths should be relative. */ walkDirectory(dir, baseDir) { const files = file.ls(dir) files.forEach((f) => { const filePath = path.posix.join(dir, f) if (file.isDir(filePath)) { return this.walkDirectory(filePath, baseDir) } this.schema.definitions.files.anyOf[1].enum.push( path.posix.relative(baseDir, filePath) ) }) } /** * @type {Action} * * Call database update on an item */ async update(data, session) { await session.variables.update(data.item, data.update, { multiple: data.multiple || false }) } /** * @type {Action} * * Set value of variable */ async set(data, session) { await session.variables.set(data.variable, data.value, { multiple: data.multiple || false }) } /** * @type {Action} * * Increase numeric value of variable */ async increase(data, session) { await session.variables.edit("inc", data.variable, data.value, { multiple: data.multiple || false }) } /** * @type {Action} * * Add element to variable list */ async push(data, session) { if (data.duplicates) { await session.variables.push(data.variable, data.value, { multiple: data.multiple || false }) } else { await session.variables.add(data.variable, data.value, { multiple: data.multiple || false }) } } /** * @type {Action} * * Remove Element from variable list */ async pull(data, session) { await session.variables.pull(data.variable, data.value, { multiple: data.multiple || false }) } /** * @type {Action} * * Create a new Item in one of the existing data collections */ async addItem(data, session) { await session.variables.create( data.collection, data.variables, data.reference ) } /** * @type {Action} * * Remove variable from item document or delete the whole document. */ async delete(data, session) { await session.variables.delete(data.variable, { multiple: data.multiple || false }) } /** * @type {Action} * * create new reference to current session in an external document. * * document can than be accessed during session via name reference. */ async getItem(data, session) { if (!adaptor.hasOwnProperties(data, ["collection", "query", "reference"])) { throw new adaptor.InvalidError( "can not load reference. collection, query or reference property missing." ) } let options = { multiple: data.multiple } if (data.sort && data.sort.length) { options.sort = {} for (let s of data.sort) { options.sort[s.by] = s.direction } } if (data.limit) { options.limit = parseInt(data.limit) } if (data.skip) { options.skip = parseInt(data.skip) } await session.createReference( data["collection"], data.query, data["reference"], options ) session.log(`create ${data.collection} reference "${data.reference}"`) } } module.exports.Plugin = Data