UNPKG

nubot

Version:

A conversational context-aware chatbot

203 lines (170 loc) 5.45 kB
'use strict' const EventEmitter = require('events').EventEmitter const User = require('./user') // TODO: Refactor brain as base class to be extended with DB connection methods // instead of emitting events with all data. Event names should be more semantic // as well, using 'connected' and 'loaded' appropriately as per adapter pattern. class Brain extends EventEmitter { // Represents somewhat persistent storage for the robot. Extend this. // // Returns a new Brain with no external storage. constructor (robot) { super() this.data = { users: {}, _private: {} } this.autoSave = true robot.on('running', () => { this.resetSaveInterval(5) }) } // Public: Store key-value pair under the private namespace and extend // existing @data before emitting the 'loaded' event. // // Returns the instance for chaining. set (key, value) { let pair if (key === Object(key)) { pair = key } else { pair = {} pair[key] = value } Object.keys(pair).forEach((key) => { this.data._private[key] = pair[key] }) this.emit('loaded', this.data) return this } // Public: Get value by key from the private namespace in @data // or return null if not found. // // Returns the value. get (key) { return this.data._private[key] != null ? this.data._private[key] : null } // Public: Remove value by key from the private namespace in @data // if it exists // // Returns the instance for chaining. remove (key) { if (this.data._private[key] != null) { delete this.data._private[key] } return this } // Public: Emits the 'save' event so that 'brain' scripts can handle // persisting. // // Returns nothing. save () { this.emit('save', this.data) } // Public: Emits the 'close' event so that 'brain' scripts can handle closing. // // Returns nothing. close () { clearInterval(this.saveInterval) this.save() this.emit('close') } // Public: Enable or disable the automatic saving // // enabled - A boolean whether to autosave or not // // Returns nothing setAutoSave (enabled) { this.autoSave = enabled } // Public: Reset the interval between save function calls. // // seconds - An Integer of seconds between saves. // // Returns nothing. resetSaveInterval (seconds) { if (this.saveInterval) { clearInterval(this.saveInterval) } this.saveInterval = setInterval(() => { if (this.autoSave) { this.save() } }, seconds * 1000) } // Public: Merge keys loaded from a DB against the in memory representation. // // Returns nothing. // // Caveats: Deeply nested structures don't merge well. // TODO: Merge recursive, including concat arrays in properties with same key mergeData (data) { for (let k in data || {}) { this.data[k] = data[k] } this.emit('loaded', this.data) } // Public: Get an Array of User objects stored in the brain. // // Returns an Array of User objects. users () { return this.data.users } // Public: Get a User object given a unique identifier. // // Returns a User instance of the specified user. userForId (id, options) { let user = this.data.users[id] if (!user) { user = new User(id, options) this.data.users[id] = user } if (options && options.room && (!user.room || user.room !== options.room)) { user = new User(id, options) this.data.users[id] = user } return user } // Public: Get a User object given a name. // // Returns a User instance for the user with the specified name. userForName (name) { let result = null const lowerName = name.toLowerCase() for (let k in this.data.users || {}) { const userName = this.data.users[k]['name'] if (userName != null && userName.toString().toLowerCase() === lowerName) { result = this.data.users[k] } } return result } // Public: Get all users whose names match fuzzyName. Currently, match // means 'starts with', but this could be extended to match initials, // nicknames, etc. // // Returns an Array of User instances matching the fuzzy name. usersForRawFuzzyName (fuzzyName) { const lowerFuzzyName = fuzzyName.toLowerCase() const users = this.data.users || {} return Object.keys(users).reduce((result, key) => { const user = users[key] if (user.name.toLowerCase().lastIndexOf(lowerFuzzyName, 0) === 0) { result.push(user) } return result }, []) } // Public: If fuzzyName is an exact match for a user, returns an array with // just that user. Otherwise, returns an array of all users for which // fuzzyName is a raw fuzzy match (see usersForRawFuzzyName). // // Returns an Array of User instances matching the fuzzy name. usersForFuzzyName (fuzzyName) { const matchedUsers = this.usersForRawFuzzyName(fuzzyName) const lowerFuzzyName = fuzzyName.toLowerCase() const fuzzyMatchedUsers = matchedUsers.filter(user => user.name.toLowerCase() === lowerFuzzyName) return fuzzyMatchedUsers.length > 0 ? fuzzyMatchedUsers : matchedUsers } } module.exports = Brain