UNPKG

4.93 kBJavaScriptView Raw
1'use strict'
2
3const EventEmitter = require('events').EventEmitter
4
5const User = require('./user')
6
7class Brain extends EventEmitter {
8 // Represents somewhat persistent storage for the robot. Extend this.
9 //
10 // Returns a new Brain with no external storage.
11 constructor (robot) {
12 super()
13 this.data = {
14 users: {},
15 _private: {}
16 }
17
18 this.autoSave = true
19
20 robot.on('running', () => {
21 this.resetSaveInterval(5)
22 })
23 }
24
25 // Public: Store key-value pair under the private namespace and extend
26 // existing @data before emitting the 'loaded' event.
27 //
28 // Returns the instance for chaining.
29 set (key, value) {
30 let pair
31 if (key === Object(key)) {
32 pair = key
33 } else {
34 pair = {}
35 pair[key] = value
36 }
37
38 Object.keys(pair).forEach((key) => {
39 this.data._private[key] = pair[key]
40 })
41
42 this.emit('loaded', this.data)
43
44 return this
45 }
46
47 // Public: Get value by key from the private namespace in @data
48 // or return null if not found.
49 //
50 // Returns the value.
51 get (key) {
52 return this.data._private[key] != null ? this.data._private[key] : null
53 }
54
55 // Public: Remove value by key from the private namespace in @data
56 // if it exists
57 //
58 // Returns the instance for chaining.
59 remove (key) {
60 if (this.data._private[key] != null) {
61 delete this.data._private[key]
62 }
63
64 return this
65 }
66
67 // Public: Emits the 'save' event so that 'brain' scripts can handle
68 // persisting.
69 //
70 // Returns nothing.
71 save () {
72 this.emit('save', this.data)
73 }
74
75 // Public: Emits the 'close' event so that 'brain' scripts can handle closing.
76 //
77 // Returns nothing.
78 close () {
79 clearInterval(this.saveInterval)
80 this.save()
81 this.emit('close')
82 }
83
84 // Public: Enable or disable the automatic saving
85 //
86 // enabled - A boolean whether to autosave or not
87 //
88 // Returns nothing
89 setAutoSave (enabled) {
90 this.autoSave = enabled
91 }
92
93 // Public: Reset the interval between save function calls.
94 //
95 // seconds - An Integer of seconds between saves.
96 //
97 // Returns nothing.
98 resetSaveInterval (seconds) {
99 if (this.saveInterval) {
100 clearInterval(this.saveInterval)
101 }
102 this.saveInterval = setInterval(() => {
103 if (this.autoSave) {
104 this.save()
105 }
106 }, seconds * 1000)
107 }
108
109 // Public: Merge keys loaded from a DB against the in memory representation.
110 //
111 // Returns nothing.
112 //
113 // Caveats: Deeply nested structures don't merge well.
114 mergeData (data) {
115 for (let k in data || {}) {
116 this.data[k] = data[k]
117 }
118
119 this.emit('loaded', this.data)
120 }
121
122 // Public: Get an Array of User objects stored in the brain.
123 //
124 // Returns an Array of User objects.
125 users () {
126 return this.data.users
127 }
128
129 // Public: Get a User object given a unique identifier.
130 //
131 // Returns a User instance of the specified user.
132 userForId (id, options) {
133 let user = this.data.users[id]
134
135 if (!user) {
136 user = new User(id, options)
137 this.data.users[id] = user
138 }
139
140 if (options && options.room && (!user.room || user.room !== options.room)) {
141 user = new User(id, options)
142 this.data.users[id] = user
143 }
144
145 return user
146 }
147
148 // Public: Get a User object given a name.
149 //
150 // Returns a User instance for the user with the specified name.
151 userForName (name) {
152 let result = null
153 const lowerName = name.toLowerCase()
154
155 for (let k in this.data.users || {}) {
156 const userName = this.data.users[k]['name']
157 if (userName != null && userName.toString().toLowerCase() === lowerName) {
158 result = this.data.users[k]
159 }
160 }
161
162 return result
163 }
164
165 // Public: Get all users whose names match fuzzyName. Currently, match
166 // means 'starts with', but this could be extended to match initials,
167 // nicknames, etc.
168 //
169 // Returns an Array of User instances matching the fuzzy name.
170 usersForRawFuzzyName (fuzzyName) {
171 const lowerFuzzyName = fuzzyName.toLowerCase()
172
173 const users = this.data.users || {}
174
175 return Object.keys(users).reduce((result, key) => {
176 const user = users[key]
177 if (user.name.toLowerCase().lastIndexOf(lowerFuzzyName, 0) === 0) {
178 result.push(user)
179 }
180 return result
181 }, [])
182 }
183
184 // Public: If fuzzyName is an exact match for a user, returns an array with
185 // just that user. Otherwise, returns an array of all users for which
186 // fuzzyName is a raw fuzzy match (see usersForRawFuzzyName).
187 //
188 // Returns an Array of User instances matching the fuzzy name.
189 usersForFuzzyName (fuzzyName) {
190 const matchedUsers = this.usersForRawFuzzyName(fuzzyName)
191 const lowerFuzzyName = fuzzyName.toLowerCase()
192 const fuzzyMatchedUsers = matchedUsers.filter(user => user.name.toLowerCase() === lowerFuzzyName)
193
194 return fuzzyMatchedUsers.length > 0 ? fuzzyMatchedUsers : matchedUsers
195 }
196}
197
198module.exports = Brain