Migrate from simple-xmpp to xmpp/client

This commit is contained in:
nioc 2019-11-21 01:44:43 +01:00
parent 36d3c30a0e
commit 48046dbae2
13 changed files with 588 additions and 227 deletions

View file

@ -12,7 +12,6 @@ User ⇄ XMPP client ⇄ XMPP Server ⇄ **XMPP Bot** ⇄ REST A
## Key features ## Key features
- Call outgoing webhook on XMPP incoming messages from user chat or group chat (Multi-user chat "MUC"), - Call outgoing webhook on XMPP incoming messages from user chat or group chat (Multi-user chat "MUC"),
- Send message templates (with values to apply to variables in that template) to user or room (MUC) on incoming authorized (basic or bearer) webhook. - Send message templates (with values to apply to variables in that template) to user or room (MUC) on incoming authorized (basic or bearer) webhook.
## Installation ## Installation
@ -86,29 +85,24 @@ User ⇄ XMPP client ⇄ XMPP Server ⇄ **XMPP Bot** ⇄ REST A
### Logger ### Logger
- `level` log4js level (all < trace < debug < info < warn < error < fatal < mark < off) - `level` log4js level (all < trace < debug < info < warn < error < fatal < mark < off)
- `file`, `console` and `stdout` define log appenders (see [log4js doc](https://log4js-node.github.io/log4js-node/appenders.html)) - `file`, `console` and `stdout` define log appenders (see [log4js doc](https://log4js-node.github.io/log4js-node/appenders.html))
### Webhooks listener ### Webhooks listener
- `path` and `port` define the listening endpoint - `path` and `port` define the listening endpoint
- `ssl` define key and certificat location and port used for exposing in https, make sure that user of the process is allowed to read cert - `ssl` define key and certificat location and port used for exposing in https, make sure that user of the process is allowed to read cert
- `users` is an array of user/password for basic authentication - `users` is an array of user/password for basic authentication
- `accessLog` define the listener logger - `accessLog` define the listener logger
### XMPP Server ### XMPP Server
- `host` and `port` define XMPP server - `service` and `domain` define XMPP server
- `jid` and `password` define XMPP "bot" user credentials - `username` and `password` define XMPP "bot" user credentials
- `rooms` list rooms (and optionnal password) where bot will listen - `rooms` list rooms (and optionnal password) where bot will listen
### Incoming webhooks (list) ### Incoming webhooks (list)
- `path` is the webhook key:a POST request on this path will trigger corresponding `action` - `path` is the webhook key:a POST request on this path will trigger corresponding `action`
- `action` among enumeration: - `action` among enumeration:
- `send_xmpp_message` will send message (`message` in request body) to `destination` (from request body) ; if `destination` is found in `config.xmppServer.rooms` array, message will send as a groupchat). Request exemple: - `send_xmpp_message` will send message (`message` in request body) to `destination` (from request body) ; if `destination` is found in `config.xmppServer.rooms` array, message will send as a groupchat). Request exemple:
@ -125,12 +119,11 @@ User &rlarr; XMPP client &rlarr; XMPP Server &rlarr; **XMPP Bot** &rlarr; REST A
} }
``` ```
- `send_xmpp_template` will send template with merged variables (using JMESPath) to `destination` (user or room if `sendToGroup` set to true) - `send_xmpp_template` will send template with merged variables (using JMESPath) to `destination` (user or room if `type` set to `chat` or `groupchat`)
### XMPP hooks (list) ### XMPP hooks (list)
- `room` is the XMPP hook key: an incoming groupchat (or chat) from this room (or this user) will trigger corresponding `action` - `room` is the XMPP hook key: an incoming groupchat (or chat) from this room (or this user) will trigger corresponding `action`
- `action` among enumeration: - `action` among enumeration:
- `outgoing_webhook` will execute a request to corresponding webhook with `args` as webhook code - `outgoing_webhook` will execute a request to corresponding webhook with `args` as webhook code

View file

