UNPKG

adaptorex

Version:

Connect all your live interactive storytelling devices and software

503 lines (458 loc) 14.6 kB
/** @typedef {import('./types').AdaptorFunction} AdaptorFunction */ /** * Log arguments and return them the way the came in * * @type {AdaptorFunction} * * @returns {array} the arguments that came in */ function test(args, {session, game}) { return new Promise((resolve, reject) => { session.log.info("called test function with args " + args) return resolve(args) }) } /** * Get a random Integer. Provide maximum only to get a number between 0 (inclusive) and max. Provide min and max argument to get a number between min (inclusive) and max (inclusive) * * If you provide no arguments but one or more "next states", random will randomly output to one of the next with an even chance for each next. * * @type {AdaptorFunction} * * @param {[string, string]} args - 0: With two arguments this is the minimum value. With one argument only, this is the maximum value, 1: Maximum value * @returns {integer|Object<string, integer>} A random value or, if applicable, triggers one of the specified "next states" chosen randomly. */ function random(args, {session, game}) { let min, max if(Array.isArray(args) && args.length > 0) { if(args.length == 1) { min = 0 max = Math.floor(args[0]) } else if(args.length == 2) { min = Math.ceil(args[0]) max = Math.floor(args[1]) } } else { if(session.action.payload.next && session.action.payload.next.length > 0) { min = 0 max = session.action.payload.next.length -1 return {next: Math.floor(Math.random() * (max - min + 1) + min)} } } return Math.floor(Math.random() * (max - min + 1) + min) } /** * Get the current Clock Time (Berlin, UTC+2) in 24 hour Format. E.g.: "14:23" * * @returns {string} - current time */ function getTime() { return new Promise((resolve, reject) => { let time = new Date().toLocaleTimeString('de-DE', { hour: 'numeric', hour12: false, minute: 'numeric' }) return resolve(time) }) } /** * Get the date that lies a certain amount of time past or future of some other date. * * Add/Subtract argument is formatted `HH:MM:SS` for example: `1:00` add 1 minute or `-23:30:00` subtract 23 and half an hour. * * @example * dateTimeOffset("2023-06-22T12:00","15:00") // results in "2023-06-22T12:15" * * dateTimeOffset("2023-12-24T12:00","-48:30:00") // means 2 days and 30 minutes before and results in "2023-12-22T11:30" * * @param {[string, string]} args - 0: The date to add or subtract time from in [ISO Format](https://en.wikipedia.org/wiki/ISO_8601). Use "now" to set the current date/time, 1: The amount of time that is supposed to be added or subtracted from the original date (prepend `-` to subtract). */ function dateTimeOffset(args) { /** @type Date */ let origin if(args[0] == "now") { origin = adaptor.now() } else { origin = new Date(args[0]) } let offset if(args[1][0] == "-") { offset = -hmsToSeconds(args[1].slice(1)) } else { offset = hmsToSeconds(args[1]) } if(isNaN(offset)) { throw new adaptor.InvalidError(`Could not set date-time offset. "${args[1]}" is not a valid time value.`) } let offset_date = new Date(origin.setSeconds(origin.getSeconds() + offset)) return offset_date.toISOString() } /** * Convert an [ISO Formatted Time string](https://en.wikipedia.org/wiki/ISO_8601) into another kind of date and or time format. * * Uses the common javascript [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) to convert date and time. * * You can pass any of the formatting options Intl.DateTimeFormat allows by providing a two part string argument for each option, e.g. `"short weekday"`. * * Pass a locale indicator to set the date and or time locale. E.g. "de-DE" for german or "en-GB" for British english locale. * * @example * dateTimeFormat("2023-06-22","short date","en-US") // results in "06/22/23" * * dateTimeFormat("2023-06-22T17:35:12.345","weekday","hour","minute","1 secondDigits","de") // results in "Donnerstag, 17:35:12,3" * * @type {AdaptorFunction} * * @param {Array<string>} args - First argument is the date that will be formatted in ISO Format. Use "now" to set the current date/time. More arguments can be a locale and other formatting options (see above) * @returns {string} - A date and or time format */ function dateTimeFormat(args, {session}) { let date_arg = args.shift() /** @type Date */ let date if(date_arg == "now") { date = adaptor.now() } else { date = new Date(date_arg) } let options = {} if(args.length) { for(let i=args.length-1; i>=0;i--) { let opt = args[i].split(/_| /, 2) let indicator = opt[0] let style = "" if(opt.length > 1) { style = opt[0] indicator = opt[1] } switch(indicator) { case "dateStyle": case "date": options.dateStyle = style || "full" args.splice(i,1) break case "timeStyle": case "time": options.timeStyle = style || "short" args.splice(i,1) break case "calendar": options.calendar = style || "gregory" args.splice(i,1) break case "numbering": case "numberingSystem": options.numberingSystem = style || "arab" args.splice(i,1) break case "formatMatcher": case "localeMatcher": options[indicator] = style || "best fit" args.splice(i,1) break case "zone": case "timeZone": options.timeZone = style || "UTC" args.splice(i,1) break case "hour12": options.hour12 = true args.splice(i,1) break case "hour24": options.hour12 = false args.splice(i,1) break case "cycle": case "hourCycle": options.hourCycle = style || "h24" args.splice(i,1) break case "period": case "dayPeriod": case "weekday": case "era": case "zoneName": case "timeZoneName": options[indicator] = style || "long" args.splice(i,1) break case "year": case "month": case "day": case "hour": case "minute": case "second": options[indicator] = style || "numeric" args.splice(i,1) break case "digits": case "secondDigits": case "fractionalSecondDigits": options.fractionalSecondDigits = style || 1 args.splice(i,1) break } } } if(adaptor.isEmpty(options)) { options = {dateStyle: "full", timeStyle: "short"} } let locale = args[0] || adaptor.locale let result = new Intl.DateTimeFormat(locale, options).format(date) session.log.debug(`Format date and/or time from "${date_arg}" (${locale}) to "${result}"`) return result } /** * */ function hmsToSeconds(str) { var p = str.split(':'), s = 0, m = 1; s += m * parseFloat(p.pop()); m *= 60; while (p.length > 0) { s += m * parseInt(p.pop(), 10); m *= 60; } return s; } /** * Get Element in array at a specific index position. * * @param {Array<[Array,number]>} param0 - #1 The Array where to find element at index #2 the index number * @returns {*} Some value */ async function atIndex([array, index]) { return array[index] } /** * Get the number of elements inside an array * * @param {Array<[Array]>} param0 - #1 The Array to get the length of * @returns {number} Array length */ async function length([array]) { return array.length } /** * Get the number of hours that are left until a specific date/time * * Number of hours is being rounded down to the next full hour * * date/time in the past returns negative number of hours (-n) * * @param {string} schedule - UTC formatted date/time string (e.g. 2022-12-24T16:00) * @param {*} param1 * @returns {integer} number of hours */ async function hoursUntil(schedule, {session, game}) { let today=new Date() let schedule_date = new Date(schedule) let one_hour=1000*60*60 let hours_until = Math.floor((schedule_date.getTime()-today.getTime())/(one_hour)) session.log(hours_until + " hours left until " + schedule_date.toLocaleString('de-DE')) return hours_until } /** * Get the current Date in DD.MM.YYYY format. E.g.: "23.02.2022" * * @returns {string} - current date */ function getDate() { return new Promise((resolve, reject) => { let today = new Date() let dd = String(today.getDate()).padStart(2, '0'); let mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! let yyyy = today.getFullYear(); return resolve(dd + "." + mm + "." + yyyy) }) } /** * Get the number of milliseconds between midnight of January 1, 1970 and now * * @returns {integer} - Number of milliseconds since January 1, 1970 */ function timestamp() { return new Promise((resolve, reject) => { const now = new Date() const millis = now.getTime() resolve(millis) }) } /** * Takes a state name as argument and returns the time that passed since it was dispatched in the format hh:mm:ss * * @param {string} arg - Name property of a state in the current level * @returns time that has passed since the state was dispatched last */ function since(arg, {session, game}) { return new Promise((resolve, reject) => { since_state = arg game.db.sessions.find({ _id: session._id }) .then(result => { if (result.length) { if (result[0].state_data.hasOwnProperty(since_state)) { let t = new Date(Date.now() - new Date(result[0].state_data[since_state].date)) let date_since = t.toTimeString() date_since = date_since.split(' ')[0] return resolve(date_since) } else { session.log.error("no such state " + since_state) return resolve(undefined) } } else { return resolve(undefined) } }) .catch(error => { //log.error(this.name, error) return reject(error) }) }) } /** * Delete active sessions based on a find query or session name string * * @deprecated please use Control cancel action instead * * @param {Object|string} args - find query that addresses currently active sessions. */ async function cancelSessions(args, {session, game}) { try { await game.deleteSession(args[0]) } catch (error) { if(error instanceof adaptor.NotFoundError) { session.log.error(error) } else { throw error } } } /** * cancel sessions based on player reference * * @param {string} args - player reference name */ function cancelPlayerSessions(args, refs) { return new Promise((resolve, reject) => { let name = "cancelPlayerSessions" let reference = refs.variables.getReference(args) if (!reference) { return reject(name + " " + args + " is not a valid reference") } let query = reference.query let collection = "players" let key = 'sessions._id' let value = query.sessions._id //log.info("REFERENCE", reference) let match = {} //match['player.sessions._id'] = query.sessions._id match[collection + '.' + key] = value // log.info("MATCH", match) if (!refs.db.hasOwnProperty(collection)) { //log.error(name, collection + " is not an existing collection") return reject(name + ": " + collection + " is not an existing collection") } refs.db.sessions.c.aggregate([{ $lookup: { from: collection, localField: "_id", foreignField: "sessions._id", as: collection } }, { $match: match }, { $project: { _id: 1 } } ]).toArray() .then(result => { if (!result.length) { log.warn(name, key + ":" + value + " did not return any results in " + collection) return resolve(undefined) } let object_ids = [] for (let res of result) { if (!refs._id.equals(res._id)) { object_ids.push(res._id) } } if (object_ids.length) { log.info(name, "cancel sessions with references to " + JSON.stringify(match)) log.info(name, object_ids) refs.game_event.emit('cancel', { _id: { $in: object_ids } }) } else { log.info(name, "no session to delete.") } return resolve(undefined) }) .catch(error => { return reject(error) }) }) } /** * cancel sessions based on references * * @param {string} args.0 - name of collection to find references in * @param {string} args.1 - key part of query that finds documents in that collection * @param {string} args.2 - value part of query that finds documents in that collection */ function cancelSessionsFor(args, {session, game}) { return new Promise((resolve, reject) => { let name = "cancelSessions" let collection = args[0] let key = args[1] let value = args[2] let match = {} match[collection + '.' + key] = value if (!game.db.hasOwnProperty(collection)) { //log.error(name, collection + " is not an existing collection") return reject(name + ": " + collection + " is not an existing collection") } game.db.sessions.c.aggregate([{ $lookup: { from: collection, localField: "_id", foreignField: "sessions._id", as: collection } }, { $match: match }, { $project: { _id: 1 } } ]).toArray() .then(result => { if (!result.length) { log.warn(name, key + ":" + value + " did not return any results in " + collection) return resolve(undefined) } let object_ids = [] for (let res of result) { object_ids.push(res._id) } log.info(name, "cancel sessions with references to " + JSON.stringify(match)) log.info(name, object_ids) game.deleteSession({ _id: { $in: object_ids } }) return resolve(undefined) }) .catch(error => { return reject(error) }) }) } module.exports = { test: test, random:random, hoursUntil:hoursUntil, getTime: getTime, getDate: getDate, timestamp: timestamp, since:since, dateTimeOffset:dateTimeOffset, dateTimeFormat:dateTimeFormat, atIndex: atIndex, length: length, cancelSessions: cancelSessions }