added media send/recieve, extended text messages send/recieve

This commit is contained in:
Adhiraj
2020-04-16 16:20:48 +05:30
parent 6629c08c70
commit 9ea4b0b381
13 changed files with 987 additions and 61 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
node_modules
auth_info.json
test_pvt.js
media_decode_tests.js

View File

@@ -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.

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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) },

View File

@@ -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

Binary file not shown.

View File

@@ -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

Binary file not shown.

564
package-lock.json generated
View File

@@ -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=="
}
}
}

View File

@@ -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"
}
}