UNPKG

4.5 kBJavaScriptView Raw
1'use strict'
2
3const inspect = require('util').inspect
4
5const TextMessage = require('./message').TextMessage
6const Middleware = require('./middleware')
7
8class Listener {
9 // Listeners receive every message from the chat source and decide if they
10 // want to act on it.
11 // An identifier should be provided in the options parameter to uniquely
12 // identify the listener (options.id).
13 //
14 // robot - A Robot instance.
15 // matcher - A Function that determines if this listener should trigger the
16 // callback.
17 // options - An Object of additional parameters keyed on extension name
18 // (optional).
19 // callback - A Function that is triggered if the incoming message matches.
20 constructor (robot, matcher, options, callback) {
21 this.robot = robot
22 this.matcher = matcher
23 this.options = options
24 this.callback = callback
25
26 if (this.matcher == null) {
27 throw new Error('Missing a matcher for Listener')
28 }
29
30 if (this.callback == null) {
31 this.callback = this.options
32 this.options = {}
33 }
34
35 if (this.options.id == null) {
36 this.options.id = null
37 }
38
39 if (this.callback == null || typeof this.callback !== 'function') {
40 throw new Error('Missing a callback for Listener')
41 }
42 }
43
44 // Public: Determines if the listener likes the content of the message. If
45 // so, a Response built from the given Message is passed through all
46 // registered middleware and potentially the Listener callback. Note that
47 // middleware can intercept the message and prevent the callback from ever
48 // being executed.
49 //
50 // message - A Message instance.
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 //
54 // Returns a boolean of whether the matcher matched.
55 // Returns before executing callback
56 call (message, middleware, didMatchCallback) {
57 // middleware argument is optional
58 if (didMatchCallback == null && typeof middleware === 'function') {
59 didMatchCallback = middleware
60 middleware = undefined
61 }
62
63 // ensure we have a Middleware object
64 if (middleware == null) {
65 middleware = new Middleware(this.robot)
66 }
67
68 const match = this.matcher(message)
69 if (match) {
70 if (this.regex) {
71 this.robot.logger.debug(`Message '${message}' matched regex /${inspect(this.regex)}/; listener.options = ${inspect(this.options)}`)
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 }
85
86 // When everything is finished (down the middleware stack and back up),
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 }
95
96 const response = new this.robot.Response(this.robot, message, match)
97 middleware.execute({ listener: this, response }, executeListener, allDone)
98 return true
99 } else {
100 if (didMatchCallback != null) {
101 // No, we didn't try to execute the listener callback
102 process.nextTick(() => didMatchCallback(false))
103 }
104 return false
105 }
106 }
107}
108
109class TextListener extends Listener {
110 // TextListeners receive every message from the chat source and decide if they
111 // want to act on it.
112 //
113 // robot - A Robot instance.
114 // regex - A Regex that determines if this listener should trigger the
115 // callback.
116 // options - An Object of additional parameters keyed on extension name
117 // (optional).
118 // callback - A Function that is triggered if the incoming message matches.
119 constructor (robot, regex, options, callback) {
120 function matcher (message) {
121 if (message instanceof TextMessage) {
122 return message.match(regex)
123 }
124 }
125
126 super(robot, matcher, options, callback)
127 this.regex = regex
128 }
129}
130
131module.exports = {
132 Listener,
133 TextListener
134}