@ -41,9 +41,9 @@
} }
}, },
"xmppServer": { "xmppServer": {
"host": "domain-xmpp.ltd", "service": "xmpps://domain-xmpp.ltd:5223",
"port": 5222, "domain": "domain-xmpp.ltd",
"jid": "bot@domain-xmpp.ltd", "username": "bot@domain-xmpp.ltd",
"password": "botPass", "password": "botPass",
"rooms": [ "rooms": [
{ {
@ -62,14 +62,14 @@
"action": "send_xmpp_template", "action": "send_xmpp_template",
"args": { "args": {
"destination": "grafana@conference.domain-xmpp.ltd", "destination": "grafana@conference.domain-xmpp.ltd",
"sendToGroup": true "type": "groupchat"
}, },
"template": "${title}\r\n${message}\r\n${evalMatches[].metric}: ${evalMatches[].value}\r\n${imageUrl}" "template": "${title}\r\n${message}\r\n${evalMatches[].metric}: ${evalMatches[].value}\r\n${imageUrl}"
} }
], ],
"xmppHooks": [ "xmppHooks": [
{ {
"room": "bot", "room": "bot@domain-xmpp.ltd",
"action": "outgoing_webhook", "action": "outgoing_webhook",
"args": ["w1"] "args": ["w1"]
}, },

View file

@ -14,7 +14,11 @@ module.exports = (logger, xmpp) => {
nodeCleanup(function (exitCode, signal) { nodeCleanup(function (exitCode, signal) {
logger.warn(`Received ${exitCode}/${signal} (application is closing), disconnect from XMPP server`) logger.warn(`Received ${exitCode}/${signal} (application is closing), disconnect from XMPP server`)
try { try {
xmpp.disconnect() xmpp.close()
.then(logger.debug('Connection successfully closed'))
.catch((error) => {
logger.error('Error during XMPP disconnection', error)
})
} catch (error) { } catch (error) {
logger.error('Error during XMPP disconnection: ' + error.message) logger.error('Error during XMPP disconnection: ' + error.message)
} }

View file

@ -9,7 +9,7 @@
* @license AGPL-3.0+ * @license AGPL-3.0+
*/ */
module.exports = (logger, config, xmpp, user, destination, message, sendToGroup, code, callback = () => {}) => { module.exports = (logger, config, xmpp, user, destination, message, type, code, callback = () => {}) => {
let webhook = config.getOutgoingWebhook(code) let webhook = config.getOutgoingWebhook(code)
if (!webhook) { if (!webhook) {
logger.warn(`There is no webhook with code "${code}"`) logger.warn(`There is no webhook with code "${code}"`)
@ -79,7 +79,7 @@ module.exports = (logger, config, xmpp, user, destination, message, sendToGroup,
logger.trace('Response:', body) logger.trace('Response:', body)
if (body && typeof (body) === 'object' && 'reply' in body === true) { if (body && typeof (body) === 'object' && 'reply' in body === true) {
logger.debug(`There is a reply to send back in chat ${destination}: ${body.reply}`) logger.debug(`There is a reply to send back in chat ${destination}: ${body.reply}`)
xmpp.send(destination, body.reply, sendToGroup) xmpp.send(destination, body.reply, type)
callback(null, `Message sent. There is a reply to send back in chat ${destination}: ${body.reply}`, null) callback(null, `Message sent. There is a reply to send back in chat ${destination}: ${body.reply}`, null)
return return
} }

View file

@ -82,11 +82,11 @@ module.exports = (logger, config, xmpp) => {
let message = req.body.message let message = req.body.message
// check if destination is a group chat // check if destination is a group chat
const sendToGroup = config.xmpp.rooms.some((room) => room.id === destination) const type = config.xmpp.rooms.some((room) => room.id === destination) ? 'groupchat' : 'chat'
// send message // send message
logger.trace(`Send to ${destination} (group:${sendToGroup}) following message :\r\n${message}`) logger.trace(`Send to ${destination} (group:${type}) following message :\r\n${message}`)
xmpp.send(destination, message, sendToGroup) xmpp.send(destination, message, type)
return res.status(200).send({ 'status': 'ok', destination }) return res.status(200).send({ 'status': 'ok', destination })
case 'send_xmpp_template': case 'send_xmpp_template':
@ -99,8 +99,8 @@ module.exports = (logger, config, xmpp) => {
logger.trace(`Arguments: ${webhook.args}`) logger.trace(`Arguments: ${webhook.args}`)
// send message // send message
logger.trace(`Send to ${webhook.args.destination} (group:${webhook.args.sendToGroup}) following message :\r\n${msg}`) logger.trace(`Send to ${webhook.args.destination} (group:${webhook.args.type}) following message :\r\n${msg}`)
xmpp.send(webhook.args.destination, msg, webhook.args.sendToGroup) xmpp.send(webhook.args.destination, msg, webhook.args.type)
return res.status(200).send('ok') return res.status(200).send('ok')
default: default:

View file

@ -6,81 +6,140 @@
* @exports xmpp * @exports xmpp
* @file This files defines the XMPP bot * @file This files defines the XMPP bot
* @author nioc * @author nioc
* @since 1.0.0 * @since 2.0.0
* @license AGPL-3.0+ * @license AGPL-3.0+
*/ */
module.exports = (logger, config) => { module.exports = (logger, config) => {
const xmpp = require('simple-xmpp') const { client, xml, jid } = require('@xmpp/client')
const outgoing = require('../outgoing') const outgoing = require('../outgoing')
this.jid = null this.jid = null
// declare send chat/groupchat function
this.send = async (to, message, type) => {
logger.debug(`Send ${type} message to ${to}: '${message}'`)
const stanza = xml(
'message', {
to,
type
},
xml(
'body', {
},
message)
)
await xmppClient.send(stanza)
}
// declare close function
this.close = async () => {
await xmppClient.stop()
}
// create XMPP client
const xmppClient = client(config.xmpp)
// handle connection // handle connection
xmpp.on('online', function (data) { xmppClient.on('online', (address) => {
logger.info(`XMPP connected on ${config.xmpp.host}:${config.xmpp.port} with JID: ${data.jid.user}`) logger.info(`XMPP connected on ${config.xmpp.service} with JID: ${address.toString()}`)
this.jid = data.jid.user this.jid = address
// send presence
xmppClient.send(xml('presence'))
.then(logger.debug('presence sent'))
.catch((error) => {
logger.warn('presence returned following error:', error)
})
// join rooms // join rooms
config.xmpp.rooms.forEach(function (room) { config.xmpp.rooms.forEach(function (room) {
logger.debug(`Join room: ${room.id} ('${room.id}/${data.jid.user}')`) let occupantJid = room.id + '/' + address.local
xmpp.join(room.id + '/' + data.jid.user, room.password) logger.debug(`Join room: ${room.id} ('${occupantJid}')`)
const stanza = xml(
'presence', {
to: occupantJid
},
xml(
'x', {
xmlns: 'http://jabber.org/protocol/muc'
}
)
)
xmppClient.send(stanza)
logger.info(`Joined room: ${room.id}`) logger.info(`Joined room: ${room.id}`)
}) })
}) })
// handle direct message // handle stanzas
xmpp.on('chat', function (from, message) { xmppClient.on('stanza', stanza => {
logger.info(`Incoming chat message from ${from}`) if (!stanza.is('message')) {
// not a message, do nothing
return
}
let type = stanza.attrs.type
switch (type) {
case 'chat':
case 'groupchat':
let body = stanza.getChild('body')
if (!body) {
// empty body, do nothing
return
}
let fromJid = jid(stanza.attrs.from)
// for chat, "to" and "replyTo" must be something like "user@domain.ltd", "from" is local part "user"
let to = this.jid.bare()
let from = fromJid.local
let replyTo = fromJid.bare()
if (type === 'groupchat') {
// for groupchat, "to" and "replyTo" is conference name, "from" is nickname
to = fromJid.bare()
from = fromJid.getResource()
replyTo = to
if (from === this.jid.local || stanza.getChild('delay')) {
// message from bot or old message, do nothing
return
}
}
let message = body.text()
logger.info(`Incoming ${type} message from ${from} (${fromJid.toString()}) to ${to}`)
logger.debug(`Message: "${message}"`) logger.debug(`Message: "${message}"`)
let xmppHook = config.getXmppHookAction(this.jid) let xmppHook = config.getXmppHookAction(to.toString())
if (!xmppHook) { if (!xmppHook) {
logger.error(`There is no action for incoming chat message to ${this.jid}`) logger.error(`There is no action for incoming ${type} message to: "${to}"`)
return return
} }
switch (xmppHook.action) { switch (xmppHook.action) {
case 'outgoing_webhook': case 'outgoing_webhook':
logger.debug(`Call outgoing webhook: ${xmppHook.args[0]}`) logger.debug(`Call outgoing webhook: ${xmppHook.args[0]}`)
outgoing(logger, config, xmpp, from, from, message, false, xmppHook.args[0]) outgoing(logger, config, this, from.toString(), replyTo.toString(), message, type, xmppHook.args[0])
break break
default: default:
break break
} }
break
}
}) })
// handle group message // handle status
xmpp.on('groupchat', function (conference, from, message, stamp, delay) { xmppClient.on('status', (status) => {
// logger.trace(`Get following group message: "${message}" in ${conference} from ${from}. stamp = ${stamp} - delay = `, delay) logger.trace(`Status changed to ${status}`)
if (from === this.jid || stamp !== null) {
// message from bot, do nothing
return
}
logger.info(`Incoming groupchat message from ${from} in ${conference}`)
logger.debug(`Message: "${message}"`)
let xmppHook = config.getXmppHookAction(conference)
if (!xmppHook) {
logger.error(`There is no action for incoming groupchat message from conference: "${conference}"`)
return
}
switch (xmppHook.action) {
case 'outgoing_webhook':
logger.debug(`Call outgoing webhook: ${xmppHook.args[0]}`)
outgoing(logger, config, xmpp, from, conference, message, true, xmppHook.args[0])
break
default:
break
}
}) })
// trace input/output
// xmppClient.on('input', (input) => {
// logger.trace('<<<<', input)
// })
// xmppClient.on('output', (output) => {
// logger.trace('>>>', output)
// })
// handle error // handle error
xmpp.on('error', function (err) { xmppClient.on('error', (err) => {
logger.error(err) logger.error('XMPP client encountered following error:', err.message)
process.exit(99) process.exit(99)
}) })
// connect // connect
xmpp.connect(config.xmpp) xmppClient.start()
.catch(logger.error)
// get roster return this
xmpp.getRoster()
return xmpp
} }

446
package-lock.json generated
View file

@ -163,28 +163,219 @@
"integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
"dev": true "dev": true
}, },
"@xmpp/jid": { "@xmpp/client": {
"version": "0.0.2", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/jid/-/jid-0.0.2.tgz", "resolved": "https://registry.npmjs.org/@xmpp/client/-/client-0.8.0.tgz",
"integrity": "sha1-DVKMqdWNr8gzZlVk/+YvMyoxZ/I=" "integrity": "sha512-PaQt1D18fmpLk6UGqpCvuy0eTsWASMgowS8IAsUbDZV0bTz0fV0CjVe1tHVEy45O2myIadk14UX88onSmsRvtQ==",
},
"@xmpp/streamparser": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@xmpp/streamparser/-/streamparser-0.0.6.tgz",
"integrity": "sha1-EYAz6p23yGoctGED8mnr/3n28eo=",
"requires": { "requires": {
"@xmpp/xml": "^0.1.3", "@xmpp/client-core": "^0.8.0",
"inherits": "^2.0.3", "@xmpp/iq": "^0.8.0",
"ltx": "^2.5.0" "@xmpp/middleware": "^0.8.0",
"@xmpp/reconnect": "^0.8.0",
"@xmpp/resolve": "^0.8.0",
"@xmpp/resource-binding": "^0.8.0",
"@xmpp/sasl": "^0.8.0",
"@xmpp/sasl-anonymous": "^0.8.0",
"@xmpp/sasl-plain": "^0.8.0",
"@xmpp/sasl-scram-sha-1": "^0.8.0",
"@xmpp/session-establishment": "^0.8.0",
"@xmpp/starttls": "^0.8.0",
"@xmpp/stream-features": "^0.8.0",
"@xmpp/tcp": "^0.8.0",
"@xmpp/tls": "^0.8.0",
"@xmpp/websocket": "^0.8.0"
}
},
"@xmpp/client-core": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/client-core/-/client-core-0.8.0.tgz",
"integrity": "sha512-RL7EDhQPLkWtX75p4UyAuyxsnBsOzYg+vq5Lkyo0CAaO6D5Zk7sqGlpYUHFsUp2akDDOjgsDZZuhdzJPtV4mCg==",
"requires": {
"@xmpp/connection": "^0.8.0",
"@xmpp/jid": "^0.8.0",
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/connection": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/connection/-/connection-0.8.0.tgz",
"integrity": "sha512-8KqdSP0jYnnugvZeEydO8L/Geygj/tEJAFSrBqWX0aNjgMDvoKhZsdvY0gk4o8pvXZCzQN7K8r8jvPoPpxAnPA==",
"requires": {
"@xmpp/error": "^0.8.0",
"@xmpp/events": "^0.8.0",
"@xmpp/jid": "^0.8.0",
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/connection-tcp": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/connection-tcp/-/connection-tcp-0.8.0.tgz",
"integrity": "sha512-gyT05MmrynqRe98G1WKYmHLginIAXPlU0atuvHJ/kultccQazZ2sXV7Ci43Qpv/UBNLCTCYu5Fq1UXWs4jwUfA==",
"requires": {
"@xmpp/connection": "^0.8.0",
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/error": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/error/-/error-0.8.0.tgz",
"integrity": "sha512-xs5eZg5gDckLFlfnIxzFdlUAAAMnWdGHNQkBGar+W93Z4BCVeFh6Z64xtJW6tv0ip+8oY4HB1mqiCerump5ZSA=="
},
"@xmpp/events": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/events/-/events-0.8.0.tgz",
"integrity": "sha512-ZEdIOUVsDqfCVG2Y3crUDYLAedBW+6ND6LCkwRyaWlOtDAPQiPzJGGFx9upcoyWtIoe1byh/JOjv11RcqQhKYQ==",
"requires": {
"events": "^3.0.0"
}
},
"@xmpp/id": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/id/-/id-0.8.0.tgz",
"integrity": "sha512-kar4sAz3tFu5oSChjAhSX7TI6YoSIH9VWqIJLn93qtq/4hZc+nuIkNfYhdKS9ABesnFmqEE/ncZ1Kh2QTNEzOQ=="
},
"@xmpp/iq": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/iq/-/iq-0.8.0.tgz",
"integrity": "sha512-vZzb7LvgGL4AvalBmBl0iNZuoO7pGUqir7gbueSDytNpPMwLI1dyziuzVwh9/RStEtF1BYQuC3lW/rvGB9Itog==",
"requires": {
"@xmpp/events": "^0.8.0",
"@xmpp/id": "^0.8.0",
"@xmpp/middleware": "^0.8.0",
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/jid": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/jid/-/jid-0.8.0.tgz",
"integrity": "sha512-Pwwqmz3zG/MMPLuWkVlNk54Wrh4kVa651//uQWkzyPYdzlXFt/nwC14PvSP16kGypI+J7xBaQEzqbzyu8Dve5g=="
},
"@xmpp/middleware": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/middleware/-/middleware-0.8.0.tgz",
"integrity": "sha512-7IlFXCcya9SDKobDtDLgAvtnPJZYoGOgzW9lEIknz9sDMKqh2mwl+64G3Vs8h7hSBOp+MFVrQV+2q23ETKEnMw==",
"requires": {
"@xmpp/error": "^0.8.0",
"@xmpp/jid": "^0.8.0",
"@xmpp/xml": "^0.8.0",
"koa-compose": "^4.1.0"
}
},
"@xmpp/reconnect": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/reconnect/-/reconnect-0.8.0.tgz",
"integrity": "sha512-nDam7dUk+wYOuFIj8logdL6vBmmVJBCY7DczIhg3YXtBy8VaceMvPJl13EEG/OoNzm7ZmxzDjfp3gIul8fY+xQ==",
"requires": {
"@xmpp/events": "^0.8.0"
}
},
"@xmpp/resolve": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/resolve/-/resolve-0.8.0.tgz",
"integrity": "sha512-L2jX6gOtAh8hGF19f6VI3HaRHrQ4Vy4+8YUWO8sIUpx7YdIg4L9dt2ZSj6CuFHwok1h/Kbtil0v8bHnCEuZDCQ==",
"requires": {
"@xmpp/connection": "^0.8.0",
"@xmpp/xml": "^0.8.0",
"node-fetch": "^2.3.0"
}
},
"@xmpp/resource-binding": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/resource-binding/-/resource-binding-0.8.0.tgz",
"integrity": "sha512-4sbmBIuIAC5mOJoHjiOpsuEyro23LB4I5u6F8n9OE1sRW0l1LdiwtaSq3Qzuc0YIWF1CWF9CjCboxWdyjD5EVA==",
"requires": {
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/sasl": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/sasl/-/sasl-0.8.0.tgz",
"integrity": "sha512-fVlzuTJnC+0rBnJ7HnlqYayl0ZvfR+q2yxh6VfqUPZw0eV4zwKy15eoU8txiLbWT/ilm4bo2zid7CCOYGPd5zw==",
"requires": {
"@xmpp/error": "^0.8.0",
"@xmpp/xml": "^0.8.0",
"js-base64": "^2.5.1",
"saslmechanisms": "^0.1.1"
}
},
"@xmpp/sasl-anonymous": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/sasl-anonymous/-/sasl-anonymous-0.8.0.tgz",
"integrity": "sha512-2V+pmreLdFvY0dX8XTliSqC/CyToYyDstKUAD637juf0Mjqy7UmGgCLQQ2hH/t5J1wWxfeHgxk16zdjj7WyUkg==",
"requires": {
"sasl-anonymous": "^0.1.0"
}
},
"@xmpp/sasl-plain": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/sasl-plain/-/sasl-plain-0.8.0.tgz",
"integrity": "sha512-IJFCf6YcVCp+ZWVIFzpM0wJGnbjTwbjY+CO+5KSBsIrh94uNT1cNlqs+9aiT3jqf8UbWy1D08+6mi7IWxytR4g==",
"requires": {
"sasl-plain": "^0.1.0"
}
},
"@xmpp/sasl-scram-sha-1": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/sasl-scram-sha-1/-/sasl-scram-sha-1-0.8.0.tgz",
"integrity": "sha512-PrpMEdnIPAjSmEOukB2oOksoWrbV/zfwPNIDKvTW8a73rcRxGVvx0032q1IXQYjxrogSL8DyVmuKvpFwUpvEnw==",
"requires": {
"sasl-scram-sha-1": "^1.2.0"
}
},
"@xmpp/session-establishment": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/session-establishment/-/session-establishment-0.8.0.tgz",
"integrity": "sha512-lvkcszV22/xZp4ZDTGiHf67xXJJ0TQMKpgUO2Amzyufc3qXJ4gP5nfGTNslI+LS3DjrgisRWiK8OTvjLGdIL5A==",
"requires": {
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/starttls": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/starttls/-/starttls-0.8.0.tgz",
"integrity": "sha512-mj4Es4wugcDtfTaGjOZey3eI1JI5WMedjP7ahVewfKUOaxbpeojzCjqazXf9/La3rVTamn5qa1MJ2kEo4FxWMw==",
"requires": {
"@xmpp/xml": "^0.8.0"
}
},
"@xmpp/stream-features": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/stream-features/-/stream-features-0.8.0.tgz",
"integrity": "sha512-3lzWxlPejgFMSjFzSKhAIsY/ub1j5tLPFMvLFTGiXlrArRg/rx4kYITuyOxaQDA9Dty5xP4sBqA4vf4jezKt9w=="
},
"@xmpp/tcp": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/tcp/-/tcp-0.8.0.tgz",
"integrity": "sha512-ZGkAHrFUEbq66dtSSYl9L2xppn/1qFrUM4fGpLwWaHQwnNxwIZwJGlDTMowszkZ3/J6ZK1YNViLq1htWWv80Ig==",
"requires": {
"@xmpp/connection-tcp": "^0.8.0"
}
},
"@xmpp/tls": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/tls/-/tls-0.8.0.tgz",
"integrity": "sha512-Vn3YJCclOBWeNjZZPb0Bl0SjbamK8N0UiLic1kdHh8MS7zC+kbbglFKa/ZppF4tqpP5VoycRrEeYHieyz+UMLA==",
"requires": {
"@xmpp/connection": "^0.8.0",
"@xmpp/connection-tcp": "^0.8.0"
}
},
"@xmpp/websocket": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/websocket/-/websocket-0.8.0.tgz",
"integrity": "sha512-SQMS0IdxNsv/6ywP9G9EZa9lLraJd2KaNnPgjK4yZ/BMavfr38+gvOaX3KIhQC5uV87bV91E9p+T97p5BW0Glw==",
"requires": {
"@xmpp/connection": "^0.8.0",
"@xmpp/xml": "^0.8.0",
"ws": "^7.0.0"
} }
}, },
"@xmpp/xml": { "@xmpp/xml": {
"version": "0.1.3", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/@xmpp/xml/-/xml-0.1.3.tgz", "resolved": "https://registry.npmjs.org/@xmpp/xml/-/xml-0.8.0.tgz",
"integrity": "sha1-HxQ5nlPkGWiFWGmPbGLnHjmoam4=", "integrity": "sha512-W0bW5TCMPi2dtndZhzeTzExtz1skXt/fPQFZ9aXg4Y/pniUVo//vfUY4092C8smT7AqiI8jYVEIYh4xqmd2LDQ==",
"requires": { "requires": {
"inherits": "^2.0.3", "ltx": "^2.8.1"
"ltx": "^2.6.2"
} }
}, },
"abbrev": { "abbrev": {
@ -426,6 +617,11 @@
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
"dev": true "dev": true
}, },
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -447,11 +643,6 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
}, },
"backoff": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.3.0.tgz",
"integrity": "sha1-7nx+OAk/kuRyhZ22NedlJFT8Ieo="
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@ -535,6 +726,11 @@
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
"dev": true "dev": true
}, },
"bitwise-xor": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/bitwise-xor/-/bitwise-xor-0.0.0.tgz",
"integrity": "sha1-BAqBcrW7jMVisLcRnyMLKhp4Dj0="
},
"body-parser": { "body-parser": {
"version": "1.19.0", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
@ -633,11 +829,6 @@
} }
} }
}, },
"browser-request": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz",
"integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc="
},
"browser-stdout": { "browser-stdout": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
@ -811,6 +1002,15 @@
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
"dev": true "dev": true
}, },
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
"class-utils": { "class-utils": {
"version": "0.3.6", "version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@ -1060,6 +1260,31 @@
"capture-stack-trace": "^1.0.0" "capture-stack-trace": "^1.0.0"
} }
}, },
"create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"requires": {
"cipher-base": "^1.0.1",
"inherits": "^2.0.1",
"md5.js": "^1.3.4",
"ripemd160": "^2.0.1",
"sha.js": "^2.4.0"
}
},
"create-hmac": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"requires": {
"cipher-base": "^1.0.3",
"create-hash": "^1.1.0",
"inherits": "^2.0.1",
"ripemd160": "^2.0.0",
"safe-buffer": "^5.0.1",
"sha.js": "^2.4.8"
}
},
"cross-spawn": { "cross-spawn": {
"version": "6.0.5", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@ -1574,6 +1799,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
}, },
"events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
"integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA=="
},
"execa": { "execa": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
@ -3381,6 +3611,11 @@
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
}, },
"js-base64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
"integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw=="
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -3466,6 +3701,11 @@
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true "dev": true
}, },
"koa-compose": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
"integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw=="
},
"latest-version": { "latest-version": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
@ -3518,11 +3758,6 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
}, },
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
},
"lodash.flattendeep": { "lodash.flattendeep": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
@ -3594,11 +3829,18 @@
} }
}, },
"ltx": { "ltx": {
"version": "2.8.1", "version": "2.9.2",
"resolved": "https://registry.npmjs.org/ltx/-/ltx-2.8.1.tgz", "resolved": "https://registry.npmjs.org/ltx/-/ltx-2.9.2.tgz",
"integrity": "sha512-l4H1FS9I6IVqwvIpUHsSgyxE6t2jP7qd/2MeVG1UhmVK6vlHsQpfm2KNUcbdImeE0ai04vl1qTCF4CPCJqhknQ==", "integrity": "sha512-llB7HflFhlfsYYT1SAe80elCBO5C20ryLdwPB/A/BZk38hhVeZztDlWQ9uTyvKNPX4aK6sA+JfS1f/mfzp5cxA==",
"requires": { "requires": {
"inherits": "^2.0.1" "inherits": "^2.0.4"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}
} }
}, },
"make-dir": { "make-dir": {
@ -3732,7 +3974,8 @@
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}, },
"mixin-deep": { "mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
@ -4081,39 +4324,10 @@
} }
} }
}, },
"node-xmpp-client": { "node-fetch": {
"version": "3.2.0", "version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-xmpp-client/-/node-xmpp-client-3.2.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha1-r0Un3wzFq9JpDLohOcwezcgeoYk=", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
"requires": {
"browser-request": "^0.3.3",
"debug": "^2.2.0",
"md5.js": "^1.3.3",
"minimist": "^1.2.0",
"node-xmpp-core": "^5.0.9",
"request": "^2.65.0",
"ws": "^1.1.1"
}
},
"node-xmpp-core": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/node-xmpp-core/-/node-xmpp-core-5.0.9.tgz",
"integrity": "sha1-XCjCjtsfs/i+uixnYHd2E/SPNCo=",
"requires": {
"@xmpp/jid": "^0.0.2",
"@xmpp/streamparser": "^0.0.6",
"@xmpp/xml": "^0.1.3",
"debug": "^2.2.0",
"inherits": "^2.0.1",
"lodash.assign": "^4.0.0",
"node-xmpp-tls-connect": "^1.0.1",
"reconnect-core": "https://github.com/dodo/reconnect-core/tarball/merged"
}
},
"node-xmpp-tls-connect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/node-xmpp-tls-connect/-/node-xmpp-tls-connect-1.0.1.tgz",
"integrity": "sha1-kazkOsJrE4hhsr5HjfnfGdYdxcM="
}, },
"nodemon": { "nodemon": {
"version": "1.19.4", "version": "1.19.4",
@ -4469,11 +4683,6 @@
"word-wrap": "~1.2.3" "word-wrap": "~1.2.3"
} }
}, },
"options": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
"integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
},
"os-homedir": { "os-homedir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@ -4720,16 +4929,19 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
}, },
"qbox": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/qbox/-/qbox-0.1.7.tgz",
"integrity": "sha1-6A8NxdCfhp2IghaMP2asjdKEDwI="
},
"qs": { "qs": {
"version": "6.7.0", "version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
}, },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"requires": {
"safe-buffer": "^5.1.0"
}
},
"range-parser": { "range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -4813,13 +5025,6 @@
"readable-stream": "^2.0.2" "readable-stream": "^2.0.2"
} }
}, },
"reconnect-core": {
"version": "https://github.com/dodo/reconnect-core/tarball/merged",
"integrity": "sha512-wZK/v5ZaNaSUs2Wnwh2YSX/Jqv6bQHKNEwojdzV11tByKziR9ikOssf5tvUhx+8/oCBz6AakOFAjZuqPoiRHJQ==",
"requires": {
"backoff": "~2.3.0"
}
},
"regex-not": { "regex-not": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@ -4979,6 +5184,15 @@
"glob": "^7.1.3" "glob": "^7.1.3"
} }
}, },
"ripemd160": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
"requires": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1"
}
},
"run-async": { "run-async": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@ -5016,6 +5230,32 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"sasl-anonymous": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/sasl-anonymous/-/sasl-anonymous-0.1.0.tgz",
"integrity": "sha1-9UTH6CTfKkDZrUczgpVyzI2e1aU="
},
"sasl-plain": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/sasl-plain/-/sasl-plain-0.1.0.tgz",
"integrity": "sha1-zxRefAIiK2TWDAgG2c0q5TgEJsw="
},
"sasl-scram-sha-1": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sasl-scram-sha-1/-/sasl-scram-sha-1-1.2.1.tgz",
"integrity": "sha1-2I1R/qoP8yDY6x1vx1ZXZT+dzUs=",
"requires": {
"bitwise-xor": "0.0.0",
"create-hash": "^1.1.0",
"create-hmac": "^1.1.3",
"randombytes": "^2.0.1"
}
},
"saslmechanisms": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/saslmechanisms/-/saslmechanisms-0.1.1.tgz",
"integrity": "sha1-R4vhQpUA/PqngL6IszQ87X0qkYI="
},
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@ -5111,6 +5351,15 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
}, },
"sha.js": {
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
"shebang-command": { "shebang-command": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -5132,15 +5381,6 @@
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true "dev": true
}, },
"simple-xmpp": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/simple-xmpp/-/simple-xmpp-1.3.1.tgz",
"integrity": "sha512-o0wGVlI8Q4o0qTz6Kylbo1QPOMVn+DA/vyHHZecqcQ+LK4ZWGe3wtRON9QjHAkSyxB36PoagmiUz4pHADau8Mw==",
"requires": {
"node-xmpp-client": "^3.0.0",
"qbox": "0.1.x"
}
},
"sinon": { "sinon": {
"version": "7.5.0", "version": "7.5.0",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz",
@ -5883,11 +6123,6 @@
} }
} }
}, },
"ultron": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
},
"undefsafe": { "undefsafe": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz",
@ -6224,12 +6459,11 @@
} }
}, },
"ws": { "ws": {
"version": "1.1.5", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz",
"integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==",
"requires": { "requires": {
"options": ">=0.0.5", "async-limiter": "^1.0.0"
"ultron": "1.0.x"
} }
}, },
"xdg-basedir": { "xdg-basedir": {

View file

@ -19,6 +19,7 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@xmpp/client": "^0.8.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"express": "^4.17.1", "express": "^4.17.1",
"express-basic-auth": "^1.2.0", "express-basic-auth": "^1.2.0",
@ -26,8 +27,7 @@
"log4js": "^4.5.1", "log4js": "^4.5.1",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"node-cleanup": "^2.1.2", "node-cleanup": "^2.1.2",
"request": "^2.88.0", "request": "^2.88.0"
"simple-xmpp": "^1.3.1"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.2.0", "chai": "^4.2.0",

View file

@ -41,9 +41,9 @@
} }
}, },
"xmppServer": { "xmppServer": {
"host": "domain-xmpp.ltd", "service": "xmpps://domain-xmpp.ltd:5223",
"port": 5222, "domain": "domain-xmpp.ltd",
"jid": "bot@domain-xmpp.ltd", "username": "bot@domain-xmpp.ltd",
"password": "botPass", "password": "botPass",
"rooms": [ "rooms": [
{ {
@ -62,7 +62,7 @@
"action": "send_xmpp_template", "action": "send_xmpp_template",
"args": { "args": {
"destination": "grafana@conference.domain-xmpp.ltd", "destination": "grafana@conference.domain-xmpp.ltd",
"sendToGroup": true "type": "groupchat"
}, },
"template": "${title}\r\n${message}\r\n${evalMatches[].metric}: ${evalMatches[].value}\r\n${imageUrl}" "template": "${title}\r\n${message}\r\n${evalMatches[].metric}: ${evalMatches[].value}\r\n${imageUrl}"
}, },
@ -73,7 +73,7 @@
], ],
"xmppHooks": [ "xmppHooks": [
{ {
"room": "bot", "room": "bot@domain-xmpp.ltd",
"action": "outgoing_webhook", "action": "outgoing_webhook",
"args": ["w1"] "args": ["w1"]
}, },

View file

@ -54,7 +54,7 @@ describe('Outgoing webhook component', () => {
describe('Unkwnow outgoing webhook', () => { describe('Unkwnow outgoing webhook', () => {
it('Should not execute request', (done) => { it('Should not execute request', (done) => {
Outgoing(logger, config, xmpp, 'user', 'destination', 'message', true, 'code', (error, response, body) => { Outgoing(logger, config, xmpp, 'user', 'destination', 'message', 'type', 'code', (error, response, body) => {
should.not.equal(error, null) should.not.equal(error, null)
sinon.assert.notCalled(reqSpy) sinon.assert.notCalled(reqSpy)
done() done()
@ -64,7 +64,7 @@ describe('Outgoing webhook component', () => {
describe('POST with basic authorization and JSON content-type and reply message to XMPP', () => { describe('POST with basic authorization and JSON content-type and reply message to XMPP', () => {
it('Should send basic authentication and JSON content-type in header and send an XMPP message', (done) => { it('Should send basic authentication and JSON content-type in header and send an XMPP message', (done) => {
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a first message', true, 'w1', (error, response, body) => { Outgoing(logger, config, xmpp, 'user', 'destination', 'This a first message', 'type', 'w1', (error, response, body) => {
should.equal(error, null) should.equal(error, null)
sinon.assert.calledOnce(reqSpy) sinon.assert.calledOnce(reqSpy)
const req = reqSpy.args[0][0] const req = reqSpy.args[0][0]
@ -78,7 +78,7 @@ describe('Outgoing webhook component', () => {
const xmppSendArgs = xmppSendStub.args[0] const xmppSendArgs = xmppSendStub.args[0]
xmppSendArgs[0].should.equal('destination') xmppSendArgs[0].should.equal('destination')
xmppSendArgs[1].should.equal('This is a reply') xmppSendArgs[1].should.equal('This is a reply')
xmppSendArgs[2].should.equal(true) xmppSendArgs[2].should.equal('type')
done() done()
}) })
}) })
@ -86,7 +86,7 @@ describe('Outgoing webhook component', () => {
describe('POST with bearer authorization and JSON content-type', () => { describe('POST with bearer authorization and JSON content-type', () => {
it('Should send basic authentication in header', (done) => { it('Should send basic authentication in header', (done) => {
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', true, 'w2', (error, response, body) => { Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', 'type', 'w2', (error, response, body) => {
should.equal(error, null) should.equal(error, null)
sinon.assert.calledOnce(reqSpy) sinon.assert.calledOnce(reqSpy)
const req = reqSpy.args[0][0] const req = reqSpy.args[0][0]
@ -101,7 +101,7 @@ describe('Outgoing webhook component', () => {
describe('POST without authorization', () => { describe('POST without authorization', () => {
it('Should not send authorization in header and handle 401', (done) => { it('Should not send authorization in header and handle 401', (done) => {
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', true, 'w3', (error, response, body) => { Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', 'type', 'w3', (error, response, body) => {
should.not.equal(error, null) should.not.equal(error, null)
sinon.assert.calledOnce(reqSpy) sinon.assert.calledOnce(reqSpy)
done() done()
@ -111,7 +111,7 @@ describe('Outgoing webhook component', () => {
describe('POST with HTTP error', () => { describe('POST with HTTP error', () => {
it('Should handle error', (done) => { it('Should handle error', (done) => {
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', true, 'w4', (error, response, body) => { Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', 'type', 'w4', (error, response, body) => {
should.not.equal(error, null) should.not.equal(error, null)
sinon.assert.calledOnce(reqSpy) sinon.assert.calledOnce(reqSpy)
done() done()

View file

@ -12,9 +12,10 @@ describe('Server', () => {
xmppStub = sinon.stub() xmppStub = sinon.stub()
webhookStub = sinon.stub() webhookStub = sinon.stub()
mock('./../lib/xmpp', () => { mock('./../lib/xmpp', () => {
let xmpp = {} this.send = () => {}
this.close = () => {}
xmppStub() xmppStub()
return xmpp return this
}) })
// mock webhook component // mock webhook component

View file

@ -138,7 +138,7 @@ describe('Webhook component', () => {
args.should.have.length(3) args.should.have.length(3)
args[0].should.equal(options.json.destination) args[0].should.equal(options.json.destination)
args[1].should.equal(options.json.message) args[1].should.equal(options.json.message)
args[2].should.equal(false) args[2].should.equal('chat')
done() done()
}) })
}) })
@ -165,7 +165,7 @@ describe('Webhook component', () => {
args.should.have.length(3) args.should.have.length(3)
args[0].should.equal('grafana@conference.domain-xmpp.ltd') args[0].should.equal('grafana@conference.domain-xmpp.ltd')
args[1].should.equal('This is a title\r\nThis is a message\r\nmetric: value\r\nhttps://domain.ltd:port/path/image') args[1].should.equal('This is a title\r\nThis is a message\r\nmetric: value\r\nhttps://domain.ltd:port/path/image')
args[2].should.equal(true) args[2].should.equal('groupchat')
done() done()
}) })
}) })

View file

@ -1,14 +1,15 @@
'use strict' 'use strict'
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production'
const should = require('chai').should()
const sinon = require('sinon') const sinon = require('sinon')
const EventEmitter = require('events').EventEmitter const EventEmitter = require('events').EventEmitter
const mock = require('mock-require') const mock = require('mock-require')
const xml = require('@xmpp/xml')
const jid = require('@xmpp/jid')
describe('XMPP component', () => { describe('XMPP component', () => {
const simpleXmppEvents = new EventEmitter() const simpleXmppEvents = new EventEmitter()
let logger, config, outgoingStub, xmppJoinStub let logger, config, outgoingStub, xmppSendStub
before('Setup', (done) => { before('Setup', (done) => {
// create default logger // create default logger
@ -20,15 +21,20 @@ describe('XMPP component', () => {
// update logger with configuration // update logger with configuration
logger.updateConfig(config.logger) logger.updateConfig(config.logger)
// mock simple-xmpp module // mock @xmpp/client module
xmppJoinStub = sinon.stub() xmppSendStub = sinon.stub().resolves()
mock('simple-xmpp', { mock('@xmpp/client', {
connect: () => {}, client: () => {
join: xmppJoinStub, this.start = async () => {}
on: (eventName, callback) => { this.stop = async () => {}
this.send = xmppSendStub
this.on = (eventName, callback) => {
simpleXmppEvents.on(eventName, callback) simpleXmppEvents.on(eventName, callback)
}
return this
}, },
getRoster: () => {} xml: require('@xmpp/xml'),
jid: require('@xmpp/jid')
}) })
// mock outgoing // mock outgoing
@ -50,19 +56,26 @@ describe('XMPP component', () => {
describe('Connect to XMPP server', () => { describe('Connect to XMPP server', () => {
it('Should connect to XMPP server and join rooms when application start', (done) => { it('Should connect to XMPP server and join rooms when application start', (done) => {
require('./../lib/xmpp')(logger, config) require('./../lib/xmpp')(logger, config)
simpleXmppEvents.emit('online', { jid: { user: 'bot' } }) simpleXmppEvents.emit('online', jid('bot@domain-xmpp.ltd/resource'))
sinon.assert.called(xmppJoinStub) sinon.assert.called(xmppSendStub)
// 1 "send" call for presence and n "send" calls for joining rooms
let roomsLength = config.xmpp.rooms.length let roomsLength = config.xmpp.rooms.length
sinon.assert.callCount(xmppJoinStub, roomsLength) sinon.assert.callCount(xmppSendStub, roomsLength + 1)
for (let index = 0; index < roomsLength; index++) { for (let index = 1; index < roomsLength + 1; index++) {
const args = xmppJoinStub.args[index] const args = xmppSendStub.args[index]
args.should.have.length(2) args.should.have.length(1)
args[0].should.equal(config.xmpp.rooms[index].id + '/' + 'bot') let occupantJid = config.xmpp.rooms[index - 1].id + '/' + 'bot'
if (config.xmpp.rooms[index].password === null) { const stanza = xml(
should.equal(args[1], null) 'presence', {
} else { to: occupantJid
args[1].should.equal(config.xmpp.rooms[index].password) },
xml(
'x', {
xmlns: 'http://jabber.org/protocol/muc'
} }
)
)
args[0].should.deep.equal(stanza)
} }
done() done()
}) })
@ -70,14 +83,24 @@ describe('XMPP component', () => {
describe('Bot receive a message from someone', () => { describe('Bot receive a message from someone', () => {
it('Should trigger outgoing webhook with valid arguments', (done) => { it('Should trigger outgoing webhook with valid arguments', (done) => {
simpleXmppEvents.emit('chat', 'someone', 'This is the message text') simpleXmppEvents.emit('stanza', xml(
'message', {
from: 'someone@domain-xmpp.ltd',
to: 'bot@domain-xmpp.ltd',
type: 'chat'
},
xml(
'body', {
},
'This is the message text')
))
sinon.assert.calledOnce(outgoingStub) sinon.assert.calledOnce(outgoingStub)
const args = outgoingStub.args[0] const args = outgoingStub.args[0]
args.should.have.length(8) args.should.have.length(8)
args[3].should.equal('someone') args[3].should.equal('someone')
args[4].should.equal('someone') args[4].should.equal('someone@domain-xmpp.ltd')
args[5].should.equal('This is the message text') args[5].should.equal('This is the message text')
args[6].should.equal(false) args[6].should.equal('chat')
args[7].should.equal('w1') args[7].should.equal('w1')
done() done()
}) })
@ -85,7 +108,17 @@ describe('XMPP component', () => {
describe('Bot receive a message from himself in a room', () => { describe('Bot receive a message from himself in a room', () => {
it('Should not trigger outgoing webhook', (done) => { it('Should not trigger outgoing webhook', (done) => {
simpleXmppEvents.emit('groupchat', 'roomname@conference.domain-xmpp.ltd', 'bot', 'This is the message text', null) simpleXmppEvents.emit('stanza', xml(
'message', {
from: 'roomname@conference.domain-xmpp.ltd/bot',
to: 'roomname@conference.domain-xmpp.ltd',
type: 'groupchat'
},
xml(
'body', {
},
'This is the message text')
))
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
@ -93,7 +126,17 @@ describe('XMPP component', () => {
describe('Bot receive a message in an unknown room', () => { describe('Bot receive a message in an unknown room', () => {
it('Should not trigger outgoing webhook', (done) => { it('Should not trigger outgoing webhook', (done) => {
simpleXmppEvents.emit('groupchat', 'unknownroomname@conference.domain-xmpp.ltd', 'someone', 'This is the message text', null) simpleXmppEvents.emit('stanza', xml(
'message', {
from: 'unknownroomname@conference.domain-xmpp.ltd/someone',
to: 'unknownroomname@conference.domain-xmpp.ltd',
type: 'groupchat'
},
xml(
'body', {
},
'This is the message text')
))
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
@ -102,6 +145,23 @@ describe('XMPP component', () => {
describe('Bot receive an old message in a room', () => { describe('Bot receive an old message in a room', () => {
it('Should not trigger outgoing webhook', (done) => { it('Should not trigger outgoing webhook', (done) => {
simpleXmppEvents.emit('groupchat', 'roomname@conference.domain-xmpp.ltd', 'someone', 'This is the message text', 'stamp') simpleXmppEvents.emit('groupchat', 'roomname@conference.domain-xmpp.ltd', 'someone', 'This is the message text', 'stamp')
simpleXmppEvents.emit('stanza', xml(
'message', {
from: 'roomname@conference.domain-xmpp.ltd/someone',
to: 'roomname@conference.domain-xmpp.ltd',
type: 'groupchat'
},
xml(
'body', {
},
'This is the message text'),
xml(
'delay', {
xmlns: 'urn:xmpp:delay',
from: 'roomname@conference.domain-xmpp.ltd'
},
'This is the message text')
))
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
@ -109,14 +169,24 @@ describe('XMPP component', () => {
describe('Bot receive a message in a room', () => { describe('Bot receive a message in a room', () => {
it('Should trigger outgoing webhook with valid arguments', (done) => { it('Should trigger outgoing webhook with valid arguments', (done) => {
simpleXmppEvents.emit('groupchat', 'roomname@conference.domain-xmpp.ltd', 'someone', 'This is the message text', null) simpleXmppEvents.emit('stanza', xml(
'message', {
from: 'roomname@conference.domain-xmpp.ltd/someone',
to: 'roomname@conference.domain-xmpp.ltd',
type: 'groupchat'
},
xml(
'body', {
},
'This is the message text')
))
sinon.assert.calledOnce(outgoingStub) sinon.assert.calledOnce(outgoingStub)
const args = outgoingStub.args[0] const args = outgoingStub.args[0]
args.should.have.length(8) args.should.have.length(8)
args[3].should.equal('someone') args[3].should.equal('someone')
args[4].should.equal('roomname@conference.domain-xmpp.ltd') args[4].should.equal('roomname@conference.domain-xmpp.ltd')
args[5].should.equal('This is the message text') args[5].should.equal('This is the message text')
args[6].should.equal(true) args[6].should.equal('groupchat')
args[7].should.equal('w1') args[7].should.equal('w1')
done() done()
}) })
@ -131,12 +201,12 @@ describe('XMPP component', () => {
}) })
it('Should log error and exit with 99 code', (done) => { it('Should log error and exit with 99 code', (done) => {
let error = 'This the error text' let error = 'This the error text'
simpleXmppEvents.emit('error', error) simpleXmppEvents.emit('error', new Error(error))
require('fs').readFile(config.logger.file.path + config.logger.file.filename, 'utf8', (err, data) => { require('fs').readFile(config.logger.file.path + config.logger.file.filename, 'utf8', (err, data) => {
if (err) { if (err) {
throw err throw err
} }
data.should.match(new RegExp(error + '\n$')) data.should.match(new RegExp('XMPP client encountered following error: ' + error + '\n$'))
sinon.assert.calledWith(process.exit, 99) sinon.assert.calledWith(process.exit, 99)
done() done()
}) })