hubot 8.0.3 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/hubot.js +1 -1
- package/package.json +6 -13
- package/src/adapter.js +30 -23
- package/src/adapters/campfire.js +1 -1
- package/src/adapters/shell.js +51 -50
- package/src/brain.js +1 -0
- package/src/datastore.js +25 -28
- package/src/datastores/memory.js +3 -3
- package/src/listener.js +27 -47
- package/src/message.js +1 -1
- package/src/middleware.js +13 -54
- package/src/response.js +25 -49
- package/src/robot.js +65 -100
package/bin/hubot.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hubot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"author": "hubot",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github",
|
|
@@ -15,28 +15,21 @@
|
|
|
15
15
|
"url": "https://github.com/hubotio/hubot.git"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"async": "^3.2.4",
|
|
19
18
|
"cline": "^0.8.2",
|
|
20
19
|
"coffeescript": "^2.7.0",
|
|
21
20
|
"connect-multiparty": "^2.2.0",
|
|
22
21
|
"express": "^4.18.2",
|
|
23
22
|
"express-basic-auth": "^1.2.1",
|
|
24
23
|
"optparse": "^1.0.5",
|
|
25
|
-
"pino": "^8.11.0"
|
|
26
|
-
"standard": "^17.1.0"
|
|
24
|
+
"pino": "^8.11.0"
|
|
27
25
|
},
|
|
28
26
|
"devDependencies": {
|
|
29
|
-
"chai": "^4.3.7",
|
|
30
|
-
"is-circular": "^1.0.2",
|
|
31
|
-
"mocha": "^10.2.0",
|
|
32
|
-
"mockery": "^2.1.0",
|
|
33
27
|
"semantic-release": "^21.0.1",
|
|
34
|
-
"
|
|
35
|
-
"sinon-chai": "^3.7.0"
|
|
28
|
+
"standard": "^17.1.0"
|
|
36
29
|
},
|
|
37
30
|
"engines": {
|
|
38
|
-
"node": "
|
|
39
|
-
"npm": "
|
|
31
|
+
"node": ">= 18",
|
|
32
|
+
"npm": ">= 9"
|
|
40
33
|
},
|
|
41
34
|
"main": "./index",
|
|
42
35
|
"bin": {
|
|
@@ -45,7 +38,7 @@
|
|
|
45
38
|
"scripts": {
|
|
46
39
|
"start": "bin/hubot",
|
|
47
40
|
"pretest": "standard",
|
|
48
|
-
"test": "
|
|
41
|
+
"test": "node --test test/*_test.js",
|
|
49
42
|
"test:smoke": "node src/**/*.js",
|
|
50
43
|
"test:e2e": "bin/e2e-test.sh"
|
|
51
44
|
},
|
package/src/adapter.js
CHANGED
|
@@ -16,8 +16,8 @@ class Adapter extends EventEmitter {
|
|
|
16
16
|
// envelope - A Object with message, room and user details.
|
|
17
17
|
// strings - One or more Strings for each message to send.
|
|
18
18
|
//
|
|
19
|
-
// Returns
|
|
20
|
-
send (envelope
|
|
19
|
+
// Returns results from adapter.
|
|
20
|
+
async send (envelope, ...strings) {}
|
|
21
21
|
|
|
22
22
|
// Public: Raw method for sending emote data back to the chat source.
|
|
23
23
|
// Defaults as an alias for send
|
|
@@ -25,10 +25,9 @@ class Adapter extends EventEmitter {
|
|
|
25
25
|
// envelope - A Object with message, room and user details.
|
|
26
26
|
// strings - One or more Strings for each message to send.
|
|
27
27
|
//
|
|
28
|
-
// Returns
|
|
29
|
-
emote (envelope
|
|
30
|
-
|
|
31
|
-
return this.send.apply(this, [envelope].concat(strings))
|
|
28
|
+
// Returns results from adapter.
|
|
29
|
+
async emote (envelope, ...strings) {
|
|
30
|
+
return this.send(envelope, ...strings)
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// Public: Raw method for building a reply and sending it back to the chat
|
|
@@ -37,63 +36,68 @@ class Adapter extends EventEmitter {
|
|
|
37
36
|
// envelope - A Object with message, room and user details.
|
|
38
37
|
// strings - One or more Strings for each reply to send.
|
|
39
38
|
//
|
|
40
|
-
// Returns
|
|
41
|
-
reply (envelope
|
|
39
|
+
// Returns results from adapter.
|
|
40
|
+
async reply (envelope, ...strings) {}
|
|
42
41
|
|
|
43
42
|
// Public: Raw method for setting a topic on the chat source. Extend this.
|
|
44
43
|
//
|
|
45
44
|
// envelope - A Object with message, room and user details.
|
|
46
45
|
// strings - One more more Strings to set as the topic.
|
|
47
46
|
//
|
|
48
|
-
// Returns
|
|
49
|
-
topic (envelope
|
|
47
|
+
// Returns results from adapter.
|
|
48
|
+
async topic (envelope, ...strings) {}
|
|
50
49
|
|
|
51
50
|
// Public: Raw method for playing a sound in the chat source. Extend this.
|
|
52
51
|
//
|
|
53
52
|
// envelope - A Object with message, room and user details.
|
|
54
53
|
// strings - One or more strings for each play message to send.
|
|
55
54
|
//
|
|
56
|
-
// Returns
|
|
57
|
-
play (envelope
|
|
55
|
+
// Returns results from adapter.
|
|
56
|
+
async play (envelope, ...strings) {}
|
|
58
57
|
|
|
59
58
|
// Public: Raw method for invoking the bot to run. Extend this.
|
|
60
59
|
//
|
|
61
|
-
// Returns
|
|
62
|
-
run () {}
|
|
60
|
+
// Returns whatever the extended adapter returns.
|
|
61
|
+
async run () {}
|
|
63
62
|
|
|
64
63
|
// Public: Raw method for shutting the bot down. Extend this.
|
|
65
64
|
//
|
|
66
65
|
// Returns nothing.
|
|
67
|
-
close () {
|
|
66
|
+
close () {
|
|
67
|
+
this.removeAllListeners()
|
|
68
|
+
}
|
|
68
69
|
|
|
69
70
|
// Public: Dispatch a received message to the robot.
|
|
70
71
|
//
|
|
71
72
|
// Returns nothing.
|
|
72
|
-
receive (message) {
|
|
73
|
-
this.robot.receive(message)
|
|
73
|
+
async receive (message) {
|
|
74
|
+
await this.robot.receive(message)
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
// Public: Get an Array of User objects stored in the brain.
|
|
77
78
|
//
|
|
78
79
|
// Returns an Array of User objects.
|
|
80
|
+
// @deprecated Use @robot.brain
|
|
79
81
|
users () {
|
|
80
|
-
this.robot.logger.warning('@users() is going to be deprecated in
|
|
82
|
+
this.robot.logger.warning('@users() is going to be deprecated in 11.0.0 use @robot.brain.users()')
|
|
81
83
|
return this.robot.brain.users()
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
// Public: Get a User object given a unique identifier.
|
|
85
87
|
//
|
|
86
88
|
// Returns a User instance of the specified user.
|
|
89
|
+
// @deprecated Use @robot.brain
|
|
87
90
|
userForId (id, options) {
|
|
88
|
-
this.robot.logger.warning('@userForId() is going to be deprecated in
|
|
91
|
+
this.robot.logger.warning('@userForId() is going to be deprecated in 11.0.0 use @robot.brain.userForId()')
|
|
89
92
|
return this.robot.brain.userForId(id, options)
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
// Public: Get a User object given a name.
|
|
93
96
|
//
|
|
94
97
|
// Returns a User instance for the user with the specified name.
|
|
98
|
+
// @deprecated Use @robot.brain
|
|
95
99
|
userForName (name) {
|
|
96
|
-
this.robot.logger.warning('@userForName() is going to be deprecated in
|
|
100
|
+
this.robot.logger.warning('@userForName() is going to be deprecated in 11.0.0 use @robot.brain.userForName()')
|
|
97
101
|
return this.robot.brain.userForName(name)
|
|
98
102
|
}
|
|
99
103
|
|
|
@@ -102,8 +106,9 @@ class Adapter extends EventEmitter {
|
|
|
102
106
|
// nicknames, etc.
|
|
103
107
|
//
|
|
104
108
|
// Returns an Array of User instances matching the fuzzy name.
|
|
109
|
+
// @deprecated Use @robot.brain
|
|
105
110
|
usersForRawFuzzyName (fuzzyName) {
|
|
106
|
-
this.robot.logger.warning('@userForRawFuzzyName() is going to be deprecated in
|
|
111
|
+
this.robot.logger.warning('@userForRawFuzzyName() is going to be deprecated in 11.0.0 use @robot.brain.userForRawFuzzyName()')
|
|
107
112
|
return this.robot.brain.usersForRawFuzzyName(fuzzyName)
|
|
108
113
|
}
|
|
109
114
|
|
|
@@ -112,8 +117,9 @@ class Adapter extends EventEmitter {
|
|
|
112
117
|
// fuzzyName is a raw fuzzy match (see usersForRawFuzzyName).
|
|
113
118
|
//
|
|
114
119
|
// Returns an Array of User instances matching the fuzzy name.
|
|
120
|
+
// @deprecated Use @robot.brain
|
|
115
121
|
usersForFuzzyName (fuzzyName) {
|
|
116
|
-
this.robot.logger.warning('@userForFuzzyName() is going to be deprecated in
|
|
122
|
+
this.robot.logger.warning('@userForFuzzyName() is going to be deprecated in 11.0.0 use @robot.brain.userForFuzzyName()')
|
|
117
123
|
return this.robot.brain.usersForFuzzyName(fuzzyName)
|
|
118
124
|
}
|
|
119
125
|
|
|
@@ -123,8 +129,9 @@ class Adapter extends EventEmitter {
|
|
|
123
129
|
// send the request.
|
|
124
130
|
//
|
|
125
131
|
// Returns a ScopedClient instance.
|
|
132
|
+
// @deprecated Use node.js fetch.
|
|
126
133
|
http (url) {
|
|
127
|
-
this.robot.logger.warning('@http() is going to be deprecated in
|
|
134
|
+
this.robot.logger.warning('@http() is going to be deprecated in 11.0.0 use @robot.http()')
|
|
128
135
|
return this.robot.http(url)
|
|
129
136
|
}
|
|
130
137
|
}
|
package/src/adapters/campfire.js
CHANGED
package/src/adapters/shell.js
CHANGED
|
@@ -17,49 +17,52 @@ const historyPath = '.hubot_history'
|
|
|
17
17
|
const bold = str => `\x1b[1m${str}\x1b[22m`
|
|
18
18
|
|
|
19
19
|
class Shell extends Adapter {
|
|
20
|
+
#rl = null
|
|
20
21
|
constructor (robot) {
|
|
21
22
|
super(robot)
|
|
22
23
|
this.name = 'Shell'
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
send (envelope
|
|
26
|
-
const strings = [].slice.call(arguments, 1)
|
|
27
|
-
|
|
26
|
+
async send (envelope, ...strings) {
|
|
28
27
|
Array.from(strings).forEach(str => console.log(bold(str)))
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
emote (envelope
|
|
32
|
-
const strings = [].slice.call(arguments, 1)
|
|
30
|
+
async emote (envelope, ...strings) {
|
|
33
31
|
Array.from(strings).map(str => this.send(envelope, `* ${str}`))
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
reply (envelope
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.send.apply(this, [envelope].concat(strings))
|
|
34
|
+
async reply (envelope, ...strings) {
|
|
35
|
+
strings = strings.map((s) => `${envelope.user.name}: ${s}`)
|
|
36
|
+
this.send(envelope, ...strings)
|
|
40
37
|
}
|
|
41
38
|
|
|
42
|
-
run () {
|
|
39
|
+
async run () {
|
|
43
40
|
this.buildCli()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.log(error.message)
|
|
47
|
-
}
|
|
41
|
+
try {
|
|
42
|
+
const { readlineInterface, history } = await this.#loadHistory()
|
|
48
43
|
this.cli.history(history)
|
|
49
|
-
this.cli.interact(`${this.robot.name}> `)
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
this.cli.interact(`${this.robot.name ?? this.robot.alias}> `)
|
|
45
|
+
this.#rl = readlineInterface
|
|
46
|
+
this.emit('connected', this)
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.log(error)
|
|
49
|
+
}
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
close () {
|
|
53
|
+
super.close()
|
|
54
|
+
// Getting an error message on GitHubt Actions: error: 'this[#rl].close is not a function'
|
|
55
|
+
if (this.#rl?.close) {
|
|
56
|
+
this.#rl.close()
|
|
57
|
+
}
|
|
58
|
+
this.cli.removeAllListeners()
|
|
59
|
+
this.cli.close()
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
buildCli () {
|
|
60
63
|
this.cli = cline()
|
|
61
64
|
|
|
62
|
-
this.cli.command('*', input => {
|
|
65
|
+
this.cli.command('*', async input => {
|
|
63
66
|
let userId = process.env.HUBOT_SHELL_USER_ID || '1'
|
|
64
67
|
if (userId.match(/A\d+z/)) {
|
|
65
68
|
userId = parseInt(userId)
|
|
@@ -67,7 +70,7 @@ class Shell extends Adapter {
|
|
|
67
70
|
|
|
68
71
|
const userName = process.env.HUBOT_SHELL_USER_NAME || 'Shell'
|
|
69
72
|
const user = this.robot.brain.userForId(userId, { name: userName, room: 'Shell' })
|
|
70
|
-
this.receive(new TextMessage(user, input, 'messageId'))
|
|
73
|
+
await this.receive(new TextMessage(user, input, 'messageId'))
|
|
71
74
|
})
|
|
72
75
|
|
|
73
76
|
this.cli.command('history', () => {
|
|
@@ -90,7 +93,7 @@ class Shell extends Adapter {
|
|
|
90
93
|
history = this.cli.history()
|
|
91
94
|
|
|
92
95
|
if (history.length <= historySize) {
|
|
93
|
-
return
|
|
96
|
+
return
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
const startIndex = history.length - historySize
|
|
@@ -100,11 +103,35 @@ class Shell extends Adapter {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
const outstream = fs.createWriteStream(historyPath, fileOpts)
|
|
103
|
-
outstream.on('end', this.shutdown.bind(this))
|
|
104
106
|
for (i = 0, len = history.length; i < len; i++) {
|
|
105
107
|
item = history[i]
|
|
106
108
|
outstream.write(item + '\n')
|
|
107
109
|
}
|
|
110
|
+
outstream.end()
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async #loadHistory () {
|
|
115
|
+
if (!fs.existsSync(historyPath)) {
|
|
116
|
+
return new Error('No history available')
|
|
117
|
+
}
|
|
118
|
+
const instream = fs.createReadStream(historyPath)
|
|
119
|
+
const outstream = new Stream()
|
|
120
|
+
outstream.readable = true
|
|
121
|
+
outstream.writable = true
|
|
122
|
+
const history = []
|
|
123
|
+
const readlineInterface = readline.createInterface({ input: instream, output: outstream, terminal: false })
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
readlineInterface.on('line', line => {
|
|
126
|
+
line = line.trim()
|
|
127
|
+
if (line.length > 0) {
|
|
128
|
+
history.push(line)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
readlineInterface.on('close', () => {
|
|
132
|
+
resolve({ readlineInterface, history })
|
|
133
|
+
})
|
|
134
|
+
readlineInterface.on('error', reject)
|
|
108
135
|
})
|
|
109
136
|
}
|
|
110
137
|
}
|
|
@@ -112,29 +139,3 @@ class Shell extends Adapter {
|
|
|
112
139
|
// Prevent output buffer "swallowing" every other character on OSX / Node version > 16.19.0.
|
|
113
140
|
process.stdout._handle.setBlocking(false)
|
|
114
141
|
exports.use = robot => new Shell(robot)
|
|
115
|
-
|
|
116
|
-
// load history from .hubot_history.
|
|
117
|
-
//
|
|
118
|
-
// callback - A Function that is called with the loaded history items (or an empty array if there is no history)
|
|
119
|
-
function loadHistory (callback) {
|
|
120
|
-
if (!fs.existsSync(historyPath)) {
|
|
121
|
-
return callback(new Error('No history available'))
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const instream = fs.createReadStream(historyPath)
|
|
125
|
-
const outstream = new Stream()
|
|
126
|
-
outstream.readable = true
|
|
127
|
-
outstream.writable = true
|
|
128
|
-
|
|
129
|
-
const items = []
|
|
130
|
-
|
|
131
|
-
readline.createInterface({ input: instream, output: outstream, terminal: false })
|
|
132
|
-
.on('line', function (line) {
|
|
133
|
-
line = line.trim()
|
|
134
|
-
if (line.length > 0) {
|
|
135
|
-
items.push(line)
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
.on('close', () => callback(null, items))
|
|
139
|
-
.on('error', callback)
|
|
140
|
-
}
|
package/src/brain.js
CHANGED
package/src/datastore.js
CHANGED
|
@@ -13,52 +13,49 @@ class DataStore {
|
|
|
13
13
|
// write has completed.
|
|
14
14
|
//
|
|
15
15
|
// Value can be any JSON-serializable type.
|
|
16
|
-
set (key, value) {
|
|
17
|
-
return this._set(key, value, 'global')
|
|
16
|
+
async set (key, value) {
|
|
17
|
+
return await this._set(key, value, 'global')
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Public: Assuming `key` represents an object in the database,
|
|
21
21
|
// sets its `objectKey` to `value`. If `key` isn't already
|
|
22
22
|
// present, it's instantiated as an empty object.
|
|
23
|
-
setObject (key, objectKey, value) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
})
|
|
23
|
+
async setObject (key, objectKey, value) {
|
|
24
|
+
const object = await this.get(key)
|
|
25
|
+
const target = object || {}
|
|
26
|
+
target[objectKey] = value
|
|
27
|
+
return await this.set(key, target)
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
// Public: Adds the supplied value(s) to the end of the existing
|
|
32
31
|
// array in the database marked by `key`. If `key` isn't already
|
|
33
32
|
// present, it's instantiated as an empty array.
|
|
34
|
-
setArray (key, value) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
})
|
|
33
|
+
async setArray (key, value) {
|
|
34
|
+
const object = await this.get(key)
|
|
35
|
+
const target = object ?? []
|
|
36
|
+
// Extend the array if the value is also an array, otherwise
|
|
37
|
+
// push the single value on the end.
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return await this.set(key, target.concat(value))
|
|
40
|
+
} else {
|
|
41
|
+
return await this.set(key, target.concat([value]))
|
|
42
|
+
}
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
// Public: Get value by key if in the database or return `undefined`
|
|
48
46
|
// if not found. Returns a promise which resolves to the
|
|
49
47
|
// requested value.
|
|
50
|
-
get (key) {
|
|
51
|
-
return this._get(key, 'global')
|
|
48
|
+
async get (key) {
|
|
49
|
+
return await this._get(key, 'global')
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
// Public: Digs inside the object at `key` for a key named
|
|
55
53
|
// `objectKey`. If `key` isn't already present, or if it doesn't
|
|
56
54
|
// contain an `objectKey`, returns `undefined`.
|
|
57
|
-
getObject (key, objectKey) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
})
|
|
55
|
+
async getObject (key, objectKey) {
|
|
56
|
+
const object = await this.get(key)
|
|
57
|
+
const target = object || {}
|
|
58
|
+
return target[objectKey]
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
// Private: Implements the underlying `set` logic for the datastore.
|
|
@@ -70,7 +67,7 @@ class DataStore {
|
|
|
70
67
|
// This returns a resolved promise when the `set` operation is
|
|
71
68
|
// successful, and a rejected promise if the operation fails.
|
|
72
69
|
_set (key, value, table) {
|
|
73
|
-
|
|
70
|
+
throw new DataStoreUnavailable('Setter called on the abstract class.')
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
// Private: Implements the underlying `get` logic for the datastore.
|
|
@@ -82,7 +79,7 @@ class DataStore {
|
|
|
82
79
|
// This returns a resolved promise containing the fetched value on
|
|
83
80
|
// success, and a rejected promise if the operation fails.
|
|
84
81
|
_get (key, table) {
|
|
85
|
-
|
|
82
|
+
throw new DataStoreUnavailable('Getter called on the abstract class.')
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
85
|
|
package/src/datastores/memory.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const DataStore = require('../datastore').DataStore
|
|
3
|
+
const DataStore = require('../datastore.js').DataStore
|
|
4
4
|
|
|
5
5
|
class InMemoryDataStore extends DataStore {
|
|
6
6
|
constructor (robot) {
|
|
@@ -11,11 +11,11 @@ class InMemoryDataStore extends DataStore {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
_get (key, table) {
|
|
14
|
+
async _get (key, table) {
|
|
15
15
|
return Promise.resolve(this.data[table][key])
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
_set (key, value, table) {
|
|
18
|
+
async _set (key, value, table) {
|
|
19
19
|
return Promise.resolve(this.data[table][key] = value)
|
|
20
20
|
}
|
|
21
21
|
}
|
package/src/listener.js
CHANGED
|
@@ -20,19 +20,19 @@ class Listener {
|
|
|
20
20
|
constructor (robot, matcher, options, callback) {
|
|
21
21
|
this.robot = robot
|
|
22
22
|
this.matcher = matcher
|
|
23
|
-
this.options = options
|
|
23
|
+
this.options = options ?? {}
|
|
24
24
|
this.callback = callback
|
|
25
25
|
|
|
26
26
|
if (this.matcher == null) {
|
|
27
27
|
throw new Error('Missing a matcher for Listener')
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
if (this.callback
|
|
30
|
+
if (!this.callback) {
|
|
31
31
|
this.callback = this.options
|
|
32
32
|
this.options = {}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
if (this.options
|
|
35
|
+
if (!this.options?.id) {
|
|
36
36
|
this.options.id = null
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -49,60 +49,40 @@ class Listener {
|
|
|
49
49
|
//
|
|
50
50
|
// message - A Message instance.
|
|
51
51
|
// middleware - Optional Middleware object to execute before the Listener callback
|
|
52
|
-
// callback - Optional Function called with a boolean of whether the matcher matched
|
|
53
52
|
//
|
|
54
|
-
// Returns
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
middleware = undefined
|
|
53
|
+
// Returns the result of the callback.
|
|
54
|
+
async call (message, middleware) {
|
|
55
|
+
if (middleware && typeof middleware === 'function') {
|
|
56
|
+
const fn = middleware
|
|
57
|
+
middleware = new Middleware(this.robot)
|
|
58
|
+
middleware.register(fn)
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
if (middleware == null) {
|
|
61
|
+
if (!middleware) {
|
|
65
62
|
middleware = new Middleware(this.robot)
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
const match = this.matcher(message)
|
|
69
|
-
if (match)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// special middleware-like function that always executes the Listener's
|
|
75
|
-
// callback and calls done (never calls 'next')
|
|
76
|
-
const executeListener = (context, done) => {
|
|
77
|
-
this.robot.logger.debug(`Executing listener callback for Message '${message}'`)
|
|
78
|
-
try {
|
|
79
|
-
this.callback(context.response)
|
|
80
|
-
} catch (err) {
|
|
81
|
-
this.robot.emit('error', err, context.response)
|
|
82
|
-
}
|
|
83
|
-
done()
|
|
84
|
-
}
|
|
66
|
+
if (!match) return null
|
|
67
|
+
if (this.regex) {
|
|
68
|
+
this.robot.logger.debug(`Message '${message}' matched regex /${inspect(this.regex)}/; listener.options = ${inspect(this.options)}`)
|
|
69
|
+
}
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
// pass control back to the robot
|
|
88
|
-
const allDone = function allDone () {
|
|
89
|
-
// Yes, we tried to execute the listener callback (middleware may
|
|
90
|
-
// have intercepted before actually executing though)
|
|
91
|
-
if (didMatchCallback != null) {
|
|
92
|
-
process.nextTick(() => didMatchCallback(true))
|
|
93
|
-
}
|
|
94
|
-
}
|
|
71
|
+
const response = new this.robot.Response(this.robot, message, match)
|
|
95
72
|
|
|
96
|
-
|
|
97
|
-
middleware.execute({ listener: this, response }
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
73
|
+
try {
|
|
74
|
+
const shouldContinue = await middleware.execute({ listener: this, response })
|
|
75
|
+
if (shouldContinue === false) return null
|
|
76
|
+
} catch (e) {
|
|
77
|
+
this.robot.logger.error(`Error executing middleware for listener: ${e.stack}`)
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return await this.callback(response)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
this.robot.logger.error(`Error executing listener callback: ${e.stack}`)
|
|
83
|
+
this.robot.emit('error', e, response)
|
|
105
84
|
}
|
|
85
|
+
return null
|
|
106
86
|
}
|
|
107
87
|
}
|
|
108
88
|
|
package/src/message.js
CHANGED
package/src/middleware.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const async = require('async')
|
|
4
|
-
|
|
5
3
|
class Middleware {
|
|
6
4
|
constructor (robot) {
|
|
7
5
|
this.robot = robot
|
|
@@ -16,68 +14,29 @@ class Middleware {
|
|
|
16
14
|
// context - context object that is passed through the middleware stack.
|
|
17
15
|
// When handling errors, this is assumed to have a `response` property.
|
|
18
16
|
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// executed middleware.
|
|
24
|
-
//
|
|
25
|
-
// Returns nothing
|
|
26
|
-
// Returns before executing any middleware
|
|
27
|
-
execute (context, next, done) {
|
|
28
|
-
const self = this
|
|
29
|
-
|
|
30
|
-
if (done == null) {
|
|
31
|
-
done = function () {}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Execute a single piece of middleware and update the completion callback
|
|
35
|
-
// (each piece of middleware can wrap the 'done' callback with additional
|
|
36
|
-
// logic).
|
|
37
|
-
function executeSingleMiddleware (doneFunc, middlewareFunc, cb) {
|
|
38
|
-
// Match the async.reduce interface
|
|
39
|
-
function nextFunc (newDoneFunc) {
|
|
40
|
-
cb(null, newDoneFunc || doneFunc)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Catch errors in synchronous middleware
|
|
17
|
+
// Returns bool, true | false, whether or not to continue execution
|
|
18
|
+
async execute (context) {
|
|
19
|
+
let shouldContinue = true
|
|
20
|
+
for await (const middleware of this.stack) {
|
|
44
21
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
doneFunc()
|
|
22
|
+
shouldContinue = await middleware(context)
|
|
23
|
+
if (shouldContinue === false) break
|
|
24
|
+
} catch (e) {
|
|
25
|
+
this.robot.emit('error', e, context.response)
|
|
26
|
+
break
|
|
51
27
|
}
|
|
52
28
|
}
|
|
53
|
-
|
|
54
|
-
// Executed when the middleware stack is finished
|
|
55
|
-
function allDone (_, finalDoneFunc) {
|
|
56
|
-
next(context, finalDoneFunc)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Execute each piece of middleware, collecting the latest 'done' callback
|
|
60
|
-
// at each step.
|
|
61
|
-
process.nextTick(async.reduce.bind(null, this.stack, done, executeSingleMiddleware, allDone))
|
|
29
|
+
return shouldContinue
|
|
62
30
|
}
|
|
63
31
|
|
|
64
32
|
// Public: Registers new middleware
|
|
65
33
|
//
|
|
66
|
-
// middleware -
|
|
67
|
-
// continue the pipeline or interrupt it. The function is called
|
|
68
|
-
// with (robot, context, next, done). If execution should
|
|
69
|
-
// continue (next middleware, final callback), the middleware
|
|
70
|
-
// should call the 'next' function with 'done' as an optional
|
|
71
|
-
// argument.
|
|
72
|
-
// If not, the middleware should call the 'done' function with
|
|
73
|
-
// no arguments. Middleware may wrap the 'done' function in
|
|
74
|
-
// order to execute logic after the final callback has been
|
|
75
|
-
// executed.
|
|
34
|
+
// middleware - Middleware function to execute prior to the listener callback. Return false to prevent execution of the listener callback.
|
|
76
35
|
//
|
|
77
36
|
// Returns nothing.
|
|
78
37
|
register (middleware) {
|
|
79
|
-
if (middleware.length !==
|
|
80
|
-
throw new Error(`Incorrect number of arguments for middleware callback (expected
|
|
38
|
+
if (middleware.length !== 1) {
|
|
39
|
+
throw new Error(`Incorrect number of arguments for middleware callback (expected 1, got ${middleware.length})`)
|
|
81
40
|
}
|
|
82
41
|
this.stack.push(middleware)
|
|
83
42
|
}
|
package/src/response.js
CHANGED
|
@@ -14,8 +14,7 @@ class Response {
|
|
|
14
14
|
this.match = match
|
|
15
15
|
this.envelope = {
|
|
16
16
|
room: this.message.room,
|
|
17
|
-
user: this.message.user
|
|
18
|
-
message: this.message
|
|
17
|
+
user: this.message.user
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -24,10 +23,9 @@ class Response {
|
|
|
24
23
|
// strings - One or more strings to be posted. The order of these strings
|
|
25
24
|
// should be kept intact.
|
|
26
25
|
//
|
|
27
|
-
// Returns
|
|
28
|
-
send (
|
|
29
|
-
|
|
30
|
-
this.runWithMiddleware.apply(this, ['send', { plaintext: true }].concat(strings))
|
|
26
|
+
// Returns result from middleware.
|
|
27
|
+
async send (...strings) {
|
|
28
|
+
return await this.#runWithMiddleware('send', { plaintext: true }, ...strings)
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
// Public: Posts an emote back to the chat source
|
|
@@ -35,10 +33,9 @@ class Response {
|
|
|
35
33
|
// strings - One or more strings to be posted. The order of these strings
|
|
36
34
|
// should be kept intact.
|
|
37
35
|
//
|
|
38
|
-
// Returns
|
|
39
|
-
emote (
|
|
40
|
-
|
|
41
|
-
this.runWithMiddleware.apply(this, ['emote', { plaintext: true }].concat(strings))
|
|
36
|
+
// Returns result from middleware.
|
|
37
|
+
async emote (...strings) {
|
|
38
|
+
return await this.#runWithMiddleware('emote', { plaintext: true }, ...strings)
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
// Public: Posts a message mentioning the current user.
|
|
@@ -46,10 +43,9 @@ class Response {
|
|
|
46
43
|
// strings - One or more strings to be posted. The order of these strings
|
|
47
44
|
// should be kept intact.
|
|
48
45
|
//
|
|
49
|
-
// Returns
|
|
50
|
-
reply (
|
|
51
|
-
|
|
52
|
-
this.runWithMiddleware.apply(this, ['reply', { plaintext: true }].concat(strings))
|
|
46
|
+
// Returns result from middleware.
|
|
47
|
+
async reply (...strings) {
|
|
48
|
+
return await this.#runWithMiddleware('reply', { plaintext: true }, ...strings)
|
|
53
49
|
}
|
|
54
50
|
|
|
55
51
|
// Public: Posts a topic changing message
|
|
@@ -57,10 +53,9 @@ class Response {
|
|
|
57
53
|
// strings - One or more strings to set as the topic of the
|
|
58
54
|
// room the bot is in.
|
|
59
55
|
//
|
|
60
|
-
// Returns
|
|
61
|
-
topic (
|
|
62
|
-
|
|
63
|
-
this.runWithMiddleware.apply(this, ['topic', { plaintext: true }].concat(strings))
|
|
56
|
+
// Returns result from middleware.
|
|
57
|
+
async topic (...strings) {
|
|
58
|
+
return await this.#runWithMiddleware('topic', { plaintext: true }, ...strings)
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
// Public: Play a sound in the chat source
|
|
@@ -68,10 +63,9 @@ class Response {
|
|
|
68
63
|
// strings - One or more strings to be posted as sounds to play. The order of
|
|
69
64
|
// these strings should be kept intact.
|
|
70
65
|
//
|
|
71
|
-
// Returns
|
|
72
|
-
play (
|
|
73
|
-
|
|
74
|
-
this.runWithMiddleware.apply(this, ['play'].concat(strings))
|
|
66
|
+
// Returns result from middleware.
|
|
67
|
+
async play (...strings) {
|
|
68
|
+
return await this.#runWithMiddleware('play', {}, ...strings)
|
|
75
69
|
}
|
|
76
70
|
|
|
77
71
|
// Public: Posts a message in an unlogged room
|
|
@@ -79,27 +73,17 @@ class Response {
|
|
|
79
73
|
// strings - One or more strings to be posted. The order of these strings
|
|
80
74
|
// should be kept intact.
|
|
81
75
|
//
|
|
82
|
-
// Returns
|
|
83
|
-
locked (
|
|
84
|
-
|
|
85
|
-
this.runWithMiddleware.apply(this, ['locked', { plaintext: true }].concat(strings))
|
|
76
|
+
// Returns result from middleware.
|
|
77
|
+
async locked (...strings) {
|
|
78
|
+
await this.#runWithMiddleware('locked', { plaintext: true }, ...strings)
|
|
86
79
|
}
|
|
87
80
|
|
|
88
|
-
//
|
|
81
|
+
// Call with a method for the given strings using response
|
|
89
82
|
// middleware.
|
|
90
|
-
runWithMiddleware (methodName, opts
|
|
91
|
-
const self = this
|
|
92
|
-
const strings = [].slice.call(arguments, 2)
|
|
93
|
-
const copy = strings.slice(0)
|
|
94
|
-
let callback
|
|
95
|
-
|
|
96
|
-
if (typeof copy[copy.length - 1] === 'function') {
|
|
97
|
-
callback = copy.pop()
|
|
98
|
-
}
|
|
99
|
-
|
|
83
|
+
async #runWithMiddleware (methodName, opts, ...strings) {
|
|
100
84
|
const context = {
|
|
101
85
|
response: this,
|
|
102
|
-
strings
|
|
86
|
+
strings,
|
|
103
87
|
method: methodName
|
|
104
88
|
}
|
|
105
89
|
|
|
@@ -107,17 +91,9 @@ class Response {
|
|
|
107
91
|
context.plaintext = true
|
|
108
92
|
}
|
|
109
93
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (callback != null) {
|
|
114
|
-
result.push(callback)
|
|
115
|
-
}
|
|
116
|
-
self.robot.adapter[methodName].apply(self.robot.adapter, [self.envelope].concat(result))
|
|
117
|
-
done()
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return this.robot.middleware.response.execute(context, runAdapterSend, responseMiddlewareDone)
|
|
94
|
+
const shouldContinue = await this.robot.middleware.response.execute(context)
|
|
95
|
+
if (shouldContinue === false) return
|
|
96
|
+
return await this.robot.adapter[methodName](this.envelope, ...context.strings)
|
|
121
97
|
}
|
|
122
98
|
|
|
123
99
|
// Public: Picks a random item from the given items.
|
package/src/robot.js
CHANGED
|
@@ -4,7 +4,6 @@ const fs = require('fs')
|
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const pathToFileURL = require('url').pathToFileURL
|
|
6
6
|
|
|
7
|
-
const async = require('async')
|
|
8
7
|
const pino = require('pino')
|
|
9
8
|
const HttpClient = require('./httpclient')
|
|
10
9
|
|
|
@@ -51,11 +50,6 @@ class Robot {
|
|
|
51
50
|
name,
|
|
52
51
|
level: process.env.HUBOT_LOG_LEVEL || 'info'
|
|
53
52
|
})
|
|
54
|
-
Reflect.defineProperty(this.logger, 'warning', {
|
|
55
|
-
value: this.logger.warn,
|
|
56
|
-
enumerable: true,
|
|
57
|
-
configurable: true
|
|
58
|
-
})
|
|
59
53
|
|
|
60
54
|
this.pingIntervalId = null
|
|
61
55
|
this.globalHttpOptions = {}
|
|
@@ -73,10 +67,6 @@ class Robot {
|
|
|
73
67
|
this.on('error', (err, res) => {
|
|
74
68
|
return this.invokeErrorHandlers(err, res)
|
|
75
69
|
})
|
|
76
|
-
this.onUncaughtException = err => {
|
|
77
|
-
return this.emit('error', err)
|
|
78
|
-
}
|
|
79
|
-
process.on('uncaughtException', this.onUncaughtException)
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
// Public: Adds a custom Listener with the provided matcher, options, and
|
|
@@ -233,9 +223,8 @@ class Robot {
|
|
|
233
223
|
options = {}
|
|
234
224
|
}
|
|
235
225
|
|
|
236
|
-
this.listen(isCatchAllMessage, options,
|
|
237
|
-
|
|
238
|
-
callback(msg)
|
|
226
|
+
this.listen(isCatchAllMessage, options, async msg => {
|
|
227
|
+
await callback(msg.message)
|
|
239
228
|
})
|
|
240
229
|
}
|
|
241
230
|
|
|
@@ -244,11 +233,8 @@ class Robot {
|
|
|
244
233
|
//
|
|
245
234
|
// middleware - A function that determines whether or not a given matching
|
|
246
235
|
// Listener should be executed. The function is called with
|
|
247
|
-
// (context
|
|
248
|
-
//
|
|
249
|
-
// should call the 'next' function with 'done' as an argument.
|
|
250
|
-
// If not, the middleware should call the 'done' function with
|
|
251
|
-
// no arguments.
|
|
236
|
+
// (context). If execution should, the middleware should return
|
|
237
|
+
// true. If not, the middleware should return false.
|
|
252
238
|
//
|
|
253
239
|
// Returns nothing.
|
|
254
240
|
listenerMiddleware (middleware) {
|
|
@@ -260,9 +246,8 @@ class Robot {
|
|
|
260
246
|
//
|
|
261
247
|
// middleware - A function that examines an outgoing message and can modify
|
|
262
248
|
// it or prevent its sending. The function is called with
|
|
263
|
-
// (context
|
|
264
|
-
//
|
|
265
|
-
// stop, the middleware should call done(). To modify the
|
|
249
|
+
// (context). If execution should continue, return true
|
|
250
|
+
// otherwise return false to stop. To modify the
|
|
266
251
|
// outgoing message, set context.string to a new message.
|
|
267
252
|
//
|
|
268
253
|
// Returns nothing.
|
|
@@ -273,11 +258,10 @@ class Robot {
|
|
|
273
258
|
// Public: Registers new middleware for execution before matching
|
|
274
259
|
//
|
|
275
260
|
// middleware - A function that determines whether or not listeners should be
|
|
276
|
-
// checked. The function is called with (context
|
|
277
|
-
//
|
|
278
|
-
// middleware or matching phase, it should
|
|
279
|
-
//
|
|
280
|
-
// should call the 'done' function with no arguments.
|
|
261
|
+
// checked. The function is called with (context). If execution
|
|
262
|
+
// should continue to the next
|
|
263
|
+
// middleware or matching phase, it should return true or nothing
|
|
264
|
+
// otherwise return false to stop.
|
|
281
265
|
//
|
|
282
266
|
// Returns nothing.
|
|
283
267
|
receiveMiddleware (middleware) {
|
|
@@ -290,14 +274,12 @@ class Robot {
|
|
|
290
274
|
// message - A Message instance. Listeners can flag this message as 'done' to
|
|
291
275
|
// prevent further execution.
|
|
292
276
|
//
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// pass control back to the robot
|
|
300
|
-
this.middleware.receive.execute({ response: new Response(this, message) }, this.processListeners.bind(this), cb)
|
|
277
|
+
// Returns array of results from listeners.
|
|
278
|
+
async receive (message) {
|
|
279
|
+
const context = { response: new Response(this, message) }
|
|
280
|
+
const shouldContinue = await this.middleware.receive.execute(context)
|
|
281
|
+
if (shouldContinue === false) return null
|
|
282
|
+
return await this.processListeners(context)
|
|
301
283
|
}
|
|
302
284
|
|
|
303
285
|
// Private: Passes the given message to any interested Listeners.
|
|
@@ -305,45 +287,40 @@ class Robot {
|
|
|
305
287
|
// message - A Message instance. Listeners can flag this message as 'done' to
|
|
306
288
|
// prevent further execution.
|
|
307
289
|
//
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
// Returns nothing.
|
|
311
|
-
// Returns before executing callback
|
|
312
|
-
processListeners (context, done) {
|
|
290
|
+
// Returns array of results from listeners.
|
|
291
|
+
async processListeners (context) {
|
|
313
292
|
// Try executing all registered Listeners in order of registration
|
|
314
293
|
// and return after message is done being processed
|
|
294
|
+
const results = []
|
|
315
295
|
let anyListenersExecuted = false
|
|
316
|
-
|
|
317
|
-
async.detectSeries(this.listeners, (listener, done) => {
|
|
296
|
+
for await (const listener of this.listeners) {
|
|
318
297
|
try {
|
|
319
|
-
listener.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
)
|
|
327
|
-
})
|
|
298
|
+
const match = listener.matcher(context.response.message)
|
|
299
|
+
if (!match) {
|
|
300
|
+
continue
|
|
301
|
+
}
|
|
302
|
+
const result = await listener.call(context.response.message, this.middleware.listener)
|
|
303
|
+
results.push(result)
|
|
304
|
+
anyListenersExecuted = true
|
|
328
305
|
} catch (err) {
|
|
329
|
-
this.emit('error', err,
|
|
330
|
-
// Continue to next listener when there is an error
|
|
331
|
-
done(null, false)
|
|
306
|
+
this.emit('error', err, context)
|
|
332
307
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
_ => {
|
|
336
|
-
// If no registered Listener matched the message
|
|
337
|
-
|
|
338
|
-
if (!(context.response.message instanceof Message.CatchAllMessage) && !anyListenersExecuted) {
|
|
339
|
-
this.logger.debug('No listeners executed; falling back to catch-all')
|
|
340
|
-
this.receive(new Message.CatchAllMessage(context.response.message), done)
|
|
341
|
-
} else {
|
|
342
|
-
if (done != null) {
|
|
343
|
-
process.nextTick(done)
|
|
344
|
-
}
|
|
308
|
+
if (context.response.message.done) {
|
|
309
|
+
break
|
|
345
310
|
}
|
|
346
|
-
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (!isCatchAllMessage(context.response.message) && !anyListenersExecuted) {
|
|
314
|
+
this.logger.debug('No listeners executed; falling back to catch-all')
|
|
315
|
+
try {
|
|
316
|
+
const result = await this.receive(new Message.CatchAllMessage(context.response.message))
|
|
317
|
+
results.push(result)
|
|
318
|
+
} catch (err) {
|
|
319
|
+
this.emit('error', err, context)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return results
|
|
347
324
|
}
|
|
348
325
|
|
|
349
326
|
async loadmjs (filePath) {
|
|
@@ -389,7 +366,7 @@ class Robot {
|
|
|
389
366
|
this.parseHelp(full)
|
|
390
367
|
} catch (error) {
|
|
391
368
|
this.logger.error(`Unable to load ${full}: ${error.stack}`)
|
|
392
|
-
|
|
369
|
+
throw error
|
|
393
370
|
}
|
|
394
371
|
}
|
|
395
372
|
|
|
@@ -424,7 +401,7 @@ class Robot {
|
|
|
424
401
|
Object.keys(packages).forEach(key => require(key)(this, packages[key]))
|
|
425
402
|
} catch (error) {
|
|
426
403
|
this.logger.error(`Error loading scripts from npm package - ${error.stack}`)
|
|
427
|
-
|
|
404
|
+
throw error
|
|
428
405
|
}
|
|
429
406
|
}
|
|
430
407
|
|
|
@@ -472,9 +449,8 @@ class Robot {
|
|
|
472
449
|
this.server = app.listen(port, address)
|
|
473
450
|
this.router = app
|
|
474
451
|
} catch (error) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
process.exit(1)
|
|
452
|
+
this.logger.error(`Error trying to start HTTP server: ${error}\n${error.stack}`)
|
|
453
|
+
throw error
|
|
478
454
|
}
|
|
479
455
|
|
|
480
456
|
let herokuUrl = process.env.HEROKU_URL
|
|
@@ -533,9 +509,9 @@ class Robot {
|
|
|
533
509
|
}
|
|
534
510
|
}
|
|
535
511
|
}
|
|
536
|
-
} catch (
|
|
537
|
-
this.logger.error(`Cannot load adapter ${adapterPath ?? '[no path set]'} ${this.adapterName} - ${
|
|
538
|
-
|
|
512
|
+
} catch (error) {
|
|
513
|
+
this.logger.error(`Cannot load adapter ${adapterPath ?? '[no path set]'} ${this.adapterName} - ${error}`)
|
|
514
|
+
throw error
|
|
539
515
|
}
|
|
540
516
|
}
|
|
541
517
|
|
|
@@ -616,10 +592,8 @@ class Robot {
|
|
|
616
592
|
// strings - One or more Strings for each message to send.
|
|
617
593
|
//
|
|
618
594
|
// Returns whatever the extending adapter returns.
|
|
619
|
-
send (envelope
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return this.adapter.send.apply(this.adapter, [envelope].concat(strings))
|
|
595
|
+
async send (envelope, ...strings) {
|
|
596
|
+
return await this.adapter.send(envelope, ...strings)
|
|
623
597
|
}
|
|
624
598
|
|
|
625
599
|
// Public: A helper reply function which delegates to the adapter's reply
|
|
@@ -629,10 +603,8 @@ class Robot {
|
|
|
629
603
|
// strings - One or more Strings for each message to send.
|
|
630
604
|
//
|
|
631
605
|
// Returns whatever the extending adapter returns.
|
|
632
|
-
reply (envelope
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return this.adapter.reply.apply(this.adapter, [envelope].concat(strings))
|
|
606
|
+
async reply (envelope, ...strings) {
|
|
607
|
+
return await this.adapter.reply(envelope, ...strings)
|
|
636
608
|
}
|
|
637
609
|
|
|
638
610
|
// Public: A helper send function to message a room that the robot is in.
|
|
@@ -641,11 +613,9 @@ class Robot {
|
|
|
641
613
|
// strings - One or more Strings for each message to send.
|
|
642
614
|
//
|
|
643
615
|
// Returns whatever the extending adapter returns.
|
|
644
|
-
messageRoom (room
|
|
645
|
-
const strings = [].slice.call(arguments, 1)
|
|
616
|
+
async messageRoom (room, ...strings) {
|
|
646
617
|
const envelope = { room }
|
|
647
|
-
|
|
648
|
-
return this.adapter.send.apply(this.adapter, [envelope].concat(strings))
|
|
618
|
+
return await this.adapter.send(envelope, ...strings)
|
|
649
619
|
}
|
|
650
620
|
|
|
651
621
|
// Public: A wrapper around the EventEmitter API to make usage
|
|
@@ -656,10 +626,8 @@ class Robot {
|
|
|
656
626
|
// when event happens.
|
|
657
627
|
//
|
|
658
628
|
// Returns nothing.
|
|
659
|
-
on (event
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
this.events.on.apply(this.events, [event].concat(args))
|
|
629
|
+
on (event, ...args) {
|
|
630
|
+
this.events.on(event, ...args)
|
|
663
631
|
}
|
|
664
632
|
|
|
665
633
|
// Public: A wrapper around the EventEmitter API to make usage
|
|
@@ -669,19 +637,17 @@ class Robot {
|
|
|
669
637
|
// args... - Arguments emitted by the event
|
|
670
638
|
//
|
|
671
639
|
// Returns nothing.
|
|
672
|
-
emit (event
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
this.events.emit.apply(this.events, [event].concat(args))
|
|
640
|
+
emit (event, ...args) {
|
|
641
|
+
this.events.emit(event, ...args)
|
|
676
642
|
}
|
|
677
643
|
|
|
678
644
|
// Public: Kick off the event loop for the adapter
|
|
679
645
|
//
|
|
680
|
-
// Returns
|
|
681
|
-
run () {
|
|
646
|
+
// Returns whatever the adapter returns.
|
|
647
|
+
async run () {
|
|
682
648
|
this.emit('running')
|
|
683
649
|
|
|
684
|
-
this.adapter.run()
|
|
650
|
+
return await this.adapter.run()
|
|
685
651
|
}
|
|
686
652
|
|
|
687
653
|
// Public: Gracefully shutdown the robot process
|
|
@@ -691,13 +657,12 @@ class Robot {
|
|
|
691
657
|
if (this.pingIntervalId != null) {
|
|
692
658
|
clearInterval(this.pingIntervalId)
|
|
693
659
|
}
|
|
694
|
-
|
|
695
|
-
this.adapter.close()
|
|
660
|
+
this.adapter?.close()
|
|
696
661
|
if (this.server) {
|
|
697
662
|
this.server.close()
|
|
698
663
|
}
|
|
699
|
-
|
|
700
664
|
this.brain.close()
|
|
665
|
+
this.events.removeAllListeners()
|
|
701
666
|
}
|
|
702
667
|
|
|
703
668
|
// Public: The version of Hubot from npm
|