mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
added media send/recieve, extended text messages send/recieve
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
node_modules
|
||||
auth_info.json
|
||||
test_pvt.js
|
||||
media_decode_tests.js
|
||||
|
||||
98
README.md
98
README.md
@@ -1,22 +1,23 @@
|
||||
# Baileys
|
||||
Reverse Engineered WhatsApp Web API in Node.js. Baileys does not require Selenium or any other browser to be interface with WhatsApp Web, it does so directly using WebSockets.
|
||||
|
||||
Reverse Engineered WhatsApp Web API in pure Node.js. Baileys does not require Selenium or any other browser to be interface with WhatsApp Web, it does so directly using a WebSocket.
|
||||
|
||||
Thank you to [Sigalor](https://github.com/sigalor/whatsapp-web-reveng) for writing the guide reverse engineering WhatsApp Web and to the go reimplementation written by [Rhymen](https://github.com/Rhymen/go-whatsapp/tree/484cfe758705761d76724e01839d6fc473dc10c4)
|
||||
Thank you to [Sigalor](https://github.com/sigalor/whatsapp-web-reveng) for writing the guide to reverse engineering WhatsApp Web and thanks to [Rhymen](https://github.com/Rhymen/go-whatsapp/tree/484cfe758705761d76724e01839d6fc473dc10c4) for the __go__ reimplementation.
|
||||
|
||||
Baileys is super easy to use:
|
||||
1. Install from npm using
|
||||
* Install from npm using
|
||||
``` npm install github:adiwajshing/Baileys ```
|
||||
2. Then import in your code using
|
||||
* Then import in your code using
|
||||
``` javascript
|
||||
const WhatsAppWeb = require('Baileys')
|
||||
```
|
||||
3. Create an instance of Baileys & connect using
|
||||
* Create an instance of Baileys & connect using
|
||||
``` javascript
|
||||
let client = new WhatsAppWeb()
|
||||
client.connect()
|
||||
```
|
||||
If the connection is successful, you will see a QR code printed on your terminal screen, scan it with WhatsApp on your phone and you'll be logged in!
|
||||
4. Implement the following event handlers in your code:
|
||||
* Implement the following event handlers in your code:
|
||||
``` javascript
|
||||
client.handlers.onConnected = () => { /* when you're successfully authenticated with the WhatsApp Web servers */ }
|
||||
```
|
||||
@@ -29,21 +30,77 @@ Baileys is super easy to use:
|
||||
``` javascript
|
||||
client.handlers.onDisconnect = () => { /* called when internet gets disconnected */ }
|
||||
```
|
||||
5. Send a text message using
|
||||
* Get the type of message using
|
||||
``` javascript
|
||||
client.handlers.onUnreadMessage = (m) => {
|
||||
const messageType = client.getMessageType(m.message) // get what type of message it is -- text, image, video
|
||||
}
|
||||
```
|
||||
* Decode a media message using
|
||||
``` javascript
|
||||
client.handlers.onUnreadMessage = (m) => {
|
||||
const messageType = client.getMessageType(m.message) // get what type of message it is -- text, image, video
|
||||
|
||||
// if the message is not a text message
|
||||
if (messageType !== WhatsAppWeb.MessageType.text && messageType !== WhatsAppWeb.MessageType.extendedText) {
|
||||
client.decodeMediaMessage(m.message, "filename") // extension applied automatically
|
||||
.then (meta => console.log(m.key.remoteJid + " sent media, saved at: " + meta.fileName))
|
||||
.catch (err => console.log("error in decoding message: " + err))
|
||||
}
|
||||
}
|
||||
```
|
||||
* Send a text message using
|
||||
``` javascript
|
||||
client.sendTextMessage(id, txtMessage)
|
||||
client.sendTextMessage(id, txtMessage)
|
||||
```
|
||||
Or if you want to quote another message:
|
||||
``` javascript
|
||||
client.sendTextMessage(id, txtMessage, quotedMessage)
|
||||
```
|
||||
The id is the phone number of the person the message is being sent to, it must be in the format '[country code][phone number]@s.whatsapp.net', for example '+19999999999@s.whatsapp.net'
|
||||
6. Send a read reciept using
|
||||
* Send a media (image, video, sticker, pdf) message using
|
||||
``` javascript
|
||||
client.sendMediaMessage(id, mediaBuffer, mediaType, info)
|
||||
```
|
||||
- The thumbnail can be generated automatically for images & stickers.
|
||||
- ```mediaBuffer``` is just a Buffer containing the contents of the media you want to send
|
||||
- ```mediaType``` represents the type of message you are sending. This can be one of the following:
|
||||
``` javascript
|
||||
[
|
||||
WhatsAppWeb.MessageType.image, // an image message
|
||||
WhatsAppWeb.MessageType.video, // a video message
|
||||
WhatsAppWeb.MessageType.audio, // an audio message
|
||||
WhatsAppWeb.MessageType.sticker // a sticker message
|
||||
]
|
||||
```
|
||||
- ```info``` is a JSON object, providing some information about the media. It can have the following __optional__ values:
|
||||
``` javascript
|
||||
info = {
|
||||
caption: "hello there!", // the caption to send with the media (cannot be sent with stickers though)
|
||||
thumbnail: null, /* has to be a base 64 encoded JPEG if you want to send a custom thumb,
|
||||
or set to null if you don't want to send a thumbnail.
|
||||
Do not enter this field if you want to automatically generate a thumb
|
||||
*/
|
||||
mimetype: "application/pdf", /* specify the type of media (optional for all media types except documents),
|
||||
for pdf files => set to "application/pdf",
|
||||
for txt files => set to "application/txt"
|
||||
etc.
|
||||
*/
|
||||
gif: true // only applicable to video messages, if the video should be treated as a GIF
|
||||
}
|
||||
```
|
||||
- Tested formats: png, jpeg, webp (sticker), mp4, ogg
|
||||
- To automatically generate thumbnails for videos, you need to have ``` ffmpeg ``` installed on your system
|
||||
* Send a read reciept using
|
||||
``` javascript
|
||||
client.sendReadReceipt(id, messageID)
|
||||
```
|
||||
The id is in the same format as mentioned earlier. The message ID is the unique identifier of the message that you are marking as read
|
||||
7. Tell someone what your status is right now by using
|
||||
* Update your status by using
|
||||
``` javascript
|
||||
client.updatePresence(id, presence)
|
||||
```
|
||||
Presence can be one of the following:
|
||||
This lets the person with ``` id ``` know your status. where ``` presence ``` can be one of the following:
|
||||
``` javascript
|
||||
static Presence = {
|
||||
available: "available", // "online"
|
||||
@@ -53,20 +110,22 @@ Baileys is super easy to use:
|
||||
paused: "paused" // I have no clue
|
||||
}
|
||||
```
|
||||
8. Once you want to close your session, you can get your authentication credentials using:
|
||||
|
||||
* Once you want to close your session, you can get your authentication credentials using:
|
||||
``` javascript
|
||||
const authJSON = client.base64EncodedAuthInfo()
|
||||
```
|
||||
and then save this JSON to a file
|
||||
9. If you want to restore your session (i.e. log back in without having to scan the QR code), simply retreive your previously saved credentials and use
|
||||
* If you want to restore your session (i.e. log back in without having to scan the QR code), simply retreive your previously saved credentials and use
|
||||
``` javascript
|
||||
const authJSON = JSON.parse( fs.readFileSync("auth_info.json") )
|
||||
client.login( authJSON )
|
||||
client.login(authJSON)
|
||||
```
|
||||
This will use the credentials to connect & log back in. No need to call ``` connect() ``` after calling this function
|
||||
10. If you want to query whether a number is registered on WhatsApp, use:
|
||||
* If you want to query whether a number is registered on WhatsApp, use:
|
||||
``` javascript
|
||||
client.isOnWhatsApp ("[countrycode][some10digitnumber]@s.whatsapp.net", (exists, id) => {
|
||||
client.isOnWhatsApp ("[countrycode][some10digitnumber]@s.whatsapp.net")
|
||||
.then ((exists, id) => {
|
||||
if (exists) {
|
||||
console.log(id + " is on WhatsApp")
|
||||
} else {
|
||||
@@ -74,10 +133,13 @@ Baileys is super easy to use:
|
||||
}
|
||||
})
|
||||
```
|
||||
Of course, replace ``` [countrycode][some10digitnumber] ``` with an actual country code & number
|
||||
Of course, replace ``` [countrycode][some10digitnumber] ``` with an actual country code & number. ``` isOnWhatsApp ``` returns a promise.
|
||||
|
||||
|
||||
Do check out test.js to see example usage of all these functions.
|
||||
Do check out & run [example.js](example/example.js) to see example usage of all these functions.
|
||||
To run the example script, download or clone the repo and then type the following in terminal:
|
||||
1. ``` cd path/to/Baileys/example ```
|
||||
2. ``` node example.js ```
|
||||
|
||||
# Note
|
||||
I am in no way affiliated with WhatsApp. This was written for educational purposes. Use at your own discretion.
|
||||
@@ -1,4 +1,7 @@
|
||||
const Utils = require("./WhatsAppWeb.Utils")
|
||||
const HKDF = require("futoin-hkdf")
|
||||
const fs = require("fs")
|
||||
const fetch = require("node-fetch")
|
||||
/*
|
||||
Contains the code for recieving messages and forwarding what to do with them to the correct functions
|
||||
*/
|
||||
@@ -23,11 +26,15 @@ module.exports = function(WhatsAppWeb) {
|
||||
// got an empty message, usually get one after sending a message or something, just return then
|
||||
return
|
||||
}
|
||||
// get the message tag.
|
||||
// If a query was done, the server will respond with the same message tag we sent the query with
|
||||
const messageTag = message.slice(0, commaIndex)
|
||||
//console.log(messageTag)
|
||||
|
||||
let json
|
||||
if (data[0] === "[" || data[0] === "{") { // if the first character is a "[", then the data must just be plain JSON array or object
|
||||
json = JSON.parse( data ) // simply parse the JSON
|
||||
console.log("JSON: " + data)
|
||||
json = JSON.parse( data ) // parse the JSON
|
||||
//console.log("JSON: " + data)
|
||||
} else if (this.status === Status.connected) {
|
||||
/*
|
||||
If the data recieved was not a JSON, then it must be an encrypted message.
|
||||
@@ -51,8 +58,7 @@ module.exports = function(WhatsAppWeb) {
|
||||
// if we recieved a message that was encrypted but we weren't conencted, then there must be an error
|
||||
return this.gotError([3, "recieved encrypted message when not connected: " + this.status, message])
|
||||
}
|
||||
|
||||
//console.log(json)
|
||||
|
||||
// the first item in the recieved JSON, if it exists, it tells us what the message is about
|
||||
switch (json[0]) {
|
||||
case "Conn":
|
||||
@@ -108,9 +114,10 @@ module.exports = function(WhatsAppWeb) {
|
||||
if (!this.chats[ id ]) { // if we haven't added this ID before, add them now
|
||||
this.chats[ id ] = {user: { jid: id, count: 0 }, messages: []}
|
||||
}
|
||||
|
||||
this.chats[id].messages.push(message[2]) // append this message to the array
|
||||
|
||||
}
|
||||
|
||||
|
||||
const id = json[0][2].key.remoteJid // get the ID whose chats we just processed
|
||||
this.clearUnreadMessages(id) // forward to the handler any any unread messages
|
||||
@@ -139,18 +146,17 @@ module.exports = function(WhatsAppWeb) {
|
||||
|
||||
/*
|
||||
if the recieved JSON wasn't an array, then we must have recieved a status for a request we made
|
||||
this would include creating new sessions, logging in & queries
|
||||
*/
|
||||
// if we're connected and we had a pending query
|
||||
if (this.status === Status.connected) {
|
||||
if (json.status && this.queryCallbacks.length > 0) {
|
||||
for (var i in this.queryCallbacks) {
|
||||
if (this.queryCallbacks[i].queryJSON[1] === "exist") {
|
||||
this.queryCallbacks[i].callback(json.status == 200, this.queryCallbacks[i].queryJSON[2])
|
||||
this.queryCallbacks.splice(i, 1)
|
||||
break
|
||||
}
|
||||
// if this message is responding to a query
|
||||
if (this.queryCallbacks[messageTag]) {
|
||||
const q = this.queryCallbacks[messageTag]
|
||||
if (q.queryJSON[1] === "exist") {
|
||||
q.callback(json.status == 200, q.queryJSON[2])
|
||||
} else if (q.queryJSON[1] === "mediaConn") {
|
||||
q.callback(json.media_conn)
|
||||
}
|
||||
delete this.queryCallbacks[messageTag]
|
||||
}
|
||||
} else {
|
||||
// if we're trying to establish a new connection or are trying to log in
|
||||
@@ -166,12 +172,6 @@ module.exports = function(WhatsAppWeb) {
|
||||
} else {
|
||||
this.generateKeysForAuth(json.ref)
|
||||
}
|
||||
} else if (this.queryCallbacks.length > 0) {
|
||||
for (var i in this.queryCallbacks) {
|
||||
if (this.queryCallbacks[i].queryJSON[1] == "query") {
|
||||
this.queryCallbacks[i].callback( )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
@@ -185,7 +185,7 @@ module.exports = function(WhatsAppWeb) {
|
||||
console.log("reuse previous ref")
|
||||
return this.gotError([ json.status, "request for new key denied", message ])
|
||||
default:
|
||||
break
|
||||
return this.gotError([ json.status, "unknown error", message ])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,6 +221,70 @@ module.exports = function(WhatsAppWeb) {
|
||||
this.handlers.onUnreadMessage ( message )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get what type of message it is
|
||||
WhatsAppWeb.prototype.getMessageType = function (message) {
|
||||
return Object.keys(message)[0]
|
||||
/*for (var key in WhatsAppWeb.MessageType) {
|
||||
if (WhatsAppWeb.MessageType[key] === relvantKey) {
|
||||
return key
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// decode a media message (video, image, document, audio) & save it to the given file; returns a promise with metadata
|
||||
WhatsAppWeb.prototype.decodeMediaMessage = function (message, fileName) {
|
||||
|
||||
const getExtension = function (mimetype) {
|
||||
const str = mimetype.split(";")[0].split("/")
|
||||
return str[1]
|
||||
}
|
||||
/*
|
||||
can infer media type from the key in the message
|
||||
it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc.
|
||||
*/
|
||||
let type = this.getMessageType(message)
|
||||
if (!type) {
|
||||
return Promise.reject("unknown message type")
|
||||
}
|
||||
if (type === WhatsAppWeb.MessageType.extendedText || type === WhatsAppWeb.MessageType.text) {
|
||||
return Promise.reject("cannot decode text message")
|
||||
}
|
||||
|
||||
message = message[type]
|
||||
// get the keys to decrypt the message
|
||||
const mediaKeys = Utils.getMediaKeys(Buffer.from(message.mediaKey, 'base64'), type)
|
||||
const iv = mediaKeys.iv
|
||||
const cipherKey = mediaKeys.cipherKey
|
||||
const macKey = mediaKeys.macKey
|
||||
|
||||
// download the message
|
||||
let p = fetch(message.url).then (res => res.buffer())
|
||||
p = p.then(buffer => {
|
||||
// first part is actual file
|
||||
let file = buffer.slice(0, buffer.length-10)
|
||||
// last 10 bytes is HMAC sign of file
|
||||
let mac = buffer.slice(buffer.length-10, buffer.length)
|
||||
|
||||
// sign IV+file & check for match with mac
|
||||
let testBuff = Buffer.concat([iv, file])
|
||||
let sign = Utils.hmacSign(testBuff, macKey).slice(0, 10)
|
||||
|
||||
// our sign should equal the mac
|
||||
if (sign.equals(mac)) {
|
||||
let decrypted = Utils.aesDecryptWithIV(file, cipherKey, iv) // decrypt media
|
||||
|
||||
const trueFileName = fileName + "." + getExtension(message.mimetype)
|
||||
fs.writeFileSync(trueFileName, decrypted)
|
||||
|
||||
message.fileName = trueFileName
|
||||
return message
|
||||
} else {
|
||||
throw "HMAC sign does not match"
|
||||
}
|
||||
})
|
||||
return p
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
const Utils = require("./WhatsAppWeb.Utils")
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
/*
|
||||
Contains the code for sending stuff to WhatsApp
|
||||
@@ -19,15 +20,13 @@ module.exports = function(WhatsAppWeb) {
|
||||
}
|
||||
}
|
||||
// check if given number is registered on WhatsApp
|
||||
WhatsAppWeb.prototype.isOnWhatsApp = function (jid, callback) {
|
||||
WhatsAppWeb.prototype.isOnWhatsApp = function (jid) {
|
||||
const json = [
|
||||
"query",
|
||||
"exist",
|
||||
jid
|
||||
]
|
||||
this.sendJSON(json) // send
|
||||
|
||||
this.queryCallbacks.push({queryJSON: json, callback: callback})
|
||||
return this.query(json)
|
||||
}
|
||||
// tell someone about your presence -- online, typing, offline etc.
|
||||
WhatsAppWeb.prototype.updatePresence = function (jid, type) {
|
||||
@@ -38,11 +37,111 @@ module.exports = function(WhatsAppWeb) {
|
||||
]
|
||||
this.sendBinary(json, [10, 128])
|
||||
}
|
||||
// send a text message to someone, optionally you can provide the time at which you want the message to be sent
|
||||
WhatsAppWeb.prototype.sendTextMessage = function (id, txt, timestamp=null) {
|
||||
const message = {conversation: txt}
|
||||
// send a text message to someone, optionally you can provide a quoted message & the timestamp for the message
|
||||
WhatsAppWeb.prototype.sendTextMessage = function (id, txt, quoted=null, timestamp=null) {
|
||||
let message
|
||||
if (quoted) {
|
||||
message = {
|
||||
extendedTextMessage: {
|
||||
text: txt,
|
||||
contextInfo: {
|
||||
participant: quoted.key.remoteJid,
|
||||
stanzaId: quoted.key.id,
|
||||
quotedMessage: quoted.message
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message = {conversation: txt}
|
||||
}
|
||||
|
||||
return this.sendMessage(id, message, timestamp)
|
||||
}
|
||||
// send a media message to someone, optionally you can provide a caption, thumbnail, mimetype & the timestamp for the message
|
||||
WhatsAppWeb.prototype.sendMediaMessage = function (id, buffer, mediaType, info=null, timestamp=null) {
|
||||
// path to upload the media
|
||||
const mediaPathMap = {
|
||||
imageMessage: "/mms/image",
|
||||
videoMessage: "/mms/video",
|
||||
documentMessage: "/mms/document",
|
||||
audioMessage: "/mms/audio",
|
||||
stickerMessage: "/mms/image"
|
||||
}
|
||||
// gives WhatsApp info to process the media
|
||||
const defaultMimetypeMap = {
|
||||
imageMessage: "image/jpeg",
|
||||
videoMessage: "video/mp4",
|
||||
documentMessage: "appliction/pdf",
|
||||
audioMessage: "audio/ogg; codecs=opus",
|
||||
stickerMessage: "image/webp"
|
||||
}
|
||||
if (!info) {
|
||||
info = {}
|
||||
}
|
||||
if (mediaType === WhatsAppWeb.MessageType.text || mediaType === WhatsAppWeb.MessageType.extendedText) {
|
||||
return Promise.reject("use sendTextMessage() to send text messages")
|
||||
}
|
||||
if (mediaType === WhatsAppWeb.MessageType.document && !info.mimetype) {
|
||||
return Promise.reject("mimetype required to send a document")
|
||||
}
|
||||
if (mediaType === WhatsAppWeb.MessageType.sticker && info.caption) {
|
||||
return Promise.reject("cannot send a caption with a sticker")
|
||||
}
|
||||
if (!info.mimetype) {
|
||||
info.mimetype = defaultMimetypeMap[mediaType]
|
||||
}
|
||||
|
||||
// generate a media key
|
||||
const mediaKey = Utils.randomBytes(32)
|
||||
const mediaKeys = Utils.getMediaKeys(mediaKey, mediaType)
|
||||
const enc = Utils.aesEncrypWithIV(buffer, mediaKeys.cipherKey, mediaKeys.iv)
|
||||
const mac = Utils.hmacSign(Buffer.concat([mediaKeys.iv, enc]), mediaKeys.macKey).slice(0, 10)
|
||||
const body = Buffer.concat([enc, mac]) // body is enc + mac
|
||||
const fileSha256 = Utils.sha256(buffer)
|
||||
// url safe Base64 encode the SHA256 hash of the body
|
||||
const fileEncSha256B64 = Utils.sha256(body).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '')
|
||||
|
||||
const promise =
|
||||
Utils.generateThumbnail(buffer, mediaType, info)
|
||||
.then (() => this.query(["query", "mediaConn"])) // send a query JSON to obtain the url & auth token to upload our media
|
||||
.then ((json) => {
|
||||
const auth = json.auth // the auth token
|
||||
let hostname = "https://" + json.hosts[0].hostname // first hostname available
|
||||
hostname += mediaPathMap[mediaType] + "/" + fileEncSha256B64 // append path
|
||||
hostname += "?auth=" + auth // add auth token
|
||||
hostname += "&token=" + fileEncSha256B64 // file hash
|
||||
|
||||
return fetch(hostname, {method: 'POST', body: body, headers: {Origin: "https://web.whatsapp.com"}})
|
||||
})
|
||||
.then (res => res.json())
|
||||
.then (json => {
|
||||
if (json.url) {
|
||||
return json.url
|
||||
} else {
|
||||
throw "UPLOAD FAILED GOT: " + JSON.stringify(json)
|
||||
}
|
||||
})
|
||||
.then (url => {
|
||||
let message = {}
|
||||
message[mediaType] = {
|
||||
caption: info.caption,
|
||||
url: url,
|
||||
mediaKey: mediaKey.toString('base64'),
|
||||
mimetype: info.mimetype,
|
||||
fileEncSha256: fileEncSha256B64,
|
||||
fileSha256: fileSha256.toString('base64'),
|
||||
fileLength: buffer.length,
|
||||
jpegThumbnail: info.thumbnail
|
||||
}
|
||||
if (mediaType === WhatsAppWeb.MessageType.video && info.gif) {
|
||||
message[mediaType].gifPlayback = info.gif
|
||||
}
|
||||
//console.log(message)
|
||||
return this.sendMessage(id, message, timestamp)
|
||||
})
|
||||
|
||||
return promise
|
||||
}
|
||||
// generic send message construct
|
||||
WhatsAppWeb.prototype.sendMessage = function (id, message, timestamp=null) {
|
||||
if (!timestamp) { // if no timestamp was provided,
|
||||
@@ -66,7 +165,8 @@ module.exports = function(WhatsAppWeb) {
|
||||
{epoch: this.msgCount.toString(), type: "relay" },
|
||||
[ ['message', null, messageJSON] ]
|
||||
]
|
||||
return this.sendBinary(json, [16, 128])
|
||||
this.sendBinary(json, [16, 128])
|
||||
return messageJSON
|
||||
}
|
||||
// send a binary message, the tags parameter tell WhatsApp what the message is all about
|
||||
WhatsAppWeb.prototype.sendBinary = function (json, tags) {
|
||||
@@ -74,19 +174,30 @@ module.exports = function(WhatsAppWeb) {
|
||||
|
||||
var buff = Utils.aesEncrypt(binary, this.authInfo.encKey) // encrypt it using AES and our encKey
|
||||
const sign = Utils.hmacSign(buff, this.authInfo.macKey) // sign the message using HMAC and our macKey
|
||||
|
||||
const tag = Utils.generateMessageTag()
|
||||
buff = Buffer.concat([
|
||||
Buffer.from( Utils.generateMessageTag() + "," ), // generate & prefix the message tag
|
||||
Buffer.from(tag + ","), // generate & prefix the message tag
|
||||
Buffer.from(tags), // prefix some bytes that tell whatsapp what the message is about
|
||||
sign, // the HMAC sign of the message
|
||||
buff // the actual encrypted buffer
|
||||
])
|
||||
this.send(buff) // send it off
|
||||
return tag
|
||||
}
|
||||
// send query message to WhatsApp servers; returns a promise
|
||||
WhatsAppWeb.prototype.query = function (json) {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const tag = this.sendJSON(json) // send
|
||||
this.queryCallbacks[tag] = {queryJSON: json, callback: resolve, errCallback: reject}
|
||||
})
|
||||
return promise
|
||||
}
|
||||
// send a JSON message to WhatsApp servers
|
||||
WhatsAppWeb.prototype.sendJSON = function (json) {
|
||||
const str = JSON.stringify(json)
|
||||
this.send( Utils.generateMessageTag() + "," + str )
|
||||
const tag = Utils.generateMessageTag()
|
||||
this.send(tag + "," + str)
|
||||
return tag
|
||||
}
|
||||
WhatsAppWeb.prototype.send = function (m) {
|
||||
this.msgCount += 1 // increment message count, it makes the 'epoch' field when sending binary messages
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const WebSocket = require('ws')
|
||||
const Curve = require ('curve25519-js')
|
||||
const HKDF = require('futoin-hkdf')
|
||||
const Utils = require('./WhatsAppWeb.Utils')
|
||||
const QR = require('qrcode-terminal')
|
||||
|
||||
@@ -68,7 +67,7 @@ module.exports = function (WhatsAppWeb) {
|
||||
// generate shared key from our private key & the secret shared by the server
|
||||
const sharedKey = Curve.sharedKey( this.curveKeys.private, secret.slice(0, 32) )
|
||||
// expand the key to 80 bytes using HKDF
|
||||
const expandedKey = HKDF(sharedKey, 80, [ Buffer.alloc(32), '', 'SHA-256' ])
|
||||
const expandedKey = Utils.hkdf(sharedKey, 80)
|
||||
|
||||
// perform HMAC validation.
|
||||
const hmacValidationKey = expandedKey.slice(32, 64)
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
const Crypto = require("crypto")
|
||||
const HKDF = require("futoin-hkdf")
|
||||
const sharp = require("sharp")
|
||||
const VideoThumb = require("video-thumb")
|
||||
const fs = require("fs")
|
||||
|
||||
/*
|
||||
Basic cryptographic utilities to interact with WhatsApp servers
|
||||
@@ -6,19 +10,98 @@ const Crypto = require("crypto")
|
||||
module.exports = {
|
||||
// decrypt AES 256 CBC; where the IV is prefixed to the buffer
|
||||
aesDecrypt: function (buffer, key) {
|
||||
const aes = Crypto.createDecipheriv('aes-256-cbc', key, buffer.slice(0,16) ) // first 16 bytes of buffer is IV
|
||||
return Buffer.concat( [ aes.update(buffer.slice(16, buffer.length)), aes.final() ] )
|
||||
return this.aesDecryptWithIV(buffer.slice(16, buffer.length), key, buffer.slice(0,16))
|
||||
},
|
||||
// encrypt AES 256 CBC; where the IV is prefixed to the buffer
|
||||
// decrypt AES 256 CBC
|
||||
aesDecryptWithIV: function (buffer, key, IV) {
|
||||
const aes = Crypto.createDecipheriv('aes-256-cbc', key, IV )
|
||||
return Buffer.concat( [ aes.update(buffer), aes.final() ] )
|
||||
},
|
||||
// encrypt AES 256 CBC; where a random IV is prefixed to the buffer
|
||||
aesEncrypt: function (buffer, key) {
|
||||
const IV = this.randomBytes(16)
|
||||
const aes = Crypto.createCipheriv('aes-256-cbc', key, IV)
|
||||
return Buffer.concat( [ IV, aes.update(buffer), aes.final() ] ) // prefix IV to the buffer
|
||||
},
|
||||
// encrypt AES 256 CBC with a given IV
|
||||
aesEncrypWithIV: function (buffer, key, IV) {
|
||||
const aes = Crypto.createCipheriv('aes-256-cbc', key, IV)
|
||||
return Buffer.concat( [ aes.update(buffer), aes.final() ] ) // prefix IV to the buffer
|
||||
},
|
||||
// sign HMAC using SHA 256
|
||||
hmacSign: function (buffer, key) {
|
||||
return Crypto.createHmac('sha256', key).update(buffer).digest()
|
||||
},
|
||||
sha256: function (buffer) {
|
||||
return Crypto.createHash('sha256').update(buffer).digest()
|
||||
},
|
||||
// HKDF key expansion
|
||||
hkdf: function (buffer, expandedLength, info) {
|
||||
return HKDF(buffer, expandedLength, {salt: Buffer.alloc(32), info: info, hash: 'SHA-256'})
|
||||
},
|
||||
// generates all the keys required to encrypt/decrypt & sign a media message
|
||||
getMediaKeys: function (buffer, mediaType) {
|
||||
// info to put into the HKDF key expansion
|
||||
const appInfo = {
|
||||
'imageMessage': 'WhatsApp Image Keys',
|
||||
'videoMessage': 'WhatsApp Video Keys',
|
||||
'audioMessage': 'WhatsApp Audio Keys',
|
||||
'documentMessage': 'WhatsApp Document Keys',
|
||||
'stickerMessage': 'WhatsApp Image Keys'
|
||||
}
|
||||
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
||||
const expandedMediaKey = this.hkdf(buffer, 112, appInfo[mediaType])
|
||||
return {
|
||||
iv: expandedMediaKey.slice(0, 16),
|
||||
cipherKey: expandedMediaKey.slice(16, 48),
|
||||
macKey: expandedMediaKey.slice(48, 80)
|
||||
}
|
||||
},
|
||||
// generates a thumbnail for a given media, if required
|
||||
generateThumbnail: function (buffer, mediaType, info) {
|
||||
let promise
|
||||
if (info.thumbnail === null || info.thumbnail) { // don't do anything if the thumbnail is already provided, or is null
|
||||
if (mediaType === 'audioMessage') {
|
||||
promise = Promise.reject("audio messages cannot have thumbnails")
|
||||
} else {
|
||||
promise = Promise.resolve()
|
||||
}
|
||||
} else {
|
||||
if (mediaType === 'imageMessage' || mediaType === 'stickerMessage') {
|
||||
promise = sharp(buffer) // generate a 48x48 thumb
|
||||
.resize(48, 48)
|
||||
.jpeg()
|
||||
.toBuffer()
|
||||
.then (buffer => info.thumbnail = buffer.toString('base64'))
|
||||
} else if (mediaType === 'videoMessage') {
|
||||
const filename = "./" + this.randomBytes(5).toString("hex") + ".mp4"
|
||||
fs.writeFileSync(filename, buffer)
|
||||
|
||||
promise = new Promise ( (resolve, reject) => {
|
||||
VideoThumb.extract (filename, filename + ".png", "00:00:00", "48x48", (err) => {
|
||||
if (err) {
|
||||
console.log("could not generate video thumb: " + err)
|
||||
resolve()
|
||||
} else {
|
||||
const buff = fs.readFileSync(filename + ".png")
|
||||
return sharp(buff)
|
||||
.jpeg()
|
||||
.toBuffer()
|
||||
.then (buffer => info.thumbnail = buffer.toString('base64'))
|
||||
.then (() => {
|
||||
fs.unlinkSync(filename)
|
||||
fs.unlinkSync(filename + ".png")
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
promise = Promise.resolve()
|
||||
}
|
||||
}
|
||||
return promise
|
||||
},
|
||||
// generate a buffer with random bytes of the specified length
|
||||
randomBytes: function (length) { return Crypto.randomBytes(length) },
|
||||
|
||||
|
||||
@@ -22,6 +22,16 @@ class WhatsAppWeb {
|
||||
paused: "paused" // I have no clue
|
||||
}
|
||||
|
||||
// set of message types that are supported by the library
|
||||
static MessageType = {
|
||||
text: "conversation",
|
||||
image: "imageMessage",
|
||||
video: "videoMessage",
|
||||
sticker: "stickerMessage",
|
||||
document: "documentMessage",
|
||||
extendedText: "extendedTextMessage"
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.conn = null // the websocket connection
|
||||
|
||||
@@ -34,7 +44,7 @@ class WhatsAppWeb {
|
||||
this.autoReconnect = true // reconnect automatically after an unexpected disconnect
|
||||
this.lastSeen = null // updated by sending a keep alive request to the server, and the server responds with our updated last seen
|
||||
|
||||
this.queryCallbacks = []
|
||||
this.queryCallbacks = {}
|
||||
|
||||
this.encoder = new BinaryCoding.Encoder()
|
||||
this.decoder = new BinaryCoding.Decoder()
|
||||
|
||||
BIN
example/.DS_Store
vendored
Normal file
BIN
example/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
const WhatsAppWeb = require("./WhatsAppWeb")
|
||||
const WhatsAppWeb = require("../WhatsAppWeb")
|
||||
const fs = require("fs")
|
||||
|
||||
let client = new WhatsAppWeb() // instantiate
|
||||
@@ -21,18 +21,47 @@ client.handlers.onConnected = () => {
|
||||
}
|
||||
// called when you have a pending unread message or recieve a new message
|
||||
client.handlers.onUnreadMessage = (m) => {
|
||||
console.log("recieved message: " + JSON.stringify(m)) // log and see what the message looks like
|
||||
// console.log("recieved message: " + JSON.stringify(m)) // uncomment to see what the raw message looks like
|
||||
|
||||
const messageType = client.getMessageType(m.message) // get what type of message it is -- text, image, video
|
||||
console.log("got message of type: " + messageType)
|
||||
|
||||
if (messageType === WhatsAppWeb.MessageType.text) { // if it is plain text
|
||||
const text = m.message.conversation
|
||||
console.log (m.key.remoteJid + " sent: " + text)
|
||||
} else if (messageType === WhatsAppWeb.MessageType.extendedText) { // if it is a quoted thing
|
||||
const text = m.message.extendedTextMessage.text // the actual text
|
||||
const quotedMessage = m.message.extendedTextMessage.contextInfo.quotedMessage // message that was replied to
|
||||
console.log (m.key.remoteJid + " sent: " + text + " and quoted a " + client.getMessageType(quotedMessage))
|
||||
} else { // if it is a media (audio, image, video) message
|
||||
// decode, decrypt & save the media.
|
||||
// The extension to the is applied automatically based on the media type
|
||||
client.decodeMediaMessage(m.message, "media_in_" + m.key.id)
|
||||
.then (meta => console.log(m.key.remoteJid + " sent media, saved at: " + meta.fileName))
|
||||
.catch (err => console.log("error in decoding message: " + err))
|
||||
}
|
||||
console.log("responding...")
|
||||
|
||||
/* send a message after at least a 1 second timeout after recieving a message, otherwise WhatsApp will reject the message otherwise */
|
||||
setTimeout(() => client.sendReadReceipt(m.key.remoteJid, m.key.id), 2*1000) // send a read reciept for the message in 2 seconds
|
||||
setTimeout(() => client.updatePresence(m.key.remoteJid, WhatsAppWeb.Presence.composing), 2.5*1000) // let them know you're typing in 2.5 seconds
|
||||
setTimeout(() => client.sendTextMessage(m.key.remoteJid, "hello!"), 4*1000) // send the actual message after 4 seconds
|
||||
setTimeout(() => {
|
||||
if (Math.random() > 0.5) { // choose at random
|
||||
client.sendTextMessage(m.key.remoteJid, "hello!", m) // send a "hello!" & quote the message recieved
|
||||
} else {
|
||||
const buffer = fs.readFileSync("./ma_gif.mp4") // load the gif
|
||||
const info = {
|
||||
gif: true, // the video is a gif
|
||||
caption: "hello!" // the caption
|
||||
}
|
||||
client.sendMediaMessage (m.key.remoteJid, buffer, WhatsAppWeb.MessageType.video, info) // send this gif!
|
||||
}
|
||||
}, 4*1000) // send after 4 seconds
|
||||
}
|
||||
// called if an error occurs
|
||||
client.handlers.onError = (err) => console.log(err)
|
||||
client.handlers.onDisconnect = () => { /* internet got disconnected, save chats here or whatever; will reconnect automatically */ }
|
||||
|
||||
|
||||
const readline = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
BIN
example/ma_gif.mp4
Normal file
BIN
example/ma_gif.mp4
Normal file
Binary file not shown.
564
package-lock.json
generated
564
package-lock.json
generated
@@ -68,21 +68,365 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz",
|
||||
"integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q=="
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
|
||||
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
},
|
||||
"color": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
|
||||
"integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.1",
|
||||
"color-string": "^1.5.2"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
|
||||
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"curve25519-js": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz",
|
||||
"integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w=="
|
||||
},
|
||||
"decompress-response": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||
"requires": {
|
||||
"mimic-response": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"requires": {
|
||||
"minipass": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"futoin-hkdf": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.3.1.tgz",
|
||||
"integrity": "sha512-k1DvCXIFAIx3hK8CSwApotX3JUDwA2Wb55zxyIgqwQpCBF2ZHgVqfHpyjG8mRpmsjRH7SWS1N/vj8EdSF9zBhw=="
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"requires": {
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wide-align": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
|
||||
"integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
|
||||
"integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==",
|
||||
"requires": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
|
||||
"integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
|
||||
},
|
||||
"napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.15.0.tgz",
|
||||
"integrity": "sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
|
||||
"integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
},
|
||||
"noop-logger": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
"gauge": "~2.7.3",
|
||||
"set-blocking": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
|
||||
"integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"node-abi": "^2.7.0",
|
||||
"noop-logger": "^0.1.1",
|
||||
"npmlog": "^4.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^3.0.3",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"which-pm-runs": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"protobufjs": {
|
||||
"version": "6.8.9",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.9.tgz",
|
||||
@@ -103,15 +447,235 @@
|
||||
"long": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"qrcode-terminal": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz",
|
||||
"integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"sharp": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.25.2.tgz",
|
||||
"integrity": "sha512-l1GN0kFNtJr3U9i9pt7a+vo2Ij0xv4tTKDIPx8W6G9WELhPwrMyZZJKAAQNBSI785XB4uZfS5Wpz8C9jWV4AFQ==",
|
||||
"requires": {
|
||||
"color": "^3.1.2",
|
||||
"detect-libc": "^1.0.3",
|
||||
"node-addon-api": "^2.0.0",
|
||||
"npmlog": "^4.1.2",
|
||||
"prebuild-install": "^5.3.3",
|
||||
"semver": "^7.1.3",
|
||||
"simple-get": "^3.1.0",
|
||||
"tar": "^6.0.1",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"requires": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||
"requires": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"tar": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz",
|
||||
"integrity": "sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.3",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^3.0.0",
|
||||
"minizlib": "^2.1.0",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
|
||||
"integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
|
||||
"requires": {
|
||||
"bl": "^4.0.1",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"video-thumb": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/video-thumb/-/video-thumb-0.0.3.tgz",
|
||||
"integrity": "sha1-GMbS8wRIO1tVWzdZzUOI9uigjQg="
|
||||
},
|
||||
"which-pm-runs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
|
||||
"integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
"dependencies": {
|
||||
"curve25519-js": "0.0.4",
|
||||
"futoin-hkdf": "^1.3.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"protobufjs": "^6.8.9",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"sharp": "^0.25.2",
|
||||
"video-thumb": "0.0.3",
|
||||
"ws": "^7.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user