diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..1d9dd7b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +# Ignore artifacts: +lib +coverage +*.lock +.eslintrc.json +src/WABinary/index.ts \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 10a24e6..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - parser: "@typescript-eslint/parser", // Specifies the ESLint parser - parserOptions: { - ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features - sourceType: "module" // Allows for the use of imports - }, - extends: [ - "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin - "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier - "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. - ], - rules: { - // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs - // e.g. "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-namespace": "off", - "@typescript-eslint/ban-types": "off" - } -} \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..7d37e83 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@adiwajshing" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 529e08a..b96baf0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules auth_info*.json +baileys_store*.json output.csv */.DS_Store .DS_Store @@ -10,4 +11,6 @@ lib docs browser-token.json Proxy -messages*.json \ No newline at end of file +messages*.json +test.ts +TestData \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index d1ca80a..0000000 --- a/.prettierrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - semi: false, - trailingComma: "all", - singleQuote: true, - printWidth: 120, - tabWidth: 4 -} \ No newline at end of file diff --git a/Example/ConversationExtract.js b/Example/ConversationExtract.js deleted file mode 100644 index d1a7333..0000000 --- a/Example/ConversationExtract.js +++ /dev/null @@ -1,97 +0,0 @@ -const WhatsAppWeb = require("../WhatsAppWeb") -const fs = require("fs") - -/** - * Extract all your WhatsApp conversations & save them to a file - * produceAnonData => should the Id of the chat be recorded - * */ -function extractChats (authCreds, outputFile, produceAnonData=false, offset=null) { - let client = new WhatsAppWeb() // instantiate an instance - // internal extract function - const extract = function () { - let rows = 0 - let chats = Object.keys(client.chats) - let encounteredOffset - if (offset) { - encounteredOffset = false - } else { - encounteredOffset = true - fs.writeFileSync(outputFile, "chat,input,output\n") // write header to file - } - - const extractChat = function (index) { - const id = chats[index] - if (id.includes("g.us") || !encounteredOffset) { // skip groups - if (id === offset) { - encounteredOffset = true - } - if (index+1 < chats.length) { - return extractChat(index+1) - } - return - } - console.log("extracting for " + id + "...") - - var curInput = "" - var curOutput = "" - var lastMessage - return client.loadEntireConversation (id, m => { - var text - if (!m.message) { // if message not present, return - return - } else if (m.message.conversation) { // if its a plain text message - text = m.message.conversation - } else if (m.message.extendedTextMessage && m.message.extendedTextMessage.contextInfo) { // if its a reply to a previous message - const mText = m.message.extendedTextMessage.text - const quotedMessage = m.message.extendedTextMessage.contextInfo.quotedMessage - // if it's like a '.' and the quoted message has no text, then just forget it - if (mText.length <= 2 && !quotedMessage.conversation) { - return - } - // if somebody sent like a '.', then the text should be the quoted message - if (mText.length <= 2) { - text = quotedMessage.conversation - } else { // otherwise just use this text - text = mText - } - } else { - return - } - // if the person who sent the message has switched, flush the row - if (lastMessage && !m.key.fromMe && lastMessage.key.fromMe) { - - let row = "" + (produceAnonData ? "" : id) + ",\"" + curInput + "\",\"" + curOutput + "\"\n" - fs.appendFileSync (outputFile, row) - rows += 1 - curInput = "" - curOutput = "" - } - - if (m.key.fromMe) { - curOutput += curOutput === "" ? text : ("\n"+text) - } else { - curInput += curInput === "" ? text : ("\n"+text) - } - - lastMessage = m - }, 50, false) // load from the start, in chunks of 50 - .then (() => console.log("finished extraction for " + id)) - .then (() => { - if (index+1 < chats.length) { - return extractChat(index+1) - } - }) - } - - extractChat(0) - .then (() => { - console.log("extracted all; total " + rows + " rows") - client.logout () - }) - } - client.connect (authCreds) - .then (() => extract()) - .catch (err => console.log("got error: " + error)) -} -let creds = null//JSON.parse(fs.readFileSync("auth_info.json")) -extractChats(creds, "output.csv") \ No newline at end of file diff --git a/Example/example-legacy.ts b/Example/example-legacy.ts new file mode 100644 index 0000000..bae2b89 --- /dev/null +++ b/Example/example-legacy.ts @@ -0,0 +1,76 @@ +import { Boom } from '@hapi/boom' +import P from 'pino' +import { AnyMessageContent, delay, DisconnectReason, makeInMemoryStore, makeWALegacySocket, useSingleFileLegacyAuthState } from '../src' + +// the store maintains the data of the WA connection in memory +// can be written out to a file & read from it +const store = makeInMemoryStore({ logger: P().child({ level: 'debug', stream: 'store' }) }) +store.readFromFile('./baileys_store.json') +// save every 10s +setInterval(() => { + store.writeToFile('./baileys_store.json') +}, 10_000) + +const { state, saveState } = useSingleFileLegacyAuthState('./auth_info.json') + +// start a connection +const startSock = () => { + + const sock = makeWALegacySocket({ + logger: P({ level: 'debug' }), + printQRInTerminal: true, + auth: state + }) + store.bind(sock.ev) + + const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => { + await sock.presenceSubscribe(jid) + await delay(500) + + await sock.sendPresenceUpdate('composing', jid) + await delay(2000) + + await sock.sendPresenceUpdate('paused', jid) + + await sock.sendMessage(jid, msg) + } + + sock.ev.on('messages.upsert', async m => { + if(m.type === 'append' || m.type === 'notify') { + console.log(JSON.stringify(m, undefined, 2)) + } + + const msg = m.messages[0] + if(!msg.key.fromMe && m.type === 'notify') { + console.log('replying to', m.messages[0].key.remoteJid) + await sock!.chatRead(msg.key, 1) + await sendMessageWTyping({ text: 'Hello there!' }, msg.key.remoteJid) + } + + }) + + sock.ev.on('messages.update', m => console.log(JSON.stringify(m, undefined, 2))) + sock.ev.on('presence.update', m => console.log(m)) + sock.ev.on('chats.update', m => console.log(m)) + sock.ev.on('contacts.update', m => console.log(m)) + + sock.ev.on('connection.update', (update) => { + const { connection, lastDisconnect } = update + if(connection === 'close') { + // reconnect if not logged out + if((lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) { + startSock() + } else { + console.log('connection closed') + } + } + + console.log('connection update', update) + }) + // listen for when the auth credentials is updated + sock.ev.on('creds.update', saveState) + + return sock +} + +startSock() \ No newline at end of file diff --git a/Example/example.ts b/Example/example.ts index 5d51e09..2a89d22 100644 --- a/Example/example.ts +++ b/Example/example.ts @@ -1,153 +1,86 @@ -import { - WAConnection, - MessageType, - Presence, - MessageOptions, - Mimetype, - WALocationMessage, - WA_MESSAGE_STUB_TYPES, - ReconnectMode, - ProxyAgent, - waChatKey, -} from '../src/WAConnection' -import * as fs from 'fs' +import { Boom } from '@hapi/boom' +import P from 'pino' +import makeWASocket, { AnyMessageContent, delay, DisconnectReason, makeInMemoryStore, useSingleFileAuthState } from '../src' -async function example() { - const conn = new WAConnection() // instantiate - conn.autoReconnect = ReconnectMode.onConnectionLost // only automatically reconnect when the connection breaks - conn.logger.level = 'debug' // set to 'debug' to see what kind of stuff you can implement - // attempt to reconnect at most 10 times in a row - conn.connectOptions.maxRetries = 10 - conn.chatOrderingKey = waChatKey(true) // order chats such that pinned chats are on top - conn.on('chats-received', ({ hasNewChats }) => { - console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`) - }) - conn.on('contacts-received', () => { - console.log(`you have ${Object.keys(conn.contacts).length} contacts`) - }) - conn.on('initial-data-received', () => { - console.log('received all initial messages') - }) +// the store maintains the data of the WA connection in memory +// can be written out to a file & read from it +const store = makeInMemoryStore({ logger: P().child({ level: 'debug', stream: 'store' }) }) +store.readFromFile('./baileys_store_multi.json') +// save every 10s +setInterval(() => { + store.writeToFile('./baileys_store_multi.json') +}, 10_000) - // loads the auth file credentials if present - /* Note: one can take this auth_info.json file and login again from any computer without having to scan the QR code, - and get full access to one's WhatsApp. Despite the convenience, be careful with this file */ - fs.existsSync('./auth_info.json') && conn.loadAuthInfo ('./auth_info.json') - // uncomment the following line to proxy the connection; some random proxy I got off of: https://proxyscrape.com/free-proxy-list - //conn.connectOptions.agent = ProxyAgent ('http://1.0.180.120:8080') - await conn.connect() - // credentials are updated on every connect - const authInfo = conn.base64EncodedAuthInfo() // get all the auth info we need to restore this session - fs.writeFileSync('./auth_info.json', JSON.stringify(authInfo, null, '\t')) // save this info to a file +const { state, saveState } = useSingleFileAuthState('./auth_info_multi.json') - console.log('oh hello ' + conn.user.name + ' (' + conn.user.jid + ')') - // uncomment to load all unread messages - //const unread = await conn.loadAllUnreadMessages () - //console.log ('you have ' + unread.length + ' unread messages') +// start a connection +const startSock = () => { + + const sock = makeWASocket({ + logger: P({ level: 'trace' }), + printQRInTerminal: true, + auth: state, + // implement to handle retries + getMessage: async key => { + return { + conversation: 'hello' + } + } + }) - /** - * The universal event for anything that happens - * New messages, updated messages, read & delivered messages, participants typing etc. - */ - conn.on('chat-update', async chat => { - if (chat.presences) { // receive presence updates -- composing, available, etc. - Object.values(chat.presences).forEach(presence => console.log( `${presence.name}'s presence is ${presence.lastKnownPresence} in ${chat.jid}`)) - } - if(chat.imgUrl) { - console.log('imgUrl of chat changed ', chat.imgUrl) - return - } - // only do something when a new message is received - if (!chat.hasNewMessage) { - if(chat.messages) { - console.log('updated message: ', chat.messages.first) - } - return - } + store.bind(sock.ev) + + const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => { + await sock.presenceSubscribe(jid) + await delay(500) + + await sock.sendPresenceUpdate('composing', jid) + await delay(2000) + + await sock.sendPresenceUpdate('paused', jid) + + await sock.sendMessage(jid, msg) + } + + sock.ev.on('chats.set', item => console.log(`recv ${item.chats.length} chats (is latest: ${item.isLatest})`)) + sock.ev.on('messages.set', item => console.log(`recv ${item.messages.length} messages (is latest: ${item.isLatest})`)) + sock.ev.on('contacts.set', item => console.log(`recv ${item.contacts.length} contacts`)) + + sock.ev.on('messages.upsert', async m => { + console.log(JSON.stringify(m, undefined, 2)) - const m = chat.messages.all()[0] // pull the new message from the update - const messageStubType = WA_MESSAGE_STUB_TYPES[m.messageStubType] || 'MESSAGE' - console.log('got notification of type: ' + messageStubType) - - const messageContent = m.message - // if it is not a regular text or media message - if (!messageContent) return + const msg = m.messages[0] + if(!msg.key.fromMe && m.type === 'notify') { + console.log('replying to', m.messages[0].key.remoteJid) + await sock!.sendReadReceipt(msg.key.remoteJid, msg.key.participant, [msg.key.id]) + await sendMessageWTyping({ text: 'Hello there!' }, msg.key.remoteJid) + } - if (m.key.fromMe) { - console.log('relayed my own message') - return - } + }) - let sender = m.key.remoteJid - if (m.key.participant) { - // participant exists if the message is in a group - sender += ' (' + m.key.participant + ')' - } - const messageType = Object.keys (messageContent)[0] // message will always contain one key signifying what kind of message - if (messageType === MessageType.text) { - const text = m.message.conversation - console.log(sender + ' sent: ' + text) - } else if (messageType === MessageType.extendedText) { - const text = m.message.extendedTextMessage.text - console.log(sender + ' sent: ' + text + ' and quoted message: ' + JSON.stringify(m.message)) - } else if (messageType === MessageType.contact) { - const contact = m.message.contactMessage - console.log(sender + ' sent contact (' + contact.displayName + '): ' + contact.vcard) - } else if (messageType === MessageType.location || messageType === MessageType.liveLocation) { - const locMessage = m.message[messageType] as WALocationMessage - console.log(`${sender} sent location (lat: ${locMessage.degreesLatitude}, long: ${locMessage.degreesLongitude})`) - - await conn.downloadAndSaveMediaMessage(m, './Media/media_loc_thumb_in_' + m.key.id) // save location thumbnail + sock.ev.on('messages.update', m => console.log(m)) + sock.ev.on('message-receipt.update', m => console.log(m)) + sock.ev.on('presence.update', m => console.log(m)) + sock.ev.on('chats.update', m => console.log(m)) + sock.ev.on('contacts.upsert', m => console.log(m)) - if (messageType === MessageType.liveLocation) { - console.log(`${sender} sent live location for duration: ${m.duration/60}`) - } - } else { - // if it is a media (audio, image, video, sticker) message - // decode, decrypt & save the media. - // The extension to the is applied automatically based on the media type - try { - const savedFile = await conn.downloadAndSaveMediaMessage(m, './Media/media_in_' + m.key.id) - console.log(sender + ' sent media, saved at: ' + savedFile) - } catch (err) { - console.log('error in decoding message: ' + err) - } - } - // send a reply after 3 seconds - setTimeout(async () => { - await conn.chatRead(m.key.remoteJid) // mark chat read - await conn.updatePresence(m.key.remoteJid, Presence.available) // tell them we're available - await conn.updatePresence(m.key.remoteJid, Presence.composing) // tell them we're composing + sock.ev.on('connection.update', (update) => { + const { connection, lastDisconnect } = update + if(connection === 'close') { + // reconnect if not logged out + if((lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) { + startSock() + } else { + console.log('connection closed') + } + } + + console.log('connection update', update) + }) + // listen for when the auth credentials is updated + sock.ev.on('creds.update', saveState) - const options: MessageOptions = { quoted: m } - let content - let type: MessageType - const rand = Math.random() - if (rand > 0.66) { // choose at random - content = 'hello!' // send a "hello!" & quote the message recieved - type = MessageType.text - } else if (rand > 0.33) { // choose at random - content = { degreesLatitude: 32.123123, degreesLongitude: 12.12123123 } - type = MessageType.location - } else { - content = fs.readFileSync('./Media/ma_gif.mp4') // load the gif - options.mimetype = Mimetype.gif - type = MessageType.video - } - const response = await conn.sendMessage(m.key.remoteJid, content, type, options) - console.log("sent message with ID '" + response.key.id + "' successfully") - }, 3 * 1000) - }) - - /* example of custom functionality for tracking battery */ - conn.on('CB:action,,battery', json => { - const batteryLevelStr = json[2][0][1].value - const batterylevel = parseInt(batteryLevelStr) - console.log('battery level: ' + batterylevel) - }) - conn.on('close', ({reason, isReconnecting}) => ( - console.log ('oh no got disconnected: ' + reason + ', reconnecting: ' + isReconnecting) - )) + return sock } -example().catch((err) => console.log(`encountered error: ${err}`)) \ No newline at end of file +startSock() \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index bc4e5a9..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Adhiraj Singh - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index ca0d523..123a792 100644 --- a/README.md +++ b/README.md @@ -2,217 +2,297 @@ Baileys does not require Selenium or any other browser to be interface with WhatsApp Web, it does so directly using a **WebSocket**. Not running Selenium or Chromimum saves you like **half a gig** of ram :/ - Thank you to [@Sigalor](https://github.com/sigalor/whatsapp-web-reveng) for writing his observations on the workings of WhatsApp Web and thanks to [@Rhymen](https://github.com/Rhymen/go-whatsapp/) for the __go__ implementation. + Baileys supports interacting with the multi-device & web versions of WhatsApp. - Baileys is type-safe, extensible and simple to use. If you require more functionality than provided, it'll super easy for you to write an extension. More on this [here](#writing-custom-functionality). + Thank you to [@pokearaujo](https://github.com/pokearaujo/multidevice) for writing his observations on the workings of WhatsApp Multi-Device. Also, thank you to [@Sigalor](https://github.com/sigalor/whatsapp-web-reveng) for writing his observations on the workings of WhatsApp Web and thanks to [@Rhymen](https://github.com/Rhymen/go-whatsapp/) for the __go__ implementation. + + Baileys is type-safe, extensible and simple to use. If you require more functionality than provided, it'll super easy for you to write an extension. More on this [here](#WritingCustomFunctionality). If you're interested in building a WhatsApp bot, you may wanna check out [WhatsAppInfoBot](https://github.com/adiwajshing/WhatsappInfoBot) and an actual bot built with it, [Messcat](https://github.com/ashokatechmin/Messcat). **Read the docs [here](https://adiwajshing.github.io/Baileys)** **Join the Discord [here](https://discord.gg/7FYURJyqng)** -## Multi-Device Support - -Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo [here](https://github.com/adiwajshing/baileys/tree/multi-device). - -The master branch only supports WA Web at the moment. - ## Example + Do check out & run [example.ts](Example/example.ts) to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal: 1. ``` cd path/to/Baileys ``` -2. ``` npm install ``` -3. ``` npm run example ``` +2. ``` yarn ``` +3. + - ``` yarn example ``` for the multi-device edition + - ``` yarn example:legacy ``` for the legacy web edition ## Install -Create and cd to your NPM project directory and then in terminal, write: -1. stable: `npm install @adiwajshing/baileys` -2. stabl-ish w quicker fixes & latest features: `npm install github:adiwajshing/baileys` -Do note, the library will likely vary if you're using the NPM package, read that [here](https://www.npmjs.com/package/@adiwajshing/baileys) +Right now, the multi-device branch is only available from GitHub, install using: +``` +yarn add github:adiwajshing/baileys#multi-device +``` Then import in your code using: ``` ts -import { WAConnection } from '@adiwajshing/baileys' +import makeWASocket from '@adiwajshing/baileys-md' ``` ## Unit Tests -Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run `npm test`. -You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. -Set the phone number you can randomly send messages to in a `.env` file with `TEST_JID=1234@s.whatsapp.net` +TODO ## Connecting + ``` ts -import { WAConnection } from '@adiwajshing/baileys' +import makeWASocket from '@adiwajshing/baileys-md' async function connectToWhatsApp () { - const conn = new WAConnection() - // called when WA sends chats - // this can take up to a few minutes if you have thousands of chats! - conn.on('chats-received', async ({ hasNewChats }) => { - console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`) - - const unread = await conn.loadAllUnreadMessages () - console.log ("you have " + unread.length + " unread messages") + const sock = makeWASocket({ + // can provide additional config here + printQRInTerminal: true }) - // called when WA sends chats - // this can take up to a few minutes if you have thousands of contacts! - conn.on('contacts-received', () => { - console.log('you have ' + Object.keys(conn.contacts).length + ' contacts') + sock.ev.on('connection.update', (update) => { + const { connection, lastDisconnect } = update + if(connection === 'close') { + const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut + console.log('connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect) + // reconnect if not logged out + if(shouldReconnect) { + sock = startSock() + } + } else if(connection === 'open') { + console.log('opened connection') + } }) + sock.ev.on('messages.upsert', m => { + console.log(JSON.stringify(m, undefined, 2)) - await conn.connect () - conn.on('chat-update', chatUpdate => { - // `chatUpdate` is a partial object, containing the updated properties of the chat - // received a new message - if (chatUpdate.messages && chatUpdate.count) { - const message = chatUpdate.messages.all()[0] - console.log (message) - } else console.log (chatUpdate) // see updates (can be archived, pinned etc.) + console.log('replying to', m.messages[0].key.remoteJid) + await sock.sendMessage(m.messages[0].key.remoteJid!, { text: 'Hello there!' }) }) } // run in main file -connectToWhatsApp () -.catch (err => console.log("unexpected error: " + err) ) // catch any errors +connectToWhatsApp() ``` 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! -Do note, the `conn.chats` object is a [KeyedDB](https://github.com/adiwajshing/keyed-db). This is done for the following reasons: -- Most applications require chats to be ordered in descending order of time. (`KeyedDB` does this in `log(N)` time) -- Most applications require pagination of chats (Use `chats.paginated()`) -- Most applications require **O(1)** access to chats via the chat ID. (Use `chats.get(jid)` with `KeyedDB`) +**Note:** install `qrcode-terminal` using `yarn add qrcode-terminal` to auto-print the QR to the terminal. + +## Notable Differences Between Baileys v3 & v4 + +1. Baileys has been written from the ground up to have a more "functional" structure. This is done primarily for simplicity & more testability +2. The Baileys event emitter will emit all events and be used to generate a source of truth for the connected user's account. Access the event emitter using (`sock.ev`) +3. Baileys no longer maintains an internal state of chats/contacts/messages. You should ideally take this on your own, simply because your state in MD is its own source of truth & there is no one-size-fits-all way to handle the storage for this. However, a simple storage extension has been provided. This also serves as a good demonstration of how to use the Baileys event emitter to construct a source of truth. +4. A baileys "socket" is meant to be a temporary & disposable object -- this is done to maintain simplicity & prevent bugs. I felt the entire Baileys object became too bloated as it supported too many configurations. You're encouraged to write your own implementation to handle missing functionality. +5. Moreover, Baileys does not offer an inbuilt reconnect mechanism anymore (though it's super easy to set one up on your own with your own rules, check the example script) ## Configuring the Connection -You can configure the connection via the `connectOptions` property. You can even specify an HTTPS proxy. For example: +You can configure the connection by passing a `SocketConfig` object. +The entire `SocketConfig` structure is mentioned here with default values: ``` ts -import { WAConnection, ProxyAgent } from '@adiwajshing/baileys' - -const conn = new WAConnecion () -conn.connectOptions.agent = ProxyAgent ('http://some-host:1234') - -await conn.connect () -console.log ("oh hello " + conn.user.name + "! You connected via a proxy") -``` - -The entire `WAConnectOptions` struct is mentioned here with default values: -``` ts -conn.connectOptions = { - /** fails the connection if no data is received for X seconds */ - maxIdleTimeMs?: 60_000, - /** maximum attempts to connect */ - maxRetries?: 10, - /** max time for the phone to respond to a connectivity test */ - phoneResponseTime?: 15_000, - /** minimum time between new connections */ - connectCooldownMs?: 4000, - /** agent used for WS connections (could be a proxy agent) */ - agent?: Agent = undefined, - /** agent used for fetch requests -- uploading/downloading media */ - fetchAgent?: Agent = undefined, - /** always uses takeover for connecting */ - alwaysUseTakeover: true - /** log QR to terminal */ - logQR: true -} as WAConnectOptions +type SocketConfig = { + /** provide an auth state object to maintain the auth state */ + auth?: AuthenticationState + /** the WS url to connect to WA */ + waWebSocketUrl: string | URL + /** Fails the connection if the connection times out in this time interval or no data is received */ + connectTimeoutMs: number + /** Default timeout for queries, undefined for no timeout */ + defaultQueryTimeoutMs: number | undefined + /** ping-pong interval for WS connection */ + keepAliveIntervalMs: number + /** proxy agent */ + agent?: Agent + /** pino logger */ + logger: Logger + /** version to connect with */ + version: WAVersion + /** override browser config */ + browser: WABrowserDescription + /** agent used for fetch requests -- uploading/downloading media */ + fetchAgent?: Agent + /** should the QR be printed in the terminal */ + printQRInTerminal: boolean + /** + * fetch a message from your store + * implement this so that messages failed to send (solves the "this message can take a while" issue) can be retried + * */ + getMessage: (key: proto.IMessageKey) => Promise +} ``` ## Saving & Restoring Sessions You obviously don't want to keep scanning the QR code every time you want to connect. -So, you can save the credentials to log back in via: +So, you can load the credentials to log back in: ``` ts +import makeWASocket, { BufferJSON, useSingleFileAuthState } from '@adiwajshing/baileys-md' import * as fs from 'fs' -const conn = new WAConnection() +// utility function to help save the auth state in a single file +// it's utility ends at demos -- as re-writing a large file over and over again is very inefficient +const { state, saveState } = useSingleFileAuthState('./auth_info_multi.json') +// will use the given state to connect +// so if valid credentials are available -- it'll connect without QR +const conn = makeSocket({ auth: state }) // this will be called as soon as the credentials are updated -conn.on ('open', () => { - // save credentials whenever updated - console.log (`credentials updated!`) - const authInfo = conn.base64EncodedAuthInfo() // get all the auth info we need to restore this session - fs.writeFileSync('./auth_info.json', JSON.stringify(authInfo, null, '\t')) // save this info to a file -}) -await conn.connect() // connect +sock.ev.on ('creds.update', saveState) ``` -Then, to restore a session: +**Note**: When a message is received/sent, due to signal sessions needing updating, the auth keys (`authState.keys`) will update. Whenever that happens, you must save the updated keys. Not doing so will prevent your messages from reaching the recipient & other unexpected consequences. The `useSingleFileAuthState` function automatically takes care of that, but for any other serious implementation -- you will need to be very careful with the key state management. + +## Listening to Connection Updates + +Baileys now fires the `connection.update` event to let you know something has updated in the connection. This data has the following structure: ``` ts -const conn = new WAConnection() -conn.loadAuthInfo ('./auth_info.json') // will load JSON credentials from file -await conn.connect() -// yay connected without scanning QR -/* - Optionally, you can load the credentials yourself from somewhere - & pass in the JSON object to loadAuthInfo () as well. -*/ -``` - -If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too: -``` ts -conn.loadAuthInfo ('./auth_info_browser.json') -await conn.connect() // works the same -``` -See the browser credentials type in the docs. - -**Note**: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the `open` event. Not doing so *may* lead WA to log you out after a few weeks with a 419 error code. - -## QR Callback - -If you want to do some custom processing with the QR code used to authenticate, you can register for the following event: -``` ts -conn.on('qr', qr => { - // Now, use the 'qr' string to display in QR UI or send somewhere +type ConnectionState = { + /** connection is now open, connecting or closed */ + connection: WAConnectionState + /** the error that caused the connection to close */ + lastDisconnect?: { + error: Error + date: Date + } + /** is this a new login */ + isNewLogin?: boolean + /** the current QR code */ + qr?: string + /** has the device received all pending notifications while it was offline */ + receivedPendingNotifications?: boolean } -await conn.connect () ``` +Note: this also offers any updates to the QR + ## Handling Events -Baileys now uses the EventEmitter syntax for events. +Baileys uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code. -Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone. +The events are typed up in a type map, as mentioned here: ``` ts -/** when the connection has opened successfully */ -on (event: 'open', listener: (result: WAOpenResult) => void): this -/** when the connection is opening */ -on (event: 'connecting', listener: () => void): this -/** when the connection has closed */ -on (event: 'close', listener: (err: {reason?: DisconnectReason | string, isReconnecting: boolean}) => void): this -/** when the socket is closed */ -on (event: 'ws-close', listener: (err: {reason?: DisconnectReason | string}) => void): this -/** when a new QR is generated, ready for scanning */ -on (event: 'qr', listener: (qr: string) => void): this -/** when the connection to the phone changes */ -on (event: 'connection-phone-change', listener: (state: {connected: boolean}) => void): this -/** when a contact is updated */ -on (event: 'contact-update', listener: (update: WAContactUpdate) => void): this -/** when a new chat is added */ -on (event: 'chat-new', listener: (chat: WAChat) => void): this -/** when contacts are sent by WA */ -on (event: 'contacts-received', listener: (u: { updatedContacts: Partial[] }) => void): this -/** when chats are sent by WA, and when all messages are received */ -on (event: 'chats-received', listener: (update: {hasNewChats?: boolean}) => void): this -/** when all initial messages are received from WA */ -on (event: 'initial-data-received', listener: (update: {chatsWithMissingMessages: { jid: string, count: number }[] }) => void): this -/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */ -on (event: 'chats-update', listener: (chats: WAChatUpdate[]) => void): this -/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */ -on (event: 'chat-update', listener: (chat: WAChatUpdate) => void): this -/** when participants are added to a group */ -on (event: 'group-participants-update', listener: (update: {jid: string, participants: string[], actor?: string, action: WAParticipantAction}) => void): this -/** when the group is updated */ -on (event: 'group-update', listener: (update: Partial & {jid: string, actor?: string}) => void): this -/** when WA sends back a pong */ -on (event: 'received-pong', listener: () => void): this -/** when a user is blocked or unblockd */ -on (event: 'blocklist-update', listener: (update: BlocklistUpdate) => void): this + +export type BaileysEventMap = { + /** connection state has been updated -- WS closed, opened, connecting etc. */ + 'connection.update': Partial + /** auth credentials updated -- some pre key state, device ID etc. */ + 'creds.update': Partial + /** set chats (history sync), chats are reverse chronologically sorted */ + 'chats.set': { chats: Chat[], isLatest: boolean } + /** set messages (history sync), messages are reverse chronologically sorted */ + 'messages.set': { messages: WAMessage[], isLatest: boolean } + /** set contacts (history sync) */ + 'contacts.set': { contacts: Contact[] } + /** upsert chats */ + 'chats.upsert': Chat[] + /** update the given chats */ + 'chats.update': Partial[] + /** delete chats with given ID */ + 'chats.delete': string[] + /** presence of contact in a chat updated */ + 'presence.update': { id: string, presences: { [participant: string]: PresenceData } } + + 'contacts.upsert': Contact[] + 'contacts.update': Partial[] + + 'messages.delete': { keys: WAMessageKey[] } | { jid: string, all: true } + 'messages.update': WAMessageUpdate[] + /** + * add/update the given messages. If they were received while the connection was online, + * the update will have type: "notify" + * */ + 'messages.upsert': { messages: WAMessage[], type: MessageUpdateType } + + 'message-info.update': MessageInfoUpdate[] + + 'groups.update': Partial[] + /** apply an action to participants in a group */ + 'group-participants.update': { id: string, participants: string[], action: ParticipantAction } + + 'blocklist.set': { blocklist: string[] } + 'blocklist.update': { blocklist: string[], type: 'add' | 'remove' } +} +``` + +You can listen to these events like this: +``` ts + +const sock = makeWASocket() +sock.ev.on('messages.upsert', ({ messages }) => { + console.log('got messages', messages) +}) + +``` + +## Implementing a Data Store + +As mentioned earlier, Baileys does not come with a defacto storage for chats, contacts, messages. However, a simple in-memory implementation has been provided. The store listens for chat updates, new messages, message updates etc. to always have an up to date version of the data. + +It can be used as follows: + +``` ts +import makeWASocket, { makeInMemoryStore } from '@adiwajshing/baileys-md' +// the store maintains the data of the WA connection in memory +// can be written out to a file & read from it +const store = makeInMemoryStore({ }) +// can be read from a file +store.readFromFile('./baileys_store.json') +// saves the state to a file every 10s +setInterval(() => { + store.writeToFile('./baileys_store.json') +}, 10_000) + +const sock = makeWASocket({ }) +// will listen from this socket +// the store can listen from a new socket once the current socket outlives its lifetime +store.bind(sock.ev) + +sock.ev.on('chats.set', () => { + // can use "store.chats" however you want, even after the socket dies out + // "chats" => a KeyedDB instance + console.log('got chats', store.chats.all()) +}) + +sock.ev.on('contacts.set', () => { + console.log('got contacts', Object.values(store.contacts)) +}) + +``` + +The store also provides some simple functions such as `loadMessages` that utilize the store to speed up data retrieval. + +**Note:** I highly recommend building your own data store especially for MD connections, as storing someone's entire chat history in memory is a terrible waste of RAM. + +## Using the Legacy Version + +The API for the legacy and MD versions has been made as similar as possible so ya'll can switch between them seamlessly. + +Example on using the eg. version: +``` ts +import P from "pino" +import { Boom } from "@hapi/boom" +import { makeWALegacySocket } from '@adiwajshing/baileys-md' + +// store can be used with legacy version as well +const store = makeInMemoryStore({ logger: P().child({ level: 'debug', stream: 'store' }) }) + +const sock = makeWALegacySocket({ + logger: P({ level: 'debug' }), + printQRInTerminal: true, + auth: state +}) +// bind to the socket +store.bind(sock.ev) +``` + +If you need a type representing either the legacy or MD version: +``` ts +// this type can have any of the socket types underneath +import { AnyWASocket } from '@adiwajshing/baileys-md' ``` ## Sending Messages @@ -222,13 +302,16 @@ on (event: 'blocklist-update', listener: (update: BlocklistUpdate) => void): thi ### Non-Media Messages ``` ts -import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys' +import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys-md' const id = 'abcd@s.whatsapp.net' // the WhatsApp ID // send a simple text! -const sentMsg = await conn.sendMessage (id, 'oh hello there', MessageType.text) +const sentMsg = await sock.sendMessage(id, { text: 'oh hello there' }) // send a location! -const sentMsg = await conn.sendMessage(id, {degreesLatitude: 24.121231, degreesLongitude: 55.1121221}, MessageType.location) +const sentMsg = await sock.sendMessage( + id, + { location: { degreesLatitude: 24.121231, degreesLongitude: 55.1121221 } } +) // send a contact! const vcard = 'BEGIN:VCARD\n' // metadata of the contact card + 'VERSION:3.0\n' @@ -236,38 +319,74 @@ const vcard = 'BEGIN:VCARD\n' // metadata of the contact card + 'ORG:Ashoka Uni;\n' // the organization of the contact + 'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n' // WhatsApp ID + phone number + 'END:VCARD' -const sentMsg = await conn.sendMessage(id, {displayname: "Jeff", vcard: vcard}, MessageType.contact) -// send a list message! -const rows = [ - {title: 'Row 1', description: "Hello it's description 1", rowId:"rowid1"}, - {title: 'Row 2', description: "Hello it's description 2", rowId:"rowid2"} -] - -const sections = [{title: "Section 1", rows: rows}] - -const button = { - buttonText: 'Click Me!', - description: "Hello it's list message", - sections: sections, - listType: 1 -} - -const sendMsg = await conn.sendMessage(id, button, MessageType.listMessage) +const sentMsg = await sock.sendMessage( + id, + { + contacts: { + displayName: 'Jeff', + contacts: [{ vcard }] + } + } +) // send a buttons message! const buttons = [ {buttonId: 'id1', buttonText: {displayText: 'Button 1'}, type: 1}, - {buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1} + {buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1}, + {buttonId: 'id3', buttonText: {displayText: 'Button 3'}, type: 1} ] const buttonMessage = { - contentText: "Hi it's button message", - footerText: 'Hello World', + text: "Hi it's button message", + footer: 'Hello World', buttons: buttons, headerType: 1 } -const sendMsg = await conn.sendMessage(id, buttonMessage, MessageType.buttonsMessage) +const sendMsg = await sock.sendMessage(id, buttonMessage) + +//send a template message! +const templateButtons = [ + {index: 1, urlButton: {displayText: '⭐ Star Baileys on GitHub!', url: 'https://github.com/adiwajshing/Baileys'}}, + {index: 2, callButton: {displayText: 'Call me!', phoneNumber: '+1 (234) 5678-901'}}, + {index: 3, quickReplyButton: {displayText: 'This is a reply, just like normal buttons!', id: 'id-like-buttons-message'}}, +] + +const templateMessage = { + text: "Hi it's a template message", + footer: 'Hello World', + templateButtons: templateButtons +} + +const sendMsg = await sock.sendMessage(id, templateMessage) + +// send a list message! +const sections = [ + { + title: "Section 1", + rows: [ + {title: "Option 1", rowId: "option1"}, + {title: "Option 2", rowId: "option2", description: "This is a description"} + ] + }, + { + title: "Section 2", + rows: [ + {title: "Option 3", rowId: "option3"}, + {title: "Option 4", rowId: "option4", description: "This is a description V2"} + ] + }, +] + +const listMessage = { + text: "This is a list", + footer: "nice footer, link: https://google.com", + title: "Amazing boldfaced list title", + buttonText: "Required, text on the button to view the list", + sections +} + +const sendMsg = await sock.sendMessage(id, listMessage) ``` ### Media Messages @@ -277,46 +396,75 @@ Sending media (video, stickers, images) is easier & more efficient than ever. - When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream. ``` ts -import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys' +import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys-md' // Sending gifs -await conn.sendMessage( +await sock.sendMessage( id, - fs.readFileSync("Media/ma_gif.mp4"), // load a gif and send it - MessageType.video, - { mimetype: Mimetype.gif, caption: "hello!" } -) -await conn.sendMessage( - id, - { url: 'Media/ma_gif.mp4' }, // send directly from local file - MessageType.video, - { mimetype: Mimetype.gif, caption: "hello!" } + { + video: fs.readFileSync("Media/ma_gif.mp4"), + caption: "hello!", + gifPlayback: true + } ) -await conn.sendMessage( +await sock.sendMessage( id, - { url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5' }, // send directly from remote url! - MessageType.video, - { mimetype: Mimetype.gif, caption: "hello!" } + { + video: "./Media/ma_gif.mp4", + caption: "hello!", + gifPlayback: true + } ) // send an audio file -await conn.sendMessage( +await sock.sendMessage( id, + { audio: { url: "./Media/audio.mp3" }, mimetype: 'audio/mp4' } { url: "Media/audio.mp3" }, // can send mp3, mp4, & ogg - MessageType.audio, - { mimetype: Mimetype.mp4Audio } // some metadata (can't have caption in audio) ) -``` +// send a buttons message with image header! +const buttons = [ + {buttonId: 'id1', buttonText: {displayText: 'Button 1'}, type: 1}, + {buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1}, + {buttonId: 'id3', buttonText: {displayText: 'Button 3'}, type: 1} +] + +const buttonMessage = { + image: {url: 'https://example.com/image.jpeg'}, + caption: "Hi it's button message", + footerText: 'Hello World', + buttons: buttons, + headerType: 4 +} + +const sendMsg = await sock.sendMessage(id, buttonMessage) + +//send a template message with an image **attached**! +const templateButtons = [ + {index: 1, urlButton: {displayText: '⭐ Star Baileys on GitHub!', url: 'https://github.com/adiwajshing/Baileys'}}, + {index: 2, callButton: {displayText: 'Call me!', phoneNumber: '+1 (234) 5678-901'}}, + {index: 3, quickReplyButton: {displayText: 'This is a reply, just like normal buttons!', id: 'id-like-buttons-message'}}, +] + +const buttonMessage = { + text: "Hi it's a template message", + footer: 'Hello World', + templateButtons: templateButttons, + image: {url: 'https://example.com/image.jpeg'} +} + +const sendMsg = await sock.sendMessage(id, templateMessage) +``` ### Notes - `id` is the WhatsApp ID of the person or group you're sending the message to. - - It must be in the format ```[country code without +][phone number]@s.whatsapp.net```, for example ```19999999999@s.whatsapp.net``` for people. For groups, it must be in the format ``` 123456789-123345@g.us ```. + - It must be in the format ```[country code][phone number]@s.whatsapp.net```, for example ```+19999999999@s.whatsapp.net``` for people. For groups, it must be in the format ``` 123456789-123345@g.us ```. - For broadcast lists it's `[timestamp of creation]@broadcast`. - For stories, the ID is `status@broadcast`. -- For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have `ffmpeg` installed on your system. -- **MessageOptions**: some extra info about the message. It can have the following __optional__ values: +- For media messages, the thumbnail can be generated automatically for images & stickers provided you add `jimp` or `sharp` as a dependency in your project using `yarn add jimp` or `yarn add sharp`. Thumbnails for videos can also be generated automatically, though, you need to have `ffmpeg` installed on your system. +- **MiscGenerationOptions**: some extra info about the message. It can have the following __optional__ values: ``` ts const info: MessageOptions = { quoted: quotedMessage, // the message you want to quote @@ -328,7 +476,7 @@ await conn.sendMessage( Do not enter this field if you want to automatically generate a thumb */ mimetype: Mimetype.pdf, /* (for media messages) specify the type of media (optional for all media types except documents), - import {Mimetype} from '@adiwajshing/baileys' + import {Mimetype} from '@adiwajshing/baileys-md' */ filename: 'somefile.pdf', // (for media messages) file name for the media /* will send audio messages as voice notes, if set to true */ @@ -343,19 +491,22 @@ await conn.sendMessage( ## Forwarding Messages ``` ts -const messages = await conn.loadConversation ('1234@s.whatsapp.net', 1) -const message = messages[0] // get the last message from this conversation -await conn.forwardMessage ('455@s.whatsapp.net', message) // WA forward the message! +const msg = getMessageFromStore('455@s.whatsapp.net', 'HSJHJWH7323HSJSJ') // implement this on your end +await sock.sendMessage('1234@s.whatsapp.net', { forward: msg }) // WA forward the message! ``` ## Reading Messages +A set of message IDs must be explicitly marked read now. +Cannot mark an entire "chat" read as it were with Baileys Web. +This does mean you have to keep track of unread messages. + ``` ts const id = '1234-123@g.us' const messageID = 'AHASHH123123AHGA' // id of the message you want to read +const participant = '912121232@s.whatsapp.net' // the ID of the user that sent the message (undefined for individual chats) -await conn.chatRead (id) // mark all messages in chat as read (equivalent of opening a chat in WA) -await conn.chatRead (id, 'unread') // mark the chat as unread +await sock.sendReadReceipt(id, participant, [messageID]) ``` The message ID is the unique identifier of the message that you are marking as read. On a `WAMessage`, the `messageID` can be accessed using ```messageID = message.key.id```. @@ -363,18 +514,12 @@ The message ID is the unique identifier of the message that you are marking as r ## Update Presence ``` ts -import { Presence } from '@adiwajshing/baileys' -await conn.updatePresence(id, Presence.available) +await sock.sendPresenceUpdate('available', id) ``` This lets the person/group with ``` id ``` know whether you're online, offline, typing etc. where ``` presence ``` can be one of the following: ``` ts -export enum Presence { - available = 'available', // "online" - composing = 'composing', // "typing..." - recording = 'recording', // "recording..." - paused = 'paused' // stopped typing, back to "online" -} +type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused' ``` The presence expires after about 10 seconds. @@ -383,16 +528,24 @@ The presence expires after about 10 seconds. If you want to save the media you received ``` ts -import { MessageType } from '@adiwajshing/baileys' -conn.on ('message-new', async m => { +import { writeFile } from 'fs/promises' +import { downloadContentFromMessage } from '@adiwajshing/baileys-md' + +sock.ev.on('messages.upsert', async ({ messages }) => { + const m = messages[0] + if (!m.message) return // if there is no text or media message const messageType = Object.keys (m.message)[0]// get what type of message it is -- text, image, video - // if the message is not a text message - if (messageType !== MessageType.text && messageType !== MessageType.extendedText) { - const buffer = await conn.downloadMediaMessage(m) // to decrypt & use as a buffer - - const savedFilename = await conn.downloadAndSaveMediaMessage (m) // to decrypt & save to file - console.log(m.key.remoteJid + " sent media, saved at: " + savedFilename) + // if the message is an image + if (messageType === 'imageMessage') { + // download stream + const stream = await downloadContentFromMessage(m.message.imageMessage, 'image') + let buffer = Buffer.from([]) + for await(const chunk of stream) { + buffer = Buffer.concat([buffer, chunk]) + } + // save to file + await writeFile('./my-download.jpeg', buffer) } } ``` @@ -401,108 +554,112 @@ conn.on ('message-new', async m => { ``` ts const jid = '1234@s.whatsapp.net' // can also be a group -const response = await conn.sendMessage (jid, 'hello!', MessageType.text) // send a message - -await conn.deleteMessage (jid, {id: response.messageID, remoteJid: jid, fromMe: true}) // will delete the sent message for everyone! -await conn.clearMessage (jid, {id: response.messageID, remoteJid: jid, fromMe: true}) // will delete the sent message for only you! +const response = await sock.sendMessage(jid, { text: 'hello!' }) // send a message +// sends a message to delete the given message +// this deletes the message for everyone +await sock.sendMessage(jid, { delete: response.key }) ``` +Note: deleting for oneself is supported via `chatModify` (next section) + ## Modifying Chats -``` ts -const jid = '1234@s.whatsapp.net' // can also be a group -await conn.modifyChat (jid, ChatModification.archive) // archive chat -await conn.modifyChat (jid, ChatModification.unarchive) // unarchive chat +WA uses an encrypted form of communication to send chat/app updates. This has been implemented mostly and you can send the following updates: -const response = await conn.modifyChat (jid, ChatModification.pin) // pin the chat -await conn.modifyChat (jid, ChatModification.unpin) // unpin it +- Archive a chat + ``` ts + const lastMsgInChat = await getLastMessageInChat('123456@s.whatsapp.net') // implement this on your end + await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, '123456@s.whatsapp.net') + ``` +- Mute/unmute a chat + ``` ts + // mute for 8 hours + await sock.chatModify({ mute: 8*60*60*1000 }, '123456@s.whatsapp.net', []) + // unmute + await sock.chatModify({ mute: null }, '123456@s.whatsapp.net', []) + ``` +- Mark a chat read/unread + ``` ts + const lastMsgInChat = await getLastMessageInChat('123456@s.whatsapp.net') // implement this on your end + // mark it unread + await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, '123456@s.whatsapp.net') + ``` -await conn.modifyChat (jid, ChatModification.mute, 8*60*60*1000) // mute for 8 hours -setTimeout (() => { - conn.modifyChat (jid, ChatModification.unmute) -}, 5000) // unmute after 5 seconds +- Delete message for me + ``` ts + await sock.chatModify( + { clear: { message: { id: 'ATWYHDNNWU81732J', fromMe: true } } }, + '123456@s.whatsapp.net', + [] + ) + ``` -await conn.modifyChat (jid, ChatModification.delete) // will delete the chat (can be a group or broadcast list as well) -``` - -**Note:** to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the `WAChat` objects of the respective chats, as a `mute` or `pin` property. +Note: if you mess up one of your updates, WA can log you out of all your devices and you'll have to login again. ## Disappearing Messages ``` ts const jid = '1234@s.whatsapp.net' // can also be a group // turn on disappearing messages -await conn.toggleDisappearingMessages( +await sock.sendMessage( jid, - WA_DEFAULT_EPHEMERAL // this is 1 week in seconds -- how long you want messages to appear for -) -// will automatically send as a disappearing message -await conn.sendMessage(jid, 'Hello poof!', MessageType.text) + // this is 1 week in seconds -- how long you want messages to appear for + { disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL } +) +// will send as a disappearing message +await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL }) // turn off disappearing messages -await conn.toggleDisappearingMessages(jid, 0) +await sock.sendMessage( + jid, + { disappearingMessagesInChat: false } +) ``` ## Misc -- To load chats in a paginated manner - ``` ts - const {chats, cursor} = await conn.loadChats (25) - if (cursor) { - const moreChats = await conn.loadChats (25, cursor) // load the next 25 chats - } - ``` - To check if a given ID is on WhatsApp - Note: this method falls back to using `https://wa.me` to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet. ``` ts const id = '123456' - const exists = await conn.isOnWhatsApp (id) - if (exists) console.log (`${id} exists on WhatsApp, as jid: ${exists.jid}`) + const [result] = await sock.onWhatsApp(id) + if (result.exists) console.log (`${id} exists on WhatsApp, as jid: ${result.jid}`) ``` - To query chat history on a group or with someone - ``` ts - // query the last 25 messages (replace 25 with the number of messages you want to query) - const messages = await conn.loadMessages ("xyz-abc@g.us", 25) - console.log("got back " + messages.length + " messages") - ``` - You can also load the entire conversation history if you want - ``` ts - await conn.loadAllMessages ("xyz@c.us", message => console.log("Loaded message with ID: " + message.key.id)) - console.log("queried all messages") // promise resolves once all messages are retrieved - ``` + TODO, if possible - To get the status of some person ``` ts - const status = await conn.getStatus ("xyz@c.us") // leave empty to get your own status + const status = await sock.fetchStatus("xyz@s.whatsapp.net") console.log("status: " + status) ``` - To get the display picture of some person/group ``` ts - const ppUrl = await conn.getProfilePicture ("xyz@g.us") // leave empty to get your own + // for low res picture + const ppUrl = await sock.profilePictureUrl("xyz@g.us") console.log("download profile picture from: " + ppUrl) + // for high res picture + const ppUrl = await sock.profilePictureUrl("xyz@g.us", 'image') ``` - To change your display picture or a group's ``` ts const jid = '111234567890-1594482450@g.us' // can be your own too - const img = fs.readFileSync ('new-profile-picture.jpeg') // can be PNG also - await conn.updateProfilePicture (jid, img) + await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' }) ``` - To get someone's presence (if they're typing, online) ``` ts // the presence update is fetched and called here - conn.on ('CB:Presence', json => console.log(json.id + " presence is " + json.type)) - await conn.requestPresenceUpdate ("xyz@c.us") // request the update - ``` -- To search through messages - ``` ts - const response = await conn.searchMessages ('so cool', null, 25, 1) // search in all chats - console.log (`got ${response.messages.length} messages in search`) - - const response2 = await conn.searchMessages ('so cool', '1234@c.us', 25, 1) // search in given chat + sock.ev.on('presence-update', json => console.log(json)) + // request updates for a chat + await sock.presenceSubscribe("xyz@s.whatsapp.net") ``` - To block or unblock user ``` ts - await conn.blockUser ("xyz@c.us", "add") // Block user - await conn.blockUser ("xyz@c.us", "remove") // Unblock user + await sock.updateBlockStatus("xyz@s.whatsapp.net", "block") // Block user + await sock.updateBlockStatus("xyz@s.whatsapp.net", "unblock") // Unblock user + ``` +- To get a business profile, such as description, category + ```ts + const profile = await sock.getBusinessProfile("xyz@s.whatsapp.net") + console.log("business description: " + profile.description + ", category: " + profile.category) ``` Of course, replace ``` xyz ``` with an actual ID. @@ -510,76 +667,72 @@ Of course, replace ``` xyz ``` with an actual ID. - To create a group ``` ts // title & participants - const group = await conn.groupCreate ("My Fab Group", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"]) + const group = await sock.groupCreate("My Fab Group", ["1234@s.whatsapp.net", "4564@s.whatsapp.net"]) console.log ("created group with id: " + group.gid) - conn.sendMessage(group.gid, "hello everyone", MessageType.extendedText) // say hello to everyone on the group + sock.sendMessage(group.id, { text: 'hello there' }) // say hello to everyone on the group ``` -- To add people to a group +- To add/remove people to a group or demote/promote people ``` ts // id & people to add to the group (will throw error if it fails) - const response = await conn.groupAdd ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"]) - ``` -- To make/demote admins on a group - ``` ts - // id & people to make admin (will throw error if it fails) - await conn.groupMakeAdmin ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"]) - await conn.groupDemoteAdmin ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"]) // demote admins + const response = await sock.groupParticipantsUpdate( + "abcd-xyz@g.us", + ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"], + "add" // replace this parameter with "remove", "demote" or "promote" + ) ``` - To change the group's subject ``` ts - await conn.groupUpdateSubject("abcd-xyz@g.us", "New Subject!") + await sock.groupUpdateSubject("abcd-xyz@g.us", "New Subject!") ``` - To change the group's description ``` ts - await conn.groupUpdateDescription("abcd-xyz@g.us", "This group has a new description") + await sock.groupUpdateDescription("abcd-xyz@g.us", "New Description!") ``` - To change group settings ``` ts - import { GroupSettingChange } from '@adiwajshing/baileys' // only allow admins to send messages - await conn.groupSettingChange ("abcd-xyz@g.us", GroupSettingChange.messageSend, true) + await sock.groupSettingUpdate("abcd-xyz@g.us", 'announcement') // allow everyone to modify the group's settings -- like display picture etc. - await conn.groupSettingChange ("abcd-xyz@g.us", GroupSettingChange.settingsChange, false) + await sock.groupSettingUpdate("abcd-xyz@g.us", 'unlocked') // only allow admins to modify the group's settings - await conn.groupSettingChange ("abcd-xyz@g.us", GroupSettingChange.settingsChange, true) + await sock.groupSettingUpdate("abcd-xyz@g.us", 'locked') ``` - To leave a group ``` ts - await conn.groupLeave ("abcd-xyz@g.us") // (will throw error if it fails) + await sock.groupLeave("abcd-xyz@g.us") // (will throw error if it fails) ``` - To get the invite code for a group ``` ts - const code = await conn.groupInviteCode ("abcd-xyz@g.us") + const code = await sock.groupInviteCode("abcd-xyz@g.us") console.log("group code: " + code) ``` +- To revoke the invite code in a group + ```ts + const code = await sock.groupRevokeInvite("abcd-xyz@g.us") + console.log("New group code: " + code) + ``` - To query the metadata of a group ``` ts - const metadata = await conn.groupMetadata ("abcd-xyz@g.us") - console.log(json.id + ", title: " + json.subject + ", description: " + json.desc) - - // Or if you've left the group -- call this - const metadata2 = await conn.groupMetadataMinimal ("abcd-xyz@g.us") + const metadata = await sock.groupMetadata("abcd-xyz@g.us") + console.log(metadata.id + ", title: " + metadata.subject + ", description: " + metadata.desc) ``` - To join the group using the invitation code ``` ts - const response = await conn.acceptInvite ("xxx") - console.log("joined to: " + response.gid) + const response = await sock.groupAcceptInvite("xxx") + console.log("joined to: " + response) ``` Of course, replace ``` xxx ``` with invitation code. -- To revokes the current invite link of a group - ``` ts - const response = await conn.revokeInvite ("abcd-xyz@g.us") - console.log("new group code: " + response.code) - ``` ## Broadcast Lists & Stories +**Note:** messages cannot be sent to broadcast lists from the MD version right now + - You can send messages to broadcast lists the same way you send messages to groups & individual chats. - Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them. - Broadcast IDs are in the format `12345678@broadcast` - To query a broadcast list's recipients & name: ``` ts - const bList = await conn.getBroadcastListInfo ("1234@broadcast") + const bList = await sock.getBroadcastListInfo("1234@broadcast") console.log (`list name: ${bList.name}, recps: ${bList.recipients}`) ``` @@ -588,53 +741,34 @@ Baileys is written, keeping in mind, that you may require other custom functiona First, enable the logging of unhandled messages from WhatsApp by setting ``` ts -conn.logger.level = 'debug' +const sock = makeWASocket({ + logger: P({ level: 'debug' }), +}) ``` This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples: 1. Functionality to track of the battery percentage of your phone. You enable logging and you'll see a message about your battery pop up in the console: - ```s22, ["action",null,[["battery",{"live":"false","value":"52"},null]]] ``` + ```{"level":10,"fromMe":false,"frame":{"tag":"ib","attrs":{"from":"@s.whatsapp.net"},"content":[{"tag":"edge_routing","attrs":{},"content":[{"tag":"routing_info","attrs":{},"content":{"type":"Buffer","data":[8,2,8,5]}}]}]},"msg":"communication"} ``` - You now know what a battery update looks like. It'll have the following characteristics. - - Given ```const bMessage = ["action",null,[["battery",{"live":"false","value":"52"},null]]]``` - - ```bMessage[0]``` is always ``` "action" ``` - - ```bMessage[1]``` is always ``` null ``` - - ```bMessage[2][0][0]``` is always ``` "battery" ``` + The "frame" is what the message received is, it has three components: + - `tag` -- what this frame is about (eg. message will have "message") + - `attrs` -- a string key-value pair with some metadata (contains ID of the message usually) + - `content` -- the actual data (eg. a message node will have the actual message content in it) + - read more about this format [here](/src/WABinary/readme.md) Hence, you can register a callback for an event using the following: ``` ts - conn.on (`CB:action,,battery`, json => { - const batteryLevelStr = json[2][0][1].value - const batterylevel = parseInt (batteryLevelStr) - console.log ("battery level: " + batterylevel + "%") - }) + // for any message with tag 'edge_routing' + sock.ws.on(`CB:edge_routing`, (node: BinaryNode) => { }) + // for any message with tag 'edge_routing' and id attribute = abcd + sock.ws.on(`CB:edge_routing,id:abcd`, (node: BinaryNode) => { }) + // for any message with tag 'edge_routing', id attribute = abcd & first content node routing_info + sock.ws.on(`CB:edge_routing,id:abcd,routing_info`, (node: BinaryNode) => { }) ``` - This callback will be fired any time a message is received matching the following criteria: - ``` message [0] === "action" && message [1] === null && message[2][0][0] === "battery" ``` -2. Functionality to keep track of the pushname changes on your phone. - You enable logging and you'll see an unhandled message about your pushanme pop up like this: - ```s24, ["Conn",{"pushname":"adiwajshing"}]``` - - You now know what a pushname update looks like. It'll have the following characteristics. - - Given ```const pMessage = ["Conn",{"pushname":"adiwajshing"}] ``` - - ```pMessage[0]``` is always ``` "Conn" ``` - - ```pMessage[1]``` always has the key ``` "pushname" ``` - - ```pMessage[2]``` is always ``` undefined ``` - - Following this, one can implement the following callback: - ``` ts - conn.on ('CB:Conn,pushname', json => { - const pushname = json[1].pushname - conn.user.name = pushname // update on client too - console.log ("Name updated: " + pushname) - }) - ``` - This callback will be fired any time a message is received matching the following criteria: - ``` message [0] === "Conn" && message [1].pushname ``` - -A little more testing will reveal that almost all WhatsApp messages are in the format illustrated above. -Note: except for the first parameter (in the above cases, ```"action"``` or ```"Conn"```), all the other parameters are optional. ### Note + This library was originally a project for **CS-2362 at Ashoka University** and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this. + + Also, this repo is now licenced under GPL 3 since it uses [libsignal-node](https://git.questbook.io/backend/service-coderunner/-/merge_requests/1) diff --git a/WABinary/Binary.js b/WABinary/Binary.js new file mode 100644 index 0000000..c37e1eb --- /dev/null +++ b/WABinary/Binary.js @@ -0,0 +1,587 @@ +const { hexAt, hexLongIsNegative, hexLongToHex, negateHexLong, NUM_HEX_IN_LONG } = require("./HexHelper"); +const { inflateSync } = require("zlib") + +var l = "", + d = 0; + + const i = 65533, + n = new Uint8Array(10), + s = new Uint8Array(0); + +function u(e) { + if (e === l) return d; + for (var t = e.length, r = 0, a = 0; a < t; a++) { + var i = e.charCodeAt(a); + if (i < 128) r++; + else if (i < 2048) r += 2; + else if (i < 55296 || (57344 <= i && i <= 65535)) r += 3; + else if (55296 <= i && i < 56320 && a + 1 !== t) { + var n = e.charCodeAt(a + 1); + 56320 <= n && n < 57344 ? (a++, (r += 4)) : (r += 3); + } else r += 3; + } + return (l = e), (d = r); +} +function c(e, t, r) { + var a = t >> 21; + if (e) { + var i = Boolean(2097151 & t || r); + return 0 === a || (-1 === a && i); + } + return 0 === a; +} +function p(e, t, r, a, i = undefined) { + return e.readWithViewParser(t, r, a, i); +} +function f(e, t, r, a = undefined, i = undefined) { + return e.readWithBytesParser(t, r, a, i); +} +function h(e, t, r, a) { + return a ? e.getInt8(t) : e.getUint8(t); +} +function _(e, t, r, a) { + return e.getUint16(t, a); +} +function m(e, t, r, a) { + return e.getInt32(t, a); +} +function g(e, t, r, a) { + return e.getUint32(t, a); +} +function v(e, t, r, a, i) { + return a(e.getInt32(i ? t + 4 : t, i), e.getInt32(i ? t : t + 4, i)); +} +function y(e, t, r, a) { + return e.getFloat32(t, a); +} +function E(e, t, r, a) { + return e.getFloat64(t, a); +} +function S(e, t, r, a) { + for (var i = Math.min(a, 10), n = 0, s = 128; n < i && 128 & s; ) + s = e[t + n++]; + if (10 === n && s > 1) throw new Error("ParseError: varint exceeds 64 bits"); + return 128 & s ? n + 1 : n; +} +function T(e, t, r, a) { + var i = 0, + n = 0, + s = r; + 10 === r && (n = 1 & e[t + --s]); + for (var o = s - 1; o >= 0; o--) + (i = (i << 7) | (n >>> 25)), (n = (n << 7) | (127 & e[t + o])); + return a(i, n); +} +function A(e, t, r) { + var a = t + e.byteOffset, + i = e.buffer; + return 0 === a && r === i.byteLength ? i : i.slice(a, a + r); +} +function b(e, t, r) { + return e.subarray(t, t + r); +} +function C(e, t, r) { + for (var a = t + r, n = [], s = null, o = t; o < a; o++) { + n.length > 5e3 && + (s || (s = []), s.push(String.fromCharCode.apply(String, n)), (n = [])); + var l = 0 | e[o]; + if (0 == (128 & l)) n.push(l); + else if (192 == (224 & l)) { + var d = H(e, o + 1, a); + if (d) { + o++; + var u = ((31 & l) << 6) | (63 & d); + u >= 128 ? n.push(u) : n.push(i); + } else n.push(i); + } else if (224 == (240 & l)) { + var c = H(e, o + 1, a), + p = H(e, o + 2, a); + if (c && p) { + o += 2; + var f = ((15 & l) << 12) | ((63 & c) << 6) | (63 & p); + f >= 2048 && !(55296 <= f && f < 57344) ? n.push(f) : n.push(i); + } else c ? (o++, n.push(i)) : n.push(i); + } else if (240 == (248 & l)) { + var h = H(e, o + 1, a), + _ = H(e, o + 2, a), + m = H(e, o + 3, a); + if (h && _ && m) { + o += 3; + var g = ((7 & l) << 18) | ((63 & h) << 12) | ((63 & _) << 6) | (63 & m); + if (g >= 65536 && g <= 1114111) { + var v = g - 65536; + n.push(55296 | (v >> 10), 56320 | (1023 & v)); + } else n.push(i); + } else h && _ ? ((o += 2), n.push(i)) : h ? (o++, n.push(i)) : n.push(i); + } else n.push(i); + } + var y = String.fromCharCode.apply(String, n); + return s ? (s.push(y), s.join("")) : y; +} +function P(e, t, r, a, i) { + return e.writeToView(t, r, a, i); +} +function O(e, t, r, a, i = undefined) { + return e.writeToBytes(t, r, a, i); +} +function M(e, t, r, a) { + e[t] = a; +} +function w(e, t, r, a, i) { + e.setUint16(t, a, i); +} +function I(e, t, r, a, i) { + e.setInt16(t, a, i); +} +function R(e, t, r, a, i) { + e.setUint32(t, a, i); +} +function D(e, t, r, a, i) { + e.setInt32(t, a, i); +} +function N(e, t, r, a, i) { + var n = a < 0, + s = n ? -a : a, + o = Math.floor(s / 4294967296), + l = s - 4294967296 * o; + n && ((o = ~o), 0 === l ? o++ : (l = -l)), + e.setUint32(i ? t + 4 : t, o, i), + e.setUint32(i ? t : t + 4, l, i); +} +function L(e, t, r, a, i) { + e.setFloat32(t, a, i); +} +function k(e, t, r, a, i) { + e.setFloat64(t, a, i); +} +function U(e, t, r, a, i) { + for (var n = a, s = i, o = t + r - 1, l = t; l < o; l++) + (e[l] = 128 | (127 & s)), (s = (n << 25) | (s >>> 7)), (n >>>= 7); + e[o] = s; +} +function G(e, t, r, a) { + for (var i = t, n = a.length, s = 0; s < n; s++) { + var o = a.charCodeAt(s); + if (o < 128) e[i++] = o; + else if (o < 2048) (e[i++] = 192 | (o >> 6)), (e[i++] = 128 | (63 & o)); + else if (o < 55296 || 57344 <= o) + (e[i++] = 224 | (o >> 12)), + (e[i++] = 128 | ((o >> 6) & 63)), + (e[i++] = 128 | (63 & o)); + else if (55296 <= o && o < 56320 && s + 1 !== n) { + var l = a.charCodeAt(s + 1); + if (56320 <= l && l < 57344) { + s++; + var d = 65536 + (((1023 & o) << 10) | (1023 & l)); + (e[i++] = 240 | (d >> 18)), + (e[i++] = 128 | ((d >> 12) & 63)), + (e[i++] = 128 | ((d >> 6) & 63)), + (e[i++] = 128 | (63 & d)); + } else (e[i++] = 239), (e[i++] = 191), (e[i++] = 189); + } else (e[i++] = 239), (e[i++] = 191), (e[i++] = 189); + } +} +function F(e, t, r, i, n) { + for ( + var s = hexLongIsNegative(i), + o = hexLongToHex(i), + l = 0, + d = 0, + u = 0; + u < 16; + u++ + ) + (l = (l << 4) | (d >>> 28)), (d = (d << 4) | hexAt(o, u)); + s && ((l = ~l), 0 === d ? l++ : (d = -d)), + e.setUint32(n ? t + 4 : t, l, n), + e.setUint32(n ? t : t + 4, d, n); +} +function x(e, t, r, a) { + for (var i = 0; i < r; i++) e[t + i] = a[i]; +} +function B(e, t) { + var r, a; + for (e ? ((r = 5), (a = e >>> 3)) : ((r = 1), (a = t >>> 7)); a; ) + r++, (a >>>= 7); + return r; +} +function Y(e, t, r, a) { + if ("number" != typeof e || e != e || Math.floor(e) !== e || e < t || e >= r) { + console.trace('here') + throw new TypeError( + "string" == typeof e + ? `WriteError: string "${e}" is not a valid ${a}` + : `WriteError: ${String(e)} is not a valid ${a}` + ); + } + +} +function K(e, t, r) { + var a = + 4294967296 * (t >= 0 || e ? t : 4294967296 + t) + + (r >= 0 ? r : 4294967296 + r); + if (!c(e, t, r)) + throw new Error(`ReadError: integer exceeded 53 bits (${a})`); + return a; +} +function j(e, t) { + return K(!0, e, t); +} +function W(e, t) { + return K(!1, e, t); +} +function H(e, t, r) { + if (t >= r) return 0; + var a = 0 | e[t]; + return 128 == (192 & a) ? a : 0; +} + +module.exports.numUtf8Bytes = u; +module.exports.longFitsInDouble = c; +module.exports.parseInt64OrThrow = j; +module.exports.parseUint64OrThrow = W; + +class Binary { + /** @type {Uint8Array} */ + buffer; + readEndIndex; + writeIndex; + bytesTrashed = 0; + earliestIndex = 0; + readIndex = 0; + /** @type {DataView} */ + view = null; + littleEndian = false; + hiddenReads = 0; + hiddenWrites = 0; + + constructor(data = new Uint8Array(0), littleEndian = false) { + if (data instanceof ArrayBuffer) { + this.buffer = new Uint8Array(data); + this.readEndIndex = data.byteLength; + this.writeIndex = data.byteLength; + } + + if (data instanceof Uint8Array) { + this.buffer = data; + this.readEndIndex = data.length; + this.writeIndex = data.length; + } + + this.littleEndian = littleEndian; + } + + size() { + return this.readEndIndex - this.readIndex; + } + + peek(e, t = undefined) { + this.hiddenReads++; + + const r = this.readIndex; + const a = this.bytesTrashed; + + try { + return e(this, t); + } finally { + this.hiddenReads--, (this.readIndex = r - (this.bytesTrashed - a)); + } + } + + advance(e) { + this.shiftReadOrThrow(e); + } + + readWithViewParser(e, t, r, a) { + return t(this.getView(), this.shiftReadOrThrow(e), e, r, a); + } + + readWithBytesParser(e, t, r, a) { + return t(this.buffer, this.shiftReadOrThrow(e), e, r, a); + } + + readUint8() { + //return this.readWithViewParser(1, h, false) + return p(this, 1, h, !1); + } + readInt8() { + return p(this, 1, h, !0); + } + readUint16(e = this.littleEndian) { + return p(this, 2, _, e); + } + readInt32(e = this.littleEndian) { + return p(this, 4, m, e); + } + readUint32(e = this.littleEndian) { + return p(this, 4, g, e); + } + readInt64(e = this.littleEndian) { + return p(this, 8, v, j, e); + } + readUint64(e = this.littleEndian) { + return p(this, 8, v, W, e); + } + readLong(e, t = this.littleEndian) { + return p(this, 8, v, e, t); + } + readFloat32(e = this.littleEndian) { + return p(this, 4, y, e); + } + readFloat64(e = this.littleEndian) { + return p(this, 8, E, e); + } + readVarInt(e) { + var t = f(this, 0, S, this.size()); + return f(this, t, T, e); + } + readBuffer(e = this.size()) { + return 0 === e ? new ArrayBuffer(0) : f(this, e, A); + } + readByteArray(e = this.size()) { + return 0 === e ? new Uint8Array(0) : f(this, e, b); + } + readBinary(e = this.size(), t = this.littleEndian) { + if (0 === e) return new Binary(void 0, t); + var r = f(this, e, b); + return new Binary(r, t); + } + indexOf(e) { + if (0 === e.length) return 0; + for ( + var t = this.buffer, + r = this.readEndIndex, + a = this.readIndex, + i = 0, + n = a, + s = a; + s < r; + s++ + ) + if (t[s] === e[i]) { + if ((0 === i && (n = s), ++i === e.byteLength)) + return s - a - e.byteLength + 1; + } else i > 0 && ((i = 0), (s = n)); + return -1; + 1; + } + readString(e) { + return f(this, e, C); + } + ensureCapacity(e) { + this.maybeReallocate(this.readIndex + e); + } + ensureAdditionalCapacity(e) { + this.maybeReallocate(this.writeIndex + e); + } + writeToView(e, t, r, a) { + var i = this.shiftWriteMaybeReallocate(e); + return t(this.getView(), i, e, r, a); + } + writeToBytes(e, t, r, a) { + var i = this.shiftWriteMaybeReallocate(e); + return t(this.buffer, i, e, r, a); + } + write(...e) { + for (var t = 0; t < e.length; t++) { + var r = e[t]; + "string" == typeof r + ? this.writeString(r) + : "number" == typeof r + ? this.writeUint8(r) + : r instanceof Binary + ? this.writeBinary(r) + : r instanceof ArrayBuffer + ? this.writeBuffer(r) + : r instanceof Uint8Array && this.writeByteArray(r); + } + } + writeUint8(e) { + Y(e, 0, 256, "uint8"), O(this, 1, M, e, !1); + } + writeInt8(e) { + Y(e, -128, 128, "signed int8"), O(this, 1, M, e, !0); + } + writeUint16(e, t = this.littleEndian) { + Y(e, 0, 65536, "uint16"), P(this, 2, w, e, t); + } + writeInt16(e, t = this.littleEndian) { + Y(e, -32768, 32768, "signed int16"), P(this, 2, I, e, t); + } + writeUint32(e, t = this.littleEndian) { + Y(e, 0, 4294967296, "uint32"), P(this, 4, R, e, t); + } + writeInt32(e, t = this.littleEndian) { + Y(e, -2147483648, 2147483648, "signed int32"), P(this, 4, D, e, t); + } + writeUint64(e, t = this.littleEndian) { + Y(e, 0, 0x10000000000000000, "uint64"), P(this, 8, N, e, t); + } + writeInt64(e, t = this.littleEndian) { + Y(e, -0x8000000000000000, 0x8000000000000000, "signed int64"), + P(this, 8, N, e, t); + } + writeFloat32(e, t = this.littleEndian) { + P(this, 4, L, e, t); + } + writeFloat64(e, t = this.littleEndian) { + P(this, 8, k, e, t); + } + writeVarInt(e) { + Y(e, -0x8000000000000000, 0x8000000000000000, "varint (signed int64)"); + var t = e < 0, + r = t ? -e : e, + a = Math.floor(r / 4294967296), + i = r - 4294967296 * a; + t && ((a = ~a), 0 === i ? a++ : (i = -i)), O(this, B(a, i), U, a, i); + } + writeVarIntFromHexLong(e) { + for ( + var t = hexLongIsNegative(e), + r = t ? negateHexLong(e) : e, + i = hexLongToHex(r), + n = 0, + s = 0, + o = 0; + o < NUM_HEX_IN_LONG; + o++ + ) + (n = (n << 4) | (s >>> 28)), (s = (s << 4) | hexAt(i, o)); + t && ((n = ~n), 0 === s ? n++ : (s = -s)), O(this, B(n, s), U, n, s); + } + writeBinary(e) { + var t = e.peek((e) => e.readByteArray()); + if (t.length) { + var r = this.shiftWriteMaybeReallocate(t.length); + this.buffer.set(t, r); + } + } + writeBuffer(e) { + this.writeByteArray(new Uint8Array(e)); + } + writeByteArray(e) { + var t = this.shiftWriteMaybeReallocate(e.length); + this.buffer.set(e, t); + } + writeBufferView(e) { + this.writeByteArray(new Uint8Array(e.buffer, e.byteOffset, e.byteLength)); + } + writeString(e) { + O(this, u(e), G, e); + } + writeHexLong(e, t = this.littleEndian) { + P(this, 8, F, e, t); + } + writeBytes(...e) { + for (var t = 0; t < e.length; t++) Y(e[t], 0, 256, "byte"); + O(this, e.length, x, e); + } + writeAtomically(e, t) { + this.hiddenWrites++; + var r = this.writeIndex, + a = this.bytesTrashed; + try { + var i = e(this, t); + return (r = this.writeIndex), (a = this.bytesTrashed), i; + } finally { + this.hiddenWrites--, (this.writeIndex = r - (this.bytesTrashed - a)); + } + } + + writeWithVarIntLength(e, t) { + var r = this.writeIndex, + a = this.writeAtomically(e, t), + i = this.writeIndex; + this.writeVarInt(i - r); + for (var s = this.writeIndex - i, o = this.buffer, l = 0; l < s; l++) + n[l] = o[i + l]; + for (var d = i - 1; d >= r; d--) o[d + s] = o[d]; + for (var u = 0; u < s; u++) o[r + u] = n[u]; + return a; + } + + static build(...e) { + let t = 0; + let r = 0; + for (t = 0, r = 0; r < e.length; r++) { + let a = e[r]; + "string" == typeof a + ? (t += u(a)) + : "number" == typeof a + ? t++ + : a instanceof Binary + ? (t += a.size()) + : a instanceof ArrayBuffer + ? (t += a.byteLength) + : a instanceof Uint8Array && (t += a.length); + } + + var i = new Binary(); + return i.ensureCapacity(t), i.write.apply(i, arguments), i; + } + + getView() { + return ( + this.view || + (this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset)) + ); + } + + shiftReadOrThrow(e) { + if (e < 0) + throw new Error("ReadError: given negative number of bytes to read"); + var t = this.readIndex, + r = t + e; + if (r > this.readEndIndex) + throw new Error( + t === this.readEndIndex + ? "ReadError: tried to read from depleted binary" + : "ReadError: tried to read beyond end of binary" + ); + return ( + (this.readIndex = r), this.hiddenReads || (this.earliestIndex = r), t + ); + } + + maybeReallocate(e) { + const t = this.buffer; + if (e <= t.length) { + return e; + } + + const r = this.earliestIndex; + const a = e - r; + const i = Math.max(a, 2 * (t.length - r), 64); + const n = new Uint8Array(i); + return ( + r + ? (n.set(t.subarray(r)), + (this.bytesTrashed += r), + (this.readIndex -= r), + (this.readEndIndex -= r), + (this.writeIndex -= r), + (this.earliestIndex = 0)) + : n.set(t), + (this.buffer = n), + (this.view = null), + a + ); + } + + shiftWriteMaybeReallocate(e) { + const t = this.maybeReallocate(this.writeIndex + e); + const r = this.writeIndex; + return ( + (this.writeIndex = t), this.hiddenWrites || (this.readEndIndex = t), r + ); + } + decompressed = () => { + if (2 & this.readUint8()) { + const result = inflateSync(this.readByteArray()) + return new Binary(result) + } + return this + } +} + +module.exports.Binary = Binary \ No newline at end of file diff --git a/WABinary/Constants.js b/WABinary/Constants.js new file mode 100644 index 0000000..8adfeed --- /dev/null +++ b/WABinary/Constants.js @@ -0,0 +1,22 @@ + +const buildMap = (data) => { + const map = new Map(); + for (let r = 0; r < data.length; r++) { + map.set(data[r], r); + } + + return map; +} + +module.exports.DEVICE = { PRIMARY_DEVICE: 0, PRIMARY_VERSION: 0 } + +module.exports.SINGLE_BYTE_TOKEN = ["xmlstreamstart", "xmlstreamend", "s.whatsapp.net", "type", "participant", "from", "receipt", "id", "broadcast", "status", "message", "notification", "notify", "to", "jid", "user", "class", "offline", "g.us", "result", "mediatype", "enc", "skmsg", "off_cnt", "xmlns", "presence", "participants", "ack", "t", "iq", "device_hash", "read", "value", "media", "picture", "chatstate", "unavailable", "text", "urn:xmpp:whatsapp:push", "devices", "verified_name", "contact", "composing", "edge_routing", "routing_info", "item", "image", "verified_level", "get", "fallback_hostname", "2", "media_conn", "1", "v", "handshake", "fallback_class", "count", "config", "offline_preview", "download_buckets", "w:profile:picture", "set", "creation", "location", "fallback_ip4", "msg", "urn:xmpp:ping", "fallback_ip6", "call-creator", "relaylatency", "success", "subscribe", "video", "business_hours_config", "platform", "hostname", "version", "unknown", "0", "ping", "hash", "edit", "subject", "max_buckets", "download", "delivery", "props", "sticker", "name", "last", "contacts", "business", "primary", "preview", "w:p", "pkmsg", "call-id", "retry", "prop", "call", "auth_ttl", "available", "relay_id", "last_id", "day_of_week", "w", "host", "seen", "bits", "list", "atn", "upload", "is_new", "w:stats", "key", "paused", "specific_hours", "multicast", "stream:error", "mmg.whatsapp.net", "code", "deny", "played", "profile", "fna", "device-list", "close_time", "latency", "gcm", "pop", "audio", "26", "w:web", "open_time", "error", "auth", "ip4", "update", "profile_options", "config_value", "category", "catalog_not_created", "00", "config_code", "mode", "catalog_status", "ip6", "blocklist", "registration", "7", "web", "fail", "w:m", "cart_enabled", "ttl", "gif", "300", "device_orientation", "identity", "query", "401", "media-gig2-1.cdn.whatsapp.net", "in", "3", "te2", "add", "fallback", "categories", "ptt", "encrypt", "notice", "thumbnail-document", "item-not-found", "12", "thumbnail-image", "stage", "thumbnail-link", "usync", "out", "thumbnail-video", "8", "01", "context", "sidelist", "thumbnail-gif", "terminate", "not-authorized", "orientation", "dhash", "capability", "side_list", "md-app-state", "description", "serial", "readreceipts", "te", "business_hours", "md-msg-hist", "tag", "attribute_padding", "document", "open_24h", "delete", "expiration", "active", "prev_v_id", "true", "passive", "index", "4", "conflict", "remove", "w:gp2", "config_expo_key", "screen_height", "replaced", "02", "screen_width", "uploadfieldstat", "2:47DEQpj8", "media-bog1-1.cdn.whatsapp.net", "encopt", "url", "catalog_exists", "keygen", "rate", "offer", "opus", "media-mia3-1.cdn.whatsapp.net", "privacy", "media-mia3-2.cdn.whatsapp.net", "signature", "preaccept", "token_id", "media-eze1-1.cdn.whatsapp.net"]; +module.exports.DICTIONARY_0_TOKEN = ["media-for1-1.cdn.whatsapp.net", "relay", "media-gru2-2.cdn.whatsapp.net", "uncompressed", "medium", "voip_settings", "device", "reason", "media-lim1-1.cdn.whatsapp.net", "media-qro1-2.cdn.whatsapp.net", "media-gru1-2.cdn.whatsapp.net", "action", "features", "media-gru2-1.cdn.whatsapp.net", "media-gru1-1.cdn.whatsapp.net", "media-otp1-1.cdn.whatsapp.net", "kyc-id", "priority", "phash", "mute", "token", "100", "media-qro1-1.cdn.whatsapp.net", "none", "media-mrs2-2.cdn.whatsapp.net", "sign_credential", "03", "media-mrs2-1.cdn.whatsapp.net", "protocol", "timezone", "transport", "eph_setting", "1080", "original_dimensions", "media-frx5-1.cdn.whatsapp.net", "background", "disable", "original_image_url", "5", "transaction-id", "direct_path", "103", "appointment_only", "request_image_url", "peer_pid", "address", "105", "104", "102", "media-cdt1-1.cdn.whatsapp.net", "101", "109", "110", "106", "background_location", "v_id", "sync", "status-old", "111", "107", "ppic", "media-scl2-1.cdn.whatsapp.net", "business_profile", "108", "invite", "04", "audio_duration", "media-mct1-1.cdn.whatsapp.net", "media-cdg2-1.cdn.whatsapp.net", "media-los2-1.cdn.whatsapp.net", "invis", "net", "voip_payload_type", "status-revoke-delay", "404", "state", "use_correct_order_for_hmac_sha1", "ver", "media-mad1-1.cdn.whatsapp.net", "order", "540", "skey", "blinded_credential", "android", "contact_remove", "enable_downlink_relay_latency_only", "duration", "enable_vid_one_way_codec_nego", "6", "media-sof1-1.cdn.whatsapp.net", "accept", "all", "signed_credential", "media-atl3-1.cdn.whatsapp.net", "media-lhr8-1.cdn.whatsapp.net", "website", "05", "latitude", "media-dfw5-1.cdn.whatsapp.net", "forbidden", "enable_audio_piggyback_network_mtu_fix", "media-dfw5-2.cdn.whatsapp.net", "note.m4r", "media-atl3-2.cdn.whatsapp.net", "jb_nack_discard_count_fix", "longitude", "Opening.m4r", "media-arn2-1.cdn.whatsapp.net", "email", "timestamp", "admin", "media-pmo1-1.cdn.whatsapp.net", "America/Sao_Paulo", "contact_add", "media-sin6-1.cdn.whatsapp.net", "interactive", "8000", "acs_public_key", "sigquit_anr_detector_release_rollover_percent", "media.fmed1-2.fna.whatsapp.net", "groupadd", "enabled_for_video_upgrade", "latency_update_threshold", "media-frt3-2.cdn.whatsapp.net", "calls_row_constraint_layout", "media.fgbb2-1.fna.whatsapp.net", "mms4_media_retry_notification_encryption_enabled", "timeout", "media-sin6-3.cdn.whatsapp.net", "audio_nack_jitter_multiplier", "jb_discard_count_adjust_pct_rc", "audio_reserve_bps", "delta", "account_sync", "default", "media.fjed4-6.fna.whatsapp.net", "06", "lock_video_orientation", "media-frt3-1.cdn.whatsapp.net", "w:g2", "media-sin6-2.cdn.whatsapp.net", "audio_nack_algo_mask", "media.fgbb2-2.fna.whatsapp.net", "media.fmed1-1.fna.whatsapp.net", "cond_range_target_bitrate", "mms4_server_error_receipt_encryption_enabled", "vid_rc_dyn", "fri", "cart_v1_1_order_message_changes_enabled", "reg_push", "jb_hist_deposit_value", "privatestats", "media.fist7-2.fna.whatsapp.net", "thu", "jb_discard_count_adjust_pct", "mon", "group_call_video_maximization_enabled", "mms_cat_v1_forward_hot_override_enabled", "audio_nack_new_rtt", "media.fsub2-3.fna.whatsapp.net", "media_upload_aggressive_retry_exponential_backoff_enabled", "tue", "wed", "media.fruh4-2.fna.whatsapp.net", "audio_nack_max_seq_req", "max_rtp_audio_packet_resends", "jb_hist_max_cdf_value", "07", "audio_nack_max_jb_delay", "mms_forward_partially_downloaded_video", "media-lcy1-1.cdn.whatsapp.net", "resume", "jb_inband_fec_aware", "new_commerce_entry_point_enabled", "480", "payments_upi_generate_qr_amount_limit", "sigquit_anr_detector_rollover_percent", "media.fsdu2-1.fna.whatsapp.net", "fbns", "aud_pkt_reorder_pct", "dec", "stop_probing_before_accept_send", "media_upload_max_aggressive_retries", "edit_business_profile_new_mode_enabled", "media.fhex4-1.fna.whatsapp.net", "media.fjed4-3.fna.whatsapp.net", "sigquit_anr_detector_64bit_rollover_percent", "cond_range_ema_jb_last_delay", "watls_enable_early_data_http_get", "media.fsdu2-2.fna.whatsapp.net", "message_qr_disambiguation_enabled", "media-mxp1-1.cdn.whatsapp.net", "sat", "vertical", "media.fruh4-5.fna.whatsapp.net", "200", "media-sof1-2.cdn.whatsapp.net", "-1", "height", "product_catalog_hide_show_items_enabled", "deep_copy_frm_last", "tsoffline", "vp8/h.264", "media.fgye5-3.fna.whatsapp.net", "media.ftuc1-2.fna.whatsapp.net", "smb_upsell_chat_banner_enabled", "canonical", "08", "9", ".", "media.fgyd4-4.fna.whatsapp.net", "media.fsti4-1.fna.whatsapp.net", "mms_vcache_aggregation_enabled", "mms_hot_content_timespan_in_seconds", "nse_ver", "rte", "third_party_sticker_web_sync", "cond_range_target_total_bitrate", "media_upload_aggressive_retry_enabled", "instrument_spam_report_enabled", "disable_reconnect_tone", "move_media_folder_from_sister_app", "one_tap_calling_in_group_chat_size", "10", "storage_mgmt_banner_threshold_mb", "enable_backup_passive_mode", "sharechat_inline_player_enabled", "media.fcnq2-1.fna.whatsapp.net", "media.fhex4-2.fna.whatsapp.net", "media.fist6-3.fna.whatsapp.net", "ephemeral_drop_column_stage", "reconnecting_after_network_change_threshold_ms", "media-lhr8-2.cdn.whatsapp.net", "cond_jb_last_delay_ema_alpha", "entry_point_block_logging_enabled", "critical_event_upload_log_config", "respect_initial_bitrate_estimate", "smaller_image_thumbs_status_enabled", "media.fbtz1-4.fna.whatsapp.net", "media.fjed4-1.fna.whatsapp.net", "width", "720", "enable_frame_dropper", "enable_one_side_mode", "urn:xmpp:whatsapp:dirty", "new_sticker_animation_behavior_v2", "media.flim3-2.fna.whatsapp.net", "media.fuio6-2.fna.whatsapp.net", "skip_forced_signaling", "dleq_proof", "status_video_max_bitrate", "lazy_send_probing_req", "enhanced_storage_management", "android_privatestats_endpoint_dit_enabled", "media.fscl13-2.fna.whatsapp.net", "video_duration"]; +module.exports.DICTIONARY_1_TOKEN = ["group_call_discoverability_enabled", "media.faep9-2.fna.whatsapp.net", "msgr", "bloks_loggedin_access_app_id", "db_status_migration_step", "watls_prefer_ip6", "jabber:iq:privacy", "68", "media.fsaw1-11.fna.whatsapp.net", "mms4_media_conn_persist_enabled", "animated_stickers_thread_clean_up", "media.fcgk3-2.fna.whatsapp.net", "media.fcgk4-6.fna.whatsapp.net", "media.fgye5-2.fna.whatsapp.net", "media.flpb1-1.fna.whatsapp.net", "media.fsub2-1.fna.whatsapp.net", "media.fuio6-3.fna.whatsapp.net", "not-allowed", "partial_pjpeg_bw_threshold", "cap_estimated_bitrate", "mms_chatd_resume_check_over_thrift", "smb_upsell_business_profile_enabled", "product_catalog_webclient", "groups", "sigquit_anr_detector_release_updated_rollout", "syncd_key_rotation_enabled", "media.fdmm2-1.fna.whatsapp.net", "media-hou1-1.cdn.whatsapp.net", "remove_old_chat_notifications", "smb_biztools_deeplink_enabled", "use_downloadable_filters_int", "group_qr_codes_enabled", "max_receipt_processing_time", "optimistic_image_processing_enabled", "smaller_video_thumbs_status_enabled", "watls_early_data", "reconnecting_before_relay_failover_threshold_ms", "cond_range_packet_loss_pct", "groups_privacy_blacklist", "status-revoke-drop", "stickers_animated_thumbnail_download", "dedupe_transcode_shared_images", "dedupe_transcode_shared_videos", "media.fcnq2-2.fna.whatsapp.net", "media.fgyd4-1.fna.whatsapp.net", "media.fist7-1.fna.whatsapp.net", "media.flim3-3.fna.whatsapp.net", "add_contact_by_qr_enabled", "https://faq.whatsapp.com/payments", "multicast_limit_global", "sticker_notification_preview", "smb_better_catalog_list_adapters_enabled", "bloks_use_minscript_android", "pen_smoothing_enabled", "media.fcgk4-5.fna.whatsapp.net", "media.fevn1-3.fna.whatsapp.net", "media.fpoj7-1.fna.whatsapp.net", "media-arn2-2.cdn.whatsapp.net", "reconnecting_before_network_change_threshold_ms", "android_media_use_fresco_for_gifs", "cond_in_congestion", "status_image_max_edge", "sticker_search_enabled", "starred_stickers_web_sync", "db_blank_me_jid_migration_step", "media.fist6-2.fna.whatsapp.net", "media.ftuc1-1.fna.whatsapp.net", "09", "anr_fast_logs_upload_rollout", "camera_core_integration_enabled", "11", "third_party_sticker_caching", "thread_dump_contact_support", "wam_privatestats_enabled", "vcard_as_document_size_kb", "maxfpp", "fbip", "ephemeral_allow_group_members", "media-bom1-2.cdn.whatsapp.net", "media-xsp1-1.cdn.whatsapp.net", "disable_prewarm", "frequently_forwarded_max", "media.fbtz1-5.fna.whatsapp.net", "media.fevn7-1.fna.whatsapp.net", "media.fgyd4-2.fna.whatsapp.net", "sticker_tray_animation_fully_visible_items", "green_alert_banner_duration", "reconnecting_after_p2p_failover_threshold_ms", "connected", "share_biz_vcard_enabled", "stickers_animation", "0a", "1200", "WhatsApp", "group_description_length", "p_v_id", "payments_upi_intent_transaction_limit", "frequently_forwarded_messages", "media-xsp1-2.cdn.whatsapp.net", "media.faep8-1.fna.whatsapp.net", "media.faep8-2.fna.whatsapp.net", "media.faep9-1.fna.whatsapp.net", "media.fdmm2-2.fna.whatsapp.net", "media.fgzt3-1.fna.whatsapp.net", "media.flim4-2.fna.whatsapp.net", "media.frao1-1.fna.whatsapp.net", "media.fscl9-2.fna.whatsapp.net", "media.fsub2-2.fna.whatsapp.net", "superadmin", "media.fbog10-1.fna.whatsapp.net", "media.fcgh28-1.fna.whatsapp.net", "media.fjdo10-1.fna.whatsapp.net", "third_party_animated_sticker_import", "delay_fec", "attachment_picker_refresh", "android_linked_devices_re_auth_enabled", "rc_dyn", "green_alert_block_jitter", "add_contact_logging_enabled", "biz_message_logging_enabled", "conversation_media_preview_v2", "media-jnb1-1.cdn.whatsapp.net", "ab_key", "media.fcgk4-2.fna.whatsapp.net", "media.fevn1-1.fna.whatsapp.net", "media.fist6-1.fna.whatsapp.net", "media.fruh4-4.fna.whatsapp.net", "media.fsti4-2.fna.whatsapp.net", "mms_vcard_autodownload_size_kb", "watls_enabled", "notif_ch_override_off", "media.fsaw1-14.fna.whatsapp.net", "media.fscl13-1.fna.whatsapp.net", "db_group_participant_migration_step", "1020", "cond_range_sterm_rtt", "invites_logging_enabled", "triggered_block_enabled", "group_call_max_participants", "media-iad3-1.cdn.whatsapp.net", "product_catalog_open_deeplink", "shops_required_tos_version", "image_max_kbytes", "cond_low_quality_vid_mode", "db_receipt_migration_step", "jb_early_prob_hist_shrink", "media.fdmm2-3.fna.whatsapp.net", "media.fdmm2-4.fna.whatsapp.net", "media.fruh4-1.fna.whatsapp.net", "media.fsaw2-2.fna.whatsapp.net", "remove_geolocation_videos", "new_animation_behavior", "fieldstats_beacon_chance", "403", "authkey_reset_on_ban", "continuous_ptt_playback", "reconnecting_after_relay_failover_threshold_ms", "false", "group", "sun", "conversation_swipe_to_reply", "ephemeral_messages_setting", "smaller_video_thumbs_enabled", "md_device_sync_enabled", "bloks_shops_pdp_url_regex", "lasso_integration_enabled", "media-bom1-1.cdn.whatsapp.net", "new_backup_format_enabled", "256", "media.faep6-1.fna.whatsapp.net", "media.fasr1-1.fna.whatsapp.net", "media.fbtz1-7.fna.whatsapp.net", "media.fesb4-1.fna.whatsapp.net", "media.fjdo1-2.fna.whatsapp.net", "media.frba2-1.fna.whatsapp.net", "watls_no_dns", "600", "db_broadcast_me_jid_migration_step", "new_wam_runtime_enabled", "group_update", "enhanced_block_enabled", "sync_wifi_threshold_kb", "mms_download_nc_cat", "bloks_minification_enabled", "ephemeral_messages_enabled", "reject", "voip_outgoing_xml_signaling", "creator", "dl_bw", "payments_request_messages", "target_bitrate", "bloks_rendercore_enabled", "media-hbe1-1.cdn.whatsapp.net", "media-hel3-1.cdn.whatsapp.net", "media-kut2-2.cdn.whatsapp.net", "media-lax3-1.cdn.whatsapp.net", "media-lax3-2.cdn.whatsapp.net", "sticker_pack_deeplink_enabled", "hq_image_bw_threshold", "status_info", "voip", "dedupe_transcode_videos", "grp_uii_cleanup", "linked_device_max_count", "media.flim1-1.fna.whatsapp.net", "media.fsaw2-1.fna.whatsapp.net", "reconnecting_after_call_active_threshold_ms", "1140", "catalog_pdp_new_design", "media.fbtz1-10.fna.whatsapp.net", "media.fsaw1-15.fna.whatsapp.net", "0b", "consumer_rc_provider", "mms_async_fast_forward_ttl", "jb_eff_size_fix", "voip_incoming_xml_signaling", "media_provider_share_by_uuid", "suspicious_links", "dedupe_transcode_images", "green_alert_modal_start", "media-cgk1-1.cdn.whatsapp.net", "media-lga3-1.cdn.whatsapp.net", "template_doc_mime_types", "important_messages", "user_add", "vcard_max_size_kb", "media.fada2-1.fna.whatsapp.net", "media.fbog2-5.fna.whatsapp.net", "media.fbtz1-3.fna.whatsapp.net", "media.fcgk3-1.fna.whatsapp.net", "media.fcgk7-1.fna.whatsapp.net", "media.flim1-3.fna.whatsapp.net", "media.fscl9-1.fna.whatsapp.net", "ctwa_context_enterprise_enabled", "media.fsaw1-13.fna.whatsapp.net", "media.fuio11-2.fna.whatsapp.net", "status_collapse_muted", "db_migration_level_force", "recent_stickers_web_sync", "bloks_session_state", "bloks_shops_enabled", "green_alert_setting_deep_links_enabled", "restrict_groups", "battery", "green_alert_block_start", "refresh", "ctwa_context_enabled", "md_messaging_enabled", "status_image_quality", "md_blocklist_v2_server", "media-del1-1.cdn.whatsapp.net", "13", "userrate", "a_v_id", "cond_rtt_ema_alpha", "invalid"]; +module.exports.DICTIONARY_2_TOKEN = ["media.fada1-1.fna.whatsapp.net", "media.fadb3-2.fna.whatsapp.net", "media.fbhz2-1.fna.whatsapp.net", "media.fcor2-1.fna.whatsapp.net", "media.fjed4-2.fna.whatsapp.net", "media.flhe4-1.fna.whatsapp.net", "media.frak1-2.fna.whatsapp.net", "media.fsub6-3.fna.whatsapp.net", "media.fsub6-7.fna.whatsapp.net", "media.fvvi1-1.fna.whatsapp.net", "search_v5_eligible", "wam_real_time_enabled", "report_disk_event", "max_tx_rott_based_bitrate", "product", "media.fjdo10-2.fna.whatsapp.net", "video_frame_crc_sample_interval", "media_max_autodownload", "15", "h.264", "wam_privatestats_buffer_count", "md_phash_v2_enabled", "account_transfer_enabled", "business_product_catalog", "enable_non_dyn_codec_param_fix", "is_user_under_epd_jurisdiction", "media.fbog2-4.fna.whatsapp.net", "media.fbtz1-2.fna.whatsapp.net", "media.fcfc1-1.fna.whatsapp.net", "media.fjed4-5.fna.whatsapp.net", "media.flhe4-2.fna.whatsapp.net", "media.flim1-2.fna.whatsapp.net", "media.flos5-1.fna.whatsapp.net", "android_key_store_auth_ver", "010", "anr_process_monitor", "delete_old_auth_key", "media.fcor10-3.fna.whatsapp.net", "storage_usage_enabled", "android_camera2_support_level", "dirty", "consumer_content_provider", "status_video_max_duration", "0c", "bloks_cache_enabled", "media.fadb2-2.fna.whatsapp.net", "media.fbko1-1.fna.whatsapp.net", "media.fbtz1-9.fna.whatsapp.net", "media.fcgk4-4.fna.whatsapp.net", "media.fesb4-2.fna.whatsapp.net", "media.fevn1-2.fna.whatsapp.net", "media.fist2-4.fna.whatsapp.net", "media.fjdo1-1.fna.whatsapp.net", "media.fruh4-6.fna.whatsapp.net", "media.fsrg5-1.fna.whatsapp.net", "media.fsub6-6.fna.whatsapp.net", "minfpp", "5000", "locales", "video_max_bitrate", "use_new_auth_key", "bloks_http_enabled", "heartbeat_interval", "media.fbog11-1.fna.whatsapp.net", "ephemeral_group_query_ts", "fec_nack", "search_in_storage_usage", "c", "media-amt2-1.cdn.whatsapp.net", "linked_devices_ui_enabled", "14", "async_data_load_on_startup", "voip_incoming_xml_ack", "16", "db_migration_step", "init_bwe", "max_participants", "wam_buffer_count", "media.fada2-2.fna.whatsapp.net", "media.fadb3-1.fna.whatsapp.net", "media.fcor2-2.fna.whatsapp.net", "media.fdiy1-2.fna.whatsapp.net", "media.frba3-2.fna.whatsapp.net", "media.fsaw2-3.fna.whatsapp.net", "1280", "status_grid_enabled", "w:biz", "product_catalog_deeplink", "media.fgye10-2.fna.whatsapp.net", "media.fuio11-1.fna.whatsapp.net", "optimistic_upload", "work_manager_init", "lc", "catalog_message", "cond_net_medium", "enable_periodical_aud_rr_processing", "cond_range_ema_rtt", "media-tir2-1.cdn.whatsapp.net", "frame_ms", "group_invite_sending", "payments_web_enabled", "wallpapers_v2", "0d", "browser", "hq_image_max_edge", "image_edit_zoom", "linked_devices_re_auth_enabled", "media.faly3-2.fna.whatsapp.net", "media.fdoh5-3.fna.whatsapp.net", "media.fesb3-1.fna.whatsapp.net", "media.fknu1-1.fna.whatsapp.net", "media.fmex3-1.fna.whatsapp.net", "media.fruh4-3.fna.whatsapp.net", "255", "web_upgrade_to_md_modal", "audio_piggyback_timeout_msec", "enable_audio_oob_fec_feature", "from_ip", "image_max_edge", "message_qr_enabled", "powersave", "receipt_pre_acking", "video_max_edge", "full", "011", "012", "enable_audio_oob_fec_for_sender", "md_voip_enabled", "enable_privatestats", "max_fec_ratio", "payments_cs_faq_url", "media-xsp1-3.cdn.whatsapp.net", "hq_image_quality", "media.fasr1-2.fna.whatsapp.net", "media.fbog3-1.fna.whatsapp.net", "media.ffjr1-6.fna.whatsapp.net", "media.fist2-3.fna.whatsapp.net", "media.flim4-3.fna.whatsapp.net", "media.fpbc2-4.fna.whatsapp.net", "media.fpku1-1.fna.whatsapp.net", "media.frba1-1.fna.whatsapp.net", "media.fudi1-1.fna.whatsapp.net", "media.fvvi1-2.fna.whatsapp.net", "gcm_fg_service", "enable_dec_ltr_size_check", "clear", "lg", "media.fgru11-1.fna.whatsapp.net", "18", "media-lga3-2.cdn.whatsapp.net", "pkey", "0e", "max_subject", "cond_range_lterm_rtt", "announcement_groups", "biz_profile_options", "s_t", "media.fabv2-1.fna.whatsapp.net", "media.fcai3-1.fna.whatsapp.net", "media.fcgh1-1.fna.whatsapp.net", "media.fctg1-4.fna.whatsapp.net", "media.fdiy1-1.fna.whatsapp.net", "media.fisb4-1.fna.whatsapp.net", "media.fpku1-2.fna.whatsapp.net", "media.fros9-1.fna.whatsapp.net", "status_v3_text", "usync_sidelist", "17", "announcement", "...", "md_group_notification", "0f", "animated_pack_in_store", "013", "America/Mexico_City", "1260", "media-ams4-1.cdn.whatsapp.net", "media-cgk1-2.cdn.whatsapp.net", "media-cpt1-1.cdn.whatsapp.net", "media-maa2-1.cdn.whatsapp.net", "media.fgye10-1.fna.whatsapp.net", "e", "catalog_cart", "hfm_string_changes", "init_bitrate", "packless_hsm", "group_info", "America/Belem", "50", "960", "cond_range_bwe", "decode", "encode", "media.fada1-8.fna.whatsapp.net", "media.fadb1-2.fna.whatsapp.net", "media.fasu6-1.fna.whatsapp.net", "media.fbog4-1.fna.whatsapp.net", "media.fcgk9-2.fna.whatsapp.net", "media.fdoh5-2.fna.whatsapp.net", "media.ffjr1-2.fna.whatsapp.net", "media.fgua1-1.fna.whatsapp.net", "media.fgye1-1.fna.whatsapp.net", "media.fist1-4.fna.whatsapp.net", "media.fpbc2-2.fna.whatsapp.net", "media.fres2-1.fna.whatsapp.net", "media.fsdq1-2.fna.whatsapp.net", "media.fsub6-5.fna.whatsapp.net", "profilo_enabled", "template_hsm", "use_disorder_prefetching_timer", "video_codec_priority", "vpx_max_qp", "ptt_reduce_recording_delay", "25", "iphone", "Windows", "s_o", "Africa/Lagos", "abt", "media-kut2-1.cdn.whatsapp.net", "media-mba1-1.cdn.whatsapp.net", "media-mxp1-2.cdn.whatsapp.net", "md_blocklist_v2", "url_text", "enable_short_offset", "group_join_permissions", "enable_audio_piggyback_feature", "image_quality", "media.fcgk7-2.fna.whatsapp.net", "media.fcgk8-2.fna.whatsapp.net", "media.fclo7-1.fna.whatsapp.net", "media.fcmn1-1.fna.whatsapp.net", "media.feoh1-1.fna.whatsapp.net", "media.fgyd4-3.fna.whatsapp.net", "media.fjed4-4.fna.whatsapp.net", "media.flim1-4.fna.whatsapp.net", "media.flim2-4.fna.whatsapp.net", "media.fplu6-1.fna.whatsapp.net", "media.frak1-1.fna.whatsapp.net", "media.fsdq1-1.fna.whatsapp.net", "to_ip", "015", "vp8", "19", "21", "1320", "auth_key_ver", "message_processing_dedup", "server-error", "wap4_enabled", "420", "014", "cond_range_rtt", "ptt_fast_lock_enabled", "media-ort2-1.cdn.whatsapp.net", "fwd_ui_start_ts"]; + +module.exports.DICTIONARY_3_TOKEN = ["contact_blacklist", "Asia/Jakarta", "media.fepa10-1.fna.whatsapp.net", "media.fmex10-3.fna.whatsapp.net", "disorder_prefetching_start_when_empty", "America/Bogota", "use_local_probing_rx_bitrate", "America/Argentina/Buenos_Aires", "cross_post", "media.fabb1-1.fna.whatsapp.net", "media.fbog4-2.fna.whatsapp.net", "media.fcgk9-1.fna.whatsapp.net", "media.fcmn2-1.fna.whatsapp.net", "media.fdel3-1.fna.whatsapp.net", "media.ffjr1-1.fna.whatsapp.net", "media.fgdl5-1.fna.whatsapp.net", "media.flpb1-2.fna.whatsapp.net", "media.fmex2-1.fna.whatsapp.net", "media.frba2-2.fna.whatsapp.net", "media.fros2-2.fna.whatsapp.net", "media.fruh2-1.fna.whatsapp.net", "media.fybz2-2.fna.whatsapp.net", "options", "20", "a", "017", "018", "mute_always", "user_notice", "Asia/Kolkata", "gif_provider", "locked", "media-gua1-1.cdn.whatsapp.net", "piggyback_exclude_force_flush", "24", "media.frec39-1.fna.whatsapp.net", "user_remove", "file_max_size", "cond_packet_loss_pct_ema_alpha", "media.facc1-1.fna.whatsapp.net", "media.fadb2-1.fna.whatsapp.net", "media.faly3-1.fna.whatsapp.net", "media.fbdo6-2.fna.whatsapp.net", "media.fcmn2-2.fna.whatsapp.net", "media.fctg1-3.fna.whatsapp.net", "media.ffez1-2.fna.whatsapp.net", "media.fist1-3.fna.whatsapp.net", "media.fist2-2.fna.whatsapp.net", "media.flim2-2.fna.whatsapp.net", "media.fmct2-3.fna.whatsapp.net", "media.fpei3-1.fna.whatsapp.net", "media.frba3-1.fna.whatsapp.net", "media.fsdu8-2.fna.whatsapp.net", "media.fstu2-1.fna.whatsapp.net", "media_type", "receipt_agg", "016", "enable_pli_for_crc_mismatch", "live", "enc_rekey", "frskmsg", "d", "media.fdel11-2.fna.whatsapp.net", "proto", "2250", "audio_piggyback_enable_cache", "skip_nack_if_ltrp_sent", "mark_dtx_jb_frames", "web_service_delay", "7282", "catalog_send_all", "outgoing", "360", "30", "LIMITED", "019", "audio_picker", "bpv2_phase", "media.fada1-7.fna.whatsapp.net", "media.faep7-1.fna.whatsapp.net", "media.fbko1-2.fna.whatsapp.net", "media.fbni1-2.fna.whatsapp.net", "media.fbtz1-1.fna.whatsapp.net", "media.fbtz1-8.fna.whatsapp.net", "media.fcjs3-1.fna.whatsapp.net", "media.fesb3-2.fna.whatsapp.net", "media.fgdl5-4.fna.whatsapp.net", "media.fist2-1.fna.whatsapp.net", "media.flhe2-2.fna.whatsapp.net", "media.flim2-1.fna.whatsapp.net", "media.fmex1-1.fna.whatsapp.net", "media.fpat3-2.fna.whatsapp.net", "media.fpat3-3.fna.whatsapp.net", "media.fros2-1.fna.whatsapp.net", "media.fsdu8-1.fna.whatsapp.net", "media.fsub3-2.fna.whatsapp.net", "payments_chat_plugin", "cond_congestion_no_rtcp_thr", "green_alert", "not-a-biz", "..", "shops_pdp_urls_config", "source", "media-dus1-1.cdn.whatsapp.net", "mute_video", "01b", "currency", "max_keys", "resume_check", "contact_array", "qr_scanning", "23", "b", "media.fbfh15-1.fna.whatsapp.net", "media.flim22-1.fna.whatsapp.net", "media.fsdu11-1.fna.whatsapp.net", "media.fsdu15-1.fna.whatsapp.net", "Chrome", "fts_version", "60", "media.fada1-6.fna.whatsapp.net", "media.faep4-2.fna.whatsapp.net", "media.fbaq5-1.fna.whatsapp.net", "media.fbni1-1.fna.whatsapp.net", "media.fcai3-2.fna.whatsapp.net", "media.fdel3-2.fna.whatsapp.net", "media.fdmm3-2.fna.whatsapp.net", "media.fhex3-1.fna.whatsapp.net", "media.fisb4-2.fna.whatsapp.net", "media.fkhi5-2.fna.whatsapp.net", "media.flos2-1.fna.whatsapp.net", "media.fmct2-1.fna.whatsapp.net", "media.fntr7-1.fna.whatsapp.net", "media.frak3-1.fna.whatsapp.net", "media.fruh5-2.fna.whatsapp.net", "media.fsub6-1.fna.whatsapp.net", "media.fuab1-2.fna.whatsapp.net", "media.fuio1-1.fna.whatsapp.net", "media.fver1-1.fna.whatsapp.net", "media.fymy1-1.fna.whatsapp.net", "product_catalog", "1380", "audio_oob_fec_max_pkts", "22", "254", "media-ort2-2.cdn.whatsapp.net", "media-sjc3-1.cdn.whatsapp.net", "1600", "01a", "01c", "405", "key_frame_interval", "body", "media.fcgh20-1.fna.whatsapp.net", "media.fesb10-2.fna.whatsapp.net", "125", "2000", "media.fbsb1-1.fna.whatsapp.net", "media.fcmn3-2.fna.whatsapp.net", "media.fcpq1-1.fna.whatsapp.net", "media.fdel1-2.fna.whatsapp.net", "media.ffor2-1.fna.whatsapp.net", "media.fgdl1-4.fna.whatsapp.net", "media.fhex2-1.fna.whatsapp.net", "media.fist1-2.fna.whatsapp.net", "media.fjed5-2.fna.whatsapp.net", "media.flim6-4.fna.whatsapp.net", "media.flos2-2.fna.whatsapp.net", "media.fntr6-2.fna.whatsapp.net", "media.fpku3-2.fna.whatsapp.net", "media.fros8-1.fna.whatsapp.net", "media.fymy1-2.fna.whatsapp.net", "ul_bw", "ltrp_qp_offset", "request", "nack", "dtx_delay_state_reset", "timeoffline", "28", "01f", "32", "enable_ltr_pool", "wa_msys_crypto", "01d", "58", "dtx_freeze_hg_update", "nack_if_rpsi_throttled", "253", "840", "media.famd15-1.fna.whatsapp.net", "media.fbog17-2.fna.whatsapp.net", "media.fcai19-2.fna.whatsapp.net", "media.fcai21-4.fna.whatsapp.net", "media.fesb10-4.fna.whatsapp.net", "media.fesb10-5.fna.whatsapp.net", "media.fmaa12-1.fna.whatsapp.net", "media.fmex11-3.fna.whatsapp.net", "media.fpoa33-1.fna.whatsapp.net", "1050", "021", "clean", "cond_range_ema_packet_loss_pct", "media.fadb6-5.fna.whatsapp.net", "media.faqp4-1.fna.whatsapp.net", "media.fbaq3-1.fna.whatsapp.net", "media.fbel2-1.fna.whatsapp.net", "media.fblr4-2.fna.whatsapp.net", "media.fclo8-1.fna.whatsapp.net", "media.fcoo1-2.fna.whatsapp.net", "media.ffjr1-4.fna.whatsapp.net", "media.ffor9-1.fna.whatsapp.net", "media.fisb3-1.fna.whatsapp.net", "media.fkhi2-2.fna.whatsapp.net", "media.fkhi4-1.fna.whatsapp.net", "media.fpbc1-2.fna.whatsapp.net", "media.fruh2-2.fna.whatsapp.net", "media.fruh5-1.fna.whatsapp.net", "media.fsub3-1.fna.whatsapp.net", "payments_transaction_limit", "252", "27", "29", "tintagel", "01e", "237", "780", "callee_updated_payload", "020", "257", "price", "025", "239", "payments_cs_phone_number", "mediaretry", "w:auth:backup:token", "Glass.caf", "max_bitrate", "240", "251", "660", "media.fbog16-1.fna.whatsapp.net", "media.fcgh21-1.fna.whatsapp.net", "media.fkul19-2.fna.whatsapp.net", "media.flim21-2.fna.whatsapp.net", "media.fmex10-4.fna.whatsapp.net", "64", "33", "34", "35", "interruption", "media.fabv3-1.fna.whatsapp.net", "media.fadb6-1.fna.whatsapp.net", "media.fagr1-1.fna.whatsapp.net", "media.famd1-1.fna.whatsapp.net", "media.famm6-1.fna.whatsapp.net", "media.faqp2-3.fna.whatsapp.net"]; +module.exports.DICTIONARIES = [module.exports.DICTIONARY_0_TOKEN, module.exports.DICTIONARY_1_TOKEN, module.exports.DICTIONARY_2_TOKEN, module.exports.DICTIONARY_3_TOKEN]; + +module.exports.SINGLE_BYTE_TOKEN_MAP = buildMap(module.exports.SINGLE_BYTE_TOKEN); +module.exports.DICTIONARIES_MAP = module.exports.DICTIONARIES.map((dict) => buildMap(dict)) \ No newline at end of file diff --git a/WABinary/HexHelper.js b/WABinary/HexHelper.js new file mode 100644 index 0000000..e274291 --- /dev/null +++ b/WABinary/HexHelper.js @@ -0,0 +1,117 @@ +const { randomBytes } = require('crypto') + +const r = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70], + a = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102]; + +const i = (e) => { + for (var t = [], a = 0; a < e.length; a++) { + var i = e[a]; + t.push(r[i >> 4], r[15 & i]); + } + return String.fromCharCode.apply(String, t); +}; + +const n = (e, t) => { + var r = e.charCodeAt(t); + return r <= 57 ? r - 48 : r <= 70 ? 10 + r - 65 : 10 + r - 97; +}; + +const s = (e) => { + if (/[^0-9a-fA-F]/.test(e)) throw new Error(`"${e}" is not a valid hex`); + return e; +}; + +const o = (e, t) => { + for (var r = t - e.length, a = e, i = 0; i < r; i++) a = "0" + a; + return a; +}; + +const l = (e) => { + return "-" === e[0]; +}; + +const d = (e) => { + if (e > 4294967295 || e < -4294967296) + throw new Error("uint32ToLowerCaseHex given number over 32 bits"); + return o((e >= 0 ? e : 4294967296 + e).toString(16), 8); +}; + +module.exports.NUM_HEX_IN_LONG = 16; +module.exports.HEX_LOWER = a; + +module.exports.randomHex = function (e) { + var t = new Uint8Array(e); + var bytes = randomBytes(t.length); + t.set(bytes); + return i(t); +}; + +module.exports.toHex = i; + +module.exports.toLowerCaseHex = function (e) { + for (var t = [], r = 0; r < e.length; r++) { + var i = e[r]; + t.push(a[i >> 4], a[15 & i]); + } + return String.fromCharCode.apply(String, t); +}; + +module.exports.parseHex = function (e) { + var t = s(e); + if (t.length % 2 != 0) + throw new Error( + `parseHex given hex "${t}" which is not a multiple of 8-bits.` + ); + for ( + var r = new Uint8Array(t.length >> 1), a = 0, i = 0; + a < t.length; + a += 2, i++ + ) + r[i] = (n(t, a) << 4) | n(t, a + 1); + return r.buffer; +}; + +module.exports.hexAt = n; +module.exports.hexOrThrow = s; +module.exports.bytesToBuffer = function (e) { + var t = e.buffer; + return 0 === e.byteOffset && e.length === t.byteLength + ? t + : t.slice(e.byteOffset, e.byteOffset + e.length); +}; + +module.exports.bytesToDebugString = function (e) { + var t = !0, + r = e.length; + for (; t && r; ) { + var a = e[--r]; + t = 32 <= a && a < 127; + } + return t ? JSON.stringify(String.fromCharCode.apply(String, e)) : i(e); +}; + +module.exports.createHexLong = function (e, t = !1) { + var r = s(e); + return ( + (function (e, t) { + if (e.length > t) throw new Error(`"${e}" is longer than ${4 * t} bits.`); + })(r, 16), + `${t ? "-" : ""}0x${o(r, 16)}` + ); +}; + +module.exports.createHexLongFrom32Bits = function (e, t, r = !1) { + var a = d(e), + i = d(t); + return `${r ? "-" : ""}0x${a}${i}`; +}; + +module.exports.hexLongToHex = function (e) { + return e.substring(e.indexOf("0x") + 2); +}; + +module.exports.hexLongIsNegative = l; + +module.exports.negateHexLong = function (e) { + return l(e) ? e.slice(1) : "-" + e; +}; diff --git a/WABinary/readme.md b/WABinary/readme.md new file mode 100644 index 0000000..267c69d --- /dev/null +++ b/WABinary/readme.md @@ -0,0 +1,15 @@ +# WABinary + +Contains the raw JS code to parse WA binary messages. WA uses a tree like structure to encode information, the type for which is written below: + +``` ts +export type BinaryNode = { + tag: string + attrs: Attributes + content?: BinaryNode[] | string | Uint8Array +} +``` + +Do note, the multi-device binary format is very similar to the one on WA Web, though they are not backwards compatible. + +Originally from [pokearaujo/multidevice](https://github.com/pokearaujo/multidevice) \ No newline at end of file diff --git a/WAProto/GenerateStatics.sh b/WAProto/GenerateStatics.sh new file mode 100644 index 0000000..7d77a28 --- /dev/null +++ b/WAProto/GenerateStatics.sh @@ -0,0 +1,4 @@ +yarn pbjs -t static-module -w commonjs -o ./WAProto/index.js ./WAProto/WAProto.proto; +yarn pbts -o ./WAProto/index.d.ts ./WAProto/index.js; + +#protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=env=node,useOptionals=true,forceLong=long --ts_proto_out=. ./src/Binary/WAMessage.proto; \ No newline at end of file diff --git a/src/Binary/WAMessage.proto b/WAProto/WAProto.proto similarity index 88% rename from src/Binary/WAMessage.proto rename to WAProto/WAProto.proto index e7fa38c..1d28b5d 100644 --- a/src/Binary/WAMessage.proto +++ b/WAProto/WAProto.proto @@ -36,7 +36,10 @@ message UserAgent { IGLITE_ANDROID = 22; PAGE = 23; MACOS = 24; - VR = 25; + OCULUS_MSG = 25; + OCULUS_CALL = 26; + MILAN = 27; + CAPI = 28; } optional UserAgentPlatform platform = 1; optional AppVersion appVersion = 2; @@ -113,21 +116,11 @@ message CompanionRegData { message ClientPayload { optional uint64 username = 1; optional bool passive = 3; - enum ClientPayloadClientFeature { - NONE = 0; - } - repeated ClientPayloadClientFeature clientFeatures = 4; optional UserAgent userAgent = 5; optional WebInfo webInfo = 6; optional string pushName = 7; optional sfixed32 sessionId = 9; optional bool shortConnect = 10; - enum ClientPayloadIOSAppExtension { - SHARE_EXTENSION = 0; - SERVICE_EXTENSION = 1; - INTENTS_EXTENSION = 2; - } - optional ClientPayloadIOSAppExtension iosAppExtension = 30; enum ClientPayloadConnectType { CELLULAR_UNKNOWN = 0; WIFI_UNKNOWN = 1; @@ -169,15 +162,24 @@ message ClientPayload { optional bytes fbCat = 21; optional bytes fbUserAgent = 22; optional bool oc = 23; + optional uint32 lc = 24; + enum ClientPayloadIOSAppExtension { + SHARE_EXTENSION = 0; + SERVICE_EXTENSION = 1; + INTENTS_EXTENSION = 2; + } + optional ClientPayloadIOSAppExtension iosAppExtension = 30; + optional uint64 fbAppId = 31; + optional bytes fbDeviceId = 32; } -//message Details { -// optional uint32 serial = 1; -// optional string issuer = 2; -// optional uint64 expires = 3; -// optional string subject = 4; -// optional bytes key = 5; -//} +message Details { + optional uint32 serial = 1; + optional string issuer = 2; + optional uint64 expires = 3; + optional string subject = 4; + optional bytes key = 5; +} message NoiseCertificate { optional bytes details = 1; @@ -226,8 +228,9 @@ message BizIdentityInfo { SELF = 0; BSP = 1; } - optional BizIdentityInfoActualActorsType actualActors = 6; + optional BizIdentityInfoActualActorsType actualActors = 6; optional uint64 privacyModeTs = 7; + optional uint64 featureControls = 8; } message BizAccountLinkInfo { @@ -241,7 +244,6 @@ message BizAccountLinkInfo { optional BizAccountLinkInfoHostStorageType hostStorage = 4; enum BizAccountLinkInfoAccountType { ENTERPRISE = 0; - PAGE = 1; } optional BizAccountLinkInfoAccountType accountType = 5; } @@ -251,14 +253,6 @@ message BizAccountPayload { optional bytes bizAcctLinkInfo = 2; } -//message Details { -// optional uint64 serial = 1; -// optional string issuer = 2; -// optional string verifiedName = 4; -// repeated LocalizedName localizedNames = 8; -// optional uint64 issueTime = 10; -//} - message VerifiedNameCertificate { optional bytes details = 1; optional bytes signature = 2; @@ -345,6 +339,17 @@ message RecentEmojiWeightsAction { repeated RecentEmojiWeight weights = 1; } +message FavoriteStickerAction { + optional string directPath = 1; + optional string lastUploadTimestamp = 2; + optional string handle = 3; + optional string encFilehash = 4; + optional string stickerHashWithoutMeta = 5; + optional string mediaKey = 6; + optional int64 mediaKeyTimestamp = 7; + optional bool isFavorite = 8; +} + message ArchiveChatAction { optional bool archived = 1; optional SyncActionMessageRange messageRange = 2; @@ -387,6 +392,14 @@ message KeyExpiration { optional int32 expiredKeyEpoch = 1; } +message PrimaryFeature { + repeated string flags = 1; +} + +message AndroidUnsupportedActions { + optional bool allowed = 1; +} + message SyncActionValue { optional int64 timestamp = 1; optional StarAction starAction = 2; @@ -409,6 +422,9 @@ message SyncActionValue { optional ClearChatAction clearChatAction = 21; optional DeleteChatAction deleteChatAction = 22; optional UnarchiveChatsSetting unarchiveChatsSetting = 23; + optional PrimaryFeature primaryFeature = 24; + optional FavoriteStickerAction favoriteStickerAction = 25; + optional AndroidUnsupportedActions androidUnsupportedActions = 26; } message RecentEmojiWeight { @@ -507,8 +523,6 @@ message MediaRetryNotification { message MsgOpaqueData { optional string body = 1; optional string caption = 3; - optional string clientUrl = 4; -// optional string loc = 4; optional double lng = 5; optional double lat = 7; optional int32 paymentAmount1000 = 8; @@ -517,6 +531,9 @@ message MsgOpaqueData { optional string matchedText = 11; optional string title = 12; optional string description = 13; + optional bytes futureproofBuffer = 14; + optional string clientUrl = 15; + optional string loc = 16; } message MsgRowOpaqueData { @@ -524,6 +541,27 @@ message MsgRowOpaqueData { optional MsgOpaqueData quotedMsg = 2; } +message GlobalSettings { + optional WallpaperSettings lightThemeWallpaper = 1; + optional MediaVisibility mediaVisibility = 2; + optional WallpaperSettings darkThemeWallpaper = 3; +} + +message WallpaperSettings { + optional string filename = 1; + optional uint32 opacity = 2; +} + +message GroupParticipant { + required string userJid = 1; + enum GroupParticipantRank { + REGULAR = 0; + ADMIN = 1; + SUPERADMIN = 2; + } + optional GroupParticipantRank rank = 2; +} + message Pushname { optional string id = 1; optional string pushname = 2; @@ -554,6 +592,20 @@ message Conversation { optional string name = 13; optional string pHash = 14; optional bool notSpam = 15; + optional bool archived = 16; + optional DisappearingMode disappearingMode = 17; + optional uint32 unreadMentionCount = 18; + optional bool markedAsUnread = 19; + repeated GroupParticipant participant = 20; + optional bytes tcToken = 21; + optional uint64 tcTokenTimestamp = 22; + optional bytes contactPrimaryIdentityKey = 23; + optional uint32 pinned = 24; + optional uint64 muteEndTime = 25; + optional WallpaperSettings wallpaper = 26; + optional MediaVisibility mediaVisibility = 27; + optional uint64 tcTokenSenderTimestamp = 28; + optional bool suspended = 29; } message HistorySync { @@ -570,91 +622,21 @@ message HistorySync { optional uint32 chunkOrder = 5; optional uint32 progress = 6; repeated Pushname pushnames = 7; + optional GlobalSettings globalSettings = 8; + optional bytes threadIdUserSecret = 9; + optional uint32 threadDsTimeframeOffset = 10; } +enum MediaVisibility { + DEFAULT = 0; + OFF = 1; + ON = 2; +} message EphemeralSetting { optional sfixed32 duration = 1; optional sfixed64 timestamp = 2; } -message PaymentBackground { - optional string id = 1; - optional string fileLength = 2; - optional uint32 width = 3; - optional uint32 height = 4; - optional string mimetype = 5; - optional fixed32 placeholderArgb = 6; - optional fixed32 textArgb = 7; - optional fixed32 subtextArgb = 8; -} - -message Money { - optional int64 value = 1; - optional uint32 offset = 2; - optional string currencyCode = 3; -} - -message HydratedQuickReplyButton { - optional string displayText = 1; - optional string id = 2; -} - -message HydratedURLButton { - optional string displayText = 1; - optional string url = 2; -} - -message HydratedCallButton { - optional string displayText = 1; - optional string phoneNumber = 2; -} - -message HydratedTemplateButton { - optional uint32 index = 4; - oneof hydratedButton { - HydratedQuickReplyButton quickReplyButton = 1; - HydratedURLButton urlButton = 2; - HydratedCallButton callButton = 3; - } -} - -message QuickReplyButton { - optional HighlyStructuredMessage displayText = 1; - optional string id = 2; -} - -message URLButton { - optional HighlyStructuredMessage displayText = 1; - optional HighlyStructuredMessage url = 2; -} - -message CallButton { - optional HighlyStructuredMessage displayText = 1; - optional HighlyStructuredMessage phoneNumber = 2; -} - -message TemplateButton { - optional uint32 index = 4; - oneof button { - QuickReplyButton quickReplyButton = 1; - URLButton urlButton = 2; - CallButton callButton = 3; - } -} - -message Location { - optional double degreesLatitude = 1; - optional double degreesLongitude = 2; - optional string name = 3; -} - -message Point { - optional int32 xDeprecated = 1; - optional int32 yDeprecated = 2; - optional double x = 3; - optional double y = 4; -} - message InteractiveAnnotation { repeated Point polygonVertices = 1; oneof action { @@ -725,6 +707,10 @@ message ContextInfo { optional string entryPointConversionSource = 29; optional string entryPointConversionApp = 30; optional uint32 entryPointConversionDelaySeconds = 31; + optional DisappearingMode disappearingMode = 32; + optional ActionLink actionLink = 33; + optional string groupSubject = 34; + optional string parentGroupJid = 35; } message SenderKeyDistributionMessage { @@ -758,6 +744,7 @@ message ImageMessage { optional string thumbnailDirectPath = 26; optional bytes thumbnailSha256 = 27; optional bytes thumbnailEncSha256 = 28; + optional string staticUrl = 29; } message InvoiceMessage { @@ -830,6 +817,11 @@ message ExtendedTextMessage { optional int64 mediaKeyTimestamp = 23; optional uint32 thumbnailHeight = 24; optional uint32 thumbnailWidth = 25; + enum ExtendedTextMessageInviteLinkGroupType { + DEFAULT = 0; + PARENT = 1; + } + optional ExtendedTextMessageInviteLinkGroupType inviteLinkGroupType = 26; } message DocumentMessage { @@ -867,6 +859,7 @@ message AudioMessage { optional int64 mediaKeyTimestamp = 10; optional ContextInfo contextInfo = 17; optional bytes streamingSidecar = 18; + optional bytes waveform = 19; } message VideoMessage { @@ -897,6 +890,7 @@ message VideoMessage { optional string thumbnailDirectPath = 21; optional bytes thumbnailSha256 = 22; optional bytes thumbnailEncSha256 = 23; + optional string staticUrl = 24; } message Call { @@ -932,6 +926,7 @@ message ProtocolMessage { optional AppStateSyncKeyRequest appStateSyncKeyRequest = 8; optional InitialSecurityNotificationSettingSync initialSecurityNotificationSettingSync = 9; optional AppStateFatalExceptionNotification appStateFatalExceptionNotification = 10; + optional DisappearingMode disappearingMode = 11; } message HistorySyncNotification { @@ -1188,6 +1183,8 @@ message ProductMessage { optional ProductSnapshot product = 1; optional string businessOwnerJid = 2; optional CatalogSnapshot catalog = 4; + optional string body = 5; + optional string footer = 6; optional ContextInfo contextInfo = 17; } @@ -1275,6 +1272,67 @@ message ListResponseMessage { optional string description = 5; } +message Header { + optional string title = 1; + optional string subtitle = 2; + optional bool hasMediaAttachment = 5; + oneof media { + DocumentMessage documentMessage = 3; + ImageMessage imageMessage = 4; + bytes jpegThumbnail = 6; + VideoMessage videoMessage = 7; + } +} + +message Body { + optional string text = 1; +} + +message Footer { + optional string text = 1; +} + +message ShopMessage { + optional string id = 1; + enum ShopMessageSurface { + UNKNOWN_SURFACE = 0; + FB = 1; + IG = 2; + WA = 3; + } + optional ShopMessageSurface surface = 2; + optional int32 messageVersion = 3; +} + +message CollectionMessage { + optional string bizJid = 1; + optional string id = 2; + optional int32 messageVersion = 3; +} + +message NativeFlowButton { + optional string name = 1; + optional string buttonParamsJson = 2; +} + +message NativeFlowMessage { + repeated NativeFlowButton buttons = 1; + optional string messageParamsJson = 2; + optional int32 messageVersion = 3; +} + +message InteractiveMessage { + optional Header header = 1; + optional Body body = 2; + optional Footer footer = 3; + optional ContextInfo contextInfo = 15; + oneof interactiveMessage { + ShopMessage shopStorefrontMessage = 4; + CollectionMessage collectionMessage = 5; + NativeFlowMessage nativeFlowMessage = 6; + } +} + message GroupInviteMessage { optional string groupJid = 1; optional string inviteCode = 2; @@ -1283,6 +1341,11 @@ message GroupInviteMessage { optional bytes jpegThumbnail = 5; optional string caption = 6; optional ContextInfo contextInfo = 7; + enum GroupInviteMessageGroupType { + DEFAULT = 0; + PARENT = 1; + } + optional GroupInviteMessageGroupType groupType = 8; } message DeviceSentMessage { @@ -1353,6 +1416,19 @@ message ButtonsResponseMessage { } } +message ReactionMessage { + optional MessageKey key = 1; + optional string text = 2; + optional string groupingKey = 3; + optional int64 senderTimestampMs = 4; +} + +message StickerSyncRMRMessage { + repeated string filehash = 1; + optional string rmrSource = 2; + optional int64 requestTimestamp = 3; +} + message Message { optional string conversation = 1; optional SenderKeyDistributionMessage senderKeyDistributionMessage = 2; @@ -1390,6 +1466,115 @@ message Message { optional ButtonsMessage buttonsMessage = 42; optional ButtonsResponseMessage buttonsResponseMessage = 43; optional PaymentInviteMessage paymentInviteMessage = 44; + optional InteractiveMessage interactiveMessage = 45; + optional ReactionMessage reactionMessage = 46; + optional StickerSyncRMRMessage stickerSyncRmrMessage = 47; +} + +message ActionLink { + optional string url = 1; + optional string buttonTitle = 2; +} + +message DisappearingMode { + enum DisappearingModeInitiator { + CHANGED_IN_CHAT = 0; + INITIATED_BY_ME = 1; + INITIATED_BY_OTHER = 2; + } + optional DisappearingModeInitiator initiator = 1; +} + +message MediaData { + optional bytes mediaKey = 1; + optional int64 mediaKeyTimestamp = 2; + optional bytes fileSha256 = 3; + optional bytes fileEncSha256 = 4; + optional string directPath = 5; +} + +message PaymentBackground { + optional string id = 1; + optional uint64 fileLength = 2; + optional uint32 width = 3; + optional uint32 height = 4; + optional string mimetype = 5; + optional fixed32 placeholderArgb = 6; + optional fixed32 textArgb = 7; + optional fixed32 subtextArgb = 8; + optional MediaData mediaData = 9; + enum PaymentBackgroundType { + UNKNOWN = 0; + DEFAULT = 1; + } + optional PaymentBackgroundType type = 10; +} + +message Money { + optional int64 value = 1; + optional uint32 offset = 2; + optional string currencyCode = 3; +} + +message HydratedQuickReplyButton { + optional string displayText = 1; + optional string id = 2; +} + +message HydratedURLButton { + optional string displayText = 1; + optional string url = 2; +} + +message HydratedCallButton { + optional string displayText = 1; + optional string phoneNumber = 2; +} + +message HydratedTemplateButton { + optional uint32 index = 4; + oneof hydratedButton { + HydratedQuickReplyButton quickReplyButton = 1; + HydratedURLButton urlButton = 2; + HydratedCallButton callButton = 3; + } +} + +message QuickReplyButton { + optional HighlyStructuredMessage displayText = 1; + optional string id = 2; +} + +message URLButton { + optional HighlyStructuredMessage displayText = 1; + optional HighlyStructuredMessage url = 2; +} + +message CallButton { + optional HighlyStructuredMessage displayText = 1; + optional HighlyStructuredMessage phoneNumber = 2; +} + +message TemplateButton { + optional uint32 index = 4; + oneof button { + QuickReplyButton quickReplyButton = 1; + URLButton urlButton = 2; + CallButton callButton = 3; + } +} + +message Location { + optional double degreesLatitude = 1; + optional double degreesLongitude = 2; + optional string name = 3; +} + +message Point { + optional int32 xDeprecated = 1; + optional int32 yDeprecated = 2; + optional double x = 3; + optional double y = 4; } message CompanionProps { @@ -1451,16 +1636,29 @@ message MessageKey { optional string participant = 4; } +message Reaction { + optional MessageKey key = 1; + optional string text = 2; + optional string groupingKey = 3; + optional int64 senderTimestampMs = 4; + optional bool unread = 5; +} + +message UserReceipt { + required string userJid = 1; + optional int64 receiptTimestamp = 2; + optional int64 readTimestamp = 3; + optional int64 playedTimestamp = 4; + repeated string pendingDeviceJid = 5; + repeated string deliveredDeviceJid = 6; +} + message PhotoChange { optional bytes oldPhoto = 1; optional bytes newPhoto = 2; optional uint32 newPhotoId = 3; } -message MediaData { - optional string localPath = 1; -} - message WebFeatures { enum WebFeaturesFlag { NOT_STARTED = 0; @@ -1510,6 +1708,9 @@ message WebFeatures { optional WebFeaturesFlag ephemeralAllowGroupMembers = 44; optional WebFeaturesFlag ephemeral24HDuration = 45; optional WebFeaturesFlag mdForceUpgrade = 46; + optional WebFeaturesFlag disappearingMode = 47; + optional WebFeaturesFlag externalMdOptInAvailable = 48; + optional WebFeaturesFlag noDeleteMessageTimeLimit = 49; } message NotificationMessageInfo { @@ -1745,6 +1946,8 @@ message WebMessageInfo { BIZ_PRIVACY_MODE_INIT_BSP = 127; BIZ_PRIVACY_MODE_TO_FB = 128; BIZ_PRIVACY_MODE_TO_BSP = 129; + DISAPPEARING_MODE = 130; + E2E_DEVICE_FETCH_FAILED = 131; } optional WebMessageInfoStubType messageStubType = 24; optional bool clearMedia = 25; @@ -1768,4 +1971,11 @@ message WebMessageInfo { optional string verifiedBizName = 37; optional MediaData mediaData = 38; optional PhotoChange photoChange = 39; -} \ No newline at end of file + repeated UserReceipt userReceipt = 40; + repeated Reaction reactions = 41; + optional MediaData quotedStickerData = 42; + optional bytes futureproofData = 43; + optional string statusPsaCampaignId = 44; + optional uint32 statusPsaCampaignDuration = 45; + optional uint64 statusPsaCampaignReadTimestamp = 46; +} diff --git a/WAMessage/WAMessage.d.ts b/WAProto/index.d.ts similarity index 88% rename from WAMessage/WAMessage.d.ts rename to WAProto/index.d.ts index 51d9d1b..b5e645c 100644 --- a/WAMessage/WAMessage.d.ts +++ b/WAProto/index.d.ts @@ -307,7 +307,10 @@ export namespace proto { IGLITE_ANDROID = 22, PAGE = 23, MACOS = 24, - VR = 25 + OCULUS_MSG = 25, + OCULUS_CALL = 26, + MILAN = 27, + CAPI = 28 } /** UserAgentReleaseChannel enum. */ @@ -838,9 +841,6 @@ export namespace proto { /** ClientPayload passive */ passive?: (boolean|null); - /** ClientPayload clientFeatures */ - clientFeatures?: (proto.ClientPayload.ClientPayloadClientFeature[]|null); - /** ClientPayload userAgent */ userAgent?: (proto.IUserAgent|null); @@ -856,9 +856,6 @@ export namespace proto { /** ClientPayload shortConnect */ shortConnect?: (boolean|null); - /** ClientPayload iosAppExtension */ - iosAppExtension?: (proto.ClientPayload.ClientPayloadIOSAppExtension|null); - /** ClientPayload connectType */ connectType?: (proto.ClientPayload.ClientPayloadConnectType|null); @@ -894,6 +891,18 @@ export namespace proto { /** ClientPayload oc */ oc?: (boolean|null); + + /** ClientPayload lc */ + lc?: (number|null); + + /** ClientPayload iosAppExtension */ + iosAppExtension?: (proto.ClientPayload.ClientPayloadIOSAppExtension|null); + + /** ClientPayload fbAppId */ + fbAppId?: (number|Long|null); + + /** ClientPayload fbDeviceId */ + fbDeviceId?: (Uint8Array|null); } /** Represents a ClientPayload. */ @@ -911,9 +920,6 @@ export namespace proto { /** ClientPayload passive. */ public passive: boolean; - /** ClientPayload clientFeatures. */ - public clientFeatures: proto.ClientPayload.ClientPayloadClientFeature[]; - /** ClientPayload userAgent. */ public userAgent?: (proto.IUserAgent|null); @@ -929,9 +935,6 @@ export namespace proto { /** ClientPayload shortConnect. */ public shortConnect: boolean; - /** ClientPayload iosAppExtension. */ - public iosAppExtension: proto.ClientPayload.ClientPayloadIOSAppExtension; - /** ClientPayload connectType. */ public connectType: proto.ClientPayload.ClientPayloadConnectType; @@ -968,6 +971,18 @@ export namespace proto { /** ClientPayload oc. */ public oc: boolean; + /** ClientPayload lc. */ + public lc: number; + + /** ClientPayload iosAppExtension. */ + public iosAppExtension: proto.ClientPayload.ClientPayloadIOSAppExtension; + + /** ClientPayload fbAppId. */ + public fbAppId: (number|Long); + + /** ClientPayload fbDeviceId. */ + public fbDeviceId: Uint8Array; + /** * Creates a new ClientPayload instance using the specified properties. * @param [properties] Properties to set @@ -1041,18 +1056,6 @@ export namespace proto { namespace ClientPayload { - /** ClientPayloadClientFeature enum. */ - enum ClientPayloadClientFeature { - NONE = 0 - } - - /** ClientPayloadIOSAppExtension enum. */ - enum ClientPayloadIOSAppExtension { - SHARE_EXTENSION = 0, - SERVICE_EXTENSION = 1, - INTENTS_EXTENSION = 2 - } - /** ClientPayloadConnectType enum. */ enum ClientPayloadConnectType { CELLULAR_UNKNOWN = 0, @@ -1087,6 +1090,127 @@ export namespace proto { WHATSAPP = 0, MESSENGER = 1 } + + /** ClientPayloadIOSAppExtension enum. */ + enum ClientPayloadIOSAppExtension { + SHARE_EXTENSION = 0, + SERVICE_EXTENSION = 1, + INTENTS_EXTENSION = 2 + } + } + + /** Properties of a Details. */ + interface IDetails { + + /** Details serial */ + serial?: (number|null); + + /** Details issuer */ + issuer?: (string|null); + + /** Details expires */ + expires?: (number|Long|null); + + /** Details subject */ + subject?: (string|null); + + /** Details key */ + key?: (Uint8Array|null); + } + + /** Represents a Details. */ + class Details implements IDetails { + + /** + * Constructs a new Details. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IDetails); + + /** Details serial. */ + public serial: number; + + /** Details issuer. */ + public issuer: string; + + /** Details expires. */ + public expires: (number|Long); + + /** Details subject. */ + public subject: string; + + /** Details key. */ + public key: Uint8Array; + + /** + * Creates a new Details instance using the specified properties. + * @param [properties] Properties to set + * @returns Details instance + */ + public static create(properties?: proto.IDetails): proto.Details; + + /** + * Encodes the specified Details message. Does not implicitly {@link proto.Details.verify|verify} messages. + * @param message Details message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IDetails, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Details message, length delimited. Does not implicitly {@link proto.Details.verify|verify} messages. + * @param message Details message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IDetails, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Details message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Details + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Details; + + /** + * Decodes a Details message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Details + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Details; + + /** + * Verifies a Details message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Details message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Details + */ + public static fromObject(object: { [k: string]: any }): proto.Details; + + /** + * Creates a plain object from a Details message. Also converts values to other types if specified. + * @param message Details + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Details, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Details to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; } /** Properties of a NoiseCertificate. */ @@ -1610,6 +1734,9 @@ export namespace proto { /** BizIdentityInfo privacyModeTs */ privacyModeTs?: (number|Long|null); + + /** BizIdentityInfo featureControls */ + featureControls?: (number|Long|null); } /** Represents a BizIdentityInfo. */ @@ -1642,6 +1769,9 @@ export namespace proto { /** BizIdentityInfo privacyModeTs. */ public privacyModeTs: (number|Long); + /** BizIdentityInfo featureControls. */ + public featureControls: (number|Long); + /** * Creates a new BizIdentityInfo instance using the specified properties. * @param [properties] Properties to set @@ -1859,8 +1989,7 @@ export namespace proto { /** BizAccountLinkInfoAccountType enum. */ enum BizAccountLinkInfoAccountType { - ENTERPRISE = 0, - PAGE = 1 + ENTERPRISE = 0 } } @@ -3532,6 +3661,138 @@ export namespace proto { public toJSON(): { [k: string]: any }; } + /** Properties of a FavoriteStickerAction. */ + interface IFavoriteStickerAction { + + /** FavoriteStickerAction directPath */ + directPath?: (string|null); + + /** FavoriteStickerAction lastUploadTimestamp */ + lastUploadTimestamp?: (string|null); + + /** FavoriteStickerAction handle */ + handle?: (string|null); + + /** FavoriteStickerAction encFilehash */ + encFilehash?: (string|null); + + /** FavoriteStickerAction stickerHashWithoutMeta */ + stickerHashWithoutMeta?: (string|null); + + /** FavoriteStickerAction mediaKey */ + mediaKey?: (string|null); + + /** FavoriteStickerAction mediaKeyTimestamp */ + mediaKeyTimestamp?: (number|Long|null); + + /** FavoriteStickerAction isFavorite */ + isFavorite?: (boolean|null); + } + + /** Represents a FavoriteStickerAction. */ + class FavoriteStickerAction implements IFavoriteStickerAction { + + /** + * Constructs a new FavoriteStickerAction. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IFavoriteStickerAction); + + /** FavoriteStickerAction directPath. */ + public directPath: string; + + /** FavoriteStickerAction lastUploadTimestamp. */ + public lastUploadTimestamp: string; + + /** FavoriteStickerAction handle. */ + public handle: string; + + /** FavoriteStickerAction encFilehash. */ + public encFilehash: string; + + /** FavoriteStickerAction stickerHashWithoutMeta. */ + public stickerHashWithoutMeta: string; + + /** FavoriteStickerAction mediaKey. */ + public mediaKey: string; + + /** FavoriteStickerAction mediaKeyTimestamp. */ + public mediaKeyTimestamp: (number|Long); + + /** FavoriteStickerAction isFavorite. */ + public isFavorite: boolean; + + /** + * Creates a new FavoriteStickerAction instance using the specified properties. + * @param [properties] Properties to set + * @returns FavoriteStickerAction instance + */ + public static create(properties?: proto.IFavoriteStickerAction): proto.FavoriteStickerAction; + + /** + * Encodes the specified FavoriteStickerAction message. Does not implicitly {@link proto.FavoriteStickerAction.verify|verify} messages. + * @param message FavoriteStickerAction message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IFavoriteStickerAction, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified FavoriteStickerAction message, length delimited. Does not implicitly {@link proto.FavoriteStickerAction.verify|verify} messages. + * @param message FavoriteStickerAction message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IFavoriteStickerAction, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a FavoriteStickerAction message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns FavoriteStickerAction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.FavoriteStickerAction; + + /** + * Decodes a FavoriteStickerAction message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns FavoriteStickerAction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.FavoriteStickerAction; + + /** + * Verifies a FavoriteStickerAction message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a FavoriteStickerAction message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns FavoriteStickerAction + */ + public static fromObject(object: { [k: string]: any }): proto.FavoriteStickerAction; + + /** + * Creates a plain object from a FavoriteStickerAction message. Also converts values to other types if specified. + * @param message FavoriteStickerAction + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.FavoriteStickerAction, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this FavoriteStickerAction to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of an ArchiveChatAction. */ interface IArchiveChatAction { @@ -4378,6 +4639,186 @@ export namespace proto { public toJSON(): { [k: string]: any }; } + /** Properties of a PrimaryFeature. */ + interface IPrimaryFeature { + + /** PrimaryFeature flags */ + flags?: (string[]|null); + } + + /** Represents a PrimaryFeature. */ + class PrimaryFeature implements IPrimaryFeature { + + /** + * Constructs a new PrimaryFeature. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IPrimaryFeature); + + /** PrimaryFeature flags. */ + public flags: string[]; + + /** + * Creates a new PrimaryFeature instance using the specified properties. + * @param [properties] Properties to set + * @returns PrimaryFeature instance + */ + public static create(properties?: proto.IPrimaryFeature): proto.PrimaryFeature; + + /** + * Encodes the specified PrimaryFeature message. Does not implicitly {@link proto.PrimaryFeature.verify|verify} messages. + * @param message PrimaryFeature message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IPrimaryFeature, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified PrimaryFeature message, length delimited. Does not implicitly {@link proto.PrimaryFeature.verify|verify} messages. + * @param message PrimaryFeature message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IPrimaryFeature, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a PrimaryFeature message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns PrimaryFeature + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.PrimaryFeature; + + /** + * Decodes a PrimaryFeature message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns PrimaryFeature + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.PrimaryFeature; + + /** + * Verifies a PrimaryFeature message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a PrimaryFeature message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns PrimaryFeature + */ + public static fromObject(object: { [k: string]: any }): proto.PrimaryFeature; + + /** + * Creates a plain object from a PrimaryFeature message. Also converts values to other types if specified. + * @param message PrimaryFeature + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.PrimaryFeature, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this PrimaryFeature to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of an AndroidUnsupportedActions. */ + interface IAndroidUnsupportedActions { + + /** AndroidUnsupportedActions allowed */ + allowed?: (boolean|null); + } + + /** Represents an AndroidUnsupportedActions. */ + class AndroidUnsupportedActions implements IAndroidUnsupportedActions { + + /** + * Constructs a new AndroidUnsupportedActions. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IAndroidUnsupportedActions); + + /** AndroidUnsupportedActions allowed. */ + public allowed: boolean; + + /** + * Creates a new AndroidUnsupportedActions instance using the specified properties. + * @param [properties] Properties to set + * @returns AndroidUnsupportedActions instance + */ + public static create(properties?: proto.IAndroidUnsupportedActions): proto.AndroidUnsupportedActions; + + /** + * Encodes the specified AndroidUnsupportedActions message. Does not implicitly {@link proto.AndroidUnsupportedActions.verify|verify} messages. + * @param message AndroidUnsupportedActions message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IAndroidUnsupportedActions, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified AndroidUnsupportedActions message, length delimited. Does not implicitly {@link proto.AndroidUnsupportedActions.verify|verify} messages. + * @param message AndroidUnsupportedActions message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IAndroidUnsupportedActions, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes an AndroidUnsupportedActions message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns AndroidUnsupportedActions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.AndroidUnsupportedActions; + + /** + * Decodes an AndroidUnsupportedActions message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns AndroidUnsupportedActions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.AndroidUnsupportedActions; + + /** + * Verifies an AndroidUnsupportedActions message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates an AndroidUnsupportedActions message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns AndroidUnsupportedActions + */ + public static fromObject(object: { [k: string]: any }): proto.AndroidUnsupportedActions; + + /** + * Creates a plain object from an AndroidUnsupportedActions message. Also converts values to other types if specified. + * @param message AndroidUnsupportedActions + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.AndroidUnsupportedActions, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this AndroidUnsupportedActions to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of a SyncActionValue. */ interface ISyncActionValue { @@ -4443,6 +4884,15 @@ export namespace proto { /** SyncActionValue unarchiveChatsSetting */ unarchiveChatsSetting?: (proto.IUnarchiveChatsSetting|null); + + /** SyncActionValue primaryFeature */ + primaryFeature?: (proto.IPrimaryFeature|null); + + /** SyncActionValue favoriteStickerAction */ + favoriteStickerAction?: (proto.IFavoriteStickerAction|null); + + /** SyncActionValue androidUnsupportedActions */ + androidUnsupportedActions?: (proto.IAndroidUnsupportedActions|null); } /** Represents a SyncActionValue. */ @@ -4517,6 +4967,15 @@ export namespace proto { /** SyncActionValue unarchiveChatsSetting. */ public unarchiveChatsSetting?: (proto.IUnarchiveChatsSetting|null); + /** SyncActionValue primaryFeature. */ + public primaryFeature?: (proto.IPrimaryFeature|null); + + /** SyncActionValue favoriteStickerAction. */ + public favoriteStickerAction?: (proto.IFavoriteStickerAction|null); + + /** SyncActionValue androidUnsupportedActions. */ + public androidUnsupportedActions?: (proto.IAndroidUnsupportedActions|null); + /** * Creates a new SyncActionValue instance using the specified properties. * @param [properties] Properties to set @@ -6105,9 +6564,6 @@ export namespace proto { /** MsgOpaqueData caption */ caption?: (string|null); - /** MsgOpaqueData clientUrl */ - clientUrl?: (string|null); - /** MsgOpaqueData lng */ lng?: (number|null); @@ -6131,6 +6587,15 @@ export namespace proto { /** MsgOpaqueData description */ description?: (string|null); + + /** MsgOpaqueData futureproofBuffer */ + futureproofBuffer?: (Uint8Array|null); + + /** MsgOpaqueData clientUrl */ + clientUrl?: (string|null); + + /** MsgOpaqueData loc */ + loc?: (string|null); } /** Represents a MsgOpaqueData. */ @@ -6148,9 +6613,6 @@ export namespace proto { /** MsgOpaqueData caption. */ public caption: string; - /** MsgOpaqueData clientUrl. */ - public clientUrl: string; - /** MsgOpaqueData lng. */ public lng: number; @@ -6175,6 +6637,15 @@ export namespace proto { /** MsgOpaqueData description. */ public description: string; + /** MsgOpaqueData futureproofBuffer. */ + public futureproofBuffer: Uint8Array; + + /** MsgOpaqueData clientUrl. */ + public clientUrl: string; + + /** MsgOpaqueData loc. */ + public loc: string; + /** * Creates a new MsgOpaqueData instance using the specified properties. * @param [properties] Properties to set @@ -6342,6 +6813,310 @@ export namespace proto { public toJSON(): { [k: string]: any }; } + /** Properties of a GlobalSettings. */ + interface IGlobalSettings { + + /** GlobalSettings lightThemeWallpaper */ + lightThemeWallpaper?: (proto.IWallpaperSettings|null); + + /** GlobalSettings mediaVisibility */ + mediaVisibility?: (proto.MediaVisibility|null); + + /** GlobalSettings darkThemeWallpaper */ + darkThemeWallpaper?: (proto.IWallpaperSettings|null); + } + + /** Represents a GlobalSettings. */ + class GlobalSettings implements IGlobalSettings { + + /** + * Constructs a new GlobalSettings. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IGlobalSettings); + + /** GlobalSettings lightThemeWallpaper. */ + public lightThemeWallpaper?: (proto.IWallpaperSettings|null); + + /** GlobalSettings mediaVisibility. */ + public mediaVisibility: proto.MediaVisibility; + + /** GlobalSettings darkThemeWallpaper. */ + public darkThemeWallpaper?: (proto.IWallpaperSettings|null); + + /** + * Creates a new GlobalSettings instance using the specified properties. + * @param [properties] Properties to set + * @returns GlobalSettings instance + */ + public static create(properties?: proto.IGlobalSettings): proto.GlobalSettings; + + /** + * Encodes the specified GlobalSettings message. Does not implicitly {@link proto.GlobalSettings.verify|verify} messages. + * @param message GlobalSettings message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IGlobalSettings, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified GlobalSettings message, length delimited. Does not implicitly {@link proto.GlobalSettings.verify|verify} messages. + * @param message GlobalSettings message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IGlobalSettings, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a GlobalSettings message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns GlobalSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.GlobalSettings; + + /** + * Decodes a GlobalSettings message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns GlobalSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.GlobalSettings; + + /** + * Verifies a GlobalSettings message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a GlobalSettings message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns GlobalSettings + */ + public static fromObject(object: { [k: string]: any }): proto.GlobalSettings; + + /** + * Creates a plain object from a GlobalSettings message. Also converts values to other types if specified. + * @param message GlobalSettings + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.GlobalSettings, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this GlobalSettings to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a WallpaperSettings. */ + interface IWallpaperSettings { + + /** WallpaperSettings filename */ + filename?: (string|null); + + /** WallpaperSettings opacity */ + opacity?: (number|null); + } + + /** Represents a WallpaperSettings. */ + class WallpaperSettings implements IWallpaperSettings { + + /** + * Constructs a new WallpaperSettings. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IWallpaperSettings); + + /** WallpaperSettings filename. */ + public filename: string; + + /** WallpaperSettings opacity. */ + public opacity: number; + + /** + * Creates a new WallpaperSettings instance using the specified properties. + * @param [properties] Properties to set + * @returns WallpaperSettings instance + */ + public static create(properties?: proto.IWallpaperSettings): proto.WallpaperSettings; + + /** + * Encodes the specified WallpaperSettings message. Does not implicitly {@link proto.WallpaperSettings.verify|verify} messages. + * @param message WallpaperSettings message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IWallpaperSettings, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified WallpaperSettings message, length delimited. Does not implicitly {@link proto.WallpaperSettings.verify|verify} messages. + * @param message WallpaperSettings message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IWallpaperSettings, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a WallpaperSettings message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns WallpaperSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.WallpaperSettings; + + /** + * Decodes a WallpaperSettings message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns WallpaperSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.WallpaperSettings; + + /** + * Verifies a WallpaperSettings message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a WallpaperSettings message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns WallpaperSettings + */ + public static fromObject(object: { [k: string]: any }): proto.WallpaperSettings; + + /** + * Creates a plain object from a WallpaperSettings message. Also converts values to other types if specified. + * @param message WallpaperSettings + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.WallpaperSettings, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this WallpaperSettings to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a GroupParticipant. */ + interface IGroupParticipant { + + /** GroupParticipant userJid */ + userJid: string; + + /** GroupParticipant rank */ + rank?: (proto.GroupParticipant.GroupParticipantRank|null); + } + + /** Represents a GroupParticipant. */ + class GroupParticipant implements IGroupParticipant { + + /** + * Constructs a new GroupParticipant. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IGroupParticipant); + + /** GroupParticipant userJid. */ + public userJid: string; + + /** GroupParticipant rank. */ + public rank: proto.GroupParticipant.GroupParticipantRank; + + /** + * Creates a new GroupParticipant instance using the specified properties. + * @param [properties] Properties to set + * @returns GroupParticipant instance + */ + public static create(properties?: proto.IGroupParticipant): proto.GroupParticipant; + + /** + * Encodes the specified GroupParticipant message. Does not implicitly {@link proto.GroupParticipant.verify|verify} messages. + * @param message GroupParticipant message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IGroupParticipant, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified GroupParticipant message, length delimited. Does not implicitly {@link proto.GroupParticipant.verify|verify} messages. + * @param message GroupParticipant message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IGroupParticipant, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a GroupParticipant message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns GroupParticipant + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.GroupParticipant; + + /** + * Decodes a GroupParticipant message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns GroupParticipant + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.GroupParticipant; + + /** + * Verifies a GroupParticipant message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a GroupParticipant message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns GroupParticipant + */ + public static fromObject(object: { [k: string]: any }): proto.GroupParticipant; + + /** + * Creates a plain object from a GroupParticipant message. Also converts values to other types if specified. + * @param message GroupParticipant + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.GroupParticipant, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this GroupParticipant to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + namespace GroupParticipant { + + /** GroupParticipantRank enum. */ + enum GroupParticipantRank { + REGULAR = 0, + ADMIN = 1, + SUPERADMIN = 2 + } + } + /** Properties of a Pushname. */ interface IPushname { @@ -6581,6 +7356,48 @@ export namespace proto { /** Conversation notSpam */ notSpam?: (boolean|null); + + /** Conversation archived */ + archived?: (boolean|null); + + /** Conversation disappearingMode */ + disappearingMode?: (proto.IDisappearingMode|null); + + /** Conversation unreadMentionCount */ + unreadMentionCount?: (number|null); + + /** Conversation markedAsUnread */ + markedAsUnread?: (boolean|null); + + /** Conversation participant */ + participant?: (proto.IGroupParticipant[]|null); + + /** Conversation tcToken */ + tcToken?: (Uint8Array|null); + + /** Conversation tcTokenTimestamp */ + tcTokenTimestamp?: (number|Long|null); + + /** Conversation contactPrimaryIdentityKey */ + contactPrimaryIdentityKey?: (Uint8Array|null); + + /** Conversation pinned */ + pinned?: (number|null); + + /** Conversation muteEndTime */ + muteEndTime?: (number|Long|null); + + /** Conversation wallpaper */ + wallpaper?: (proto.IWallpaperSettings|null); + + /** Conversation mediaVisibility */ + mediaVisibility?: (proto.MediaVisibility|null); + + /** Conversation tcTokenSenderTimestamp */ + tcTokenSenderTimestamp?: (number|Long|null); + + /** Conversation suspended */ + suspended?: (boolean|null); } /** Represents a Conversation. */ @@ -6637,6 +7454,48 @@ export namespace proto { /** Conversation notSpam. */ public notSpam: boolean; + /** Conversation archived. */ + public archived: boolean; + + /** Conversation disappearingMode. */ + public disappearingMode?: (proto.IDisappearingMode|null); + + /** Conversation unreadMentionCount. */ + public unreadMentionCount: number; + + /** Conversation markedAsUnread. */ + public markedAsUnread: boolean; + + /** Conversation participant. */ + public participant: proto.IGroupParticipant[]; + + /** Conversation tcToken. */ + public tcToken: Uint8Array; + + /** Conversation tcTokenTimestamp. */ + public tcTokenTimestamp: (number|Long); + + /** Conversation contactPrimaryIdentityKey. */ + public contactPrimaryIdentityKey: Uint8Array; + + /** Conversation pinned. */ + public pinned: number; + + /** Conversation muteEndTime. */ + public muteEndTime: (number|Long); + + /** Conversation wallpaper. */ + public wallpaper?: (proto.IWallpaperSettings|null); + + /** Conversation mediaVisibility. */ + public mediaVisibility: proto.MediaVisibility; + + /** Conversation tcTokenSenderTimestamp. */ + public tcTokenSenderTimestamp: (number|Long); + + /** Conversation suspended. */ + public suspended: boolean; + /** * Creates a new Conversation instance using the specified properties. * @param [properties] Properties to set @@ -6737,6 +7596,15 @@ export namespace proto { /** HistorySync pushnames */ pushnames?: (proto.IPushname[]|null); + + /** HistorySync globalSettings */ + globalSettings?: (proto.IGlobalSettings|null); + + /** HistorySync threadIdUserSecret */ + threadIdUserSecret?: (Uint8Array|null); + + /** HistorySync threadDsTimeframeOffset */ + threadDsTimeframeOffset?: (number|null); } /** Represents a HistorySync. */ @@ -6766,6 +7634,15 @@ export namespace proto { /** HistorySync pushnames. */ public pushnames: proto.IPushname[]; + /** HistorySync globalSettings. */ + public globalSettings?: (proto.IGlobalSettings|null); + + /** HistorySync threadIdUserSecret. */ + public threadIdUserSecret: Uint8Array; + + /** HistorySync threadDsTimeframeOffset. */ + public threadDsTimeframeOffset: number; + /** * Creates a new HistorySync instance using the specified properties. * @param [properties] Properties to set @@ -6849,6 +7726,13 @@ export namespace proto { } } + /** MediaVisibility enum. */ + enum MediaVisibility { + DEFAULT = 0, + OFF = 1, + ON = 2 + } + /** Properties of an EphemeralSetting. */ interface IEphemeralSetting { @@ -6945,1248 +7829,6 @@ export namespace proto { public toJSON(): { [k: string]: any }; } - /** Properties of a PaymentBackground. */ - interface IPaymentBackground { - - /** PaymentBackground id */ - id?: (string|null); - - /** PaymentBackground fileLength */ - fileLength?: (string|null); - - /** PaymentBackground width */ - width?: (number|null); - - /** PaymentBackground height */ - height?: (number|null); - - /** PaymentBackground mimetype */ - mimetype?: (string|null); - - /** PaymentBackground placeholderArgb */ - placeholderArgb?: (number|null); - - /** PaymentBackground textArgb */ - textArgb?: (number|null); - - /** PaymentBackground subtextArgb */ - subtextArgb?: (number|null); - } - - /** Represents a PaymentBackground. */ - class PaymentBackground implements IPaymentBackground { - - /** - * Constructs a new PaymentBackground. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IPaymentBackground); - - /** PaymentBackground id. */ - public id: string; - - /** PaymentBackground fileLength. */ - public fileLength: string; - - /** PaymentBackground width. */ - public width: number; - - /** PaymentBackground height. */ - public height: number; - - /** PaymentBackground mimetype. */ - public mimetype: string; - - /** PaymentBackground placeholderArgb. */ - public placeholderArgb: number; - - /** PaymentBackground textArgb. */ - public textArgb: number; - - /** PaymentBackground subtextArgb. */ - public subtextArgb: number; - - /** - * Creates a new PaymentBackground instance using the specified properties. - * @param [properties] Properties to set - * @returns PaymentBackground instance - */ - public static create(properties?: proto.IPaymentBackground): proto.PaymentBackground; - - /** - * Encodes the specified PaymentBackground message. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. - * @param message PaymentBackground message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IPaymentBackground, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified PaymentBackground message, length delimited. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. - * @param message PaymentBackground message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IPaymentBackground, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a PaymentBackground message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns PaymentBackground - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.PaymentBackground; - - /** - * Decodes a PaymentBackground message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns PaymentBackground - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.PaymentBackground; - - /** - * Verifies a PaymentBackground message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a PaymentBackground message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns PaymentBackground - */ - public static fromObject(object: { [k: string]: any }): proto.PaymentBackground; - - /** - * Creates a plain object from a PaymentBackground message. Also converts values to other types if specified. - * @param message PaymentBackground - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.PaymentBackground, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this PaymentBackground to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a Money. */ - interface IMoney { - - /** Money value */ - value?: (number|Long|null); - - /** Money offset */ - offset?: (number|null); - - /** Money currencyCode */ - currencyCode?: (string|null); - } - - /** Represents a Money. */ - class Money implements IMoney { - - /** - * Constructs a new Money. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IMoney); - - /** Money value. */ - public value: (number|Long); - - /** Money offset. */ - public offset: number; - - /** Money currencyCode. */ - public currencyCode: string; - - /** - * Creates a new Money instance using the specified properties. - * @param [properties] Properties to set - * @returns Money instance - */ - public static create(properties?: proto.IMoney): proto.Money; - - /** - * Encodes the specified Money message. Does not implicitly {@link proto.Money.verify|verify} messages. - * @param message Money message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IMoney, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified Money message, length delimited. Does not implicitly {@link proto.Money.verify|verify} messages. - * @param message Money message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IMoney, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a Money message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns Money - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Money; - - /** - * Decodes a Money message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns Money - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Money; - - /** - * Verifies a Money message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a Money message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns Money - */ - public static fromObject(object: { [k: string]: any }): proto.Money; - - /** - * Creates a plain object from a Money message. Also converts values to other types if specified. - * @param message Money - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.Money, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this Money to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a HydratedQuickReplyButton. */ - interface IHydratedQuickReplyButton { - - /** HydratedQuickReplyButton displayText */ - displayText?: (string|null); - - /** HydratedQuickReplyButton id */ - id?: (string|null); - } - - /** Represents a HydratedQuickReplyButton. */ - class HydratedQuickReplyButton implements IHydratedQuickReplyButton { - - /** - * Constructs a new HydratedQuickReplyButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IHydratedQuickReplyButton); - - /** HydratedQuickReplyButton displayText. */ - public displayText: string; - - /** HydratedQuickReplyButton id. */ - public id: string; - - /** - * Creates a new HydratedQuickReplyButton instance using the specified properties. - * @param [properties] Properties to set - * @returns HydratedQuickReplyButton instance - */ - public static create(properties?: proto.IHydratedQuickReplyButton): proto.HydratedQuickReplyButton; - - /** - * Encodes the specified HydratedQuickReplyButton message. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. - * @param message HydratedQuickReplyButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IHydratedQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified HydratedQuickReplyButton message, length delimited. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. - * @param message HydratedQuickReplyButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IHydratedQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a HydratedQuickReplyButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns HydratedQuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedQuickReplyButton; - - /** - * Decodes a HydratedQuickReplyButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns HydratedQuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedQuickReplyButton; - - /** - * Verifies a HydratedQuickReplyButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a HydratedQuickReplyButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns HydratedQuickReplyButton - */ - public static fromObject(object: { [k: string]: any }): proto.HydratedQuickReplyButton; - - /** - * Creates a plain object from a HydratedQuickReplyButton message. Also converts values to other types if specified. - * @param message HydratedQuickReplyButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.HydratedQuickReplyButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this HydratedQuickReplyButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a HydratedURLButton. */ - interface IHydratedURLButton { - - /** HydratedURLButton displayText */ - displayText?: (string|null); - - /** HydratedURLButton url */ - url?: (string|null); - } - - /** Represents a HydratedURLButton. */ - class HydratedURLButton implements IHydratedURLButton { - - /** - * Constructs a new HydratedURLButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IHydratedURLButton); - - /** HydratedURLButton displayText. */ - public displayText: string; - - /** HydratedURLButton url. */ - public url: string; - - /** - * Creates a new HydratedURLButton instance using the specified properties. - * @param [properties] Properties to set - * @returns HydratedURLButton instance - */ - public static create(properties?: proto.IHydratedURLButton): proto.HydratedURLButton; - - /** - * Encodes the specified HydratedURLButton message. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. - * @param message HydratedURLButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IHydratedURLButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified HydratedURLButton message, length delimited. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. - * @param message HydratedURLButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IHydratedURLButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a HydratedURLButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns HydratedURLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedURLButton; - - /** - * Decodes a HydratedURLButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns HydratedURLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedURLButton; - - /** - * Verifies a HydratedURLButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a HydratedURLButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns HydratedURLButton - */ - public static fromObject(object: { [k: string]: any }): proto.HydratedURLButton; - - /** - * Creates a plain object from a HydratedURLButton message. Also converts values to other types if specified. - * @param message HydratedURLButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.HydratedURLButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this HydratedURLButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a HydratedCallButton. */ - interface IHydratedCallButton { - - /** HydratedCallButton displayText */ - displayText?: (string|null); - - /** HydratedCallButton phoneNumber */ - phoneNumber?: (string|null); - } - - /** Represents a HydratedCallButton. */ - class HydratedCallButton implements IHydratedCallButton { - - /** - * Constructs a new HydratedCallButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IHydratedCallButton); - - /** HydratedCallButton displayText. */ - public displayText: string; - - /** HydratedCallButton phoneNumber. */ - public phoneNumber: string; - - /** - * Creates a new HydratedCallButton instance using the specified properties. - * @param [properties] Properties to set - * @returns HydratedCallButton instance - */ - public static create(properties?: proto.IHydratedCallButton): proto.HydratedCallButton; - - /** - * Encodes the specified HydratedCallButton message. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. - * @param message HydratedCallButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IHydratedCallButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified HydratedCallButton message, length delimited. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. - * @param message HydratedCallButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IHydratedCallButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a HydratedCallButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns HydratedCallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedCallButton; - - /** - * Decodes a HydratedCallButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns HydratedCallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedCallButton; - - /** - * Verifies a HydratedCallButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a HydratedCallButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns HydratedCallButton - */ - public static fromObject(object: { [k: string]: any }): proto.HydratedCallButton; - - /** - * Creates a plain object from a HydratedCallButton message. Also converts values to other types if specified. - * @param message HydratedCallButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.HydratedCallButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this HydratedCallButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a HydratedTemplateButton. */ - interface IHydratedTemplateButton { - - /** HydratedTemplateButton index */ - index?: (number|null); - - /** HydratedTemplateButton quickReplyButton */ - quickReplyButton?: (proto.IHydratedQuickReplyButton|null); - - /** HydratedTemplateButton urlButton */ - urlButton?: (proto.IHydratedURLButton|null); - - /** HydratedTemplateButton callButton */ - callButton?: (proto.IHydratedCallButton|null); - } - - /** Represents a HydratedTemplateButton. */ - class HydratedTemplateButton implements IHydratedTemplateButton { - - /** - * Constructs a new HydratedTemplateButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IHydratedTemplateButton); - - /** HydratedTemplateButton index. */ - public index: number; - - /** HydratedTemplateButton quickReplyButton. */ - public quickReplyButton?: (proto.IHydratedQuickReplyButton|null); - - /** HydratedTemplateButton urlButton. */ - public urlButton?: (proto.IHydratedURLButton|null); - - /** HydratedTemplateButton callButton. */ - public callButton?: (proto.IHydratedCallButton|null); - - /** HydratedTemplateButton hydratedButton. */ - public hydratedButton?: ("quickReplyButton"|"urlButton"|"callButton"); - - /** - * Creates a new HydratedTemplateButton instance using the specified properties. - * @param [properties] Properties to set - * @returns HydratedTemplateButton instance - */ - public static create(properties?: proto.IHydratedTemplateButton): proto.HydratedTemplateButton; - - /** - * Encodes the specified HydratedTemplateButton message. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. - * @param message HydratedTemplateButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IHydratedTemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified HydratedTemplateButton message, length delimited. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. - * @param message HydratedTemplateButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IHydratedTemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a HydratedTemplateButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns HydratedTemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedTemplateButton; - - /** - * Decodes a HydratedTemplateButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns HydratedTemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedTemplateButton; - - /** - * Verifies a HydratedTemplateButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a HydratedTemplateButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns HydratedTemplateButton - */ - public static fromObject(object: { [k: string]: any }): proto.HydratedTemplateButton; - - /** - * Creates a plain object from a HydratedTemplateButton message. Also converts values to other types if specified. - * @param message HydratedTemplateButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.HydratedTemplateButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this HydratedTemplateButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a QuickReplyButton. */ - interface IQuickReplyButton { - - /** QuickReplyButton displayText */ - displayText?: (proto.IHighlyStructuredMessage|null); - - /** QuickReplyButton id */ - id?: (string|null); - } - - /** Represents a QuickReplyButton. */ - class QuickReplyButton implements IQuickReplyButton { - - /** - * Constructs a new QuickReplyButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IQuickReplyButton); - - /** QuickReplyButton displayText. */ - public displayText?: (proto.IHighlyStructuredMessage|null); - - /** QuickReplyButton id. */ - public id: string; - - /** - * Creates a new QuickReplyButton instance using the specified properties. - * @param [properties] Properties to set - * @returns QuickReplyButton instance - */ - public static create(properties?: proto.IQuickReplyButton): proto.QuickReplyButton; - - /** - * Encodes the specified QuickReplyButton message. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. - * @param message QuickReplyButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified QuickReplyButton message, length delimited. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. - * @param message QuickReplyButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a QuickReplyButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns QuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.QuickReplyButton; - - /** - * Decodes a QuickReplyButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns QuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.QuickReplyButton; - - /** - * Verifies a QuickReplyButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a QuickReplyButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns QuickReplyButton - */ - public static fromObject(object: { [k: string]: any }): proto.QuickReplyButton; - - /** - * Creates a plain object from a QuickReplyButton message. Also converts values to other types if specified. - * @param message QuickReplyButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.QuickReplyButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this QuickReplyButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a URLButton. */ - interface IURLButton { - - /** URLButton displayText */ - displayText?: (proto.IHighlyStructuredMessage|null); - - /** URLButton url */ - url?: (proto.IHighlyStructuredMessage|null); - } - - /** Represents a URLButton. */ - class URLButton implements IURLButton { - - /** - * Constructs a new URLButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IURLButton); - - /** URLButton displayText. */ - public displayText?: (proto.IHighlyStructuredMessage|null); - - /** URLButton url. */ - public url?: (proto.IHighlyStructuredMessage|null); - - /** - * Creates a new URLButton instance using the specified properties. - * @param [properties] Properties to set - * @returns URLButton instance - */ - public static create(properties?: proto.IURLButton): proto.URLButton; - - /** - * Encodes the specified URLButton message. Does not implicitly {@link proto.URLButton.verify|verify} messages. - * @param message URLButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IURLButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified URLButton message, length delimited. Does not implicitly {@link proto.URLButton.verify|verify} messages. - * @param message URLButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IURLButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a URLButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns URLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.URLButton; - - /** - * Decodes a URLButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns URLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.URLButton; - - /** - * Verifies a URLButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a URLButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns URLButton - */ - public static fromObject(object: { [k: string]: any }): proto.URLButton; - - /** - * Creates a plain object from a URLButton message. Also converts values to other types if specified. - * @param message URLButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.URLButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this URLButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a CallButton. */ - interface ICallButton { - - /** CallButton displayText */ - displayText?: (proto.IHighlyStructuredMessage|null); - - /** CallButton phoneNumber */ - phoneNumber?: (proto.IHighlyStructuredMessage|null); - } - - /** Represents a CallButton. */ - class CallButton implements ICallButton { - - /** - * Constructs a new CallButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.ICallButton); - - /** CallButton displayText. */ - public displayText?: (proto.IHighlyStructuredMessage|null); - - /** CallButton phoneNumber. */ - public phoneNumber?: (proto.IHighlyStructuredMessage|null); - - /** - * Creates a new CallButton instance using the specified properties. - * @param [properties] Properties to set - * @returns CallButton instance - */ - public static create(properties?: proto.ICallButton): proto.CallButton; - - /** - * Encodes the specified CallButton message. Does not implicitly {@link proto.CallButton.verify|verify} messages. - * @param message CallButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.ICallButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified CallButton message, length delimited. Does not implicitly {@link proto.CallButton.verify|verify} messages. - * @param message CallButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.ICallButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a CallButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns CallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.CallButton; - - /** - * Decodes a CallButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns CallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.CallButton; - - /** - * Verifies a CallButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a CallButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns CallButton - */ - public static fromObject(object: { [k: string]: any }): proto.CallButton; - - /** - * Creates a plain object from a CallButton message. Also converts values to other types if specified. - * @param message CallButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.CallButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this CallButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a TemplateButton. */ - interface ITemplateButton { - - /** TemplateButton index */ - index?: (number|null); - - /** TemplateButton quickReplyButton */ - quickReplyButton?: (proto.IQuickReplyButton|null); - - /** TemplateButton urlButton */ - urlButton?: (proto.IURLButton|null); - - /** TemplateButton callButton */ - callButton?: (proto.ICallButton|null); - } - - /** Represents a TemplateButton. */ - class TemplateButton implements ITemplateButton { - - /** - * Constructs a new TemplateButton. - * @param [properties] Properties to set - */ - constructor(properties?: proto.ITemplateButton); - - /** TemplateButton index. */ - public index: number; - - /** TemplateButton quickReplyButton. */ - public quickReplyButton?: (proto.IQuickReplyButton|null); - - /** TemplateButton urlButton. */ - public urlButton?: (proto.IURLButton|null); - - /** TemplateButton callButton. */ - public callButton?: (proto.ICallButton|null); - - /** TemplateButton button. */ - public button?: ("quickReplyButton"|"urlButton"|"callButton"); - - /** - * Creates a new TemplateButton instance using the specified properties. - * @param [properties] Properties to set - * @returns TemplateButton instance - */ - public static create(properties?: proto.ITemplateButton): proto.TemplateButton; - - /** - * Encodes the specified TemplateButton message. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. - * @param message TemplateButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.ITemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified TemplateButton message, length delimited. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. - * @param message TemplateButton message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.ITemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a TemplateButton message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns TemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.TemplateButton; - - /** - * Decodes a TemplateButton message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns TemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.TemplateButton; - - /** - * Verifies a TemplateButton message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a TemplateButton message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns TemplateButton - */ - public static fromObject(object: { [k: string]: any }): proto.TemplateButton; - - /** - * Creates a plain object from a TemplateButton message. Also converts values to other types if specified. - * @param message TemplateButton - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.TemplateButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this TemplateButton to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a Location. */ - interface ILocation { - - /** Location degreesLatitude */ - degreesLatitude?: (number|null); - - /** Location degreesLongitude */ - degreesLongitude?: (number|null); - - /** Location name */ - name?: (string|null); - } - - /** Represents a Location. */ - class Location implements ILocation { - - /** - * Constructs a new Location. - * @param [properties] Properties to set - */ - constructor(properties?: proto.ILocation); - - /** Location degreesLatitude. */ - public degreesLatitude: number; - - /** Location degreesLongitude. */ - public degreesLongitude: number; - - /** Location name. */ - public name: string; - - /** - * Creates a new Location instance using the specified properties. - * @param [properties] Properties to set - * @returns Location instance - */ - public static create(properties?: proto.ILocation): proto.Location; - - /** - * Encodes the specified Location message. Does not implicitly {@link proto.Location.verify|verify} messages. - * @param message Location message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.ILocation, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified Location message, length delimited. Does not implicitly {@link proto.Location.verify|verify} messages. - * @param message Location message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.ILocation, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a Location message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns Location - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Location; - - /** - * Decodes a Location message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns Location - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Location; - - /** - * Verifies a Location message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a Location message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns Location - */ - public static fromObject(object: { [k: string]: any }): proto.Location; - - /** - * Creates a plain object from a Location message. Also converts values to other types if specified. - * @param message Location - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.Location, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this Location to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - - /** Properties of a Point. */ - interface IPoint { - - /** Point xDeprecated */ - xDeprecated?: (number|null); - - /** Point yDeprecated */ - yDeprecated?: (number|null); - - /** Point x */ - x?: (number|null); - - /** Point y */ - y?: (number|null); - } - - /** Represents a Point. */ - class Point implements IPoint { - - /** - * Constructs a new Point. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IPoint); - - /** Point xDeprecated. */ - public xDeprecated: number; - - /** Point yDeprecated. */ - public yDeprecated: number; - - /** Point x. */ - public x: number; - - /** Point y. */ - public y: number; - - /** - * Creates a new Point instance using the specified properties. - * @param [properties] Properties to set - * @returns Point instance - */ - public static create(properties?: proto.IPoint): proto.Point; - - /** - * Encodes the specified Point message. Does not implicitly {@link proto.Point.verify|verify} messages. - * @param message Point message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IPoint, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified Point message, length delimited. Does not implicitly {@link proto.Point.verify|verify} messages. - * @param message Point message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IPoint, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a Point message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns Point - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Point; - - /** - * Decodes a Point message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns Point - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Point; - - /** - * Verifies a Point message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a Point message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns Point - */ - public static fromObject(object: { [k: string]: any }): proto.Point; - - /** - * Creates a plain object from a Point message. Also converts values to other types if specified. - * @param message Point - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.Point, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this Point to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - /** Properties of an InteractiveAnnotation. */ interface IInteractiveAnnotation { @@ -8827,6 +8469,18 @@ export namespace proto { /** ContextInfo entryPointConversionDelaySeconds */ entryPointConversionDelaySeconds?: (number|null); + + /** ContextInfo disappearingMode */ + disappearingMode?: (proto.IDisappearingMode|null); + + /** ContextInfo actionLink */ + actionLink?: (proto.IActionLink|null); + + /** ContextInfo groupSubject */ + groupSubject?: (string|null); + + /** ContextInfo parentGroupJid */ + parentGroupJid?: (string|null); } /** Represents a ContextInfo. */ @@ -8895,6 +8549,18 @@ export namespace proto { /** ContextInfo entryPointConversionDelaySeconds. */ public entryPointConversionDelaySeconds: number; + /** ContextInfo disappearingMode. */ + public disappearingMode?: (proto.IDisappearingMode|null); + + /** ContextInfo actionLink. */ + public actionLink?: (proto.IActionLink|null); + + /** ContextInfo groupSubject. */ + public groupSubject: string; + + /** ContextInfo parentGroupJid. */ + public parentGroupJid: string; + /** * Creates a new ContextInfo instance using the specified properties. * @param [properties] Properties to set @@ -9139,6 +8805,9 @@ export namespace proto { /** ImageMessage thumbnailEncSha256 */ thumbnailEncSha256?: (Uint8Array|null); + + /** ImageMessage staticUrl */ + staticUrl?: (string|null); } /** Represents an ImageMessage. */ @@ -9225,6 +8894,9 @@ export namespace proto { /** ImageMessage thumbnailEncSha256. */ public thumbnailEncSha256: Uint8Array; + /** ImageMessage staticUrl. */ + public staticUrl: string; + /** * Creates a new ImageMessage instance using the specified properties. * @param [properties] Properties to set @@ -9766,6 +9438,9 @@ export namespace proto { /** ExtendedTextMessage thumbnailWidth */ thumbnailWidth?: (number|null); + + /** ExtendedTextMessage inviteLinkGroupType */ + inviteLinkGroupType?: (proto.ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType|null); } /** Represents an ExtendedTextMessage. */ @@ -9834,6 +9509,9 @@ export namespace proto { /** ExtendedTextMessage thumbnailWidth. */ public thumbnailWidth: number; + /** ExtendedTextMessage inviteLinkGroupType. */ + public inviteLinkGroupType: proto.ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType; + /** * Creates a new ExtendedTextMessage instance using the specified properties. * @param [properties] Properties to set @@ -9922,6 +9600,12 @@ export namespace proto { NONE = 0, VIDEO = 1 } + + /** ExtendedTextMessageInviteLinkGroupType enum. */ + enum ExtendedTextMessageInviteLinkGroupType { + DEFAULT = 0, + PARENT = 1 + } } /** Properties of a DocumentMessage. */ @@ -10160,6 +9844,9 @@ export namespace proto { /** AudioMessage streamingSidecar */ streamingSidecar?: (Uint8Array|null); + + /** AudioMessage waveform */ + waveform?: (Uint8Array|null); } /** Represents an AudioMessage. */ @@ -10207,6 +9894,9 @@ export namespace proto { /** AudioMessage streamingSidecar. */ public streamingSidecar: Uint8Array; + /** AudioMessage waveform. */ + public waveform: Uint8Array; + /** * Creates a new AudioMessage instance using the specified properties. * @param [properties] Properties to set @@ -10346,6 +10036,9 @@ export namespace proto { /** VideoMessage thumbnailEncSha256 */ thumbnailEncSha256?: (Uint8Array|null); + + /** VideoMessage staticUrl */ + staticUrl?: (string|null); } /** Represents a VideoMessage. */ @@ -10423,6 +10116,9 @@ export namespace proto { /** VideoMessage thumbnailEncSha256. */ public thumbnailEncSha256: Uint8Array; + /** VideoMessage staticUrl. */ + public staticUrl: string; + /** * Creates a new VideoMessage instance using the specified properties. * @param [properties] Properties to set @@ -10737,6 +10433,9 @@ export namespace proto { /** ProtocolMessage appStateFatalExceptionNotification */ appStateFatalExceptionNotification?: (proto.IAppStateFatalExceptionNotification|null); + + /** ProtocolMessage disappearingMode */ + disappearingMode?: (proto.IDisappearingMode|null); } /** Represents a ProtocolMessage. */ @@ -10775,6 +10474,9 @@ export namespace proto { /** ProtocolMessage appStateFatalExceptionNotification. */ public appStateFatalExceptionNotification?: (proto.IAppStateFatalExceptionNotification|null); + /** ProtocolMessage disappearingMode. */ + public disappearingMode?: (proto.IDisappearingMode|null); + /** * Creates a new ProtocolMessage instance using the specified properties. * @param [properties] Properties to set @@ -14130,6 +13832,12 @@ export namespace proto { /** ProductMessage catalog */ catalog?: (proto.ICatalogSnapshot|null); + /** ProductMessage body */ + body?: (string|null); + + /** ProductMessage footer */ + footer?: (string|null); + /** ProductMessage contextInfo */ contextInfo?: (proto.IContextInfo|null); } @@ -14152,6 +13860,12 @@ export namespace proto { /** ProductMessage catalog. */ public catalog?: (proto.ICatalogSnapshot|null); + /** ProductMessage body. */ + public body: string; + + /** ProductMessage footer. */ + public footer: string; + /** ProductMessage contextInfo. */ public contextInfo?: (proto.IContextInfo|null); @@ -15332,6 +15046,857 @@ export namespace proto { } } + /** Properties of a Header. */ + interface IHeader { + + /** Header title */ + title?: (string|null); + + /** Header subtitle */ + subtitle?: (string|null); + + /** Header hasMediaAttachment */ + hasMediaAttachment?: (boolean|null); + + /** Header documentMessage */ + documentMessage?: (proto.IDocumentMessage|null); + + /** Header imageMessage */ + imageMessage?: (proto.IImageMessage|null); + + /** Header jpegThumbnail */ + jpegThumbnail?: (Uint8Array|null); + + /** Header videoMessage */ + videoMessage?: (proto.IVideoMessage|null); + } + + /** Represents a Header. */ + class Header implements IHeader { + + /** + * Constructs a new Header. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IHeader); + + /** Header title. */ + public title: string; + + /** Header subtitle. */ + public subtitle: string; + + /** Header hasMediaAttachment. */ + public hasMediaAttachment: boolean; + + /** Header documentMessage. */ + public documentMessage?: (proto.IDocumentMessage|null); + + /** Header imageMessage. */ + public imageMessage?: (proto.IImageMessage|null); + + /** Header jpegThumbnail. */ + public jpegThumbnail?: (Uint8Array|null); + + /** Header videoMessage. */ + public videoMessage?: (proto.IVideoMessage|null); + + /** Header media. */ + public media?: ("documentMessage"|"imageMessage"|"jpegThumbnail"|"videoMessage"); + + /** + * Creates a new Header instance using the specified properties. + * @param [properties] Properties to set + * @returns Header instance + */ + public static create(properties?: proto.IHeader): proto.Header; + + /** + * Encodes the specified Header message. Does not implicitly {@link proto.Header.verify|verify} messages. + * @param message Header message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IHeader, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Header message, length delimited. Does not implicitly {@link proto.Header.verify|verify} messages. + * @param message Header message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IHeader, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Header message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Header + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Header; + + /** + * Decodes a Header message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Header + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Header; + + /** + * Verifies a Header message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Header message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Header + */ + public static fromObject(object: { [k: string]: any }): proto.Header; + + /** + * Creates a plain object from a Header message. Also converts values to other types if specified. + * @param message Header + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Header, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Header to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a Body. */ + interface IBody { + + /** Body text */ + text?: (string|null); + } + + /** Represents a Body. */ + class Body implements IBody { + + /** + * Constructs a new Body. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IBody); + + /** Body text. */ + public text: string; + + /** + * Creates a new Body instance using the specified properties. + * @param [properties] Properties to set + * @returns Body instance + */ + public static create(properties?: proto.IBody): proto.Body; + + /** + * Encodes the specified Body message. Does not implicitly {@link proto.Body.verify|verify} messages. + * @param message Body message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IBody, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Body message, length delimited. Does not implicitly {@link proto.Body.verify|verify} messages. + * @param message Body message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IBody, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Body message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Body + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Body; + + /** + * Decodes a Body message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Body + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Body; + + /** + * Verifies a Body message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Body message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Body + */ + public static fromObject(object: { [k: string]: any }): proto.Body; + + /** + * Creates a plain object from a Body message. Also converts values to other types if specified. + * @param message Body + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Body, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Body to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a Footer. */ + interface IFooter { + + /** Footer text */ + text?: (string|null); + } + + /** Represents a Footer. */ + class Footer implements IFooter { + + /** + * Constructs a new Footer. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IFooter); + + /** Footer text. */ + public text: string; + + /** + * Creates a new Footer instance using the specified properties. + * @param [properties] Properties to set + * @returns Footer instance + */ + public static create(properties?: proto.IFooter): proto.Footer; + + /** + * Encodes the specified Footer message. Does not implicitly {@link proto.Footer.verify|verify} messages. + * @param message Footer message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IFooter, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Footer message, length delimited. Does not implicitly {@link proto.Footer.verify|verify} messages. + * @param message Footer message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IFooter, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Footer message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Footer + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Footer; + + /** + * Decodes a Footer message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Footer + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Footer; + + /** + * Verifies a Footer message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Footer message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Footer + */ + public static fromObject(object: { [k: string]: any }): proto.Footer; + + /** + * Creates a plain object from a Footer message. Also converts values to other types if specified. + * @param message Footer + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Footer, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Footer to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a ShopMessage. */ + interface IShopMessage { + + /** ShopMessage id */ + id?: (string|null); + + /** ShopMessage surface */ + surface?: (proto.ShopMessage.ShopMessageSurface|null); + + /** ShopMessage messageVersion */ + messageVersion?: (number|null); + } + + /** Represents a ShopMessage. */ + class ShopMessage implements IShopMessage { + + /** + * Constructs a new ShopMessage. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IShopMessage); + + /** ShopMessage id. */ + public id: string; + + /** ShopMessage surface. */ + public surface: proto.ShopMessage.ShopMessageSurface; + + /** ShopMessage messageVersion. */ + public messageVersion: number; + + /** + * Creates a new ShopMessage instance using the specified properties. + * @param [properties] Properties to set + * @returns ShopMessage instance + */ + public static create(properties?: proto.IShopMessage): proto.ShopMessage; + + /** + * Encodes the specified ShopMessage message. Does not implicitly {@link proto.ShopMessage.verify|verify} messages. + * @param message ShopMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IShopMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ShopMessage message, length delimited. Does not implicitly {@link proto.ShopMessage.verify|verify} messages. + * @param message ShopMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IShopMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a ShopMessage message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ShopMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.ShopMessage; + + /** + * Decodes a ShopMessage message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ShopMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.ShopMessage; + + /** + * Verifies a ShopMessage message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a ShopMessage message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ShopMessage + */ + public static fromObject(object: { [k: string]: any }): proto.ShopMessage; + + /** + * Creates a plain object from a ShopMessage message. Also converts values to other types if specified. + * @param message ShopMessage + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.ShopMessage, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ShopMessage to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + namespace ShopMessage { + + /** ShopMessageSurface enum. */ + enum ShopMessageSurface { + UNKNOWN_SURFACE = 0, + FB = 1, + IG = 2, + WA = 3 + } + } + + /** Properties of a CollectionMessage. */ + interface ICollectionMessage { + + /** CollectionMessage bizJid */ + bizJid?: (string|null); + + /** CollectionMessage id */ + id?: (string|null); + + /** CollectionMessage messageVersion */ + messageVersion?: (number|null); + } + + /** Represents a CollectionMessage. */ + class CollectionMessage implements ICollectionMessage { + + /** + * Constructs a new CollectionMessage. + * @param [properties] Properties to set + */ + constructor(properties?: proto.ICollectionMessage); + + /** CollectionMessage bizJid. */ + public bizJid: string; + + /** CollectionMessage id. */ + public id: string; + + /** CollectionMessage messageVersion. */ + public messageVersion: number; + + /** + * Creates a new CollectionMessage instance using the specified properties. + * @param [properties] Properties to set + * @returns CollectionMessage instance + */ + public static create(properties?: proto.ICollectionMessage): proto.CollectionMessage; + + /** + * Encodes the specified CollectionMessage message. Does not implicitly {@link proto.CollectionMessage.verify|verify} messages. + * @param message CollectionMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.ICollectionMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified CollectionMessage message, length delimited. Does not implicitly {@link proto.CollectionMessage.verify|verify} messages. + * @param message CollectionMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.ICollectionMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a CollectionMessage message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns CollectionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.CollectionMessage; + + /** + * Decodes a CollectionMessage message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns CollectionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.CollectionMessage; + + /** + * Verifies a CollectionMessage message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a CollectionMessage message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns CollectionMessage + */ + public static fromObject(object: { [k: string]: any }): proto.CollectionMessage; + + /** + * Creates a plain object from a CollectionMessage message. Also converts values to other types if specified. + * @param message CollectionMessage + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.CollectionMessage, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this CollectionMessage to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a NativeFlowButton. */ + interface INativeFlowButton { + + /** NativeFlowButton name */ + name?: (string|null); + + /** NativeFlowButton buttonParamsJson */ + buttonParamsJson?: (string|null); + } + + /** Represents a NativeFlowButton. */ + class NativeFlowButton implements INativeFlowButton { + + /** + * Constructs a new NativeFlowButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.INativeFlowButton); + + /** NativeFlowButton name. */ + public name: string; + + /** NativeFlowButton buttonParamsJson. */ + public buttonParamsJson: string; + + /** + * Creates a new NativeFlowButton instance using the specified properties. + * @param [properties] Properties to set + * @returns NativeFlowButton instance + */ + public static create(properties?: proto.INativeFlowButton): proto.NativeFlowButton; + + /** + * Encodes the specified NativeFlowButton message. Does not implicitly {@link proto.NativeFlowButton.verify|verify} messages. + * @param message NativeFlowButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.INativeFlowButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified NativeFlowButton message, length delimited. Does not implicitly {@link proto.NativeFlowButton.verify|verify} messages. + * @param message NativeFlowButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.INativeFlowButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a NativeFlowButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns NativeFlowButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.NativeFlowButton; + + /** + * Decodes a NativeFlowButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns NativeFlowButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.NativeFlowButton; + + /** + * Verifies a NativeFlowButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a NativeFlowButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns NativeFlowButton + */ + public static fromObject(object: { [k: string]: any }): proto.NativeFlowButton; + + /** + * Creates a plain object from a NativeFlowButton message. Also converts values to other types if specified. + * @param message NativeFlowButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.NativeFlowButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this NativeFlowButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a NativeFlowMessage. */ + interface INativeFlowMessage { + + /** NativeFlowMessage buttons */ + buttons?: (proto.INativeFlowButton[]|null); + + /** NativeFlowMessage messageParamsJson */ + messageParamsJson?: (string|null); + + /** NativeFlowMessage messageVersion */ + messageVersion?: (number|null); + } + + /** Represents a NativeFlowMessage. */ + class NativeFlowMessage implements INativeFlowMessage { + + /** + * Constructs a new NativeFlowMessage. + * @param [properties] Properties to set + */ + constructor(properties?: proto.INativeFlowMessage); + + /** NativeFlowMessage buttons. */ + public buttons: proto.INativeFlowButton[]; + + /** NativeFlowMessage messageParamsJson. */ + public messageParamsJson: string; + + /** NativeFlowMessage messageVersion. */ + public messageVersion: number; + + /** + * Creates a new NativeFlowMessage instance using the specified properties. + * @param [properties] Properties to set + * @returns NativeFlowMessage instance + */ + public static create(properties?: proto.INativeFlowMessage): proto.NativeFlowMessage; + + /** + * Encodes the specified NativeFlowMessage message. Does not implicitly {@link proto.NativeFlowMessage.verify|verify} messages. + * @param message NativeFlowMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.INativeFlowMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified NativeFlowMessage message, length delimited. Does not implicitly {@link proto.NativeFlowMessage.verify|verify} messages. + * @param message NativeFlowMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.INativeFlowMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a NativeFlowMessage message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns NativeFlowMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.NativeFlowMessage; + + /** + * Decodes a NativeFlowMessage message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns NativeFlowMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.NativeFlowMessage; + + /** + * Verifies a NativeFlowMessage message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a NativeFlowMessage message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns NativeFlowMessage + */ + public static fromObject(object: { [k: string]: any }): proto.NativeFlowMessage; + + /** + * Creates a plain object from a NativeFlowMessage message. Also converts values to other types if specified. + * @param message NativeFlowMessage + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.NativeFlowMessage, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this NativeFlowMessage to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of an InteractiveMessage. */ + interface IInteractiveMessage { + + /** InteractiveMessage header */ + header?: (proto.IHeader|null); + + /** InteractiveMessage body */ + body?: (proto.IBody|null); + + /** InteractiveMessage footer */ + footer?: (proto.IFooter|null); + + /** InteractiveMessage contextInfo */ + contextInfo?: (proto.IContextInfo|null); + + /** InteractiveMessage shopStorefrontMessage */ + shopStorefrontMessage?: (proto.IShopMessage|null); + + /** InteractiveMessage collectionMessage */ + collectionMessage?: (proto.ICollectionMessage|null); + + /** InteractiveMessage nativeFlowMessage */ + nativeFlowMessage?: (proto.INativeFlowMessage|null); + } + + /** Represents an InteractiveMessage. */ + class InteractiveMessage implements IInteractiveMessage { + + /** + * Constructs a new InteractiveMessage. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IInteractiveMessage); + + /** InteractiveMessage header. */ + public header?: (proto.IHeader|null); + + /** InteractiveMessage body. */ + public body?: (proto.IBody|null); + + /** InteractiveMessage footer. */ + public footer?: (proto.IFooter|null); + + /** InteractiveMessage contextInfo. */ + public contextInfo?: (proto.IContextInfo|null); + + /** InteractiveMessage shopStorefrontMessage. */ + public shopStorefrontMessage?: (proto.IShopMessage|null); + + /** InteractiveMessage collectionMessage. */ + public collectionMessage?: (proto.ICollectionMessage|null); + + /** InteractiveMessage nativeFlowMessage. */ + public nativeFlowMessage?: (proto.INativeFlowMessage|null); + + /** InteractiveMessage interactiveMessage. */ + public interactiveMessage?: ("shopStorefrontMessage"|"collectionMessage"|"nativeFlowMessage"); + + /** + * Creates a new InteractiveMessage instance using the specified properties. + * @param [properties] Properties to set + * @returns InteractiveMessage instance + */ + public static create(properties?: proto.IInteractiveMessage): proto.InteractiveMessage; + + /** + * Encodes the specified InteractiveMessage message. Does not implicitly {@link proto.InteractiveMessage.verify|verify} messages. + * @param message InteractiveMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IInteractiveMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified InteractiveMessage message, length delimited. Does not implicitly {@link proto.InteractiveMessage.verify|verify} messages. + * @param message InteractiveMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IInteractiveMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes an InteractiveMessage message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns InteractiveMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.InteractiveMessage; + + /** + * Decodes an InteractiveMessage message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns InteractiveMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.InteractiveMessage; + + /** + * Verifies an InteractiveMessage message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates an InteractiveMessage message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns InteractiveMessage + */ + public static fromObject(object: { [k: string]: any }): proto.InteractiveMessage; + + /** + * Creates a plain object from an InteractiveMessage message. Also converts values to other types if specified. + * @param message InteractiveMessage + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.InteractiveMessage, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this InteractiveMessage to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of a GroupInviteMessage. */ interface IGroupInviteMessage { @@ -15355,6 +15920,9 @@ export namespace proto { /** GroupInviteMessage contextInfo */ contextInfo?: (proto.IContextInfo|null); + + /** GroupInviteMessage groupType */ + groupType?: (proto.GroupInviteMessage.GroupInviteMessageGroupType|null); } /** Represents a GroupInviteMessage. */ @@ -15387,6 +15955,9 @@ export namespace proto { /** GroupInviteMessage contextInfo. */ public contextInfo?: (proto.IContextInfo|null); + /** GroupInviteMessage groupType. */ + public groupType: proto.GroupInviteMessage.GroupInviteMessageGroupType; + /** * Creates a new GroupInviteMessage instance using the specified properties. * @param [properties] Properties to set @@ -15458,6 +16029,15 @@ export namespace proto { public toJSON(): { [k: string]: any }; } + namespace GroupInviteMessage { + + /** GroupInviteMessageGroupType enum. */ + enum GroupInviteMessageGroupType { + DEFAULT = 0, + PARENT = 1 + } + } + /** Properties of a DeviceSentMessage. */ interface IDeviceSentMessage { @@ -16235,6 +16815,216 @@ export namespace proto { } } + /** Properties of a ReactionMessage. */ + interface IReactionMessage { + + /** ReactionMessage key */ + key?: (proto.IMessageKey|null); + + /** ReactionMessage text */ + text?: (string|null); + + /** ReactionMessage groupingKey */ + groupingKey?: (string|null); + + /** ReactionMessage senderTimestampMs */ + senderTimestampMs?: (number|Long|null); + } + + /** Represents a ReactionMessage. */ + class ReactionMessage implements IReactionMessage { + + /** + * Constructs a new ReactionMessage. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IReactionMessage); + + /** ReactionMessage key. */ + public key?: (proto.IMessageKey|null); + + /** ReactionMessage text. */ + public text: string; + + /** ReactionMessage groupingKey. */ + public groupingKey: string; + + /** ReactionMessage senderTimestampMs. */ + public senderTimestampMs: (number|Long); + + /** + * Creates a new ReactionMessage instance using the specified properties. + * @param [properties] Properties to set + * @returns ReactionMessage instance + */ + public static create(properties?: proto.IReactionMessage): proto.ReactionMessage; + + /** + * Encodes the specified ReactionMessage message. Does not implicitly {@link proto.ReactionMessage.verify|verify} messages. + * @param message ReactionMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IReactionMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ReactionMessage message, length delimited. Does not implicitly {@link proto.ReactionMessage.verify|verify} messages. + * @param message ReactionMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IReactionMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a ReactionMessage message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ReactionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.ReactionMessage; + + /** + * Decodes a ReactionMessage message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ReactionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.ReactionMessage; + + /** + * Verifies a ReactionMessage message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a ReactionMessage message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ReactionMessage + */ + public static fromObject(object: { [k: string]: any }): proto.ReactionMessage; + + /** + * Creates a plain object from a ReactionMessage message. Also converts values to other types if specified. + * @param message ReactionMessage + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.ReactionMessage, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ReactionMessage to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a StickerSyncRMRMessage. */ + interface IStickerSyncRMRMessage { + + /** StickerSyncRMRMessage filehash */ + filehash?: (string[]|null); + + /** StickerSyncRMRMessage rmrSource */ + rmrSource?: (string|null); + + /** StickerSyncRMRMessage requestTimestamp */ + requestTimestamp?: (number|Long|null); + } + + /** Represents a StickerSyncRMRMessage. */ + class StickerSyncRMRMessage implements IStickerSyncRMRMessage { + + /** + * Constructs a new StickerSyncRMRMessage. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IStickerSyncRMRMessage); + + /** StickerSyncRMRMessage filehash. */ + public filehash: string[]; + + /** StickerSyncRMRMessage rmrSource. */ + public rmrSource: string; + + /** StickerSyncRMRMessage requestTimestamp. */ + public requestTimestamp: (number|Long); + + /** + * Creates a new StickerSyncRMRMessage instance using the specified properties. + * @param [properties] Properties to set + * @returns StickerSyncRMRMessage instance + */ + public static create(properties?: proto.IStickerSyncRMRMessage): proto.StickerSyncRMRMessage; + + /** + * Encodes the specified StickerSyncRMRMessage message. Does not implicitly {@link proto.StickerSyncRMRMessage.verify|verify} messages. + * @param message StickerSyncRMRMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IStickerSyncRMRMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified StickerSyncRMRMessage message, length delimited. Does not implicitly {@link proto.StickerSyncRMRMessage.verify|verify} messages. + * @param message StickerSyncRMRMessage message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IStickerSyncRMRMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a StickerSyncRMRMessage message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns StickerSyncRMRMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.StickerSyncRMRMessage; + + /** + * Decodes a StickerSyncRMRMessage message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns StickerSyncRMRMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.StickerSyncRMRMessage; + + /** + * Verifies a StickerSyncRMRMessage message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a StickerSyncRMRMessage message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns StickerSyncRMRMessage + */ + public static fromObject(object: { [k: string]: any }): proto.StickerSyncRMRMessage; + + /** + * Creates a plain object from a StickerSyncRMRMessage message. Also converts values to other types if specified. + * @param message StickerSyncRMRMessage + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.StickerSyncRMRMessage, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this StickerSyncRMRMessage to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of a Message. */ interface IMessage { @@ -16345,6 +17135,15 @@ export namespace proto { /** Message paymentInviteMessage */ paymentInviteMessage?: (proto.IPaymentInviteMessage|null); + + /** Message interactiveMessage */ + interactiveMessage?: (proto.IInteractiveMessage|null); + + /** Message reactionMessage */ + reactionMessage?: (proto.IReactionMessage|null); + + /** Message stickerSyncRmrMessage */ + stickerSyncRmrMessage?: (proto.IStickerSyncRMRMessage|null); } /** Represents a Message. */ @@ -16464,6 +17263,15 @@ export namespace proto { /** Message paymentInviteMessage. */ public paymentInviteMessage?: (proto.IPaymentInviteMessage|null); + /** Message interactiveMessage. */ + public interactiveMessage?: (proto.IInteractiveMessage|null); + + /** Message reactionMessage. */ + public reactionMessage?: (proto.IReactionMessage|null); + + /** Message stickerSyncRmrMessage. */ + public stickerSyncRmrMessage?: (proto.IStickerSyncRMRMessage|null); + /** * Creates a new Message instance using the specified properties. * @param [properties] Properties to set @@ -16535,6 +17343,1579 @@ export namespace proto { public toJSON(): { [k: string]: any }; } + /** Properties of an ActionLink. */ + interface IActionLink { + + /** ActionLink url */ + url?: (string|null); + + /** ActionLink buttonTitle */ + buttonTitle?: (string|null); + } + + /** Represents an ActionLink. */ + class ActionLink implements IActionLink { + + /** + * Constructs a new ActionLink. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IActionLink); + + /** ActionLink url. */ + public url: string; + + /** ActionLink buttonTitle. */ + public buttonTitle: string; + + /** + * Creates a new ActionLink instance using the specified properties. + * @param [properties] Properties to set + * @returns ActionLink instance + */ + public static create(properties?: proto.IActionLink): proto.ActionLink; + + /** + * Encodes the specified ActionLink message. Does not implicitly {@link proto.ActionLink.verify|verify} messages. + * @param message ActionLink message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IActionLink, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ActionLink message, length delimited. Does not implicitly {@link proto.ActionLink.verify|verify} messages. + * @param message ActionLink message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IActionLink, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes an ActionLink message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ActionLink + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.ActionLink; + + /** + * Decodes an ActionLink message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ActionLink + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.ActionLink; + + /** + * Verifies an ActionLink message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates an ActionLink message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ActionLink + */ + public static fromObject(object: { [k: string]: any }): proto.ActionLink; + + /** + * Creates a plain object from an ActionLink message. Also converts values to other types if specified. + * @param message ActionLink + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.ActionLink, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ActionLink to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a DisappearingMode. */ + interface IDisappearingMode { + + /** DisappearingMode initiator */ + initiator?: (proto.DisappearingMode.DisappearingModeInitiator|null); + } + + /** Represents a DisappearingMode. */ + class DisappearingMode implements IDisappearingMode { + + /** + * Constructs a new DisappearingMode. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IDisappearingMode); + + /** DisappearingMode initiator. */ + public initiator: proto.DisappearingMode.DisappearingModeInitiator; + + /** + * Creates a new DisappearingMode instance using the specified properties. + * @param [properties] Properties to set + * @returns DisappearingMode instance + */ + public static create(properties?: proto.IDisappearingMode): proto.DisappearingMode; + + /** + * Encodes the specified DisappearingMode message. Does not implicitly {@link proto.DisappearingMode.verify|verify} messages. + * @param message DisappearingMode message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IDisappearingMode, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified DisappearingMode message, length delimited. Does not implicitly {@link proto.DisappearingMode.verify|verify} messages. + * @param message DisappearingMode message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IDisappearingMode, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a DisappearingMode message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns DisappearingMode + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.DisappearingMode; + + /** + * Decodes a DisappearingMode message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns DisappearingMode + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.DisappearingMode; + + /** + * Verifies a DisappearingMode message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a DisappearingMode message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns DisappearingMode + */ + public static fromObject(object: { [k: string]: any }): proto.DisappearingMode; + + /** + * Creates a plain object from a DisappearingMode message. Also converts values to other types if specified. + * @param message DisappearingMode + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.DisappearingMode, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this DisappearingMode to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + namespace DisappearingMode { + + /** DisappearingModeInitiator enum. */ + enum DisappearingModeInitiator { + CHANGED_IN_CHAT = 0, + INITIATED_BY_ME = 1, + INITIATED_BY_OTHER = 2 + } + } + + /** Properties of a MediaData. */ + interface IMediaData { + + /** MediaData mediaKey */ + mediaKey?: (Uint8Array|null); + + /** MediaData mediaKeyTimestamp */ + mediaKeyTimestamp?: (number|Long|null); + + /** MediaData fileSha256 */ + fileSha256?: (Uint8Array|null); + + /** MediaData fileEncSha256 */ + fileEncSha256?: (Uint8Array|null); + + /** MediaData directPath */ + directPath?: (string|null); + } + + /** Represents a MediaData. */ + class MediaData implements IMediaData { + + /** + * Constructs a new MediaData. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IMediaData); + + /** MediaData mediaKey. */ + public mediaKey: Uint8Array; + + /** MediaData mediaKeyTimestamp. */ + public mediaKeyTimestamp: (number|Long); + + /** MediaData fileSha256. */ + public fileSha256: Uint8Array; + + /** MediaData fileEncSha256. */ + public fileEncSha256: Uint8Array; + + /** MediaData directPath. */ + public directPath: string; + + /** + * Creates a new MediaData instance using the specified properties. + * @param [properties] Properties to set + * @returns MediaData instance + */ + public static create(properties?: proto.IMediaData): proto.MediaData; + + /** + * Encodes the specified MediaData message. Does not implicitly {@link proto.MediaData.verify|verify} messages. + * @param message MediaData message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IMediaData, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified MediaData message, length delimited. Does not implicitly {@link proto.MediaData.verify|verify} messages. + * @param message MediaData message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IMediaData, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a MediaData message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns MediaData + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.MediaData; + + /** + * Decodes a MediaData message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns MediaData + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.MediaData; + + /** + * Verifies a MediaData message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a MediaData message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns MediaData + */ + public static fromObject(object: { [k: string]: any }): proto.MediaData; + + /** + * Creates a plain object from a MediaData message. Also converts values to other types if specified. + * @param message MediaData + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.MediaData, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this MediaData to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a PaymentBackground. */ + interface IPaymentBackground { + + /** PaymentBackground id */ + id?: (string|null); + + /** PaymentBackground fileLength */ + fileLength?: (number|Long|null); + + /** PaymentBackground width */ + width?: (number|null); + + /** PaymentBackground height */ + height?: (number|null); + + /** PaymentBackground mimetype */ + mimetype?: (string|null); + + /** PaymentBackground placeholderArgb */ + placeholderArgb?: (number|null); + + /** PaymentBackground textArgb */ + textArgb?: (number|null); + + /** PaymentBackground subtextArgb */ + subtextArgb?: (number|null); + + /** PaymentBackground mediaData */ + mediaData?: (proto.IMediaData|null); + + /** PaymentBackground type */ + type?: (proto.PaymentBackground.PaymentBackgroundType|null); + } + + /** Represents a PaymentBackground. */ + class PaymentBackground implements IPaymentBackground { + + /** + * Constructs a new PaymentBackground. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IPaymentBackground); + + /** PaymentBackground id. */ + public id: string; + + /** PaymentBackground fileLength. */ + public fileLength: (number|Long); + + /** PaymentBackground width. */ + public width: number; + + /** PaymentBackground height. */ + public height: number; + + /** PaymentBackground mimetype. */ + public mimetype: string; + + /** PaymentBackground placeholderArgb. */ + public placeholderArgb: number; + + /** PaymentBackground textArgb. */ + public textArgb: number; + + /** PaymentBackground subtextArgb. */ + public subtextArgb: number; + + /** PaymentBackground mediaData. */ + public mediaData?: (proto.IMediaData|null); + + /** PaymentBackground type. */ + public type: proto.PaymentBackground.PaymentBackgroundType; + + /** + * Creates a new PaymentBackground instance using the specified properties. + * @param [properties] Properties to set + * @returns PaymentBackground instance + */ + public static create(properties?: proto.IPaymentBackground): proto.PaymentBackground; + + /** + * Encodes the specified PaymentBackground message. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. + * @param message PaymentBackground message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IPaymentBackground, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified PaymentBackground message, length delimited. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. + * @param message PaymentBackground message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IPaymentBackground, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a PaymentBackground message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns PaymentBackground + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.PaymentBackground; + + /** + * Decodes a PaymentBackground message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns PaymentBackground + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.PaymentBackground; + + /** + * Verifies a PaymentBackground message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a PaymentBackground message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns PaymentBackground + */ + public static fromObject(object: { [k: string]: any }): proto.PaymentBackground; + + /** + * Creates a plain object from a PaymentBackground message. Also converts values to other types if specified. + * @param message PaymentBackground + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.PaymentBackground, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this PaymentBackground to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + namespace PaymentBackground { + + /** PaymentBackgroundType enum. */ + enum PaymentBackgroundType { + UNKNOWN = 0, + DEFAULT = 1 + } + } + + /** Properties of a Money. */ + interface IMoney { + + /** Money value */ + value?: (number|Long|null); + + /** Money offset */ + offset?: (number|null); + + /** Money currencyCode */ + currencyCode?: (string|null); + } + + /** Represents a Money. */ + class Money implements IMoney { + + /** + * Constructs a new Money. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IMoney); + + /** Money value. */ + public value: (number|Long); + + /** Money offset. */ + public offset: number; + + /** Money currencyCode. */ + public currencyCode: string; + + /** + * Creates a new Money instance using the specified properties. + * @param [properties] Properties to set + * @returns Money instance + */ + public static create(properties?: proto.IMoney): proto.Money; + + /** + * Encodes the specified Money message. Does not implicitly {@link proto.Money.verify|verify} messages. + * @param message Money message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IMoney, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Money message, length delimited. Does not implicitly {@link proto.Money.verify|verify} messages. + * @param message Money message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IMoney, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Money message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Money + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Money; + + /** + * Decodes a Money message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Money + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Money; + + /** + * Verifies a Money message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Money message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Money + */ + public static fromObject(object: { [k: string]: any }): proto.Money; + + /** + * Creates a plain object from a Money message. Also converts values to other types if specified. + * @param message Money + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Money, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Money to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a HydratedQuickReplyButton. */ + interface IHydratedQuickReplyButton { + + /** HydratedQuickReplyButton displayText */ + displayText?: (string|null); + + /** HydratedQuickReplyButton id */ + id?: (string|null); + } + + /** Represents a HydratedQuickReplyButton. */ + class HydratedQuickReplyButton implements IHydratedQuickReplyButton { + + /** + * Constructs a new HydratedQuickReplyButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IHydratedQuickReplyButton); + + /** HydratedQuickReplyButton displayText. */ + public displayText: string; + + /** HydratedQuickReplyButton id. */ + public id: string; + + /** + * Creates a new HydratedQuickReplyButton instance using the specified properties. + * @param [properties] Properties to set + * @returns HydratedQuickReplyButton instance + */ + public static create(properties?: proto.IHydratedQuickReplyButton): proto.HydratedQuickReplyButton; + + /** + * Encodes the specified HydratedQuickReplyButton message. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. + * @param message HydratedQuickReplyButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IHydratedQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified HydratedQuickReplyButton message, length delimited. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. + * @param message HydratedQuickReplyButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IHydratedQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a HydratedQuickReplyButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns HydratedQuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedQuickReplyButton; + + /** + * Decodes a HydratedQuickReplyButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns HydratedQuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedQuickReplyButton; + + /** + * Verifies a HydratedQuickReplyButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a HydratedQuickReplyButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns HydratedQuickReplyButton + */ + public static fromObject(object: { [k: string]: any }): proto.HydratedQuickReplyButton; + + /** + * Creates a plain object from a HydratedQuickReplyButton message. Also converts values to other types if specified. + * @param message HydratedQuickReplyButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.HydratedQuickReplyButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this HydratedQuickReplyButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a HydratedURLButton. */ + interface IHydratedURLButton { + + /** HydratedURLButton displayText */ + displayText?: (string|null); + + /** HydratedURLButton url */ + url?: (string|null); + } + + /** Represents a HydratedURLButton. */ + class HydratedURLButton implements IHydratedURLButton { + + /** + * Constructs a new HydratedURLButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IHydratedURLButton); + + /** HydratedURLButton displayText. */ + public displayText: string; + + /** HydratedURLButton url. */ + public url: string; + + /** + * Creates a new HydratedURLButton instance using the specified properties. + * @param [properties] Properties to set + * @returns HydratedURLButton instance + */ + public static create(properties?: proto.IHydratedURLButton): proto.HydratedURLButton; + + /** + * Encodes the specified HydratedURLButton message. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. + * @param message HydratedURLButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IHydratedURLButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified HydratedURLButton message, length delimited. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. + * @param message HydratedURLButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IHydratedURLButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a HydratedURLButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns HydratedURLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedURLButton; + + /** + * Decodes a HydratedURLButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns HydratedURLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedURLButton; + + /** + * Verifies a HydratedURLButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a HydratedURLButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns HydratedURLButton + */ + public static fromObject(object: { [k: string]: any }): proto.HydratedURLButton; + + /** + * Creates a plain object from a HydratedURLButton message. Also converts values to other types if specified. + * @param message HydratedURLButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.HydratedURLButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this HydratedURLButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a HydratedCallButton. */ + interface IHydratedCallButton { + + /** HydratedCallButton displayText */ + displayText?: (string|null); + + /** HydratedCallButton phoneNumber */ + phoneNumber?: (string|null); + } + + /** Represents a HydratedCallButton. */ + class HydratedCallButton implements IHydratedCallButton { + + /** + * Constructs a new HydratedCallButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IHydratedCallButton); + + /** HydratedCallButton displayText. */ + public displayText: string; + + /** HydratedCallButton phoneNumber. */ + public phoneNumber: string; + + /** + * Creates a new HydratedCallButton instance using the specified properties. + * @param [properties] Properties to set + * @returns HydratedCallButton instance + */ + public static create(properties?: proto.IHydratedCallButton): proto.HydratedCallButton; + + /** + * Encodes the specified HydratedCallButton message. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. + * @param message HydratedCallButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IHydratedCallButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified HydratedCallButton message, length delimited. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. + * @param message HydratedCallButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IHydratedCallButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a HydratedCallButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns HydratedCallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedCallButton; + + /** + * Decodes a HydratedCallButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns HydratedCallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedCallButton; + + /** + * Verifies a HydratedCallButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a HydratedCallButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns HydratedCallButton + */ + public static fromObject(object: { [k: string]: any }): proto.HydratedCallButton; + + /** + * Creates a plain object from a HydratedCallButton message. Also converts values to other types if specified. + * @param message HydratedCallButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.HydratedCallButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this HydratedCallButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a HydratedTemplateButton. */ + interface IHydratedTemplateButton { + + /** HydratedTemplateButton index */ + index?: (number|null); + + /** HydratedTemplateButton quickReplyButton */ + quickReplyButton?: (proto.IHydratedQuickReplyButton|null); + + /** HydratedTemplateButton urlButton */ + urlButton?: (proto.IHydratedURLButton|null); + + /** HydratedTemplateButton callButton */ + callButton?: (proto.IHydratedCallButton|null); + } + + /** Represents a HydratedTemplateButton. */ + class HydratedTemplateButton implements IHydratedTemplateButton { + + /** + * Constructs a new HydratedTemplateButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IHydratedTemplateButton); + + /** HydratedTemplateButton index. */ + public index: number; + + /** HydratedTemplateButton quickReplyButton. */ + public quickReplyButton?: (proto.IHydratedQuickReplyButton|null); + + /** HydratedTemplateButton urlButton. */ + public urlButton?: (proto.IHydratedURLButton|null); + + /** HydratedTemplateButton callButton. */ + public callButton?: (proto.IHydratedCallButton|null); + + /** HydratedTemplateButton hydratedButton. */ + public hydratedButton?: ("quickReplyButton"|"urlButton"|"callButton"); + + /** + * Creates a new HydratedTemplateButton instance using the specified properties. + * @param [properties] Properties to set + * @returns HydratedTemplateButton instance + */ + public static create(properties?: proto.IHydratedTemplateButton): proto.HydratedTemplateButton; + + /** + * Encodes the specified HydratedTemplateButton message. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. + * @param message HydratedTemplateButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IHydratedTemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified HydratedTemplateButton message, length delimited. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. + * @param message HydratedTemplateButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IHydratedTemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a HydratedTemplateButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns HydratedTemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.HydratedTemplateButton; + + /** + * Decodes a HydratedTemplateButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns HydratedTemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.HydratedTemplateButton; + + /** + * Verifies a HydratedTemplateButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a HydratedTemplateButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns HydratedTemplateButton + */ + public static fromObject(object: { [k: string]: any }): proto.HydratedTemplateButton; + + /** + * Creates a plain object from a HydratedTemplateButton message. Also converts values to other types if specified. + * @param message HydratedTemplateButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.HydratedTemplateButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this HydratedTemplateButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a QuickReplyButton. */ + interface IQuickReplyButton { + + /** QuickReplyButton displayText */ + displayText?: (proto.IHighlyStructuredMessage|null); + + /** QuickReplyButton id */ + id?: (string|null); + } + + /** Represents a QuickReplyButton. */ + class QuickReplyButton implements IQuickReplyButton { + + /** + * Constructs a new QuickReplyButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IQuickReplyButton); + + /** QuickReplyButton displayText. */ + public displayText?: (proto.IHighlyStructuredMessage|null); + + /** QuickReplyButton id. */ + public id: string; + + /** + * Creates a new QuickReplyButton instance using the specified properties. + * @param [properties] Properties to set + * @returns QuickReplyButton instance + */ + public static create(properties?: proto.IQuickReplyButton): proto.QuickReplyButton; + + /** + * Encodes the specified QuickReplyButton message. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. + * @param message QuickReplyButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified QuickReplyButton message, length delimited. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. + * @param message QuickReplyButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IQuickReplyButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a QuickReplyButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns QuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.QuickReplyButton; + + /** + * Decodes a QuickReplyButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns QuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.QuickReplyButton; + + /** + * Verifies a QuickReplyButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a QuickReplyButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns QuickReplyButton + */ + public static fromObject(object: { [k: string]: any }): proto.QuickReplyButton; + + /** + * Creates a plain object from a QuickReplyButton message. Also converts values to other types if specified. + * @param message QuickReplyButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.QuickReplyButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this QuickReplyButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a URLButton. */ + interface IURLButton { + + /** URLButton displayText */ + displayText?: (proto.IHighlyStructuredMessage|null); + + /** URLButton url */ + url?: (proto.IHighlyStructuredMessage|null); + } + + /** Represents a URLButton. */ + class URLButton implements IURLButton { + + /** + * Constructs a new URLButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IURLButton); + + /** URLButton displayText. */ + public displayText?: (proto.IHighlyStructuredMessage|null); + + /** URLButton url. */ + public url?: (proto.IHighlyStructuredMessage|null); + + /** + * Creates a new URLButton instance using the specified properties. + * @param [properties] Properties to set + * @returns URLButton instance + */ + public static create(properties?: proto.IURLButton): proto.URLButton; + + /** + * Encodes the specified URLButton message. Does not implicitly {@link proto.URLButton.verify|verify} messages. + * @param message URLButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IURLButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified URLButton message, length delimited. Does not implicitly {@link proto.URLButton.verify|verify} messages. + * @param message URLButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IURLButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a URLButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns URLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.URLButton; + + /** + * Decodes a URLButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns URLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.URLButton; + + /** + * Verifies a URLButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a URLButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns URLButton + */ + public static fromObject(object: { [k: string]: any }): proto.URLButton; + + /** + * Creates a plain object from a URLButton message. Also converts values to other types if specified. + * @param message URLButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.URLButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this URLButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a CallButton. */ + interface ICallButton { + + /** CallButton displayText */ + displayText?: (proto.IHighlyStructuredMessage|null); + + /** CallButton phoneNumber */ + phoneNumber?: (proto.IHighlyStructuredMessage|null); + } + + /** Represents a CallButton. */ + class CallButton implements ICallButton { + + /** + * Constructs a new CallButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.ICallButton); + + /** CallButton displayText. */ + public displayText?: (proto.IHighlyStructuredMessage|null); + + /** CallButton phoneNumber. */ + public phoneNumber?: (proto.IHighlyStructuredMessage|null); + + /** + * Creates a new CallButton instance using the specified properties. + * @param [properties] Properties to set + * @returns CallButton instance + */ + public static create(properties?: proto.ICallButton): proto.CallButton; + + /** + * Encodes the specified CallButton message. Does not implicitly {@link proto.CallButton.verify|verify} messages. + * @param message CallButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.ICallButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified CallButton message, length delimited. Does not implicitly {@link proto.CallButton.verify|verify} messages. + * @param message CallButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.ICallButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a CallButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns CallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.CallButton; + + /** + * Decodes a CallButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns CallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.CallButton; + + /** + * Verifies a CallButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a CallButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns CallButton + */ + public static fromObject(object: { [k: string]: any }): proto.CallButton; + + /** + * Creates a plain object from a CallButton message. Also converts values to other types if specified. + * @param message CallButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.CallButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this CallButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a TemplateButton. */ + interface ITemplateButton { + + /** TemplateButton index */ + index?: (number|null); + + /** TemplateButton quickReplyButton */ + quickReplyButton?: (proto.IQuickReplyButton|null); + + /** TemplateButton urlButton */ + urlButton?: (proto.IURLButton|null); + + /** TemplateButton callButton */ + callButton?: (proto.ICallButton|null); + } + + /** Represents a TemplateButton. */ + class TemplateButton implements ITemplateButton { + + /** + * Constructs a new TemplateButton. + * @param [properties] Properties to set + */ + constructor(properties?: proto.ITemplateButton); + + /** TemplateButton index. */ + public index: number; + + /** TemplateButton quickReplyButton. */ + public quickReplyButton?: (proto.IQuickReplyButton|null); + + /** TemplateButton urlButton. */ + public urlButton?: (proto.IURLButton|null); + + /** TemplateButton callButton. */ + public callButton?: (proto.ICallButton|null); + + /** TemplateButton button. */ + public button?: ("quickReplyButton"|"urlButton"|"callButton"); + + /** + * Creates a new TemplateButton instance using the specified properties. + * @param [properties] Properties to set + * @returns TemplateButton instance + */ + public static create(properties?: proto.ITemplateButton): proto.TemplateButton; + + /** + * Encodes the specified TemplateButton message. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. + * @param message TemplateButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.ITemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified TemplateButton message, length delimited. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. + * @param message TemplateButton message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.ITemplateButton, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a TemplateButton message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns TemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.TemplateButton; + + /** + * Decodes a TemplateButton message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns TemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.TemplateButton; + + /** + * Verifies a TemplateButton message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a TemplateButton message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns TemplateButton + */ + public static fromObject(object: { [k: string]: any }): proto.TemplateButton; + + /** + * Creates a plain object from a TemplateButton message. Also converts values to other types if specified. + * @param message TemplateButton + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.TemplateButton, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this TemplateButton to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a Location. */ + interface ILocation { + + /** Location degreesLatitude */ + degreesLatitude?: (number|null); + + /** Location degreesLongitude */ + degreesLongitude?: (number|null); + + /** Location name */ + name?: (string|null); + } + + /** Represents a Location. */ + class Location implements ILocation { + + /** + * Constructs a new Location. + * @param [properties] Properties to set + */ + constructor(properties?: proto.ILocation); + + /** Location degreesLatitude. */ + public degreesLatitude: number; + + /** Location degreesLongitude. */ + public degreesLongitude: number; + + /** Location name. */ + public name: string; + + /** + * Creates a new Location instance using the specified properties. + * @param [properties] Properties to set + * @returns Location instance + */ + public static create(properties?: proto.ILocation): proto.Location; + + /** + * Encodes the specified Location message. Does not implicitly {@link proto.Location.verify|verify} messages. + * @param message Location message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.ILocation, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Location message, length delimited. Does not implicitly {@link proto.Location.verify|verify} messages. + * @param message Location message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.ILocation, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Location message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Location + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Location; + + /** + * Decodes a Location message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Location + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Location; + + /** + * Verifies a Location message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Location message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Location + */ + public static fromObject(object: { [k: string]: any }): proto.Location; + + /** + * Creates a plain object from a Location message. Also converts values to other types if specified. + * @param message Location + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Location, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Location to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a Point. */ + interface IPoint { + + /** Point xDeprecated */ + xDeprecated?: (number|null); + + /** Point yDeprecated */ + yDeprecated?: (number|null); + + /** Point x */ + x?: (number|null); + + /** Point y */ + y?: (number|null); + } + + /** Represents a Point. */ + class Point implements IPoint { + + /** + * Constructs a new Point. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IPoint); + + /** Point xDeprecated. */ + public xDeprecated: number; + + /** Point yDeprecated. */ + public yDeprecated: number; + + /** Point x. */ + public x: number; + + /** Point y. */ + public y: number; + + /** + * Creates a new Point instance using the specified properties. + * @param [properties] Properties to set + * @returns Point instance + */ + public static create(properties?: proto.IPoint): proto.Point; + + /** + * Encodes the specified Point message. Does not implicitly {@link proto.Point.verify|verify} messages. + * @param message Point message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IPoint, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Point message, length delimited. Does not implicitly {@link proto.Point.verify|verify} messages. + * @param message Point message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IPoint, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Point message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Point + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Point; + + /** + * Decodes a Point message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Point + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Point; + + /** + * Verifies a Point message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Point message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Point + */ + public static fromObject(object: { [k: string]: any }): proto.Point; + + /** + * Creates a plain object from a Point message. Also converts values to other types if specified. + * @param message Point + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Point, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Point to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of a CompanionProps. */ interface ICompanionProps { @@ -17281,6 +19662,240 @@ export namespace proto { public toJSON(): { [k: string]: any }; } + /** Properties of a Reaction. */ + interface IReaction { + + /** Reaction key */ + key?: (proto.IMessageKey|null); + + /** Reaction text */ + text?: (string|null); + + /** Reaction groupingKey */ + groupingKey?: (string|null); + + /** Reaction senderTimestampMs */ + senderTimestampMs?: (number|Long|null); + + /** Reaction unread */ + unread?: (boolean|null); + } + + /** Represents a Reaction. */ + class Reaction implements IReaction { + + /** + * Constructs a new Reaction. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IReaction); + + /** Reaction key. */ + public key?: (proto.IMessageKey|null); + + /** Reaction text. */ + public text: string; + + /** Reaction groupingKey. */ + public groupingKey: string; + + /** Reaction senderTimestampMs. */ + public senderTimestampMs: (number|Long); + + /** Reaction unread. */ + public unread: boolean; + + /** + * Creates a new Reaction instance using the specified properties. + * @param [properties] Properties to set + * @returns Reaction instance + */ + public static create(properties?: proto.IReaction): proto.Reaction; + + /** + * Encodes the specified Reaction message. Does not implicitly {@link proto.Reaction.verify|verify} messages. + * @param message Reaction message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IReaction, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Reaction message, length delimited. Does not implicitly {@link proto.Reaction.verify|verify} messages. + * @param message Reaction message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IReaction, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Reaction message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Reaction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.Reaction; + + /** + * Decodes a Reaction message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Reaction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.Reaction; + + /** + * Verifies a Reaction message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Reaction message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Reaction + */ + public static fromObject(object: { [k: string]: any }): proto.Reaction; + + /** + * Creates a plain object from a Reaction message. Also converts values to other types if specified. + * @param message Reaction + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.Reaction, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Reaction to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + + /** Properties of a UserReceipt. */ + interface IUserReceipt { + + /** UserReceipt userJid */ + userJid: string; + + /** UserReceipt receiptTimestamp */ + receiptTimestamp?: (number|Long|null); + + /** UserReceipt readTimestamp */ + readTimestamp?: (number|Long|null); + + /** UserReceipt playedTimestamp */ + playedTimestamp?: (number|Long|null); + + /** UserReceipt pendingDeviceJid */ + pendingDeviceJid?: (string[]|null); + + /** UserReceipt deliveredDeviceJid */ + deliveredDeviceJid?: (string[]|null); + } + + /** Represents a UserReceipt. */ + class UserReceipt implements IUserReceipt { + + /** + * Constructs a new UserReceipt. + * @param [properties] Properties to set + */ + constructor(properties?: proto.IUserReceipt); + + /** UserReceipt userJid. */ + public userJid: string; + + /** UserReceipt receiptTimestamp. */ + public receiptTimestamp: (number|Long); + + /** UserReceipt readTimestamp. */ + public readTimestamp: (number|Long); + + /** UserReceipt playedTimestamp. */ + public playedTimestamp: (number|Long); + + /** UserReceipt pendingDeviceJid. */ + public pendingDeviceJid: string[]; + + /** UserReceipt deliveredDeviceJid. */ + public deliveredDeviceJid: string[]; + + /** + * Creates a new UserReceipt instance using the specified properties. + * @param [properties] Properties to set + * @returns UserReceipt instance + */ + public static create(properties?: proto.IUserReceipt): proto.UserReceipt; + + /** + * Encodes the specified UserReceipt message. Does not implicitly {@link proto.UserReceipt.verify|verify} messages. + * @param message UserReceipt message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: proto.IUserReceipt, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified UserReceipt message, length delimited. Does not implicitly {@link proto.UserReceipt.verify|verify} messages. + * @param message UserReceipt message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: proto.IUserReceipt, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a UserReceipt message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns UserReceipt + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.UserReceipt; + + /** + * Decodes a UserReceipt message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns UserReceipt + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.UserReceipt; + + /** + * Verifies a UserReceipt message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a UserReceipt message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns UserReceipt + */ + public static fromObject(object: { [k: string]: any }): proto.UserReceipt; + + /** + * Creates a plain object from a UserReceipt message. Also converts values to other types if specified. + * @param message UserReceipt + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: proto.UserReceipt, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this UserReceipt to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of a PhotoChange. */ interface IPhotoChange { @@ -17383,96 +19998,6 @@ export namespace proto { public toJSON(): { [k: string]: any }; } - /** Properties of a MediaData. */ - interface IMediaData { - - /** MediaData localPath */ - localPath?: (string|null); - } - - /** Represents a MediaData. */ - class MediaData implements IMediaData { - - /** - * Constructs a new MediaData. - * @param [properties] Properties to set - */ - constructor(properties?: proto.IMediaData); - - /** MediaData localPath. */ - public localPath: string; - - /** - * Creates a new MediaData instance using the specified properties. - * @param [properties] Properties to set - * @returns MediaData instance - */ - public static create(properties?: proto.IMediaData): proto.MediaData; - - /** - * Encodes the specified MediaData message. Does not implicitly {@link proto.MediaData.verify|verify} messages. - * @param message MediaData message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: proto.IMediaData, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified MediaData message, length delimited. Does not implicitly {@link proto.MediaData.verify|verify} messages. - * @param message MediaData message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: proto.IMediaData, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a MediaData message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns MediaData - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): proto.MediaData; - - /** - * Decodes a MediaData message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns MediaData - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): proto.MediaData; - - /** - * Verifies a MediaData message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a MediaData message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns MediaData - */ - public static fromObject(object: { [k: string]: any }): proto.MediaData; - - /** - * Creates a plain object from a MediaData message. Also converts values to other types if specified. - * @param message MediaData - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: proto.MediaData, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this MediaData to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } - /** Properties of a WebFeatures. */ interface IWebFeatures { @@ -17601,6 +20126,15 @@ export namespace proto { /** WebFeatures mdForceUpgrade */ mdForceUpgrade?: (proto.WebFeatures.WebFeaturesFlag|null); + + /** WebFeatures disappearingMode */ + disappearingMode?: (proto.WebFeatures.WebFeaturesFlag|null); + + /** WebFeatures externalMdOptInAvailable */ + externalMdOptInAvailable?: (proto.WebFeatures.WebFeaturesFlag|null); + + /** WebFeatures noDeleteMessageTimeLimit */ + noDeleteMessageTimeLimit?: (proto.WebFeatures.WebFeaturesFlag|null); } /** Represents a WebFeatures. */ @@ -17738,6 +20272,15 @@ export namespace proto { /** WebFeatures mdForceUpgrade. */ public mdForceUpgrade: proto.WebFeatures.WebFeaturesFlag; + /** WebFeatures disappearingMode. */ + public disappearingMode: proto.WebFeatures.WebFeaturesFlag; + + /** WebFeatures externalMdOptInAvailable. */ + public externalMdOptInAvailable: proto.WebFeatures.WebFeaturesFlag; + + /** WebFeatures noDeleteMessageTimeLimit. */ + public noDeleteMessageTimeLimit: proto.WebFeatures.WebFeaturesFlag; + /** * Creates a new WebFeatures instance using the specified properties. * @param [properties] Properties to set @@ -18348,6 +20891,27 @@ export namespace proto { /** WebMessageInfo photoChange */ photoChange?: (proto.IPhotoChange|null); + + /** WebMessageInfo userReceipt */ + userReceipt?: (proto.IUserReceipt[]|null); + + /** WebMessageInfo reactions */ + reactions?: (proto.IReaction[]|null); + + /** WebMessageInfo quotedStickerData */ + quotedStickerData?: (proto.IMediaData|null); + + /** WebMessageInfo futureproofData */ + futureproofData?: (Uint8Array|null); + + /** WebMessageInfo statusPsaCampaignId */ + statusPsaCampaignId?: (string|null); + + /** WebMessageInfo statusPsaCampaignDuration */ + statusPsaCampaignDuration?: (number|null); + + /** WebMessageInfo statusPsaCampaignReadTimestamp */ + statusPsaCampaignReadTimestamp?: (number|Long|null); } /** Represents a WebMessageInfo. */ @@ -18449,6 +21013,27 @@ export namespace proto { /** WebMessageInfo photoChange. */ public photoChange?: (proto.IPhotoChange|null); + /** WebMessageInfo userReceipt. */ + public userReceipt: proto.IUserReceipt[]; + + /** WebMessageInfo reactions. */ + public reactions: proto.IReaction[]; + + /** WebMessageInfo quotedStickerData. */ + public quotedStickerData?: (proto.IMediaData|null); + + /** WebMessageInfo futureproofData. */ + public futureproofData: Uint8Array; + + /** WebMessageInfo statusPsaCampaignId. */ + public statusPsaCampaignId: string; + + /** WebMessageInfo statusPsaCampaignDuration. */ + public statusPsaCampaignDuration: number; + + /** WebMessageInfo statusPsaCampaignReadTimestamp. */ + public statusPsaCampaignReadTimestamp: (number|Long); + /** * Creates a new WebMessageInfo instance using the specified properties. * @param [properties] Properties to set @@ -18663,7 +21248,9 @@ export namespace proto { BIZ_PRIVACY_MODE_INIT_FB = 126, BIZ_PRIVACY_MODE_INIT_BSP = 127, BIZ_PRIVACY_MODE_TO_FB = 128, - BIZ_PRIVACY_MODE_TO_BSP = 129 + BIZ_PRIVACY_MODE_TO_BSP = 129, + DISAPPEARING_MODE = 130, + E2E_DEVICE_FETCH_FAILED = 131 } /** WebMessageInfoBizPrivacyStatus enum. */ diff --git a/WAMessage/WAMessage.js b/WAProto/index.js similarity index 87% rename from WAMessage/WAMessage.js rename to WAProto/index.js index 140aede..fa9f78b 100644 --- a/WAMessage/WAMessage.js +++ b/WAProto/index.js @@ -622,6 +622,9 @@ $root.proto = (function() { case 23: case 24: case 25: + case 26: + case 27: + case 28: break; } if (message.appVersion != null && message.hasOwnProperty("appVersion")) { @@ -785,10 +788,22 @@ $root.proto = (function() { case 24: message.platform = 24; break; - case "VR": + case "OCULUS_MSG": case 25: message.platform = 25; break; + case "OCULUS_CALL": + case 26: + message.platform = 26; + break; + case "MILAN": + case 27: + message.platform = 27; + break; + case "CAPI": + case 28: + message.platform = 28; + break; } if (object.appVersion != null) { if (typeof object.appVersion !== "object") @@ -933,7 +948,10 @@ $root.proto = (function() { * @property {number} IGLITE_ANDROID=22 IGLITE_ANDROID value * @property {number} PAGE=23 PAGE value * @property {number} MACOS=24 MACOS value - * @property {number} VR=25 VR value + * @property {number} OCULUS_MSG=25 OCULUS_MSG value + * @property {number} OCULUS_CALL=26 OCULUS_CALL value + * @property {number} MILAN=27 MILAN value + * @property {number} CAPI=28 CAPI value */ UserAgent.UserAgentPlatform = (function() { var valuesById = {}, values = Object.create(valuesById); @@ -962,7 +980,10 @@ $root.proto = (function() { values[valuesById[22] = "IGLITE_ANDROID"] = 22; values[valuesById[23] = "PAGE"] = 23; values[valuesById[24] = "MACOS"] = 24; - values[valuesById[25] = "VR"] = 25; + values[valuesById[25] = "OCULUS_MSG"] = 25; + values[valuesById[26] = "OCULUS_CALL"] = 26; + values[valuesById[27] = "MILAN"] = 27; + values[valuesById[28] = "CAPI"] = 28; return values; })(); @@ -2391,13 +2412,11 @@ $root.proto = (function() { * @interface IClientPayload * @property {number|Long|null} [username] ClientPayload username * @property {boolean|null} [passive] ClientPayload passive - * @property {Array.|null} [clientFeatures] ClientPayload clientFeatures * @property {proto.IUserAgent|null} [userAgent] ClientPayload userAgent * @property {proto.IWebInfo|null} [webInfo] ClientPayload webInfo * @property {string|null} [pushName] ClientPayload pushName * @property {number|null} [sessionId] ClientPayload sessionId * @property {boolean|null} [shortConnect] ClientPayload shortConnect - * @property {proto.ClientPayload.ClientPayloadIOSAppExtension|null} [iosAppExtension] ClientPayload iosAppExtension * @property {proto.ClientPayload.ClientPayloadConnectType|null} [connectType] ClientPayload connectType * @property {proto.ClientPayload.ClientPayloadConnectReason|null} [connectReason] ClientPayload connectReason * @property {Array.|null} [shards] ClientPayload shards @@ -2410,6 +2429,10 @@ $root.proto = (function() { * @property {Uint8Array|null} [fbCat] ClientPayload fbCat * @property {Uint8Array|null} [fbUserAgent] ClientPayload fbUserAgent * @property {boolean|null} [oc] ClientPayload oc + * @property {number|null} [lc] ClientPayload lc + * @property {proto.ClientPayload.ClientPayloadIOSAppExtension|null} [iosAppExtension] ClientPayload iosAppExtension + * @property {number|Long|null} [fbAppId] ClientPayload fbAppId + * @property {Uint8Array|null} [fbDeviceId] ClientPayload fbDeviceId */ /** @@ -2421,7 +2444,6 @@ $root.proto = (function() { * @param {proto.IClientPayload=} [properties] Properties to set */ function ClientPayload(properties) { - this.clientFeatures = []; this.shards = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) @@ -2445,14 +2467,6 @@ $root.proto = (function() { */ ClientPayload.prototype.passive = false; - /** - * ClientPayload clientFeatures. - * @member {Array.} clientFeatures - * @memberof proto.ClientPayload - * @instance - */ - ClientPayload.prototype.clientFeatures = $util.emptyArray; - /** * ClientPayload userAgent. * @member {proto.IUserAgent|null|undefined} userAgent @@ -2493,14 +2507,6 @@ $root.proto = (function() { */ ClientPayload.prototype.shortConnect = false; - /** - * ClientPayload iosAppExtension. - * @member {proto.ClientPayload.ClientPayloadIOSAppExtension} iosAppExtension - * @memberof proto.ClientPayload - * @instance - */ - ClientPayload.prototype.iosAppExtension = 0; - /** * ClientPayload connectType. * @member {proto.ClientPayload.ClientPayloadConnectType} connectType @@ -2597,6 +2603,38 @@ $root.proto = (function() { */ ClientPayload.prototype.oc = false; + /** + * ClientPayload lc. + * @member {number} lc + * @memberof proto.ClientPayload + * @instance + */ + ClientPayload.prototype.lc = 0; + + /** + * ClientPayload iosAppExtension. + * @member {proto.ClientPayload.ClientPayloadIOSAppExtension} iosAppExtension + * @memberof proto.ClientPayload + * @instance + */ + ClientPayload.prototype.iosAppExtension = 0; + + /** + * ClientPayload fbAppId. + * @member {number|Long} fbAppId + * @memberof proto.ClientPayload + * @instance + */ + ClientPayload.prototype.fbAppId = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * ClientPayload fbDeviceId. + * @member {Uint8Array} fbDeviceId + * @memberof proto.ClientPayload + * @instance + */ + ClientPayload.prototype.fbDeviceId = $util.newBuffer([]); + /** * Creates a new ClientPayload instance using the specified properties. * @function create @@ -2625,9 +2663,6 @@ $root.proto = (function() { writer.uint32(/* id 1, wireType 0 =*/8).uint64(message.username); if (message.passive != null && Object.hasOwnProperty.call(message, "passive")) writer.uint32(/* id 3, wireType 0 =*/24).bool(message.passive); - if (message.clientFeatures != null && message.clientFeatures.length) - for (var i = 0; i < message.clientFeatures.length; ++i) - writer.uint32(/* id 4, wireType 0 =*/32).int32(message.clientFeatures[i]); if (message.userAgent != null && Object.hasOwnProperty.call(message, "userAgent")) $root.proto.UserAgent.encode(message.userAgent, writer.uint32(/* id 5, wireType 2 =*/42).fork()).ldelim(); if (message.webInfo != null && Object.hasOwnProperty.call(message, "webInfo")) @@ -2663,8 +2698,14 @@ $root.proto = (function() { writer.uint32(/* id 22, wireType 2 =*/178).bytes(message.fbUserAgent); if (message.oc != null && Object.hasOwnProperty.call(message, "oc")) writer.uint32(/* id 23, wireType 0 =*/184).bool(message.oc); + if (message.lc != null && Object.hasOwnProperty.call(message, "lc")) + writer.uint32(/* id 24, wireType 0 =*/192).uint32(message.lc); if (message.iosAppExtension != null && Object.hasOwnProperty.call(message, "iosAppExtension")) writer.uint32(/* id 30, wireType 0 =*/240).int32(message.iosAppExtension); + if (message.fbAppId != null && Object.hasOwnProperty.call(message, "fbAppId")) + writer.uint32(/* id 31, wireType 0 =*/248).uint64(message.fbAppId); + if (message.fbDeviceId != null && Object.hasOwnProperty.call(message, "fbDeviceId")) + writer.uint32(/* id 32, wireType 2 =*/258).bytes(message.fbDeviceId); return writer; }; @@ -2705,16 +2746,6 @@ $root.proto = (function() { case 3: message.passive = reader.bool(); break; - case 4: - if (!(message.clientFeatures && message.clientFeatures.length)) - message.clientFeatures = []; - if ((tag & 7) === 2) { - var end2 = reader.uint32() + reader.pos; - while (reader.pos < end2) - message.clientFeatures.push(reader.int32()); - } else - message.clientFeatures.push(reader.int32()); - break; case 5: message.userAgent = $root.proto.UserAgent.decode(reader, reader.uint32()); break; @@ -2730,9 +2761,6 @@ $root.proto = (function() { case 10: message.shortConnect = reader.bool(); break; - case 30: - message.iosAppExtension = reader.int32(); - break; case 12: message.connectType = reader.int32(); break; @@ -2776,6 +2804,18 @@ $root.proto = (function() { case 23: message.oc = reader.bool(); break; + case 24: + message.lc = reader.uint32(); + break; + case 30: + message.iosAppExtension = reader.int32(); + break; + case 31: + message.fbAppId = reader.uint64(); + break; + case 32: + message.fbDeviceId = reader.bytes(); + break; default: reader.skipType(tag & 7); break; @@ -2817,17 +2857,6 @@ $root.proto = (function() { if (message.passive != null && message.hasOwnProperty("passive")) if (typeof message.passive !== "boolean") return "passive: boolean expected"; - if (message.clientFeatures != null && message.hasOwnProperty("clientFeatures")) { - if (!Array.isArray(message.clientFeatures)) - return "clientFeatures: array expected"; - for (var i = 0; i < message.clientFeatures.length; ++i) - switch (message.clientFeatures[i]) { - default: - return "clientFeatures: enum value[] expected"; - case 0: - break; - } - } if (message.userAgent != null && message.hasOwnProperty("userAgent")) { var error = $root.proto.UserAgent.verify(message.userAgent); if (error) @@ -2847,15 +2876,6 @@ $root.proto = (function() { if (message.shortConnect != null && message.hasOwnProperty("shortConnect")) if (typeof message.shortConnect !== "boolean") return "shortConnect: boolean expected"; - if (message.iosAppExtension != null && message.hasOwnProperty("iosAppExtension")) - switch (message.iosAppExtension) { - default: - return "iosAppExtension: enum value expected"; - case 0: - case 1: - case 2: - break; - } if (message.connectType != null && message.hasOwnProperty("connectType")) switch (message.connectType) { default: @@ -2932,6 +2952,24 @@ $root.proto = (function() { if (message.oc != null && message.hasOwnProperty("oc")) if (typeof message.oc !== "boolean") return "oc: boolean expected"; + if (message.lc != null && message.hasOwnProperty("lc")) + if (!$util.isInteger(message.lc)) + return "lc: integer expected"; + if (message.iosAppExtension != null && message.hasOwnProperty("iosAppExtension")) + switch (message.iosAppExtension) { + default: + return "iosAppExtension: enum value expected"; + case 0: + case 1: + case 2: + break; + } + if (message.fbAppId != null && message.hasOwnProperty("fbAppId")) + if (!$util.isInteger(message.fbAppId) && !(message.fbAppId && $util.isInteger(message.fbAppId.low) && $util.isInteger(message.fbAppId.high))) + return "fbAppId: integer|Long expected"; + if (message.fbDeviceId != null && message.hasOwnProperty("fbDeviceId")) + if (!(message.fbDeviceId && typeof message.fbDeviceId.length === "number" || $util.isString(message.fbDeviceId))) + return "fbDeviceId: buffer expected"; return null; }; @@ -2958,19 +2996,6 @@ $root.proto = (function() { message.username = new $util.LongBits(object.username.low >>> 0, object.username.high >>> 0).toNumber(true); if (object.passive != null) message.passive = Boolean(object.passive); - if (object.clientFeatures) { - if (!Array.isArray(object.clientFeatures)) - throw TypeError(".proto.ClientPayload.clientFeatures: array expected"); - message.clientFeatures = []; - for (var i = 0; i < object.clientFeatures.length; ++i) - switch (object.clientFeatures[i]) { - default: - case "NONE": - case 0: - message.clientFeatures[i] = 0; - break; - } - } if (object.userAgent != null) { if (typeof object.userAgent !== "object") throw TypeError(".proto.ClientPayload.userAgent: object expected"); @@ -2987,20 +3012,6 @@ $root.proto = (function() { message.sessionId = object.sessionId | 0; if (object.shortConnect != null) message.shortConnect = Boolean(object.shortConnect); - switch (object.iosAppExtension) { - case "SHARE_EXTENSION": - case 0: - message.iosAppExtension = 0; - break; - case "SERVICE_EXTENSION": - case 1: - message.iosAppExtension = 1; - break; - case "INTENTS_EXTENSION": - case 2: - message.iosAppExtension = 2; - break; - } switch (object.connectType) { case "CELLULAR_UNKNOWN": case 0: @@ -3134,6 +3145,36 @@ $root.proto = (function() { message.fbUserAgent = object.fbUserAgent; if (object.oc != null) message.oc = Boolean(object.oc); + if (object.lc != null) + message.lc = object.lc >>> 0; + switch (object.iosAppExtension) { + case "SHARE_EXTENSION": + case 0: + message.iosAppExtension = 0; + break; + case "SERVICE_EXTENSION": + case 1: + message.iosAppExtension = 1; + break; + case "INTENTS_EXTENSION": + case 2: + message.iosAppExtension = 2; + break; + } + if (object.fbAppId != null) + if ($util.Long) + (message.fbAppId = $util.Long.fromValue(object.fbAppId)).unsigned = true; + else if (typeof object.fbAppId === "string") + message.fbAppId = parseInt(object.fbAppId, 10); + else if (typeof object.fbAppId === "number") + message.fbAppId = object.fbAppId; + else if (typeof object.fbAppId === "object") + message.fbAppId = new $util.LongBits(object.fbAppId.low >>> 0, object.fbAppId.high >>> 0).toNumber(true); + if (object.fbDeviceId != null) + if (typeof object.fbDeviceId === "string") + $util.base64.decode(object.fbDeviceId, message.fbDeviceId = $util.newBuffer($util.base64.length(object.fbDeviceId)), 0); + else if (object.fbDeviceId.length) + message.fbDeviceId = object.fbDeviceId; return message; }; @@ -3150,10 +3191,8 @@ $root.proto = (function() { if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) { - object.clientFeatures = []; + if (options.arrays || options.defaults) object.shards = []; - } if (options.defaults) { if ($util.Long) { var long = new $util.Long(0, 0, true); @@ -3189,7 +3228,20 @@ $root.proto = (function() { object.fbUserAgent = $util.newBuffer(object.fbUserAgent); } object.oc = false; + object.lc = 0; object.iosAppExtension = options.enums === String ? "SHARE_EXTENSION" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.fbAppId = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.fbAppId = options.longs === String ? "0" : 0; + if (options.bytes === String) + object.fbDeviceId = ""; + else { + object.fbDeviceId = []; + if (options.bytes !== Array) + object.fbDeviceId = $util.newBuffer(object.fbDeviceId); + } } if (message.username != null && message.hasOwnProperty("username")) if (typeof message.username === "number") @@ -3198,11 +3250,6 @@ $root.proto = (function() { object.username = options.longs === String ? $util.Long.prototype.toString.call(message.username) : options.longs === Number ? new $util.LongBits(message.username.low >>> 0, message.username.high >>> 0).toNumber(true) : message.username; if (message.passive != null && message.hasOwnProperty("passive")) object.passive = message.passive; - if (message.clientFeatures && message.clientFeatures.length) { - object.clientFeatures = []; - for (var j = 0; j < message.clientFeatures.length; ++j) - object.clientFeatures[j] = options.enums === String ? $root.proto.ClientPayload.ClientPayloadClientFeature[message.clientFeatures[j]] : message.clientFeatures[j]; - } if (message.userAgent != null && message.hasOwnProperty("userAgent")) object.userAgent = $root.proto.UserAgent.toObject(message.userAgent, options); if (message.webInfo != null && message.hasOwnProperty("webInfo")) @@ -3240,8 +3287,17 @@ $root.proto = (function() { object.fbUserAgent = options.bytes === String ? $util.base64.encode(message.fbUserAgent, 0, message.fbUserAgent.length) : options.bytes === Array ? Array.prototype.slice.call(message.fbUserAgent) : message.fbUserAgent; if (message.oc != null && message.hasOwnProperty("oc")) object.oc = message.oc; + if (message.lc != null && message.hasOwnProperty("lc")) + object.lc = message.lc; if (message.iosAppExtension != null && message.hasOwnProperty("iosAppExtension")) object.iosAppExtension = options.enums === String ? $root.proto.ClientPayload.ClientPayloadIOSAppExtension[message.iosAppExtension] : message.iosAppExtension; + if (message.fbAppId != null && message.hasOwnProperty("fbAppId")) + if (typeof message.fbAppId === "number") + object.fbAppId = options.longs === String ? String(message.fbAppId) : message.fbAppId; + else + object.fbAppId = options.longs === String ? $util.Long.prototype.toString.call(message.fbAppId) : options.longs === Number ? new $util.LongBits(message.fbAppId.low >>> 0, message.fbAppId.high >>> 0).toNumber(true) : message.fbAppId; + if (message.fbDeviceId != null && message.hasOwnProperty("fbDeviceId")) + object.fbDeviceId = options.bytes === String ? $util.base64.encode(message.fbDeviceId, 0, message.fbDeviceId.length) : options.bytes === Array ? Array.prototype.slice.call(message.fbDeviceId) : message.fbDeviceId; return object; }; @@ -3256,34 +3312,6 @@ $root.proto = (function() { return this.constructor.toObject(this, $protobuf.util.toJSONOptions); }; - /** - * ClientPayloadClientFeature enum. - * @name proto.ClientPayload.ClientPayloadClientFeature - * @enum {number} - * @property {number} NONE=0 NONE value - */ - ClientPayload.ClientPayloadClientFeature = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[0] = "NONE"] = 0; - return values; - })(); - - /** - * ClientPayloadIOSAppExtension enum. - * @name proto.ClientPayload.ClientPayloadIOSAppExtension - * @enum {number} - * @property {number} SHARE_EXTENSION=0 SHARE_EXTENSION value - * @property {number} SERVICE_EXTENSION=1 SERVICE_EXTENSION value - * @property {number} INTENTS_EXTENSION=2 INTENTS_EXTENSION value - */ - ClientPayload.ClientPayloadIOSAppExtension = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[0] = "SHARE_EXTENSION"] = 0; - values[valuesById[1] = "SERVICE_EXTENSION"] = 1; - values[valuesById[2] = "INTENTS_EXTENSION"] = 2; - return values; - })(); - /** * ClientPayloadConnectType enum. * @name proto.ClientPayload.ClientPayloadConnectType @@ -3360,9 +3388,324 @@ $root.proto = (function() { return values; })(); + /** + * ClientPayloadIOSAppExtension enum. + * @name proto.ClientPayload.ClientPayloadIOSAppExtension + * @enum {number} + * @property {number} SHARE_EXTENSION=0 SHARE_EXTENSION value + * @property {number} SERVICE_EXTENSION=1 SERVICE_EXTENSION value + * @property {number} INTENTS_EXTENSION=2 INTENTS_EXTENSION value + */ + ClientPayload.ClientPayloadIOSAppExtension = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "SHARE_EXTENSION"] = 0; + values[valuesById[1] = "SERVICE_EXTENSION"] = 1; + values[valuesById[2] = "INTENTS_EXTENSION"] = 2; + return values; + })(); + return ClientPayload; })(); + proto.Details = (function() { + + /** + * Properties of a Details. + * @memberof proto + * @interface IDetails + * @property {number|null} [serial] Details serial + * @property {string|null} [issuer] Details issuer + * @property {number|Long|null} [expires] Details expires + * @property {string|null} [subject] Details subject + * @property {Uint8Array|null} [key] Details key + */ + + /** + * Constructs a new Details. + * @memberof proto + * @classdesc Represents a Details. + * @implements IDetails + * @constructor + * @param {proto.IDetails=} [properties] Properties to set + */ + function Details(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Details serial. + * @member {number} serial + * @memberof proto.Details + * @instance + */ + Details.prototype.serial = 0; + + /** + * Details issuer. + * @member {string} issuer + * @memberof proto.Details + * @instance + */ + Details.prototype.issuer = ""; + + /** + * Details expires. + * @member {number|Long} expires + * @memberof proto.Details + * @instance + */ + Details.prototype.expires = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * Details subject. + * @member {string} subject + * @memberof proto.Details + * @instance + */ + Details.prototype.subject = ""; + + /** + * Details key. + * @member {Uint8Array} key + * @memberof proto.Details + * @instance + */ + Details.prototype.key = $util.newBuffer([]); + + /** + * Creates a new Details instance using the specified properties. + * @function create + * @memberof proto.Details + * @static + * @param {proto.IDetails=} [properties] Properties to set + * @returns {proto.Details} Details instance + */ + Details.create = function create(properties) { + return new Details(properties); + }; + + /** + * Encodes the specified Details message. Does not implicitly {@link proto.Details.verify|verify} messages. + * @function encode + * @memberof proto.Details + * @static + * @param {proto.IDetails} message Details message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Details.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.serial != null && Object.hasOwnProperty.call(message, "serial")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.serial); + if (message.issuer != null && Object.hasOwnProperty.call(message, "issuer")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.issuer); + if (message.expires != null && Object.hasOwnProperty.call(message, "expires")) + writer.uint32(/* id 3, wireType 0 =*/24).uint64(message.expires); + if (message.subject != null && Object.hasOwnProperty.call(message, "subject")) + writer.uint32(/* id 4, wireType 2 =*/34).string(message.subject); + if (message.key != null && Object.hasOwnProperty.call(message, "key")) + writer.uint32(/* id 5, wireType 2 =*/42).bytes(message.key); + return writer; + }; + + /** + * Encodes the specified Details message, length delimited. Does not implicitly {@link proto.Details.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Details + * @static + * @param {proto.IDetails} message Details message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Details.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Details message from the specified reader or buffer. + * @function decode + * @memberof proto.Details + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Details} Details + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Details.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Details(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.serial = reader.uint32(); + break; + case 2: + message.issuer = reader.string(); + break; + case 3: + message.expires = reader.uint64(); + break; + case 4: + message.subject = reader.string(); + break; + case 5: + message.key = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Details message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Details + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Details} Details + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Details.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Details message. + * @function verify + * @memberof proto.Details + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Details.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.serial != null && message.hasOwnProperty("serial")) + if (!$util.isInteger(message.serial)) + return "serial: integer expected"; + if (message.issuer != null && message.hasOwnProperty("issuer")) + if (!$util.isString(message.issuer)) + return "issuer: string expected"; + if (message.expires != null && message.hasOwnProperty("expires")) + if (!$util.isInteger(message.expires) && !(message.expires && $util.isInteger(message.expires.low) && $util.isInteger(message.expires.high))) + return "expires: integer|Long expected"; + if (message.subject != null && message.hasOwnProperty("subject")) + if (!$util.isString(message.subject)) + return "subject: string expected"; + if (message.key != null && message.hasOwnProperty("key")) + if (!(message.key && typeof message.key.length === "number" || $util.isString(message.key))) + return "key: buffer expected"; + return null; + }; + + /** + * Creates a Details message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Details + * @static + * @param {Object.} object Plain object + * @returns {proto.Details} Details + */ + Details.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Details) + return object; + var message = new $root.proto.Details(); + if (object.serial != null) + message.serial = object.serial >>> 0; + if (object.issuer != null) + message.issuer = String(object.issuer); + if (object.expires != null) + if ($util.Long) + (message.expires = $util.Long.fromValue(object.expires)).unsigned = true; + else if (typeof object.expires === "string") + message.expires = parseInt(object.expires, 10); + else if (typeof object.expires === "number") + message.expires = object.expires; + else if (typeof object.expires === "object") + message.expires = new $util.LongBits(object.expires.low >>> 0, object.expires.high >>> 0).toNumber(true); + if (object.subject != null) + message.subject = String(object.subject); + if (object.key != null) + if (typeof object.key === "string") + $util.base64.decode(object.key, message.key = $util.newBuffer($util.base64.length(object.key)), 0); + else if (object.key.length) + message.key = object.key; + return message; + }; + + /** + * Creates a plain object from a Details message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Details + * @static + * @param {proto.Details} message Details + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Details.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.serial = 0; + object.issuer = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.expires = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.expires = options.longs === String ? "0" : 0; + object.subject = ""; + if (options.bytes === String) + object.key = ""; + else { + object.key = []; + if (options.bytes !== Array) + object.key = $util.newBuffer(object.key); + } + } + if (message.serial != null && message.hasOwnProperty("serial")) + object.serial = message.serial; + if (message.issuer != null && message.hasOwnProperty("issuer")) + object.issuer = message.issuer; + if (message.expires != null && message.hasOwnProperty("expires")) + if (typeof message.expires === "number") + object.expires = options.longs === String ? String(message.expires) : message.expires; + else + object.expires = options.longs === String ? $util.Long.prototype.toString.call(message.expires) : options.longs === Number ? new $util.LongBits(message.expires.low >>> 0, message.expires.high >>> 0).toNumber(true) : message.expires; + if (message.subject != null && message.hasOwnProperty("subject")) + object.subject = message.subject; + if (message.key != null && message.hasOwnProperty("key")) + object.key = options.bytes === String ? $util.base64.encode(message.key, 0, message.key.length) : options.bytes === Array ? Array.prototype.slice.call(message.key) : message.key; + return object; + }; + + /** + * Converts this Details to JSON. + * @function toJSON + * @memberof proto.Details + * @instance + * @returns {Object.} JSON object + */ + Details.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Details; + })(); + proto.NoiseCertificate = (function() { /** @@ -4597,6 +4940,7 @@ $root.proto = (function() { * @property {proto.BizIdentityInfo.BizIdentityInfoHostStorageType|null} [hostStorage] BizIdentityInfo hostStorage * @property {proto.BizIdentityInfo.BizIdentityInfoActualActorsType|null} [actualActors] BizIdentityInfo actualActors * @property {number|Long|null} [privacyModeTs] BizIdentityInfo privacyModeTs + * @property {number|Long|null} [featureControls] BizIdentityInfo featureControls */ /** @@ -4670,6 +5014,14 @@ $root.proto = (function() { */ BizIdentityInfo.prototype.privacyModeTs = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + /** + * BizIdentityInfo featureControls. + * @member {number|Long} featureControls + * @memberof proto.BizIdentityInfo + * @instance + */ + BizIdentityInfo.prototype.featureControls = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + /** * Creates a new BizIdentityInfo instance using the specified properties. * @function create @@ -4708,6 +5060,8 @@ $root.proto = (function() { writer.uint32(/* id 6, wireType 0 =*/48).int32(message.actualActors); if (message.privacyModeTs != null && Object.hasOwnProperty.call(message, "privacyModeTs")) writer.uint32(/* id 7, wireType 0 =*/56).uint64(message.privacyModeTs); + if (message.featureControls != null && Object.hasOwnProperty.call(message, "featureControls")) + writer.uint32(/* id 8, wireType 0 =*/64).uint64(message.featureControls); return writer; }; @@ -4763,6 +5117,9 @@ $root.proto = (function() { case 7: message.privacyModeTs = reader.uint64(); break; + case 8: + message.featureControls = reader.uint64(); + break; default: reader.skipType(tag & 7); break; @@ -4837,6 +5194,9 @@ $root.proto = (function() { if (message.privacyModeTs != null && message.hasOwnProperty("privacyModeTs")) if (!$util.isInteger(message.privacyModeTs) && !(message.privacyModeTs && $util.isInteger(message.privacyModeTs.low) && $util.isInteger(message.privacyModeTs.high))) return "privacyModeTs: integer|Long expected"; + if (message.featureControls != null && message.hasOwnProperty("featureControls")) + if (!$util.isInteger(message.featureControls) && !(message.featureControls && $util.isInteger(message.featureControls.low) && $util.isInteger(message.featureControls.high))) + return "featureControls: integer|Long expected"; return null; }; @@ -4904,6 +5264,15 @@ $root.proto = (function() { message.privacyModeTs = object.privacyModeTs; else if (typeof object.privacyModeTs === "object") message.privacyModeTs = new $util.LongBits(object.privacyModeTs.low >>> 0, object.privacyModeTs.high >>> 0).toNumber(true); + if (object.featureControls != null) + if ($util.Long) + (message.featureControls = $util.Long.fromValue(object.featureControls)).unsigned = true; + else if (typeof object.featureControls === "string") + message.featureControls = parseInt(object.featureControls, 10); + else if (typeof object.featureControls === "number") + message.featureControls = object.featureControls; + else if (typeof object.featureControls === "object") + message.featureControls = new $util.LongBits(object.featureControls.low >>> 0, object.featureControls.high >>> 0).toNumber(true); return message; }; @@ -4932,6 +5301,11 @@ $root.proto = (function() { object.privacyModeTs = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; } else object.privacyModeTs = options.longs === String ? "0" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.featureControls = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.featureControls = options.longs === String ? "0" : 0; } if (message.vlevel != null && message.hasOwnProperty("vlevel")) object.vlevel = options.enums === String ? $root.proto.BizIdentityInfo.BizIdentityInfoVerifiedLevelValue[message.vlevel] : message.vlevel; @@ -4950,6 +5324,11 @@ $root.proto = (function() { object.privacyModeTs = options.longs === String ? String(message.privacyModeTs) : message.privacyModeTs; else object.privacyModeTs = options.longs === String ? $util.Long.prototype.toString.call(message.privacyModeTs) : options.longs === Number ? new $util.LongBits(message.privacyModeTs.low >>> 0, message.privacyModeTs.high >>> 0).toNumber(true) : message.privacyModeTs; + if (message.featureControls != null && message.hasOwnProperty("featureControls")) + if (typeof message.featureControls === "number") + object.featureControls = options.longs === String ? String(message.featureControls) : message.featureControls; + else + object.featureControls = options.longs === String ? $util.Long.prototype.toString.call(message.featureControls) : options.longs === Number ? new $util.LongBits(message.featureControls.low >>> 0, message.featureControls.high >>> 0).toNumber(true) : message.featureControls; return object; }; @@ -5219,7 +5598,6 @@ $root.proto = (function() { default: return "accountType: enum value expected"; case 0: - case 1: break; } return null; @@ -5272,10 +5650,6 @@ $root.proto = (function() { case 0: message.accountType = 0; break; - case "PAGE": - case 1: - message.accountType = 1; - break; } return message; }; @@ -5357,12 +5731,10 @@ $root.proto = (function() { * @name proto.BizAccountLinkInfo.BizAccountLinkInfoAccountType * @enum {number} * @property {number} ENTERPRISE=0 ENTERPRISE value - * @property {number} PAGE=1 PAGE value */ BizAccountLinkInfo.BizAccountLinkInfoAccountType = (function() { var valuesById = {}, values = Object.create(valuesById); values[valuesById[0] = "ENTERPRISE"] = 0; - values[valuesById[1] = "PAGE"] = 1; return values; })(); @@ -9200,6 +9572,362 @@ $root.proto = (function() { return RecentEmojiWeightsAction; })(); + proto.FavoriteStickerAction = (function() { + + /** + * Properties of a FavoriteStickerAction. + * @memberof proto + * @interface IFavoriteStickerAction + * @property {string|null} [directPath] FavoriteStickerAction directPath + * @property {string|null} [lastUploadTimestamp] FavoriteStickerAction lastUploadTimestamp + * @property {string|null} [handle] FavoriteStickerAction handle + * @property {string|null} [encFilehash] FavoriteStickerAction encFilehash + * @property {string|null} [stickerHashWithoutMeta] FavoriteStickerAction stickerHashWithoutMeta + * @property {string|null} [mediaKey] FavoriteStickerAction mediaKey + * @property {number|Long|null} [mediaKeyTimestamp] FavoriteStickerAction mediaKeyTimestamp + * @property {boolean|null} [isFavorite] FavoriteStickerAction isFavorite + */ + + /** + * Constructs a new FavoriteStickerAction. + * @memberof proto + * @classdesc Represents a FavoriteStickerAction. + * @implements IFavoriteStickerAction + * @constructor + * @param {proto.IFavoriteStickerAction=} [properties] Properties to set + */ + function FavoriteStickerAction(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * FavoriteStickerAction directPath. + * @member {string} directPath + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.directPath = ""; + + /** + * FavoriteStickerAction lastUploadTimestamp. + * @member {string} lastUploadTimestamp + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.lastUploadTimestamp = ""; + + /** + * FavoriteStickerAction handle. + * @member {string} handle + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.handle = ""; + + /** + * FavoriteStickerAction encFilehash. + * @member {string} encFilehash + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.encFilehash = ""; + + /** + * FavoriteStickerAction stickerHashWithoutMeta. + * @member {string} stickerHashWithoutMeta + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.stickerHashWithoutMeta = ""; + + /** + * FavoriteStickerAction mediaKey. + * @member {string} mediaKey + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.mediaKey = ""; + + /** + * FavoriteStickerAction mediaKeyTimestamp. + * @member {number|Long} mediaKeyTimestamp + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.mediaKeyTimestamp = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * FavoriteStickerAction isFavorite. + * @member {boolean} isFavorite + * @memberof proto.FavoriteStickerAction + * @instance + */ + FavoriteStickerAction.prototype.isFavorite = false; + + /** + * Creates a new FavoriteStickerAction instance using the specified properties. + * @function create + * @memberof proto.FavoriteStickerAction + * @static + * @param {proto.IFavoriteStickerAction=} [properties] Properties to set + * @returns {proto.FavoriteStickerAction} FavoriteStickerAction instance + */ + FavoriteStickerAction.create = function create(properties) { + return new FavoriteStickerAction(properties); + }; + + /** + * Encodes the specified FavoriteStickerAction message. Does not implicitly {@link proto.FavoriteStickerAction.verify|verify} messages. + * @function encode + * @memberof proto.FavoriteStickerAction + * @static + * @param {proto.IFavoriteStickerAction} message FavoriteStickerAction message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + FavoriteStickerAction.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.directPath != null && Object.hasOwnProperty.call(message, "directPath")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.directPath); + if (message.lastUploadTimestamp != null && Object.hasOwnProperty.call(message, "lastUploadTimestamp")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.lastUploadTimestamp); + if (message.handle != null && Object.hasOwnProperty.call(message, "handle")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.handle); + if (message.encFilehash != null && Object.hasOwnProperty.call(message, "encFilehash")) + writer.uint32(/* id 4, wireType 2 =*/34).string(message.encFilehash); + if (message.stickerHashWithoutMeta != null && Object.hasOwnProperty.call(message, "stickerHashWithoutMeta")) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.stickerHashWithoutMeta); + if (message.mediaKey != null && Object.hasOwnProperty.call(message, "mediaKey")) + writer.uint32(/* id 6, wireType 2 =*/50).string(message.mediaKey); + if (message.mediaKeyTimestamp != null && Object.hasOwnProperty.call(message, "mediaKeyTimestamp")) + writer.uint32(/* id 7, wireType 0 =*/56).int64(message.mediaKeyTimestamp); + if (message.isFavorite != null && Object.hasOwnProperty.call(message, "isFavorite")) + writer.uint32(/* id 8, wireType 0 =*/64).bool(message.isFavorite); + return writer; + }; + + /** + * Encodes the specified FavoriteStickerAction message, length delimited. Does not implicitly {@link proto.FavoriteStickerAction.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.FavoriteStickerAction + * @static + * @param {proto.IFavoriteStickerAction} message FavoriteStickerAction message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + FavoriteStickerAction.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a FavoriteStickerAction message from the specified reader or buffer. + * @function decode + * @memberof proto.FavoriteStickerAction + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.FavoriteStickerAction} FavoriteStickerAction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + FavoriteStickerAction.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.FavoriteStickerAction(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.directPath = reader.string(); + break; + case 2: + message.lastUploadTimestamp = reader.string(); + break; + case 3: + message.handle = reader.string(); + break; + case 4: + message.encFilehash = reader.string(); + break; + case 5: + message.stickerHashWithoutMeta = reader.string(); + break; + case 6: + message.mediaKey = reader.string(); + break; + case 7: + message.mediaKeyTimestamp = reader.int64(); + break; + case 8: + message.isFavorite = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a FavoriteStickerAction message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.FavoriteStickerAction + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.FavoriteStickerAction} FavoriteStickerAction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + FavoriteStickerAction.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a FavoriteStickerAction message. + * @function verify + * @memberof proto.FavoriteStickerAction + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + FavoriteStickerAction.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.directPath != null && message.hasOwnProperty("directPath")) + if (!$util.isString(message.directPath)) + return "directPath: string expected"; + if (message.lastUploadTimestamp != null && message.hasOwnProperty("lastUploadTimestamp")) + if (!$util.isString(message.lastUploadTimestamp)) + return "lastUploadTimestamp: string expected"; + if (message.handle != null && message.hasOwnProperty("handle")) + if (!$util.isString(message.handle)) + return "handle: string expected"; + if (message.encFilehash != null && message.hasOwnProperty("encFilehash")) + if (!$util.isString(message.encFilehash)) + return "encFilehash: string expected"; + if (message.stickerHashWithoutMeta != null && message.hasOwnProperty("stickerHashWithoutMeta")) + if (!$util.isString(message.stickerHashWithoutMeta)) + return "stickerHashWithoutMeta: string expected"; + if (message.mediaKey != null && message.hasOwnProperty("mediaKey")) + if (!$util.isString(message.mediaKey)) + return "mediaKey: string expected"; + if (message.mediaKeyTimestamp != null && message.hasOwnProperty("mediaKeyTimestamp")) + if (!$util.isInteger(message.mediaKeyTimestamp) && !(message.mediaKeyTimestamp && $util.isInteger(message.mediaKeyTimestamp.low) && $util.isInteger(message.mediaKeyTimestamp.high))) + return "mediaKeyTimestamp: integer|Long expected"; + if (message.isFavorite != null && message.hasOwnProperty("isFavorite")) + if (typeof message.isFavorite !== "boolean") + return "isFavorite: boolean expected"; + return null; + }; + + /** + * Creates a FavoriteStickerAction message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.FavoriteStickerAction + * @static + * @param {Object.} object Plain object + * @returns {proto.FavoriteStickerAction} FavoriteStickerAction + */ + FavoriteStickerAction.fromObject = function fromObject(object) { + if (object instanceof $root.proto.FavoriteStickerAction) + return object; + var message = new $root.proto.FavoriteStickerAction(); + if (object.directPath != null) + message.directPath = String(object.directPath); + if (object.lastUploadTimestamp != null) + message.lastUploadTimestamp = String(object.lastUploadTimestamp); + if (object.handle != null) + message.handle = String(object.handle); + if (object.encFilehash != null) + message.encFilehash = String(object.encFilehash); + if (object.stickerHashWithoutMeta != null) + message.stickerHashWithoutMeta = String(object.stickerHashWithoutMeta); + if (object.mediaKey != null) + message.mediaKey = String(object.mediaKey); + if (object.mediaKeyTimestamp != null) + if ($util.Long) + (message.mediaKeyTimestamp = $util.Long.fromValue(object.mediaKeyTimestamp)).unsigned = false; + else if (typeof object.mediaKeyTimestamp === "string") + message.mediaKeyTimestamp = parseInt(object.mediaKeyTimestamp, 10); + else if (typeof object.mediaKeyTimestamp === "number") + message.mediaKeyTimestamp = object.mediaKeyTimestamp; + else if (typeof object.mediaKeyTimestamp === "object") + message.mediaKeyTimestamp = new $util.LongBits(object.mediaKeyTimestamp.low >>> 0, object.mediaKeyTimestamp.high >>> 0).toNumber(); + if (object.isFavorite != null) + message.isFavorite = Boolean(object.isFavorite); + return message; + }; + + /** + * Creates a plain object from a FavoriteStickerAction message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.FavoriteStickerAction + * @static + * @param {proto.FavoriteStickerAction} message FavoriteStickerAction + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + FavoriteStickerAction.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.directPath = ""; + object.lastUploadTimestamp = ""; + object.handle = ""; + object.encFilehash = ""; + object.stickerHashWithoutMeta = ""; + object.mediaKey = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.mediaKeyTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.mediaKeyTimestamp = options.longs === String ? "0" : 0; + object.isFavorite = false; + } + if (message.directPath != null && message.hasOwnProperty("directPath")) + object.directPath = message.directPath; + if (message.lastUploadTimestamp != null && message.hasOwnProperty("lastUploadTimestamp")) + object.lastUploadTimestamp = message.lastUploadTimestamp; + if (message.handle != null && message.hasOwnProperty("handle")) + object.handle = message.handle; + if (message.encFilehash != null && message.hasOwnProperty("encFilehash")) + object.encFilehash = message.encFilehash; + if (message.stickerHashWithoutMeta != null && message.hasOwnProperty("stickerHashWithoutMeta")) + object.stickerHashWithoutMeta = message.stickerHashWithoutMeta; + if (message.mediaKey != null && message.hasOwnProperty("mediaKey")) + object.mediaKey = message.mediaKey; + if (message.mediaKeyTimestamp != null && message.hasOwnProperty("mediaKeyTimestamp")) + if (typeof message.mediaKeyTimestamp === "number") + object.mediaKeyTimestamp = options.longs === String ? String(message.mediaKeyTimestamp) : message.mediaKeyTimestamp; + else + object.mediaKeyTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.mediaKeyTimestamp) : options.longs === Number ? new $util.LongBits(message.mediaKeyTimestamp.low >>> 0, message.mediaKeyTimestamp.high >>> 0).toNumber() : message.mediaKeyTimestamp; + if (message.isFavorite != null && message.hasOwnProperty("isFavorite")) + object.isFavorite = message.isFavorite; + return object; + }; + + /** + * Converts this FavoriteStickerAction to JSON. + * @function toJSON + * @memberof proto.FavoriteStickerAction + * @instance + * @returns {Object.} JSON object + */ + FavoriteStickerAction.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return FavoriteStickerAction; + })(); + proto.ArchiveChatAction = (function() { /** @@ -11123,6 +11851,396 @@ $root.proto = (function() { return KeyExpiration; })(); + proto.PrimaryFeature = (function() { + + /** + * Properties of a PrimaryFeature. + * @memberof proto + * @interface IPrimaryFeature + * @property {Array.|null} [flags] PrimaryFeature flags + */ + + /** + * Constructs a new PrimaryFeature. + * @memberof proto + * @classdesc Represents a PrimaryFeature. + * @implements IPrimaryFeature + * @constructor + * @param {proto.IPrimaryFeature=} [properties] Properties to set + */ + function PrimaryFeature(properties) { + this.flags = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * PrimaryFeature flags. + * @member {Array.} flags + * @memberof proto.PrimaryFeature + * @instance + */ + PrimaryFeature.prototype.flags = $util.emptyArray; + + /** + * Creates a new PrimaryFeature instance using the specified properties. + * @function create + * @memberof proto.PrimaryFeature + * @static + * @param {proto.IPrimaryFeature=} [properties] Properties to set + * @returns {proto.PrimaryFeature} PrimaryFeature instance + */ + PrimaryFeature.create = function create(properties) { + return new PrimaryFeature(properties); + }; + + /** + * Encodes the specified PrimaryFeature message. Does not implicitly {@link proto.PrimaryFeature.verify|verify} messages. + * @function encode + * @memberof proto.PrimaryFeature + * @static + * @param {proto.IPrimaryFeature} message PrimaryFeature message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + PrimaryFeature.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.flags != null && message.flags.length) + for (var i = 0; i < message.flags.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.flags[i]); + return writer; + }; + + /** + * Encodes the specified PrimaryFeature message, length delimited. Does not implicitly {@link proto.PrimaryFeature.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.PrimaryFeature + * @static + * @param {proto.IPrimaryFeature} message PrimaryFeature message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + PrimaryFeature.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a PrimaryFeature message from the specified reader or buffer. + * @function decode + * @memberof proto.PrimaryFeature + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.PrimaryFeature} PrimaryFeature + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + PrimaryFeature.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.PrimaryFeature(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.flags && message.flags.length)) + message.flags = []; + message.flags.push(reader.string()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a PrimaryFeature message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.PrimaryFeature + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.PrimaryFeature} PrimaryFeature + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + PrimaryFeature.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a PrimaryFeature message. + * @function verify + * @memberof proto.PrimaryFeature + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + PrimaryFeature.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.flags != null && message.hasOwnProperty("flags")) { + if (!Array.isArray(message.flags)) + return "flags: array expected"; + for (var i = 0; i < message.flags.length; ++i) + if (!$util.isString(message.flags[i])) + return "flags: string[] expected"; + } + return null; + }; + + /** + * Creates a PrimaryFeature message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.PrimaryFeature + * @static + * @param {Object.} object Plain object + * @returns {proto.PrimaryFeature} PrimaryFeature + */ + PrimaryFeature.fromObject = function fromObject(object) { + if (object instanceof $root.proto.PrimaryFeature) + return object; + var message = new $root.proto.PrimaryFeature(); + if (object.flags) { + if (!Array.isArray(object.flags)) + throw TypeError(".proto.PrimaryFeature.flags: array expected"); + message.flags = []; + for (var i = 0; i < object.flags.length; ++i) + message.flags[i] = String(object.flags[i]); + } + return message; + }; + + /** + * Creates a plain object from a PrimaryFeature message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.PrimaryFeature + * @static + * @param {proto.PrimaryFeature} message PrimaryFeature + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + PrimaryFeature.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.flags = []; + if (message.flags && message.flags.length) { + object.flags = []; + for (var j = 0; j < message.flags.length; ++j) + object.flags[j] = message.flags[j]; + } + return object; + }; + + /** + * Converts this PrimaryFeature to JSON. + * @function toJSON + * @memberof proto.PrimaryFeature + * @instance + * @returns {Object.} JSON object + */ + PrimaryFeature.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return PrimaryFeature; + })(); + + proto.AndroidUnsupportedActions = (function() { + + /** + * Properties of an AndroidUnsupportedActions. + * @memberof proto + * @interface IAndroidUnsupportedActions + * @property {boolean|null} [allowed] AndroidUnsupportedActions allowed + */ + + /** + * Constructs a new AndroidUnsupportedActions. + * @memberof proto + * @classdesc Represents an AndroidUnsupportedActions. + * @implements IAndroidUnsupportedActions + * @constructor + * @param {proto.IAndroidUnsupportedActions=} [properties] Properties to set + */ + function AndroidUnsupportedActions(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * AndroidUnsupportedActions allowed. + * @member {boolean} allowed + * @memberof proto.AndroidUnsupportedActions + * @instance + */ + AndroidUnsupportedActions.prototype.allowed = false; + + /** + * Creates a new AndroidUnsupportedActions instance using the specified properties. + * @function create + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {proto.IAndroidUnsupportedActions=} [properties] Properties to set + * @returns {proto.AndroidUnsupportedActions} AndroidUnsupportedActions instance + */ + AndroidUnsupportedActions.create = function create(properties) { + return new AndroidUnsupportedActions(properties); + }; + + /** + * Encodes the specified AndroidUnsupportedActions message. Does not implicitly {@link proto.AndroidUnsupportedActions.verify|verify} messages. + * @function encode + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {proto.IAndroidUnsupportedActions} message AndroidUnsupportedActions message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + AndroidUnsupportedActions.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.allowed != null && Object.hasOwnProperty.call(message, "allowed")) + writer.uint32(/* id 1, wireType 0 =*/8).bool(message.allowed); + return writer; + }; + + /** + * Encodes the specified AndroidUnsupportedActions message, length delimited. Does not implicitly {@link proto.AndroidUnsupportedActions.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {proto.IAndroidUnsupportedActions} message AndroidUnsupportedActions message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + AndroidUnsupportedActions.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an AndroidUnsupportedActions message from the specified reader or buffer. + * @function decode + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.AndroidUnsupportedActions} AndroidUnsupportedActions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + AndroidUnsupportedActions.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.AndroidUnsupportedActions(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.allowed = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an AndroidUnsupportedActions message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.AndroidUnsupportedActions} AndroidUnsupportedActions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + AndroidUnsupportedActions.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an AndroidUnsupportedActions message. + * @function verify + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + AndroidUnsupportedActions.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.allowed != null && message.hasOwnProperty("allowed")) + if (typeof message.allowed !== "boolean") + return "allowed: boolean expected"; + return null; + }; + + /** + * Creates an AndroidUnsupportedActions message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {Object.} object Plain object + * @returns {proto.AndroidUnsupportedActions} AndroidUnsupportedActions + */ + AndroidUnsupportedActions.fromObject = function fromObject(object) { + if (object instanceof $root.proto.AndroidUnsupportedActions) + return object; + var message = new $root.proto.AndroidUnsupportedActions(); + if (object.allowed != null) + message.allowed = Boolean(object.allowed); + return message; + }; + + /** + * Creates a plain object from an AndroidUnsupportedActions message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.AndroidUnsupportedActions + * @static + * @param {proto.AndroidUnsupportedActions} message AndroidUnsupportedActions + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + AndroidUnsupportedActions.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.allowed = false; + if (message.allowed != null && message.hasOwnProperty("allowed")) + object.allowed = message.allowed; + return object; + }; + + /** + * Converts this AndroidUnsupportedActions to JSON. + * @function toJSON + * @memberof proto.AndroidUnsupportedActions + * @instance + * @returns {Object.} JSON object + */ + AndroidUnsupportedActions.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return AndroidUnsupportedActions; + })(); + proto.SyncActionValue = (function() { /** @@ -11150,6 +12268,9 @@ $root.proto = (function() { * @property {proto.IClearChatAction|null} [clearChatAction] SyncActionValue clearChatAction * @property {proto.IDeleteChatAction|null} [deleteChatAction] SyncActionValue deleteChatAction * @property {proto.IUnarchiveChatsSetting|null} [unarchiveChatsSetting] SyncActionValue unarchiveChatsSetting + * @property {proto.IPrimaryFeature|null} [primaryFeature] SyncActionValue primaryFeature + * @property {proto.IFavoriteStickerAction|null} [favoriteStickerAction] SyncActionValue favoriteStickerAction + * @property {proto.IAndroidUnsupportedActions|null} [androidUnsupportedActions] SyncActionValue androidUnsupportedActions */ /** @@ -11335,6 +12456,30 @@ $root.proto = (function() { */ SyncActionValue.prototype.unarchiveChatsSetting = null; + /** + * SyncActionValue primaryFeature. + * @member {proto.IPrimaryFeature|null|undefined} primaryFeature + * @memberof proto.SyncActionValue + * @instance + */ + SyncActionValue.prototype.primaryFeature = null; + + /** + * SyncActionValue favoriteStickerAction. + * @member {proto.IFavoriteStickerAction|null|undefined} favoriteStickerAction + * @memberof proto.SyncActionValue + * @instance + */ + SyncActionValue.prototype.favoriteStickerAction = null; + + /** + * SyncActionValue androidUnsupportedActions. + * @member {proto.IAndroidUnsupportedActions|null|undefined} androidUnsupportedActions + * @memberof proto.SyncActionValue + * @instance + */ + SyncActionValue.prototype.androidUnsupportedActions = null; + /** * Creates a new SyncActionValue instance using the specified properties. * @function create @@ -11401,6 +12546,12 @@ $root.proto = (function() { $root.proto.DeleteChatAction.encode(message.deleteChatAction, writer.uint32(/* id 22, wireType 2 =*/178).fork()).ldelim(); if (message.unarchiveChatsSetting != null && Object.hasOwnProperty.call(message, "unarchiveChatsSetting")) $root.proto.UnarchiveChatsSetting.encode(message.unarchiveChatsSetting, writer.uint32(/* id 23, wireType 2 =*/186).fork()).ldelim(); + if (message.primaryFeature != null && Object.hasOwnProperty.call(message, "primaryFeature")) + $root.proto.PrimaryFeature.encode(message.primaryFeature, writer.uint32(/* id 24, wireType 2 =*/194).fork()).ldelim(); + if (message.favoriteStickerAction != null && Object.hasOwnProperty.call(message, "favoriteStickerAction")) + $root.proto.FavoriteStickerAction.encode(message.favoriteStickerAction, writer.uint32(/* id 25, wireType 2 =*/202).fork()).ldelim(); + if (message.androidUnsupportedActions != null && Object.hasOwnProperty.call(message, "androidUnsupportedActions")) + $root.proto.AndroidUnsupportedActions.encode(message.androidUnsupportedActions, writer.uint32(/* id 26, wireType 2 =*/210).fork()).ldelim(); return writer; }; @@ -11498,6 +12649,15 @@ $root.proto = (function() { case 23: message.unarchiveChatsSetting = $root.proto.UnarchiveChatsSetting.decode(reader, reader.uint32()); break; + case 24: + message.primaryFeature = $root.proto.PrimaryFeature.decode(reader, reader.uint32()); + break; + case 25: + message.favoriteStickerAction = $root.proto.FavoriteStickerAction.decode(reader, reader.uint32()); + break; + case 26: + message.androidUnsupportedActions = $root.proto.AndroidUnsupportedActions.decode(reader, reader.uint32()); + break; default: reader.skipType(tag & 7); break; @@ -11636,6 +12796,21 @@ $root.proto = (function() { if (error) return "unarchiveChatsSetting." + error; } + if (message.primaryFeature != null && message.hasOwnProperty("primaryFeature")) { + var error = $root.proto.PrimaryFeature.verify(message.primaryFeature); + if (error) + return "primaryFeature." + error; + } + if (message.favoriteStickerAction != null && message.hasOwnProperty("favoriteStickerAction")) { + var error = $root.proto.FavoriteStickerAction.verify(message.favoriteStickerAction); + if (error) + return "favoriteStickerAction." + error; + } + if (message.androidUnsupportedActions != null && message.hasOwnProperty("androidUnsupportedActions")) { + var error = $root.proto.AndroidUnsupportedActions.verify(message.androidUnsupportedActions); + if (error) + return "androidUnsupportedActions." + error; + } return null; }; @@ -11760,6 +12935,21 @@ $root.proto = (function() { throw TypeError(".proto.SyncActionValue.unarchiveChatsSetting: object expected"); message.unarchiveChatsSetting = $root.proto.UnarchiveChatsSetting.fromObject(object.unarchiveChatsSetting); } + if (object.primaryFeature != null) { + if (typeof object.primaryFeature !== "object") + throw TypeError(".proto.SyncActionValue.primaryFeature: object expected"); + message.primaryFeature = $root.proto.PrimaryFeature.fromObject(object.primaryFeature); + } + if (object.favoriteStickerAction != null) { + if (typeof object.favoriteStickerAction !== "object") + throw TypeError(".proto.SyncActionValue.favoriteStickerAction: object expected"); + message.favoriteStickerAction = $root.proto.FavoriteStickerAction.fromObject(object.favoriteStickerAction); + } + if (object.androidUnsupportedActions != null) { + if (typeof object.androidUnsupportedActions !== "object") + throw TypeError(".proto.SyncActionValue.androidUnsupportedActions: object expected"); + message.androidUnsupportedActions = $root.proto.AndroidUnsupportedActions.fromObject(object.androidUnsupportedActions); + } return message; }; @@ -11802,6 +12992,9 @@ $root.proto = (function() { object.clearChatAction = null; object.deleteChatAction = null; object.unarchiveChatsSetting = null; + object.primaryFeature = null; + object.favoriteStickerAction = null; + object.androidUnsupportedActions = null; } if (message.timestamp != null && message.hasOwnProperty("timestamp")) if (typeof message.timestamp === "number") @@ -11848,6 +13041,12 @@ $root.proto = (function() { object.deleteChatAction = $root.proto.DeleteChatAction.toObject(message.deleteChatAction, options); if (message.unarchiveChatsSetting != null && message.hasOwnProperty("unarchiveChatsSetting")) object.unarchiveChatsSetting = $root.proto.UnarchiveChatsSetting.toObject(message.unarchiveChatsSetting, options); + if (message.primaryFeature != null && message.hasOwnProperty("primaryFeature")) + object.primaryFeature = $root.proto.PrimaryFeature.toObject(message.primaryFeature, options); + if (message.favoriteStickerAction != null && message.hasOwnProperty("favoriteStickerAction")) + object.favoriteStickerAction = $root.proto.FavoriteStickerAction.toObject(message.favoriteStickerAction, options); + if (message.androidUnsupportedActions != null && message.hasOwnProperty("androidUnsupportedActions")) + object.androidUnsupportedActions = $root.proto.AndroidUnsupportedActions.toObject(message.androidUnsupportedActions, options); return object; }; @@ -15499,7 +16698,6 @@ $root.proto = (function() { * @interface IMsgOpaqueData * @property {string|null} [body] MsgOpaqueData body * @property {string|null} [caption] MsgOpaqueData caption - * @property {string|null} [clientUrl] MsgOpaqueData clientUrl * @property {number|null} [lng] MsgOpaqueData lng * @property {number|null} [lat] MsgOpaqueData lat * @property {number|null} [paymentAmount1000] MsgOpaqueData paymentAmount1000 @@ -15508,6 +16706,9 @@ $root.proto = (function() { * @property {string|null} [matchedText] MsgOpaqueData matchedText * @property {string|null} [title] MsgOpaqueData title * @property {string|null} [description] MsgOpaqueData description + * @property {Uint8Array|null} [futureproofBuffer] MsgOpaqueData futureproofBuffer + * @property {string|null} [clientUrl] MsgOpaqueData clientUrl + * @property {string|null} [loc] MsgOpaqueData loc */ /** @@ -15541,14 +16742,6 @@ $root.proto = (function() { */ MsgOpaqueData.prototype.caption = ""; - /** - * MsgOpaqueData clientUrl. - * @member {string} clientUrl - * @memberof proto.MsgOpaqueData - * @instance - */ - MsgOpaqueData.prototype.clientUrl = ""; - /** * MsgOpaqueData lng. * @member {number} lng @@ -15613,6 +16806,30 @@ $root.proto = (function() { */ MsgOpaqueData.prototype.description = ""; + /** + * MsgOpaqueData futureproofBuffer. + * @member {Uint8Array} futureproofBuffer + * @memberof proto.MsgOpaqueData + * @instance + */ + MsgOpaqueData.prototype.futureproofBuffer = $util.newBuffer([]); + + /** + * MsgOpaqueData clientUrl. + * @member {string} clientUrl + * @memberof proto.MsgOpaqueData + * @instance + */ + MsgOpaqueData.prototype.clientUrl = ""; + + /** + * MsgOpaqueData loc. + * @member {string} loc + * @memberof proto.MsgOpaqueData + * @instance + */ + MsgOpaqueData.prototype.loc = ""; + /** * Creates a new MsgOpaqueData instance using the specified properties. * @function create @@ -15641,8 +16858,6 @@ $root.proto = (function() { writer.uint32(/* id 1, wireType 2 =*/10).string(message.body); if (message.caption != null && Object.hasOwnProperty.call(message, "caption")) writer.uint32(/* id 3, wireType 2 =*/26).string(message.caption); - if (message.clientUrl != null && Object.hasOwnProperty.call(message, "clientUrl")) - writer.uint32(/* id 4, wireType 2 =*/34).string(message.clientUrl); if (message.lng != null && Object.hasOwnProperty.call(message, "lng")) writer.uint32(/* id 5, wireType 1 =*/41).double(message.lng); if (message.lat != null && Object.hasOwnProperty.call(message, "lat")) @@ -15659,6 +16874,12 @@ $root.proto = (function() { writer.uint32(/* id 12, wireType 2 =*/98).string(message.title); if (message.description != null && Object.hasOwnProperty.call(message, "description")) writer.uint32(/* id 13, wireType 2 =*/106).string(message.description); + if (message.futureproofBuffer != null && Object.hasOwnProperty.call(message, "futureproofBuffer")) + writer.uint32(/* id 14, wireType 2 =*/114).bytes(message.futureproofBuffer); + if (message.clientUrl != null && Object.hasOwnProperty.call(message, "clientUrl")) + writer.uint32(/* id 15, wireType 2 =*/122).string(message.clientUrl); + if (message.loc != null && Object.hasOwnProperty.call(message, "loc")) + writer.uint32(/* id 16, wireType 2 =*/130).string(message.loc); return writer; }; @@ -15699,9 +16920,6 @@ $root.proto = (function() { case 3: message.caption = reader.string(); break; - case 4: - message.clientUrl = reader.string(); - break; case 5: message.lng = reader.double(); break; @@ -15726,6 +16944,15 @@ $root.proto = (function() { case 13: message.description = reader.string(); break; + case 14: + message.futureproofBuffer = reader.bytes(); + break; + case 15: + message.clientUrl = reader.string(); + break; + case 16: + message.loc = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -15767,9 +16994,6 @@ $root.proto = (function() { if (message.caption != null && message.hasOwnProperty("caption")) if (!$util.isString(message.caption)) return "caption: string expected"; - if (message.clientUrl != null && message.hasOwnProperty("clientUrl")) - if (!$util.isString(message.clientUrl)) - return "clientUrl: string expected"; if (message.lng != null && message.hasOwnProperty("lng")) if (typeof message.lng !== "number") return "lng: number expected"; @@ -15794,6 +17018,15 @@ $root.proto = (function() { if (message.description != null && message.hasOwnProperty("description")) if (!$util.isString(message.description)) return "description: string expected"; + if (message.futureproofBuffer != null && message.hasOwnProperty("futureproofBuffer")) + if (!(message.futureproofBuffer && typeof message.futureproofBuffer.length === "number" || $util.isString(message.futureproofBuffer))) + return "futureproofBuffer: buffer expected"; + if (message.clientUrl != null && message.hasOwnProperty("clientUrl")) + if (!$util.isString(message.clientUrl)) + return "clientUrl: string expected"; + if (message.loc != null && message.hasOwnProperty("loc")) + if (!$util.isString(message.loc)) + return "loc: string expected"; return null; }; @@ -15813,8 +17046,6 @@ $root.proto = (function() { message.body = String(object.body); if (object.caption != null) message.caption = String(object.caption); - if (object.clientUrl != null) - message.clientUrl = String(object.clientUrl); if (object.lng != null) message.lng = Number(object.lng); if (object.lat != null) @@ -15831,6 +17062,15 @@ $root.proto = (function() { message.title = String(object.title); if (object.description != null) message.description = String(object.description); + if (object.futureproofBuffer != null) + if (typeof object.futureproofBuffer === "string") + $util.base64.decode(object.futureproofBuffer, message.futureproofBuffer = $util.newBuffer($util.base64.length(object.futureproofBuffer)), 0); + else if (object.futureproofBuffer.length) + message.futureproofBuffer = object.futureproofBuffer; + if (object.clientUrl != null) + message.clientUrl = String(object.clientUrl); + if (object.loc != null) + message.loc = String(object.loc); return message; }; @@ -15850,7 +17090,6 @@ $root.proto = (function() { if (options.defaults) { object.body = ""; object.caption = ""; - object.clientUrl = ""; object.lng = 0; object.lat = 0; object.paymentAmount1000 = 0; @@ -15859,13 +17098,20 @@ $root.proto = (function() { object.matchedText = ""; object.title = ""; object.description = ""; + if (options.bytes === String) + object.futureproofBuffer = ""; + else { + object.futureproofBuffer = []; + if (options.bytes !== Array) + object.futureproofBuffer = $util.newBuffer(object.futureproofBuffer); + } + object.clientUrl = ""; + object.loc = ""; } if (message.body != null && message.hasOwnProperty("body")) object.body = message.body; if (message.caption != null && message.hasOwnProperty("caption")) object.caption = message.caption; - if (message.clientUrl != null && message.hasOwnProperty("clientUrl")) - object.clientUrl = message.clientUrl; if (message.lng != null && message.hasOwnProperty("lng")) object.lng = options.json && !isFinite(message.lng) ? String(message.lng) : message.lng; if (message.lat != null && message.hasOwnProperty("lat")) @@ -15882,6 +17128,12 @@ $root.proto = (function() { object.title = message.title; if (message.description != null && message.hasOwnProperty("description")) object.description = message.description; + if (message.futureproofBuffer != null && message.hasOwnProperty("futureproofBuffer")) + object.futureproofBuffer = options.bytes === String ? $util.base64.encode(message.futureproofBuffer, 0, message.futureproofBuffer.length) : options.bytes === Array ? Array.prototype.slice.call(message.futureproofBuffer) : message.futureproofBuffer; + if (message.clientUrl != null && message.hasOwnProperty("clientUrl")) + object.clientUrl = message.clientUrl; + if (message.loc != null && message.hasOwnProperty("loc")) + object.loc = message.loc; return object; }; @@ -16119,6 +17371,720 @@ $root.proto = (function() { return MsgRowOpaqueData; })(); + proto.GlobalSettings = (function() { + + /** + * Properties of a GlobalSettings. + * @memberof proto + * @interface IGlobalSettings + * @property {proto.IWallpaperSettings|null} [lightThemeWallpaper] GlobalSettings lightThemeWallpaper + * @property {proto.MediaVisibility|null} [mediaVisibility] GlobalSettings mediaVisibility + * @property {proto.IWallpaperSettings|null} [darkThemeWallpaper] GlobalSettings darkThemeWallpaper + */ + + /** + * Constructs a new GlobalSettings. + * @memberof proto + * @classdesc Represents a GlobalSettings. + * @implements IGlobalSettings + * @constructor + * @param {proto.IGlobalSettings=} [properties] Properties to set + */ + function GlobalSettings(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GlobalSettings lightThemeWallpaper. + * @member {proto.IWallpaperSettings|null|undefined} lightThemeWallpaper + * @memberof proto.GlobalSettings + * @instance + */ + GlobalSettings.prototype.lightThemeWallpaper = null; + + /** + * GlobalSettings mediaVisibility. + * @member {proto.MediaVisibility} mediaVisibility + * @memberof proto.GlobalSettings + * @instance + */ + GlobalSettings.prototype.mediaVisibility = 0; + + /** + * GlobalSettings darkThemeWallpaper. + * @member {proto.IWallpaperSettings|null|undefined} darkThemeWallpaper + * @memberof proto.GlobalSettings + * @instance + */ + GlobalSettings.prototype.darkThemeWallpaper = null; + + /** + * Creates a new GlobalSettings instance using the specified properties. + * @function create + * @memberof proto.GlobalSettings + * @static + * @param {proto.IGlobalSettings=} [properties] Properties to set + * @returns {proto.GlobalSettings} GlobalSettings instance + */ + GlobalSettings.create = function create(properties) { + return new GlobalSettings(properties); + }; + + /** + * Encodes the specified GlobalSettings message. Does not implicitly {@link proto.GlobalSettings.verify|verify} messages. + * @function encode + * @memberof proto.GlobalSettings + * @static + * @param {proto.IGlobalSettings} message GlobalSettings message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GlobalSettings.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.lightThemeWallpaper != null && Object.hasOwnProperty.call(message, "lightThemeWallpaper")) + $root.proto.WallpaperSettings.encode(message.lightThemeWallpaper, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.mediaVisibility != null && Object.hasOwnProperty.call(message, "mediaVisibility")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.mediaVisibility); + if (message.darkThemeWallpaper != null && Object.hasOwnProperty.call(message, "darkThemeWallpaper")) + $root.proto.WallpaperSettings.encode(message.darkThemeWallpaper, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GlobalSettings message, length delimited. Does not implicitly {@link proto.GlobalSettings.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.GlobalSettings + * @static + * @param {proto.IGlobalSettings} message GlobalSettings message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GlobalSettings.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GlobalSettings message from the specified reader or buffer. + * @function decode + * @memberof proto.GlobalSettings + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.GlobalSettings} GlobalSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GlobalSettings.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.GlobalSettings(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.lightThemeWallpaper = $root.proto.WallpaperSettings.decode(reader, reader.uint32()); + break; + case 2: + message.mediaVisibility = reader.int32(); + break; + case 3: + message.darkThemeWallpaper = $root.proto.WallpaperSettings.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GlobalSettings message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.GlobalSettings + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.GlobalSettings} GlobalSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GlobalSettings.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GlobalSettings message. + * @function verify + * @memberof proto.GlobalSettings + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GlobalSettings.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.lightThemeWallpaper != null && message.hasOwnProperty("lightThemeWallpaper")) { + var error = $root.proto.WallpaperSettings.verify(message.lightThemeWallpaper); + if (error) + return "lightThemeWallpaper." + error; + } + if (message.mediaVisibility != null && message.hasOwnProperty("mediaVisibility")) + switch (message.mediaVisibility) { + default: + return "mediaVisibility: enum value expected"; + case 0: + case 1: + case 2: + break; + } + if (message.darkThemeWallpaper != null && message.hasOwnProperty("darkThemeWallpaper")) { + var error = $root.proto.WallpaperSettings.verify(message.darkThemeWallpaper); + if (error) + return "darkThemeWallpaper." + error; + } + return null; + }; + + /** + * Creates a GlobalSettings message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.GlobalSettings + * @static + * @param {Object.} object Plain object + * @returns {proto.GlobalSettings} GlobalSettings + */ + GlobalSettings.fromObject = function fromObject(object) { + if (object instanceof $root.proto.GlobalSettings) + return object; + var message = new $root.proto.GlobalSettings(); + if (object.lightThemeWallpaper != null) { + if (typeof object.lightThemeWallpaper !== "object") + throw TypeError(".proto.GlobalSettings.lightThemeWallpaper: object expected"); + message.lightThemeWallpaper = $root.proto.WallpaperSettings.fromObject(object.lightThemeWallpaper); + } + switch (object.mediaVisibility) { + case "DEFAULT": + case 0: + message.mediaVisibility = 0; + break; + case "OFF": + case 1: + message.mediaVisibility = 1; + break; + case "ON": + case 2: + message.mediaVisibility = 2; + break; + } + if (object.darkThemeWallpaper != null) { + if (typeof object.darkThemeWallpaper !== "object") + throw TypeError(".proto.GlobalSettings.darkThemeWallpaper: object expected"); + message.darkThemeWallpaper = $root.proto.WallpaperSettings.fromObject(object.darkThemeWallpaper); + } + return message; + }; + + /** + * Creates a plain object from a GlobalSettings message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.GlobalSettings + * @static + * @param {proto.GlobalSettings} message GlobalSettings + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GlobalSettings.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.lightThemeWallpaper = null; + object.mediaVisibility = options.enums === String ? "DEFAULT" : 0; + object.darkThemeWallpaper = null; + } + if (message.lightThemeWallpaper != null && message.hasOwnProperty("lightThemeWallpaper")) + object.lightThemeWallpaper = $root.proto.WallpaperSettings.toObject(message.lightThemeWallpaper, options); + if (message.mediaVisibility != null && message.hasOwnProperty("mediaVisibility")) + object.mediaVisibility = options.enums === String ? $root.proto.MediaVisibility[message.mediaVisibility] : message.mediaVisibility; + if (message.darkThemeWallpaper != null && message.hasOwnProperty("darkThemeWallpaper")) + object.darkThemeWallpaper = $root.proto.WallpaperSettings.toObject(message.darkThemeWallpaper, options); + return object; + }; + + /** + * Converts this GlobalSettings to JSON. + * @function toJSON + * @memberof proto.GlobalSettings + * @instance + * @returns {Object.} JSON object + */ + GlobalSettings.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GlobalSettings; + })(); + + proto.WallpaperSettings = (function() { + + /** + * Properties of a WallpaperSettings. + * @memberof proto + * @interface IWallpaperSettings + * @property {string|null} [filename] WallpaperSettings filename + * @property {number|null} [opacity] WallpaperSettings opacity + */ + + /** + * Constructs a new WallpaperSettings. + * @memberof proto + * @classdesc Represents a WallpaperSettings. + * @implements IWallpaperSettings + * @constructor + * @param {proto.IWallpaperSettings=} [properties] Properties to set + */ + function WallpaperSettings(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * WallpaperSettings filename. + * @member {string} filename + * @memberof proto.WallpaperSettings + * @instance + */ + WallpaperSettings.prototype.filename = ""; + + /** + * WallpaperSettings opacity. + * @member {number} opacity + * @memberof proto.WallpaperSettings + * @instance + */ + WallpaperSettings.prototype.opacity = 0; + + /** + * Creates a new WallpaperSettings instance using the specified properties. + * @function create + * @memberof proto.WallpaperSettings + * @static + * @param {proto.IWallpaperSettings=} [properties] Properties to set + * @returns {proto.WallpaperSettings} WallpaperSettings instance + */ + WallpaperSettings.create = function create(properties) { + return new WallpaperSettings(properties); + }; + + /** + * Encodes the specified WallpaperSettings message. Does not implicitly {@link proto.WallpaperSettings.verify|verify} messages. + * @function encode + * @memberof proto.WallpaperSettings + * @static + * @param {proto.IWallpaperSettings} message WallpaperSettings message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + WallpaperSettings.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.filename != null && Object.hasOwnProperty.call(message, "filename")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.filename); + if (message.opacity != null && Object.hasOwnProperty.call(message, "opacity")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.opacity); + return writer; + }; + + /** + * Encodes the specified WallpaperSettings message, length delimited. Does not implicitly {@link proto.WallpaperSettings.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.WallpaperSettings + * @static + * @param {proto.IWallpaperSettings} message WallpaperSettings message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + WallpaperSettings.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a WallpaperSettings message from the specified reader or buffer. + * @function decode + * @memberof proto.WallpaperSettings + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.WallpaperSettings} WallpaperSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + WallpaperSettings.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.WallpaperSettings(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.filename = reader.string(); + break; + case 2: + message.opacity = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a WallpaperSettings message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.WallpaperSettings + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.WallpaperSettings} WallpaperSettings + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + WallpaperSettings.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a WallpaperSettings message. + * @function verify + * @memberof proto.WallpaperSettings + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + WallpaperSettings.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.filename != null && message.hasOwnProperty("filename")) + if (!$util.isString(message.filename)) + return "filename: string expected"; + if (message.opacity != null && message.hasOwnProperty("opacity")) + if (!$util.isInteger(message.opacity)) + return "opacity: integer expected"; + return null; + }; + + /** + * Creates a WallpaperSettings message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.WallpaperSettings + * @static + * @param {Object.} object Plain object + * @returns {proto.WallpaperSettings} WallpaperSettings + */ + WallpaperSettings.fromObject = function fromObject(object) { + if (object instanceof $root.proto.WallpaperSettings) + return object; + var message = new $root.proto.WallpaperSettings(); + if (object.filename != null) + message.filename = String(object.filename); + if (object.opacity != null) + message.opacity = object.opacity >>> 0; + return message; + }; + + /** + * Creates a plain object from a WallpaperSettings message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.WallpaperSettings + * @static + * @param {proto.WallpaperSettings} message WallpaperSettings + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + WallpaperSettings.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.filename = ""; + object.opacity = 0; + } + if (message.filename != null && message.hasOwnProperty("filename")) + object.filename = message.filename; + if (message.opacity != null && message.hasOwnProperty("opacity")) + object.opacity = message.opacity; + return object; + }; + + /** + * Converts this WallpaperSettings to JSON. + * @function toJSON + * @memberof proto.WallpaperSettings + * @instance + * @returns {Object.} JSON object + */ + WallpaperSettings.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return WallpaperSettings; + })(); + + proto.GroupParticipant = (function() { + + /** + * Properties of a GroupParticipant. + * @memberof proto + * @interface IGroupParticipant + * @property {string} userJid GroupParticipant userJid + * @property {proto.GroupParticipant.GroupParticipantRank|null} [rank] GroupParticipant rank + */ + + /** + * Constructs a new GroupParticipant. + * @memberof proto + * @classdesc Represents a GroupParticipant. + * @implements IGroupParticipant + * @constructor + * @param {proto.IGroupParticipant=} [properties] Properties to set + */ + function GroupParticipant(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GroupParticipant userJid. + * @member {string} userJid + * @memberof proto.GroupParticipant + * @instance + */ + GroupParticipant.prototype.userJid = ""; + + /** + * GroupParticipant rank. + * @member {proto.GroupParticipant.GroupParticipantRank} rank + * @memberof proto.GroupParticipant + * @instance + */ + GroupParticipant.prototype.rank = 0; + + /** + * Creates a new GroupParticipant instance using the specified properties. + * @function create + * @memberof proto.GroupParticipant + * @static + * @param {proto.IGroupParticipant=} [properties] Properties to set + * @returns {proto.GroupParticipant} GroupParticipant instance + */ + GroupParticipant.create = function create(properties) { + return new GroupParticipant(properties); + }; + + /** + * Encodes the specified GroupParticipant message. Does not implicitly {@link proto.GroupParticipant.verify|verify} messages. + * @function encode + * @memberof proto.GroupParticipant + * @static + * @param {proto.IGroupParticipant} message GroupParticipant message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GroupParticipant.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + writer.uint32(/* id 1, wireType 2 =*/10).string(message.userJid); + if (message.rank != null && Object.hasOwnProperty.call(message, "rank")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.rank); + return writer; + }; + + /** + * Encodes the specified GroupParticipant message, length delimited. Does not implicitly {@link proto.GroupParticipant.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.GroupParticipant + * @static + * @param {proto.IGroupParticipant} message GroupParticipant message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GroupParticipant.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GroupParticipant message from the specified reader or buffer. + * @function decode + * @memberof proto.GroupParticipant + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.GroupParticipant} GroupParticipant + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GroupParticipant.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.GroupParticipant(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.userJid = reader.string(); + break; + case 2: + message.rank = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + if (!message.hasOwnProperty("userJid")) + throw $util.ProtocolError("missing required 'userJid'", { instance: message }); + return message; + }; + + /** + * Decodes a GroupParticipant message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.GroupParticipant + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.GroupParticipant} GroupParticipant + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GroupParticipant.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GroupParticipant message. + * @function verify + * @memberof proto.GroupParticipant + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GroupParticipant.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.userJid)) + return "userJid: string expected"; + if (message.rank != null && message.hasOwnProperty("rank")) + switch (message.rank) { + default: + return "rank: enum value expected"; + case 0: + case 1: + case 2: + break; + } + return null; + }; + + /** + * Creates a GroupParticipant message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.GroupParticipant + * @static + * @param {Object.} object Plain object + * @returns {proto.GroupParticipant} GroupParticipant + */ + GroupParticipant.fromObject = function fromObject(object) { + if (object instanceof $root.proto.GroupParticipant) + return object; + var message = new $root.proto.GroupParticipant(); + if (object.userJid != null) + message.userJid = String(object.userJid); + switch (object.rank) { + case "REGULAR": + case 0: + message.rank = 0; + break; + case "ADMIN": + case 1: + message.rank = 1; + break; + case "SUPERADMIN": + case 2: + message.rank = 2; + break; + } + return message; + }; + + /** + * Creates a plain object from a GroupParticipant message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.GroupParticipant + * @static + * @param {proto.GroupParticipant} message GroupParticipant + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GroupParticipant.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.userJid = ""; + object.rank = options.enums === String ? "REGULAR" : 0; + } + if (message.userJid != null && message.hasOwnProperty("userJid")) + object.userJid = message.userJid; + if (message.rank != null && message.hasOwnProperty("rank")) + object.rank = options.enums === String ? $root.proto.GroupParticipant.GroupParticipantRank[message.rank] : message.rank; + return object; + }; + + /** + * Converts this GroupParticipant to JSON. + * @function toJSON + * @memberof proto.GroupParticipant + * @instance + * @returns {Object.} JSON object + */ + GroupParticipant.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * GroupParticipantRank enum. + * @name proto.GroupParticipant.GroupParticipantRank + * @enum {number} + * @property {number} REGULAR=0 REGULAR value + * @property {number} ADMIN=1 ADMIN value + * @property {number} SUPERADMIN=2 SUPERADMIN value + */ + GroupParticipant.GroupParticipantRank = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "REGULAR"] = 0; + values[valuesById[1] = "ADMIN"] = 1; + values[valuesById[2] = "SUPERADMIN"] = 2; + return values; + })(); + + return GroupParticipant; + })(); + proto.Pushname = (function() { /** @@ -16579,6 +18545,20 @@ $root.proto = (function() { * @property {string|null} [name] Conversation name * @property {string|null} [pHash] Conversation pHash * @property {boolean|null} [notSpam] Conversation notSpam + * @property {boolean|null} [archived] Conversation archived + * @property {proto.IDisappearingMode|null} [disappearingMode] Conversation disappearingMode + * @property {number|null} [unreadMentionCount] Conversation unreadMentionCount + * @property {boolean|null} [markedAsUnread] Conversation markedAsUnread + * @property {Array.|null} [participant] Conversation participant + * @property {Uint8Array|null} [tcToken] Conversation tcToken + * @property {number|Long|null} [tcTokenTimestamp] Conversation tcTokenTimestamp + * @property {Uint8Array|null} [contactPrimaryIdentityKey] Conversation contactPrimaryIdentityKey + * @property {number|null} [pinned] Conversation pinned + * @property {number|Long|null} [muteEndTime] Conversation muteEndTime + * @property {proto.IWallpaperSettings|null} [wallpaper] Conversation wallpaper + * @property {proto.MediaVisibility|null} [mediaVisibility] Conversation mediaVisibility + * @property {number|Long|null} [tcTokenSenderTimestamp] Conversation tcTokenSenderTimestamp + * @property {boolean|null} [suspended] Conversation suspended */ /** @@ -16591,6 +18571,7 @@ $root.proto = (function() { */ function Conversation(properties) { this.messages = []; + this.participant = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -16717,6 +18698,118 @@ $root.proto = (function() { */ Conversation.prototype.notSpam = false; + /** + * Conversation archived. + * @member {boolean} archived + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.archived = false; + + /** + * Conversation disappearingMode. + * @member {proto.IDisappearingMode|null|undefined} disappearingMode + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.disappearingMode = null; + + /** + * Conversation unreadMentionCount. + * @member {number} unreadMentionCount + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.unreadMentionCount = 0; + + /** + * Conversation markedAsUnread. + * @member {boolean} markedAsUnread + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.markedAsUnread = false; + + /** + * Conversation participant. + * @member {Array.} participant + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.participant = $util.emptyArray; + + /** + * Conversation tcToken. + * @member {Uint8Array} tcToken + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.tcToken = $util.newBuffer([]); + + /** + * Conversation tcTokenTimestamp. + * @member {number|Long} tcTokenTimestamp + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.tcTokenTimestamp = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * Conversation contactPrimaryIdentityKey. + * @member {Uint8Array} contactPrimaryIdentityKey + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.contactPrimaryIdentityKey = $util.newBuffer([]); + + /** + * Conversation pinned. + * @member {number} pinned + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.pinned = 0; + + /** + * Conversation muteEndTime. + * @member {number|Long} muteEndTime + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.muteEndTime = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * Conversation wallpaper. + * @member {proto.IWallpaperSettings|null|undefined} wallpaper + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.wallpaper = null; + + /** + * Conversation mediaVisibility. + * @member {proto.MediaVisibility} mediaVisibility + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.mediaVisibility = 0; + + /** + * Conversation tcTokenSenderTimestamp. + * @member {number|Long} tcTokenSenderTimestamp + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.tcTokenSenderTimestamp = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * Conversation suspended. + * @member {boolean} suspended + * @memberof proto.Conversation + * @instance + */ + Conversation.prototype.suspended = false; + /** * Creates a new Conversation instance using the specified properties. * @function create @@ -16771,6 +18864,35 @@ $root.proto = (function() { writer.uint32(/* id 14, wireType 2 =*/114).string(message.pHash); if (message.notSpam != null && Object.hasOwnProperty.call(message, "notSpam")) writer.uint32(/* id 15, wireType 0 =*/120).bool(message.notSpam); + if (message.archived != null && Object.hasOwnProperty.call(message, "archived")) + writer.uint32(/* id 16, wireType 0 =*/128).bool(message.archived); + if (message.disappearingMode != null && Object.hasOwnProperty.call(message, "disappearingMode")) + $root.proto.DisappearingMode.encode(message.disappearingMode, writer.uint32(/* id 17, wireType 2 =*/138).fork()).ldelim(); + if (message.unreadMentionCount != null && Object.hasOwnProperty.call(message, "unreadMentionCount")) + writer.uint32(/* id 18, wireType 0 =*/144).uint32(message.unreadMentionCount); + if (message.markedAsUnread != null && Object.hasOwnProperty.call(message, "markedAsUnread")) + writer.uint32(/* id 19, wireType 0 =*/152).bool(message.markedAsUnread); + if (message.participant != null && message.participant.length) + for (var i = 0; i < message.participant.length; ++i) + $root.proto.GroupParticipant.encode(message.participant[i], writer.uint32(/* id 20, wireType 2 =*/162).fork()).ldelim(); + if (message.tcToken != null && Object.hasOwnProperty.call(message, "tcToken")) + writer.uint32(/* id 21, wireType 2 =*/170).bytes(message.tcToken); + if (message.tcTokenTimestamp != null && Object.hasOwnProperty.call(message, "tcTokenTimestamp")) + writer.uint32(/* id 22, wireType 0 =*/176).uint64(message.tcTokenTimestamp); + if (message.contactPrimaryIdentityKey != null && Object.hasOwnProperty.call(message, "contactPrimaryIdentityKey")) + writer.uint32(/* id 23, wireType 2 =*/186).bytes(message.contactPrimaryIdentityKey); + if (message.pinned != null && Object.hasOwnProperty.call(message, "pinned")) + writer.uint32(/* id 24, wireType 0 =*/192).uint32(message.pinned); + if (message.muteEndTime != null && Object.hasOwnProperty.call(message, "muteEndTime")) + writer.uint32(/* id 25, wireType 0 =*/200).uint64(message.muteEndTime); + if (message.wallpaper != null && Object.hasOwnProperty.call(message, "wallpaper")) + $root.proto.WallpaperSettings.encode(message.wallpaper, writer.uint32(/* id 26, wireType 2 =*/210).fork()).ldelim(); + if (message.mediaVisibility != null && Object.hasOwnProperty.call(message, "mediaVisibility")) + writer.uint32(/* id 27, wireType 0 =*/216).int32(message.mediaVisibility); + if (message.tcTokenSenderTimestamp != null && Object.hasOwnProperty.call(message, "tcTokenSenderTimestamp")) + writer.uint32(/* id 28, wireType 0 =*/224).uint64(message.tcTokenSenderTimestamp); + if (message.suspended != null && Object.hasOwnProperty.call(message, "suspended")) + writer.uint32(/* id 29, wireType 0 =*/232).bool(message.suspended); return writer; }; @@ -16852,6 +18974,50 @@ $root.proto = (function() { case 15: message.notSpam = reader.bool(); break; + case 16: + message.archived = reader.bool(); + break; + case 17: + message.disappearingMode = $root.proto.DisappearingMode.decode(reader, reader.uint32()); + break; + case 18: + message.unreadMentionCount = reader.uint32(); + break; + case 19: + message.markedAsUnread = reader.bool(); + break; + case 20: + if (!(message.participant && message.participant.length)) + message.participant = []; + message.participant.push($root.proto.GroupParticipant.decode(reader, reader.uint32())); + break; + case 21: + message.tcToken = reader.bytes(); + break; + case 22: + message.tcTokenTimestamp = reader.uint64(); + break; + case 23: + message.contactPrimaryIdentityKey = reader.bytes(); + break; + case 24: + message.pinned = reader.uint32(); + break; + case 25: + message.muteEndTime = reader.uint64(); + break; + case 26: + message.wallpaper = $root.proto.WallpaperSettings.decode(reader, reader.uint32()); + break; + case 27: + message.mediaVisibility = reader.int32(); + break; + case 28: + message.tcTokenSenderTimestamp = reader.uint64(); + break; + case 29: + message.suspended = reader.bool(); + break; default: reader.skipType(tag & 7); break; @@ -16944,6 +19110,64 @@ $root.proto = (function() { if (message.notSpam != null && message.hasOwnProperty("notSpam")) if (typeof message.notSpam !== "boolean") return "notSpam: boolean expected"; + if (message.archived != null && message.hasOwnProperty("archived")) + if (typeof message.archived !== "boolean") + return "archived: boolean expected"; + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) { + var error = $root.proto.DisappearingMode.verify(message.disappearingMode); + if (error) + return "disappearingMode." + error; + } + if (message.unreadMentionCount != null && message.hasOwnProperty("unreadMentionCount")) + if (!$util.isInteger(message.unreadMentionCount)) + return "unreadMentionCount: integer expected"; + if (message.markedAsUnread != null && message.hasOwnProperty("markedAsUnread")) + if (typeof message.markedAsUnread !== "boolean") + return "markedAsUnread: boolean expected"; + if (message.participant != null && message.hasOwnProperty("participant")) { + if (!Array.isArray(message.participant)) + return "participant: array expected"; + for (var i = 0; i < message.participant.length; ++i) { + var error = $root.proto.GroupParticipant.verify(message.participant[i]); + if (error) + return "participant." + error; + } + } + if (message.tcToken != null && message.hasOwnProperty("tcToken")) + if (!(message.tcToken && typeof message.tcToken.length === "number" || $util.isString(message.tcToken))) + return "tcToken: buffer expected"; + if (message.tcTokenTimestamp != null && message.hasOwnProperty("tcTokenTimestamp")) + if (!$util.isInteger(message.tcTokenTimestamp) && !(message.tcTokenTimestamp && $util.isInteger(message.tcTokenTimestamp.low) && $util.isInteger(message.tcTokenTimestamp.high))) + return "tcTokenTimestamp: integer|Long expected"; + if (message.contactPrimaryIdentityKey != null && message.hasOwnProperty("contactPrimaryIdentityKey")) + if (!(message.contactPrimaryIdentityKey && typeof message.contactPrimaryIdentityKey.length === "number" || $util.isString(message.contactPrimaryIdentityKey))) + return "contactPrimaryIdentityKey: buffer expected"; + if (message.pinned != null && message.hasOwnProperty("pinned")) + if (!$util.isInteger(message.pinned)) + return "pinned: integer expected"; + if (message.muteEndTime != null && message.hasOwnProperty("muteEndTime")) + if (!$util.isInteger(message.muteEndTime) && !(message.muteEndTime && $util.isInteger(message.muteEndTime.low) && $util.isInteger(message.muteEndTime.high))) + return "muteEndTime: integer|Long expected"; + if (message.wallpaper != null && message.hasOwnProperty("wallpaper")) { + var error = $root.proto.WallpaperSettings.verify(message.wallpaper); + if (error) + return "wallpaper." + error; + } + if (message.mediaVisibility != null && message.hasOwnProperty("mediaVisibility")) + switch (message.mediaVisibility) { + default: + return "mediaVisibility: enum value expected"; + case 0: + case 1: + case 2: + break; + } + if (message.tcTokenSenderTimestamp != null && message.hasOwnProperty("tcTokenSenderTimestamp")) + if (!$util.isInteger(message.tcTokenSenderTimestamp) && !(message.tcTokenSenderTimestamp && $util.isInteger(message.tcTokenSenderTimestamp.low) && $util.isInteger(message.tcTokenSenderTimestamp.high))) + return "tcTokenSenderTimestamp: integer|Long expected"; + if (message.suspended != null && message.hasOwnProperty("suspended")) + if (typeof message.suspended !== "boolean") + return "suspended: boolean expected"; return null; }; @@ -17026,6 +19250,87 @@ $root.proto = (function() { message.pHash = String(object.pHash); if (object.notSpam != null) message.notSpam = Boolean(object.notSpam); + if (object.archived != null) + message.archived = Boolean(object.archived); + if (object.disappearingMode != null) { + if (typeof object.disappearingMode !== "object") + throw TypeError(".proto.Conversation.disappearingMode: object expected"); + message.disappearingMode = $root.proto.DisappearingMode.fromObject(object.disappearingMode); + } + if (object.unreadMentionCount != null) + message.unreadMentionCount = object.unreadMentionCount >>> 0; + if (object.markedAsUnread != null) + message.markedAsUnread = Boolean(object.markedAsUnread); + if (object.participant) { + if (!Array.isArray(object.participant)) + throw TypeError(".proto.Conversation.participant: array expected"); + message.participant = []; + for (var i = 0; i < object.participant.length; ++i) { + if (typeof object.participant[i] !== "object") + throw TypeError(".proto.Conversation.participant: object expected"); + message.participant[i] = $root.proto.GroupParticipant.fromObject(object.participant[i]); + } + } + if (object.tcToken != null) + if (typeof object.tcToken === "string") + $util.base64.decode(object.tcToken, message.tcToken = $util.newBuffer($util.base64.length(object.tcToken)), 0); + else if (object.tcToken.length) + message.tcToken = object.tcToken; + if (object.tcTokenTimestamp != null) + if ($util.Long) + (message.tcTokenTimestamp = $util.Long.fromValue(object.tcTokenTimestamp)).unsigned = true; + else if (typeof object.tcTokenTimestamp === "string") + message.tcTokenTimestamp = parseInt(object.tcTokenTimestamp, 10); + else if (typeof object.tcTokenTimestamp === "number") + message.tcTokenTimestamp = object.tcTokenTimestamp; + else if (typeof object.tcTokenTimestamp === "object") + message.tcTokenTimestamp = new $util.LongBits(object.tcTokenTimestamp.low >>> 0, object.tcTokenTimestamp.high >>> 0).toNumber(true); + if (object.contactPrimaryIdentityKey != null) + if (typeof object.contactPrimaryIdentityKey === "string") + $util.base64.decode(object.contactPrimaryIdentityKey, message.contactPrimaryIdentityKey = $util.newBuffer($util.base64.length(object.contactPrimaryIdentityKey)), 0); + else if (object.contactPrimaryIdentityKey.length) + message.contactPrimaryIdentityKey = object.contactPrimaryIdentityKey; + if (object.pinned != null) + message.pinned = object.pinned >>> 0; + if (object.muteEndTime != null) + if ($util.Long) + (message.muteEndTime = $util.Long.fromValue(object.muteEndTime)).unsigned = true; + else if (typeof object.muteEndTime === "string") + message.muteEndTime = parseInt(object.muteEndTime, 10); + else if (typeof object.muteEndTime === "number") + message.muteEndTime = object.muteEndTime; + else if (typeof object.muteEndTime === "object") + message.muteEndTime = new $util.LongBits(object.muteEndTime.low >>> 0, object.muteEndTime.high >>> 0).toNumber(true); + if (object.wallpaper != null) { + if (typeof object.wallpaper !== "object") + throw TypeError(".proto.Conversation.wallpaper: object expected"); + message.wallpaper = $root.proto.WallpaperSettings.fromObject(object.wallpaper); + } + switch (object.mediaVisibility) { + case "DEFAULT": + case 0: + message.mediaVisibility = 0; + break; + case "OFF": + case 1: + message.mediaVisibility = 1; + break; + case "ON": + case 2: + message.mediaVisibility = 2; + break; + } + if (object.tcTokenSenderTimestamp != null) + if ($util.Long) + (message.tcTokenSenderTimestamp = $util.Long.fromValue(object.tcTokenSenderTimestamp)).unsigned = true; + else if (typeof object.tcTokenSenderTimestamp === "string") + message.tcTokenSenderTimestamp = parseInt(object.tcTokenSenderTimestamp, 10); + else if (typeof object.tcTokenSenderTimestamp === "number") + message.tcTokenSenderTimestamp = object.tcTokenSenderTimestamp; + else if (typeof object.tcTokenSenderTimestamp === "object") + message.tcTokenSenderTimestamp = new $util.LongBits(object.tcTokenSenderTimestamp.low >>> 0, object.tcTokenSenderTimestamp.high >>> 0).toNumber(true); + if (object.suspended != null) + message.suspended = Boolean(object.suspended); return message; }; @@ -17042,8 +19347,10 @@ $root.proto = (function() { if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.messages = []; + object.participant = []; + } if (options.defaults) { object.id = ""; object.newJid = ""; @@ -17071,6 +19378,43 @@ $root.proto = (function() { object.name = ""; object.pHash = ""; object.notSpam = false; + object.archived = false; + object.disappearingMode = null; + object.unreadMentionCount = 0; + object.markedAsUnread = false; + if (options.bytes === String) + object.tcToken = ""; + else { + object.tcToken = []; + if (options.bytes !== Array) + object.tcToken = $util.newBuffer(object.tcToken); + } + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.tcTokenTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.tcTokenTimestamp = options.longs === String ? "0" : 0; + if (options.bytes === String) + object.contactPrimaryIdentityKey = ""; + else { + object.contactPrimaryIdentityKey = []; + if (options.bytes !== Array) + object.contactPrimaryIdentityKey = $util.newBuffer(object.contactPrimaryIdentityKey); + } + object.pinned = 0; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.muteEndTime = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.muteEndTime = options.longs === String ? "0" : 0; + object.wallpaper = null; + object.mediaVisibility = options.enums === String ? "DEFAULT" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.tcTokenSenderTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.tcTokenSenderTimestamp = options.longs === String ? "0" : 0; + object.suspended = false; } if (message.id != null && message.hasOwnProperty("id")) object.id = message.id; @@ -17114,6 +19458,46 @@ $root.proto = (function() { object.pHash = message.pHash; if (message.notSpam != null && message.hasOwnProperty("notSpam")) object.notSpam = message.notSpam; + if (message.archived != null && message.hasOwnProperty("archived")) + object.archived = message.archived; + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) + object.disappearingMode = $root.proto.DisappearingMode.toObject(message.disappearingMode, options); + if (message.unreadMentionCount != null && message.hasOwnProperty("unreadMentionCount")) + object.unreadMentionCount = message.unreadMentionCount; + if (message.markedAsUnread != null && message.hasOwnProperty("markedAsUnread")) + object.markedAsUnread = message.markedAsUnread; + if (message.participant && message.participant.length) { + object.participant = []; + for (var j = 0; j < message.participant.length; ++j) + object.participant[j] = $root.proto.GroupParticipant.toObject(message.participant[j], options); + } + if (message.tcToken != null && message.hasOwnProperty("tcToken")) + object.tcToken = options.bytes === String ? $util.base64.encode(message.tcToken, 0, message.tcToken.length) : options.bytes === Array ? Array.prototype.slice.call(message.tcToken) : message.tcToken; + if (message.tcTokenTimestamp != null && message.hasOwnProperty("tcTokenTimestamp")) + if (typeof message.tcTokenTimestamp === "number") + object.tcTokenTimestamp = options.longs === String ? String(message.tcTokenTimestamp) : message.tcTokenTimestamp; + else + object.tcTokenTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.tcTokenTimestamp) : options.longs === Number ? new $util.LongBits(message.tcTokenTimestamp.low >>> 0, message.tcTokenTimestamp.high >>> 0).toNumber(true) : message.tcTokenTimestamp; + if (message.contactPrimaryIdentityKey != null && message.hasOwnProperty("contactPrimaryIdentityKey")) + object.contactPrimaryIdentityKey = options.bytes === String ? $util.base64.encode(message.contactPrimaryIdentityKey, 0, message.contactPrimaryIdentityKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.contactPrimaryIdentityKey) : message.contactPrimaryIdentityKey; + if (message.pinned != null && message.hasOwnProperty("pinned")) + object.pinned = message.pinned; + if (message.muteEndTime != null && message.hasOwnProperty("muteEndTime")) + if (typeof message.muteEndTime === "number") + object.muteEndTime = options.longs === String ? String(message.muteEndTime) : message.muteEndTime; + else + object.muteEndTime = options.longs === String ? $util.Long.prototype.toString.call(message.muteEndTime) : options.longs === Number ? new $util.LongBits(message.muteEndTime.low >>> 0, message.muteEndTime.high >>> 0).toNumber(true) : message.muteEndTime; + if (message.wallpaper != null && message.hasOwnProperty("wallpaper")) + object.wallpaper = $root.proto.WallpaperSettings.toObject(message.wallpaper, options); + if (message.mediaVisibility != null && message.hasOwnProperty("mediaVisibility")) + object.mediaVisibility = options.enums === String ? $root.proto.MediaVisibility[message.mediaVisibility] : message.mediaVisibility; + if (message.tcTokenSenderTimestamp != null && message.hasOwnProperty("tcTokenSenderTimestamp")) + if (typeof message.tcTokenSenderTimestamp === "number") + object.tcTokenSenderTimestamp = options.longs === String ? String(message.tcTokenSenderTimestamp) : message.tcTokenSenderTimestamp; + else + object.tcTokenSenderTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.tcTokenSenderTimestamp) : options.longs === Number ? new $util.LongBits(message.tcTokenSenderTimestamp.low >>> 0, message.tcTokenSenderTimestamp.high >>> 0).toNumber(true) : message.tcTokenSenderTimestamp; + if (message.suspended != null && message.hasOwnProperty("suspended")) + object.suspended = message.suspended; return object; }; @@ -17157,6 +19541,9 @@ $root.proto = (function() { * @property {number|null} [chunkOrder] HistorySync chunkOrder * @property {number|null} [progress] HistorySync progress * @property {Array.|null} [pushnames] HistorySync pushnames + * @property {proto.IGlobalSettings|null} [globalSettings] HistorySync globalSettings + * @property {Uint8Array|null} [threadIdUserSecret] HistorySync threadIdUserSecret + * @property {number|null} [threadDsTimeframeOffset] HistorySync threadDsTimeframeOffset */ /** @@ -17225,6 +19612,30 @@ $root.proto = (function() { */ HistorySync.prototype.pushnames = $util.emptyArray; + /** + * HistorySync globalSettings. + * @member {proto.IGlobalSettings|null|undefined} globalSettings + * @memberof proto.HistorySync + * @instance + */ + HistorySync.prototype.globalSettings = null; + + /** + * HistorySync threadIdUserSecret. + * @member {Uint8Array} threadIdUserSecret + * @memberof proto.HistorySync + * @instance + */ + HistorySync.prototype.threadIdUserSecret = $util.newBuffer([]); + + /** + * HistorySync threadDsTimeframeOffset. + * @member {number} threadDsTimeframeOffset + * @memberof proto.HistorySync + * @instance + */ + HistorySync.prototype.threadDsTimeframeOffset = 0; + /** * Creates a new HistorySync instance using the specified properties. * @function create @@ -17263,6 +19674,12 @@ $root.proto = (function() { if (message.pushnames != null && message.pushnames.length) for (var i = 0; i < message.pushnames.length; ++i) $root.proto.Pushname.encode(message.pushnames[i], writer.uint32(/* id 7, wireType 2 =*/58).fork()).ldelim(); + if (message.globalSettings != null && Object.hasOwnProperty.call(message, "globalSettings")) + $root.proto.GlobalSettings.encode(message.globalSettings, writer.uint32(/* id 8, wireType 2 =*/66).fork()).ldelim(); + if (message.threadIdUserSecret != null && Object.hasOwnProperty.call(message, "threadIdUserSecret")) + writer.uint32(/* id 9, wireType 2 =*/74).bytes(message.threadIdUserSecret); + if (message.threadDsTimeframeOffset != null && Object.hasOwnProperty.call(message, "threadDsTimeframeOffset")) + writer.uint32(/* id 10, wireType 0 =*/80).uint32(message.threadDsTimeframeOffset); return writer; }; @@ -17321,6 +19738,15 @@ $root.proto = (function() { message.pushnames = []; message.pushnames.push($root.proto.Pushname.decode(reader, reader.uint32())); break; + case 8: + message.globalSettings = $root.proto.GlobalSettings.decode(reader, reader.uint32()); + break; + case 9: + message.threadIdUserSecret = reader.bytes(); + break; + case 10: + message.threadDsTimeframeOffset = reader.uint32(); + break; default: reader.skipType(tag & 7); break; @@ -17401,6 +19827,17 @@ $root.proto = (function() { return "pushnames." + error; } } + if (message.globalSettings != null && message.hasOwnProperty("globalSettings")) { + var error = $root.proto.GlobalSettings.verify(message.globalSettings); + if (error) + return "globalSettings." + error; + } + if (message.threadIdUserSecret != null && message.hasOwnProperty("threadIdUserSecret")) + if (!(message.threadIdUserSecret && typeof message.threadIdUserSecret.length === "number" || $util.isString(message.threadIdUserSecret))) + return "threadIdUserSecret: buffer expected"; + if (message.threadDsTimeframeOffset != null && message.hasOwnProperty("threadDsTimeframeOffset")) + if (!$util.isInteger(message.threadDsTimeframeOffset)) + return "threadDsTimeframeOffset: integer expected"; return null; }; @@ -17472,6 +19909,18 @@ $root.proto = (function() { message.pushnames[i] = $root.proto.Pushname.fromObject(object.pushnames[i]); } } + if (object.globalSettings != null) { + if (typeof object.globalSettings !== "object") + throw TypeError(".proto.HistorySync.globalSettings: object expected"); + message.globalSettings = $root.proto.GlobalSettings.fromObject(object.globalSettings); + } + if (object.threadIdUserSecret != null) + if (typeof object.threadIdUserSecret === "string") + $util.base64.decode(object.threadIdUserSecret, message.threadIdUserSecret = $util.newBuffer($util.base64.length(object.threadIdUserSecret)), 0); + else if (object.threadIdUserSecret.length) + message.threadIdUserSecret = object.threadIdUserSecret; + if (object.threadDsTimeframeOffset != null) + message.threadDsTimeframeOffset = object.threadDsTimeframeOffset >>> 0; return message; }; @@ -17497,6 +19946,15 @@ $root.proto = (function() { object.syncType = options.enums === String ? "INITIAL_BOOTSTRAP" : 0; object.chunkOrder = 0; object.progress = 0; + object.globalSettings = null; + if (options.bytes === String) + object.threadIdUserSecret = ""; + else { + object.threadIdUserSecret = []; + if (options.bytes !== Array) + object.threadIdUserSecret = $util.newBuffer(object.threadIdUserSecret); + } + object.threadDsTimeframeOffset = 0; } if (message.syncType != null && message.hasOwnProperty("syncType")) object.syncType = options.enums === String ? $root.proto.HistorySync.HistorySyncHistorySyncType[message.syncType] : message.syncType; @@ -17519,6 +19977,12 @@ $root.proto = (function() { for (var j = 0; j < message.pushnames.length; ++j) object.pushnames[j] = $root.proto.Pushname.toObject(message.pushnames[j], options); } + if (message.globalSettings != null && message.hasOwnProperty("globalSettings")) + object.globalSettings = $root.proto.GlobalSettings.toObject(message.globalSettings, options); + if (message.threadIdUserSecret != null && message.hasOwnProperty("threadIdUserSecret")) + object.threadIdUserSecret = options.bytes === String ? $util.base64.encode(message.threadIdUserSecret, 0, message.threadIdUserSecret.length) : options.bytes === Array ? Array.prototype.slice.call(message.threadIdUserSecret) : message.threadIdUserSecret; + if (message.threadDsTimeframeOffset != null && message.hasOwnProperty("threadDsTimeframeOffset")) + object.threadDsTimeframeOffset = message.threadDsTimeframeOffset; return object; }; @@ -17556,6 +20020,22 @@ $root.proto = (function() { return HistorySync; })(); + /** + * MediaVisibility enum. + * @name proto.MediaVisibility + * @enum {number} + * @property {number} DEFAULT=0 DEFAULT value + * @property {number} OFF=1 OFF value + * @property {number} ON=2 ON value + */ + proto.MediaVisibility = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "DEFAULT"] = 0; + values[valuesById[1] = "OFF"] = 1; + values[valuesById[2] = "ON"] = 2; + return values; + })(); + proto.EphemeralSetting = (function() { /** @@ -17780,2969 +20260,6 @@ $root.proto = (function() { return EphemeralSetting; })(); - proto.PaymentBackground = (function() { - - /** - * Properties of a PaymentBackground. - * @memberof proto - * @interface IPaymentBackground - * @property {string|null} [id] PaymentBackground id - * @property {string|null} [fileLength] PaymentBackground fileLength - * @property {number|null} [width] PaymentBackground width - * @property {number|null} [height] PaymentBackground height - * @property {string|null} [mimetype] PaymentBackground mimetype - * @property {number|null} [placeholderArgb] PaymentBackground placeholderArgb - * @property {number|null} [textArgb] PaymentBackground textArgb - * @property {number|null} [subtextArgb] PaymentBackground subtextArgb - */ - - /** - * Constructs a new PaymentBackground. - * @memberof proto - * @classdesc Represents a PaymentBackground. - * @implements IPaymentBackground - * @constructor - * @param {proto.IPaymentBackground=} [properties] Properties to set - */ - function PaymentBackground(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * PaymentBackground id. - * @member {string} id - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.id = ""; - - /** - * PaymentBackground fileLength. - * @member {string} fileLength - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.fileLength = ""; - - /** - * PaymentBackground width. - * @member {number} width - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.width = 0; - - /** - * PaymentBackground height. - * @member {number} height - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.height = 0; - - /** - * PaymentBackground mimetype. - * @member {string} mimetype - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.mimetype = ""; - - /** - * PaymentBackground placeholderArgb. - * @member {number} placeholderArgb - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.placeholderArgb = 0; - - /** - * PaymentBackground textArgb. - * @member {number} textArgb - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.textArgb = 0; - - /** - * PaymentBackground subtextArgb. - * @member {number} subtextArgb - * @memberof proto.PaymentBackground - * @instance - */ - PaymentBackground.prototype.subtextArgb = 0; - - /** - * Creates a new PaymentBackground instance using the specified properties. - * @function create - * @memberof proto.PaymentBackground - * @static - * @param {proto.IPaymentBackground=} [properties] Properties to set - * @returns {proto.PaymentBackground} PaymentBackground instance - */ - PaymentBackground.create = function create(properties) { - return new PaymentBackground(properties); - }; - - /** - * Encodes the specified PaymentBackground message. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. - * @function encode - * @memberof proto.PaymentBackground - * @static - * @param {proto.IPaymentBackground} message PaymentBackground message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - PaymentBackground.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 2 =*/10).string(message.id); - if (message.fileLength != null && Object.hasOwnProperty.call(message, "fileLength")) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.fileLength); - if (message.width != null && Object.hasOwnProperty.call(message, "width")) - writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.width); - if (message.height != null && Object.hasOwnProperty.call(message, "height")) - writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.height); - if (message.mimetype != null && Object.hasOwnProperty.call(message, "mimetype")) - writer.uint32(/* id 5, wireType 2 =*/42).string(message.mimetype); - if (message.placeholderArgb != null && Object.hasOwnProperty.call(message, "placeholderArgb")) - writer.uint32(/* id 6, wireType 5 =*/53).fixed32(message.placeholderArgb); - if (message.textArgb != null && Object.hasOwnProperty.call(message, "textArgb")) - writer.uint32(/* id 7, wireType 5 =*/61).fixed32(message.textArgb); - if (message.subtextArgb != null && Object.hasOwnProperty.call(message, "subtextArgb")) - writer.uint32(/* id 8, wireType 5 =*/69).fixed32(message.subtextArgb); - return writer; - }; - - /** - * Encodes the specified PaymentBackground message, length delimited. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.PaymentBackground - * @static - * @param {proto.IPaymentBackground} message PaymentBackground message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - PaymentBackground.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a PaymentBackground message from the specified reader or buffer. - * @function decode - * @memberof proto.PaymentBackground - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.PaymentBackground} PaymentBackground - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - PaymentBackground.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.PaymentBackground(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.id = reader.string(); - break; - case 2: - message.fileLength = reader.string(); - break; - case 3: - message.width = reader.uint32(); - break; - case 4: - message.height = reader.uint32(); - break; - case 5: - message.mimetype = reader.string(); - break; - case 6: - message.placeholderArgb = reader.fixed32(); - break; - case 7: - message.textArgb = reader.fixed32(); - break; - case 8: - message.subtextArgb = reader.fixed32(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a PaymentBackground message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.PaymentBackground - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.PaymentBackground} PaymentBackground - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - PaymentBackground.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a PaymentBackground message. - * @function verify - * @memberof proto.PaymentBackground - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - PaymentBackground.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isString(message.id)) - return "id: string expected"; - if (message.fileLength != null && message.hasOwnProperty("fileLength")) - if (!$util.isString(message.fileLength)) - return "fileLength: string expected"; - if (message.width != null && message.hasOwnProperty("width")) - if (!$util.isInteger(message.width)) - return "width: integer expected"; - if (message.height != null && message.hasOwnProperty("height")) - if (!$util.isInteger(message.height)) - return "height: integer expected"; - if (message.mimetype != null && message.hasOwnProperty("mimetype")) - if (!$util.isString(message.mimetype)) - return "mimetype: string expected"; - if (message.placeholderArgb != null && message.hasOwnProperty("placeholderArgb")) - if (!$util.isInteger(message.placeholderArgb)) - return "placeholderArgb: integer expected"; - if (message.textArgb != null && message.hasOwnProperty("textArgb")) - if (!$util.isInteger(message.textArgb)) - return "textArgb: integer expected"; - if (message.subtextArgb != null && message.hasOwnProperty("subtextArgb")) - if (!$util.isInteger(message.subtextArgb)) - return "subtextArgb: integer expected"; - return null; - }; - - /** - * Creates a PaymentBackground message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.PaymentBackground - * @static - * @param {Object.} object Plain object - * @returns {proto.PaymentBackground} PaymentBackground - */ - PaymentBackground.fromObject = function fromObject(object) { - if (object instanceof $root.proto.PaymentBackground) - return object; - var message = new $root.proto.PaymentBackground(); - if (object.id != null) - message.id = String(object.id); - if (object.fileLength != null) - message.fileLength = String(object.fileLength); - if (object.width != null) - message.width = object.width >>> 0; - if (object.height != null) - message.height = object.height >>> 0; - if (object.mimetype != null) - message.mimetype = String(object.mimetype); - if (object.placeholderArgb != null) - message.placeholderArgb = object.placeholderArgb >>> 0; - if (object.textArgb != null) - message.textArgb = object.textArgb >>> 0; - if (object.subtextArgb != null) - message.subtextArgb = object.subtextArgb >>> 0; - return message; - }; - - /** - * Creates a plain object from a PaymentBackground message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.PaymentBackground - * @static - * @param {proto.PaymentBackground} message PaymentBackground - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - PaymentBackground.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = ""; - object.fileLength = ""; - object.width = 0; - object.height = 0; - object.mimetype = ""; - object.placeholderArgb = 0; - object.textArgb = 0; - object.subtextArgb = 0; - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.fileLength != null && message.hasOwnProperty("fileLength")) - object.fileLength = message.fileLength; - if (message.width != null && message.hasOwnProperty("width")) - object.width = message.width; - if (message.height != null && message.hasOwnProperty("height")) - object.height = message.height; - if (message.mimetype != null && message.hasOwnProperty("mimetype")) - object.mimetype = message.mimetype; - if (message.placeholderArgb != null && message.hasOwnProperty("placeholderArgb")) - object.placeholderArgb = message.placeholderArgb; - if (message.textArgb != null && message.hasOwnProperty("textArgb")) - object.textArgb = message.textArgb; - if (message.subtextArgb != null && message.hasOwnProperty("subtextArgb")) - object.subtextArgb = message.subtextArgb; - return object; - }; - - /** - * Converts this PaymentBackground to JSON. - * @function toJSON - * @memberof proto.PaymentBackground - * @instance - * @returns {Object.} JSON object - */ - PaymentBackground.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return PaymentBackground; - })(); - - proto.Money = (function() { - - /** - * Properties of a Money. - * @memberof proto - * @interface IMoney - * @property {number|Long|null} [value] Money value - * @property {number|null} [offset] Money offset - * @property {string|null} [currencyCode] Money currencyCode - */ - - /** - * Constructs a new Money. - * @memberof proto - * @classdesc Represents a Money. - * @implements IMoney - * @constructor - * @param {proto.IMoney=} [properties] Properties to set - */ - function Money(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Money value. - * @member {number|Long} value - * @memberof proto.Money - * @instance - */ - Money.prototype.value = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * Money offset. - * @member {number} offset - * @memberof proto.Money - * @instance - */ - Money.prototype.offset = 0; - - /** - * Money currencyCode. - * @member {string} currencyCode - * @memberof proto.Money - * @instance - */ - Money.prototype.currencyCode = ""; - - /** - * Creates a new Money instance using the specified properties. - * @function create - * @memberof proto.Money - * @static - * @param {proto.IMoney=} [properties] Properties to set - * @returns {proto.Money} Money instance - */ - Money.create = function create(properties) { - return new Money(properties); - }; - - /** - * Encodes the specified Money message. Does not implicitly {@link proto.Money.verify|verify} messages. - * @function encode - * @memberof proto.Money - * @static - * @param {proto.IMoney} message Money message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Money.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.value != null && Object.hasOwnProperty.call(message, "value")) - writer.uint32(/* id 1, wireType 0 =*/8).int64(message.value); - if (message.offset != null && Object.hasOwnProperty.call(message, "offset")) - writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.offset); - if (message.currencyCode != null && Object.hasOwnProperty.call(message, "currencyCode")) - writer.uint32(/* id 3, wireType 2 =*/26).string(message.currencyCode); - return writer; - }; - - /** - * Encodes the specified Money message, length delimited. Does not implicitly {@link proto.Money.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.Money - * @static - * @param {proto.IMoney} message Money message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Money.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Money message from the specified reader or buffer. - * @function decode - * @memberof proto.Money - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.Money} Money - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Money.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Money(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.value = reader.int64(); - break; - case 2: - message.offset = reader.uint32(); - break; - case 3: - message.currencyCode = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Money message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.Money - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.Money} Money - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Money.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Money message. - * @function verify - * @memberof proto.Money - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Money.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.value != null && message.hasOwnProperty("value")) - if (!$util.isInteger(message.value) && !(message.value && $util.isInteger(message.value.low) && $util.isInteger(message.value.high))) - return "value: integer|Long expected"; - if (message.offset != null && message.hasOwnProperty("offset")) - if (!$util.isInteger(message.offset)) - return "offset: integer expected"; - if (message.currencyCode != null && message.hasOwnProperty("currencyCode")) - if (!$util.isString(message.currencyCode)) - return "currencyCode: string expected"; - return null; - }; - - /** - * Creates a Money message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.Money - * @static - * @param {Object.} object Plain object - * @returns {proto.Money} Money - */ - Money.fromObject = function fromObject(object) { - if (object instanceof $root.proto.Money) - return object; - var message = new $root.proto.Money(); - if (object.value != null) - if ($util.Long) - (message.value = $util.Long.fromValue(object.value)).unsigned = false; - else if (typeof object.value === "string") - message.value = parseInt(object.value, 10); - else if (typeof object.value === "number") - message.value = object.value; - else if (typeof object.value === "object") - message.value = new $util.LongBits(object.value.low >>> 0, object.value.high >>> 0).toNumber(); - if (object.offset != null) - message.offset = object.offset >>> 0; - if (object.currencyCode != null) - message.currencyCode = String(object.currencyCode); - return message; - }; - - /** - * Creates a plain object from a Money message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.Money - * @static - * @param {proto.Money} message Money - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Money.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - if ($util.Long) { - var long = new $util.Long(0, 0, false); - object.value = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.value = options.longs === String ? "0" : 0; - object.offset = 0; - object.currencyCode = ""; - } - if (message.value != null && message.hasOwnProperty("value")) - if (typeof message.value === "number") - object.value = options.longs === String ? String(message.value) : message.value; - else - object.value = options.longs === String ? $util.Long.prototype.toString.call(message.value) : options.longs === Number ? new $util.LongBits(message.value.low >>> 0, message.value.high >>> 0).toNumber() : message.value; - if (message.offset != null && message.hasOwnProperty("offset")) - object.offset = message.offset; - if (message.currencyCode != null && message.hasOwnProperty("currencyCode")) - object.currencyCode = message.currencyCode; - return object; - }; - - /** - * Converts this Money to JSON. - * @function toJSON - * @memberof proto.Money - * @instance - * @returns {Object.} JSON object - */ - Money.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Money; - })(); - - proto.HydratedQuickReplyButton = (function() { - - /** - * Properties of a HydratedQuickReplyButton. - * @memberof proto - * @interface IHydratedQuickReplyButton - * @property {string|null} [displayText] HydratedQuickReplyButton displayText - * @property {string|null} [id] HydratedQuickReplyButton id - */ - - /** - * Constructs a new HydratedQuickReplyButton. - * @memberof proto - * @classdesc Represents a HydratedQuickReplyButton. - * @implements IHydratedQuickReplyButton - * @constructor - * @param {proto.IHydratedQuickReplyButton=} [properties] Properties to set - */ - function HydratedQuickReplyButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * HydratedQuickReplyButton displayText. - * @member {string} displayText - * @memberof proto.HydratedQuickReplyButton - * @instance - */ - HydratedQuickReplyButton.prototype.displayText = ""; - - /** - * HydratedQuickReplyButton id. - * @member {string} id - * @memberof proto.HydratedQuickReplyButton - * @instance - */ - HydratedQuickReplyButton.prototype.id = ""; - - /** - * Creates a new HydratedQuickReplyButton instance using the specified properties. - * @function create - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {proto.IHydratedQuickReplyButton=} [properties] Properties to set - * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton instance - */ - HydratedQuickReplyButton.create = function create(properties) { - return new HydratedQuickReplyButton(properties); - }; - - /** - * Encodes the specified HydratedQuickReplyButton message. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. - * @function encode - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {proto.IHydratedQuickReplyButton} message HydratedQuickReplyButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedQuickReplyButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) - writer.uint32(/* id 1, wireType 2 =*/10).string(message.displayText); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.id); - return writer; - }; - - /** - * Encodes the specified HydratedQuickReplyButton message, length delimited. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {proto.IHydratedQuickReplyButton} message HydratedQuickReplyButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedQuickReplyButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a HydratedQuickReplyButton message from the specified reader or buffer. - * @function decode - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedQuickReplyButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedQuickReplyButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.displayText = reader.string(); - break; - case 2: - message.id = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a HydratedQuickReplyButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedQuickReplyButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a HydratedQuickReplyButton message. - * @function verify - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - HydratedQuickReplyButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.displayText != null && message.hasOwnProperty("displayText")) - if (!$util.isString(message.displayText)) - return "displayText: string expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isString(message.id)) - return "id: string expected"; - return null; - }; - - /** - * Creates a HydratedQuickReplyButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {Object.} object Plain object - * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton - */ - HydratedQuickReplyButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.HydratedQuickReplyButton) - return object; - var message = new $root.proto.HydratedQuickReplyButton(); - if (object.displayText != null) - message.displayText = String(object.displayText); - if (object.id != null) - message.id = String(object.id); - return message; - }; - - /** - * Creates a plain object from a HydratedQuickReplyButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.HydratedQuickReplyButton - * @static - * @param {proto.HydratedQuickReplyButton} message HydratedQuickReplyButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - HydratedQuickReplyButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.displayText = ""; - object.id = ""; - } - if (message.displayText != null && message.hasOwnProperty("displayText")) - object.displayText = message.displayText; - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - return object; - }; - - /** - * Converts this HydratedQuickReplyButton to JSON. - * @function toJSON - * @memberof proto.HydratedQuickReplyButton - * @instance - * @returns {Object.} JSON object - */ - HydratedQuickReplyButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return HydratedQuickReplyButton; - })(); - - proto.HydratedURLButton = (function() { - - /** - * Properties of a HydratedURLButton. - * @memberof proto - * @interface IHydratedURLButton - * @property {string|null} [displayText] HydratedURLButton displayText - * @property {string|null} [url] HydratedURLButton url - */ - - /** - * Constructs a new HydratedURLButton. - * @memberof proto - * @classdesc Represents a HydratedURLButton. - * @implements IHydratedURLButton - * @constructor - * @param {proto.IHydratedURLButton=} [properties] Properties to set - */ - function HydratedURLButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * HydratedURLButton displayText. - * @member {string} displayText - * @memberof proto.HydratedURLButton - * @instance - */ - HydratedURLButton.prototype.displayText = ""; - - /** - * HydratedURLButton url. - * @member {string} url - * @memberof proto.HydratedURLButton - * @instance - */ - HydratedURLButton.prototype.url = ""; - - /** - * Creates a new HydratedURLButton instance using the specified properties. - * @function create - * @memberof proto.HydratedURLButton - * @static - * @param {proto.IHydratedURLButton=} [properties] Properties to set - * @returns {proto.HydratedURLButton} HydratedURLButton instance - */ - HydratedURLButton.create = function create(properties) { - return new HydratedURLButton(properties); - }; - - /** - * Encodes the specified HydratedURLButton message. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. - * @function encode - * @memberof proto.HydratedURLButton - * @static - * @param {proto.IHydratedURLButton} message HydratedURLButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedURLButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) - writer.uint32(/* id 1, wireType 2 =*/10).string(message.displayText); - if (message.url != null && Object.hasOwnProperty.call(message, "url")) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.url); - return writer; - }; - - /** - * Encodes the specified HydratedURLButton message, length delimited. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.HydratedURLButton - * @static - * @param {proto.IHydratedURLButton} message HydratedURLButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedURLButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a HydratedURLButton message from the specified reader or buffer. - * @function decode - * @memberof proto.HydratedURLButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.HydratedURLButton} HydratedURLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedURLButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedURLButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.displayText = reader.string(); - break; - case 2: - message.url = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a HydratedURLButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.HydratedURLButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.HydratedURLButton} HydratedURLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedURLButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a HydratedURLButton message. - * @function verify - * @memberof proto.HydratedURLButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - HydratedURLButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.displayText != null && message.hasOwnProperty("displayText")) - if (!$util.isString(message.displayText)) - return "displayText: string expected"; - if (message.url != null && message.hasOwnProperty("url")) - if (!$util.isString(message.url)) - return "url: string expected"; - return null; - }; - - /** - * Creates a HydratedURLButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.HydratedURLButton - * @static - * @param {Object.} object Plain object - * @returns {proto.HydratedURLButton} HydratedURLButton - */ - HydratedURLButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.HydratedURLButton) - return object; - var message = new $root.proto.HydratedURLButton(); - if (object.displayText != null) - message.displayText = String(object.displayText); - if (object.url != null) - message.url = String(object.url); - return message; - }; - - /** - * Creates a plain object from a HydratedURLButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.HydratedURLButton - * @static - * @param {proto.HydratedURLButton} message HydratedURLButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - HydratedURLButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.displayText = ""; - object.url = ""; - } - if (message.displayText != null && message.hasOwnProperty("displayText")) - object.displayText = message.displayText; - if (message.url != null && message.hasOwnProperty("url")) - object.url = message.url; - return object; - }; - - /** - * Converts this HydratedURLButton to JSON. - * @function toJSON - * @memberof proto.HydratedURLButton - * @instance - * @returns {Object.} JSON object - */ - HydratedURLButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return HydratedURLButton; - })(); - - proto.HydratedCallButton = (function() { - - /** - * Properties of a HydratedCallButton. - * @memberof proto - * @interface IHydratedCallButton - * @property {string|null} [displayText] HydratedCallButton displayText - * @property {string|null} [phoneNumber] HydratedCallButton phoneNumber - */ - - /** - * Constructs a new HydratedCallButton. - * @memberof proto - * @classdesc Represents a HydratedCallButton. - * @implements IHydratedCallButton - * @constructor - * @param {proto.IHydratedCallButton=} [properties] Properties to set - */ - function HydratedCallButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * HydratedCallButton displayText. - * @member {string} displayText - * @memberof proto.HydratedCallButton - * @instance - */ - HydratedCallButton.prototype.displayText = ""; - - /** - * HydratedCallButton phoneNumber. - * @member {string} phoneNumber - * @memberof proto.HydratedCallButton - * @instance - */ - HydratedCallButton.prototype.phoneNumber = ""; - - /** - * Creates a new HydratedCallButton instance using the specified properties. - * @function create - * @memberof proto.HydratedCallButton - * @static - * @param {proto.IHydratedCallButton=} [properties] Properties to set - * @returns {proto.HydratedCallButton} HydratedCallButton instance - */ - HydratedCallButton.create = function create(properties) { - return new HydratedCallButton(properties); - }; - - /** - * Encodes the specified HydratedCallButton message. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. - * @function encode - * @memberof proto.HydratedCallButton - * @static - * @param {proto.IHydratedCallButton} message HydratedCallButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedCallButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) - writer.uint32(/* id 1, wireType 2 =*/10).string(message.displayText); - if (message.phoneNumber != null && Object.hasOwnProperty.call(message, "phoneNumber")) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.phoneNumber); - return writer; - }; - - /** - * Encodes the specified HydratedCallButton message, length delimited. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.HydratedCallButton - * @static - * @param {proto.IHydratedCallButton} message HydratedCallButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedCallButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a HydratedCallButton message from the specified reader or buffer. - * @function decode - * @memberof proto.HydratedCallButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.HydratedCallButton} HydratedCallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedCallButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedCallButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.displayText = reader.string(); - break; - case 2: - message.phoneNumber = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a HydratedCallButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.HydratedCallButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.HydratedCallButton} HydratedCallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedCallButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a HydratedCallButton message. - * @function verify - * @memberof proto.HydratedCallButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - HydratedCallButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.displayText != null && message.hasOwnProperty("displayText")) - if (!$util.isString(message.displayText)) - return "displayText: string expected"; - if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) - if (!$util.isString(message.phoneNumber)) - return "phoneNumber: string expected"; - return null; - }; - - /** - * Creates a HydratedCallButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.HydratedCallButton - * @static - * @param {Object.} object Plain object - * @returns {proto.HydratedCallButton} HydratedCallButton - */ - HydratedCallButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.HydratedCallButton) - return object; - var message = new $root.proto.HydratedCallButton(); - if (object.displayText != null) - message.displayText = String(object.displayText); - if (object.phoneNumber != null) - message.phoneNumber = String(object.phoneNumber); - return message; - }; - - /** - * Creates a plain object from a HydratedCallButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.HydratedCallButton - * @static - * @param {proto.HydratedCallButton} message HydratedCallButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - HydratedCallButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.displayText = ""; - object.phoneNumber = ""; - } - if (message.displayText != null && message.hasOwnProperty("displayText")) - object.displayText = message.displayText; - if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) - object.phoneNumber = message.phoneNumber; - return object; - }; - - /** - * Converts this HydratedCallButton to JSON. - * @function toJSON - * @memberof proto.HydratedCallButton - * @instance - * @returns {Object.} JSON object - */ - HydratedCallButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return HydratedCallButton; - })(); - - proto.HydratedTemplateButton = (function() { - - /** - * Properties of a HydratedTemplateButton. - * @memberof proto - * @interface IHydratedTemplateButton - * @property {number|null} [index] HydratedTemplateButton index - * @property {proto.IHydratedQuickReplyButton|null} [quickReplyButton] HydratedTemplateButton quickReplyButton - * @property {proto.IHydratedURLButton|null} [urlButton] HydratedTemplateButton urlButton - * @property {proto.IHydratedCallButton|null} [callButton] HydratedTemplateButton callButton - */ - - /** - * Constructs a new HydratedTemplateButton. - * @memberof proto - * @classdesc Represents a HydratedTemplateButton. - * @implements IHydratedTemplateButton - * @constructor - * @param {proto.IHydratedTemplateButton=} [properties] Properties to set - */ - function HydratedTemplateButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * HydratedTemplateButton index. - * @member {number} index - * @memberof proto.HydratedTemplateButton - * @instance - */ - HydratedTemplateButton.prototype.index = 0; - - /** - * HydratedTemplateButton quickReplyButton. - * @member {proto.IHydratedQuickReplyButton|null|undefined} quickReplyButton - * @memberof proto.HydratedTemplateButton - * @instance - */ - HydratedTemplateButton.prototype.quickReplyButton = null; - - /** - * HydratedTemplateButton urlButton. - * @member {proto.IHydratedURLButton|null|undefined} urlButton - * @memberof proto.HydratedTemplateButton - * @instance - */ - HydratedTemplateButton.prototype.urlButton = null; - - /** - * HydratedTemplateButton callButton. - * @member {proto.IHydratedCallButton|null|undefined} callButton - * @memberof proto.HydratedTemplateButton - * @instance - */ - HydratedTemplateButton.prototype.callButton = null; - - // OneOf field names bound to virtual getters and setters - var $oneOfFields; - - /** - * HydratedTemplateButton hydratedButton. - * @member {"quickReplyButton"|"urlButton"|"callButton"|undefined} hydratedButton - * @memberof proto.HydratedTemplateButton - * @instance - */ - Object.defineProperty(HydratedTemplateButton.prototype, "hydratedButton", { - get: $util.oneOfGetter($oneOfFields = ["quickReplyButton", "urlButton", "callButton"]), - set: $util.oneOfSetter($oneOfFields) - }); - - /** - * Creates a new HydratedTemplateButton instance using the specified properties. - * @function create - * @memberof proto.HydratedTemplateButton - * @static - * @param {proto.IHydratedTemplateButton=} [properties] Properties to set - * @returns {proto.HydratedTemplateButton} HydratedTemplateButton instance - */ - HydratedTemplateButton.create = function create(properties) { - return new HydratedTemplateButton(properties); - }; - - /** - * Encodes the specified HydratedTemplateButton message. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. - * @function encode - * @memberof proto.HydratedTemplateButton - * @static - * @param {proto.IHydratedTemplateButton} message HydratedTemplateButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedTemplateButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.quickReplyButton != null && Object.hasOwnProperty.call(message, "quickReplyButton")) - $root.proto.HydratedQuickReplyButton.encode(message.quickReplyButton, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.urlButton != null && Object.hasOwnProperty.call(message, "urlButton")) - $root.proto.HydratedURLButton.encode(message.urlButton, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); - if (message.callButton != null && Object.hasOwnProperty.call(message, "callButton")) - $root.proto.HydratedCallButton.encode(message.callButton, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); - if (message.index != null && Object.hasOwnProperty.call(message, "index")) - writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.index); - return writer; - }; - - /** - * Encodes the specified HydratedTemplateButton message, length delimited. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.HydratedTemplateButton - * @static - * @param {proto.IHydratedTemplateButton} message HydratedTemplateButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HydratedTemplateButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a HydratedTemplateButton message from the specified reader or buffer. - * @function decode - * @memberof proto.HydratedTemplateButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.HydratedTemplateButton} HydratedTemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedTemplateButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedTemplateButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 4: - message.index = reader.uint32(); - break; - case 1: - message.quickReplyButton = $root.proto.HydratedQuickReplyButton.decode(reader, reader.uint32()); - break; - case 2: - message.urlButton = $root.proto.HydratedURLButton.decode(reader, reader.uint32()); - break; - case 3: - message.callButton = $root.proto.HydratedCallButton.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a HydratedTemplateButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.HydratedTemplateButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.HydratedTemplateButton} HydratedTemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HydratedTemplateButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a HydratedTemplateButton message. - * @function verify - * @memberof proto.HydratedTemplateButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - HydratedTemplateButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - var properties = {}; - if (message.index != null && message.hasOwnProperty("index")) - if (!$util.isInteger(message.index)) - return "index: integer expected"; - if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { - properties.hydratedButton = 1; - { - var error = $root.proto.HydratedQuickReplyButton.verify(message.quickReplyButton); - if (error) - return "quickReplyButton." + error; - } - } - if (message.urlButton != null && message.hasOwnProperty("urlButton")) { - if (properties.hydratedButton === 1) - return "hydratedButton: multiple values"; - properties.hydratedButton = 1; - { - var error = $root.proto.HydratedURLButton.verify(message.urlButton); - if (error) - return "urlButton." + error; - } - } - if (message.callButton != null && message.hasOwnProperty("callButton")) { - if (properties.hydratedButton === 1) - return "hydratedButton: multiple values"; - properties.hydratedButton = 1; - { - var error = $root.proto.HydratedCallButton.verify(message.callButton); - if (error) - return "callButton." + error; - } - } - return null; - }; - - /** - * Creates a HydratedTemplateButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.HydratedTemplateButton - * @static - * @param {Object.} object Plain object - * @returns {proto.HydratedTemplateButton} HydratedTemplateButton - */ - HydratedTemplateButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.HydratedTemplateButton) - return object; - var message = new $root.proto.HydratedTemplateButton(); - if (object.index != null) - message.index = object.index >>> 0; - if (object.quickReplyButton != null) { - if (typeof object.quickReplyButton !== "object") - throw TypeError(".proto.HydratedTemplateButton.quickReplyButton: object expected"); - message.quickReplyButton = $root.proto.HydratedQuickReplyButton.fromObject(object.quickReplyButton); - } - if (object.urlButton != null) { - if (typeof object.urlButton !== "object") - throw TypeError(".proto.HydratedTemplateButton.urlButton: object expected"); - message.urlButton = $root.proto.HydratedURLButton.fromObject(object.urlButton); - } - if (object.callButton != null) { - if (typeof object.callButton !== "object") - throw TypeError(".proto.HydratedTemplateButton.callButton: object expected"); - message.callButton = $root.proto.HydratedCallButton.fromObject(object.callButton); - } - return message; - }; - - /** - * Creates a plain object from a HydratedTemplateButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.HydratedTemplateButton - * @static - * @param {proto.HydratedTemplateButton} message HydratedTemplateButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - HydratedTemplateButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) - object.index = 0; - if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { - object.quickReplyButton = $root.proto.HydratedQuickReplyButton.toObject(message.quickReplyButton, options); - if (options.oneofs) - object.hydratedButton = "quickReplyButton"; - } - if (message.urlButton != null && message.hasOwnProperty("urlButton")) { - object.urlButton = $root.proto.HydratedURLButton.toObject(message.urlButton, options); - if (options.oneofs) - object.hydratedButton = "urlButton"; - } - if (message.callButton != null && message.hasOwnProperty("callButton")) { - object.callButton = $root.proto.HydratedCallButton.toObject(message.callButton, options); - if (options.oneofs) - object.hydratedButton = "callButton"; - } - if (message.index != null && message.hasOwnProperty("index")) - object.index = message.index; - return object; - }; - - /** - * Converts this HydratedTemplateButton to JSON. - * @function toJSON - * @memberof proto.HydratedTemplateButton - * @instance - * @returns {Object.} JSON object - */ - HydratedTemplateButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return HydratedTemplateButton; - })(); - - proto.QuickReplyButton = (function() { - - /** - * Properties of a QuickReplyButton. - * @memberof proto - * @interface IQuickReplyButton - * @property {proto.IHighlyStructuredMessage|null} [displayText] QuickReplyButton displayText - * @property {string|null} [id] QuickReplyButton id - */ - - /** - * Constructs a new QuickReplyButton. - * @memberof proto - * @classdesc Represents a QuickReplyButton. - * @implements IQuickReplyButton - * @constructor - * @param {proto.IQuickReplyButton=} [properties] Properties to set - */ - function QuickReplyButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * QuickReplyButton displayText. - * @member {proto.IHighlyStructuredMessage|null|undefined} displayText - * @memberof proto.QuickReplyButton - * @instance - */ - QuickReplyButton.prototype.displayText = null; - - /** - * QuickReplyButton id. - * @member {string} id - * @memberof proto.QuickReplyButton - * @instance - */ - QuickReplyButton.prototype.id = ""; - - /** - * Creates a new QuickReplyButton instance using the specified properties. - * @function create - * @memberof proto.QuickReplyButton - * @static - * @param {proto.IQuickReplyButton=} [properties] Properties to set - * @returns {proto.QuickReplyButton} QuickReplyButton instance - */ - QuickReplyButton.create = function create(properties) { - return new QuickReplyButton(properties); - }; - - /** - * Encodes the specified QuickReplyButton message. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. - * @function encode - * @memberof proto.QuickReplyButton - * @static - * @param {proto.IQuickReplyButton} message QuickReplyButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - QuickReplyButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) - $root.proto.HighlyStructuredMessage.encode(message.displayText, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.id); - return writer; - }; - - /** - * Encodes the specified QuickReplyButton message, length delimited. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.QuickReplyButton - * @static - * @param {proto.IQuickReplyButton} message QuickReplyButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - QuickReplyButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a QuickReplyButton message from the specified reader or buffer. - * @function decode - * @memberof proto.QuickReplyButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.QuickReplyButton} QuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - QuickReplyButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.QuickReplyButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.displayText = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); - break; - case 2: - message.id = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a QuickReplyButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.QuickReplyButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.QuickReplyButton} QuickReplyButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - QuickReplyButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a QuickReplyButton message. - * @function verify - * @memberof proto.QuickReplyButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - QuickReplyButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.displayText != null && message.hasOwnProperty("displayText")) { - var error = $root.proto.HighlyStructuredMessage.verify(message.displayText); - if (error) - return "displayText." + error; - } - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isString(message.id)) - return "id: string expected"; - return null; - }; - - /** - * Creates a QuickReplyButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.QuickReplyButton - * @static - * @param {Object.} object Plain object - * @returns {proto.QuickReplyButton} QuickReplyButton - */ - QuickReplyButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.QuickReplyButton) - return object; - var message = new $root.proto.QuickReplyButton(); - if (object.displayText != null) { - if (typeof object.displayText !== "object") - throw TypeError(".proto.QuickReplyButton.displayText: object expected"); - message.displayText = $root.proto.HighlyStructuredMessage.fromObject(object.displayText); - } - if (object.id != null) - message.id = String(object.id); - return message; - }; - - /** - * Creates a plain object from a QuickReplyButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.QuickReplyButton - * @static - * @param {proto.QuickReplyButton} message QuickReplyButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - QuickReplyButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.displayText = null; - object.id = ""; - } - if (message.displayText != null && message.hasOwnProperty("displayText")) - object.displayText = $root.proto.HighlyStructuredMessage.toObject(message.displayText, options); - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - return object; - }; - - /** - * Converts this QuickReplyButton to JSON. - * @function toJSON - * @memberof proto.QuickReplyButton - * @instance - * @returns {Object.} JSON object - */ - QuickReplyButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return QuickReplyButton; - })(); - - proto.URLButton = (function() { - - /** - * Properties of a URLButton. - * @memberof proto - * @interface IURLButton - * @property {proto.IHighlyStructuredMessage|null} [displayText] URLButton displayText - * @property {proto.IHighlyStructuredMessage|null} [url] URLButton url - */ - - /** - * Constructs a new URLButton. - * @memberof proto - * @classdesc Represents a URLButton. - * @implements IURLButton - * @constructor - * @param {proto.IURLButton=} [properties] Properties to set - */ - function URLButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * URLButton displayText. - * @member {proto.IHighlyStructuredMessage|null|undefined} displayText - * @memberof proto.URLButton - * @instance - */ - URLButton.prototype.displayText = null; - - /** - * URLButton url. - * @member {proto.IHighlyStructuredMessage|null|undefined} url - * @memberof proto.URLButton - * @instance - */ - URLButton.prototype.url = null; - - /** - * Creates a new URLButton instance using the specified properties. - * @function create - * @memberof proto.URLButton - * @static - * @param {proto.IURLButton=} [properties] Properties to set - * @returns {proto.URLButton} URLButton instance - */ - URLButton.create = function create(properties) { - return new URLButton(properties); - }; - - /** - * Encodes the specified URLButton message. Does not implicitly {@link proto.URLButton.verify|verify} messages. - * @function encode - * @memberof proto.URLButton - * @static - * @param {proto.IURLButton} message URLButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - URLButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) - $root.proto.HighlyStructuredMessage.encode(message.displayText, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.url != null && Object.hasOwnProperty.call(message, "url")) - $root.proto.HighlyStructuredMessage.encode(message.url, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); - return writer; - }; - - /** - * Encodes the specified URLButton message, length delimited. Does not implicitly {@link proto.URLButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.URLButton - * @static - * @param {proto.IURLButton} message URLButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - URLButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a URLButton message from the specified reader or buffer. - * @function decode - * @memberof proto.URLButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.URLButton} URLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - URLButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.URLButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.displayText = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); - break; - case 2: - message.url = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a URLButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.URLButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.URLButton} URLButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - URLButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a URLButton message. - * @function verify - * @memberof proto.URLButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - URLButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.displayText != null && message.hasOwnProperty("displayText")) { - var error = $root.proto.HighlyStructuredMessage.verify(message.displayText); - if (error) - return "displayText." + error; - } - if (message.url != null && message.hasOwnProperty("url")) { - var error = $root.proto.HighlyStructuredMessage.verify(message.url); - if (error) - return "url." + error; - } - return null; - }; - - /** - * Creates a URLButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.URLButton - * @static - * @param {Object.} object Plain object - * @returns {proto.URLButton} URLButton - */ - URLButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.URLButton) - return object; - var message = new $root.proto.URLButton(); - if (object.displayText != null) { - if (typeof object.displayText !== "object") - throw TypeError(".proto.URLButton.displayText: object expected"); - message.displayText = $root.proto.HighlyStructuredMessage.fromObject(object.displayText); - } - if (object.url != null) { - if (typeof object.url !== "object") - throw TypeError(".proto.URLButton.url: object expected"); - message.url = $root.proto.HighlyStructuredMessage.fromObject(object.url); - } - return message; - }; - - /** - * Creates a plain object from a URLButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.URLButton - * @static - * @param {proto.URLButton} message URLButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - URLButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.displayText = null; - object.url = null; - } - if (message.displayText != null && message.hasOwnProperty("displayText")) - object.displayText = $root.proto.HighlyStructuredMessage.toObject(message.displayText, options); - if (message.url != null && message.hasOwnProperty("url")) - object.url = $root.proto.HighlyStructuredMessage.toObject(message.url, options); - return object; - }; - - /** - * Converts this URLButton to JSON. - * @function toJSON - * @memberof proto.URLButton - * @instance - * @returns {Object.} JSON object - */ - URLButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return URLButton; - })(); - - proto.CallButton = (function() { - - /** - * Properties of a CallButton. - * @memberof proto - * @interface ICallButton - * @property {proto.IHighlyStructuredMessage|null} [displayText] CallButton displayText - * @property {proto.IHighlyStructuredMessage|null} [phoneNumber] CallButton phoneNumber - */ - - /** - * Constructs a new CallButton. - * @memberof proto - * @classdesc Represents a CallButton. - * @implements ICallButton - * @constructor - * @param {proto.ICallButton=} [properties] Properties to set - */ - function CallButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * CallButton displayText. - * @member {proto.IHighlyStructuredMessage|null|undefined} displayText - * @memberof proto.CallButton - * @instance - */ - CallButton.prototype.displayText = null; - - /** - * CallButton phoneNumber. - * @member {proto.IHighlyStructuredMessage|null|undefined} phoneNumber - * @memberof proto.CallButton - * @instance - */ - CallButton.prototype.phoneNumber = null; - - /** - * Creates a new CallButton instance using the specified properties. - * @function create - * @memberof proto.CallButton - * @static - * @param {proto.ICallButton=} [properties] Properties to set - * @returns {proto.CallButton} CallButton instance - */ - CallButton.create = function create(properties) { - return new CallButton(properties); - }; - - /** - * Encodes the specified CallButton message. Does not implicitly {@link proto.CallButton.verify|verify} messages. - * @function encode - * @memberof proto.CallButton - * @static - * @param {proto.ICallButton} message CallButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - CallButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) - $root.proto.HighlyStructuredMessage.encode(message.displayText, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.phoneNumber != null && Object.hasOwnProperty.call(message, "phoneNumber")) - $root.proto.HighlyStructuredMessage.encode(message.phoneNumber, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); - return writer; - }; - - /** - * Encodes the specified CallButton message, length delimited. Does not implicitly {@link proto.CallButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.CallButton - * @static - * @param {proto.ICallButton} message CallButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - CallButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a CallButton message from the specified reader or buffer. - * @function decode - * @memberof proto.CallButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.CallButton} CallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - CallButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.CallButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.displayText = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); - break; - case 2: - message.phoneNumber = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a CallButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.CallButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.CallButton} CallButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - CallButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a CallButton message. - * @function verify - * @memberof proto.CallButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - CallButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.displayText != null && message.hasOwnProperty("displayText")) { - var error = $root.proto.HighlyStructuredMessage.verify(message.displayText); - if (error) - return "displayText." + error; - } - if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) { - var error = $root.proto.HighlyStructuredMessage.verify(message.phoneNumber); - if (error) - return "phoneNumber." + error; - } - return null; - }; - - /** - * Creates a CallButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.CallButton - * @static - * @param {Object.} object Plain object - * @returns {proto.CallButton} CallButton - */ - CallButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.CallButton) - return object; - var message = new $root.proto.CallButton(); - if (object.displayText != null) { - if (typeof object.displayText !== "object") - throw TypeError(".proto.CallButton.displayText: object expected"); - message.displayText = $root.proto.HighlyStructuredMessage.fromObject(object.displayText); - } - if (object.phoneNumber != null) { - if (typeof object.phoneNumber !== "object") - throw TypeError(".proto.CallButton.phoneNumber: object expected"); - message.phoneNumber = $root.proto.HighlyStructuredMessage.fromObject(object.phoneNumber); - } - return message; - }; - - /** - * Creates a plain object from a CallButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.CallButton - * @static - * @param {proto.CallButton} message CallButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - CallButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.displayText = null; - object.phoneNumber = null; - } - if (message.displayText != null && message.hasOwnProperty("displayText")) - object.displayText = $root.proto.HighlyStructuredMessage.toObject(message.displayText, options); - if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) - object.phoneNumber = $root.proto.HighlyStructuredMessage.toObject(message.phoneNumber, options); - return object; - }; - - /** - * Converts this CallButton to JSON. - * @function toJSON - * @memberof proto.CallButton - * @instance - * @returns {Object.} JSON object - */ - CallButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return CallButton; - })(); - - proto.TemplateButton = (function() { - - /** - * Properties of a TemplateButton. - * @memberof proto - * @interface ITemplateButton - * @property {number|null} [index] TemplateButton index - * @property {proto.IQuickReplyButton|null} [quickReplyButton] TemplateButton quickReplyButton - * @property {proto.IURLButton|null} [urlButton] TemplateButton urlButton - * @property {proto.ICallButton|null} [callButton] TemplateButton callButton - */ - - /** - * Constructs a new TemplateButton. - * @memberof proto - * @classdesc Represents a TemplateButton. - * @implements ITemplateButton - * @constructor - * @param {proto.ITemplateButton=} [properties] Properties to set - */ - function TemplateButton(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * TemplateButton index. - * @member {number} index - * @memberof proto.TemplateButton - * @instance - */ - TemplateButton.prototype.index = 0; - - /** - * TemplateButton quickReplyButton. - * @member {proto.IQuickReplyButton|null|undefined} quickReplyButton - * @memberof proto.TemplateButton - * @instance - */ - TemplateButton.prototype.quickReplyButton = null; - - /** - * TemplateButton urlButton. - * @member {proto.IURLButton|null|undefined} urlButton - * @memberof proto.TemplateButton - * @instance - */ - TemplateButton.prototype.urlButton = null; - - /** - * TemplateButton callButton. - * @member {proto.ICallButton|null|undefined} callButton - * @memberof proto.TemplateButton - * @instance - */ - TemplateButton.prototype.callButton = null; - - // OneOf field names bound to virtual getters and setters - var $oneOfFields; - - /** - * TemplateButton button. - * @member {"quickReplyButton"|"urlButton"|"callButton"|undefined} button - * @memberof proto.TemplateButton - * @instance - */ - Object.defineProperty(TemplateButton.prototype, "button", { - get: $util.oneOfGetter($oneOfFields = ["quickReplyButton", "urlButton", "callButton"]), - set: $util.oneOfSetter($oneOfFields) - }); - - /** - * Creates a new TemplateButton instance using the specified properties. - * @function create - * @memberof proto.TemplateButton - * @static - * @param {proto.ITemplateButton=} [properties] Properties to set - * @returns {proto.TemplateButton} TemplateButton instance - */ - TemplateButton.create = function create(properties) { - return new TemplateButton(properties); - }; - - /** - * Encodes the specified TemplateButton message. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. - * @function encode - * @memberof proto.TemplateButton - * @static - * @param {proto.ITemplateButton} message TemplateButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - TemplateButton.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.quickReplyButton != null && Object.hasOwnProperty.call(message, "quickReplyButton")) - $root.proto.QuickReplyButton.encode(message.quickReplyButton, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.urlButton != null && Object.hasOwnProperty.call(message, "urlButton")) - $root.proto.URLButton.encode(message.urlButton, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); - if (message.callButton != null && Object.hasOwnProperty.call(message, "callButton")) - $root.proto.CallButton.encode(message.callButton, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); - if (message.index != null && Object.hasOwnProperty.call(message, "index")) - writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.index); - return writer; - }; - - /** - * Encodes the specified TemplateButton message, length delimited. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.TemplateButton - * @static - * @param {proto.ITemplateButton} message TemplateButton message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - TemplateButton.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a TemplateButton message from the specified reader or buffer. - * @function decode - * @memberof proto.TemplateButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.TemplateButton} TemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - TemplateButton.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.TemplateButton(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 4: - message.index = reader.uint32(); - break; - case 1: - message.quickReplyButton = $root.proto.QuickReplyButton.decode(reader, reader.uint32()); - break; - case 2: - message.urlButton = $root.proto.URLButton.decode(reader, reader.uint32()); - break; - case 3: - message.callButton = $root.proto.CallButton.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a TemplateButton message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.TemplateButton - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.TemplateButton} TemplateButton - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - TemplateButton.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a TemplateButton message. - * @function verify - * @memberof proto.TemplateButton - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - TemplateButton.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - var properties = {}; - if (message.index != null && message.hasOwnProperty("index")) - if (!$util.isInteger(message.index)) - return "index: integer expected"; - if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { - properties.button = 1; - { - var error = $root.proto.QuickReplyButton.verify(message.quickReplyButton); - if (error) - return "quickReplyButton." + error; - } - } - if (message.urlButton != null && message.hasOwnProperty("urlButton")) { - if (properties.button === 1) - return "button: multiple values"; - properties.button = 1; - { - var error = $root.proto.URLButton.verify(message.urlButton); - if (error) - return "urlButton." + error; - } - } - if (message.callButton != null && message.hasOwnProperty("callButton")) { - if (properties.button === 1) - return "button: multiple values"; - properties.button = 1; - { - var error = $root.proto.CallButton.verify(message.callButton); - if (error) - return "callButton." + error; - } - } - return null; - }; - - /** - * Creates a TemplateButton message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.TemplateButton - * @static - * @param {Object.} object Plain object - * @returns {proto.TemplateButton} TemplateButton - */ - TemplateButton.fromObject = function fromObject(object) { - if (object instanceof $root.proto.TemplateButton) - return object; - var message = new $root.proto.TemplateButton(); - if (object.index != null) - message.index = object.index >>> 0; - if (object.quickReplyButton != null) { - if (typeof object.quickReplyButton !== "object") - throw TypeError(".proto.TemplateButton.quickReplyButton: object expected"); - message.quickReplyButton = $root.proto.QuickReplyButton.fromObject(object.quickReplyButton); - } - if (object.urlButton != null) { - if (typeof object.urlButton !== "object") - throw TypeError(".proto.TemplateButton.urlButton: object expected"); - message.urlButton = $root.proto.URLButton.fromObject(object.urlButton); - } - if (object.callButton != null) { - if (typeof object.callButton !== "object") - throw TypeError(".proto.TemplateButton.callButton: object expected"); - message.callButton = $root.proto.CallButton.fromObject(object.callButton); - } - return message; - }; - - /** - * Creates a plain object from a TemplateButton message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.TemplateButton - * @static - * @param {proto.TemplateButton} message TemplateButton - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - TemplateButton.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) - object.index = 0; - if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { - object.quickReplyButton = $root.proto.QuickReplyButton.toObject(message.quickReplyButton, options); - if (options.oneofs) - object.button = "quickReplyButton"; - } - if (message.urlButton != null && message.hasOwnProperty("urlButton")) { - object.urlButton = $root.proto.URLButton.toObject(message.urlButton, options); - if (options.oneofs) - object.button = "urlButton"; - } - if (message.callButton != null && message.hasOwnProperty("callButton")) { - object.callButton = $root.proto.CallButton.toObject(message.callButton, options); - if (options.oneofs) - object.button = "callButton"; - } - if (message.index != null && message.hasOwnProperty("index")) - object.index = message.index; - return object; - }; - - /** - * Converts this TemplateButton to JSON. - * @function toJSON - * @memberof proto.TemplateButton - * @instance - * @returns {Object.} JSON object - */ - TemplateButton.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return TemplateButton; - })(); - - proto.Location = (function() { - - /** - * Properties of a Location. - * @memberof proto - * @interface ILocation - * @property {number|null} [degreesLatitude] Location degreesLatitude - * @property {number|null} [degreesLongitude] Location degreesLongitude - * @property {string|null} [name] Location name - */ - - /** - * Constructs a new Location. - * @memberof proto - * @classdesc Represents a Location. - * @implements ILocation - * @constructor - * @param {proto.ILocation=} [properties] Properties to set - */ - function Location(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Location degreesLatitude. - * @member {number} degreesLatitude - * @memberof proto.Location - * @instance - */ - Location.prototype.degreesLatitude = 0; - - /** - * Location degreesLongitude. - * @member {number} degreesLongitude - * @memberof proto.Location - * @instance - */ - Location.prototype.degreesLongitude = 0; - - /** - * Location name. - * @member {string} name - * @memberof proto.Location - * @instance - */ - Location.prototype.name = ""; - - /** - * Creates a new Location instance using the specified properties. - * @function create - * @memberof proto.Location - * @static - * @param {proto.ILocation=} [properties] Properties to set - * @returns {proto.Location} Location instance - */ - Location.create = function create(properties) { - return new Location(properties); - }; - - /** - * Encodes the specified Location message. Does not implicitly {@link proto.Location.verify|verify} messages. - * @function encode - * @memberof proto.Location - * @static - * @param {proto.ILocation} message Location message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Location.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.degreesLatitude != null && Object.hasOwnProperty.call(message, "degreesLatitude")) - writer.uint32(/* id 1, wireType 1 =*/9).double(message.degreesLatitude); - if (message.degreesLongitude != null && Object.hasOwnProperty.call(message, "degreesLongitude")) - writer.uint32(/* id 2, wireType 1 =*/17).double(message.degreesLongitude); - if (message.name != null && Object.hasOwnProperty.call(message, "name")) - writer.uint32(/* id 3, wireType 2 =*/26).string(message.name); - return writer; - }; - - /** - * Encodes the specified Location message, length delimited. Does not implicitly {@link proto.Location.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.Location - * @static - * @param {proto.ILocation} message Location message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Location.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Location message from the specified reader or buffer. - * @function decode - * @memberof proto.Location - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.Location} Location - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Location.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Location(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.degreesLatitude = reader.double(); - break; - case 2: - message.degreesLongitude = reader.double(); - break; - case 3: - message.name = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Location message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.Location - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.Location} Location - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Location.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Location message. - * @function verify - * @memberof proto.Location - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Location.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.degreesLatitude != null && message.hasOwnProperty("degreesLatitude")) - if (typeof message.degreesLatitude !== "number") - return "degreesLatitude: number expected"; - if (message.degreesLongitude != null && message.hasOwnProperty("degreesLongitude")) - if (typeof message.degreesLongitude !== "number") - return "degreesLongitude: number expected"; - if (message.name != null && message.hasOwnProperty("name")) - if (!$util.isString(message.name)) - return "name: string expected"; - return null; - }; - - /** - * Creates a Location message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.Location - * @static - * @param {Object.} object Plain object - * @returns {proto.Location} Location - */ - Location.fromObject = function fromObject(object) { - if (object instanceof $root.proto.Location) - return object; - var message = new $root.proto.Location(); - if (object.degreesLatitude != null) - message.degreesLatitude = Number(object.degreesLatitude); - if (object.degreesLongitude != null) - message.degreesLongitude = Number(object.degreesLongitude); - if (object.name != null) - message.name = String(object.name); - return message; - }; - - /** - * Creates a plain object from a Location message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.Location - * @static - * @param {proto.Location} message Location - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Location.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.degreesLatitude = 0; - object.degreesLongitude = 0; - object.name = ""; - } - if (message.degreesLatitude != null && message.hasOwnProperty("degreesLatitude")) - object.degreesLatitude = options.json && !isFinite(message.degreesLatitude) ? String(message.degreesLatitude) : message.degreesLatitude; - if (message.degreesLongitude != null && message.hasOwnProperty("degreesLongitude")) - object.degreesLongitude = options.json && !isFinite(message.degreesLongitude) ? String(message.degreesLongitude) : message.degreesLongitude; - if (message.name != null && message.hasOwnProperty("name")) - object.name = message.name; - return object; - }; - - /** - * Converts this Location to JSON. - * @function toJSON - * @memberof proto.Location - * @instance - * @returns {Object.} JSON object - */ - Location.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Location; - })(); - - proto.Point = (function() { - - /** - * Properties of a Point. - * @memberof proto - * @interface IPoint - * @property {number|null} [xDeprecated] Point xDeprecated - * @property {number|null} [yDeprecated] Point yDeprecated - * @property {number|null} [x] Point x - * @property {number|null} [y] Point y - */ - - /** - * Constructs a new Point. - * @memberof proto - * @classdesc Represents a Point. - * @implements IPoint - * @constructor - * @param {proto.IPoint=} [properties] Properties to set - */ - function Point(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Point xDeprecated. - * @member {number} xDeprecated - * @memberof proto.Point - * @instance - */ - Point.prototype.xDeprecated = 0; - - /** - * Point yDeprecated. - * @member {number} yDeprecated - * @memberof proto.Point - * @instance - */ - Point.prototype.yDeprecated = 0; - - /** - * Point x. - * @member {number} x - * @memberof proto.Point - * @instance - */ - Point.prototype.x = 0; - - /** - * Point y. - * @member {number} y - * @memberof proto.Point - * @instance - */ - Point.prototype.y = 0; - - /** - * Creates a new Point instance using the specified properties. - * @function create - * @memberof proto.Point - * @static - * @param {proto.IPoint=} [properties] Properties to set - * @returns {proto.Point} Point instance - */ - Point.create = function create(properties) { - return new Point(properties); - }; - - /** - * Encodes the specified Point message. Does not implicitly {@link proto.Point.verify|verify} messages. - * @function encode - * @memberof proto.Point - * @static - * @param {proto.IPoint} message Point message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Point.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.xDeprecated != null && Object.hasOwnProperty.call(message, "xDeprecated")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.xDeprecated); - if (message.yDeprecated != null && Object.hasOwnProperty.call(message, "yDeprecated")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.yDeprecated); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 3, wireType 1 =*/25).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.y); - return writer; - }; - - /** - * Encodes the specified Point message, length delimited. Does not implicitly {@link proto.Point.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.Point - * @static - * @param {proto.IPoint} message Point message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Point.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Point message from the specified reader or buffer. - * @function decode - * @memberof proto.Point - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.Point} Point - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Point.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Point(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.xDeprecated = reader.int32(); - break; - case 2: - message.yDeprecated = reader.int32(); - break; - case 3: - message.x = reader.double(); - break; - case 4: - message.y = reader.double(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Point message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.Point - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.Point} Point - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Point.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Point message. - * @function verify - * @memberof proto.Point - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Point.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.xDeprecated != null && message.hasOwnProperty("xDeprecated")) - if (!$util.isInteger(message.xDeprecated)) - return "xDeprecated: integer expected"; - if (message.yDeprecated != null && message.hasOwnProperty("yDeprecated")) - if (!$util.isInteger(message.yDeprecated)) - return "yDeprecated: integer expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - return null; - }; - - /** - * Creates a Point message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.Point - * @static - * @param {Object.} object Plain object - * @returns {proto.Point} Point - */ - Point.fromObject = function fromObject(object) { - if (object instanceof $root.proto.Point) - return object; - var message = new $root.proto.Point(); - if (object.xDeprecated != null) - message.xDeprecated = object.xDeprecated | 0; - if (object.yDeprecated != null) - message.yDeprecated = object.yDeprecated | 0; - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - return message; - }; - - /** - * Creates a plain object from a Point message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.Point - * @static - * @param {proto.Point} message Point - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Point.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.xDeprecated = 0; - object.yDeprecated = 0; - object.x = 0; - object.y = 0; - } - if (message.xDeprecated != null && message.hasOwnProperty("xDeprecated")) - object.xDeprecated = message.xDeprecated; - if (message.yDeprecated != null && message.hasOwnProperty("yDeprecated")) - object.yDeprecated = message.yDeprecated; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - return object; - }; - - /** - * Converts this Point to JSON. - * @function toJSON - * @memberof proto.Point - * @instance - * @returns {Object.} JSON object - */ - Point.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Point; - })(); - proto.InteractiveAnnotation = (function() { /** @@ -22336,6 +21853,10 @@ $root.proto = (function() { * @property {string|null} [entryPointConversionSource] ContextInfo entryPointConversionSource * @property {string|null} [entryPointConversionApp] ContextInfo entryPointConversionApp * @property {number|null} [entryPointConversionDelaySeconds] ContextInfo entryPointConversionDelaySeconds + * @property {proto.IDisappearingMode|null} [disappearingMode] ContextInfo disappearingMode + * @property {proto.IActionLink|null} [actionLink] ContextInfo actionLink + * @property {string|null} [groupSubject] ContextInfo groupSubject + * @property {string|null} [parentGroupJid] ContextInfo parentGroupJid */ /** @@ -22506,6 +22027,38 @@ $root.proto = (function() { */ ContextInfo.prototype.entryPointConversionDelaySeconds = 0; + /** + * ContextInfo disappearingMode. + * @member {proto.IDisappearingMode|null|undefined} disappearingMode + * @memberof proto.ContextInfo + * @instance + */ + ContextInfo.prototype.disappearingMode = null; + + /** + * ContextInfo actionLink. + * @member {proto.IActionLink|null|undefined} actionLink + * @memberof proto.ContextInfo + * @instance + */ + ContextInfo.prototype.actionLink = null; + + /** + * ContextInfo groupSubject. + * @member {string} groupSubject + * @memberof proto.ContextInfo + * @instance + */ + ContextInfo.prototype.groupSubject = ""; + + /** + * ContextInfo parentGroupJid. + * @member {string} parentGroupJid + * @memberof proto.ContextInfo + * @instance + */ + ContextInfo.prototype.parentGroupJid = ""; + /** * Creates a new ContextInfo instance using the specified properties. * @function create @@ -22569,6 +22122,14 @@ $root.proto = (function() { writer.uint32(/* id 30, wireType 2 =*/242).string(message.entryPointConversionApp); if (message.entryPointConversionDelaySeconds != null && Object.hasOwnProperty.call(message, "entryPointConversionDelaySeconds")) writer.uint32(/* id 31, wireType 0 =*/248).uint32(message.entryPointConversionDelaySeconds); + if (message.disappearingMode != null && Object.hasOwnProperty.call(message, "disappearingMode")) + $root.proto.DisappearingMode.encode(message.disappearingMode, writer.uint32(/* id 32, wireType 2 =*/258).fork()).ldelim(); + if (message.actionLink != null && Object.hasOwnProperty.call(message, "actionLink")) + $root.proto.ActionLink.encode(message.actionLink, writer.uint32(/* id 33, wireType 2 =*/266).fork()).ldelim(); + if (message.groupSubject != null && Object.hasOwnProperty.call(message, "groupSubject")) + writer.uint32(/* id 34, wireType 2 =*/274).string(message.groupSubject); + if (message.parentGroupJid != null && Object.hasOwnProperty.call(message, "parentGroupJid")) + writer.uint32(/* id 35, wireType 2 =*/282).string(message.parentGroupJid); return writer; }; @@ -22662,6 +22223,18 @@ $root.proto = (function() { case 31: message.entryPointConversionDelaySeconds = reader.uint32(); break; + case 32: + message.disappearingMode = $root.proto.DisappearingMode.decode(reader, reader.uint32()); + break; + case 33: + message.actionLink = $root.proto.ActionLink.decode(reader, reader.uint32()); + break; + case 34: + message.groupSubject = reader.string(); + break; + case 35: + message.parentGroupJid = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -22766,6 +22339,22 @@ $root.proto = (function() { if (message.entryPointConversionDelaySeconds != null && message.hasOwnProperty("entryPointConversionDelaySeconds")) if (!$util.isInteger(message.entryPointConversionDelaySeconds)) return "entryPointConversionDelaySeconds: integer expected"; + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) { + var error = $root.proto.DisappearingMode.verify(message.disappearingMode); + if (error) + return "disappearingMode." + error; + } + if (message.actionLink != null && message.hasOwnProperty("actionLink")) { + var error = $root.proto.ActionLink.verify(message.actionLink); + if (error) + return "actionLink." + error; + } + if (message.groupSubject != null && message.hasOwnProperty("groupSubject")) + if (!$util.isString(message.groupSubject)) + return "groupSubject: string expected"; + if (message.parentGroupJid != null && message.hasOwnProperty("parentGroupJid")) + if (!$util.isString(message.parentGroupJid)) + return "parentGroupJid: string expected"; return null; }; @@ -22849,6 +22438,20 @@ $root.proto = (function() { message.entryPointConversionApp = String(object.entryPointConversionApp); if (object.entryPointConversionDelaySeconds != null) message.entryPointConversionDelaySeconds = object.entryPointConversionDelaySeconds >>> 0; + if (object.disappearingMode != null) { + if (typeof object.disappearingMode !== "object") + throw TypeError(".proto.ContextInfo.disappearingMode: object expected"); + message.disappearingMode = $root.proto.DisappearingMode.fromObject(object.disappearingMode); + } + if (object.actionLink != null) { + if (typeof object.actionLink !== "object") + throw TypeError(".proto.ContextInfo.actionLink: object expected"); + message.actionLink = $root.proto.ActionLink.fromObject(object.actionLink); + } + if (object.groupSubject != null) + message.groupSubject = String(object.groupSubject); + if (object.parentGroupJid != null) + message.parentGroupJid = String(object.parentGroupJid); return message; }; @@ -22902,6 +22505,10 @@ $root.proto = (function() { object.entryPointConversionSource = ""; object.entryPointConversionApp = ""; object.entryPointConversionDelaySeconds = 0; + object.disappearingMode = null; + object.actionLink = null; + object.groupSubject = ""; + object.parentGroupJid = ""; } if (message.stanzaId != null && message.hasOwnProperty("stanzaId")) object.stanzaId = message.stanzaId; @@ -22947,6 +22554,14 @@ $root.proto = (function() { object.entryPointConversionApp = message.entryPointConversionApp; if (message.entryPointConversionDelaySeconds != null && message.hasOwnProperty("entryPointConversionDelaySeconds")) object.entryPointConversionDelaySeconds = message.entryPointConversionDelaySeconds; + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) + object.disappearingMode = $root.proto.DisappearingMode.toObject(message.disappearingMode, options); + if (message.actionLink != null && message.hasOwnProperty("actionLink")) + object.actionLink = $root.proto.ActionLink.toObject(message.actionLink, options); + if (message.groupSubject != null && message.hasOwnProperty("groupSubject")) + object.groupSubject = message.groupSubject; + if (message.parentGroupJid != null && message.hasOwnProperty("parentGroupJid")) + object.parentGroupJid = message.parentGroupJid; return object; }; @@ -23214,6 +22829,7 @@ $root.proto = (function() { * @property {string|null} [thumbnailDirectPath] ImageMessage thumbnailDirectPath * @property {Uint8Array|null} [thumbnailSha256] ImageMessage thumbnailSha256 * @property {Uint8Array|null} [thumbnailEncSha256] ImageMessage thumbnailEncSha256 + * @property {string|null} [staticUrl] ImageMessage staticUrl */ /** @@ -23433,6 +23049,14 @@ $root.proto = (function() { */ ImageMessage.prototype.thumbnailEncSha256 = $util.newBuffer([]); + /** + * ImageMessage staticUrl. + * @member {string} staticUrl + * @memberof proto.ImageMessage + * @instance + */ + ImageMessage.prototype.staticUrl = ""; + /** * Creates a new ImageMessage instance using the specified properties. * @function create @@ -23509,6 +23133,8 @@ $root.proto = (function() { writer.uint32(/* id 27, wireType 2 =*/218).bytes(message.thumbnailSha256); if (message.thumbnailEncSha256 != null && Object.hasOwnProperty.call(message, "thumbnailEncSha256")) writer.uint32(/* id 28, wireType 2 =*/226).bytes(message.thumbnailEncSha256); + if (message.staticUrl != null && Object.hasOwnProperty.call(message, "staticUrl")) + writer.uint32(/* id 29, wireType 2 =*/234).string(message.staticUrl); return writer; }; @@ -23627,6 +23253,9 @@ $root.proto = (function() { case 28: message.thumbnailEncSha256 = reader.bytes(); break; + case 29: + message.staticUrl = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -23749,6 +23378,9 @@ $root.proto = (function() { if (message.thumbnailEncSha256 != null && message.hasOwnProperty("thumbnailEncSha256")) if (!(message.thumbnailEncSha256 && typeof message.thumbnailEncSha256.length === "number" || $util.isString(message.thumbnailEncSha256))) return "thumbnailEncSha256: buffer expected"; + if (message.staticUrl != null && message.hasOwnProperty("staticUrl")) + if (!$util.isString(message.staticUrl)) + return "staticUrl: string expected"; return null; }; @@ -23874,6 +23506,8 @@ $root.proto = (function() { $util.base64.decode(object.thumbnailEncSha256, message.thumbnailEncSha256 = $util.newBuffer($util.base64.length(object.thumbnailEncSha256)), 0); else if (object.thumbnailEncSha256.length) message.thumbnailEncSha256 = object.thumbnailEncSha256; + if (object.staticUrl != null) + message.staticUrl = String(object.staticUrl); return message; }; @@ -23986,6 +23620,7 @@ $root.proto = (function() { if (options.bytes !== Array) object.thumbnailEncSha256 = $util.newBuffer(object.thumbnailEncSha256); } + object.staticUrl = ""; } if (message.url != null && message.hasOwnProperty("url")) object.url = message.url; @@ -24049,6 +23684,8 @@ $root.proto = (function() { object.thumbnailSha256 = options.bytes === String ? $util.base64.encode(message.thumbnailSha256, 0, message.thumbnailSha256.length) : options.bytes === Array ? Array.prototype.slice.call(message.thumbnailSha256) : message.thumbnailSha256; if (message.thumbnailEncSha256 != null && message.hasOwnProperty("thumbnailEncSha256")) object.thumbnailEncSha256 = options.bytes === String ? $util.base64.encode(message.thumbnailEncSha256, 0, message.thumbnailEncSha256.length) : options.bytes === Array ? Array.prototype.slice.call(message.thumbnailEncSha256) : message.thumbnailEncSha256; + if (message.staticUrl != null && message.hasOwnProperty("staticUrl")) + object.staticUrl = message.staticUrl; return object; }; @@ -25235,6 +24872,7 @@ $root.proto = (function() { * @property {number|Long|null} [mediaKeyTimestamp] ExtendedTextMessage mediaKeyTimestamp * @property {number|null} [thumbnailHeight] ExtendedTextMessage thumbnailHeight * @property {number|null} [thumbnailWidth] ExtendedTextMessage thumbnailWidth + * @property {proto.ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType|null} [inviteLinkGroupType] ExtendedTextMessage inviteLinkGroupType */ /** @@ -25404,6 +25042,14 @@ $root.proto = (function() { */ ExtendedTextMessage.prototype.thumbnailWidth = 0; + /** + * ExtendedTextMessage inviteLinkGroupType. + * @member {proto.ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType} inviteLinkGroupType + * @memberof proto.ExtendedTextMessage + * @instance + */ + ExtendedTextMessage.prototype.inviteLinkGroupType = 0; + /** * Creates a new ExtendedTextMessage instance using the specified properties. * @function create @@ -25466,6 +25112,8 @@ $root.proto = (function() { writer.uint32(/* id 24, wireType 0 =*/192).uint32(message.thumbnailHeight); if (message.thumbnailWidth != null && Object.hasOwnProperty.call(message, "thumbnailWidth")) writer.uint32(/* id 25, wireType 0 =*/200).uint32(message.thumbnailWidth); + if (message.inviteLinkGroupType != null && Object.hasOwnProperty.call(message, "inviteLinkGroupType")) + writer.uint32(/* id 26, wireType 0 =*/208).int32(message.inviteLinkGroupType); return writer; }; @@ -25557,6 +25205,9 @@ $root.proto = (function() { case 25: message.thumbnailWidth = reader.uint32(); break; + case 26: + message.inviteLinkGroupType = reader.int32(); + break; default: reader.skipType(tag & 7); break; @@ -25665,6 +25316,14 @@ $root.proto = (function() { if (message.thumbnailWidth != null && message.hasOwnProperty("thumbnailWidth")) if (!$util.isInteger(message.thumbnailWidth)) return "thumbnailWidth: integer expected"; + if (message.inviteLinkGroupType != null && message.hasOwnProperty("inviteLinkGroupType")) + switch (message.inviteLinkGroupType) { + default: + return "inviteLinkGroupType: enum value expected"; + case 0: + case 1: + break; + } return null; }; @@ -25772,6 +25431,16 @@ $root.proto = (function() { message.thumbnailHeight = object.thumbnailHeight >>> 0; if (object.thumbnailWidth != null) message.thumbnailWidth = object.thumbnailWidth >>> 0; + switch (object.inviteLinkGroupType) { + case "DEFAULT": + case 0: + message.inviteLinkGroupType = 0; + break; + case "PARENT": + case 1: + message.inviteLinkGroupType = 1; + break; + } return message; }; @@ -25836,6 +25505,7 @@ $root.proto = (function() { object.mediaKeyTimestamp = options.longs === String ? "0" : 0; object.thumbnailHeight = 0; object.thumbnailWidth = 0; + object.inviteLinkGroupType = options.enums === String ? "DEFAULT" : 0; } if (message.text != null && message.hasOwnProperty("text")) object.text = message.text; @@ -25878,6 +25548,8 @@ $root.proto = (function() { object.thumbnailHeight = message.thumbnailHeight; if (message.thumbnailWidth != null && message.hasOwnProperty("thumbnailWidth")) object.thumbnailWidth = message.thumbnailWidth; + if (message.inviteLinkGroupType != null && message.hasOwnProperty("inviteLinkGroupType")) + object.inviteLinkGroupType = options.enums === String ? $root.proto.ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType[message.inviteLinkGroupType] : message.inviteLinkGroupType; return object; }; @@ -25928,6 +25600,20 @@ $root.proto = (function() { return values; })(); + /** + * ExtendedTextMessageInviteLinkGroupType enum. + * @name proto.ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType + * @enum {number} + * @property {number} DEFAULT=0 DEFAULT value + * @property {number} PARENT=1 PARENT value + */ + ExtendedTextMessage.ExtendedTextMessageInviteLinkGroupType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "DEFAULT"] = 0; + values[valuesById[1] = "PARENT"] = 1; + return values; + })(); + return ExtendedTextMessage; })(); @@ -26620,6 +26306,7 @@ $root.proto = (function() { * @property {number|Long|null} [mediaKeyTimestamp] AudioMessage mediaKeyTimestamp * @property {proto.IContextInfo|null} [contextInfo] AudioMessage contextInfo * @property {Uint8Array|null} [streamingSidecar] AudioMessage streamingSidecar + * @property {Uint8Array|null} [waveform] AudioMessage waveform */ /** @@ -26733,6 +26420,14 @@ $root.proto = (function() { */ AudioMessage.prototype.streamingSidecar = $util.newBuffer([]); + /** + * AudioMessage waveform. + * @member {Uint8Array} waveform + * @memberof proto.AudioMessage + * @instance + */ + AudioMessage.prototype.waveform = $util.newBuffer([]); + /** * Creates a new AudioMessage instance using the specified properties. * @function create @@ -26781,6 +26476,8 @@ $root.proto = (function() { $root.proto.ContextInfo.encode(message.contextInfo, writer.uint32(/* id 17, wireType 2 =*/138).fork()).ldelim(); if (message.streamingSidecar != null && Object.hasOwnProperty.call(message, "streamingSidecar")) writer.uint32(/* id 18, wireType 2 =*/146).bytes(message.streamingSidecar); + if (message.waveform != null && Object.hasOwnProperty.call(message, "waveform")) + writer.uint32(/* id 19, wireType 2 =*/154).bytes(message.waveform); return writer; }; @@ -26851,6 +26548,9 @@ $root.proto = (function() { case 18: message.streamingSidecar = reader.bytes(); break; + case 19: + message.waveform = reader.bytes(); + break; default: reader.skipType(tag & 7); break; @@ -26924,6 +26624,9 @@ $root.proto = (function() { if (message.streamingSidecar != null && message.hasOwnProperty("streamingSidecar")) if (!(message.streamingSidecar && typeof message.streamingSidecar.length === "number" || $util.isString(message.streamingSidecar))) return "streamingSidecar: buffer expected"; + if (message.waveform != null && message.hasOwnProperty("waveform")) + if (!(message.waveform && typeof message.waveform.length === "number" || $util.isString(message.waveform))) + return "waveform: buffer expected"; return null; }; @@ -26992,6 +26695,11 @@ $root.proto = (function() { $util.base64.decode(object.streamingSidecar, message.streamingSidecar = $util.newBuffer($util.base64.length(object.streamingSidecar)), 0); else if (object.streamingSidecar.length) message.streamingSidecar = object.streamingSidecar; + if (object.waveform != null) + if (typeof object.waveform === "string") + $util.base64.decode(object.waveform, message.waveform = $util.newBuffer($util.base64.length(object.waveform)), 0); + else if (object.waveform.length) + message.waveform = object.waveform; return message; }; @@ -27053,6 +26761,13 @@ $root.proto = (function() { if (options.bytes !== Array) object.streamingSidecar = $util.newBuffer(object.streamingSidecar); } + if (options.bytes === String) + object.waveform = ""; + else { + object.waveform = []; + if (options.bytes !== Array) + object.waveform = $util.newBuffer(object.waveform); + } } if (message.url != null && message.hasOwnProperty("url")) object.url = message.url; @@ -27084,6 +26799,8 @@ $root.proto = (function() { object.contextInfo = $root.proto.ContextInfo.toObject(message.contextInfo, options); if (message.streamingSidecar != null && message.hasOwnProperty("streamingSidecar")) object.streamingSidecar = options.bytes === String ? $util.base64.encode(message.streamingSidecar, 0, message.streamingSidecar.length) : options.bytes === Array ? Array.prototype.slice.call(message.streamingSidecar) : message.streamingSidecar; + if (message.waveform != null && message.hasOwnProperty("waveform")) + object.waveform = options.bytes === String ? $util.base64.encode(message.waveform, 0, message.waveform.length) : options.bytes === Array ? Array.prototype.slice.call(message.waveform) : message.waveform; return object; }; @@ -27129,6 +26846,7 @@ $root.proto = (function() { * @property {string|null} [thumbnailDirectPath] VideoMessage thumbnailDirectPath * @property {Uint8Array|null} [thumbnailSha256] VideoMessage thumbnailSha256 * @property {Uint8Array|null} [thumbnailEncSha256] VideoMessage thumbnailEncSha256 + * @property {string|null} [staticUrl] VideoMessage staticUrl */ /** @@ -27323,6 +27041,14 @@ $root.proto = (function() { */ VideoMessage.prototype.thumbnailEncSha256 = $util.newBuffer([]); + /** + * VideoMessage staticUrl. + * @member {string} staticUrl + * @memberof proto.VideoMessage + * @instance + */ + VideoMessage.prototype.staticUrl = ""; + /** * Creates a new VideoMessage instance using the specified properties. * @function create @@ -27392,6 +27118,8 @@ $root.proto = (function() { writer.uint32(/* id 22, wireType 2 =*/178).bytes(message.thumbnailSha256); if (message.thumbnailEncSha256 != null && Object.hasOwnProperty.call(message, "thumbnailEncSha256")) writer.uint32(/* id 23, wireType 2 =*/186).bytes(message.thumbnailEncSha256); + if (message.staticUrl != null && Object.hasOwnProperty.call(message, "staticUrl")) + writer.uint32(/* id 24, wireType 2 =*/194).string(message.staticUrl); return writer; }; @@ -27494,6 +27222,9 @@ $root.proto = (function() { case 23: message.thumbnailEncSha256 = reader.bytes(); break; + case 24: + message.staticUrl = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -27609,6 +27340,9 @@ $root.proto = (function() { if (message.thumbnailEncSha256 != null && message.hasOwnProperty("thumbnailEncSha256")) if (!(message.thumbnailEncSha256 && typeof message.thumbnailEncSha256.length === "number" || $util.isString(message.thumbnailEncSha256))) return "thumbnailEncSha256: buffer expected"; + if (message.staticUrl != null && message.hasOwnProperty("staticUrl")) + if (!$util.isString(message.staticUrl)) + return "staticUrl: string expected"; return null; }; @@ -27726,6 +27460,8 @@ $root.proto = (function() { $util.base64.decode(object.thumbnailEncSha256, message.thumbnailEncSha256 = $util.newBuffer($util.base64.length(object.thumbnailEncSha256)), 0); else if (object.thumbnailEncSha256.length) message.thumbnailEncSha256 = object.thumbnailEncSha256; + if (object.staticUrl != null) + message.staticUrl = String(object.staticUrl); return message; }; @@ -27816,6 +27552,7 @@ $root.proto = (function() { if (options.bytes !== Array) object.thumbnailEncSha256 = $util.newBuffer(object.thumbnailEncSha256); } + object.staticUrl = ""; } if (message.url != null && message.hasOwnProperty("url")) object.url = message.url; @@ -27870,6 +27607,8 @@ $root.proto = (function() { object.thumbnailSha256 = options.bytes === String ? $util.base64.encode(message.thumbnailSha256, 0, message.thumbnailSha256.length) : options.bytes === Array ? Array.prototype.slice.call(message.thumbnailSha256) : message.thumbnailSha256; if (message.thumbnailEncSha256 != null && message.hasOwnProperty("thumbnailEncSha256")) object.thumbnailEncSha256 = options.bytes === String ? $util.base64.encode(message.thumbnailEncSha256, 0, message.thumbnailEncSha256.length) : options.bytes === Array ? Array.prototype.slice.call(message.thumbnailEncSha256) : message.thumbnailEncSha256; + if (message.staticUrl != null && message.hasOwnProperty("staticUrl")) + object.staticUrl = message.staticUrl; return object; }; @@ -28400,6 +28139,7 @@ $root.proto = (function() { * @property {proto.IAppStateSyncKeyRequest|null} [appStateSyncKeyRequest] ProtocolMessage appStateSyncKeyRequest * @property {proto.IInitialSecurityNotificationSettingSync|null} [initialSecurityNotificationSettingSync] ProtocolMessage initialSecurityNotificationSettingSync * @property {proto.IAppStateFatalExceptionNotification|null} [appStateFatalExceptionNotification] ProtocolMessage appStateFatalExceptionNotification + * @property {proto.IDisappearingMode|null} [disappearingMode] ProtocolMessage disappearingMode */ /** @@ -28489,6 +28229,14 @@ $root.proto = (function() { */ ProtocolMessage.prototype.appStateFatalExceptionNotification = null; + /** + * ProtocolMessage disappearingMode. + * @member {proto.IDisappearingMode|null|undefined} disappearingMode + * @memberof proto.ProtocolMessage + * @instance + */ + ProtocolMessage.prototype.disappearingMode = null; + /** * Creates a new ProtocolMessage instance using the specified properties. * @function create @@ -28531,6 +28279,8 @@ $root.proto = (function() { $root.proto.InitialSecurityNotificationSettingSync.encode(message.initialSecurityNotificationSettingSync, writer.uint32(/* id 9, wireType 2 =*/74).fork()).ldelim(); if (message.appStateFatalExceptionNotification != null && Object.hasOwnProperty.call(message, "appStateFatalExceptionNotification")) $root.proto.AppStateFatalExceptionNotification.encode(message.appStateFatalExceptionNotification, writer.uint32(/* id 10, wireType 2 =*/82).fork()).ldelim(); + if (message.disappearingMode != null && Object.hasOwnProperty.call(message, "disappearingMode")) + $root.proto.DisappearingMode.encode(message.disappearingMode, writer.uint32(/* id 11, wireType 2 =*/90).fork()).ldelim(); return writer; }; @@ -28592,6 +28342,9 @@ $root.proto = (function() { case 10: message.appStateFatalExceptionNotification = $root.proto.AppStateFatalExceptionNotification.decode(reader, reader.uint32()); break; + case 11: + message.disappearingMode = $root.proto.DisappearingMode.decode(reader, reader.uint32()); + break; default: reader.skipType(tag & 7); break; @@ -28678,6 +28431,11 @@ $root.proto = (function() { if (error) return "appStateFatalExceptionNotification." + error; } + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) { + var error = $root.proto.DisappearingMode.verify(message.disappearingMode); + if (error) + return "disappearingMode." + error; + } return null; }; @@ -28772,6 +28530,11 @@ $root.proto = (function() { throw TypeError(".proto.ProtocolMessage.appStateFatalExceptionNotification: object expected"); message.appStateFatalExceptionNotification = $root.proto.AppStateFatalExceptionNotification.fromObject(object.appStateFatalExceptionNotification); } + if (object.disappearingMode != null) { + if (typeof object.disappearingMode !== "object") + throw TypeError(".proto.ProtocolMessage.disappearingMode: object expected"); + message.disappearingMode = $root.proto.DisappearingMode.fromObject(object.disappearingMode); + } return message; }; @@ -28802,6 +28565,7 @@ $root.proto = (function() { object.appStateSyncKeyRequest = null; object.initialSecurityNotificationSettingSync = null; object.appStateFatalExceptionNotification = null; + object.disappearingMode = null; } if (message.key != null && message.hasOwnProperty("key")) object.key = $root.proto.MessageKey.toObject(message.key, options); @@ -28824,6 +28588,8 @@ $root.proto = (function() { object.initialSecurityNotificationSettingSync = $root.proto.InitialSecurityNotificationSettingSync.toObject(message.initialSecurityNotificationSettingSync, options); if (message.appStateFatalExceptionNotification != null && message.hasOwnProperty("appStateFatalExceptionNotification")) object.appStateFatalExceptionNotification = $root.proto.AppStateFatalExceptionNotification.toObject(message.appStateFatalExceptionNotification, options); + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) + object.disappearingMode = $root.proto.DisappearingMode.toObject(message.disappearingMode, options); return object; }; @@ -37484,6 +37250,8 @@ $root.proto = (function() { * @property {proto.IProductSnapshot|null} [product] ProductMessage product * @property {string|null} [businessOwnerJid] ProductMessage businessOwnerJid * @property {proto.ICatalogSnapshot|null} [catalog] ProductMessage catalog + * @property {string|null} [body] ProductMessage body + * @property {string|null} [footer] ProductMessage footer * @property {proto.IContextInfo|null} [contextInfo] ProductMessage contextInfo */ @@ -37526,6 +37294,22 @@ $root.proto = (function() { */ ProductMessage.prototype.catalog = null; + /** + * ProductMessage body. + * @member {string} body + * @memberof proto.ProductMessage + * @instance + */ + ProductMessage.prototype.body = ""; + + /** + * ProductMessage footer. + * @member {string} footer + * @memberof proto.ProductMessage + * @instance + */ + ProductMessage.prototype.footer = ""; + /** * ProductMessage contextInfo. * @member {proto.IContextInfo|null|undefined} contextInfo @@ -37564,6 +37348,10 @@ $root.proto = (function() { writer.uint32(/* id 2, wireType 2 =*/18).string(message.businessOwnerJid); if (message.catalog != null && Object.hasOwnProperty.call(message, "catalog")) $root.proto.CatalogSnapshot.encode(message.catalog, writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); + if (message.body != null && Object.hasOwnProperty.call(message, "body")) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.body); + if (message.footer != null && Object.hasOwnProperty.call(message, "footer")) + writer.uint32(/* id 6, wireType 2 =*/50).string(message.footer); if (message.contextInfo != null && Object.hasOwnProperty.call(message, "contextInfo")) $root.proto.ContextInfo.encode(message.contextInfo, writer.uint32(/* id 17, wireType 2 =*/138).fork()).ldelim(); return writer; @@ -37609,6 +37397,12 @@ $root.proto = (function() { case 4: message.catalog = $root.proto.CatalogSnapshot.decode(reader, reader.uint32()); break; + case 5: + message.body = reader.string(); + break; + case 6: + message.footer = reader.string(); + break; case 17: message.contextInfo = $root.proto.ContextInfo.decode(reader, reader.uint32()); break; @@ -37660,6 +37454,12 @@ $root.proto = (function() { if (error) return "catalog." + error; } + if (message.body != null && message.hasOwnProperty("body")) + if (!$util.isString(message.body)) + return "body: string expected"; + if (message.footer != null && message.hasOwnProperty("footer")) + if (!$util.isString(message.footer)) + return "footer: string expected"; if (message.contextInfo != null && message.hasOwnProperty("contextInfo")) { var error = $root.proto.ContextInfo.verify(message.contextInfo); if (error) @@ -37692,6 +37492,10 @@ $root.proto = (function() { throw TypeError(".proto.ProductMessage.catalog: object expected"); message.catalog = $root.proto.CatalogSnapshot.fromObject(object.catalog); } + if (object.body != null) + message.body = String(object.body); + if (object.footer != null) + message.footer = String(object.footer); if (object.contextInfo != null) { if (typeof object.contextInfo !== "object") throw TypeError(".proto.ProductMessage.contextInfo: object expected"); @@ -37717,6 +37521,8 @@ $root.proto = (function() { object.product = null; object.businessOwnerJid = ""; object.catalog = null; + object.body = ""; + object.footer = ""; object.contextInfo = null; } if (message.product != null && message.hasOwnProperty("product")) @@ -37725,6 +37531,10 @@ $root.proto = (function() { object.businessOwnerJid = message.businessOwnerJid; if (message.catalog != null && message.hasOwnProperty("catalog")) object.catalog = $root.proto.CatalogSnapshot.toObject(message.catalog, options); + if (message.body != null && message.hasOwnProperty("body")) + object.body = message.body; + if (message.footer != null && message.hasOwnProperty("footer")) + object.footer = message.footer; if (message.contextInfo != null && message.hasOwnProperty("contextInfo")) object.contextInfo = $root.proto.ContextInfo.toObject(message.contextInfo, options); return object; @@ -40509,6 +40319,2116 @@ $root.proto = (function() { return ListResponseMessage; })(); + proto.Header = (function() { + + /** + * Properties of a Header. + * @memberof proto + * @interface IHeader + * @property {string|null} [title] Header title + * @property {string|null} [subtitle] Header subtitle + * @property {boolean|null} [hasMediaAttachment] Header hasMediaAttachment + * @property {proto.IDocumentMessage|null} [documentMessage] Header documentMessage + * @property {proto.IImageMessage|null} [imageMessage] Header imageMessage + * @property {Uint8Array|null} [jpegThumbnail] Header jpegThumbnail + * @property {proto.IVideoMessage|null} [videoMessage] Header videoMessage + */ + + /** + * Constructs a new Header. + * @memberof proto + * @classdesc Represents a Header. + * @implements IHeader + * @constructor + * @param {proto.IHeader=} [properties] Properties to set + */ + function Header(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Header title. + * @member {string} title + * @memberof proto.Header + * @instance + */ + Header.prototype.title = ""; + + /** + * Header subtitle. + * @member {string} subtitle + * @memberof proto.Header + * @instance + */ + Header.prototype.subtitle = ""; + + /** + * Header hasMediaAttachment. + * @member {boolean} hasMediaAttachment + * @memberof proto.Header + * @instance + */ + Header.prototype.hasMediaAttachment = false; + + /** + * Header documentMessage. + * @member {proto.IDocumentMessage|null|undefined} documentMessage + * @memberof proto.Header + * @instance + */ + Header.prototype.documentMessage = null; + + /** + * Header imageMessage. + * @member {proto.IImageMessage|null|undefined} imageMessage + * @memberof proto.Header + * @instance + */ + Header.prototype.imageMessage = null; + + /** + * Header jpegThumbnail. + * @member {Uint8Array|null|undefined} jpegThumbnail + * @memberof proto.Header + * @instance + */ + Header.prototype.jpegThumbnail = null; + + /** + * Header videoMessage. + * @member {proto.IVideoMessage|null|undefined} videoMessage + * @memberof proto.Header + * @instance + */ + Header.prototype.videoMessage = null; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * Header media. + * @member {"documentMessage"|"imageMessage"|"jpegThumbnail"|"videoMessage"|undefined} media + * @memberof proto.Header + * @instance + */ + Object.defineProperty(Header.prototype, "media", { + get: $util.oneOfGetter($oneOfFields = ["documentMessage", "imageMessage", "jpegThumbnail", "videoMessage"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new Header instance using the specified properties. + * @function create + * @memberof proto.Header + * @static + * @param {proto.IHeader=} [properties] Properties to set + * @returns {proto.Header} Header instance + */ + Header.create = function create(properties) { + return new Header(properties); + }; + + /** + * Encodes the specified Header message. Does not implicitly {@link proto.Header.verify|verify} messages. + * @function encode + * @memberof proto.Header + * @static + * @param {proto.IHeader} message Header message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Header.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.title != null && Object.hasOwnProperty.call(message, "title")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.title); + if (message.subtitle != null && Object.hasOwnProperty.call(message, "subtitle")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.subtitle); + if (message.documentMessage != null && Object.hasOwnProperty.call(message, "documentMessage")) + $root.proto.DocumentMessage.encode(message.documentMessage, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + if (message.imageMessage != null && Object.hasOwnProperty.call(message, "imageMessage")) + $root.proto.ImageMessage.encode(message.imageMessage, writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); + if (message.hasMediaAttachment != null && Object.hasOwnProperty.call(message, "hasMediaAttachment")) + writer.uint32(/* id 5, wireType 0 =*/40).bool(message.hasMediaAttachment); + if (message.jpegThumbnail != null && Object.hasOwnProperty.call(message, "jpegThumbnail")) + writer.uint32(/* id 6, wireType 2 =*/50).bytes(message.jpegThumbnail); + if (message.videoMessage != null && Object.hasOwnProperty.call(message, "videoMessage")) + $root.proto.VideoMessage.encode(message.videoMessage, writer.uint32(/* id 7, wireType 2 =*/58).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified Header message, length delimited. Does not implicitly {@link proto.Header.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Header + * @static + * @param {proto.IHeader} message Header message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Header.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Header message from the specified reader or buffer. + * @function decode + * @memberof proto.Header + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Header} Header + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Header.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Header(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.title = reader.string(); + break; + case 2: + message.subtitle = reader.string(); + break; + case 5: + message.hasMediaAttachment = reader.bool(); + break; + case 3: + message.documentMessage = $root.proto.DocumentMessage.decode(reader, reader.uint32()); + break; + case 4: + message.imageMessage = $root.proto.ImageMessage.decode(reader, reader.uint32()); + break; + case 6: + message.jpegThumbnail = reader.bytes(); + break; + case 7: + message.videoMessage = $root.proto.VideoMessage.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Header message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Header + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Header} Header + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Header.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Header message. + * @function verify + * @memberof proto.Header + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Header.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.title != null && message.hasOwnProperty("title")) + if (!$util.isString(message.title)) + return "title: string expected"; + if (message.subtitle != null && message.hasOwnProperty("subtitle")) + if (!$util.isString(message.subtitle)) + return "subtitle: string expected"; + if (message.hasMediaAttachment != null && message.hasOwnProperty("hasMediaAttachment")) + if (typeof message.hasMediaAttachment !== "boolean") + return "hasMediaAttachment: boolean expected"; + if (message.documentMessage != null && message.hasOwnProperty("documentMessage")) { + properties.media = 1; + { + var error = $root.proto.DocumentMessage.verify(message.documentMessage); + if (error) + return "documentMessage." + error; + } + } + if (message.imageMessage != null && message.hasOwnProperty("imageMessage")) { + if (properties.media === 1) + return "media: multiple values"; + properties.media = 1; + { + var error = $root.proto.ImageMessage.verify(message.imageMessage); + if (error) + return "imageMessage." + error; + } + } + if (message.jpegThumbnail != null && message.hasOwnProperty("jpegThumbnail")) { + if (properties.media === 1) + return "media: multiple values"; + properties.media = 1; + if (!(message.jpegThumbnail && typeof message.jpegThumbnail.length === "number" || $util.isString(message.jpegThumbnail))) + return "jpegThumbnail: buffer expected"; + } + if (message.videoMessage != null && message.hasOwnProperty("videoMessage")) { + if (properties.media === 1) + return "media: multiple values"; + properties.media = 1; + { + var error = $root.proto.VideoMessage.verify(message.videoMessage); + if (error) + return "videoMessage." + error; + } + } + return null; + }; + + /** + * Creates a Header message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Header + * @static + * @param {Object.} object Plain object + * @returns {proto.Header} Header + */ + Header.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Header) + return object; + var message = new $root.proto.Header(); + if (object.title != null) + message.title = String(object.title); + if (object.subtitle != null) + message.subtitle = String(object.subtitle); + if (object.hasMediaAttachment != null) + message.hasMediaAttachment = Boolean(object.hasMediaAttachment); + if (object.documentMessage != null) { + if (typeof object.documentMessage !== "object") + throw TypeError(".proto.Header.documentMessage: object expected"); + message.documentMessage = $root.proto.DocumentMessage.fromObject(object.documentMessage); + } + if (object.imageMessage != null) { + if (typeof object.imageMessage !== "object") + throw TypeError(".proto.Header.imageMessage: object expected"); + message.imageMessage = $root.proto.ImageMessage.fromObject(object.imageMessage); + } + if (object.jpegThumbnail != null) + if (typeof object.jpegThumbnail === "string") + $util.base64.decode(object.jpegThumbnail, message.jpegThumbnail = $util.newBuffer($util.base64.length(object.jpegThumbnail)), 0); + else if (object.jpegThumbnail.length) + message.jpegThumbnail = object.jpegThumbnail; + if (object.videoMessage != null) { + if (typeof object.videoMessage !== "object") + throw TypeError(".proto.Header.videoMessage: object expected"); + message.videoMessage = $root.proto.VideoMessage.fromObject(object.videoMessage); + } + return message; + }; + + /** + * Creates a plain object from a Header message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Header + * @static + * @param {proto.Header} message Header + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Header.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.title = ""; + object.subtitle = ""; + object.hasMediaAttachment = false; + } + if (message.title != null && message.hasOwnProperty("title")) + object.title = message.title; + if (message.subtitle != null && message.hasOwnProperty("subtitle")) + object.subtitle = message.subtitle; + if (message.documentMessage != null && message.hasOwnProperty("documentMessage")) { + object.documentMessage = $root.proto.DocumentMessage.toObject(message.documentMessage, options); + if (options.oneofs) + object.media = "documentMessage"; + } + if (message.imageMessage != null && message.hasOwnProperty("imageMessage")) { + object.imageMessage = $root.proto.ImageMessage.toObject(message.imageMessage, options); + if (options.oneofs) + object.media = "imageMessage"; + } + if (message.hasMediaAttachment != null && message.hasOwnProperty("hasMediaAttachment")) + object.hasMediaAttachment = message.hasMediaAttachment; + if (message.jpegThumbnail != null && message.hasOwnProperty("jpegThumbnail")) { + object.jpegThumbnail = options.bytes === String ? $util.base64.encode(message.jpegThumbnail, 0, message.jpegThumbnail.length) : options.bytes === Array ? Array.prototype.slice.call(message.jpegThumbnail) : message.jpegThumbnail; + if (options.oneofs) + object.media = "jpegThumbnail"; + } + if (message.videoMessage != null && message.hasOwnProperty("videoMessage")) { + object.videoMessage = $root.proto.VideoMessage.toObject(message.videoMessage, options); + if (options.oneofs) + object.media = "videoMessage"; + } + return object; + }; + + /** + * Converts this Header to JSON. + * @function toJSON + * @memberof proto.Header + * @instance + * @returns {Object.} JSON object + */ + Header.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Header; + })(); + + proto.Body = (function() { + + /** + * Properties of a Body. + * @memberof proto + * @interface IBody + * @property {string|null} [text] Body text + */ + + /** + * Constructs a new Body. + * @memberof proto + * @classdesc Represents a Body. + * @implements IBody + * @constructor + * @param {proto.IBody=} [properties] Properties to set + */ + function Body(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Body text. + * @member {string} text + * @memberof proto.Body + * @instance + */ + Body.prototype.text = ""; + + /** + * Creates a new Body instance using the specified properties. + * @function create + * @memberof proto.Body + * @static + * @param {proto.IBody=} [properties] Properties to set + * @returns {proto.Body} Body instance + */ + Body.create = function create(properties) { + return new Body(properties); + }; + + /** + * Encodes the specified Body message. Does not implicitly {@link proto.Body.verify|verify} messages. + * @function encode + * @memberof proto.Body + * @static + * @param {proto.IBody} message Body message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Body.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.text != null && Object.hasOwnProperty.call(message, "text")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.text); + return writer; + }; + + /** + * Encodes the specified Body message, length delimited. Does not implicitly {@link proto.Body.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Body + * @static + * @param {proto.IBody} message Body message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Body.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Body message from the specified reader or buffer. + * @function decode + * @memberof proto.Body + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Body} Body + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Body.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Body(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.text = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Body message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Body + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Body} Body + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Body.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Body message. + * @function verify + * @memberof proto.Body + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Body.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.text != null && message.hasOwnProperty("text")) + if (!$util.isString(message.text)) + return "text: string expected"; + return null; + }; + + /** + * Creates a Body message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Body + * @static + * @param {Object.} object Plain object + * @returns {proto.Body} Body + */ + Body.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Body) + return object; + var message = new $root.proto.Body(); + if (object.text != null) + message.text = String(object.text); + return message; + }; + + /** + * Creates a plain object from a Body message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Body + * @static + * @param {proto.Body} message Body + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Body.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.text = ""; + if (message.text != null && message.hasOwnProperty("text")) + object.text = message.text; + return object; + }; + + /** + * Converts this Body to JSON. + * @function toJSON + * @memberof proto.Body + * @instance + * @returns {Object.} JSON object + */ + Body.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Body; + })(); + + proto.Footer = (function() { + + /** + * Properties of a Footer. + * @memberof proto + * @interface IFooter + * @property {string|null} [text] Footer text + */ + + /** + * Constructs a new Footer. + * @memberof proto + * @classdesc Represents a Footer. + * @implements IFooter + * @constructor + * @param {proto.IFooter=} [properties] Properties to set + */ + function Footer(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Footer text. + * @member {string} text + * @memberof proto.Footer + * @instance + */ + Footer.prototype.text = ""; + + /** + * Creates a new Footer instance using the specified properties. + * @function create + * @memberof proto.Footer + * @static + * @param {proto.IFooter=} [properties] Properties to set + * @returns {proto.Footer} Footer instance + */ + Footer.create = function create(properties) { + return new Footer(properties); + }; + + /** + * Encodes the specified Footer message. Does not implicitly {@link proto.Footer.verify|verify} messages. + * @function encode + * @memberof proto.Footer + * @static + * @param {proto.IFooter} message Footer message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Footer.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.text != null && Object.hasOwnProperty.call(message, "text")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.text); + return writer; + }; + + /** + * Encodes the specified Footer message, length delimited. Does not implicitly {@link proto.Footer.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Footer + * @static + * @param {proto.IFooter} message Footer message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Footer.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Footer message from the specified reader or buffer. + * @function decode + * @memberof proto.Footer + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Footer} Footer + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Footer.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Footer(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.text = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Footer message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Footer + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Footer} Footer + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Footer.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Footer message. + * @function verify + * @memberof proto.Footer + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Footer.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.text != null && message.hasOwnProperty("text")) + if (!$util.isString(message.text)) + return "text: string expected"; + return null; + }; + + /** + * Creates a Footer message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Footer + * @static + * @param {Object.} object Plain object + * @returns {proto.Footer} Footer + */ + Footer.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Footer) + return object; + var message = new $root.proto.Footer(); + if (object.text != null) + message.text = String(object.text); + return message; + }; + + /** + * Creates a plain object from a Footer message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Footer + * @static + * @param {proto.Footer} message Footer + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Footer.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.text = ""; + if (message.text != null && message.hasOwnProperty("text")) + object.text = message.text; + return object; + }; + + /** + * Converts this Footer to JSON. + * @function toJSON + * @memberof proto.Footer + * @instance + * @returns {Object.} JSON object + */ + Footer.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Footer; + })(); + + proto.ShopMessage = (function() { + + /** + * Properties of a ShopMessage. + * @memberof proto + * @interface IShopMessage + * @property {string|null} [id] ShopMessage id + * @property {proto.ShopMessage.ShopMessageSurface|null} [surface] ShopMessage surface + * @property {number|null} [messageVersion] ShopMessage messageVersion + */ + + /** + * Constructs a new ShopMessage. + * @memberof proto + * @classdesc Represents a ShopMessage. + * @implements IShopMessage + * @constructor + * @param {proto.IShopMessage=} [properties] Properties to set + */ + function ShopMessage(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ShopMessage id. + * @member {string} id + * @memberof proto.ShopMessage + * @instance + */ + ShopMessage.prototype.id = ""; + + /** + * ShopMessage surface. + * @member {proto.ShopMessage.ShopMessageSurface} surface + * @memberof proto.ShopMessage + * @instance + */ + ShopMessage.prototype.surface = 0; + + /** + * ShopMessage messageVersion. + * @member {number} messageVersion + * @memberof proto.ShopMessage + * @instance + */ + ShopMessage.prototype.messageVersion = 0; + + /** + * Creates a new ShopMessage instance using the specified properties. + * @function create + * @memberof proto.ShopMessage + * @static + * @param {proto.IShopMessage=} [properties] Properties to set + * @returns {proto.ShopMessage} ShopMessage instance + */ + ShopMessage.create = function create(properties) { + return new ShopMessage(properties); + }; + + /** + * Encodes the specified ShopMessage message. Does not implicitly {@link proto.ShopMessage.verify|verify} messages. + * @function encode + * @memberof proto.ShopMessage + * @static + * @param {proto.IShopMessage} message ShopMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ShopMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.id); + if (message.surface != null && Object.hasOwnProperty.call(message, "surface")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.surface); + if (message.messageVersion != null && Object.hasOwnProperty.call(message, "messageVersion")) + writer.uint32(/* id 3, wireType 0 =*/24).int32(message.messageVersion); + return writer; + }; + + /** + * Encodes the specified ShopMessage message, length delimited. Does not implicitly {@link proto.ShopMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.ShopMessage + * @static + * @param {proto.IShopMessage} message ShopMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ShopMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ShopMessage message from the specified reader or buffer. + * @function decode + * @memberof proto.ShopMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.ShopMessage} ShopMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ShopMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.ShopMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + case 2: + message.surface = reader.int32(); + break; + case 3: + message.messageVersion = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ShopMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.ShopMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.ShopMessage} ShopMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ShopMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ShopMessage message. + * @function verify + * @memberof proto.ShopMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ShopMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isString(message.id)) + return "id: string expected"; + if (message.surface != null && message.hasOwnProperty("surface")) + switch (message.surface) { + default: + return "surface: enum value expected"; + case 0: + case 1: + case 2: + case 3: + break; + } + if (message.messageVersion != null && message.hasOwnProperty("messageVersion")) + if (!$util.isInteger(message.messageVersion)) + return "messageVersion: integer expected"; + return null; + }; + + /** + * Creates a ShopMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.ShopMessage + * @static + * @param {Object.} object Plain object + * @returns {proto.ShopMessage} ShopMessage + */ + ShopMessage.fromObject = function fromObject(object) { + if (object instanceof $root.proto.ShopMessage) + return object; + var message = new $root.proto.ShopMessage(); + if (object.id != null) + message.id = String(object.id); + switch (object.surface) { + case "UNKNOWN_SURFACE": + case 0: + message.surface = 0; + break; + case "FB": + case 1: + message.surface = 1; + break; + case "IG": + case 2: + message.surface = 2; + break; + case "WA": + case 3: + message.surface = 3; + break; + } + if (object.messageVersion != null) + message.messageVersion = object.messageVersion | 0; + return message; + }; + + /** + * Creates a plain object from a ShopMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.ShopMessage + * @static + * @param {proto.ShopMessage} message ShopMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ShopMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.id = ""; + object.surface = options.enums === String ? "UNKNOWN_SURFACE" : 0; + object.messageVersion = 0; + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + if (message.surface != null && message.hasOwnProperty("surface")) + object.surface = options.enums === String ? $root.proto.ShopMessage.ShopMessageSurface[message.surface] : message.surface; + if (message.messageVersion != null && message.hasOwnProperty("messageVersion")) + object.messageVersion = message.messageVersion; + return object; + }; + + /** + * Converts this ShopMessage to JSON. + * @function toJSON + * @memberof proto.ShopMessage + * @instance + * @returns {Object.} JSON object + */ + ShopMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * ShopMessageSurface enum. + * @name proto.ShopMessage.ShopMessageSurface + * @enum {number} + * @property {number} UNKNOWN_SURFACE=0 UNKNOWN_SURFACE value + * @property {number} FB=1 FB value + * @property {number} IG=2 IG value + * @property {number} WA=3 WA value + */ + ShopMessage.ShopMessageSurface = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "UNKNOWN_SURFACE"] = 0; + values[valuesById[1] = "FB"] = 1; + values[valuesById[2] = "IG"] = 2; + values[valuesById[3] = "WA"] = 3; + return values; + })(); + + return ShopMessage; + })(); + + proto.CollectionMessage = (function() { + + /** + * Properties of a CollectionMessage. + * @memberof proto + * @interface ICollectionMessage + * @property {string|null} [bizJid] CollectionMessage bizJid + * @property {string|null} [id] CollectionMessage id + * @property {number|null} [messageVersion] CollectionMessage messageVersion + */ + + /** + * Constructs a new CollectionMessage. + * @memberof proto + * @classdesc Represents a CollectionMessage. + * @implements ICollectionMessage + * @constructor + * @param {proto.ICollectionMessage=} [properties] Properties to set + */ + function CollectionMessage(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * CollectionMessage bizJid. + * @member {string} bizJid + * @memberof proto.CollectionMessage + * @instance + */ + CollectionMessage.prototype.bizJid = ""; + + /** + * CollectionMessage id. + * @member {string} id + * @memberof proto.CollectionMessage + * @instance + */ + CollectionMessage.prototype.id = ""; + + /** + * CollectionMessage messageVersion. + * @member {number} messageVersion + * @memberof proto.CollectionMessage + * @instance + */ + CollectionMessage.prototype.messageVersion = 0; + + /** + * Creates a new CollectionMessage instance using the specified properties. + * @function create + * @memberof proto.CollectionMessage + * @static + * @param {proto.ICollectionMessage=} [properties] Properties to set + * @returns {proto.CollectionMessage} CollectionMessage instance + */ + CollectionMessage.create = function create(properties) { + return new CollectionMessage(properties); + }; + + /** + * Encodes the specified CollectionMessage message. Does not implicitly {@link proto.CollectionMessage.verify|verify} messages. + * @function encode + * @memberof proto.CollectionMessage + * @static + * @param {proto.ICollectionMessage} message CollectionMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CollectionMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.bizJid != null && Object.hasOwnProperty.call(message, "bizJid")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.bizJid); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.id); + if (message.messageVersion != null && Object.hasOwnProperty.call(message, "messageVersion")) + writer.uint32(/* id 3, wireType 0 =*/24).int32(message.messageVersion); + return writer; + }; + + /** + * Encodes the specified CollectionMessage message, length delimited. Does not implicitly {@link proto.CollectionMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.CollectionMessage + * @static + * @param {proto.ICollectionMessage} message CollectionMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CollectionMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a CollectionMessage message from the specified reader or buffer. + * @function decode + * @memberof proto.CollectionMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.CollectionMessage} CollectionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CollectionMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.CollectionMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.bizJid = reader.string(); + break; + case 2: + message.id = reader.string(); + break; + case 3: + message.messageVersion = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a CollectionMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.CollectionMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.CollectionMessage} CollectionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CollectionMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a CollectionMessage message. + * @function verify + * @memberof proto.CollectionMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + CollectionMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.bizJid != null && message.hasOwnProperty("bizJid")) + if (!$util.isString(message.bizJid)) + return "bizJid: string expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isString(message.id)) + return "id: string expected"; + if (message.messageVersion != null && message.hasOwnProperty("messageVersion")) + if (!$util.isInteger(message.messageVersion)) + return "messageVersion: integer expected"; + return null; + }; + + /** + * Creates a CollectionMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.CollectionMessage + * @static + * @param {Object.} object Plain object + * @returns {proto.CollectionMessage} CollectionMessage + */ + CollectionMessage.fromObject = function fromObject(object) { + if (object instanceof $root.proto.CollectionMessage) + return object; + var message = new $root.proto.CollectionMessage(); + if (object.bizJid != null) + message.bizJid = String(object.bizJid); + if (object.id != null) + message.id = String(object.id); + if (object.messageVersion != null) + message.messageVersion = object.messageVersion | 0; + return message; + }; + + /** + * Creates a plain object from a CollectionMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.CollectionMessage + * @static + * @param {proto.CollectionMessage} message CollectionMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + CollectionMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.bizJid = ""; + object.id = ""; + object.messageVersion = 0; + } + if (message.bizJid != null && message.hasOwnProperty("bizJid")) + object.bizJid = message.bizJid; + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + if (message.messageVersion != null && message.hasOwnProperty("messageVersion")) + object.messageVersion = message.messageVersion; + return object; + }; + + /** + * Converts this CollectionMessage to JSON. + * @function toJSON + * @memberof proto.CollectionMessage + * @instance + * @returns {Object.} JSON object + */ + CollectionMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return CollectionMessage; + })(); + + proto.NativeFlowButton = (function() { + + /** + * Properties of a NativeFlowButton. + * @memberof proto + * @interface INativeFlowButton + * @property {string|null} [name] NativeFlowButton name + * @property {string|null} [buttonParamsJson] NativeFlowButton buttonParamsJson + */ + + /** + * Constructs a new NativeFlowButton. + * @memberof proto + * @classdesc Represents a NativeFlowButton. + * @implements INativeFlowButton + * @constructor + * @param {proto.INativeFlowButton=} [properties] Properties to set + */ + function NativeFlowButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * NativeFlowButton name. + * @member {string} name + * @memberof proto.NativeFlowButton + * @instance + */ + NativeFlowButton.prototype.name = ""; + + /** + * NativeFlowButton buttonParamsJson. + * @member {string} buttonParamsJson + * @memberof proto.NativeFlowButton + * @instance + */ + NativeFlowButton.prototype.buttonParamsJson = ""; + + /** + * Creates a new NativeFlowButton instance using the specified properties. + * @function create + * @memberof proto.NativeFlowButton + * @static + * @param {proto.INativeFlowButton=} [properties] Properties to set + * @returns {proto.NativeFlowButton} NativeFlowButton instance + */ + NativeFlowButton.create = function create(properties) { + return new NativeFlowButton(properties); + }; + + /** + * Encodes the specified NativeFlowButton message. Does not implicitly {@link proto.NativeFlowButton.verify|verify} messages. + * @function encode + * @memberof proto.NativeFlowButton + * @static + * @param {proto.INativeFlowButton} message NativeFlowButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NativeFlowButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.name != null && Object.hasOwnProperty.call(message, "name")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.name); + if (message.buttonParamsJson != null && Object.hasOwnProperty.call(message, "buttonParamsJson")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.buttonParamsJson); + return writer; + }; + + /** + * Encodes the specified NativeFlowButton message, length delimited. Does not implicitly {@link proto.NativeFlowButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.NativeFlowButton + * @static + * @param {proto.INativeFlowButton} message NativeFlowButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NativeFlowButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a NativeFlowButton message from the specified reader or buffer. + * @function decode + * @memberof proto.NativeFlowButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.NativeFlowButton} NativeFlowButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NativeFlowButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.NativeFlowButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.buttonParamsJson = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a NativeFlowButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.NativeFlowButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.NativeFlowButton} NativeFlowButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NativeFlowButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a NativeFlowButton message. + * @function verify + * @memberof proto.NativeFlowButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + NativeFlowButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.name != null && message.hasOwnProperty("name")) + if (!$util.isString(message.name)) + return "name: string expected"; + if (message.buttonParamsJson != null && message.hasOwnProperty("buttonParamsJson")) + if (!$util.isString(message.buttonParamsJson)) + return "buttonParamsJson: string expected"; + return null; + }; + + /** + * Creates a NativeFlowButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.NativeFlowButton + * @static + * @param {Object.} object Plain object + * @returns {proto.NativeFlowButton} NativeFlowButton + */ + NativeFlowButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.NativeFlowButton) + return object; + var message = new $root.proto.NativeFlowButton(); + if (object.name != null) + message.name = String(object.name); + if (object.buttonParamsJson != null) + message.buttonParamsJson = String(object.buttonParamsJson); + return message; + }; + + /** + * Creates a plain object from a NativeFlowButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.NativeFlowButton + * @static + * @param {proto.NativeFlowButton} message NativeFlowButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + NativeFlowButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.name = ""; + object.buttonParamsJson = ""; + } + if (message.name != null && message.hasOwnProperty("name")) + object.name = message.name; + if (message.buttonParamsJson != null && message.hasOwnProperty("buttonParamsJson")) + object.buttonParamsJson = message.buttonParamsJson; + return object; + }; + + /** + * Converts this NativeFlowButton to JSON. + * @function toJSON + * @memberof proto.NativeFlowButton + * @instance + * @returns {Object.} JSON object + */ + NativeFlowButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return NativeFlowButton; + })(); + + proto.NativeFlowMessage = (function() { + + /** + * Properties of a NativeFlowMessage. + * @memberof proto + * @interface INativeFlowMessage + * @property {Array.|null} [buttons] NativeFlowMessage buttons + * @property {string|null} [messageParamsJson] NativeFlowMessage messageParamsJson + * @property {number|null} [messageVersion] NativeFlowMessage messageVersion + */ + + /** + * Constructs a new NativeFlowMessage. + * @memberof proto + * @classdesc Represents a NativeFlowMessage. + * @implements INativeFlowMessage + * @constructor + * @param {proto.INativeFlowMessage=} [properties] Properties to set + */ + function NativeFlowMessage(properties) { + this.buttons = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * NativeFlowMessage buttons. + * @member {Array.} buttons + * @memberof proto.NativeFlowMessage + * @instance + */ + NativeFlowMessage.prototype.buttons = $util.emptyArray; + + /** + * NativeFlowMessage messageParamsJson. + * @member {string} messageParamsJson + * @memberof proto.NativeFlowMessage + * @instance + */ + NativeFlowMessage.prototype.messageParamsJson = ""; + + /** + * NativeFlowMessage messageVersion. + * @member {number} messageVersion + * @memberof proto.NativeFlowMessage + * @instance + */ + NativeFlowMessage.prototype.messageVersion = 0; + + /** + * Creates a new NativeFlowMessage instance using the specified properties. + * @function create + * @memberof proto.NativeFlowMessage + * @static + * @param {proto.INativeFlowMessage=} [properties] Properties to set + * @returns {proto.NativeFlowMessage} NativeFlowMessage instance + */ + NativeFlowMessage.create = function create(properties) { + return new NativeFlowMessage(properties); + }; + + /** + * Encodes the specified NativeFlowMessage message. Does not implicitly {@link proto.NativeFlowMessage.verify|verify} messages. + * @function encode + * @memberof proto.NativeFlowMessage + * @static + * @param {proto.INativeFlowMessage} message NativeFlowMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NativeFlowMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.buttons != null && message.buttons.length) + for (var i = 0; i < message.buttons.length; ++i) + $root.proto.NativeFlowButton.encode(message.buttons[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.messageParamsJson != null && Object.hasOwnProperty.call(message, "messageParamsJson")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.messageParamsJson); + if (message.messageVersion != null && Object.hasOwnProperty.call(message, "messageVersion")) + writer.uint32(/* id 3, wireType 0 =*/24).int32(message.messageVersion); + return writer; + }; + + /** + * Encodes the specified NativeFlowMessage message, length delimited. Does not implicitly {@link proto.NativeFlowMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.NativeFlowMessage + * @static + * @param {proto.INativeFlowMessage} message NativeFlowMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NativeFlowMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a NativeFlowMessage message from the specified reader or buffer. + * @function decode + * @memberof proto.NativeFlowMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.NativeFlowMessage} NativeFlowMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NativeFlowMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.NativeFlowMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.buttons && message.buttons.length)) + message.buttons = []; + message.buttons.push($root.proto.NativeFlowButton.decode(reader, reader.uint32())); + break; + case 2: + message.messageParamsJson = reader.string(); + break; + case 3: + message.messageVersion = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a NativeFlowMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.NativeFlowMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.NativeFlowMessage} NativeFlowMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NativeFlowMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a NativeFlowMessage message. + * @function verify + * @memberof proto.NativeFlowMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + NativeFlowMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.buttons != null && message.hasOwnProperty("buttons")) { + if (!Array.isArray(message.buttons)) + return "buttons: array expected"; + for (var i = 0; i < message.buttons.length; ++i) { + var error = $root.proto.NativeFlowButton.verify(message.buttons[i]); + if (error) + return "buttons." + error; + } + } + if (message.messageParamsJson != null && message.hasOwnProperty("messageParamsJson")) + if (!$util.isString(message.messageParamsJson)) + return "messageParamsJson: string expected"; + if (message.messageVersion != null && message.hasOwnProperty("messageVersion")) + if (!$util.isInteger(message.messageVersion)) + return "messageVersion: integer expected"; + return null; + }; + + /** + * Creates a NativeFlowMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.NativeFlowMessage + * @static + * @param {Object.} object Plain object + * @returns {proto.NativeFlowMessage} NativeFlowMessage + */ + NativeFlowMessage.fromObject = function fromObject(object) { + if (object instanceof $root.proto.NativeFlowMessage) + return object; + var message = new $root.proto.NativeFlowMessage(); + if (object.buttons) { + if (!Array.isArray(object.buttons)) + throw TypeError(".proto.NativeFlowMessage.buttons: array expected"); + message.buttons = []; + for (var i = 0; i < object.buttons.length; ++i) { + if (typeof object.buttons[i] !== "object") + throw TypeError(".proto.NativeFlowMessage.buttons: object expected"); + message.buttons[i] = $root.proto.NativeFlowButton.fromObject(object.buttons[i]); + } + } + if (object.messageParamsJson != null) + message.messageParamsJson = String(object.messageParamsJson); + if (object.messageVersion != null) + message.messageVersion = object.messageVersion | 0; + return message; + }; + + /** + * Creates a plain object from a NativeFlowMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.NativeFlowMessage + * @static + * @param {proto.NativeFlowMessage} message NativeFlowMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + NativeFlowMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.buttons = []; + if (options.defaults) { + object.messageParamsJson = ""; + object.messageVersion = 0; + } + if (message.buttons && message.buttons.length) { + object.buttons = []; + for (var j = 0; j < message.buttons.length; ++j) + object.buttons[j] = $root.proto.NativeFlowButton.toObject(message.buttons[j], options); + } + if (message.messageParamsJson != null && message.hasOwnProperty("messageParamsJson")) + object.messageParamsJson = message.messageParamsJson; + if (message.messageVersion != null && message.hasOwnProperty("messageVersion")) + object.messageVersion = message.messageVersion; + return object; + }; + + /** + * Converts this NativeFlowMessage to JSON. + * @function toJSON + * @memberof proto.NativeFlowMessage + * @instance + * @returns {Object.} JSON object + */ + NativeFlowMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return NativeFlowMessage; + })(); + + proto.InteractiveMessage = (function() { + + /** + * Properties of an InteractiveMessage. + * @memberof proto + * @interface IInteractiveMessage + * @property {proto.IHeader|null} [header] InteractiveMessage header + * @property {proto.IBody|null} [body] InteractiveMessage body + * @property {proto.IFooter|null} [footer] InteractiveMessage footer + * @property {proto.IContextInfo|null} [contextInfo] InteractiveMessage contextInfo + * @property {proto.IShopMessage|null} [shopStorefrontMessage] InteractiveMessage shopStorefrontMessage + * @property {proto.ICollectionMessage|null} [collectionMessage] InteractiveMessage collectionMessage + * @property {proto.INativeFlowMessage|null} [nativeFlowMessage] InteractiveMessage nativeFlowMessage + */ + + /** + * Constructs a new InteractiveMessage. + * @memberof proto + * @classdesc Represents an InteractiveMessage. + * @implements IInteractiveMessage + * @constructor + * @param {proto.IInteractiveMessage=} [properties] Properties to set + */ + function InteractiveMessage(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * InteractiveMessage header. + * @member {proto.IHeader|null|undefined} header + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.header = null; + + /** + * InteractiveMessage body. + * @member {proto.IBody|null|undefined} body + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.body = null; + + /** + * InteractiveMessage footer. + * @member {proto.IFooter|null|undefined} footer + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.footer = null; + + /** + * InteractiveMessage contextInfo. + * @member {proto.IContextInfo|null|undefined} contextInfo + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.contextInfo = null; + + /** + * InteractiveMessage shopStorefrontMessage. + * @member {proto.IShopMessage|null|undefined} shopStorefrontMessage + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.shopStorefrontMessage = null; + + /** + * InteractiveMessage collectionMessage. + * @member {proto.ICollectionMessage|null|undefined} collectionMessage + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.collectionMessage = null; + + /** + * InteractiveMessage nativeFlowMessage. + * @member {proto.INativeFlowMessage|null|undefined} nativeFlowMessage + * @memberof proto.InteractiveMessage + * @instance + */ + InteractiveMessage.prototype.nativeFlowMessage = null; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * InteractiveMessage interactiveMessage. + * @member {"shopStorefrontMessage"|"collectionMessage"|"nativeFlowMessage"|undefined} interactiveMessage + * @memberof proto.InteractiveMessage + * @instance + */ + Object.defineProperty(InteractiveMessage.prototype, "interactiveMessage", { + get: $util.oneOfGetter($oneOfFields = ["shopStorefrontMessage", "collectionMessage", "nativeFlowMessage"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new InteractiveMessage instance using the specified properties. + * @function create + * @memberof proto.InteractiveMessage + * @static + * @param {proto.IInteractiveMessage=} [properties] Properties to set + * @returns {proto.InteractiveMessage} InteractiveMessage instance + */ + InteractiveMessage.create = function create(properties) { + return new InteractiveMessage(properties); + }; + + /** + * Encodes the specified InteractiveMessage message. Does not implicitly {@link proto.InteractiveMessage.verify|verify} messages. + * @function encode + * @memberof proto.InteractiveMessage + * @static + * @param {proto.IInteractiveMessage} message InteractiveMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + InteractiveMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.header != null && Object.hasOwnProperty.call(message, "header")) + $root.proto.Header.encode(message.header, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.body != null && Object.hasOwnProperty.call(message, "body")) + $root.proto.Body.encode(message.body, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.footer != null && Object.hasOwnProperty.call(message, "footer")) + $root.proto.Footer.encode(message.footer, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + if (message.shopStorefrontMessage != null && Object.hasOwnProperty.call(message, "shopStorefrontMessage")) + $root.proto.ShopMessage.encode(message.shopStorefrontMessage, writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); + if (message.collectionMessage != null && Object.hasOwnProperty.call(message, "collectionMessage")) + $root.proto.CollectionMessage.encode(message.collectionMessage, writer.uint32(/* id 5, wireType 2 =*/42).fork()).ldelim(); + if (message.nativeFlowMessage != null && Object.hasOwnProperty.call(message, "nativeFlowMessage")) + $root.proto.NativeFlowMessage.encode(message.nativeFlowMessage, writer.uint32(/* id 6, wireType 2 =*/50).fork()).ldelim(); + if (message.contextInfo != null && Object.hasOwnProperty.call(message, "contextInfo")) + $root.proto.ContextInfo.encode(message.contextInfo, writer.uint32(/* id 15, wireType 2 =*/122).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified InteractiveMessage message, length delimited. Does not implicitly {@link proto.InteractiveMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.InteractiveMessage + * @static + * @param {proto.IInteractiveMessage} message InteractiveMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + InteractiveMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an InteractiveMessage message from the specified reader or buffer. + * @function decode + * @memberof proto.InteractiveMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.InteractiveMessage} InteractiveMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + InteractiveMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.InteractiveMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.header = $root.proto.Header.decode(reader, reader.uint32()); + break; + case 2: + message.body = $root.proto.Body.decode(reader, reader.uint32()); + break; + case 3: + message.footer = $root.proto.Footer.decode(reader, reader.uint32()); + break; + case 15: + message.contextInfo = $root.proto.ContextInfo.decode(reader, reader.uint32()); + break; + case 4: + message.shopStorefrontMessage = $root.proto.ShopMessage.decode(reader, reader.uint32()); + break; + case 5: + message.collectionMessage = $root.proto.CollectionMessage.decode(reader, reader.uint32()); + break; + case 6: + message.nativeFlowMessage = $root.proto.NativeFlowMessage.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an InteractiveMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.InteractiveMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.InteractiveMessage} InteractiveMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + InteractiveMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an InteractiveMessage message. + * @function verify + * @memberof proto.InteractiveMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + InteractiveMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.header != null && message.hasOwnProperty("header")) { + var error = $root.proto.Header.verify(message.header); + if (error) + return "header." + error; + } + if (message.body != null && message.hasOwnProperty("body")) { + var error = $root.proto.Body.verify(message.body); + if (error) + return "body." + error; + } + if (message.footer != null && message.hasOwnProperty("footer")) { + var error = $root.proto.Footer.verify(message.footer); + if (error) + return "footer." + error; + } + if (message.contextInfo != null && message.hasOwnProperty("contextInfo")) { + var error = $root.proto.ContextInfo.verify(message.contextInfo); + if (error) + return "contextInfo." + error; + } + if (message.shopStorefrontMessage != null && message.hasOwnProperty("shopStorefrontMessage")) { + properties.interactiveMessage = 1; + { + var error = $root.proto.ShopMessage.verify(message.shopStorefrontMessage); + if (error) + return "shopStorefrontMessage." + error; + } + } + if (message.collectionMessage != null && message.hasOwnProperty("collectionMessage")) { + if (properties.interactiveMessage === 1) + return "interactiveMessage: multiple values"; + properties.interactiveMessage = 1; + { + var error = $root.proto.CollectionMessage.verify(message.collectionMessage); + if (error) + return "collectionMessage." + error; + } + } + if (message.nativeFlowMessage != null && message.hasOwnProperty("nativeFlowMessage")) { + if (properties.interactiveMessage === 1) + return "interactiveMessage: multiple values"; + properties.interactiveMessage = 1; + { + var error = $root.proto.NativeFlowMessage.verify(message.nativeFlowMessage); + if (error) + return "nativeFlowMessage." + error; + } + } + return null; + }; + + /** + * Creates an InteractiveMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.InteractiveMessage + * @static + * @param {Object.} object Plain object + * @returns {proto.InteractiveMessage} InteractiveMessage + */ + InteractiveMessage.fromObject = function fromObject(object) { + if (object instanceof $root.proto.InteractiveMessage) + return object; + var message = new $root.proto.InteractiveMessage(); + if (object.header != null) { + if (typeof object.header !== "object") + throw TypeError(".proto.InteractiveMessage.header: object expected"); + message.header = $root.proto.Header.fromObject(object.header); + } + if (object.body != null) { + if (typeof object.body !== "object") + throw TypeError(".proto.InteractiveMessage.body: object expected"); + message.body = $root.proto.Body.fromObject(object.body); + } + if (object.footer != null) { + if (typeof object.footer !== "object") + throw TypeError(".proto.InteractiveMessage.footer: object expected"); + message.footer = $root.proto.Footer.fromObject(object.footer); + } + if (object.contextInfo != null) { + if (typeof object.contextInfo !== "object") + throw TypeError(".proto.InteractiveMessage.contextInfo: object expected"); + message.contextInfo = $root.proto.ContextInfo.fromObject(object.contextInfo); + } + if (object.shopStorefrontMessage != null) { + if (typeof object.shopStorefrontMessage !== "object") + throw TypeError(".proto.InteractiveMessage.shopStorefrontMessage: object expected"); + message.shopStorefrontMessage = $root.proto.ShopMessage.fromObject(object.shopStorefrontMessage); + } + if (object.collectionMessage != null) { + if (typeof object.collectionMessage !== "object") + throw TypeError(".proto.InteractiveMessage.collectionMessage: object expected"); + message.collectionMessage = $root.proto.CollectionMessage.fromObject(object.collectionMessage); + } + if (object.nativeFlowMessage != null) { + if (typeof object.nativeFlowMessage !== "object") + throw TypeError(".proto.InteractiveMessage.nativeFlowMessage: object expected"); + message.nativeFlowMessage = $root.proto.NativeFlowMessage.fromObject(object.nativeFlowMessage); + } + return message; + }; + + /** + * Creates a plain object from an InteractiveMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.InteractiveMessage + * @static + * @param {proto.InteractiveMessage} message InteractiveMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + InteractiveMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.header = null; + object.body = null; + object.footer = null; + object.contextInfo = null; + } + if (message.header != null && message.hasOwnProperty("header")) + object.header = $root.proto.Header.toObject(message.header, options); + if (message.body != null && message.hasOwnProperty("body")) + object.body = $root.proto.Body.toObject(message.body, options); + if (message.footer != null && message.hasOwnProperty("footer")) + object.footer = $root.proto.Footer.toObject(message.footer, options); + if (message.shopStorefrontMessage != null && message.hasOwnProperty("shopStorefrontMessage")) { + object.shopStorefrontMessage = $root.proto.ShopMessage.toObject(message.shopStorefrontMessage, options); + if (options.oneofs) + object.interactiveMessage = "shopStorefrontMessage"; + } + if (message.collectionMessage != null && message.hasOwnProperty("collectionMessage")) { + object.collectionMessage = $root.proto.CollectionMessage.toObject(message.collectionMessage, options); + if (options.oneofs) + object.interactiveMessage = "collectionMessage"; + } + if (message.nativeFlowMessage != null && message.hasOwnProperty("nativeFlowMessage")) { + object.nativeFlowMessage = $root.proto.NativeFlowMessage.toObject(message.nativeFlowMessage, options); + if (options.oneofs) + object.interactiveMessage = "nativeFlowMessage"; + } + if (message.contextInfo != null && message.hasOwnProperty("contextInfo")) + object.contextInfo = $root.proto.ContextInfo.toObject(message.contextInfo, options); + return object; + }; + + /** + * Converts this InteractiveMessage to JSON. + * @function toJSON + * @memberof proto.InteractiveMessage + * @instance + * @returns {Object.} JSON object + */ + InteractiveMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return InteractiveMessage; + })(); + proto.GroupInviteMessage = (function() { /** @@ -40522,6 +42442,7 @@ $root.proto = (function() { * @property {Uint8Array|null} [jpegThumbnail] GroupInviteMessage jpegThumbnail * @property {string|null} [caption] GroupInviteMessage caption * @property {proto.IContextInfo|null} [contextInfo] GroupInviteMessage contextInfo + * @property {proto.GroupInviteMessage.GroupInviteMessageGroupType|null} [groupType] GroupInviteMessage groupType */ /** @@ -40595,6 +42516,14 @@ $root.proto = (function() { */ GroupInviteMessage.prototype.contextInfo = null; + /** + * GroupInviteMessage groupType. + * @member {proto.GroupInviteMessage.GroupInviteMessageGroupType} groupType + * @memberof proto.GroupInviteMessage + * @instance + */ + GroupInviteMessage.prototype.groupType = 0; + /** * Creates a new GroupInviteMessage instance using the specified properties. * @function create @@ -40633,6 +42562,8 @@ $root.proto = (function() { writer.uint32(/* id 6, wireType 2 =*/50).string(message.caption); if (message.contextInfo != null && Object.hasOwnProperty.call(message, "contextInfo")) $root.proto.ContextInfo.encode(message.contextInfo, writer.uint32(/* id 7, wireType 2 =*/58).fork()).ldelim(); + if (message.groupType != null && Object.hasOwnProperty.call(message, "groupType")) + writer.uint32(/* id 8, wireType 0 =*/64).int32(message.groupType); return writer; }; @@ -40688,6 +42619,9 @@ $root.proto = (function() { case 7: message.contextInfo = $root.proto.ContextInfo.decode(reader, reader.uint32()); break; + case 8: + message.groupType = reader.int32(); + break; default: reader.skipType(tag & 7); break; @@ -40746,6 +42680,14 @@ $root.proto = (function() { if (error) return "contextInfo." + error; } + if (message.groupType != null && message.hasOwnProperty("groupType")) + switch (message.groupType) { + default: + return "groupType: enum value expected"; + case 0: + case 1: + break; + } return null; }; @@ -40788,6 +42730,16 @@ $root.proto = (function() { throw TypeError(".proto.GroupInviteMessage.contextInfo: object expected"); message.contextInfo = $root.proto.ContextInfo.fromObject(object.contextInfo); } + switch (object.groupType) { + case "DEFAULT": + case 0: + message.groupType = 0; + break; + case "PARENT": + case 1: + message.groupType = 1; + break; + } return message; }; @@ -40822,6 +42774,7 @@ $root.proto = (function() { } object.caption = ""; object.contextInfo = null; + object.groupType = options.enums === String ? "DEFAULT" : 0; } if (message.groupJid != null && message.hasOwnProperty("groupJid")) object.groupJid = message.groupJid; @@ -40840,6 +42793,8 @@ $root.proto = (function() { object.caption = message.caption; if (message.contextInfo != null && message.hasOwnProperty("contextInfo")) object.contextInfo = $root.proto.ContextInfo.toObject(message.contextInfo, options); + if (message.groupType != null && message.hasOwnProperty("groupType")) + object.groupType = options.enums === String ? $root.proto.GroupInviteMessage.GroupInviteMessageGroupType[message.groupType] : message.groupType; return object; }; @@ -40854,6 +42809,20 @@ $root.proto = (function() { return this.constructor.toObject(this, $protobuf.util.toJSONOptions); }; + /** + * GroupInviteMessageGroupType enum. + * @name proto.GroupInviteMessage.GroupInviteMessageGroupType + * @enum {number} + * @property {number} DEFAULT=0 DEFAULT value + * @property {number} PARENT=1 PARENT value + */ + GroupInviteMessage.GroupInviteMessageGroupType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "DEFAULT"] = 0; + values[valuesById[1] = "PARENT"] = 1; + return values; + })(); + return GroupInviteMessage; })(); @@ -42828,6 +44797,542 @@ $root.proto = (function() { return ButtonsResponseMessage; })(); + proto.ReactionMessage = (function() { + + /** + * Properties of a ReactionMessage. + * @memberof proto + * @interface IReactionMessage + * @property {proto.IMessageKey|null} [key] ReactionMessage key + * @property {string|null} [text] ReactionMessage text + * @property {string|null} [groupingKey] ReactionMessage groupingKey + * @property {number|Long|null} [senderTimestampMs] ReactionMessage senderTimestampMs + */ + + /** + * Constructs a new ReactionMessage. + * @memberof proto + * @classdesc Represents a ReactionMessage. + * @implements IReactionMessage + * @constructor + * @param {proto.IReactionMessage=} [properties] Properties to set + */ + function ReactionMessage(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ReactionMessage key. + * @member {proto.IMessageKey|null|undefined} key + * @memberof proto.ReactionMessage + * @instance + */ + ReactionMessage.prototype.key = null; + + /** + * ReactionMessage text. + * @member {string} text + * @memberof proto.ReactionMessage + * @instance + */ + ReactionMessage.prototype.text = ""; + + /** + * ReactionMessage groupingKey. + * @member {string} groupingKey + * @memberof proto.ReactionMessage + * @instance + */ + ReactionMessage.prototype.groupingKey = ""; + + /** + * ReactionMessage senderTimestampMs. + * @member {number|Long} senderTimestampMs + * @memberof proto.ReactionMessage + * @instance + */ + ReactionMessage.prototype.senderTimestampMs = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Creates a new ReactionMessage instance using the specified properties. + * @function create + * @memberof proto.ReactionMessage + * @static + * @param {proto.IReactionMessage=} [properties] Properties to set + * @returns {proto.ReactionMessage} ReactionMessage instance + */ + ReactionMessage.create = function create(properties) { + return new ReactionMessage(properties); + }; + + /** + * Encodes the specified ReactionMessage message. Does not implicitly {@link proto.ReactionMessage.verify|verify} messages. + * @function encode + * @memberof proto.ReactionMessage + * @static + * @param {proto.IReactionMessage} message ReactionMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ReactionMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.key != null && Object.hasOwnProperty.call(message, "key")) + $root.proto.MessageKey.encode(message.key, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.text != null && Object.hasOwnProperty.call(message, "text")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.text); + if (message.groupingKey != null && Object.hasOwnProperty.call(message, "groupingKey")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.groupingKey); + if (message.senderTimestampMs != null && Object.hasOwnProperty.call(message, "senderTimestampMs")) + writer.uint32(/* id 4, wireType 0 =*/32).int64(message.senderTimestampMs); + return writer; + }; + + /** + * Encodes the specified ReactionMessage message, length delimited. Does not implicitly {@link proto.ReactionMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.ReactionMessage + * @static + * @param {proto.IReactionMessage} message ReactionMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ReactionMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ReactionMessage message from the specified reader or buffer. + * @function decode + * @memberof proto.ReactionMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.ReactionMessage} ReactionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ReactionMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.ReactionMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = $root.proto.MessageKey.decode(reader, reader.uint32()); + break; + case 2: + message.text = reader.string(); + break; + case 3: + message.groupingKey = reader.string(); + break; + case 4: + message.senderTimestampMs = reader.int64(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ReactionMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.ReactionMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.ReactionMessage} ReactionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ReactionMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ReactionMessage message. + * @function verify + * @memberof proto.ReactionMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ReactionMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.key != null && message.hasOwnProperty("key")) { + var error = $root.proto.MessageKey.verify(message.key); + if (error) + return "key." + error; + } + if (message.text != null && message.hasOwnProperty("text")) + if (!$util.isString(message.text)) + return "text: string expected"; + if (message.groupingKey != null && message.hasOwnProperty("groupingKey")) + if (!$util.isString(message.groupingKey)) + return "groupingKey: string expected"; + if (message.senderTimestampMs != null && message.hasOwnProperty("senderTimestampMs")) + if (!$util.isInteger(message.senderTimestampMs) && !(message.senderTimestampMs && $util.isInteger(message.senderTimestampMs.low) && $util.isInteger(message.senderTimestampMs.high))) + return "senderTimestampMs: integer|Long expected"; + return null; + }; + + /** + * Creates a ReactionMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.ReactionMessage + * @static + * @param {Object.} object Plain object + * @returns {proto.ReactionMessage} ReactionMessage + */ + ReactionMessage.fromObject = function fromObject(object) { + if (object instanceof $root.proto.ReactionMessage) + return object; + var message = new $root.proto.ReactionMessage(); + if (object.key != null) { + if (typeof object.key !== "object") + throw TypeError(".proto.ReactionMessage.key: object expected"); + message.key = $root.proto.MessageKey.fromObject(object.key); + } + if (object.text != null) + message.text = String(object.text); + if (object.groupingKey != null) + message.groupingKey = String(object.groupingKey); + if (object.senderTimestampMs != null) + if ($util.Long) + (message.senderTimestampMs = $util.Long.fromValue(object.senderTimestampMs)).unsigned = false; + else if (typeof object.senderTimestampMs === "string") + message.senderTimestampMs = parseInt(object.senderTimestampMs, 10); + else if (typeof object.senderTimestampMs === "number") + message.senderTimestampMs = object.senderTimestampMs; + else if (typeof object.senderTimestampMs === "object") + message.senderTimestampMs = new $util.LongBits(object.senderTimestampMs.low >>> 0, object.senderTimestampMs.high >>> 0).toNumber(); + return message; + }; + + /** + * Creates a plain object from a ReactionMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.ReactionMessage + * @static + * @param {proto.ReactionMessage} message ReactionMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ReactionMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.key = null; + object.text = ""; + object.groupingKey = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.senderTimestampMs = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.senderTimestampMs = options.longs === String ? "0" : 0; + } + if (message.key != null && message.hasOwnProperty("key")) + object.key = $root.proto.MessageKey.toObject(message.key, options); + if (message.text != null && message.hasOwnProperty("text")) + object.text = message.text; + if (message.groupingKey != null && message.hasOwnProperty("groupingKey")) + object.groupingKey = message.groupingKey; + if (message.senderTimestampMs != null && message.hasOwnProperty("senderTimestampMs")) + if (typeof message.senderTimestampMs === "number") + object.senderTimestampMs = options.longs === String ? String(message.senderTimestampMs) : message.senderTimestampMs; + else + object.senderTimestampMs = options.longs === String ? $util.Long.prototype.toString.call(message.senderTimestampMs) : options.longs === Number ? new $util.LongBits(message.senderTimestampMs.low >>> 0, message.senderTimestampMs.high >>> 0).toNumber() : message.senderTimestampMs; + return object; + }; + + /** + * Converts this ReactionMessage to JSON. + * @function toJSON + * @memberof proto.ReactionMessage + * @instance + * @returns {Object.} JSON object + */ + ReactionMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ReactionMessage; + })(); + + proto.StickerSyncRMRMessage = (function() { + + /** + * Properties of a StickerSyncRMRMessage. + * @memberof proto + * @interface IStickerSyncRMRMessage + * @property {Array.|null} [filehash] StickerSyncRMRMessage filehash + * @property {string|null} [rmrSource] StickerSyncRMRMessage rmrSource + * @property {number|Long|null} [requestTimestamp] StickerSyncRMRMessage requestTimestamp + */ + + /** + * Constructs a new StickerSyncRMRMessage. + * @memberof proto + * @classdesc Represents a StickerSyncRMRMessage. + * @implements IStickerSyncRMRMessage + * @constructor + * @param {proto.IStickerSyncRMRMessage=} [properties] Properties to set + */ + function StickerSyncRMRMessage(properties) { + this.filehash = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * StickerSyncRMRMessage filehash. + * @member {Array.} filehash + * @memberof proto.StickerSyncRMRMessage + * @instance + */ + StickerSyncRMRMessage.prototype.filehash = $util.emptyArray; + + /** + * StickerSyncRMRMessage rmrSource. + * @member {string} rmrSource + * @memberof proto.StickerSyncRMRMessage + * @instance + */ + StickerSyncRMRMessage.prototype.rmrSource = ""; + + /** + * StickerSyncRMRMessage requestTimestamp. + * @member {number|Long} requestTimestamp + * @memberof proto.StickerSyncRMRMessage + * @instance + */ + StickerSyncRMRMessage.prototype.requestTimestamp = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Creates a new StickerSyncRMRMessage instance using the specified properties. + * @function create + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {proto.IStickerSyncRMRMessage=} [properties] Properties to set + * @returns {proto.StickerSyncRMRMessage} StickerSyncRMRMessage instance + */ + StickerSyncRMRMessage.create = function create(properties) { + return new StickerSyncRMRMessage(properties); + }; + + /** + * Encodes the specified StickerSyncRMRMessage message. Does not implicitly {@link proto.StickerSyncRMRMessage.verify|verify} messages. + * @function encode + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {proto.IStickerSyncRMRMessage} message StickerSyncRMRMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + StickerSyncRMRMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.filehash != null && message.filehash.length) + for (var i = 0; i < message.filehash.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.filehash[i]); + if (message.rmrSource != null && Object.hasOwnProperty.call(message, "rmrSource")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.rmrSource); + if (message.requestTimestamp != null && Object.hasOwnProperty.call(message, "requestTimestamp")) + writer.uint32(/* id 3, wireType 0 =*/24).int64(message.requestTimestamp); + return writer; + }; + + /** + * Encodes the specified StickerSyncRMRMessage message, length delimited. Does not implicitly {@link proto.StickerSyncRMRMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {proto.IStickerSyncRMRMessage} message StickerSyncRMRMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + StickerSyncRMRMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a StickerSyncRMRMessage message from the specified reader or buffer. + * @function decode + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.StickerSyncRMRMessage} StickerSyncRMRMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + StickerSyncRMRMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.StickerSyncRMRMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.filehash && message.filehash.length)) + message.filehash = []; + message.filehash.push(reader.string()); + break; + case 2: + message.rmrSource = reader.string(); + break; + case 3: + message.requestTimestamp = reader.int64(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a StickerSyncRMRMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.StickerSyncRMRMessage} StickerSyncRMRMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + StickerSyncRMRMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a StickerSyncRMRMessage message. + * @function verify + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + StickerSyncRMRMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.filehash != null && message.hasOwnProperty("filehash")) { + if (!Array.isArray(message.filehash)) + return "filehash: array expected"; + for (var i = 0; i < message.filehash.length; ++i) + if (!$util.isString(message.filehash[i])) + return "filehash: string[] expected"; + } + if (message.rmrSource != null && message.hasOwnProperty("rmrSource")) + if (!$util.isString(message.rmrSource)) + return "rmrSource: string expected"; + if (message.requestTimestamp != null && message.hasOwnProperty("requestTimestamp")) + if (!$util.isInteger(message.requestTimestamp) && !(message.requestTimestamp && $util.isInteger(message.requestTimestamp.low) && $util.isInteger(message.requestTimestamp.high))) + return "requestTimestamp: integer|Long expected"; + return null; + }; + + /** + * Creates a StickerSyncRMRMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {Object.} object Plain object + * @returns {proto.StickerSyncRMRMessage} StickerSyncRMRMessage + */ + StickerSyncRMRMessage.fromObject = function fromObject(object) { + if (object instanceof $root.proto.StickerSyncRMRMessage) + return object; + var message = new $root.proto.StickerSyncRMRMessage(); + if (object.filehash) { + if (!Array.isArray(object.filehash)) + throw TypeError(".proto.StickerSyncRMRMessage.filehash: array expected"); + message.filehash = []; + for (var i = 0; i < object.filehash.length; ++i) + message.filehash[i] = String(object.filehash[i]); + } + if (object.rmrSource != null) + message.rmrSource = String(object.rmrSource); + if (object.requestTimestamp != null) + if ($util.Long) + (message.requestTimestamp = $util.Long.fromValue(object.requestTimestamp)).unsigned = false; + else if (typeof object.requestTimestamp === "string") + message.requestTimestamp = parseInt(object.requestTimestamp, 10); + else if (typeof object.requestTimestamp === "number") + message.requestTimestamp = object.requestTimestamp; + else if (typeof object.requestTimestamp === "object") + message.requestTimestamp = new $util.LongBits(object.requestTimestamp.low >>> 0, object.requestTimestamp.high >>> 0).toNumber(); + return message; + }; + + /** + * Creates a plain object from a StickerSyncRMRMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.StickerSyncRMRMessage + * @static + * @param {proto.StickerSyncRMRMessage} message StickerSyncRMRMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + StickerSyncRMRMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.filehash = []; + if (options.defaults) { + object.rmrSource = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.requestTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.requestTimestamp = options.longs === String ? "0" : 0; + } + if (message.filehash && message.filehash.length) { + object.filehash = []; + for (var j = 0; j < message.filehash.length; ++j) + object.filehash[j] = message.filehash[j]; + } + if (message.rmrSource != null && message.hasOwnProperty("rmrSource")) + object.rmrSource = message.rmrSource; + if (message.requestTimestamp != null && message.hasOwnProperty("requestTimestamp")) + if (typeof message.requestTimestamp === "number") + object.requestTimestamp = options.longs === String ? String(message.requestTimestamp) : message.requestTimestamp; + else + object.requestTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.requestTimestamp) : options.longs === Number ? new $util.LongBits(message.requestTimestamp.low >>> 0, message.requestTimestamp.high >>> 0).toNumber() : message.requestTimestamp; + return object; + }; + + /** + * Converts this StickerSyncRMRMessage to JSON. + * @function toJSON + * @memberof proto.StickerSyncRMRMessage + * @instance + * @returns {Object.} JSON object + */ + StickerSyncRMRMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StickerSyncRMRMessage; + })(); + proto.Message = (function() { /** @@ -42870,6 +45375,9 @@ $root.proto = (function() { * @property {proto.IButtonsMessage|null} [buttonsMessage] Message buttonsMessage * @property {proto.IButtonsResponseMessage|null} [buttonsResponseMessage] Message buttonsResponseMessage * @property {proto.IPaymentInviteMessage|null} [paymentInviteMessage] Message paymentInviteMessage + * @property {proto.IInteractiveMessage|null} [interactiveMessage] Message interactiveMessage + * @property {proto.IReactionMessage|null} [reactionMessage] Message reactionMessage + * @property {proto.IStickerSyncRMRMessage|null} [stickerSyncRmrMessage] Message stickerSyncRmrMessage */ /** @@ -43175,6 +45683,30 @@ $root.proto = (function() { */ Message.prototype.paymentInviteMessage = null; + /** + * Message interactiveMessage. + * @member {proto.IInteractiveMessage|null|undefined} interactiveMessage + * @memberof proto.Message + * @instance + */ + Message.prototype.interactiveMessage = null; + + /** + * Message reactionMessage. + * @member {proto.IReactionMessage|null|undefined} reactionMessage + * @memberof proto.Message + * @instance + */ + Message.prototype.reactionMessage = null; + + /** + * Message stickerSyncRmrMessage. + * @member {proto.IStickerSyncRMRMessage|null|undefined} stickerSyncRmrMessage + * @memberof proto.Message + * @instance + */ + Message.prototype.stickerSyncRmrMessage = null; + /** * Creates a new Message instance using the specified properties. * @function create @@ -43271,6 +45803,12 @@ $root.proto = (function() { $root.proto.ButtonsResponseMessage.encode(message.buttonsResponseMessage, writer.uint32(/* id 43, wireType 2 =*/346).fork()).ldelim(); if (message.paymentInviteMessage != null && Object.hasOwnProperty.call(message, "paymentInviteMessage")) $root.proto.PaymentInviteMessage.encode(message.paymentInviteMessage, writer.uint32(/* id 44, wireType 2 =*/354).fork()).ldelim(); + if (message.interactiveMessage != null && Object.hasOwnProperty.call(message, "interactiveMessage")) + $root.proto.InteractiveMessage.encode(message.interactiveMessage, writer.uint32(/* id 45, wireType 2 =*/362).fork()).ldelim(); + if (message.reactionMessage != null && Object.hasOwnProperty.call(message, "reactionMessage")) + $root.proto.ReactionMessage.encode(message.reactionMessage, writer.uint32(/* id 46, wireType 2 =*/370).fork()).ldelim(); + if (message.stickerSyncRmrMessage != null && Object.hasOwnProperty.call(message, "stickerSyncRmrMessage")) + $root.proto.StickerSyncRMRMessage.encode(message.stickerSyncRmrMessage, writer.uint32(/* id 47, wireType 2 =*/378).fork()).ldelim(); return writer; }; @@ -43413,6 +45951,15 @@ $root.proto = (function() { case 44: message.paymentInviteMessage = $root.proto.PaymentInviteMessage.decode(reader, reader.uint32()); break; + case 45: + message.interactiveMessage = $root.proto.InteractiveMessage.decode(reader, reader.uint32()); + break; + case 46: + message.reactionMessage = $root.proto.ReactionMessage.decode(reader, reader.uint32()); + break; + case 47: + message.stickerSyncRmrMessage = $root.proto.StickerSyncRMRMessage.decode(reader, reader.uint32()); + break; default: reader.skipType(tag & 7); break; @@ -43626,6 +46173,21 @@ $root.proto = (function() { if (error) return "paymentInviteMessage." + error; } + if (message.interactiveMessage != null && message.hasOwnProperty("interactiveMessage")) { + var error = $root.proto.InteractiveMessage.verify(message.interactiveMessage); + if (error) + return "interactiveMessage." + error; + } + if (message.reactionMessage != null && message.hasOwnProperty("reactionMessage")) { + var error = $root.proto.ReactionMessage.verify(message.reactionMessage); + if (error) + return "reactionMessage." + error; + } + if (message.stickerSyncRmrMessage != null && message.hasOwnProperty("stickerSyncRmrMessage")) { + var error = $root.proto.StickerSyncRMRMessage.verify(message.stickerSyncRmrMessage); + if (error) + return "stickerSyncRmrMessage." + error; + } return null; }; @@ -43818,6 +46380,21 @@ $root.proto = (function() { throw TypeError(".proto.Message.paymentInviteMessage: object expected"); message.paymentInviteMessage = $root.proto.PaymentInviteMessage.fromObject(object.paymentInviteMessage); } + if (object.interactiveMessage != null) { + if (typeof object.interactiveMessage !== "object") + throw TypeError(".proto.Message.interactiveMessage: object expected"); + message.interactiveMessage = $root.proto.InteractiveMessage.fromObject(object.interactiveMessage); + } + if (object.reactionMessage != null) { + if (typeof object.reactionMessage !== "object") + throw TypeError(".proto.Message.reactionMessage: object expected"); + message.reactionMessage = $root.proto.ReactionMessage.fromObject(object.reactionMessage); + } + if (object.stickerSyncRmrMessage != null) { + if (typeof object.stickerSyncRmrMessage !== "object") + throw TypeError(".proto.Message.stickerSyncRmrMessage: object expected"); + message.stickerSyncRmrMessage = $root.proto.StickerSyncRMRMessage.fromObject(object.stickerSyncRmrMessage); + } return message; }; @@ -43871,6 +46448,9 @@ $root.proto = (function() { object.buttonsMessage = null; object.buttonsResponseMessage = null; object.paymentInviteMessage = null; + object.interactiveMessage = null; + object.reactionMessage = null; + object.stickerSyncRmrMessage = null; } if (message.conversation != null && message.hasOwnProperty("conversation")) object.conversation = message.conversation; @@ -43944,6 +46524,12 @@ $root.proto = (function() { object.buttonsResponseMessage = $root.proto.ButtonsResponseMessage.toObject(message.buttonsResponseMessage, options); if (message.paymentInviteMessage != null && message.hasOwnProperty("paymentInviteMessage")) object.paymentInviteMessage = $root.proto.PaymentInviteMessage.toObject(message.paymentInviteMessage, options); + if (message.interactiveMessage != null && message.hasOwnProperty("interactiveMessage")) + object.interactiveMessage = $root.proto.InteractiveMessage.toObject(message.interactiveMessage, options); + if (message.reactionMessage != null && message.hasOwnProperty("reactionMessage")) + object.reactionMessage = $root.proto.ReactionMessage.toObject(message.reactionMessage, options); + if (message.stickerSyncRmrMessage != null && message.hasOwnProperty("stickerSyncRmrMessage")) + object.stickerSyncRmrMessage = $root.proto.StickerSyncRMRMessage.toObject(message.stickerSyncRmrMessage, options); return object; }; @@ -43961,6 +46547,3807 @@ $root.proto = (function() { return Message; })(); + proto.ActionLink = (function() { + + /** + * Properties of an ActionLink. + * @memberof proto + * @interface IActionLink + * @property {string|null} [url] ActionLink url + * @property {string|null} [buttonTitle] ActionLink buttonTitle + */ + + /** + * Constructs a new ActionLink. + * @memberof proto + * @classdesc Represents an ActionLink. + * @implements IActionLink + * @constructor + * @param {proto.IActionLink=} [properties] Properties to set + */ + function ActionLink(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ActionLink url. + * @member {string} url + * @memberof proto.ActionLink + * @instance + */ + ActionLink.prototype.url = ""; + + /** + * ActionLink buttonTitle. + * @member {string} buttonTitle + * @memberof proto.ActionLink + * @instance + */ + ActionLink.prototype.buttonTitle = ""; + + /** + * Creates a new ActionLink instance using the specified properties. + * @function create + * @memberof proto.ActionLink + * @static + * @param {proto.IActionLink=} [properties] Properties to set + * @returns {proto.ActionLink} ActionLink instance + */ + ActionLink.create = function create(properties) { + return new ActionLink(properties); + }; + + /** + * Encodes the specified ActionLink message. Does not implicitly {@link proto.ActionLink.verify|verify} messages. + * @function encode + * @memberof proto.ActionLink + * @static + * @param {proto.IActionLink} message ActionLink message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ActionLink.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.url != null && Object.hasOwnProperty.call(message, "url")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.url); + if (message.buttonTitle != null && Object.hasOwnProperty.call(message, "buttonTitle")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.buttonTitle); + return writer; + }; + + /** + * Encodes the specified ActionLink message, length delimited. Does not implicitly {@link proto.ActionLink.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.ActionLink + * @static + * @param {proto.IActionLink} message ActionLink message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ActionLink.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an ActionLink message from the specified reader or buffer. + * @function decode + * @memberof proto.ActionLink + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.ActionLink} ActionLink + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ActionLink.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.ActionLink(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = reader.string(); + break; + case 2: + message.buttonTitle = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an ActionLink message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.ActionLink + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.ActionLink} ActionLink + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ActionLink.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an ActionLink message. + * @function verify + * @memberof proto.ActionLink + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ActionLink.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.url != null && message.hasOwnProperty("url")) + if (!$util.isString(message.url)) + return "url: string expected"; + if (message.buttonTitle != null && message.hasOwnProperty("buttonTitle")) + if (!$util.isString(message.buttonTitle)) + return "buttonTitle: string expected"; + return null; + }; + + /** + * Creates an ActionLink message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.ActionLink + * @static + * @param {Object.} object Plain object + * @returns {proto.ActionLink} ActionLink + */ + ActionLink.fromObject = function fromObject(object) { + if (object instanceof $root.proto.ActionLink) + return object; + var message = new $root.proto.ActionLink(); + if (object.url != null) + message.url = String(object.url); + if (object.buttonTitle != null) + message.buttonTitle = String(object.buttonTitle); + return message; + }; + + /** + * Creates a plain object from an ActionLink message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.ActionLink + * @static + * @param {proto.ActionLink} message ActionLink + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ActionLink.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.url = ""; + object.buttonTitle = ""; + } + if (message.url != null && message.hasOwnProperty("url")) + object.url = message.url; + if (message.buttonTitle != null && message.hasOwnProperty("buttonTitle")) + object.buttonTitle = message.buttonTitle; + return object; + }; + + /** + * Converts this ActionLink to JSON. + * @function toJSON + * @memberof proto.ActionLink + * @instance + * @returns {Object.} JSON object + */ + ActionLink.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ActionLink; + })(); + + proto.DisappearingMode = (function() { + + /** + * Properties of a DisappearingMode. + * @memberof proto + * @interface IDisappearingMode + * @property {proto.DisappearingMode.DisappearingModeInitiator|null} [initiator] DisappearingMode initiator + */ + + /** + * Constructs a new DisappearingMode. + * @memberof proto + * @classdesc Represents a DisappearingMode. + * @implements IDisappearingMode + * @constructor + * @param {proto.IDisappearingMode=} [properties] Properties to set + */ + function DisappearingMode(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * DisappearingMode initiator. + * @member {proto.DisappearingMode.DisappearingModeInitiator} initiator + * @memberof proto.DisappearingMode + * @instance + */ + DisappearingMode.prototype.initiator = 0; + + /** + * Creates a new DisappearingMode instance using the specified properties. + * @function create + * @memberof proto.DisappearingMode + * @static + * @param {proto.IDisappearingMode=} [properties] Properties to set + * @returns {proto.DisappearingMode} DisappearingMode instance + */ + DisappearingMode.create = function create(properties) { + return new DisappearingMode(properties); + }; + + /** + * Encodes the specified DisappearingMode message. Does not implicitly {@link proto.DisappearingMode.verify|verify} messages. + * @function encode + * @memberof proto.DisappearingMode + * @static + * @param {proto.IDisappearingMode} message DisappearingMode message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + DisappearingMode.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.initiator != null && Object.hasOwnProperty.call(message, "initiator")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.initiator); + return writer; + }; + + /** + * Encodes the specified DisappearingMode message, length delimited. Does not implicitly {@link proto.DisappearingMode.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.DisappearingMode + * @static + * @param {proto.IDisappearingMode} message DisappearingMode message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + DisappearingMode.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a DisappearingMode message from the specified reader or buffer. + * @function decode + * @memberof proto.DisappearingMode + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.DisappearingMode} DisappearingMode + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + DisappearingMode.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.DisappearingMode(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.initiator = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a DisappearingMode message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.DisappearingMode + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.DisappearingMode} DisappearingMode + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + DisappearingMode.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a DisappearingMode message. + * @function verify + * @memberof proto.DisappearingMode + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + DisappearingMode.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.initiator != null && message.hasOwnProperty("initiator")) + switch (message.initiator) { + default: + return "initiator: enum value expected"; + case 0: + case 1: + case 2: + break; + } + return null; + }; + + /** + * Creates a DisappearingMode message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.DisappearingMode + * @static + * @param {Object.} object Plain object + * @returns {proto.DisappearingMode} DisappearingMode + */ + DisappearingMode.fromObject = function fromObject(object) { + if (object instanceof $root.proto.DisappearingMode) + return object; + var message = new $root.proto.DisappearingMode(); + switch (object.initiator) { + case "CHANGED_IN_CHAT": + case 0: + message.initiator = 0; + break; + case "INITIATED_BY_ME": + case 1: + message.initiator = 1; + break; + case "INITIATED_BY_OTHER": + case 2: + message.initiator = 2; + break; + } + return message; + }; + + /** + * Creates a plain object from a DisappearingMode message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.DisappearingMode + * @static + * @param {proto.DisappearingMode} message DisappearingMode + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + DisappearingMode.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.initiator = options.enums === String ? "CHANGED_IN_CHAT" : 0; + if (message.initiator != null && message.hasOwnProperty("initiator")) + object.initiator = options.enums === String ? $root.proto.DisappearingMode.DisappearingModeInitiator[message.initiator] : message.initiator; + return object; + }; + + /** + * Converts this DisappearingMode to JSON. + * @function toJSON + * @memberof proto.DisappearingMode + * @instance + * @returns {Object.} JSON object + */ + DisappearingMode.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * DisappearingModeInitiator enum. + * @name proto.DisappearingMode.DisappearingModeInitiator + * @enum {number} + * @property {number} CHANGED_IN_CHAT=0 CHANGED_IN_CHAT value + * @property {number} INITIATED_BY_ME=1 INITIATED_BY_ME value + * @property {number} INITIATED_BY_OTHER=2 INITIATED_BY_OTHER value + */ + DisappearingMode.DisappearingModeInitiator = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "CHANGED_IN_CHAT"] = 0; + values[valuesById[1] = "INITIATED_BY_ME"] = 1; + values[valuesById[2] = "INITIATED_BY_OTHER"] = 2; + return values; + })(); + + return DisappearingMode; + })(); + + proto.MediaData = (function() { + + /** + * Properties of a MediaData. + * @memberof proto + * @interface IMediaData + * @property {Uint8Array|null} [mediaKey] MediaData mediaKey + * @property {number|Long|null} [mediaKeyTimestamp] MediaData mediaKeyTimestamp + * @property {Uint8Array|null} [fileSha256] MediaData fileSha256 + * @property {Uint8Array|null} [fileEncSha256] MediaData fileEncSha256 + * @property {string|null} [directPath] MediaData directPath + */ + + /** + * Constructs a new MediaData. + * @memberof proto + * @classdesc Represents a MediaData. + * @implements IMediaData + * @constructor + * @param {proto.IMediaData=} [properties] Properties to set + */ + function MediaData(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * MediaData mediaKey. + * @member {Uint8Array} mediaKey + * @memberof proto.MediaData + * @instance + */ + MediaData.prototype.mediaKey = $util.newBuffer([]); + + /** + * MediaData mediaKeyTimestamp. + * @member {number|Long} mediaKeyTimestamp + * @memberof proto.MediaData + * @instance + */ + MediaData.prototype.mediaKeyTimestamp = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * MediaData fileSha256. + * @member {Uint8Array} fileSha256 + * @memberof proto.MediaData + * @instance + */ + MediaData.prototype.fileSha256 = $util.newBuffer([]); + + /** + * MediaData fileEncSha256. + * @member {Uint8Array} fileEncSha256 + * @memberof proto.MediaData + * @instance + */ + MediaData.prototype.fileEncSha256 = $util.newBuffer([]); + + /** + * MediaData directPath. + * @member {string} directPath + * @memberof proto.MediaData + * @instance + */ + MediaData.prototype.directPath = ""; + + /** + * Creates a new MediaData instance using the specified properties. + * @function create + * @memberof proto.MediaData + * @static + * @param {proto.IMediaData=} [properties] Properties to set + * @returns {proto.MediaData} MediaData instance + */ + MediaData.create = function create(properties) { + return new MediaData(properties); + }; + + /** + * Encodes the specified MediaData message. Does not implicitly {@link proto.MediaData.verify|verify} messages. + * @function encode + * @memberof proto.MediaData + * @static + * @param {proto.IMediaData} message MediaData message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + MediaData.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.mediaKey != null && Object.hasOwnProperty.call(message, "mediaKey")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.mediaKey); + if (message.mediaKeyTimestamp != null && Object.hasOwnProperty.call(message, "mediaKeyTimestamp")) + writer.uint32(/* id 2, wireType 0 =*/16).int64(message.mediaKeyTimestamp); + if (message.fileSha256 != null && Object.hasOwnProperty.call(message, "fileSha256")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.fileSha256); + if (message.fileEncSha256 != null && Object.hasOwnProperty.call(message, "fileEncSha256")) + writer.uint32(/* id 4, wireType 2 =*/34).bytes(message.fileEncSha256); + if (message.directPath != null && Object.hasOwnProperty.call(message, "directPath")) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.directPath); + return writer; + }; + + /** + * Encodes the specified MediaData message, length delimited. Does not implicitly {@link proto.MediaData.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.MediaData + * @static + * @param {proto.IMediaData} message MediaData message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + MediaData.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a MediaData message from the specified reader or buffer. + * @function decode + * @memberof proto.MediaData + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.MediaData} MediaData + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + MediaData.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.MediaData(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.mediaKey = reader.bytes(); + break; + case 2: + message.mediaKeyTimestamp = reader.int64(); + break; + case 3: + message.fileSha256 = reader.bytes(); + break; + case 4: + message.fileEncSha256 = reader.bytes(); + break; + case 5: + message.directPath = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a MediaData message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.MediaData + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.MediaData} MediaData + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + MediaData.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a MediaData message. + * @function verify + * @memberof proto.MediaData + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + MediaData.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.mediaKey != null && message.hasOwnProperty("mediaKey")) + if (!(message.mediaKey && typeof message.mediaKey.length === "number" || $util.isString(message.mediaKey))) + return "mediaKey: buffer expected"; + if (message.mediaKeyTimestamp != null && message.hasOwnProperty("mediaKeyTimestamp")) + if (!$util.isInteger(message.mediaKeyTimestamp) && !(message.mediaKeyTimestamp && $util.isInteger(message.mediaKeyTimestamp.low) && $util.isInteger(message.mediaKeyTimestamp.high))) + return "mediaKeyTimestamp: integer|Long expected"; + if (message.fileSha256 != null && message.hasOwnProperty("fileSha256")) + if (!(message.fileSha256 && typeof message.fileSha256.length === "number" || $util.isString(message.fileSha256))) + return "fileSha256: buffer expected"; + if (message.fileEncSha256 != null && message.hasOwnProperty("fileEncSha256")) + if (!(message.fileEncSha256 && typeof message.fileEncSha256.length === "number" || $util.isString(message.fileEncSha256))) + return "fileEncSha256: buffer expected"; + if (message.directPath != null && message.hasOwnProperty("directPath")) + if (!$util.isString(message.directPath)) + return "directPath: string expected"; + return null; + }; + + /** + * Creates a MediaData message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.MediaData + * @static + * @param {Object.} object Plain object + * @returns {proto.MediaData} MediaData + */ + MediaData.fromObject = function fromObject(object) { + if (object instanceof $root.proto.MediaData) + return object; + var message = new $root.proto.MediaData(); + if (object.mediaKey != null) + if (typeof object.mediaKey === "string") + $util.base64.decode(object.mediaKey, message.mediaKey = $util.newBuffer($util.base64.length(object.mediaKey)), 0); + else if (object.mediaKey.length) + message.mediaKey = object.mediaKey; + if (object.mediaKeyTimestamp != null) + if ($util.Long) + (message.mediaKeyTimestamp = $util.Long.fromValue(object.mediaKeyTimestamp)).unsigned = false; + else if (typeof object.mediaKeyTimestamp === "string") + message.mediaKeyTimestamp = parseInt(object.mediaKeyTimestamp, 10); + else if (typeof object.mediaKeyTimestamp === "number") + message.mediaKeyTimestamp = object.mediaKeyTimestamp; + else if (typeof object.mediaKeyTimestamp === "object") + message.mediaKeyTimestamp = new $util.LongBits(object.mediaKeyTimestamp.low >>> 0, object.mediaKeyTimestamp.high >>> 0).toNumber(); + if (object.fileSha256 != null) + if (typeof object.fileSha256 === "string") + $util.base64.decode(object.fileSha256, message.fileSha256 = $util.newBuffer($util.base64.length(object.fileSha256)), 0); + else if (object.fileSha256.length) + message.fileSha256 = object.fileSha256; + if (object.fileEncSha256 != null) + if (typeof object.fileEncSha256 === "string") + $util.base64.decode(object.fileEncSha256, message.fileEncSha256 = $util.newBuffer($util.base64.length(object.fileEncSha256)), 0); + else if (object.fileEncSha256.length) + message.fileEncSha256 = object.fileEncSha256; + if (object.directPath != null) + message.directPath = String(object.directPath); + return message; + }; + + /** + * Creates a plain object from a MediaData message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.MediaData + * @static + * @param {proto.MediaData} message MediaData + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + MediaData.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.mediaKey = ""; + else { + object.mediaKey = []; + if (options.bytes !== Array) + object.mediaKey = $util.newBuffer(object.mediaKey); + } + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.mediaKeyTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.mediaKeyTimestamp = options.longs === String ? "0" : 0; + if (options.bytes === String) + object.fileSha256 = ""; + else { + object.fileSha256 = []; + if (options.bytes !== Array) + object.fileSha256 = $util.newBuffer(object.fileSha256); + } + if (options.bytes === String) + object.fileEncSha256 = ""; + else { + object.fileEncSha256 = []; + if (options.bytes !== Array) + object.fileEncSha256 = $util.newBuffer(object.fileEncSha256); + } + object.directPath = ""; + } + if (message.mediaKey != null && message.hasOwnProperty("mediaKey")) + object.mediaKey = options.bytes === String ? $util.base64.encode(message.mediaKey, 0, message.mediaKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.mediaKey) : message.mediaKey; + if (message.mediaKeyTimestamp != null && message.hasOwnProperty("mediaKeyTimestamp")) + if (typeof message.mediaKeyTimestamp === "number") + object.mediaKeyTimestamp = options.longs === String ? String(message.mediaKeyTimestamp) : message.mediaKeyTimestamp; + else + object.mediaKeyTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.mediaKeyTimestamp) : options.longs === Number ? new $util.LongBits(message.mediaKeyTimestamp.low >>> 0, message.mediaKeyTimestamp.high >>> 0).toNumber() : message.mediaKeyTimestamp; + if (message.fileSha256 != null && message.hasOwnProperty("fileSha256")) + object.fileSha256 = options.bytes === String ? $util.base64.encode(message.fileSha256, 0, message.fileSha256.length) : options.bytes === Array ? Array.prototype.slice.call(message.fileSha256) : message.fileSha256; + if (message.fileEncSha256 != null && message.hasOwnProperty("fileEncSha256")) + object.fileEncSha256 = options.bytes === String ? $util.base64.encode(message.fileEncSha256, 0, message.fileEncSha256.length) : options.bytes === Array ? Array.prototype.slice.call(message.fileEncSha256) : message.fileEncSha256; + if (message.directPath != null && message.hasOwnProperty("directPath")) + object.directPath = message.directPath; + return object; + }; + + /** + * Converts this MediaData to JSON. + * @function toJSON + * @memberof proto.MediaData + * @instance + * @returns {Object.} JSON object + */ + MediaData.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return MediaData; + })(); + + proto.PaymentBackground = (function() { + + /** + * Properties of a PaymentBackground. + * @memberof proto + * @interface IPaymentBackground + * @property {string|null} [id] PaymentBackground id + * @property {number|Long|null} [fileLength] PaymentBackground fileLength + * @property {number|null} [width] PaymentBackground width + * @property {number|null} [height] PaymentBackground height + * @property {string|null} [mimetype] PaymentBackground mimetype + * @property {number|null} [placeholderArgb] PaymentBackground placeholderArgb + * @property {number|null} [textArgb] PaymentBackground textArgb + * @property {number|null} [subtextArgb] PaymentBackground subtextArgb + * @property {proto.IMediaData|null} [mediaData] PaymentBackground mediaData + * @property {proto.PaymentBackground.PaymentBackgroundType|null} [type] PaymentBackground type + */ + + /** + * Constructs a new PaymentBackground. + * @memberof proto + * @classdesc Represents a PaymentBackground. + * @implements IPaymentBackground + * @constructor + * @param {proto.IPaymentBackground=} [properties] Properties to set + */ + function PaymentBackground(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * PaymentBackground id. + * @member {string} id + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.id = ""; + + /** + * PaymentBackground fileLength. + * @member {number|Long} fileLength + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.fileLength = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * PaymentBackground width. + * @member {number} width + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.width = 0; + + /** + * PaymentBackground height. + * @member {number} height + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.height = 0; + + /** + * PaymentBackground mimetype. + * @member {string} mimetype + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.mimetype = ""; + + /** + * PaymentBackground placeholderArgb. + * @member {number} placeholderArgb + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.placeholderArgb = 0; + + /** + * PaymentBackground textArgb. + * @member {number} textArgb + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.textArgb = 0; + + /** + * PaymentBackground subtextArgb. + * @member {number} subtextArgb + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.subtextArgb = 0; + + /** + * PaymentBackground mediaData. + * @member {proto.IMediaData|null|undefined} mediaData + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.mediaData = null; + + /** + * PaymentBackground type. + * @member {proto.PaymentBackground.PaymentBackgroundType} type + * @memberof proto.PaymentBackground + * @instance + */ + PaymentBackground.prototype.type = 0; + + /** + * Creates a new PaymentBackground instance using the specified properties. + * @function create + * @memberof proto.PaymentBackground + * @static + * @param {proto.IPaymentBackground=} [properties] Properties to set + * @returns {proto.PaymentBackground} PaymentBackground instance + */ + PaymentBackground.create = function create(properties) { + return new PaymentBackground(properties); + }; + + /** + * Encodes the specified PaymentBackground message. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. + * @function encode + * @memberof proto.PaymentBackground + * @static + * @param {proto.IPaymentBackground} message PaymentBackground message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + PaymentBackground.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.id); + if (message.fileLength != null && Object.hasOwnProperty.call(message, "fileLength")) + writer.uint32(/* id 2, wireType 0 =*/16).uint64(message.fileLength); + if (message.width != null && Object.hasOwnProperty.call(message, "width")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.width); + if (message.height != null && Object.hasOwnProperty.call(message, "height")) + writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.height); + if (message.mimetype != null && Object.hasOwnProperty.call(message, "mimetype")) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.mimetype); + if (message.placeholderArgb != null && Object.hasOwnProperty.call(message, "placeholderArgb")) + writer.uint32(/* id 6, wireType 5 =*/53).fixed32(message.placeholderArgb); + if (message.textArgb != null && Object.hasOwnProperty.call(message, "textArgb")) + writer.uint32(/* id 7, wireType 5 =*/61).fixed32(message.textArgb); + if (message.subtextArgb != null && Object.hasOwnProperty.call(message, "subtextArgb")) + writer.uint32(/* id 8, wireType 5 =*/69).fixed32(message.subtextArgb); + if (message.mediaData != null && Object.hasOwnProperty.call(message, "mediaData")) + $root.proto.MediaData.encode(message.mediaData, writer.uint32(/* id 9, wireType 2 =*/74).fork()).ldelim(); + if (message.type != null && Object.hasOwnProperty.call(message, "type")) + writer.uint32(/* id 10, wireType 0 =*/80).int32(message.type); + return writer; + }; + + /** + * Encodes the specified PaymentBackground message, length delimited. Does not implicitly {@link proto.PaymentBackground.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.PaymentBackground + * @static + * @param {proto.IPaymentBackground} message PaymentBackground message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + PaymentBackground.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a PaymentBackground message from the specified reader or buffer. + * @function decode + * @memberof proto.PaymentBackground + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.PaymentBackground} PaymentBackground + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + PaymentBackground.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.PaymentBackground(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + case 2: + message.fileLength = reader.uint64(); + break; + case 3: + message.width = reader.uint32(); + break; + case 4: + message.height = reader.uint32(); + break; + case 5: + message.mimetype = reader.string(); + break; + case 6: + message.placeholderArgb = reader.fixed32(); + break; + case 7: + message.textArgb = reader.fixed32(); + break; + case 8: + message.subtextArgb = reader.fixed32(); + break; + case 9: + message.mediaData = $root.proto.MediaData.decode(reader, reader.uint32()); + break; + case 10: + message.type = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a PaymentBackground message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.PaymentBackground + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.PaymentBackground} PaymentBackground + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + PaymentBackground.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a PaymentBackground message. + * @function verify + * @memberof proto.PaymentBackground + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + PaymentBackground.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isString(message.id)) + return "id: string expected"; + if (message.fileLength != null && message.hasOwnProperty("fileLength")) + if (!$util.isInteger(message.fileLength) && !(message.fileLength && $util.isInteger(message.fileLength.low) && $util.isInteger(message.fileLength.high))) + return "fileLength: integer|Long expected"; + if (message.width != null && message.hasOwnProperty("width")) + if (!$util.isInteger(message.width)) + return "width: integer expected"; + if (message.height != null && message.hasOwnProperty("height")) + if (!$util.isInteger(message.height)) + return "height: integer expected"; + if (message.mimetype != null && message.hasOwnProperty("mimetype")) + if (!$util.isString(message.mimetype)) + return "mimetype: string expected"; + if (message.placeholderArgb != null && message.hasOwnProperty("placeholderArgb")) + if (!$util.isInteger(message.placeholderArgb)) + return "placeholderArgb: integer expected"; + if (message.textArgb != null && message.hasOwnProperty("textArgb")) + if (!$util.isInteger(message.textArgb)) + return "textArgb: integer expected"; + if (message.subtextArgb != null && message.hasOwnProperty("subtextArgb")) + if (!$util.isInteger(message.subtextArgb)) + return "subtextArgb: integer expected"; + if (message.mediaData != null && message.hasOwnProperty("mediaData")) { + var error = $root.proto.MediaData.verify(message.mediaData); + if (error) + return "mediaData." + error; + } + if (message.type != null && message.hasOwnProperty("type")) + switch (message.type) { + default: + return "type: enum value expected"; + case 0: + case 1: + break; + } + return null; + }; + + /** + * Creates a PaymentBackground message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.PaymentBackground + * @static + * @param {Object.} object Plain object + * @returns {proto.PaymentBackground} PaymentBackground + */ + PaymentBackground.fromObject = function fromObject(object) { + if (object instanceof $root.proto.PaymentBackground) + return object; + var message = new $root.proto.PaymentBackground(); + if (object.id != null) + message.id = String(object.id); + if (object.fileLength != null) + if ($util.Long) + (message.fileLength = $util.Long.fromValue(object.fileLength)).unsigned = true; + else if (typeof object.fileLength === "string") + message.fileLength = parseInt(object.fileLength, 10); + else if (typeof object.fileLength === "number") + message.fileLength = object.fileLength; + else if (typeof object.fileLength === "object") + message.fileLength = new $util.LongBits(object.fileLength.low >>> 0, object.fileLength.high >>> 0).toNumber(true); + if (object.width != null) + message.width = object.width >>> 0; + if (object.height != null) + message.height = object.height >>> 0; + if (object.mimetype != null) + message.mimetype = String(object.mimetype); + if (object.placeholderArgb != null) + message.placeholderArgb = object.placeholderArgb >>> 0; + if (object.textArgb != null) + message.textArgb = object.textArgb >>> 0; + if (object.subtextArgb != null) + message.subtextArgb = object.subtextArgb >>> 0; + if (object.mediaData != null) { + if (typeof object.mediaData !== "object") + throw TypeError(".proto.PaymentBackground.mediaData: object expected"); + message.mediaData = $root.proto.MediaData.fromObject(object.mediaData); + } + switch (object.type) { + case "UNKNOWN": + case 0: + message.type = 0; + break; + case "DEFAULT": + case 1: + message.type = 1; + break; + } + return message; + }; + + /** + * Creates a plain object from a PaymentBackground message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.PaymentBackground + * @static + * @param {proto.PaymentBackground} message PaymentBackground + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + PaymentBackground.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.id = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.fileLength = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.fileLength = options.longs === String ? "0" : 0; + object.width = 0; + object.height = 0; + object.mimetype = ""; + object.placeholderArgb = 0; + object.textArgb = 0; + object.subtextArgb = 0; + object.mediaData = null; + object.type = options.enums === String ? "UNKNOWN" : 0; + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + if (message.fileLength != null && message.hasOwnProperty("fileLength")) + if (typeof message.fileLength === "number") + object.fileLength = options.longs === String ? String(message.fileLength) : message.fileLength; + else + object.fileLength = options.longs === String ? $util.Long.prototype.toString.call(message.fileLength) : options.longs === Number ? new $util.LongBits(message.fileLength.low >>> 0, message.fileLength.high >>> 0).toNumber(true) : message.fileLength; + if (message.width != null && message.hasOwnProperty("width")) + object.width = message.width; + if (message.height != null && message.hasOwnProperty("height")) + object.height = message.height; + if (message.mimetype != null && message.hasOwnProperty("mimetype")) + object.mimetype = message.mimetype; + if (message.placeholderArgb != null && message.hasOwnProperty("placeholderArgb")) + object.placeholderArgb = message.placeholderArgb; + if (message.textArgb != null && message.hasOwnProperty("textArgb")) + object.textArgb = message.textArgb; + if (message.subtextArgb != null && message.hasOwnProperty("subtextArgb")) + object.subtextArgb = message.subtextArgb; + if (message.mediaData != null && message.hasOwnProperty("mediaData")) + object.mediaData = $root.proto.MediaData.toObject(message.mediaData, options); + if (message.type != null && message.hasOwnProperty("type")) + object.type = options.enums === String ? $root.proto.PaymentBackground.PaymentBackgroundType[message.type] : message.type; + return object; + }; + + /** + * Converts this PaymentBackground to JSON. + * @function toJSON + * @memberof proto.PaymentBackground + * @instance + * @returns {Object.} JSON object + */ + PaymentBackground.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * PaymentBackgroundType enum. + * @name proto.PaymentBackground.PaymentBackgroundType + * @enum {number} + * @property {number} UNKNOWN=0 UNKNOWN value + * @property {number} DEFAULT=1 DEFAULT value + */ + PaymentBackground.PaymentBackgroundType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "UNKNOWN"] = 0; + values[valuesById[1] = "DEFAULT"] = 1; + return values; + })(); + + return PaymentBackground; + })(); + + proto.Money = (function() { + + /** + * Properties of a Money. + * @memberof proto + * @interface IMoney + * @property {number|Long|null} [value] Money value + * @property {number|null} [offset] Money offset + * @property {string|null} [currencyCode] Money currencyCode + */ + + /** + * Constructs a new Money. + * @memberof proto + * @classdesc Represents a Money. + * @implements IMoney + * @constructor + * @param {proto.IMoney=} [properties] Properties to set + */ + function Money(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Money value. + * @member {number|Long} value + * @memberof proto.Money + * @instance + */ + Money.prototype.value = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Money offset. + * @member {number} offset + * @memberof proto.Money + * @instance + */ + Money.prototype.offset = 0; + + /** + * Money currencyCode. + * @member {string} currencyCode + * @memberof proto.Money + * @instance + */ + Money.prototype.currencyCode = ""; + + /** + * Creates a new Money instance using the specified properties. + * @function create + * @memberof proto.Money + * @static + * @param {proto.IMoney=} [properties] Properties to set + * @returns {proto.Money} Money instance + */ + Money.create = function create(properties) { + return new Money(properties); + }; + + /** + * Encodes the specified Money message. Does not implicitly {@link proto.Money.verify|verify} messages. + * @function encode + * @memberof proto.Money + * @static + * @param {proto.IMoney} message Money message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Money.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.value != null && Object.hasOwnProperty.call(message, "value")) + writer.uint32(/* id 1, wireType 0 =*/8).int64(message.value); + if (message.offset != null && Object.hasOwnProperty.call(message, "offset")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.offset); + if (message.currencyCode != null && Object.hasOwnProperty.call(message, "currencyCode")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.currencyCode); + return writer; + }; + + /** + * Encodes the specified Money message, length delimited. Does not implicitly {@link proto.Money.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Money + * @static + * @param {proto.IMoney} message Money message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Money.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Money message from the specified reader or buffer. + * @function decode + * @memberof proto.Money + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Money} Money + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Money.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Money(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.int64(); + break; + case 2: + message.offset = reader.uint32(); + break; + case 3: + message.currencyCode = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Money message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Money + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Money} Money + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Money.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Money message. + * @function verify + * @memberof proto.Money + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Money.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.value != null && message.hasOwnProperty("value")) + if (!$util.isInteger(message.value) && !(message.value && $util.isInteger(message.value.low) && $util.isInteger(message.value.high))) + return "value: integer|Long expected"; + if (message.offset != null && message.hasOwnProperty("offset")) + if (!$util.isInteger(message.offset)) + return "offset: integer expected"; + if (message.currencyCode != null && message.hasOwnProperty("currencyCode")) + if (!$util.isString(message.currencyCode)) + return "currencyCode: string expected"; + return null; + }; + + /** + * Creates a Money message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Money + * @static + * @param {Object.} object Plain object + * @returns {proto.Money} Money + */ + Money.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Money) + return object; + var message = new $root.proto.Money(); + if (object.value != null) + if ($util.Long) + (message.value = $util.Long.fromValue(object.value)).unsigned = false; + else if (typeof object.value === "string") + message.value = parseInt(object.value, 10); + else if (typeof object.value === "number") + message.value = object.value; + else if (typeof object.value === "object") + message.value = new $util.LongBits(object.value.low >>> 0, object.value.high >>> 0).toNumber(); + if (object.offset != null) + message.offset = object.offset >>> 0; + if (object.currencyCode != null) + message.currencyCode = String(object.currencyCode); + return message; + }; + + /** + * Creates a plain object from a Money message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Money + * @static + * @param {proto.Money} message Money + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Money.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.value = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.value = options.longs === String ? "0" : 0; + object.offset = 0; + object.currencyCode = ""; + } + if (message.value != null && message.hasOwnProperty("value")) + if (typeof message.value === "number") + object.value = options.longs === String ? String(message.value) : message.value; + else + object.value = options.longs === String ? $util.Long.prototype.toString.call(message.value) : options.longs === Number ? new $util.LongBits(message.value.low >>> 0, message.value.high >>> 0).toNumber() : message.value; + if (message.offset != null && message.hasOwnProperty("offset")) + object.offset = message.offset; + if (message.currencyCode != null && message.hasOwnProperty("currencyCode")) + object.currencyCode = message.currencyCode; + return object; + }; + + /** + * Converts this Money to JSON. + * @function toJSON + * @memberof proto.Money + * @instance + * @returns {Object.} JSON object + */ + Money.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Money; + })(); + + proto.HydratedQuickReplyButton = (function() { + + /** + * Properties of a HydratedQuickReplyButton. + * @memberof proto + * @interface IHydratedQuickReplyButton + * @property {string|null} [displayText] HydratedQuickReplyButton displayText + * @property {string|null} [id] HydratedQuickReplyButton id + */ + + /** + * Constructs a new HydratedQuickReplyButton. + * @memberof proto + * @classdesc Represents a HydratedQuickReplyButton. + * @implements IHydratedQuickReplyButton + * @constructor + * @param {proto.IHydratedQuickReplyButton=} [properties] Properties to set + */ + function HydratedQuickReplyButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * HydratedQuickReplyButton displayText. + * @member {string} displayText + * @memberof proto.HydratedQuickReplyButton + * @instance + */ + HydratedQuickReplyButton.prototype.displayText = ""; + + /** + * HydratedQuickReplyButton id. + * @member {string} id + * @memberof proto.HydratedQuickReplyButton + * @instance + */ + HydratedQuickReplyButton.prototype.id = ""; + + /** + * Creates a new HydratedQuickReplyButton instance using the specified properties. + * @function create + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {proto.IHydratedQuickReplyButton=} [properties] Properties to set + * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton instance + */ + HydratedQuickReplyButton.create = function create(properties) { + return new HydratedQuickReplyButton(properties); + }; + + /** + * Encodes the specified HydratedQuickReplyButton message. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. + * @function encode + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {proto.IHydratedQuickReplyButton} message HydratedQuickReplyButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedQuickReplyButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.displayText); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.id); + return writer; + }; + + /** + * Encodes the specified HydratedQuickReplyButton message, length delimited. Does not implicitly {@link proto.HydratedQuickReplyButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {proto.IHydratedQuickReplyButton} message HydratedQuickReplyButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedQuickReplyButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a HydratedQuickReplyButton message from the specified reader or buffer. + * @function decode + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedQuickReplyButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedQuickReplyButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.displayText = reader.string(); + break; + case 2: + message.id = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a HydratedQuickReplyButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedQuickReplyButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a HydratedQuickReplyButton message. + * @function verify + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + HydratedQuickReplyButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.displayText != null && message.hasOwnProperty("displayText")) + if (!$util.isString(message.displayText)) + return "displayText: string expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isString(message.id)) + return "id: string expected"; + return null; + }; + + /** + * Creates a HydratedQuickReplyButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {Object.} object Plain object + * @returns {proto.HydratedQuickReplyButton} HydratedQuickReplyButton + */ + HydratedQuickReplyButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.HydratedQuickReplyButton) + return object; + var message = new $root.proto.HydratedQuickReplyButton(); + if (object.displayText != null) + message.displayText = String(object.displayText); + if (object.id != null) + message.id = String(object.id); + return message; + }; + + /** + * Creates a plain object from a HydratedQuickReplyButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.HydratedQuickReplyButton + * @static + * @param {proto.HydratedQuickReplyButton} message HydratedQuickReplyButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + HydratedQuickReplyButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.displayText = ""; + object.id = ""; + } + if (message.displayText != null && message.hasOwnProperty("displayText")) + object.displayText = message.displayText; + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + return object; + }; + + /** + * Converts this HydratedQuickReplyButton to JSON. + * @function toJSON + * @memberof proto.HydratedQuickReplyButton + * @instance + * @returns {Object.} JSON object + */ + HydratedQuickReplyButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return HydratedQuickReplyButton; + })(); + + proto.HydratedURLButton = (function() { + + /** + * Properties of a HydratedURLButton. + * @memberof proto + * @interface IHydratedURLButton + * @property {string|null} [displayText] HydratedURLButton displayText + * @property {string|null} [url] HydratedURLButton url + */ + + /** + * Constructs a new HydratedURLButton. + * @memberof proto + * @classdesc Represents a HydratedURLButton. + * @implements IHydratedURLButton + * @constructor + * @param {proto.IHydratedURLButton=} [properties] Properties to set + */ + function HydratedURLButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * HydratedURLButton displayText. + * @member {string} displayText + * @memberof proto.HydratedURLButton + * @instance + */ + HydratedURLButton.prototype.displayText = ""; + + /** + * HydratedURLButton url. + * @member {string} url + * @memberof proto.HydratedURLButton + * @instance + */ + HydratedURLButton.prototype.url = ""; + + /** + * Creates a new HydratedURLButton instance using the specified properties. + * @function create + * @memberof proto.HydratedURLButton + * @static + * @param {proto.IHydratedURLButton=} [properties] Properties to set + * @returns {proto.HydratedURLButton} HydratedURLButton instance + */ + HydratedURLButton.create = function create(properties) { + return new HydratedURLButton(properties); + }; + + /** + * Encodes the specified HydratedURLButton message. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. + * @function encode + * @memberof proto.HydratedURLButton + * @static + * @param {proto.IHydratedURLButton} message HydratedURLButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedURLButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.displayText); + if (message.url != null && Object.hasOwnProperty.call(message, "url")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.url); + return writer; + }; + + /** + * Encodes the specified HydratedURLButton message, length delimited. Does not implicitly {@link proto.HydratedURLButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.HydratedURLButton + * @static + * @param {proto.IHydratedURLButton} message HydratedURLButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedURLButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a HydratedURLButton message from the specified reader or buffer. + * @function decode + * @memberof proto.HydratedURLButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.HydratedURLButton} HydratedURLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedURLButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedURLButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.displayText = reader.string(); + break; + case 2: + message.url = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a HydratedURLButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.HydratedURLButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.HydratedURLButton} HydratedURLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedURLButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a HydratedURLButton message. + * @function verify + * @memberof proto.HydratedURLButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + HydratedURLButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.displayText != null && message.hasOwnProperty("displayText")) + if (!$util.isString(message.displayText)) + return "displayText: string expected"; + if (message.url != null && message.hasOwnProperty("url")) + if (!$util.isString(message.url)) + return "url: string expected"; + return null; + }; + + /** + * Creates a HydratedURLButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.HydratedURLButton + * @static + * @param {Object.} object Plain object + * @returns {proto.HydratedURLButton} HydratedURLButton + */ + HydratedURLButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.HydratedURLButton) + return object; + var message = new $root.proto.HydratedURLButton(); + if (object.displayText != null) + message.displayText = String(object.displayText); + if (object.url != null) + message.url = String(object.url); + return message; + }; + + /** + * Creates a plain object from a HydratedURLButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.HydratedURLButton + * @static + * @param {proto.HydratedURLButton} message HydratedURLButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + HydratedURLButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.displayText = ""; + object.url = ""; + } + if (message.displayText != null && message.hasOwnProperty("displayText")) + object.displayText = message.displayText; + if (message.url != null && message.hasOwnProperty("url")) + object.url = message.url; + return object; + }; + + /** + * Converts this HydratedURLButton to JSON. + * @function toJSON + * @memberof proto.HydratedURLButton + * @instance + * @returns {Object.} JSON object + */ + HydratedURLButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return HydratedURLButton; + })(); + + proto.HydratedCallButton = (function() { + + /** + * Properties of a HydratedCallButton. + * @memberof proto + * @interface IHydratedCallButton + * @property {string|null} [displayText] HydratedCallButton displayText + * @property {string|null} [phoneNumber] HydratedCallButton phoneNumber + */ + + /** + * Constructs a new HydratedCallButton. + * @memberof proto + * @classdesc Represents a HydratedCallButton. + * @implements IHydratedCallButton + * @constructor + * @param {proto.IHydratedCallButton=} [properties] Properties to set + */ + function HydratedCallButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * HydratedCallButton displayText. + * @member {string} displayText + * @memberof proto.HydratedCallButton + * @instance + */ + HydratedCallButton.prototype.displayText = ""; + + /** + * HydratedCallButton phoneNumber. + * @member {string} phoneNumber + * @memberof proto.HydratedCallButton + * @instance + */ + HydratedCallButton.prototype.phoneNumber = ""; + + /** + * Creates a new HydratedCallButton instance using the specified properties. + * @function create + * @memberof proto.HydratedCallButton + * @static + * @param {proto.IHydratedCallButton=} [properties] Properties to set + * @returns {proto.HydratedCallButton} HydratedCallButton instance + */ + HydratedCallButton.create = function create(properties) { + return new HydratedCallButton(properties); + }; + + /** + * Encodes the specified HydratedCallButton message. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. + * @function encode + * @memberof proto.HydratedCallButton + * @static + * @param {proto.IHydratedCallButton} message HydratedCallButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedCallButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.displayText); + if (message.phoneNumber != null && Object.hasOwnProperty.call(message, "phoneNumber")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.phoneNumber); + return writer; + }; + + /** + * Encodes the specified HydratedCallButton message, length delimited. Does not implicitly {@link proto.HydratedCallButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.HydratedCallButton + * @static + * @param {proto.IHydratedCallButton} message HydratedCallButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedCallButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a HydratedCallButton message from the specified reader or buffer. + * @function decode + * @memberof proto.HydratedCallButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.HydratedCallButton} HydratedCallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedCallButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedCallButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.displayText = reader.string(); + break; + case 2: + message.phoneNumber = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a HydratedCallButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.HydratedCallButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.HydratedCallButton} HydratedCallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedCallButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a HydratedCallButton message. + * @function verify + * @memberof proto.HydratedCallButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + HydratedCallButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.displayText != null && message.hasOwnProperty("displayText")) + if (!$util.isString(message.displayText)) + return "displayText: string expected"; + if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) + if (!$util.isString(message.phoneNumber)) + return "phoneNumber: string expected"; + return null; + }; + + /** + * Creates a HydratedCallButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.HydratedCallButton + * @static + * @param {Object.} object Plain object + * @returns {proto.HydratedCallButton} HydratedCallButton + */ + HydratedCallButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.HydratedCallButton) + return object; + var message = new $root.proto.HydratedCallButton(); + if (object.displayText != null) + message.displayText = String(object.displayText); + if (object.phoneNumber != null) + message.phoneNumber = String(object.phoneNumber); + return message; + }; + + /** + * Creates a plain object from a HydratedCallButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.HydratedCallButton + * @static + * @param {proto.HydratedCallButton} message HydratedCallButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + HydratedCallButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.displayText = ""; + object.phoneNumber = ""; + } + if (message.displayText != null && message.hasOwnProperty("displayText")) + object.displayText = message.displayText; + if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) + object.phoneNumber = message.phoneNumber; + return object; + }; + + /** + * Converts this HydratedCallButton to JSON. + * @function toJSON + * @memberof proto.HydratedCallButton + * @instance + * @returns {Object.} JSON object + */ + HydratedCallButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return HydratedCallButton; + })(); + + proto.HydratedTemplateButton = (function() { + + /** + * Properties of a HydratedTemplateButton. + * @memberof proto + * @interface IHydratedTemplateButton + * @property {number|null} [index] HydratedTemplateButton index + * @property {proto.IHydratedQuickReplyButton|null} [quickReplyButton] HydratedTemplateButton quickReplyButton + * @property {proto.IHydratedURLButton|null} [urlButton] HydratedTemplateButton urlButton + * @property {proto.IHydratedCallButton|null} [callButton] HydratedTemplateButton callButton + */ + + /** + * Constructs a new HydratedTemplateButton. + * @memberof proto + * @classdesc Represents a HydratedTemplateButton. + * @implements IHydratedTemplateButton + * @constructor + * @param {proto.IHydratedTemplateButton=} [properties] Properties to set + */ + function HydratedTemplateButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * HydratedTemplateButton index. + * @member {number} index + * @memberof proto.HydratedTemplateButton + * @instance + */ + HydratedTemplateButton.prototype.index = 0; + + /** + * HydratedTemplateButton quickReplyButton. + * @member {proto.IHydratedQuickReplyButton|null|undefined} quickReplyButton + * @memberof proto.HydratedTemplateButton + * @instance + */ + HydratedTemplateButton.prototype.quickReplyButton = null; + + /** + * HydratedTemplateButton urlButton. + * @member {proto.IHydratedURLButton|null|undefined} urlButton + * @memberof proto.HydratedTemplateButton + * @instance + */ + HydratedTemplateButton.prototype.urlButton = null; + + /** + * HydratedTemplateButton callButton. + * @member {proto.IHydratedCallButton|null|undefined} callButton + * @memberof proto.HydratedTemplateButton + * @instance + */ + HydratedTemplateButton.prototype.callButton = null; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * HydratedTemplateButton hydratedButton. + * @member {"quickReplyButton"|"urlButton"|"callButton"|undefined} hydratedButton + * @memberof proto.HydratedTemplateButton + * @instance + */ + Object.defineProperty(HydratedTemplateButton.prototype, "hydratedButton", { + get: $util.oneOfGetter($oneOfFields = ["quickReplyButton", "urlButton", "callButton"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new HydratedTemplateButton instance using the specified properties. + * @function create + * @memberof proto.HydratedTemplateButton + * @static + * @param {proto.IHydratedTemplateButton=} [properties] Properties to set + * @returns {proto.HydratedTemplateButton} HydratedTemplateButton instance + */ + HydratedTemplateButton.create = function create(properties) { + return new HydratedTemplateButton(properties); + }; + + /** + * Encodes the specified HydratedTemplateButton message. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. + * @function encode + * @memberof proto.HydratedTemplateButton + * @static + * @param {proto.IHydratedTemplateButton} message HydratedTemplateButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedTemplateButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.quickReplyButton != null && Object.hasOwnProperty.call(message, "quickReplyButton")) + $root.proto.HydratedQuickReplyButton.encode(message.quickReplyButton, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.urlButton != null && Object.hasOwnProperty.call(message, "urlButton")) + $root.proto.HydratedURLButton.encode(message.urlButton, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.callButton != null && Object.hasOwnProperty.call(message, "callButton")) + $root.proto.HydratedCallButton.encode(message.callButton, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + if (message.index != null && Object.hasOwnProperty.call(message, "index")) + writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.index); + return writer; + }; + + /** + * Encodes the specified HydratedTemplateButton message, length delimited. Does not implicitly {@link proto.HydratedTemplateButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.HydratedTemplateButton + * @static + * @param {proto.IHydratedTemplateButton} message HydratedTemplateButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + HydratedTemplateButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a HydratedTemplateButton message from the specified reader or buffer. + * @function decode + * @memberof proto.HydratedTemplateButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.HydratedTemplateButton} HydratedTemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedTemplateButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.HydratedTemplateButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 4: + message.index = reader.uint32(); + break; + case 1: + message.quickReplyButton = $root.proto.HydratedQuickReplyButton.decode(reader, reader.uint32()); + break; + case 2: + message.urlButton = $root.proto.HydratedURLButton.decode(reader, reader.uint32()); + break; + case 3: + message.callButton = $root.proto.HydratedCallButton.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a HydratedTemplateButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.HydratedTemplateButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.HydratedTemplateButton} HydratedTemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + HydratedTemplateButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a HydratedTemplateButton message. + * @function verify + * @memberof proto.HydratedTemplateButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + HydratedTemplateButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.index != null && message.hasOwnProperty("index")) + if (!$util.isInteger(message.index)) + return "index: integer expected"; + if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { + properties.hydratedButton = 1; + { + var error = $root.proto.HydratedQuickReplyButton.verify(message.quickReplyButton); + if (error) + return "quickReplyButton." + error; + } + } + if (message.urlButton != null && message.hasOwnProperty("urlButton")) { + if (properties.hydratedButton === 1) + return "hydratedButton: multiple values"; + properties.hydratedButton = 1; + { + var error = $root.proto.HydratedURLButton.verify(message.urlButton); + if (error) + return "urlButton." + error; + } + } + if (message.callButton != null && message.hasOwnProperty("callButton")) { + if (properties.hydratedButton === 1) + return "hydratedButton: multiple values"; + properties.hydratedButton = 1; + { + var error = $root.proto.HydratedCallButton.verify(message.callButton); + if (error) + return "callButton." + error; + } + } + return null; + }; + + /** + * Creates a HydratedTemplateButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.HydratedTemplateButton + * @static + * @param {Object.} object Plain object + * @returns {proto.HydratedTemplateButton} HydratedTemplateButton + */ + HydratedTemplateButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.HydratedTemplateButton) + return object; + var message = new $root.proto.HydratedTemplateButton(); + if (object.index != null) + message.index = object.index >>> 0; + if (object.quickReplyButton != null) { + if (typeof object.quickReplyButton !== "object") + throw TypeError(".proto.HydratedTemplateButton.quickReplyButton: object expected"); + message.quickReplyButton = $root.proto.HydratedQuickReplyButton.fromObject(object.quickReplyButton); + } + if (object.urlButton != null) { + if (typeof object.urlButton !== "object") + throw TypeError(".proto.HydratedTemplateButton.urlButton: object expected"); + message.urlButton = $root.proto.HydratedURLButton.fromObject(object.urlButton); + } + if (object.callButton != null) { + if (typeof object.callButton !== "object") + throw TypeError(".proto.HydratedTemplateButton.callButton: object expected"); + message.callButton = $root.proto.HydratedCallButton.fromObject(object.callButton); + } + return message; + }; + + /** + * Creates a plain object from a HydratedTemplateButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.HydratedTemplateButton + * @static + * @param {proto.HydratedTemplateButton} message HydratedTemplateButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + HydratedTemplateButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.index = 0; + if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { + object.quickReplyButton = $root.proto.HydratedQuickReplyButton.toObject(message.quickReplyButton, options); + if (options.oneofs) + object.hydratedButton = "quickReplyButton"; + } + if (message.urlButton != null && message.hasOwnProperty("urlButton")) { + object.urlButton = $root.proto.HydratedURLButton.toObject(message.urlButton, options); + if (options.oneofs) + object.hydratedButton = "urlButton"; + } + if (message.callButton != null && message.hasOwnProperty("callButton")) { + object.callButton = $root.proto.HydratedCallButton.toObject(message.callButton, options); + if (options.oneofs) + object.hydratedButton = "callButton"; + } + if (message.index != null && message.hasOwnProperty("index")) + object.index = message.index; + return object; + }; + + /** + * Converts this HydratedTemplateButton to JSON. + * @function toJSON + * @memberof proto.HydratedTemplateButton + * @instance + * @returns {Object.} JSON object + */ + HydratedTemplateButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return HydratedTemplateButton; + })(); + + proto.QuickReplyButton = (function() { + + /** + * Properties of a QuickReplyButton. + * @memberof proto + * @interface IQuickReplyButton + * @property {proto.IHighlyStructuredMessage|null} [displayText] QuickReplyButton displayText + * @property {string|null} [id] QuickReplyButton id + */ + + /** + * Constructs a new QuickReplyButton. + * @memberof proto + * @classdesc Represents a QuickReplyButton. + * @implements IQuickReplyButton + * @constructor + * @param {proto.IQuickReplyButton=} [properties] Properties to set + */ + function QuickReplyButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * QuickReplyButton displayText. + * @member {proto.IHighlyStructuredMessage|null|undefined} displayText + * @memberof proto.QuickReplyButton + * @instance + */ + QuickReplyButton.prototype.displayText = null; + + /** + * QuickReplyButton id. + * @member {string} id + * @memberof proto.QuickReplyButton + * @instance + */ + QuickReplyButton.prototype.id = ""; + + /** + * Creates a new QuickReplyButton instance using the specified properties. + * @function create + * @memberof proto.QuickReplyButton + * @static + * @param {proto.IQuickReplyButton=} [properties] Properties to set + * @returns {proto.QuickReplyButton} QuickReplyButton instance + */ + QuickReplyButton.create = function create(properties) { + return new QuickReplyButton(properties); + }; + + /** + * Encodes the specified QuickReplyButton message. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. + * @function encode + * @memberof proto.QuickReplyButton + * @static + * @param {proto.IQuickReplyButton} message QuickReplyButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + QuickReplyButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) + $root.proto.HighlyStructuredMessage.encode(message.displayText, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.id); + return writer; + }; + + /** + * Encodes the specified QuickReplyButton message, length delimited. Does not implicitly {@link proto.QuickReplyButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.QuickReplyButton + * @static + * @param {proto.IQuickReplyButton} message QuickReplyButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + QuickReplyButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a QuickReplyButton message from the specified reader or buffer. + * @function decode + * @memberof proto.QuickReplyButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.QuickReplyButton} QuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + QuickReplyButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.QuickReplyButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.displayText = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); + break; + case 2: + message.id = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a QuickReplyButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.QuickReplyButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.QuickReplyButton} QuickReplyButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + QuickReplyButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a QuickReplyButton message. + * @function verify + * @memberof proto.QuickReplyButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + QuickReplyButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.displayText != null && message.hasOwnProperty("displayText")) { + var error = $root.proto.HighlyStructuredMessage.verify(message.displayText); + if (error) + return "displayText." + error; + } + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isString(message.id)) + return "id: string expected"; + return null; + }; + + /** + * Creates a QuickReplyButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.QuickReplyButton + * @static + * @param {Object.} object Plain object + * @returns {proto.QuickReplyButton} QuickReplyButton + */ + QuickReplyButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.QuickReplyButton) + return object; + var message = new $root.proto.QuickReplyButton(); + if (object.displayText != null) { + if (typeof object.displayText !== "object") + throw TypeError(".proto.QuickReplyButton.displayText: object expected"); + message.displayText = $root.proto.HighlyStructuredMessage.fromObject(object.displayText); + } + if (object.id != null) + message.id = String(object.id); + return message; + }; + + /** + * Creates a plain object from a QuickReplyButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.QuickReplyButton + * @static + * @param {proto.QuickReplyButton} message QuickReplyButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + QuickReplyButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.displayText = null; + object.id = ""; + } + if (message.displayText != null && message.hasOwnProperty("displayText")) + object.displayText = $root.proto.HighlyStructuredMessage.toObject(message.displayText, options); + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + return object; + }; + + /** + * Converts this QuickReplyButton to JSON. + * @function toJSON + * @memberof proto.QuickReplyButton + * @instance + * @returns {Object.} JSON object + */ + QuickReplyButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return QuickReplyButton; + })(); + + proto.URLButton = (function() { + + /** + * Properties of a URLButton. + * @memberof proto + * @interface IURLButton + * @property {proto.IHighlyStructuredMessage|null} [displayText] URLButton displayText + * @property {proto.IHighlyStructuredMessage|null} [url] URLButton url + */ + + /** + * Constructs a new URLButton. + * @memberof proto + * @classdesc Represents a URLButton. + * @implements IURLButton + * @constructor + * @param {proto.IURLButton=} [properties] Properties to set + */ + function URLButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * URLButton displayText. + * @member {proto.IHighlyStructuredMessage|null|undefined} displayText + * @memberof proto.URLButton + * @instance + */ + URLButton.prototype.displayText = null; + + /** + * URLButton url. + * @member {proto.IHighlyStructuredMessage|null|undefined} url + * @memberof proto.URLButton + * @instance + */ + URLButton.prototype.url = null; + + /** + * Creates a new URLButton instance using the specified properties. + * @function create + * @memberof proto.URLButton + * @static + * @param {proto.IURLButton=} [properties] Properties to set + * @returns {proto.URLButton} URLButton instance + */ + URLButton.create = function create(properties) { + return new URLButton(properties); + }; + + /** + * Encodes the specified URLButton message. Does not implicitly {@link proto.URLButton.verify|verify} messages. + * @function encode + * @memberof proto.URLButton + * @static + * @param {proto.IURLButton} message URLButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + URLButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) + $root.proto.HighlyStructuredMessage.encode(message.displayText, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.url != null && Object.hasOwnProperty.call(message, "url")) + $root.proto.HighlyStructuredMessage.encode(message.url, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified URLButton message, length delimited. Does not implicitly {@link proto.URLButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.URLButton + * @static + * @param {proto.IURLButton} message URLButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + URLButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a URLButton message from the specified reader or buffer. + * @function decode + * @memberof proto.URLButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.URLButton} URLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + URLButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.URLButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.displayText = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); + break; + case 2: + message.url = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a URLButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.URLButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.URLButton} URLButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + URLButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a URLButton message. + * @function verify + * @memberof proto.URLButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + URLButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.displayText != null && message.hasOwnProperty("displayText")) { + var error = $root.proto.HighlyStructuredMessage.verify(message.displayText); + if (error) + return "displayText." + error; + } + if (message.url != null && message.hasOwnProperty("url")) { + var error = $root.proto.HighlyStructuredMessage.verify(message.url); + if (error) + return "url." + error; + } + return null; + }; + + /** + * Creates a URLButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.URLButton + * @static + * @param {Object.} object Plain object + * @returns {proto.URLButton} URLButton + */ + URLButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.URLButton) + return object; + var message = new $root.proto.URLButton(); + if (object.displayText != null) { + if (typeof object.displayText !== "object") + throw TypeError(".proto.URLButton.displayText: object expected"); + message.displayText = $root.proto.HighlyStructuredMessage.fromObject(object.displayText); + } + if (object.url != null) { + if (typeof object.url !== "object") + throw TypeError(".proto.URLButton.url: object expected"); + message.url = $root.proto.HighlyStructuredMessage.fromObject(object.url); + } + return message; + }; + + /** + * Creates a plain object from a URLButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.URLButton + * @static + * @param {proto.URLButton} message URLButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + URLButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.displayText = null; + object.url = null; + } + if (message.displayText != null && message.hasOwnProperty("displayText")) + object.displayText = $root.proto.HighlyStructuredMessage.toObject(message.displayText, options); + if (message.url != null && message.hasOwnProperty("url")) + object.url = $root.proto.HighlyStructuredMessage.toObject(message.url, options); + return object; + }; + + /** + * Converts this URLButton to JSON. + * @function toJSON + * @memberof proto.URLButton + * @instance + * @returns {Object.} JSON object + */ + URLButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return URLButton; + })(); + + proto.CallButton = (function() { + + /** + * Properties of a CallButton. + * @memberof proto + * @interface ICallButton + * @property {proto.IHighlyStructuredMessage|null} [displayText] CallButton displayText + * @property {proto.IHighlyStructuredMessage|null} [phoneNumber] CallButton phoneNumber + */ + + /** + * Constructs a new CallButton. + * @memberof proto + * @classdesc Represents a CallButton. + * @implements ICallButton + * @constructor + * @param {proto.ICallButton=} [properties] Properties to set + */ + function CallButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * CallButton displayText. + * @member {proto.IHighlyStructuredMessage|null|undefined} displayText + * @memberof proto.CallButton + * @instance + */ + CallButton.prototype.displayText = null; + + /** + * CallButton phoneNumber. + * @member {proto.IHighlyStructuredMessage|null|undefined} phoneNumber + * @memberof proto.CallButton + * @instance + */ + CallButton.prototype.phoneNumber = null; + + /** + * Creates a new CallButton instance using the specified properties. + * @function create + * @memberof proto.CallButton + * @static + * @param {proto.ICallButton=} [properties] Properties to set + * @returns {proto.CallButton} CallButton instance + */ + CallButton.create = function create(properties) { + return new CallButton(properties); + }; + + /** + * Encodes the specified CallButton message. Does not implicitly {@link proto.CallButton.verify|verify} messages. + * @function encode + * @memberof proto.CallButton + * @static + * @param {proto.ICallButton} message CallButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CallButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.displayText != null && Object.hasOwnProperty.call(message, "displayText")) + $root.proto.HighlyStructuredMessage.encode(message.displayText, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.phoneNumber != null && Object.hasOwnProperty.call(message, "phoneNumber")) + $root.proto.HighlyStructuredMessage.encode(message.phoneNumber, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified CallButton message, length delimited. Does not implicitly {@link proto.CallButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.CallButton + * @static + * @param {proto.ICallButton} message CallButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CallButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a CallButton message from the specified reader or buffer. + * @function decode + * @memberof proto.CallButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.CallButton} CallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CallButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.CallButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.displayText = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); + break; + case 2: + message.phoneNumber = $root.proto.HighlyStructuredMessage.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a CallButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.CallButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.CallButton} CallButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CallButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a CallButton message. + * @function verify + * @memberof proto.CallButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + CallButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.displayText != null && message.hasOwnProperty("displayText")) { + var error = $root.proto.HighlyStructuredMessage.verify(message.displayText); + if (error) + return "displayText." + error; + } + if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) { + var error = $root.proto.HighlyStructuredMessage.verify(message.phoneNumber); + if (error) + return "phoneNumber." + error; + } + return null; + }; + + /** + * Creates a CallButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.CallButton + * @static + * @param {Object.} object Plain object + * @returns {proto.CallButton} CallButton + */ + CallButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.CallButton) + return object; + var message = new $root.proto.CallButton(); + if (object.displayText != null) { + if (typeof object.displayText !== "object") + throw TypeError(".proto.CallButton.displayText: object expected"); + message.displayText = $root.proto.HighlyStructuredMessage.fromObject(object.displayText); + } + if (object.phoneNumber != null) { + if (typeof object.phoneNumber !== "object") + throw TypeError(".proto.CallButton.phoneNumber: object expected"); + message.phoneNumber = $root.proto.HighlyStructuredMessage.fromObject(object.phoneNumber); + } + return message; + }; + + /** + * Creates a plain object from a CallButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.CallButton + * @static + * @param {proto.CallButton} message CallButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + CallButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.displayText = null; + object.phoneNumber = null; + } + if (message.displayText != null && message.hasOwnProperty("displayText")) + object.displayText = $root.proto.HighlyStructuredMessage.toObject(message.displayText, options); + if (message.phoneNumber != null && message.hasOwnProperty("phoneNumber")) + object.phoneNumber = $root.proto.HighlyStructuredMessage.toObject(message.phoneNumber, options); + return object; + }; + + /** + * Converts this CallButton to JSON. + * @function toJSON + * @memberof proto.CallButton + * @instance + * @returns {Object.} JSON object + */ + CallButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return CallButton; + })(); + + proto.TemplateButton = (function() { + + /** + * Properties of a TemplateButton. + * @memberof proto + * @interface ITemplateButton + * @property {number|null} [index] TemplateButton index + * @property {proto.IQuickReplyButton|null} [quickReplyButton] TemplateButton quickReplyButton + * @property {proto.IURLButton|null} [urlButton] TemplateButton urlButton + * @property {proto.ICallButton|null} [callButton] TemplateButton callButton + */ + + /** + * Constructs a new TemplateButton. + * @memberof proto + * @classdesc Represents a TemplateButton. + * @implements ITemplateButton + * @constructor + * @param {proto.ITemplateButton=} [properties] Properties to set + */ + function TemplateButton(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * TemplateButton index. + * @member {number} index + * @memberof proto.TemplateButton + * @instance + */ + TemplateButton.prototype.index = 0; + + /** + * TemplateButton quickReplyButton. + * @member {proto.IQuickReplyButton|null|undefined} quickReplyButton + * @memberof proto.TemplateButton + * @instance + */ + TemplateButton.prototype.quickReplyButton = null; + + /** + * TemplateButton urlButton. + * @member {proto.IURLButton|null|undefined} urlButton + * @memberof proto.TemplateButton + * @instance + */ + TemplateButton.prototype.urlButton = null; + + /** + * TemplateButton callButton. + * @member {proto.ICallButton|null|undefined} callButton + * @memberof proto.TemplateButton + * @instance + */ + TemplateButton.prototype.callButton = null; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * TemplateButton button. + * @member {"quickReplyButton"|"urlButton"|"callButton"|undefined} button + * @memberof proto.TemplateButton + * @instance + */ + Object.defineProperty(TemplateButton.prototype, "button", { + get: $util.oneOfGetter($oneOfFields = ["quickReplyButton", "urlButton", "callButton"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new TemplateButton instance using the specified properties. + * @function create + * @memberof proto.TemplateButton + * @static + * @param {proto.ITemplateButton=} [properties] Properties to set + * @returns {proto.TemplateButton} TemplateButton instance + */ + TemplateButton.create = function create(properties) { + return new TemplateButton(properties); + }; + + /** + * Encodes the specified TemplateButton message. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. + * @function encode + * @memberof proto.TemplateButton + * @static + * @param {proto.ITemplateButton} message TemplateButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + TemplateButton.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.quickReplyButton != null && Object.hasOwnProperty.call(message, "quickReplyButton")) + $root.proto.QuickReplyButton.encode(message.quickReplyButton, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.urlButton != null && Object.hasOwnProperty.call(message, "urlButton")) + $root.proto.URLButton.encode(message.urlButton, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.callButton != null && Object.hasOwnProperty.call(message, "callButton")) + $root.proto.CallButton.encode(message.callButton, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + if (message.index != null && Object.hasOwnProperty.call(message, "index")) + writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.index); + return writer; + }; + + /** + * Encodes the specified TemplateButton message, length delimited. Does not implicitly {@link proto.TemplateButton.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.TemplateButton + * @static + * @param {proto.ITemplateButton} message TemplateButton message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + TemplateButton.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a TemplateButton message from the specified reader or buffer. + * @function decode + * @memberof proto.TemplateButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.TemplateButton} TemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + TemplateButton.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.TemplateButton(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 4: + message.index = reader.uint32(); + break; + case 1: + message.quickReplyButton = $root.proto.QuickReplyButton.decode(reader, reader.uint32()); + break; + case 2: + message.urlButton = $root.proto.URLButton.decode(reader, reader.uint32()); + break; + case 3: + message.callButton = $root.proto.CallButton.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a TemplateButton message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.TemplateButton + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.TemplateButton} TemplateButton + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + TemplateButton.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a TemplateButton message. + * @function verify + * @memberof proto.TemplateButton + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + TemplateButton.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.index != null && message.hasOwnProperty("index")) + if (!$util.isInteger(message.index)) + return "index: integer expected"; + if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { + properties.button = 1; + { + var error = $root.proto.QuickReplyButton.verify(message.quickReplyButton); + if (error) + return "quickReplyButton." + error; + } + } + if (message.urlButton != null && message.hasOwnProperty("urlButton")) { + if (properties.button === 1) + return "button: multiple values"; + properties.button = 1; + { + var error = $root.proto.URLButton.verify(message.urlButton); + if (error) + return "urlButton." + error; + } + } + if (message.callButton != null && message.hasOwnProperty("callButton")) { + if (properties.button === 1) + return "button: multiple values"; + properties.button = 1; + { + var error = $root.proto.CallButton.verify(message.callButton); + if (error) + return "callButton." + error; + } + } + return null; + }; + + /** + * Creates a TemplateButton message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.TemplateButton + * @static + * @param {Object.} object Plain object + * @returns {proto.TemplateButton} TemplateButton + */ + TemplateButton.fromObject = function fromObject(object) { + if (object instanceof $root.proto.TemplateButton) + return object; + var message = new $root.proto.TemplateButton(); + if (object.index != null) + message.index = object.index >>> 0; + if (object.quickReplyButton != null) { + if (typeof object.quickReplyButton !== "object") + throw TypeError(".proto.TemplateButton.quickReplyButton: object expected"); + message.quickReplyButton = $root.proto.QuickReplyButton.fromObject(object.quickReplyButton); + } + if (object.urlButton != null) { + if (typeof object.urlButton !== "object") + throw TypeError(".proto.TemplateButton.urlButton: object expected"); + message.urlButton = $root.proto.URLButton.fromObject(object.urlButton); + } + if (object.callButton != null) { + if (typeof object.callButton !== "object") + throw TypeError(".proto.TemplateButton.callButton: object expected"); + message.callButton = $root.proto.CallButton.fromObject(object.callButton); + } + return message; + }; + + /** + * Creates a plain object from a TemplateButton message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.TemplateButton + * @static + * @param {proto.TemplateButton} message TemplateButton + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + TemplateButton.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.index = 0; + if (message.quickReplyButton != null && message.hasOwnProperty("quickReplyButton")) { + object.quickReplyButton = $root.proto.QuickReplyButton.toObject(message.quickReplyButton, options); + if (options.oneofs) + object.button = "quickReplyButton"; + } + if (message.urlButton != null && message.hasOwnProperty("urlButton")) { + object.urlButton = $root.proto.URLButton.toObject(message.urlButton, options); + if (options.oneofs) + object.button = "urlButton"; + } + if (message.callButton != null && message.hasOwnProperty("callButton")) { + object.callButton = $root.proto.CallButton.toObject(message.callButton, options); + if (options.oneofs) + object.button = "callButton"; + } + if (message.index != null && message.hasOwnProperty("index")) + object.index = message.index; + return object; + }; + + /** + * Converts this TemplateButton to JSON. + * @function toJSON + * @memberof proto.TemplateButton + * @instance + * @returns {Object.} JSON object + */ + TemplateButton.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return TemplateButton; + })(); + + proto.Location = (function() { + + /** + * Properties of a Location. + * @memberof proto + * @interface ILocation + * @property {number|null} [degreesLatitude] Location degreesLatitude + * @property {number|null} [degreesLongitude] Location degreesLongitude + * @property {string|null} [name] Location name + */ + + /** + * Constructs a new Location. + * @memberof proto + * @classdesc Represents a Location. + * @implements ILocation + * @constructor + * @param {proto.ILocation=} [properties] Properties to set + */ + function Location(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Location degreesLatitude. + * @member {number} degreesLatitude + * @memberof proto.Location + * @instance + */ + Location.prototype.degreesLatitude = 0; + + /** + * Location degreesLongitude. + * @member {number} degreesLongitude + * @memberof proto.Location + * @instance + */ + Location.prototype.degreesLongitude = 0; + + /** + * Location name. + * @member {string} name + * @memberof proto.Location + * @instance + */ + Location.prototype.name = ""; + + /** + * Creates a new Location instance using the specified properties. + * @function create + * @memberof proto.Location + * @static + * @param {proto.ILocation=} [properties] Properties to set + * @returns {proto.Location} Location instance + */ + Location.create = function create(properties) { + return new Location(properties); + }; + + /** + * Encodes the specified Location message. Does not implicitly {@link proto.Location.verify|verify} messages. + * @function encode + * @memberof proto.Location + * @static + * @param {proto.ILocation} message Location message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Location.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.degreesLatitude != null && Object.hasOwnProperty.call(message, "degreesLatitude")) + writer.uint32(/* id 1, wireType 1 =*/9).double(message.degreesLatitude); + if (message.degreesLongitude != null && Object.hasOwnProperty.call(message, "degreesLongitude")) + writer.uint32(/* id 2, wireType 1 =*/17).double(message.degreesLongitude); + if (message.name != null && Object.hasOwnProperty.call(message, "name")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.name); + return writer; + }; + + /** + * Encodes the specified Location message, length delimited. Does not implicitly {@link proto.Location.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Location + * @static + * @param {proto.ILocation} message Location message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Location.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Location message from the specified reader or buffer. + * @function decode + * @memberof proto.Location + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Location} Location + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Location.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Location(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.degreesLatitude = reader.double(); + break; + case 2: + message.degreesLongitude = reader.double(); + break; + case 3: + message.name = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Location message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Location + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Location} Location + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Location.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Location message. + * @function verify + * @memberof proto.Location + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Location.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.degreesLatitude != null && message.hasOwnProperty("degreesLatitude")) + if (typeof message.degreesLatitude !== "number") + return "degreesLatitude: number expected"; + if (message.degreesLongitude != null && message.hasOwnProperty("degreesLongitude")) + if (typeof message.degreesLongitude !== "number") + return "degreesLongitude: number expected"; + if (message.name != null && message.hasOwnProperty("name")) + if (!$util.isString(message.name)) + return "name: string expected"; + return null; + }; + + /** + * Creates a Location message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Location + * @static + * @param {Object.} object Plain object + * @returns {proto.Location} Location + */ + Location.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Location) + return object; + var message = new $root.proto.Location(); + if (object.degreesLatitude != null) + message.degreesLatitude = Number(object.degreesLatitude); + if (object.degreesLongitude != null) + message.degreesLongitude = Number(object.degreesLongitude); + if (object.name != null) + message.name = String(object.name); + return message; + }; + + /** + * Creates a plain object from a Location message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Location + * @static + * @param {proto.Location} message Location + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Location.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.degreesLatitude = 0; + object.degreesLongitude = 0; + object.name = ""; + } + if (message.degreesLatitude != null && message.hasOwnProperty("degreesLatitude")) + object.degreesLatitude = options.json && !isFinite(message.degreesLatitude) ? String(message.degreesLatitude) : message.degreesLatitude; + if (message.degreesLongitude != null && message.hasOwnProperty("degreesLongitude")) + object.degreesLongitude = options.json && !isFinite(message.degreesLongitude) ? String(message.degreesLongitude) : message.degreesLongitude; + if (message.name != null && message.hasOwnProperty("name")) + object.name = message.name; + return object; + }; + + /** + * Converts this Location to JSON. + * @function toJSON + * @memberof proto.Location + * @instance + * @returns {Object.} JSON object + */ + Location.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Location; + })(); + + proto.Point = (function() { + + /** + * Properties of a Point. + * @memberof proto + * @interface IPoint + * @property {number|null} [xDeprecated] Point xDeprecated + * @property {number|null} [yDeprecated] Point yDeprecated + * @property {number|null} [x] Point x + * @property {number|null} [y] Point y + */ + + /** + * Constructs a new Point. + * @memberof proto + * @classdesc Represents a Point. + * @implements IPoint + * @constructor + * @param {proto.IPoint=} [properties] Properties to set + */ + function Point(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Point xDeprecated. + * @member {number} xDeprecated + * @memberof proto.Point + * @instance + */ + Point.prototype.xDeprecated = 0; + + /** + * Point yDeprecated. + * @member {number} yDeprecated + * @memberof proto.Point + * @instance + */ + Point.prototype.yDeprecated = 0; + + /** + * Point x. + * @member {number} x + * @memberof proto.Point + * @instance + */ + Point.prototype.x = 0; + + /** + * Point y. + * @member {number} y + * @memberof proto.Point + * @instance + */ + Point.prototype.y = 0; + + /** + * Creates a new Point instance using the specified properties. + * @function create + * @memberof proto.Point + * @static + * @param {proto.IPoint=} [properties] Properties to set + * @returns {proto.Point} Point instance + */ + Point.create = function create(properties) { + return new Point(properties); + }; + + /** + * Encodes the specified Point message. Does not implicitly {@link proto.Point.verify|verify} messages. + * @function encode + * @memberof proto.Point + * @static + * @param {proto.IPoint} message Point message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Point.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.xDeprecated != null && Object.hasOwnProperty.call(message, "xDeprecated")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.xDeprecated); + if (message.yDeprecated != null && Object.hasOwnProperty.call(message, "yDeprecated")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.yDeprecated); + if (message.x != null && Object.hasOwnProperty.call(message, "x")) + writer.uint32(/* id 3, wireType 1 =*/25).double(message.x); + if (message.y != null && Object.hasOwnProperty.call(message, "y")) + writer.uint32(/* id 4, wireType 1 =*/33).double(message.y); + return writer; + }; + + /** + * Encodes the specified Point message, length delimited. Does not implicitly {@link proto.Point.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Point + * @static + * @param {proto.IPoint} message Point message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Point.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Point message from the specified reader or buffer. + * @function decode + * @memberof proto.Point + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Point} Point + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Point.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Point(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.xDeprecated = reader.int32(); + break; + case 2: + message.yDeprecated = reader.int32(); + break; + case 3: + message.x = reader.double(); + break; + case 4: + message.y = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Point message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Point + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Point} Point + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Point.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Point message. + * @function verify + * @memberof proto.Point + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Point.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.xDeprecated != null && message.hasOwnProperty("xDeprecated")) + if (!$util.isInteger(message.xDeprecated)) + return "xDeprecated: integer expected"; + if (message.yDeprecated != null && message.hasOwnProperty("yDeprecated")) + if (!$util.isInteger(message.yDeprecated)) + return "yDeprecated: integer expected"; + if (message.x != null && message.hasOwnProperty("x")) + if (typeof message.x !== "number") + return "x: number expected"; + if (message.y != null && message.hasOwnProperty("y")) + if (typeof message.y !== "number") + return "y: number expected"; + return null; + }; + + /** + * Creates a Point message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Point + * @static + * @param {Object.} object Plain object + * @returns {proto.Point} Point + */ + Point.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Point) + return object; + var message = new $root.proto.Point(); + if (object.xDeprecated != null) + message.xDeprecated = object.xDeprecated | 0; + if (object.yDeprecated != null) + message.yDeprecated = object.yDeprecated | 0; + if (object.x != null) + message.x = Number(object.x); + if (object.y != null) + message.y = Number(object.y); + return message; + }; + + /** + * Creates a plain object from a Point message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Point + * @static + * @param {proto.Point} message Point + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Point.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.xDeprecated = 0; + object.yDeprecated = 0; + object.x = 0; + object.y = 0; + } + if (message.xDeprecated != null && message.hasOwnProperty("xDeprecated")) + object.xDeprecated = message.xDeprecated; + if (message.yDeprecated != null && message.hasOwnProperty("yDeprecated")) + object.yDeprecated = message.yDeprecated; + if (message.x != null && message.hasOwnProperty("x")) + object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; + if (message.y != null && message.hasOwnProperty("y")) + object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; + return object; + }; + + /** + * Converts this Point to JSON. + * @function toJSON + * @memberof proto.Point + * @instance + * @returns {Object.} JSON object + */ + Point.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Point; + })(); + proto.CompanionProps = (function() { /** @@ -45863,6 +52250,675 @@ $root.proto = (function() { return MessageKey; })(); + proto.Reaction = (function() { + + /** + * Properties of a Reaction. + * @memberof proto + * @interface IReaction + * @property {proto.IMessageKey|null} [key] Reaction key + * @property {string|null} [text] Reaction text + * @property {string|null} [groupingKey] Reaction groupingKey + * @property {number|Long|null} [senderTimestampMs] Reaction senderTimestampMs + * @property {boolean|null} [unread] Reaction unread + */ + + /** + * Constructs a new Reaction. + * @memberof proto + * @classdesc Represents a Reaction. + * @implements IReaction + * @constructor + * @param {proto.IReaction=} [properties] Properties to set + */ + function Reaction(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Reaction key. + * @member {proto.IMessageKey|null|undefined} key + * @memberof proto.Reaction + * @instance + */ + Reaction.prototype.key = null; + + /** + * Reaction text. + * @member {string} text + * @memberof proto.Reaction + * @instance + */ + Reaction.prototype.text = ""; + + /** + * Reaction groupingKey. + * @member {string} groupingKey + * @memberof proto.Reaction + * @instance + */ + Reaction.prototype.groupingKey = ""; + + /** + * Reaction senderTimestampMs. + * @member {number|Long} senderTimestampMs + * @memberof proto.Reaction + * @instance + */ + Reaction.prototype.senderTimestampMs = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Reaction unread. + * @member {boolean} unread + * @memberof proto.Reaction + * @instance + */ + Reaction.prototype.unread = false; + + /** + * Creates a new Reaction instance using the specified properties. + * @function create + * @memberof proto.Reaction + * @static + * @param {proto.IReaction=} [properties] Properties to set + * @returns {proto.Reaction} Reaction instance + */ + Reaction.create = function create(properties) { + return new Reaction(properties); + }; + + /** + * Encodes the specified Reaction message. Does not implicitly {@link proto.Reaction.verify|verify} messages. + * @function encode + * @memberof proto.Reaction + * @static + * @param {proto.IReaction} message Reaction message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Reaction.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.key != null && Object.hasOwnProperty.call(message, "key")) + $root.proto.MessageKey.encode(message.key, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.text != null && Object.hasOwnProperty.call(message, "text")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.text); + if (message.groupingKey != null && Object.hasOwnProperty.call(message, "groupingKey")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.groupingKey); + if (message.senderTimestampMs != null && Object.hasOwnProperty.call(message, "senderTimestampMs")) + writer.uint32(/* id 4, wireType 0 =*/32).int64(message.senderTimestampMs); + if (message.unread != null && Object.hasOwnProperty.call(message, "unread")) + writer.uint32(/* id 5, wireType 0 =*/40).bool(message.unread); + return writer; + }; + + /** + * Encodes the specified Reaction message, length delimited. Does not implicitly {@link proto.Reaction.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.Reaction + * @static + * @param {proto.IReaction} message Reaction message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Reaction.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Reaction message from the specified reader or buffer. + * @function decode + * @memberof proto.Reaction + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.Reaction} Reaction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Reaction.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.Reaction(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = $root.proto.MessageKey.decode(reader, reader.uint32()); + break; + case 2: + message.text = reader.string(); + break; + case 3: + message.groupingKey = reader.string(); + break; + case 4: + message.senderTimestampMs = reader.int64(); + break; + case 5: + message.unread = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Reaction message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.Reaction + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.Reaction} Reaction + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Reaction.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Reaction message. + * @function verify + * @memberof proto.Reaction + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Reaction.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.key != null && message.hasOwnProperty("key")) { + var error = $root.proto.MessageKey.verify(message.key); + if (error) + return "key." + error; + } + if (message.text != null && message.hasOwnProperty("text")) + if (!$util.isString(message.text)) + return "text: string expected"; + if (message.groupingKey != null && message.hasOwnProperty("groupingKey")) + if (!$util.isString(message.groupingKey)) + return "groupingKey: string expected"; + if (message.senderTimestampMs != null && message.hasOwnProperty("senderTimestampMs")) + if (!$util.isInteger(message.senderTimestampMs) && !(message.senderTimestampMs && $util.isInteger(message.senderTimestampMs.low) && $util.isInteger(message.senderTimestampMs.high))) + return "senderTimestampMs: integer|Long expected"; + if (message.unread != null && message.hasOwnProperty("unread")) + if (typeof message.unread !== "boolean") + return "unread: boolean expected"; + return null; + }; + + /** + * Creates a Reaction message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.Reaction + * @static + * @param {Object.} object Plain object + * @returns {proto.Reaction} Reaction + */ + Reaction.fromObject = function fromObject(object) { + if (object instanceof $root.proto.Reaction) + return object; + var message = new $root.proto.Reaction(); + if (object.key != null) { + if (typeof object.key !== "object") + throw TypeError(".proto.Reaction.key: object expected"); + message.key = $root.proto.MessageKey.fromObject(object.key); + } + if (object.text != null) + message.text = String(object.text); + if (object.groupingKey != null) + message.groupingKey = String(object.groupingKey); + if (object.senderTimestampMs != null) + if ($util.Long) + (message.senderTimestampMs = $util.Long.fromValue(object.senderTimestampMs)).unsigned = false; + else if (typeof object.senderTimestampMs === "string") + message.senderTimestampMs = parseInt(object.senderTimestampMs, 10); + else if (typeof object.senderTimestampMs === "number") + message.senderTimestampMs = object.senderTimestampMs; + else if (typeof object.senderTimestampMs === "object") + message.senderTimestampMs = new $util.LongBits(object.senderTimestampMs.low >>> 0, object.senderTimestampMs.high >>> 0).toNumber(); + if (object.unread != null) + message.unread = Boolean(object.unread); + return message; + }; + + /** + * Creates a plain object from a Reaction message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.Reaction + * @static + * @param {proto.Reaction} message Reaction + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Reaction.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.key = null; + object.text = ""; + object.groupingKey = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.senderTimestampMs = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.senderTimestampMs = options.longs === String ? "0" : 0; + object.unread = false; + } + if (message.key != null && message.hasOwnProperty("key")) + object.key = $root.proto.MessageKey.toObject(message.key, options); + if (message.text != null && message.hasOwnProperty("text")) + object.text = message.text; + if (message.groupingKey != null && message.hasOwnProperty("groupingKey")) + object.groupingKey = message.groupingKey; + if (message.senderTimestampMs != null && message.hasOwnProperty("senderTimestampMs")) + if (typeof message.senderTimestampMs === "number") + object.senderTimestampMs = options.longs === String ? String(message.senderTimestampMs) : message.senderTimestampMs; + else + object.senderTimestampMs = options.longs === String ? $util.Long.prototype.toString.call(message.senderTimestampMs) : options.longs === Number ? new $util.LongBits(message.senderTimestampMs.low >>> 0, message.senderTimestampMs.high >>> 0).toNumber() : message.senderTimestampMs; + if (message.unread != null && message.hasOwnProperty("unread")) + object.unread = message.unread; + return object; + }; + + /** + * Converts this Reaction to JSON. + * @function toJSON + * @memberof proto.Reaction + * @instance + * @returns {Object.} JSON object + */ + Reaction.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Reaction; + })(); + + proto.UserReceipt = (function() { + + /** + * Properties of a UserReceipt. + * @memberof proto + * @interface IUserReceipt + * @property {string} userJid UserReceipt userJid + * @property {number|Long|null} [receiptTimestamp] UserReceipt receiptTimestamp + * @property {number|Long|null} [readTimestamp] UserReceipt readTimestamp + * @property {number|Long|null} [playedTimestamp] UserReceipt playedTimestamp + * @property {Array.|null} [pendingDeviceJid] UserReceipt pendingDeviceJid + * @property {Array.|null} [deliveredDeviceJid] UserReceipt deliveredDeviceJid + */ + + /** + * Constructs a new UserReceipt. + * @memberof proto + * @classdesc Represents a UserReceipt. + * @implements IUserReceipt + * @constructor + * @param {proto.IUserReceipt=} [properties] Properties to set + */ + function UserReceipt(properties) { + this.pendingDeviceJid = []; + this.deliveredDeviceJid = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * UserReceipt userJid. + * @member {string} userJid + * @memberof proto.UserReceipt + * @instance + */ + UserReceipt.prototype.userJid = ""; + + /** + * UserReceipt receiptTimestamp. + * @member {number|Long} receiptTimestamp + * @memberof proto.UserReceipt + * @instance + */ + UserReceipt.prototype.receiptTimestamp = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * UserReceipt readTimestamp. + * @member {number|Long} readTimestamp + * @memberof proto.UserReceipt + * @instance + */ + UserReceipt.prototype.readTimestamp = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * UserReceipt playedTimestamp. + * @member {number|Long} playedTimestamp + * @memberof proto.UserReceipt + * @instance + */ + UserReceipt.prototype.playedTimestamp = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * UserReceipt pendingDeviceJid. + * @member {Array.} pendingDeviceJid + * @memberof proto.UserReceipt + * @instance + */ + UserReceipt.prototype.pendingDeviceJid = $util.emptyArray; + + /** + * UserReceipt deliveredDeviceJid. + * @member {Array.} deliveredDeviceJid + * @memberof proto.UserReceipt + * @instance + */ + UserReceipt.prototype.deliveredDeviceJid = $util.emptyArray; + + /** + * Creates a new UserReceipt instance using the specified properties. + * @function create + * @memberof proto.UserReceipt + * @static + * @param {proto.IUserReceipt=} [properties] Properties to set + * @returns {proto.UserReceipt} UserReceipt instance + */ + UserReceipt.create = function create(properties) { + return new UserReceipt(properties); + }; + + /** + * Encodes the specified UserReceipt message. Does not implicitly {@link proto.UserReceipt.verify|verify} messages. + * @function encode + * @memberof proto.UserReceipt + * @static + * @param {proto.IUserReceipt} message UserReceipt message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + UserReceipt.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + writer.uint32(/* id 1, wireType 2 =*/10).string(message.userJid); + if (message.receiptTimestamp != null && Object.hasOwnProperty.call(message, "receiptTimestamp")) + writer.uint32(/* id 2, wireType 0 =*/16).int64(message.receiptTimestamp); + if (message.readTimestamp != null && Object.hasOwnProperty.call(message, "readTimestamp")) + writer.uint32(/* id 3, wireType 0 =*/24).int64(message.readTimestamp); + if (message.playedTimestamp != null && Object.hasOwnProperty.call(message, "playedTimestamp")) + writer.uint32(/* id 4, wireType 0 =*/32).int64(message.playedTimestamp); + if (message.pendingDeviceJid != null && message.pendingDeviceJid.length) + for (var i = 0; i < message.pendingDeviceJid.length; ++i) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.pendingDeviceJid[i]); + if (message.deliveredDeviceJid != null && message.deliveredDeviceJid.length) + for (var i = 0; i < message.deliveredDeviceJid.length; ++i) + writer.uint32(/* id 6, wireType 2 =*/50).string(message.deliveredDeviceJid[i]); + return writer; + }; + + /** + * Encodes the specified UserReceipt message, length delimited. Does not implicitly {@link proto.UserReceipt.verify|verify} messages. + * @function encodeDelimited + * @memberof proto.UserReceipt + * @static + * @param {proto.IUserReceipt} message UserReceipt message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + UserReceipt.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a UserReceipt message from the specified reader or buffer. + * @function decode + * @memberof proto.UserReceipt + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {proto.UserReceipt} UserReceipt + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + UserReceipt.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.UserReceipt(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.userJid = reader.string(); + break; + case 2: + message.receiptTimestamp = reader.int64(); + break; + case 3: + message.readTimestamp = reader.int64(); + break; + case 4: + message.playedTimestamp = reader.int64(); + break; + case 5: + if (!(message.pendingDeviceJid && message.pendingDeviceJid.length)) + message.pendingDeviceJid = []; + message.pendingDeviceJid.push(reader.string()); + break; + case 6: + if (!(message.deliveredDeviceJid && message.deliveredDeviceJid.length)) + message.deliveredDeviceJid = []; + message.deliveredDeviceJid.push(reader.string()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + if (!message.hasOwnProperty("userJid")) + throw $util.ProtocolError("missing required 'userJid'", { instance: message }); + return message; + }; + + /** + * Decodes a UserReceipt message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof proto.UserReceipt + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {proto.UserReceipt} UserReceipt + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + UserReceipt.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a UserReceipt message. + * @function verify + * @memberof proto.UserReceipt + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + UserReceipt.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.userJid)) + return "userJid: string expected"; + if (message.receiptTimestamp != null && message.hasOwnProperty("receiptTimestamp")) + if (!$util.isInteger(message.receiptTimestamp) && !(message.receiptTimestamp && $util.isInteger(message.receiptTimestamp.low) && $util.isInteger(message.receiptTimestamp.high))) + return "receiptTimestamp: integer|Long expected"; + if (message.readTimestamp != null && message.hasOwnProperty("readTimestamp")) + if (!$util.isInteger(message.readTimestamp) && !(message.readTimestamp && $util.isInteger(message.readTimestamp.low) && $util.isInteger(message.readTimestamp.high))) + return "readTimestamp: integer|Long expected"; + if (message.playedTimestamp != null && message.hasOwnProperty("playedTimestamp")) + if (!$util.isInteger(message.playedTimestamp) && !(message.playedTimestamp && $util.isInteger(message.playedTimestamp.low) && $util.isInteger(message.playedTimestamp.high))) + return "playedTimestamp: integer|Long expected"; + if (message.pendingDeviceJid != null && message.hasOwnProperty("pendingDeviceJid")) { + if (!Array.isArray(message.pendingDeviceJid)) + return "pendingDeviceJid: array expected"; + for (var i = 0; i < message.pendingDeviceJid.length; ++i) + if (!$util.isString(message.pendingDeviceJid[i])) + return "pendingDeviceJid: string[] expected"; + } + if (message.deliveredDeviceJid != null && message.hasOwnProperty("deliveredDeviceJid")) { + if (!Array.isArray(message.deliveredDeviceJid)) + return "deliveredDeviceJid: array expected"; + for (var i = 0; i < message.deliveredDeviceJid.length; ++i) + if (!$util.isString(message.deliveredDeviceJid[i])) + return "deliveredDeviceJid: string[] expected"; + } + return null; + }; + + /** + * Creates a UserReceipt message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof proto.UserReceipt + * @static + * @param {Object.} object Plain object + * @returns {proto.UserReceipt} UserReceipt + */ + UserReceipt.fromObject = function fromObject(object) { + if (object instanceof $root.proto.UserReceipt) + return object; + var message = new $root.proto.UserReceipt(); + if (object.userJid != null) + message.userJid = String(object.userJid); + if (object.receiptTimestamp != null) + if ($util.Long) + (message.receiptTimestamp = $util.Long.fromValue(object.receiptTimestamp)).unsigned = false; + else if (typeof object.receiptTimestamp === "string") + message.receiptTimestamp = parseInt(object.receiptTimestamp, 10); + else if (typeof object.receiptTimestamp === "number") + message.receiptTimestamp = object.receiptTimestamp; + else if (typeof object.receiptTimestamp === "object") + message.receiptTimestamp = new $util.LongBits(object.receiptTimestamp.low >>> 0, object.receiptTimestamp.high >>> 0).toNumber(); + if (object.readTimestamp != null) + if ($util.Long) + (message.readTimestamp = $util.Long.fromValue(object.readTimestamp)).unsigned = false; + else if (typeof object.readTimestamp === "string") + message.readTimestamp = parseInt(object.readTimestamp, 10); + else if (typeof object.readTimestamp === "number") + message.readTimestamp = object.readTimestamp; + else if (typeof object.readTimestamp === "object") + message.readTimestamp = new $util.LongBits(object.readTimestamp.low >>> 0, object.readTimestamp.high >>> 0).toNumber(); + if (object.playedTimestamp != null) + if ($util.Long) + (message.playedTimestamp = $util.Long.fromValue(object.playedTimestamp)).unsigned = false; + else if (typeof object.playedTimestamp === "string") + message.playedTimestamp = parseInt(object.playedTimestamp, 10); + else if (typeof object.playedTimestamp === "number") + message.playedTimestamp = object.playedTimestamp; + else if (typeof object.playedTimestamp === "object") + message.playedTimestamp = new $util.LongBits(object.playedTimestamp.low >>> 0, object.playedTimestamp.high >>> 0).toNumber(); + if (object.pendingDeviceJid) { + if (!Array.isArray(object.pendingDeviceJid)) + throw TypeError(".proto.UserReceipt.pendingDeviceJid: array expected"); + message.pendingDeviceJid = []; + for (var i = 0; i < object.pendingDeviceJid.length; ++i) + message.pendingDeviceJid[i] = String(object.pendingDeviceJid[i]); + } + if (object.deliveredDeviceJid) { + if (!Array.isArray(object.deliveredDeviceJid)) + throw TypeError(".proto.UserReceipt.deliveredDeviceJid: array expected"); + message.deliveredDeviceJid = []; + for (var i = 0; i < object.deliveredDeviceJid.length; ++i) + message.deliveredDeviceJid[i] = String(object.deliveredDeviceJid[i]); + } + return message; + }; + + /** + * Creates a plain object from a UserReceipt message. Also converts values to other types if specified. + * @function toObject + * @memberof proto.UserReceipt + * @static + * @param {proto.UserReceipt} message UserReceipt + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + UserReceipt.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.pendingDeviceJid = []; + object.deliveredDeviceJid = []; + } + if (options.defaults) { + object.userJid = ""; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.receiptTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.receiptTimestamp = options.longs === String ? "0" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.readTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.readTimestamp = options.longs === String ? "0" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.playedTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.playedTimestamp = options.longs === String ? "0" : 0; + } + if (message.userJid != null && message.hasOwnProperty("userJid")) + object.userJid = message.userJid; + if (message.receiptTimestamp != null && message.hasOwnProperty("receiptTimestamp")) + if (typeof message.receiptTimestamp === "number") + object.receiptTimestamp = options.longs === String ? String(message.receiptTimestamp) : message.receiptTimestamp; + else + object.receiptTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.receiptTimestamp) : options.longs === Number ? new $util.LongBits(message.receiptTimestamp.low >>> 0, message.receiptTimestamp.high >>> 0).toNumber() : message.receiptTimestamp; + if (message.readTimestamp != null && message.hasOwnProperty("readTimestamp")) + if (typeof message.readTimestamp === "number") + object.readTimestamp = options.longs === String ? String(message.readTimestamp) : message.readTimestamp; + else + object.readTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.readTimestamp) : options.longs === Number ? new $util.LongBits(message.readTimestamp.low >>> 0, message.readTimestamp.high >>> 0).toNumber() : message.readTimestamp; + if (message.playedTimestamp != null && message.hasOwnProperty("playedTimestamp")) + if (typeof message.playedTimestamp === "number") + object.playedTimestamp = options.longs === String ? String(message.playedTimestamp) : message.playedTimestamp; + else + object.playedTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.playedTimestamp) : options.longs === Number ? new $util.LongBits(message.playedTimestamp.low >>> 0, message.playedTimestamp.high >>> 0).toNumber() : message.playedTimestamp; + if (message.pendingDeviceJid && message.pendingDeviceJid.length) { + object.pendingDeviceJid = []; + for (var j = 0; j < message.pendingDeviceJid.length; ++j) + object.pendingDeviceJid[j] = message.pendingDeviceJid[j]; + } + if (message.deliveredDeviceJid && message.deliveredDeviceJid.length) { + object.deliveredDeviceJid = []; + for (var j = 0; j < message.deliveredDeviceJid.length; ++j) + object.deliveredDeviceJid[j] = message.deliveredDeviceJid[j]; + } + return object; + }; + + /** + * Converts this UserReceipt to JSON. + * @function toJSON + * @memberof proto.UserReceipt + * @instance + * @returns {Object.} JSON object + */ + UserReceipt.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return UserReceipt; + })(); + proto.PhotoChange = (function() { /** @@ -46113,193 +53169,6 @@ $root.proto = (function() { return PhotoChange; })(); - proto.MediaData = (function() { - - /** - * Properties of a MediaData. - * @memberof proto - * @interface IMediaData - * @property {string|null} [localPath] MediaData localPath - */ - - /** - * Constructs a new MediaData. - * @memberof proto - * @classdesc Represents a MediaData. - * @implements IMediaData - * @constructor - * @param {proto.IMediaData=} [properties] Properties to set - */ - function MediaData(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * MediaData localPath. - * @member {string} localPath - * @memberof proto.MediaData - * @instance - */ - MediaData.prototype.localPath = ""; - - /** - * Creates a new MediaData instance using the specified properties. - * @function create - * @memberof proto.MediaData - * @static - * @param {proto.IMediaData=} [properties] Properties to set - * @returns {proto.MediaData} MediaData instance - */ - MediaData.create = function create(properties) { - return new MediaData(properties); - }; - - /** - * Encodes the specified MediaData message. Does not implicitly {@link proto.MediaData.verify|verify} messages. - * @function encode - * @memberof proto.MediaData - * @static - * @param {proto.IMediaData} message MediaData message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - MediaData.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.localPath != null && Object.hasOwnProperty.call(message, "localPath")) - writer.uint32(/* id 1, wireType 2 =*/10).string(message.localPath); - return writer; - }; - - /** - * Encodes the specified MediaData message, length delimited. Does not implicitly {@link proto.MediaData.verify|verify} messages. - * @function encodeDelimited - * @memberof proto.MediaData - * @static - * @param {proto.IMediaData} message MediaData message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - MediaData.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a MediaData message from the specified reader or buffer. - * @function decode - * @memberof proto.MediaData - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {proto.MediaData} MediaData - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - MediaData.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.proto.MediaData(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.localPath = reader.string(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a MediaData message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof proto.MediaData - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {proto.MediaData} MediaData - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - MediaData.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a MediaData message. - * @function verify - * @memberof proto.MediaData - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - MediaData.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.localPath != null && message.hasOwnProperty("localPath")) - if (!$util.isString(message.localPath)) - return "localPath: string expected"; - return null; - }; - - /** - * Creates a MediaData message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof proto.MediaData - * @static - * @param {Object.} object Plain object - * @returns {proto.MediaData} MediaData - */ - MediaData.fromObject = function fromObject(object) { - if (object instanceof $root.proto.MediaData) - return object; - var message = new $root.proto.MediaData(); - if (object.localPath != null) - message.localPath = String(object.localPath); - return message; - }; - - /** - * Creates a plain object from a MediaData message. Also converts values to other types if specified. - * @function toObject - * @memberof proto.MediaData - * @static - * @param {proto.MediaData} message MediaData - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - MediaData.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) - object.localPath = ""; - if (message.localPath != null && message.hasOwnProperty("localPath")) - object.localPath = message.localPath; - return object; - }; - - /** - * Converts this MediaData to JSON. - * @function toJSON - * @memberof proto.MediaData - * @instance - * @returns {Object.} JSON object - */ - MediaData.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return MediaData; - })(); - proto.WebFeatures = (function() { /** @@ -46348,6 +53217,9 @@ $root.proto = (function() { * @property {proto.WebFeatures.WebFeaturesFlag|null} [ephemeralAllowGroupMembers] WebFeatures ephemeralAllowGroupMembers * @property {proto.WebFeatures.WebFeaturesFlag|null} [ephemeral24HDuration] WebFeatures ephemeral24HDuration * @property {proto.WebFeatures.WebFeaturesFlag|null} [mdForceUpgrade] WebFeatures mdForceUpgrade + * @property {proto.WebFeatures.WebFeaturesFlag|null} [disappearingMode] WebFeatures disappearingMode + * @property {proto.WebFeatures.WebFeaturesFlag|null} [externalMdOptInAvailable] WebFeatures externalMdOptInAvailable + * @property {proto.WebFeatures.WebFeaturesFlag|null} [noDeleteMessageTimeLimit] WebFeatures noDeleteMessageTimeLimit */ /** @@ -46701,6 +53573,30 @@ $root.proto = (function() { */ WebFeatures.prototype.mdForceUpgrade = 0; + /** + * WebFeatures disappearingMode. + * @member {proto.WebFeatures.WebFeaturesFlag} disappearingMode + * @memberof proto.WebFeatures + * @instance + */ + WebFeatures.prototype.disappearingMode = 0; + + /** + * WebFeatures externalMdOptInAvailable. + * @member {proto.WebFeatures.WebFeaturesFlag} externalMdOptInAvailable + * @memberof proto.WebFeatures + * @instance + */ + WebFeatures.prototype.externalMdOptInAvailable = 0; + + /** + * WebFeatures noDeleteMessageTimeLimit. + * @member {proto.WebFeatures.WebFeaturesFlag} noDeleteMessageTimeLimit + * @memberof proto.WebFeatures + * @instance + */ + WebFeatures.prototype.noDeleteMessageTimeLimit = 0; + /** * Creates a new WebFeatures instance using the specified properties. * @function create @@ -46809,6 +53705,12 @@ $root.proto = (function() { writer.uint32(/* id 45, wireType 0 =*/360).int32(message.ephemeral24HDuration); if (message.mdForceUpgrade != null && Object.hasOwnProperty.call(message, "mdForceUpgrade")) writer.uint32(/* id 46, wireType 0 =*/368).int32(message.mdForceUpgrade); + if (message.disappearingMode != null && Object.hasOwnProperty.call(message, "disappearingMode")) + writer.uint32(/* id 47, wireType 0 =*/376).int32(message.disappearingMode); + if (message.externalMdOptInAvailable != null && Object.hasOwnProperty.call(message, "externalMdOptInAvailable")) + writer.uint32(/* id 48, wireType 0 =*/384).int32(message.externalMdOptInAvailable); + if (message.noDeleteMessageTimeLimit != null && Object.hasOwnProperty.call(message, "noDeleteMessageTimeLimit")) + writer.uint32(/* id 49, wireType 0 =*/392).int32(message.noDeleteMessageTimeLimit); return writer; }; @@ -46969,6 +53871,15 @@ $root.proto = (function() { case 46: message.mdForceUpgrade = reader.int32(); break; + case 47: + message.disappearingMode = reader.int32(); + break; + case 48: + message.externalMdOptInAvailable = reader.int32(); + break; + case 49: + message.noDeleteMessageTimeLimit = reader.int32(); + break; default: reader.skipType(tag & 7); break; @@ -47424,6 +54335,36 @@ $root.proto = (function() { case 3: break; } + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) + switch (message.disappearingMode) { + default: + return "disappearingMode: enum value expected"; + case 0: + case 1: + case 2: + case 3: + break; + } + if (message.externalMdOptInAvailable != null && message.hasOwnProperty("externalMdOptInAvailable")) + switch (message.externalMdOptInAvailable) { + default: + return "externalMdOptInAvailable: enum value expected"; + case 0: + case 1: + case 2: + case 3: + break; + } + if (message.noDeleteMessageTimeLimit != null && message.hasOwnProperty("noDeleteMessageTimeLimit")) + switch (message.noDeleteMessageTimeLimit) { + default: + return "noDeleteMessageTimeLimit: enum value expected"; + case 0: + case 1: + case 2: + case 3: + break; + } return null; }; @@ -48195,6 +55136,60 @@ $root.proto = (function() { message.mdForceUpgrade = 3; break; } + switch (object.disappearingMode) { + case "NOT_STARTED": + case 0: + message.disappearingMode = 0; + break; + case "FORCE_UPGRADE": + case 1: + message.disappearingMode = 1; + break; + case "DEVELOPMENT": + case 2: + message.disappearingMode = 2; + break; + case "PRODUCTION": + case 3: + message.disappearingMode = 3; + break; + } + switch (object.externalMdOptInAvailable) { + case "NOT_STARTED": + case 0: + message.externalMdOptInAvailable = 0; + break; + case "FORCE_UPGRADE": + case 1: + message.externalMdOptInAvailable = 1; + break; + case "DEVELOPMENT": + case 2: + message.externalMdOptInAvailable = 2; + break; + case "PRODUCTION": + case 3: + message.externalMdOptInAvailable = 3; + break; + } + switch (object.noDeleteMessageTimeLimit) { + case "NOT_STARTED": + case 0: + message.noDeleteMessageTimeLimit = 0; + break; + case "FORCE_UPGRADE": + case 1: + message.noDeleteMessageTimeLimit = 1; + break; + case "DEVELOPMENT": + case 2: + message.noDeleteMessageTimeLimit = 2; + break; + case "PRODUCTION": + case 3: + message.noDeleteMessageTimeLimit = 3; + break; + } return message; }; @@ -48254,6 +55249,9 @@ $root.proto = (function() { object.ephemeralAllowGroupMembers = options.enums === String ? "NOT_STARTED" : 0; object.ephemeral24HDuration = options.enums === String ? "NOT_STARTED" : 0; object.mdForceUpgrade = options.enums === String ? "NOT_STARTED" : 0; + object.disappearingMode = options.enums === String ? "NOT_STARTED" : 0; + object.externalMdOptInAvailable = options.enums === String ? "NOT_STARTED" : 0; + object.noDeleteMessageTimeLimit = options.enums === String ? "NOT_STARTED" : 0; } if (message.labelsDisplay != null && message.hasOwnProperty("labelsDisplay")) object.labelsDisplay = options.enums === String ? $root.proto.WebFeatures.WebFeaturesFlag[message.labelsDisplay] : message.labelsDisplay; @@ -48339,6 +55337,12 @@ $root.proto = (function() { object.ephemeral24HDuration = options.enums === String ? $root.proto.WebFeatures.WebFeaturesFlag[message.ephemeral24HDuration] : message.ephemeral24HDuration; if (message.mdForceUpgrade != null && message.hasOwnProperty("mdForceUpgrade")) object.mdForceUpgrade = options.enums === String ? $root.proto.WebFeatures.WebFeaturesFlag[message.mdForceUpgrade] : message.mdForceUpgrade; + if (message.disappearingMode != null && message.hasOwnProperty("disappearingMode")) + object.disappearingMode = options.enums === String ? $root.proto.WebFeatures.WebFeaturesFlag[message.disappearingMode] : message.disappearingMode; + if (message.externalMdOptInAvailable != null && message.hasOwnProperty("externalMdOptInAvailable")) + object.externalMdOptInAvailable = options.enums === String ? $root.proto.WebFeatures.WebFeaturesFlag[message.externalMdOptInAvailable] : message.externalMdOptInAvailable; + if (message.noDeleteMessageTimeLimit != null && message.hasOwnProperty("noDeleteMessageTimeLimit")) + object.noDeleteMessageTimeLimit = options.enums === String ? $root.proto.WebFeatures.WebFeaturesFlag[message.noDeleteMessageTimeLimit] : message.noDeleteMessageTimeLimit; return object; }; @@ -49827,6 +56831,13 @@ $root.proto = (function() { * @property {string|null} [verifiedBizName] WebMessageInfo verifiedBizName * @property {proto.IMediaData|null} [mediaData] WebMessageInfo mediaData * @property {proto.IPhotoChange|null} [photoChange] WebMessageInfo photoChange + * @property {Array.|null} [userReceipt] WebMessageInfo userReceipt + * @property {Array.|null} [reactions] WebMessageInfo reactions + * @property {proto.IMediaData|null} [quotedStickerData] WebMessageInfo quotedStickerData + * @property {Uint8Array|null} [futureproofData] WebMessageInfo futureproofData + * @property {string|null} [statusPsaCampaignId] WebMessageInfo statusPsaCampaignId + * @property {number|null} [statusPsaCampaignDuration] WebMessageInfo statusPsaCampaignDuration + * @property {number|Long|null} [statusPsaCampaignReadTimestamp] WebMessageInfo statusPsaCampaignReadTimestamp */ /** @@ -49840,6 +56851,8 @@ $root.proto = (function() { function WebMessageInfo(properties) { this.messageStubParameters = []; this.labels = []; + this.userReceipt = []; + this.reactions = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -50086,6 +57099,62 @@ $root.proto = (function() { */ WebMessageInfo.prototype.photoChange = null; + /** + * WebMessageInfo userReceipt. + * @member {Array.} userReceipt + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.userReceipt = $util.emptyArray; + + /** + * WebMessageInfo reactions. + * @member {Array.} reactions + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.reactions = $util.emptyArray; + + /** + * WebMessageInfo quotedStickerData. + * @member {proto.IMediaData|null|undefined} quotedStickerData + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.quotedStickerData = null; + + /** + * WebMessageInfo futureproofData. + * @member {Uint8Array} futureproofData + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.futureproofData = $util.newBuffer([]); + + /** + * WebMessageInfo statusPsaCampaignId. + * @member {string} statusPsaCampaignId + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.statusPsaCampaignId = ""; + + /** + * WebMessageInfo statusPsaCampaignDuration. + * @member {number} statusPsaCampaignDuration + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.statusPsaCampaignDuration = 0; + + /** + * WebMessageInfo statusPsaCampaignReadTimestamp. + * @member {number|Long} statusPsaCampaignReadTimestamp + * @memberof proto.WebMessageInfo + * @instance + */ + WebMessageInfo.prototype.statusPsaCampaignReadTimestamp = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + /** * Creates a new WebMessageInfo instance using the specified properties. * @function create @@ -50171,6 +57240,22 @@ $root.proto = (function() { $root.proto.MediaData.encode(message.mediaData, writer.uint32(/* id 38, wireType 2 =*/306).fork()).ldelim(); if (message.photoChange != null && Object.hasOwnProperty.call(message, "photoChange")) $root.proto.PhotoChange.encode(message.photoChange, writer.uint32(/* id 39, wireType 2 =*/314).fork()).ldelim(); + if (message.userReceipt != null && message.userReceipt.length) + for (var i = 0; i < message.userReceipt.length; ++i) + $root.proto.UserReceipt.encode(message.userReceipt[i], writer.uint32(/* id 40, wireType 2 =*/322).fork()).ldelim(); + if (message.reactions != null && message.reactions.length) + for (var i = 0; i < message.reactions.length; ++i) + $root.proto.Reaction.encode(message.reactions[i], writer.uint32(/* id 41, wireType 2 =*/330).fork()).ldelim(); + if (message.quotedStickerData != null && Object.hasOwnProperty.call(message, "quotedStickerData")) + $root.proto.MediaData.encode(message.quotedStickerData, writer.uint32(/* id 42, wireType 2 =*/338).fork()).ldelim(); + if (message.futureproofData != null && Object.hasOwnProperty.call(message, "futureproofData")) + writer.uint32(/* id 43, wireType 2 =*/346).bytes(message.futureproofData); + if (message.statusPsaCampaignId != null && Object.hasOwnProperty.call(message, "statusPsaCampaignId")) + writer.uint32(/* id 44, wireType 2 =*/354).string(message.statusPsaCampaignId); + if (message.statusPsaCampaignDuration != null && Object.hasOwnProperty.call(message, "statusPsaCampaignDuration")) + writer.uint32(/* id 45, wireType 0 =*/360).uint32(message.statusPsaCampaignDuration); + if (message.statusPsaCampaignReadTimestamp != null && Object.hasOwnProperty.call(message, "statusPsaCampaignReadTimestamp")) + writer.uint32(/* id 46, wireType 0 =*/368).uint64(message.statusPsaCampaignReadTimestamp); return writer; }; @@ -50299,6 +57384,31 @@ $root.proto = (function() { case 39: message.photoChange = $root.proto.PhotoChange.decode(reader, reader.uint32()); break; + case 40: + if (!(message.userReceipt && message.userReceipt.length)) + message.userReceipt = []; + message.userReceipt.push($root.proto.UserReceipt.decode(reader, reader.uint32())); + break; + case 41: + if (!(message.reactions && message.reactions.length)) + message.reactions = []; + message.reactions.push($root.proto.Reaction.decode(reader, reader.uint32())); + break; + case 42: + message.quotedStickerData = $root.proto.MediaData.decode(reader, reader.uint32()); + break; + case 43: + message.futureproofData = reader.bytes(); + break; + case 44: + message.statusPsaCampaignId = reader.string(); + break; + case 45: + message.statusPsaCampaignDuration = reader.uint32(); + break; + case 46: + message.statusPsaCampaignReadTimestamp = reader.uint64(); + break; default: reader.skipType(tag & 7); break; @@ -50525,6 +57635,8 @@ $root.proto = (function() { case 127: case 128: case 129: + case 130: + case 131: break; } if (message.clearMedia != null && message.hasOwnProperty("clearMedia")) @@ -50597,6 +57709,41 @@ $root.proto = (function() { if (error) return "photoChange." + error; } + if (message.userReceipt != null && message.hasOwnProperty("userReceipt")) { + if (!Array.isArray(message.userReceipt)) + return "userReceipt: array expected"; + for (var i = 0; i < message.userReceipt.length; ++i) { + var error = $root.proto.UserReceipt.verify(message.userReceipt[i]); + if (error) + return "userReceipt." + error; + } + } + if (message.reactions != null && message.hasOwnProperty("reactions")) { + if (!Array.isArray(message.reactions)) + return "reactions: array expected"; + for (var i = 0; i < message.reactions.length; ++i) { + var error = $root.proto.Reaction.verify(message.reactions[i]); + if (error) + return "reactions." + error; + } + } + if (message.quotedStickerData != null && message.hasOwnProperty("quotedStickerData")) { + var error = $root.proto.MediaData.verify(message.quotedStickerData); + if (error) + return "quotedStickerData." + error; + } + if (message.futureproofData != null && message.hasOwnProperty("futureproofData")) + if (!(message.futureproofData && typeof message.futureproofData.length === "number" || $util.isString(message.futureproofData))) + return "futureproofData: buffer expected"; + if (message.statusPsaCampaignId != null && message.hasOwnProperty("statusPsaCampaignId")) + if (!$util.isString(message.statusPsaCampaignId)) + return "statusPsaCampaignId: string expected"; + if (message.statusPsaCampaignDuration != null && message.hasOwnProperty("statusPsaCampaignDuration")) + if (!$util.isInteger(message.statusPsaCampaignDuration)) + return "statusPsaCampaignDuration: integer expected"; + if (message.statusPsaCampaignReadTimestamp != null && message.hasOwnProperty("statusPsaCampaignReadTimestamp")) + if (!$util.isInteger(message.statusPsaCampaignReadTimestamp) && !(message.statusPsaCampaignReadTimestamp && $util.isInteger(message.statusPsaCampaignReadTimestamp.low) && $util.isInteger(message.statusPsaCampaignReadTimestamp.high))) + return "statusPsaCampaignReadTimestamp: integer|Long expected"; return null; }; @@ -51208,6 +58355,14 @@ $root.proto = (function() { case 129: message.messageStubType = 129; break; + case "DISAPPEARING_MODE": + case 130: + message.messageStubType = 130; + break; + case "E2E_DEVICE_FETCH_FAILED": + case 131: + message.messageStubType = 131; + break; } if (object.clearMedia != null) message.clearMedia = Boolean(object.clearMedia); @@ -51287,6 +58442,49 @@ $root.proto = (function() { throw TypeError(".proto.WebMessageInfo.photoChange: object expected"); message.photoChange = $root.proto.PhotoChange.fromObject(object.photoChange); } + if (object.userReceipt) { + if (!Array.isArray(object.userReceipt)) + throw TypeError(".proto.WebMessageInfo.userReceipt: array expected"); + message.userReceipt = []; + for (var i = 0; i < object.userReceipt.length; ++i) { + if (typeof object.userReceipt[i] !== "object") + throw TypeError(".proto.WebMessageInfo.userReceipt: object expected"); + message.userReceipt[i] = $root.proto.UserReceipt.fromObject(object.userReceipt[i]); + } + } + if (object.reactions) { + if (!Array.isArray(object.reactions)) + throw TypeError(".proto.WebMessageInfo.reactions: array expected"); + message.reactions = []; + for (var i = 0; i < object.reactions.length; ++i) { + if (typeof object.reactions[i] !== "object") + throw TypeError(".proto.WebMessageInfo.reactions: object expected"); + message.reactions[i] = $root.proto.Reaction.fromObject(object.reactions[i]); + } + } + if (object.quotedStickerData != null) { + if (typeof object.quotedStickerData !== "object") + throw TypeError(".proto.WebMessageInfo.quotedStickerData: object expected"); + message.quotedStickerData = $root.proto.MediaData.fromObject(object.quotedStickerData); + } + if (object.futureproofData != null) + if (typeof object.futureproofData === "string") + $util.base64.decode(object.futureproofData, message.futureproofData = $util.newBuffer($util.base64.length(object.futureproofData)), 0); + else if (object.futureproofData.length) + message.futureproofData = object.futureproofData; + if (object.statusPsaCampaignId != null) + message.statusPsaCampaignId = String(object.statusPsaCampaignId); + if (object.statusPsaCampaignDuration != null) + message.statusPsaCampaignDuration = object.statusPsaCampaignDuration >>> 0; + if (object.statusPsaCampaignReadTimestamp != null) + if ($util.Long) + (message.statusPsaCampaignReadTimestamp = $util.Long.fromValue(object.statusPsaCampaignReadTimestamp)).unsigned = true; + else if (typeof object.statusPsaCampaignReadTimestamp === "string") + message.statusPsaCampaignReadTimestamp = parseInt(object.statusPsaCampaignReadTimestamp, 10); + else if (typeof object.statusPsaCampaignReadTimestamp === "number") + message.statusPsaCampaignReadTimestamp = object.statusPsaCampaignReadTimestamp; + else if (typeof object.statusPsaCampaignReadTimestamp === "object") + message.statusPsaCampaignReadTimestamp = new $util.LongBits(object.statusPsaCampaignReadTimestamp.low >>> 0, object.statusPsaCampaignReadTimestamp.high >>> 0).toNumber(true); return message; }; @@ -51306,6 +58504,8 @@ $root.proto = (function() { if (options.arrays || options.defaults) { object.messageStubParameters = []; object.labels = []; + object.userReceipt = []; + object.reactions = []; } if (options.defaults) { object.key = null; @@ -51354,6 +58554,21 @@ $root.proto = (function() { object.verifiedBizName = ""; object.mediaData = null; object.photoChange = null; + object.quotedStickerData = null; + if (options.bytes === String) + object.futureproofData = ""; + else { + object.futureproofData = []; + if (options.bytes !== Array) + object.futureproofData = $util.newBuffer(object.futureproofData); + } + object.statusPsaCampaignId = ""; + object.statusPsaCampaignDuration = 0; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.statusPsaCampaignReadTimestamp = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.statusPsaCampaignReadTimestamp = options.longs === String ? "0" : 0; } if (message.key != null && message.hasOwnProperty("key")) object.key = $root.proto.MessageKey.toObject(message.key, options); @@ -51430,6 +58645,29 @@ $root.proto = (function() { object.mediaData = $root.proto.MediaData.toObject(message.mediaData, options); if (message.photoChange != null && message.hasOwnProperty("photoChange")) object.photoChange = $root.proto.PhotoChange.toObject(message.photoChange, options); + if (message.userReceipt && message.userReceipt.length) { + object.userReceipt = []; + for (var j = 0; j < message.userReceipt.length; ++j) + object.userReceipt[j] = $root.proto.UserReceipt.toObject(message.userReceipt[j], options); + } + if (message.reactions && message.reactions.length) { + object.reactions = []; + for (var j = 0; j < message.reactions.length; ++j) + object.reactions[j] = $root.proto.Reaction.toObject(message.reactions[j], options); + } + if (message.quotedStickerData != null && message.hasOwnProperty("quotedStickerData")) + object.quotedStickerData = $root.proto.MediaData.toObject(message.quotedStickerData, options); + if (message.futureproofData != null && message.hasOwnProperty("futureproofData")) + object.futureproofData = options.bytes === String ? $util.base64.encode(message.futureproofData, 0, message.futureproofData.length) : options.bytes === Array ? Array.prototype.slice.call(message.futureproofData) : message.futureproofData; + if (message.statusPsaCampaignId != null && message.hasOwnProperty("statusPsaCampaignId")) + object.statusPsaCampaignId = message.statusPsaCampaignId; + if (message.statusPsaCampaignDuration != null && message.hasOwnProperty("statusPsaCampaignDuration")) + object.statusPsaCampaignDuration = message.statusPsaCampaignDuration; + if (message.statusPsaCampaignReadTimestamp != null && message.hasOwnProperty("statusPsaCampaignReadTimestamp")) + if (typeof message.statusPsaCampaignReadTimestamp === "number") + object.statusPsaCampaignReadTimestamp = options.longs === String ? String(message.statusPsaCampaignReadTimestamp) : message.statusPsaCampaignReadTimestamp; + else + object.statusPsaCampaignReadTimestamp = options.longs === String ? $util.Long.prototype.toString.call(message.statusPsaCampaignReadTimestamp) : options.longs === Number ? new $util.LongBits(message.statusPsaCampaignReadTimestamp.low >>> 0, message.statusPsaCampaignReadTimestamp.high >>> 0).toNumber(true) : message.statusPsaCampaignReadTimestamp; return object; }; @@ -51600,6 +58838,8 @@ $root.proto = (function() { * @property {number} BIZ_PRIVACY_MODE_INIT_BSP=127 BIZ_PRIVACY_MODE_INIT_BSP value * @property {number} BIZ_PRIVACY_MODE_TO_FB=128 BIZ_PRIVACY_MODE_TO_FB value * @property {number} BIZ_PRIVACY_MODE_TO_BSP=129 BIZ_PRIVACY_MODE_TO_BSP value + * @property {number} DISAPPEARING_MODE=130 DISAPPEARING_MODE value + * @property {number} E2E_DEVICE_FETCH_FAILED=131 E2E_DEVICE_FETCH_FAILED value */ WebMessageInfo.WebMessageInfoStubType = (function() { var valuesById = {}, values = Object.create(valuesById); @@ -51733,6 +58973,8 @@ $root.proto = (function() { values[valuesById[127] = "BIZ_PRIVACY_MODE_INIT_BSP"] = 127; values[valuesById[128] = "BIZ_PRIVACY_MODE_TO_FB"] = 128; values[valuesById[129] = "BIZ_PRIVACY_MODE_TO_BSP"] = 129; + values[valuesById[130] = "DISAPPEARING_MODE"] = 130; + values[valuesById[131] = "E2E_DEVICE_FETCH_FAILED"] = 131; return values; })(); @@ -51760,4 +59002,4 @@ $root.proto = (function() { return proto; })(); -module.exports = $root; \ No newline at end of file +module.exports = $root; diff --git a/WASignalGroup/GroupProtocol.js b/WASignalGroup/GroupProtocol.js new file mode 100644 index 0000000..efa722a --- /dev/null +++ b/WASignalGroup/GroupProtocol.js @@ -0,0 +1,1697 @@ +/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ +"use strict"; + +var $protobuf = require("protobufjs/minimal"); + +// Common aliases +var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + +// Exported root namespace +var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); + +$root.groupproto = (function() { + + /** + * Namespace groupproto. + * @exports groupproto + * @namespace + */ + var groupproto = {}; + + groupproto.SenderKeyMessage = (function() { + + /** + * Properties of a SenderKeyMessage. + * @memberof groupproto + * @interface ISenderKeyMessage + * @property {number|null} [id] SenderKeyMessage id + * @property {number|null} [iteration] SenderKeyMessage iteration + * @property {Uint8Array|null} [ciphertext] SenderKeyMessage ciphertext + */ + + /** + * Constructs a new SenderKeyMessage. + * @memberof groupproto + * @classdesc Represents a SenderKeyMessage. + * @implements ISenderKeyMessage + * @constructor + * @param {groupproto.ISenderKeyMessage=} [properties] Properties to set + */ + function SenderKeyMessage(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderKeyMessage id. + * @member {number} id + * @memberof groupproto.SenderKeyMessage + * @instance + */ + SenderKeyMessage.prototype.id = 0; + + /** + * SenderKeyMessage iteration. + * @member {number} iteration + * @memberof groupproto.SenderKeyMessage + * @instance + */ + SenderKeyMessage.prototype.iteration = 0; + + /** + * SenderKeyMessage ciphertext. + * @member {Uint8Array} ciphertext + * @memberof groupproto.SenderKeyMessage + * @instance + */ + SenderKeyMessage.prototype.ciphertext = $util.newBuffer([]); + + /** + * Creates a new SenderKeyMessage instance using the specified properties. + * @function create + * @memberof groupproto.SenderKeyMessage + * @static + * @param {groupproto.ISenderKeyMessage=} [properties] Properties to set + * @returns {groupproto.SenderKeyMessage} SenderKeyMessage instance + */ + SenderKeyMessage.create = function create(properties) { + return new SenderKeyMessage(properties); + }; + + /** + * Encodes the specified SenderKeyMessage message. Does not implicitly {@link groupproto.SenderKeyMessage.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderKeyMessage + * @static + * @param {groupproto.ISenderKeyMessage} message SenderKeyMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.id); + if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.iteration); + if (message.ciphertext != null && Object.hasOwnProperty.call(message, "ciphertext")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.ciphertext); + return writer; + }; + + /** + * Encodes the specified SenderKeyMessage message, length delimited. Does not implicitly {@link groupproto.SenderKeyMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderKeyMessage + * @static + * @param {groupproto.ISenderKeyMessage} message SenderKeyMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderKeyMessage message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderKeyMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderKeyMessage} SenderKeyMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.uint32(); + break; + case 2: + message.iteration = reader.uint32(); + break; + case 3: + message.ciphertext = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderKeyMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderKeyMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderKeyMessage} SenderKeyMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderKeyMessage message. + * @function verify + * @memberof groupproto.SenderKeyMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderKeyMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isInteger(message.id)) + return "id: integer expected"; + if (message.iteration != null && message.hasOwnProperty("iteration")) + if (!$util.isInteger(message.iteration)) + return "iteration: integer expected"; + if (message.ciphertext != null && message.hasOwnProperty("ciphertext")) + if (!(message.ciphertext && typeof message.ciphertext.length === "number" || $util.isString(message.ciphertext))) + return "ciphertext: buffer expected"; + return null; + }; + + /** + * Creates a SenderKeyMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderKeyMessage + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderKeyMessage} SenderKeyMessage + */ + SenderKeyMessage.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderKeyMessage) + return object; + var message = new $root.groupproto.SenderKeyMessage(); + if (object.id != null) + message.id = object.id >>> 0; + if (object.iteration != null) + message.iteration = object.iteration >>> 0; + if (object.ciphertext != null) + if (typeof object.ciphertext === "string") + $util.base64.decode(object.ciphertext, message.ciphertext = $util.newBuffer($util.base64.length(object.ciphertext)), 0); + else if (object.ciphertext.length) + message.ciphertext = object.ciphertext; + return message; + }; + + /** + * Creates a plain object from a SenderKeyMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderKeyMessage + * @static + * @param {groupproto.SenderKeyMessage} message SenderKeyMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderKeyMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.id = 0; + object.iteration = 0; + if (options.bytes === String) + object.ciphertext = ""; + else { + object.ciphertext = []; + if (options.bytes !== Array) + object.ciphertext = $util.newBuffer(object.ciphertext); + } + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + if (message.iteration != null && message.hasOwnProperty("iteration")) + object.iteration = message.iteration; + if (message.ciphertext != null && message.hasOwnProperty("ciphertext")) + object.ciphertext = options.bytes === String ? $util.base64.encode(message.ciphertext, 0, message.ciphertext.length) : options.bytes === Array ? Array.prototype.slice.call(message.ciphertext) : message.ciphertext; + return object; + }; + + /** + * Converts this SenderKeyMessage to JSON. + * @function toJSON + * @memberof groupproto.SenderKeyMessage + * @instance + * @returns {Object.} JSON object + */ + SenderKeyMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderKeyMessage; + })(); + + groupproto.SenderKeyDistributionMessage = (function() { + + /** + * Properties of a SenderKeyDistributionMessage. + * @memberof groupproto + * @interface ISenderKeyDistributionMessage + * @property {number|null} [id] SenderKeyDistributionMessage id + * @property {number|null} [iteration] SenderKeyDistributionMessage iteration + * @property {Uint8Array|null} [chainKey] SenderKeyDistributionMessage chainKey + * @property {Uint8Array|null} [signingKey] SenderKeyDistributionMessage signingKey + */ + + /** + * Constructs a new SenderKeyDistributionMessage. + * @memberof groupproto + * @classdesc Represents a SenderKeyDistributionMessage. + * @implements ISenderKeyDistributionMessage + * @constructor + * @param {groupproto.ISenderKeyDistributionMessage=} [properties] Properties to set + */ + function SenderKeyDistributionMessage(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderKeyDistributionMessage id. + * @member {number} id + * @memberof groupproto.SenderKeyDistributionMessage + * @instance + */ + SenderKeyDistributionMessage.prototype.id = 0; + + /** + * SenderKeyDistributionMessage iteration. + * @member {number} iteration + * @memberof groupproto.SenderKeyDistributionMessage + * @instance + */ + SenderKeyDistributionMessage.prototype.iteration = 0; + + /** + * SenderKeyDistributionMessage chainKey. + * @member {Uint8Array} chainKey + * @memberof groupproto.SenderKeyDistributionMessage + * @instance + */ + SenderKeyDistributionMessage.prototype.chainKey = $util.newBuffer([]); + + /** + * SenderKeyDistributionMessage signingKey. + * @member {Uint8Array} signingKey + * @memberof groupproto.SenderKeyDistributionMessage + * @instance + */ + SenderKeyDistributionMessage.prototype.signingKey = $util.newBuffer([]); + + /** + * Creates a new SenderKeyDistributionMessage instance using the specified properties. + * @function create + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {groupproto.ISenderKeyDistributionMessage=} [properties] Properties to set + * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage instance + */ + SenderKeyDistributionMessage.create = function create(properties) { + return new SenderKeyDistributionMessage(properties); + }; + + /** + * Encodes the specified SenderKeyDistributionMessage message. Does not implicitly {@link groupproto.SenderKeyDistributionMessage.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {groupproto.ISenderKeyDistributionMessage} message SenderKeyDistributionMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyDistributionMessage.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.id); + if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.iteration); + if (message.chainKey != null && Object.hasOwnProperty.call(message, "chainKey")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.chainKey); + if (message.signingKey != null && Object.hasOwnProperty.call(message, "signingKey")) + writer.uint32(/* id 4, wireType 2 =*/34).bytes(message.signingKey); + return writer; + }; + + /** + * Encodes the specified SenderKeyDistributionMessage message, length delimited. Does not implicitly {@link groupproto.SenderKeyDistributionMessage.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {groupproto.ISenderKeyDistributionMessage} message SenderKeyDistributionMessage message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyDistributionMessage.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderKeyDistributionMessage message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyDistributionMessage.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyDistributionMessage(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.uint32(); + break; + case 2: + message.iteration = reader.uint32(); + break; + case 3: + message.chainKey = reader.bytes(); + break; + case 4: + message.signingKey = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderKeyDistributionMessage message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyDistributionMessage.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderKeyDistributionMessage message. + * @function verify + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderKeyDistributionMessage.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isInteger(message.id)) + return "id: integer expected"; + if (message.iteration != null && message.hasOwnProperty("iteration")) + if (!$util.isInteger(message.iteration)) + return "iteration: integer expected"; + if (message.chainKey != null && message.hasOwnProperty("chainKey")) + if (!(message.chainKey && typeof message.chainKey.length === "number" || $util.isString(message.chainKey))) + return "chainKey: buffer expected"; + if (message.signingKey != null && message.hasOwnProperty("signingKey")) + if (!(message.signingKey && typeof message.signingKey.length === "number" || $util.isString(message.signingKey))) + return "signingKey: buffer expected"; + return null; + }; + + /** + * Creates a SenderKeyDistributionMessage message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage + */ + SenderKeyDistributionMessage.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderKeyDistributionMessage) + return object; + var message = new $root.groupproto.SenderKeyDistributionMessage(); + if (object.id != null) + message.id = object.id >>> 0; + if (object.iteration != null) + message.iteration = object.iteration >>> 0; + if (object.chainKey != null) + if (typeof object.chainKey === "string") + $util.base64.decode(object.chainKey, message.chainKey = $util.newBuffer($util.base64.length(object.chainKey)), 0); + else if (object.chainKey.length) + message.chainKey = object.chainKey; + if (object.signingKey != null) + if (typeof object.signingKey === "string") + $util.base64.decode(object.signingKey, message.signingKey = $util.newBuffer($util.base64.length(object.signingKey)), 0); + else if (object.signingKey.length) + message.signingKey = object.signingKey; + return message; + }; + + /** + * Creates a plain object from a SenderKeyDistributionMessage message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderKeyDistributionMessage + * @static + * @param {groupproto.SenderKeyDistributionMessage} message SenderKeyDistributionMessage + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderKeyDistributionMessage.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.id = 0; + object.iteration = 0; + if (options.bytes === String) + object.chainKey = ""; + else { + object.chainKey = []; + if (options.bytes !== Array) + object.chainKey = $util.newBuffer(object.chainKey); + } + if (options.bytes === String) + object.signingKey = ""; + else { + object.signingKey = []; + if (options.bytes !== Array) + object.signingKey = $util.newBuffer(object.signingKey); + } + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + if (message.iteration != null && message.hasOwnProperty("iteration")) + object.iteration = message.iteration; + if (message.chainKey != null && message.hasOwnProperty("chainKey")) + object.chainKey = options.bytes === String ? $util.base64.encode(message.chainKey, 0, message.chainKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.chainKey) : message.chainKey; + if (message.signingKey != null && message.hasOwnProperty("signingKey")) + object.signingKey = options.bytes === String ? $util.base64.encode(message.signingKey, 0, message.signingKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.signingKey) : message.signingKey; + return object; + }; + + /** + * Converts this SenderKeyDistributionMessage to JSON. + * @function toJSON + * @memberof groupproto.SenderKeyDistributionMessage + * @instance + * @returns {Object.} JSON object + */ + SenderKeyDistributionMessage.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderKeyDistributionMessage; + })(); + + groupproto.SenderChainKey = (function() { + + /** + * Properties of a SenderChainKey. + * @memberof groupproto + * @interface ISenderChainKey + * @property {number|null} [iteration] SenderChainKey iteration + * @property {Uint8Array|null} [seed] SenderChainKey seed + */ + + /** + * Constructs a new SenderChainKey. + * @memberof groupproto + * @classdesc Represents a SenderChainKey. + * @implements ISenderChainKey + * @constructor + * @param {groupproto.ISenderChainKey=} [properties] Properties to set + */ + function SenderChainKey(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderChainKey iteration. + * @member {number} iteration + * @memberof groupproto.SenderChainKey + * @instance + */ + SenderChainKey.prototype.iteration = 0; + + /** + * SenderChainKey seed. + * @member {Uint8Array} seed + * @memberof groupproto.SenderChainKey + * @instance + */ + SenderChainKey.prototype.seed = $util.newBuffer([]); + + /** + * Creates a new SenderChainKey instance using the specified properties. + * @function create + * @memberof groupproto.SenderChainKey + * @static + * @param {groupproto.ISenderChainKey=} [properties] Properties to set + * @returns {groupproto.SenderChainKey} SenderChainKey instance + */ + SenderChainKey.create = function create(properties) { + return new SenderChainKey(properties); + }; + + /** + * Encodes the specified SenderChainKey message. Does not implicitly {@link groupproto.SenderChainKey.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderChainKey + * @static + * @param {groupproto.ISenderChainKey} message SenderChainKey message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderChainKey.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.iteration); + if (message.seed != null && Object.hasOwnProperty.call(message, "seed")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.seed); + return writer; + }; + + /** + * Encodes the specified SenderChainKey message, length delimited. Does not implicitly {@link groupproto.SenderChainKey.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderChainKey + * @static + * @param {groupproto.ISenderChainKey} message SenderChainKey message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderChainKey.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderChainKey message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderChainKey + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderChainKey} SenderChainKey + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderChainKey.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderChainKey(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.iteration = reader.uint32(); + break; + case 2: + message.seed = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderChainKey message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderChainKey + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderChainKey} SenderChainKey + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderChainKey.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderChainKey message. + * @function verify + * @memberof groupproto.SenderChainKey + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderChainKey.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.iteration != null && message.hasOwnProperty("iteration")) + if (!$util.isInteger(message.iteration)) + return "iteration: integer expected"; + if (message.seed != null && message.hasOwnProperty("seed")) + if (!(message.seed && typeof message.seed.length === "number" || $util.isString(message.seed))) + return "seed: buffer expected"; + return null; + }; + + /** + * Creates a SenderChainKey message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderChainKey + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderChainKey} SenderChainKey + */ + SenderChainKey.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderChainKey) + return object; + var message = new $root.groupproto.SenderChainKey(); + if (object.iteration != null) + message.iteration = object.iteration >>> 0; + if (object.seed != null) + if (typeof object.seed === "string") + $util.base64.decode(object.seed, message.seed = $util.newBuffer($util.base64.length(object.seed)), 0); + else if (object.seed.length) + message.seed = object.seed; + return message; + }; + + /** + * Creates a plain object from a SenderChainKey message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderChainKey + * @static + * @param {groupproto.SenderChainKey} message SenderChainKey + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderChainKey.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.iteration = 0; + if (options.bytes === String) + object.seed = ""; + else { + object.seed = []; + if (options.bytes !== Array) + object.seed = $util.newBuffer(object.seed); + } + } + if (message.iteration != null && message.hasOwnProperty("iteration")) + object.iteration = message.iteration; + if (message.seed != null && message.hasOwnProperty("seed")) + object.seed = options.bytes === String ? $util.base64.encode(message.seed, 0, message.seed.length) : options.bytes === Array ? Array.prototype.slice.call(message.seed) : message.seed; + return object; + }; + + /** + * Converts this SenderChainKey to JSON. + * @function toJSON + * @memberof groupproto.SenderChainKey + * @instance + * @returns {Object.} JSON object + */ + SenderChainKey.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderChainKey; + })(); + + groupproto.SenderMessageKey = (function() { + + /** + * Properties of a SenderMessageKey. + * @memberof groupproto + * @interface ISenderMessageKey + * @property {number|null} [iteration] SenderMessageKey iteration + * @property {Uint8Array|null} [seed] SenderMessageKey seed + */ + + /** + * Constructs a new SenderMessageKey. + * @memberof groupproto + * @classdesc Represents a SenderMessageKey. + * @implements ISenderMessageKey + * @constructor + * @param {groupproto.ISenderMessageKey=} [properties] Properties to set + */ + function SenderMessageKey(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderMessageKey iteration. + * @member {number} iteration + * @memberof groupproto.SenderMessageKey + * @instance + */ + SenderMessageKey.prototype.iteration = 0; + + /** + * SenderMessageKey seed. + * @member {Uint8Array} seed + * @memberof groupproto.SenderMessageKey + * @instance + */ + SenderMessageKey.prototype.seed = $util.newBuffer([]); + + /** + * Creates a new SenderMessageKey instance using the specified properties. + * @function create + * @memberof groupproto.SenderMessageKey + * @static + * @param {groupproto.ISenderMessageKey=} [properties] Properties to set + * @returns {groupproto.SenderMessageKey} SenderMessageKey instance + */ + SenderMessageKey.create = function create(properties) { + return new SenderMessageKey(properties); + }; + + /** + * Encodes the specified SenderMessageKey message. Does not implicitly {@link groupproto.SenderMessageKey.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderMessageKey + * @static + * @param {groupproto.ISenderMessageKey} message SenderMessageKey message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderMessageKey.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.iteration); + if (message.seed != null && Object.hasOwnProperty.call(message, "seed")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.seed); + return writer; + }; + + /** + * Encodes the specified SenderMessageKey message, length delimited. Does not implicitly {@link groupproto.SenderMessageKey.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderMessageKey + * @static + * @param {groupproto.ISenderMessageKey} message SenderMessageKey message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderMessageKey.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderMessageKey message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderMessageKey + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderMessageKey} SenderMessageKey + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderMessageKey.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderMessageKey(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.iteration = reader.uint32(); + break; + case 2: + message.seed = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderMessageKey message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderMessageKey + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderMessageKey} SenderMessageKey + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderMessageKey.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderMessageKey message. + * @function verify + * @memberof groupproto.SenderMessageKey + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderMessageKey.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.iteration != null && message.hasOwnProperty("iteration")) + if (!$util.isInteger(message.iteration)) + return "iteration: integer expected"; + if (message.seed != null && message.hasOwnProperty("seed")) + if (!(message.seed && typeof message.seed.length === "number" || $util.isString(message.seed))) + return "seed: buffer expected"; + return null; + }; + + /** + * Creates a SenderMessageKey message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderMessageKey + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderMessageKey} SenderMessageKey + */ + SenderMessageKey.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderMessageKey) + return object; + var message = new $root.groupproto.SenderMessageKey(); + if (object.iteration != null) + message.iteration = object.iteration >>> 0; + if (object.seed != null) + if (typeof object.seed === "string") + $util.base64.decode(object.seed, message.seed = $util.newBuffer($util.base64.length(object.seed)), 0); + else if (object.seed.length) + message.seed = object.seed; + return message; + }; + + /** + * Creates a plain object from a SenderMessageKey message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderMessageKey + * @static + * @param {groupproto.SenderMessageKey} message SenderMessageKey + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderMessageKey.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.iteration = 0; + if (options.bytes === String) + object.seed = ""; + else { + object.seed = []; + if (options.bytes !== Array) + object.seed = $util.newBuffer(object.seed); + } + } + if (message.iteration != null && message.hasOwnProperty("iteration")) + object.iteration = message.iteration; + if (message.seed != null && message.hasOwnProperty("seed")) + object.seed = options.bytes === String ? $util.base64.encode(message.seed, 0, message.seed.length) : options.bytes === Array ? Array.prototype.slice.call(message.seed) : message.seed; + return object; + }; + + /** + * Converts this SenderMessageKey to JSON. + * @function toJSON + * @memberof groupproto.SenderMessageKey + * @instance + * @returns {Object.} JSON object + */ + SenderMessageKey.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderMessageKey; + })(); + + groupproto.SenderSigningKey = (function() { + + /** + * Properties of a SenderSigningKey. + * @memberof groupproto + * @interface ISenderSigningKey + * @property {Uint8Array|null} ["public"] SenderSigningKey public + * @property {Uint8Array|null} ["private"] SenderSigningKey private + */ + + /** + * Constructs a new SenderSigningKey. + * @memberof groupproto + * @classdesc Represents a SenderSigningKey. + * @implements ISenderSigningKey + * @constructor + * @param {groupproto.ISenderSigningKey=} [properties] Properties to set + */ + function SenderSigningKey(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderSigningKey public. + * @member {Uint8Array} public + * @memberof groupproto.SenderSigningKey + * @instance + */ + SenderSigningKey.prototype["public"] = $util.newBuffer([]); + + /** + * SenderSigningKey private. + * @member {Uint8Array} private + * @memberof groupproto.SenderSigningKey + * @instance + */ + SenderSigningKey.prototype["private"] = $util.newBuffer([]); + + /** + * Creates a new SenderSigningKey instance using the specified properties. + * @function create + * @memberof groupproto.SenderSigningKey + * @static + * @param {groupproto.ISenderSigningKey=} [properties] Properties to set + * @returns {groupproto.SenderSigningKey} SenderSigningKey instance + */ + SenderSigningKey.create = function create(properties) { + return new SenderSigningKey(properties); + }; + + /** + * Encodes the specified SenderSigningKey message. Does not implicitly {@link groupproto.SenderSigningKey.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderSigningKey + * @static + * @param {groupproto.ISenderSigningKey} message SenderSigningKey message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderSigningKey.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message["public"] != null && Object.hasOwnProperty.call(message, "public")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message["public"]); + if (message["private"] != null && Object.hasOwnProperty.call(message, "private")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message["private"]); + return writer; + }; + + /** + * Encodes the specified SenderSigningKey message, length delimited. Does not implicitly {@link groupproto.SenderSigningKey.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderSigningKey + * @static + * @param {groupproto.ISenderSigningKey} message SenderSigningKey message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderSigningKey.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderSigningKey message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderSigningKey + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderSigningKey} SenderSigningKey + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderSigningKey.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderSigningKey(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message["public"] = reader.bytes(); + break; + case 2: + message["private"] = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderSigningKey message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderSigningKey + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderSigningKey} SenderSigningKey + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderSigningKey.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderSigningKey message. + * @function verify + * @memberof groupproto.SenderSigningKey + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderSigningKey.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message["public"] != null && message.hasOwnProperty("public")) + if (!(message["public"] && typeof message["public"].length === "number" || $util.isString(message["public"]))) + return "public: buffer expected"; + if (message["private"] != null && message.hasOwnProperty("private")) + if (!(message["private"] && typeof message["private"].length === "number" || $util.isString(message["private"]))) + return "private: buffer expected"; + return null; + }; + + /** + * Creates a SenderSigningKey message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderSigningKey + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderSigningKey} SenderSigningKey + */ + SenderSigningKey.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderSigningKey) + return object; + var message = new $root.groupproto.SenderSigningKey(); + if (object["public"] != null) + if (typeof object["public"] === "string") + $util.base64.decode(object["public"], message["public"] = $util.newBuffer($util.base64.length(object["public"])), 0); + else if (object["public"].length) + message["public"] = object["public"]; + if (object["private"] != null) + if (typeof object["private"] === "string") + $util.base64.decode(object["private"], message["private"] = $util.newBuffer($util.base64.length(object["private"])), 0); + else if (object["private"].length) + message["private"] = object["private"]; + return message; + }; + + /** + * Creates a plain object from a SenderSigningKey message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderSigningKey + * @static + * @param {groupproto.SenderSigningKey} message SenderSigningKey + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderSigningKey.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object["public"] = ""; + else { + object["public"] = []; + if (options.bytes !== Array) + object["public"] = $util.newBuffer(object["public"]); + } + if (options.bytes === String) + object["private"] = ""; + else { + object["private"] = []; + if (options.bytes !== Array) + object["private"] = $util.newBuffer(object["private"]); + } + } + if (message["public"] != null && message.hasOwnProperty("public")) + object["public"] = options.bytes === String ? $util.base64.encode(message["public"], 0, message["public"].length) : options.bytes === Array ? Array.prototype.slice.call(message["public"]) : message["public"]; + if (message["private"] != null && message.hasOwnProperty("private")) + object["private"] = options.bytes === String ? $util.base64.encode(message["private"], 0, message["private"].length) : options.bytes === Array ? Array.prototype.slice.call(message["private"]) : message["private"]; + return object; + }; + + /** + * Converts this SenderSigningKey to JSON. + * @function toJSON + * @memberof groupproto.SenderSigningKey + * @instance + * @returns {Object.} JSON object + */ + SenderSigningKey.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderSigningKey; + })(); + + groupproto.SenderKeyStateStructure = (function() { + + /** + * Properties of a SenderKeyStateStructure. + * @memberof groupproto + * @interface ISenderKeyStateStructure + * @property {number|null} [senderKeyId] SenderKeyStateStructure senderKeyId + * @property {groupproto.ISenderChainKey|null} [senderChainKey] SenderKeyStateStructure senderChainKey + * @property {groupproto.ISenderSigningKey|null} [senderSigningKey] SenderKeyStateStructure senderSigningKey + * @property {Array.|null} [senderMessageKeys] SenderKeyStateStructure senderMessageKeys + */ + + /** + * Constructs a new SenderKeyStateStructure. + * @memberof groupproto + * @classdesc Represents a SenderKeyStateStructure. + * @implements ISenderKeyStateStructure + * @constructor + * @param {groupproto.ISenderKeyStateStructure=} [properties] Properties to set + */ + function SenderKeyStateStructure(properties) { + this.senderMessageKeys = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderKeyStateStructure senderKeyId. + * @member {number} senderKeyId + * @memberof groupproto.SenderKeyStateStructure + * @instance + */ + SenderKeyStateStructure.prototype.senderKeyId = 0; + + /** + * SenderKeyStateStructure senderChainKey. + * @member {groupproto.ISenderChainKey|null|undefined} senderChainKey + * @memberof groupproto.SenderKeyStateStructure + * @instance + */ + SenderKeyStateStructure.prototype.senderChainKey = null; + + /** + * SenderKeyStateStructure senderSigningKey. + * @member {groupproto.ISenderSigningKey|null|undefined} senderSigningKey + * @memberof groupproto.SenderKeyStateStructure + * @instance + */ + SenderKeyStateStructure.prototype.senderSigningKey = null; + + /** + * SenderKeyStateStructure senderMessageKeys. + * @member {Array.} senderMessageKeys + * @memberof groupproto.SenderKeyStateStructure + * @instance + */ + SenderKeyStateStructure.prototype.senderMessageKeys = $util.emptyArray; + + /** + * Creates a new SenderKeyStateStructure instance using the specified properties. + * @function create + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {groupproto.ISenderKeyStateStructure=} [properties] Properties to set + * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure instance + */ + SenderKeyStateStructure.create = function create(properties) { + return new SenderKeyStateStructure(properties); + }; + + /** + * Encodes the specified SenderKeyStateStructure message. Does not implicitly {@link groupproto.SenderKeyStateStructure.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {groupproto.ISenderKeyStateStructure} message SenderKeyStateStructure message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyStateStructure.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.senderKeyId != null && Object.hasOwnProperty.call(message, "senderKeyId")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.senderKeyId); + if (message.senderChainKey != null && Object.hasOwnProperty.call(message, "senderChainKey")) + $root.groupproto.SenderChainKey.encode(message.senderChainKey, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.senderSigningKey != null && Object.hasOwnProperty.call(message, "senderSigningKey")) + $root.groupproto.SenderSigningKey.encode(message.senderSigningKey, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + if (message.senderMessageKeys != null && message.senderMessageKeys.length) + for (var i = 0; i < message.senderMessageKeys.length; ++i) + $root.groupproto.SenderMessageKey.encode(message.senderMessageKeys[i], writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified SenderKeyStateStructure message, length delimited. Does not implicitly {@link groupproto.SenderKeyStateStructure.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {groupproto.ISenderKeyStateStructure} message SenderKeyStateStructure message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyStateStructure.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderKeyStateStructure message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyStateStructure.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyStateStructure(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.senderKeyId = reader.uint32(); + break; + case 2: + message.senderChainKey = $root.groupproto.SenderChainKey.decode(reader, reader.uint32()); + break; + case 3: + message.senderSigningKey = $root.groupproto.SenderSigningKey.decode(reader, reader.uint32()); + break; + case 4: + if (!(message.senderMessageKeys && message.senderMessageKeys.length)) + message.senderMessageKeys = []; + message.senderMessageKeys.push($root.groupproto.SenderMessageKey.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderKeyStateStructure message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyStateStructure.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderKeyStateStructure message. + * @function verify + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderKeyStateStructure.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.senderKeyId != null && message.hasOwnProperty("senderKeyId")) + if (!$util.isInteger(message.senderKeyId)) + return "senderKeyId: integer expected"; + if (message.senderChainKey != null && message.hasOwnProperty("senderChainKey")) { + var error = $root.groupproto.SenderChainKey.verify(message.senderChainKey); + if (error) + return "senderChainKey." + error; + } + if (message.senderSigningKey != null && message.hasOwnProperty("senderSigningKey")) { + var error = $root.groupproto.SenderSigningKey.verify(message.senderSigningKey); + if (error) + return "senderSigningKey." + error; + } + if (message.senderMessageKeys != null && message.hasOwnProperty("senderMessageKeys")) { + if (!Array.isArray(message.senderMessageKeys)) + return "senderMessageKeys: array expected"; + for (var i = 0; i < message.senderMessageKeys.length; ++i) { + var error = $root.groupproto.SenderMessageKey.verify(message.senderMessageKeys[i]); + if (error) + return "senderMessageKeys." + error; + } + } + return null; + }; + + /** + * Creates a SenderKeyStateStructure message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure + */ + SenderKeyStateStructure.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderKeyStateStructure) + return object; + var message = new $root.groupproto.SenderKeyStateStructure(); + if (object.senderKeyId != null) + message.senderKeyId = object.senderKeyId >>> 0; + if (object.senderChainKey != null) { + if (typeof object.senderChainKey !== "object") + throw TypeError(".groupproto.SenderKeyStateStructure.senderChainKey: object expected"); + message.senderChainKey = $root.groupproto.SenderChainKey.fromObject(object.senderChainKey); + } + if (object.senderSigningKey != null) { + if (typeof object.senderSigningKey !== "object") + throw TypeError(".groupproto.SenderKeyStateStructure.senderSigningKey: object expected"); + message.senderSigningKey = $root.groupproto.SenderSigningKey.fromObject(object.senderSigningKey); + } + if (object.senderMessageKeys) { + if (!Array.isArray(object.senderMessageKeys)) + throw TypeError(".groupproto.SenderKeyStateStructure.senderMessageKeys: array expected"); + message.senderMessageKeys = []; + for (var i = 0; i < object.senderMessageKeys.length; ++i) { + if (typeof object.senderMessageKeys[i] !== "object") + throw TypeError(".groupproto.SenderKeyStateStructure.senderMessageKeys: object expected"); + message.senderMessageKeys[i] = $root.groupproto.SenderMessageKey.fromObject(object.senderMessageKeys[i]); + } + } + return message; + }; + + /** + * Creates a plain object from a SenderKeyStateStructure message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderKeyStateStructure + * @static + * @param {groupproto.SenderKeyStateStructure} message SenderKeyStateStructure + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderKeyStateStructure.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.senderMessageKeys = []; + if (options.defaults) { + object.senderKeyId = 0; + object.senderChainKey = null; + object.senderSigningKey = null; + } + if (message.senderKeyId != null && message.hasOwnProperty("senderKeyId")) + object.senderKeyId = message.senderKeyId; + if (message.senderChainKey != null && message.hasOwnProperty("senderChainKey")) + object.senderChainKey = $root.groupproto.SenderChainKey.toObject(message.senderChainKey, options); + if (message.senderSigningKey != null && message.hasOwnProperty("senderSigningKey")) + object.senderSigningKey = $root.groupproto.SenderSigningKey.toObject(message.senderSigningKey, options); + if (message.senderMessageKeys && message.senderMessageKeys.length) { + object.senderMessageKeys = []; + for (var j = 0; j < message.senderMessageKeys.length; ++j) + object.senderMessageKeys[j] = $root.groupproto.SenderMessageKey.toObject(message.senderMessageKeys[j], options); + } + return object; + }; + + /** + * Converts this SenderKeyStateStructure to JSON. + * @function toJSON + * @memberof groupproto.SenderKeyStateStructure + * @instance + * @returns {Object.} JSON object + */ + SenderKeyStateStructure.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderKeyStateStructure; + })(); + + groupproto.SenderKeyRecordStructure = (function() { + + /** + * Properties of a SenderKeyRecordStructure. + * @memberof groupproto + * @interface ISenderKeyRecordStructure + * @property {Array.|null} [senderKeyStates] SenderKeyRecordStructure senderKeyStates + */ + + /** + * Constructs a new SenderKeyRecordStructure. + * @memberof groupproto + * @classdesc Represents a SenderKeyRecordStructure. + * @implements ISenderKeyRecordStructure + * @constructor + * @param {groupproto.ISenderKeyRecordStructure=} [properties] Properties to set + */ + function SenderKeyRecordStructure(properties) { + this.senderKeyStates = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SenderKeyRecordStructure senderKeyStates. + * @member {Array.} senderKeyStates + * @memberof groupproto.SenderKeyRecordStructure + * @instance + */ + SenderKeyRecordStructure.prototype.senderKeyStates = $util.emptyArray; + + /** + * Creates a new SenderKeyRecordStructure instance using the specified properties. + * @function create + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {groupproto.ISenderKeyRecordStructure=} [properties] Properties to set + * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure instance + */ + SenderKeyRecordStructure.create = function create(properties) { + return new SenderKeyRecordStructure(properties); + }; + + /** + * Encodes the specified SenderKeyRecordStructure message. Does not implicitly {@link groupproto.SenderKeyRecordStructure.verify|verify} messages. + * @function encode + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {groupproto.ISenderKeyRecordStructure} message SenderKeyRecordStructure message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyRecordStructure.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.senderKeyStates != null && message.senderKeyStates.length) + for (var i = 0; i < message.senderKeyStates.length; ++i) + $root.groupproto.SenderKeyStateStructure.encode(message.senderKeyStates[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified SenderKeyRecordStructure message, length delimited. Does not implicitly {@link groupproto.SenderKeyRecordStructure.verify|verify} messages. + * @function encodeDelimited + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {groupproto.ISenderKeyRecordStructure} message SenderKeyRecordStructure message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SenderKeyRecordStructure.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SenderKeyRecordStructure message from the specified reader or buffer. + * @function decode + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyRecordStructure.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyRecordStructure(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.senderKeyStates && message.senderKeyStates.length)) + message.senderKeyStates = []; + message.senderKeyStates.push($root.groupproto.SenderKeyStateStructure.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SenderKeyRecordStructure message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SenderKeyRecordStructure.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SenderKeyRecordStructure message. + * @function verify + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SenderKeyRecordStructure.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.senderKeyStates != null && message.hasOwnProperty("senderKeyStates")) { + if (!Array.isArray(message.senderKeyStates)) + return "senderKeyStates: array expected"; + for (var i = 0; i < message.senderKeyStates.length; ++i) { + var error = $root.groupproto.SenderKeyStateStructure.verify(message.senderKeyStates[i]); + if (error) + return "senderKeyStates." + error; + } + } + return null; + }; + + /** + * Creates a SenderKeyRecordStructure message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {Object.} object Plain object + * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure + */ + SenderKeyRecordStructure.fromObject = function fromObject(object) { + if (object instanceof $root.groupproto.SenderKeyRecordStructure) + return object; + var message = new $root.groupproto.SenderKeyRecordStructure(); + if (object.senderKeyStates) { + if (!Array.isArray(object.senderKeyStates)) + throw TypeError(".groupproto.SenderKeyRecordStructure.senderKeyStates: array expected"); + message.senderKeyStates = []; + for (var i = 0; i < object.senderKeyStates.length; ++i) { + if (typeof object.senderKeyStates[i] !== "object") + throw TypeError(".groupproto.SenderKeyRecordStructure.senderKeyStates: object expected"); + message.senderKeyStates[i] = $root.groupproto.SenderKeyStateStructure.fromObject(object.senderKeyStates[i]); + } + } + return message; + }; + + /** + * Creates a plain object from a SenderKeyRecordStructure message. Also converts values to other types if specified. + * @function toObject + * @memberof groupproto.SenderKeyRecordStructure + * @static + * @param {groupproto.SenderKeyRecordStructure} message SenderKeyRecordStructure + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SenderKeyRecordStructure.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.senderKeyStates = []; + if (message.senderKeyStates && message.senderKeyStates.length) { + object.senderKeyStates = []; + for (var j = 0; j < message.senderKeyStates.length; ++j) + object.senderKeyStates[j] = $root.groupproto.SenderKeyStateStructure.toObject(message.senderKeyStates[j], options); + } + return object; + }; + + /** + * Converts this SenderKeyRecordStructure to JSON. + * @function toJSON + * @memberof groupproto.SenderKeyRecordStructure + * @instance + * @returns {Object.} JSON object + */ + SenderKeyRecordStructure.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SenderKeyRecordStructure; + })(); + + return groupproto; +})(); + +module.exports = $root; diff --git a/WASignalGroup/ciphertext_message.js b/WASignalGroup/ciphertext_message.js new file mode 100644 index 0000000..f9bb771 --- /dev/null +++ b/WASignalGroup/ciphertext_message.js @@ -0,0 +1,16 @@ +class CiphertextMessage { + UNSUPPORTED_VERSION = 1; + + CURRENT_VERSION = 3; + + WHISPER_TYPE = 2; + + PREKEY_TYPE = 3; + + SENDERKEY_TYPE = 4; + + SENDERKEY_DISTRIBUTION_TYPE = 5; + + ENCRYPTED_MESSAGE_OVERHEAD = 53; +} +module.exports = CiphertextMessage; \ No newline at end of file diff --git a/WASignalGroup/generate-proto.sh b/WASignalGroup/generate-proto.sh new file mode 100644 index 0000000..051876d --- /dev/null +++ b/WASignalGroup/generate-proto.sh @@ -0,0 +1 @@ +yarn pbjs -t static-module -w commonjs -o ./WASignalGroup/GroupProtocol.js ./WASignalGroup/group.proto \ No newline at end of file diff --git a/WASignalGroup/group.proto b/WASignalGroup/group.proto new file mode 100644 index 0000000..e6f549b --- /dev/null +++ b/WASignalGroup/group.proto @@ -0,0 +1,42 @@ +package groupproto; + +message SenderKeyMessage { + optional uint32 id = 1; + optional uint32 iteration = 2; + optional bytes ciphertext = 3; + } + + message SenderKeyDistributionMessage { + optional uint32 id = 1; + optional uint32 iteration = 2; + optional bytes chainKey = 3; + optional bytes signingKey = 4; + } + + message SenderChainKey { + optional uint32 iteration = 1; + optional bytes seed = 2; +} + +message SenderMessageKey { + optional uint32 iteration = 1; + optional bytes seed = 2; +} + +message SenderSigningKey { + optional bytes public = 1; + optional bytes private = 2; +} + +message SenderKeyStateStructure { + + + optional uint32 senderKeyId = 1; + optional SenderChainKey senderChainKey = 2; + optional SenderSigningKey senderSigningKey = 3; + repeated SenderMessageKey senderMessageKeys = 4; +} + +message SenderKeyRecordStructure { + repeated SenderKeyStateStructure senderKeyStates = 1; +} \ No newline at end of file diff --git a/WASignalGroup/group_cipher.js b/WASignalGroup/group_cipher.js new file mode 100644 index 0000000..a10de25 --- /dev/null +++ b/WASignalGroup/group_cipher.js @@ -0,0 +1,106 @@ +const SenderKeyMessage = require('./sender_key_message'); +const crypto = require('libsignal/src/crypto'); + +class GroupCipher { + constructor(senderKeyStore, senderKeyName) { + this.senderKeyStore = senderKeyStore; + this.senderKeyName = senderKeyName; + } + + async encrypt(paddedPlaintext) { + try { + const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName); + const senderKeyState = record.getSenderKeyState(); + const senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey(); + + const ciphertext = await this.getCipherText( + senderKey.getIv(), + senderKey.getCipherKey(), + paddedPlaintext + ); + + const senderKeyMessage = new SenderKeyMessage( + senderKeyState.getKeyId(), + senderKey.getIteration(), + ciphertext, + senderKeyState.getSigningKeyPrivate() + ); + senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext()); + await this.senderKeyStore.storeSenderKey(this.senderKeyName, record); + return senderKeyMessage.serialize(); + } catch (e) { + //console.log(e.stack); + throw new Error('NoSessionException'); + } + } + + async decrypt(senderKeyMessageBytes) { + const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName); + if (!record) throw new Error(`No sender key for: ${this.senderKeyName}`); + + const senderKeyMessage = new SenderKeyMessage(null, null, null, null, senderKeyMessageBytes); + + const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); + //senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); + const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration()); + // senderKeyState.senderKeyStateStructure.senderSigningKey.private = + + const plaintext = await this.getPlainText( + senderKey.getIv(), + senderKey.getCipherKey(), + senderKeyMessage.getCipherText() + ); + + await this.senderKeyStore.storeSenderKey(this.senderKeyName, record); + + return plaintext; + } + + getSenderKey(senderKeyState, iteration) { + let senderChainKey = senderKeyState.getSenderChainKey(); + if (senderChainKey.getIteration() > iteration) { + if (senderKeyState.hasSenderMessageKey(iteration)) { + return senderKeyState.removeSenderMessageKey(iteration); + } + throw new Error( + `Received message with old counter: ${senderChainKey.getIteration()}, ${iteration}` + ); + } + + if (senderChainKey.getIteration() - iteration > 2000) { + throw new Error('Over 2000 messages into the future!'); + } + + while (senderChainKey.getIteration() < iteration) { + senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()); + senderChainKey = senderChainKey.getNext(); + } + + senderKeyState.setSenderChainKey(senderChainKey.getNext()); + return senderChainKey.getSenderMessageKey(); + } + + getPlainText(iv, key, ciphertext) { + try { + const plaintext = crypto.decrypt(key, ciphertext, iv); + return plaintext; + } catch (e) { + //console.log(e.stack); + throw new Error('InvalidMessageException'); + } + } + + getCipherText(iv, key, plaintext) { + try { + iv = typeof iv === 'string' ? Buffer.from(iv, 'base64') : iv; + key = typeof key === 'string' ? Buffer.from(key, 'base64') : key; + const crypted = crypto.encrypt(key, Buffer.from(plaintext), iv); + return crypted; + } catch (e) { + //console.log(e.stack); + throw new Error('InvalidMessageException'); + } + } +} + +module.exports = GroupCipher; \ No newline at end of file diff --git a/WASignalGroup/group_session_builder.js b/WASignalGroup/group_session_builder.js new file mode 100644 index 0000000..24c84a2 --- /dev/null +++ b/WASignalGroup/group_session_builder.js @@ -0,0 +1,46 @@ +//const utils = require('../../common/utils'); +const SenderKeyDistributionMessage = require('./sender_key_distribution_message'); + +const keyhelper = require("./keyhelper"); +class GroupSessionBuilder { + constructor(senderKeyStore) { + this.senderKeyStore = senderKeyStore; + } + + async process(senderKeyName, senderKeyDistributionMessage) { + //console.log('GroupSessionBuilder process', senderKeyName, senderKeyDistributionMessage); + const senderKeyRecord = await this.senderKeyStore.loadSenderKey(senderKeyName); + senderKeyRecord.addSenderKeyState( + senderKeyDistributionMessage.getId(), + senderKeyDistributionMessage.getIteration(), + senderKeyDistributionMessage.getChainKey(), + senderKeyDistributionMessage.getSignatureKey() + ); + await this.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); + } + + // [{"senderKeyId":1742199468,"senderChainKey":{"iteration":0,"seed":"yxMY9VFQcXEP34olRAcGCtsgx1XoKsHfDIh+1ea4HAQ="},"senderSigningKey":{"public":""}}] + async create(senderKeyName) { + const senderKeyRecord = await this.senderKeyStore.loadSenderKey(senderKeyName); + //console.log('GroupSessionBuilder create session', senderKeyName, senderKeyRecord); + + if (senderKeyRecord.isEmpty()) { + const keyId = keyhelper.generateSenderKeyId(); + const senderKey = keyhelper.generateSenderKey(); + const signingKey = keyhelper.generateSenderSigningKey(); + + senderKeyRecord.setSenderKeyState(keyId, 0, senderKey, signingKey); + await this.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); + } + + const state = senderKeyRecord.getSenderKeyState(); + + return new SenderKeyDistributionMessage( + state.getKeyId(), + state.getSenderChainKey().getIteration(), + state.getSenderChainKey().getSeed(), + state.getSigningKeyPublic() + ); + } +} +module.exports = GroupSessionBuilder; \ No newline at end of file diff --git a/WASignalGroup/index.js b/WASignalGroup/index.js new file mode 100644 index 0000000..69b935d --- /dev/null +++ b/WASignalGroup/index.js @@ -0,0 +1,5 @@ +module.exports.GroupSessionBuilder = require('./group_session_builder') +module.exports.SenderKeyDistributionMessage = require('./sender_key_distribution_message') +module.exports.SenderKeyRecord = require('./sender_key_record') +module.exports.SenderKeyName = require('./sender_key_name') +module.exports.GroupCipher = require('./group_cipher') \ No newline at end of file diff --git a/WASignalGroup/keyhelper.js b/WASignalGroup/keyhelper.js new file mode 100644 index 0000000..a244581 --- /dev/null +++ b/WASignalGroup/keyhelper.js @@ -0,0 +1,21 @@ +const curve = require('libsignal/src/curve'); +const nodeCrypto = require('crypto'); + +exports.generateSenderKey = function() { + return nodeCrypto.randomBytes(32); +} + +exports.generateSenderKeyId = function() { + return nodeCrypto.randomInt(2147483647); +} + +exports.generateSenderSigningKey = function(key) { + if (!key) { + key = curve.generateKeyPair(); + } + + return { + public: key.pubKey, + private: key.privKey, + }; +} diff --git a/WASignalGroup/protobufs.js b/WASignalGroup/protobufs.js new file mode 100644 index 0000000..76daa5c --- /dev/null +++ b/WASignalGroup/protobufs.js @@ -0,0 +1,3 @@ +const { groupproto } = require('./GroupProtocol') + +module.exports = groupproto \ No newline at end of file diff --git a/WASignalGroup/readme.md b/WASignalGroup/readme.md new file mode 100644 index 0000000..8290d9c --- /dev/null +++ b/WASignalGroup/readme.md @@ -0,0 +1,6 @@ +# Signal-Group + +This contains the code to decrypt/encrypt WA group messages. +Originally from [pokearaujo/libsignal-node](https://github.com/pokearaujo/libsignal-node) + +The code has been moved outside the signal package as I felt it didn't belong in ths signal package, as it isn't inherently a part of signal but of WA. \ No newline at end of file diff --git a/WASignalGroup/sender_chain_key.js b/WASignalGroup/sender_chain_key.js new file mode 100644 index 0000000..9a3125b --- /dev/null +++ b/WASignalGroup/sender_chain_key.js @@ -0,0 +1,50 @@ +const SenderMessageKey = require('./sender_message_key'); +//const HKDF = require('./hkdf'); +const crypto = require('libsignal/src/crypto'); + +class SenderChainKey { + MESSAGE_KEY_SEED = Buffer.from([0x01]); + + CHAIN_KEY_SEED = Buffer.from([0x02]); + + iteration = 0; + + chainKey = Buffer.alloc(0); + + constructor(iteration, chainKey) { + this.iteration = iteration; + this.chainKey = chainKey; + } + + getIteration() { + return this.iteration; + } + + getSenderMessageKey() { + return new SenderMessageKey( + this.iteration, + this.getDerivative(this.MESSAGE_KEY_SEED, this.chainKey) + ); + } + + getNext() { + return new SenderChainKey( + this.iteration + 1, + this.getDerivative(this.CHAIN_KEY_SEED, this.chainKey) + ); + } + + getSeed() { + return typeof this.chainKey === 'string' ? Buffer.from(this.chainKey, 'base64') : this.chainKey; + } + + getDerivative(seed, key) { + key = typeof key === 'string' ? Buffer.from(key, 'base64') : key; + const hash = crypto.calculateMAC(key, seed); + //const hash = new Hash().hmac_hash(key, seed, 'sha256', ''); + + return hash; + } +} + +module.exports = SenderChainKey; \ No newline at end of file diff --git a/WASignalGroup/sender_key_distribution_message.js b/WASignalGroup/sender_key_distribution_message.js new file mode 100644 index 0000000..9c1400f --- /dev/null +++ b/WASignalGroup/sender_key_distribution_message.js @@ -0,0 +1,78 @@ +const CiphertextMessage = require('./ciphertext_message'); +const protobufs = require('./protobufs'); + +class SenderKeyDistributionMessage extends CiphertextMessage { + constructor( + id = null, + iteration = null, + chainKey = null, + signatureKey = null, + serialized = null + ) { + super(); + if (serialized) { + try { + const version = serialized[0]; + const message = serialized.slice(1); + + const distributionMessage = protobufs.SenderKeyDistributionMessage.decode( + message + ).toJSON(); + this.serialized = serialized; + this.id = distributionMessage.id; + this.iteration = distributionMessage.iteration; + this.chainKey = distributionMessage.chainKey; + this.signatureKey = distributionMessage.signingKey; + } catch (e) { + throw new Error(e); + } + } else { + const version = this.intsToByteHighAndLow(this.CURRENT_VERSION, this.CURRENT_VERSION); + this.id = id; + this.iteration = iteration; + this.chainKey = chainKey; + this.signatureKey = signatureKey; + const message = protobufs.SenderKeyDistributionMessage.encode( + protobufs.SenderKeyDistributionMessage.create({ + id, + iteration, + chainKey, + signingKey: this.signatureKey, + }) + ).finish(); + this.serialized = Buffer.concat([Buffer.from([version]), message]); + } + } + + intsToByteHighAndLow(highValue, lowValue) { + return (((highValue << 4) | lowValue) & 0xff) % 256; + } + + serialize() { + return this.serialized; + } + + getType() { + return this.SENDERKEY_DISTRIBUTION_TYPE; + } + + getIteration() { + return this.iteration; + } + + getChainKey() { + return typeof this.chainKey === 'string' ? Buffer.from(this.chainKey, 'base64') : this.chainKey; + } + + getSignatureKey() { + return typeof this.signatureKey === 'string' + ? Buffer.from(this.signatureKey, 'base64') + : this.signatureKey; + } + + getId() { + return this.id; + } +} + +module.exports = SenderKeyDistributionMessage; \ No newline at end of file diff --git a/WASignalGroup/sender_key_message.js b/WASignalGroup/sender_key_message.js new file mode 100644 index 0000000..80683a6 --- /dev/null +++ b/WASignalGroup/sender_key_message.js @@ -0,0 +1,92 @@ +const CiphertextMessage = require('./ciphertext_message'); +const curve = require('libsignal/src/curve'); +const protobufs = require('./protobufs'); + +class SenderKeyMessage extends CiphertextMessage { + SIGNATURE_LENGTH = 64; + + constructor( + keyId = null, + iteration = null, + ciphertext = null, + signatureKey = null, + serialized = null + ) { + super(); + if (serialized) { + const version = serialized[0]; + const message = serialized.slice(1, serialized.length - this.SIGNATURE_LENGTH); + const signature = serialized.slice(-1 * this.SIGNATURE_LENGTH); + const senderKeyMessage = protobufs.SenderKeyMessage.decode(message).toJSON(); + senderKeyMessage.ciphertext = Buffer.from(senderKeyMessage.ciphertext, 'base64'); + + this.serialized = serialized; + this.messageVersion = (version & 0xff) >> 4; + + this.keyId = senderKeyMessage.id; + this.iteration = senderKeyMessage.iteration; + this.ciphertext = senderKeyMessage.ciphertext; + this.signature = signature; + } else { + const version = (((this.CURRENT_VERSION << 4) | this.CURRENT_VERSION) & 0xff) % 256; + ciphertext = Buffer.from(ciphertext); // .toString('base64'); + const message = protobufs.SenderKeyMessage.encode( + protobufs.SenderKeyMessage.create({ + id: keyId, + iteration, + ciphertext, + }) + ).finish(); + + const signature = this.getSignature( + signatureKey, + Buffer.concat([Buffer.from([version]), message]) + ); + this.serialized = Buffer.concat([Buffer.from([version]), message, Buffer.from(signature)]); + this.messageVersion = this.CURRENT_VERSION; + this.keyId = keyId; + this.iteration = iteration; + this.ciphertext = ciphertext; + this.signature = signature; + } + } + + getKeyId() { + return this.keyId; + } + + getIteration() { + return this.iteration; + } + + getCipherText() { + return this.ciphertext; + } + + verifySignature(signatureKey) { + const part1 = this.serialized.slice(0, this.serialized.length - this.SIGNATURE_LENGTH + 1); + const part2 = this.serialized.slice(-1 * this.SIGNATURE_LENGTH); + const res = curve.verifySignature(signatureKey, part1, part2); + if (!res) throw new Error('Invalid signature!'); + } + + getSignature(signatureKey, serialized) { + const signature = Buffer.from( + curve.calculateSignature( + signatureKey, + serialized + ) + ); + return signature; + } + + serialize() { + return this.serialized; + } + + getType() { + return 4; + } +} + +module.exports = SenderKeyMessage; \ No newline at end of file diff --git a/WASignalGroup/sender_key_name.js b/WASignalGroup/sender_key_name.js new file mode 100644 index 0000000..e7f5290 --- /dev/null +++ b/WASignalGroup/sender_key_name.js @@ -0,0 +1,70 @@ +function isNull(str) { + return str === null || str.value === ''; +} + +/** + * java String hashCode 的实现 + * @param strKey + * @return intValue + */ +function intValue(num) { + const MAX_VALUE = 0x7fffffff; + const MIN_VALUE = -0x80000000; + if (num > MAX_VALUE || num < MIN_VALUE) { + // eslint-disable-next-line + return (num &= 0xffffffff); + } + return num; +} + +function hashCode(strKey) { + let hash = 0; + if (!isNull(strKey)) { + for (let i = 0; i < strKey.length; i++) { + hash = hash * 31 + strKey.charCodeAt(i); + hash = intValue(hash); + } + } + return hash; +} + +/** + * 将js页面的number类型转换为java的int类型 + * @param num + * @return intValue + */ + +class SenderKeyName { + constructor(groupId, sender) { + this.groupId = groupId; + this.sender = sender; + } + + getGroupId() { + return this.groupId; + } + + getSender() { + return this.sender; + } + + serialize() { + return `${this.groupId}::${this.sender.id}::${this.sender.deviceId}`; + } + + toString() { + return this.serialize(); + } + + equals(other) { + if (other === null) return false; + if (!(other instanceof SenderKeyName)) return false; + return this.groupId === other.groupId && this.sender.toString() === other.sender.toString(); + } + + hashCode() { + return hashCode(this.groupId) ^ hashCode(this.sender.toString()); + } +} + +module.exports = SenderKeyName; \ No newline at end of file diff --git a/WASignalGroup/sender_key_record.js b/WASignalGroup/sender_key_record.js new file mode 100644 index 0000000..e17f290 --- /dev/null +++ b/WASignalGroup/sender_key_record.js @@ -0,0 +1,54 @@ +const SenderKeyState = require('./sender_key_state'); + +class SenderKeyRecord { + MAX_STATES = 5; + + constructor(serialized) { + this.senderKeyStates = []; + + if (serialized) { + const list = serialized; + for (let i = 0; i < list.length; i++) { + const structure = list[i]; + this.senderKeyStates.push( + new SenderKeyState(null, null, null, null, null, null, structure) + ); + } + } + } + + isEmpty() { + return this.senderKeyStates.length === 0; + } + + getSenderKeyState(keyId) { + if (!keyId && this.senderKeyStates.length) return this.senderKeyStates[0]; + for (let i = 0; i < this.senderKeyStates.length; i++) { + const state = this.senderKeyStates[i]; + if (state.getKeyId() === keyId) { + return state; + } + } + throw new Error(`No keys for: ${keyId}`); + } + + addSenderKeyState(id, iteration, chainKey, signatureKey) { + this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, null, signatureKey)); + } + + setSenderKeyState(id, iteration, chainKey, keyPair) { + this.senderKeyStates.length = 0; + this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, keyPair)); + } + + serialize() { + const recordStructure = []; + for (let i = 0; i < this.senderKeyStates.length; i++) { + const senderKeyState = this.senderKeyStates[i]; + recordStructure.push(senderKeyState.getStructure()); + } + return recordStructure; + } + } + + module.exports = SenderKeyRecord; \ No newline at end of file diff --git a/WASignalGroup/sender_key_state.js b/WASignalGroup/sender_key_state.js new file mode 100644 index 0000000..c3e0fc9 --- /dev/null +++ b/WASignalGroup/sender_key_state.js @@ -0,0 +1,129 @@ +const SenderChainKey = require('./sender_chain_key'); +const SenderMessageKey = require('./sender_message_key'); + +const protobufs = require('./protobufs'); + +class SenderKeyState { + MAX_MESSAGE_KEYS = 2000; + + constructor( + id = null, + iteration = null, + chainKey = null, + signatureKeyPair = null, + signatureKeyPublic = null, + signatureKeyPrivate = null, + senderKeyStateStructure = null + ) { + if (senderKeyStateStructure) { + this.senderKeyStateStructure = senderKeyStateStructure; + } else { + if (signatureKeyPair) { + signatureKeyPublic = signatureKeyPair.public; + signatureKeyPrivate = signatureKeyPair.private; + } + + chainKey = typeof chainKey === 'string' ? Buffer.from(chainKey, 'base64') : chainKey; + this.senderKeyStateStructure = protobufs.SenderKeyStateStructure.create(); + const senderChainKeyStructure = protobufs.SenderChainKey.create(); + senderChainKeyStructure.iteration = iteration; + senderChainKeyStructure.seed = chainKey; + this.senderKeyStateStructure.senderChainKey = senderChainKeyStructure; + + const signingKeyStructure = protobufs.SenderSigningKey.create(); + signingKeyStructure.public = + typeof signatureKeyPublic === 'string' ? + Buffer.from(signatureKeyPublic, 'base64') : + signatureKeyPublic; + if (signatureKeyPrivate) { + signingKeyStructure.private = + typeof signatureKeyPrivate === 'string' ? + Buffer.from(signatureKeyPrivate, 'base64') : + signatureKeyPrivate; + } + this.senderKeyStateStructure.senderKeyId = id; + this.senderChainKey = senderChainKeyStructure; + this.senderKeyStateStructure.senderSigningKey = signingKeyStructure; + } + this.senderKeyStateStructure.senderMessageKeys = + this.senderKeyStateStructure.senderMessageKeys || []; + } + + SenderKeyState(senderKeyStateStructure) { + this.senderKeyStateStructure = senderKeyStateStructure; + } + + getKeyId() { + return this.senderKeyStateStructure.senderKeyId; + } + + getSenderChainKey() { + return new SenderChainKey( + this.senderKeyStateStructure.senderChainKey.iteration, + this.senderKeyStateStructure.senderChainKey.seed + ); + } + + setSenderChainKey(chainKey) { + const senderChainKeyStructure = protobufs.SenderChainKey.create({ + iteration: chainKey.getIteration(), + seed: chainKey.getSeed(), + }); + this.senderKeyStateStructure.senderChainKey = senderChainKeyStructure; + } + + getSigningKeyPublic() { + return typeof this.senderKeyStateStructure.senderSigningKey.public === 'string' ? + Buffer.from(this.senderKeyStateStructure.senderSigningKey.public, 'base64') : + this.senderKeyStateStructure.senderSigningKey.public; + } + + getSigningKeyPrivate() { + return typeof this.senderKeyStateStructure.senderSigningKey.private === 'string' ? + Buffer.from(this.senderKeyStateStructure.senderSigningKey.private, 'base64') : + this.senderKeyStateStructure.senderSigningKey.private; + } + + hasSenderMessageKey(iteration) { + const list = this.senderKeyStateStructure.senderMessageKeys; + for (let o = 0; o < list.length; o++) { + const senderMessageKey = list[o]; + if (senderMessageKey.iteration === iteration) return true; + } + return false; + } + + addSenderMessageKey(senderMessageKey) { + const senderMessageKeyStructure = protobufs.SenderKeyStateStructure.create({ + iteration: senderMessageKey.getIteration(), + seed: senderMessageKey.getSeed(), + }); + this.senderKeyStateStructure.senderMessageKeys.push(senderMessageKeyStructure); + + if (this.senderKeyStateStructure.senderMessageKeys.length > this.MAX_MESSAGE_KEYS) { + this.senderKeyStateStructure.senderMessageKeys.shift(); + } + } + + removeSenderMessageKey(iteration) { + let result = null; + + this.senderKeyStateStructure.senderMessageKeys = this.senderKeyStateStructure.senderMessageKeys.filter( + senderMessageKey => { + if (senderMessageKey.iteration === iteration) result = senderMessageKey; + return senderMessageKey.iteration !== iteration; + } + ); + + if (result != null) { + return new SenderMessageKey(result.iteration, result.seed); + } + return null; + } + + getStructure() { + return this.senderKeyStateStructure; + } +} + +module.exports = SenderKeyState; \ No newline at end of file diff --git a/WASignalGroup/sender_message_key.js b/WASignalGroup/sender_message_key.js new file mode 100644 index 0000000..7639704 --- /dev/null +++ b/WASignalGroup/sender_message_key.js @@ -0,0 +1,39 @@ +const { deriveSecrets } = require('libsignal/src/crypto'); +class SenderMessageKey { + iteration = 0; + + iv = Buffer.alloc(0); + + cipherKey = Buffer.alloc(0); + + seed = Buffer.alloc(0); + + constructor(iteration, seed) { + const derivative = deriveSecrets(seed, Buffer.alloc(32), Buffer.from('WhisperGroup')); + const keys = new Uint8Array(32); + keys.set(new Uint8Array(derivative[0].slice(16))); + keys.set(new Uint8Array(derivative[1].slice(0, 16)), 16); + this.iv = Buffer.from(derivative[0].slice(0, 16)); + this.cipherKey = Buffer.from(keys.buffer); + + this.iteration = iteration; + this.seed = seed; + } + + getIteration() { + return this.iteration; + } + + getIv() { + return this.iv; + } + + getCipherKey() { + return this.cipherKey; + } + + getSeed() { + return this.seed; + } +} +module.exports = SenderMessageKey; \ No newline at end of file diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..af9e3d4 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +module.exports = { + "roots": [ + "/src" + ], + "testMatch": [ + "**/Tests/test.*.+(ts|tsx|js)", + ], + "transform": { + "^.+\\.(ts|tsx)$": "ts-jest" + }, +} \ No newline at end of file diff --git a/package.json b/package.json index db772af..5f03ecb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@adiwajshing/baileys", - "version": "3.5.3", - "description": "WhatsApp Web API", + "version": "4.0.0", + "description": "WhatsApp API", "homepage": "https://github.com/adiwajshing/Baileys", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -13,18 +13,21 @@ "whatsapp", "whatsapp-chat", "whatsapp-group", - "automation" + "automation", + "multi-device" ], "scripts": { - "test": "mocha --timeout 240000 -r ts-node/register src/Tests/Tests.*.ts", - "prepack": "tsc", - "lint": "eslint '*/*.ts' --quiet --fix", + "test": "jest", + "prepare": "tsc", "build:all": "tsc && typedoc", "build:docs": "typedoc", "build:tsc": "tsc", "example": "node --inspect -r ts-node/register Example/example.ts", - "gen-protobuf": "bash src/Binary/GenerateStatics.sh", - "browser-decode": "yarn ts-node src/BrowserMessageDecoding.ts" + "example:legacy": "node --inspect -r ts-node/register Example/example-legacy.ts", + "gen-protobuf": "bash src/BinaryNode/GenerateStatics.sh", + "browser-decode": "yarn ts-node src/BrowserMessageDecoding.ts", + "lint": "eslint ./src --ext .js,.ts,.jsx,.tsx", + "lint:fix": "eslint ./src --fix --ext .js,.ts,.jsx,.tsx" }, "author": "Adhiraj Singh", "license": "MIT", @@ -32,33 +35,44 @@ "url": "git@github.com:adiwajshing/baileys.git" }, "dependencies": { - "@adiwajshing/keyed-db": "^0.2.2", + "@hapi/boom": "^9.1.3", + "axios": "^0.24.0", "curve25519-js": "^0.0.4", - "futoin-hkdf": "^1.3.2", - "got": "^11.8.1", - "https-proxy-agent": "^5.0.0", - "jimp": "^0.16.1", + "libsignal": "git+https://github.com/adiwajshing/libsignal-node", "music-metadata": "^7.4.1", - "pino": "^6.7.0", - "pino-pretty": "^4.3.0", + "node-cache": "^5.1.2", + "pino": "^7.0.0", "protobufjs": "^6.10.1", + "ws": "^8.0.0" + }, + "peerDependencies": { + "jimp": "^0.16.1", "qrcode-terminal": "^0.12.0", - "ws": "^7.3.1" + "sharp": "^0.29.3", + "@adiwajshing/keyed-db": "^0.2.4" }, "files": [ "lib/*", - "WAMessage/*" + "WAProto/*", + "WASignalGroup/*.js", + "WABinary/*.js" ], "devDependencies": { + "@adiwajshing/eslint-config": "git+https://github.com/adiwajshing/eslint-config", + "@adiwajshing/keyed-db": "^0.2.4", "@types/got": "^9.6.11", - "@types/mocha": "^7.0.2", + "@types/jest": "^26.0.24", "@types/node": "^14.6.2", - "@types/pino": "^6.3.2", - "@types/ws": "^7.2.6", - "assert": "^2.0.0", - "dotenv": "^8.2.0", - "mocha": "^8.1.3", - "ts-node-dev": "^1.0.0", + "@types/pino": "^7.0.0", + "@types/sharp": "^0.29.4", + "@types/ws": "^8.0.0", + "eslint": "^7.0.0", + "jest": "^27.0.6", + "jimp": "^0.16.1", + "qrcode-terminal": "^0.12.0", + "sharp": "^0.29.3", + "ts-jest": "^27.0.3", + "ts-node": "^10.0.0", "typedoc": "^0.20.0-beta.27", "typescript": "^4.0.0" } diff --git a/src/Binary/Constants.ts b/src/Binary/Constants.ts deleted file mode 100644 index d26fd00..0000000 --- a/src/Binary/Constants.ts +++ /dev/null @@ -1,205 +0,0 @@ -import {proto} from '../../WAMessage/WAMessage' - -export namespace WA { - export const Tags = { - LIST_EMPTY: 0, - STREAM_END: 2, - DICTIONARY_0: 236, - DICTIONARY_1: 237, - DICTIONARY_2: 238, - DICTIONARY_3: 239, - LIST_8: 248, - LIST_16: 249, - JID_PAIR: 250, - HEX_8: 251, - BINARY_8: 252, - BINARY_20: 253, - BINARY_32: 254, - NIBBLE_8: 255, - SINGLE_BYTE_MAX: 256, - PACKED_MAX: 254, - } - export const DoubleByteTokens = [] - export const SingleByteTokens = [ - null, - null, - null, - '200', - '400', - '404', - '500', - '501', - '502', - 'action', - 'add', - 'after', - 'archive', - 'author', - 'available', - 'battery', - 'before', - 'body', - 'broadcast', - 'chat', - 'clear', - 'code', - 'composing', - 'contacts', - 'count', - 'create', - 'debug', - 'delete', - 'demote', - 'duplicate', - 'encoding', - 'error', - 'false', - 'filehash', - 'from', - 'g.us', - 'group', - 'groups_v2', - 'height', - 'id', - 'image', - 'in', - 'index', - 'invis', - 'item', - 'jid', - 'kind', - 'last', - 'leave', - 'live', - 'log', - 'media', - 'message', - 'mimetype', - 'missing', - 'modify', - 'name', - 'notification', - 'notify', - 'out', - 'owner', - 'participant', - 'paused', - 'picture', - 'played', - 'presence', - 'preview', - 'promote', - 'query', - 'raw', - 'read', - 'receipt', - 'received', - 'recipient', - 'recording', - 'relay', - 'remove', - 'response', - 'resume', - 'retry', - 's.whatsapp.net', - 'seconds', - 'set', - 'size', - 'status', - 'subject', - 'subscribe', - 't', - 'text', - 'to', - 'true', - 'type', - 'unarchive', - 'unavailable', - 'url', - 'user', - 'value', - 'web', - 'width', - 'mute', - 'read_only', - 'admin', - 'creator', - 'short', - 'update', - 'powersave', - 'checksum', - 'epoch', - 'block', - 'previous', - '409', - 'replaced', - 'reason', - 'spam', - 'modify_tag', - 'message_info', - 'delivery', - 'emoji', - 'title', - 'description', - 'canonical-url', - 'matched-text', - 'star', - 'unstar', - 'media_key', - 'filename', - 'identity', - 'unread', - 'page', - 'page_count', - 'search', - 'media_message', - 'security', - 'call_log', - 'profile', - 'ciphertext', - 'invite', - 'gif', - 'vcard', - 'frequent', - 'privacy', - 'blacklist', - 'whitelist', - 'verify', - 'location', - 'document', - 'elapsed', - 'revoke_invite', - 'expiration', - 'unsubscribe', - 'disable', - 'vname', - 'old_jid', - 'new_jid', - 'announcement', - 'locked', - 'prop', - 'label', - 'color', - 'call', - 'offer', - 'call-id', - 'quick_reply', - 'sticker', - 'pay_t', - 'accept', - 'reject', - 'sticker_pack', - 'invalid', - 'canceled', - 'missed', - 'connected', - 'result', - 'audio', - 'video', - 'recent', - ] - export const Message = proto.WebMessageInfo - export type NodeAttributes = { [key: string]: string } | string | null - export type NodeData = Array | any | null - export type Node = [string, NodeAttributes, NodeData] -} diff --git a/src/Binary/Decoder.ts b/src/Binary/Decoder.ts deleted file mode 100644 index 57a83db..0000000 --- a/src/Binary/Decoder.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { WA } from './Constants' - -export default class Decoder { - buffer: Buffer = null - index = 0 - - checkEOS(length: number) { - if (this.index + length > this.buffer.length) { - throw new Error('end of stream') - } - } - next() { - const value = this.buffer[this.index] - this.index += 1 - return value - } - readByte() { - this.checkEOS(1) - return this.next() - } - readStringFromChars(length: number) { - this.checkEOS(length) - const value = this.buffer.slice(this.index, this.index + length) - - this.index += length - return value.toString ('utf-8') - } - readBytes(n: number): Buffer { - this.checkEOS(n) - const value = this.buffer.slice(this.index, this.index + n) - this.index += n - return value - } - readInt(n: number, littleEndian = false) { - this.checkEOS(n) - let val = 0 - for (let i = 0; i < n; i++) { - const shift = littleEndian ? i : n - 1 - i - val |= this.next() << (shift * 8) - } - return val - } - readInt20() { - this.checkEOS(3) - return ((this.next() & 15) << 16) + (this.next() << 8) + this.next() - } - unpackHex(value: number) { - if (value >= 0 && value < 16) { - return value < 10 ? '0'.charCodeAt(0) + value : 'A'.charCodeAt(0) + value - 10 - } - throw new Error('invalid hex: ' + value) - } - unpackNibble(value: number) { - if (value >= 0 && value <= 9) { - return '0'.charCodeAt(0) + value - } - switch (value) { - case 10: - return '-'.charCodeAt(0) - case 11: - return '.'.charCodeAt(0) - case 15: - return '\0'.charCodeAt(0) - default: - throw new Error('invalid nibble: ' + value) - } - } - unpackByte(tag: number, value: number) { - if (tag === WA.Tags.NIBBLE_8) { - return this.unpackNibble(value) - } else if (tag === WA.Tags.HEX_8) { - return this.unpackHex(value) - } else { - throw new Error('unknown tag: ' + tag) - } - } - readPacked8(tag: number) { - const startByte = this.readByte() - let value = '' - - for (let i = 0; i < (startByte & 127); i++) { - const curByte = this.readByte() - value += String.fromCharCode(this.unpackByte(tag, (curByte & 0xf0) >> 4)) - value += String.fromCharCode(this.unpackByte(tag, curByte & 0x0f)) - } - if (startByte >> 7 !== 0) { - value = value.slice(0, -1) - } - return value - } - readRangedVarInt(min, max, description = 'unknown') { - // value = - throw new Error('WTF; should not be called') - } - isListTag(tag: number) { - return tag === WA.Tags.LIST_EMPTY || tag === WA.Tags.LIST_8 || tag === WA.Tags.LIST_16 - } - readListSize(tag: number) { - switch (tag) { - case WA.Tags.LIST_EMPTY: - return 0 - case WA.Tags.LIST_8: - return this.readByte() - case WA.Tags.LIST_16: - return this.readInt(2) - default: - throw new Error('invalid tag for list size: ' + tag) - } - } - - readString(tag: number): string { - if (tag >= 3 && tag <= 235) { - const token = this.getToken(tag) - return token// === 's.whatsapp.net' ? 'c.us' : token - } - - switch (tag) { - case WA.Tags.DICTIONARY_0: - case WA.Tags.DICTIONARY_1: - case WA.Tags.DICTIONARY_2: - case WA.Tags.DICTIONARY_3: - return this.getTokenDouble(tag - WA.Tags.DICTIONARY_0, this.readByte()) - case WA.Tags.LIST_EMPTY: - return null - case WA.Tags.BINARY_8: - return this.readStringFromChars(this.readByte()) - case WA.Tags.BINARY_20: - return this.readStringFromChars(this.readInt20()) - case WA.Tags.BINARY_32: - return this.readStringFromChars(this.readInt(4)) - case WA.Tags.JID_PAIR: - const i = this.readString(this.readByte()) - const j = this.readString(this.readByte()) - if (typeof i === 'string' && j) { - return i + '@' + j - } - throw new Error('invalid jid pair: ' + i + ', ' + j) - case WA.Tags.HEX_8: - case WA.Tags.NIBBLE_8: - return this.readPacked8(tag) - default: - throw new Error('invalid string with tag: ' + tag) - } - } - readAttributes(n: number) { - if (n !== 0) { - const attributes: WA.NodeAttributes = {} - for (let i = 0; i < n; i++) { - const key = this.readString(this.readByte()) - const b = this.readByte() - - attributes[key] = this.readString(b) - } - return attributes - } - return null - } - readList(tag: number) { - const arr = [...new Array(this.readListSize(tag))] - return arr.map(() => this.readNode()) - } - getToken(index: number) { - if (index < 3 || index >= WA.SingleByteTokens.length) { - throw new Error('invalid token index: ' + index) - } - return WA.SingleByteTokens[index] - } - getTokenDouble(index1, index2): string { - const n = 256 * index1 + index2 - if (n < 0 || n > WA.DoubleByteTokens.length) { - throw new Error('invalid double token index: ' + n) - } - return WA.DoubleByteTokens[n] - } - readNode(): WA.Node { - const listSize = this.readListSize(this.readByte()) - const descrTag = this.readByte() - if (descrTag === WA.Tags.STREAM_END) { - throw new Error('unexpected stream end') - } - - const descr = this.readString(descrTag) - if (listSize === 0 || !descr) { - throw new Error('invalid node') - } - - const attrs = this.readAttributes((listSize - 1) >> 1) - let content: WA.NodeData = null - - if (listSize % 2 === 0) { - const tag = this.readByte() - if (this.isListTag(tag)) { - content = this.readList(tag) - } else { - let decoded: Buffer | string - switch (tag) { - case WA.Tags.BINARY_8: - decoded = this.readBytes(this.readByte()) - break - case WA.Tags.BINARY_20: - decoded = this.readBytes(this.readInt20()) - break - case WA.Tags.BINARY_32: - decoded = this.readBytes(this.readInt(4)) - break - default: - decoded = this.readString(tag) - break - } - - if (descr === 'message' && Buffer.isBuffer(decoded)) { - content = WA.Message.decode(decoded) - } else { - content = decoded - } - } - } - - return [descr, attrs, content] - } - - read(buffer: Buffer) { - this.buffer = buffer - this.index = 0 - return this.readNode() - } -} diff --git a/src/Binary/Encoder.ts b/src/Binary/Encoder.ts deleted file mode 100644 index 63331e9..0000000 --- a/src/Binary/Encoder.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Message } from 'protobufjs' -import { WA } from './Constants' - -export default class Encoder { - data: number[] = [] - - pushByte(value: number) { - this.data.push(value & 0xff) - } - pushInt(value: number, n: number, littleEndian=false) { - for (let i = 0; i < n; i++) { - const curShift = littleEndian ? i : n - 1 - i - this.data.push((value >> (curShift * 8)) & 0xff) - } - } - pushInt20(value: number) { - this.pushBytes([(value >> 16) & 0x0f, (value >> 8) & 0xff, value & 0xff]) - } - pushBytes(bytes: Uint8Array | Buffer | number[]) { - bytes.forEach (b => this.data.push(b)) - } - writeByteLength(length: number) { - if (length >= 4294967296) throw new Error('string too large to encode: ' + length) - - if (length >= 1 << 20) { - this.pushByte(WA.Tags.BINARY_32) - this.pushInt(length, 4) // 32 bit integer - } else if (length >= 256) { - this.pushByte(WA.Tags.BINARY_20) - this.pushInt20(length) - } else { - this.pushByte(WA.Tags.BINARY_8) - this.pushByte(length) - } - } - writeStringRaw(string: string) { - const bytes = Buffer.from (string, 'utf-8') - this.writeByteLength(bytes.length) - this.pushBytes(bytes) - } - writeJid(left: string, right: string) { - this.pushByte(WA.Tags.JID_PAIR) - left && left.length > 0 ? this.writeString(left) : this.writeToken(WA.Tags.LIST_EMPTY) - this.writeString(right) - } - writeToken(token: number) { - if (token < 245) { - this.pushByte(token) - } else if (token <= 500) { - throw new Error('invalid token') - } - } - writeString(token: string, i: boolean = null) { - if (token === 'c.us') token = 's.whatsapp.net' - - const tokenIndex = WA.SingleByteTokens.indexOf(token) - if (!i && token === 's.whatsapp.net') { - this.writeToken(tokenIndex) - } else if (tokenIndex >= 0) { - if (tokenIndex < WA.Tags.SINGLE_BYTE_MAX) { - this.writeToken(tokenIndex) - } else { - const overflow = tokenIndex - WA.Tags.SINGLE_BYTE_MAX - const dictionaryIndex = overflow >> 8 - if (dictionaryIndex < 0 || dictionaryIndex > 3) { - throw new Error('double byte dict token out of range: ' + token + ', ' + tokenIndex) - } - this.writeToken(WA.Tags.DICTIONARY_0 + dictionaryIndex) - this.writeToken(overflow % 256) - } - } else if (token) { - const jidSepIndex = token.indexOf('@') - if (jidSepIndex <= 0) { - this.writeStringRaw(token) - } else { - this.writeJid(token.slice(0, jidSepIndex), token.slice(jidSepIndex + 1, token.length)) - } - } - } - writeAttributes(attrs: Record | string, keys: string[]) { - if (!attrs) { - return - } - keys.forEach((key) => { - this.writeString(key) - this.writeString(attrs[key]) - }) - } - writeListStart(listSize: number) { - if (listSize === 0) { - this.pushByte(WA.Tags.LIST_EMPTY) - } else if (listSize < 256) { - this.pushBytes([WA.Tags.LIST_8, listSize]) - } else { - this.pushBytes([WA.Tags.LIST_16, listSize]) - } - } - writeChildren(children: string | Array | Buffer | Object) { - if (!children) return - - if (typeof children === 'string') { - this.writeString(children, true) - } else if (Buffer.isBuffer(children)) { - this.writeByteLength (children.length) - this.pushBytes(children) - } else if (Array.isArray(children)) { - this.writeListStart(children.length) - children.forEach(c => c && this.writeNode(c)) - } else if (typeof children === 'object') { - const buffer = WA.Message.encode(children as any).finish() - this.writeByteLength(buffer.length) - this.pushBytes(buffer) - } else { - throw new Error('invalid children: ' + children + ' (' + typeof children + ')') - } - } - getValidKeys(obj: Object) { - return obj ? Object.keys(obj).filter((key) => obj[key] !== null && obj[key] !== undefined) : [] - } - writeNode(node: WA.Node) { - if (!node) { - return - } else if (node.length !== 3) { - throw new Error('invalid node given: ' + node) - } - const validAttributes = this.getValidKeys(node[1]) - - this.writeListStart(2 * validAttributes.length + 1 + (node[2] ? 1 : 0)) - this.writeString(node[0]) - this.writeAttributes(node[1], validAttributes) - this.writeChildren(node[2]) - } - write(data) { - this.data = [] - this.writeNode(data) - - return Buffer.from(this.data) - } -} diff --git a/src/Binary/GenerateStatics.sh b/src/Binary/GenerateStatics.sh deleted file mode 100644 index 3c8c85b..0000000 --- a/src/Binary/GenerateStatics.sh +++ /dev/null @@ -1,4 +0,0 @@ -yarn pbjs -t static-module -w commonjs -o ./WAMessage/WAMessage.js ./src/Binary/WAMessage.proto; -yarn pbts -o ./WAMessage/WAMessage.d.ts ./WAMessage/WAMessage.js; - -#protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=env=node,useOptionals=true,forceLong=long --ts_proto_out=. ./src/Binary/WAMessage.proto; \ No newline at end of file diff --git a/src/BrowserMessageDecoding.ts b/src/BrowserMessageDecoding.ts deleted file mode 100644 index addcbec..0000000 --- a/src/BrowserMessageDecoding.ts +++ /dev/null @@ -1,42 +0,0 @@ -import fs from 'fs' -import { decryptWA } from './WAConnection' -import Decoder from './Binary/Decoder' - -interface BrowserMessagesInfo { - bundle: { encKey: string, macKey: string } - harFilePath: string -} -interface WSMessage { - type: 'send' | 'receive', - data: string -} -const file = fs.readFileSync ('./browser-messages.json', {encoding: 'utf-8'}) -const json: BrowserMessagesInfo = JSON.parse (file) - -const encKey = Buffer.from (json.bundle.encKey, 'base64') -const macKey = Buffer.from (json.bundle.macKey, 'base64') - -const harFile = JSON.parse ( fs.readFileSync( json.harFilePath , {encoding: 'utf-8'})) -const entries = harFile['log']['entries'] -let wsMessages: WSMessage[] = [] -entries.forEach ((e, i) => { - if ('_webSocketMessages' in e) { - wsMessages.push (...e['_webSocketMessages']) - } -}) -const decrypt = (buffer, fromMe) => decryptWA (buffer, macKey, encKey, new Decoder(), fromMe) - -console.log ('parsing ' + wsMessages.length + ' messages') -const list = wsMessages.map ((item, i) => { - const buffer = item.data.includes(',') ? item.data : Buffer.from (item.data, 'base64') - try { - const [tag, json, binaryTags] = decrypt (buffer, item.type === 'send') - - return {tag, json: json && JSON.stringify(json), binaryTags} - } catch (error) { - return { error: error.message, data: buffer.toString('utf-8') } - } -}) -.filter (Boolean) -const str = JSON.stringify (list, null, '\t') -fs.writeFileSync ('decoded-ws.json', str) \ No newline at end of file diff --git a/src/Defaults/index.ts b/src/Defaults/index.ts new file mode 100644 index 0000000..f596479 --- /dev/null +++ b/src/Defaults/index.ts @@ -0,0 +1,59 @@ +import P from 'pino' +import type { CommonSocketConfig, LegacySocketConfig, MediaType, SocketConfig } from '../Types' +import { Browsers } from '../Utils' + +export const UNAUTHORIZED_CODES = [401, 403, 419] + +export const DEFAULT_ORIGIN = 'https://web.whatsapp.com' +export const DEF_CALLBACK_PREFIX = 'CB:' +export const DEF_TAG_PREFIX = 'TAG:' +export const PHONE_CONNECTION_CB = 'CB:Pong' + +export const WA_DEFAULT_EPHEMERAL = 7*24*60*60 + +export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0' +export const NOISE_WA_HEADER = new Uint8Array([87, 65, 5, 2]) // last is "DICT_VERSION" + +/** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */ +export const URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)?/gi + +const BASE_CONNECTION_CONFIG: CommonSocketConfig = { + version: [2, 2149, 4], + browser: Browsers.baileys('Chrome'), + + waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat', + connectTimeoutMs: 20_000, + keepAliveIntervalMs: 25_000, + logger: P().child({ class: 'baileys' }), + printQRInTerminal: false, + emitOwnEvents: true, + defaultQueryTimeoutMs: 60_000, + customUploadHosts: [], +} + +export const DEFAULT_CONNECTION_CONFIG: SocketConfig = { + ...BASE_CONNECTION_CONFIG, + waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat', + getMessage: async() => undefined +} + +export const DEFAULT_LEGACY_CONNECTION_CONFIG: LegacySocketConfig = { + ...BASE_CONNECTION_CONFIG, + waWebSocketUrl: 'wss://web.whatsapp.com/ws', + phoneResponseTimeMs: 20_000, + expectResponseTimeout: 60_000, +} + +export const MEDIA_PATH_MAP: { [T in MediaType]: string } = { + image: '/mms/image', + video: '/mms/video', + document: '/mms/document', + audio: '/mms/audio', + sticker: '/mms/image', + history: '', + 'md-app-state': '' +} + +export const MEDIA_KEYS = Object.keys(MEDIA_PATH_MAP) as MediaType[] + +export const KEY_BUNDLE_TYPE = '' \ No newline at end of file diff --git a/src/LegacySocket/auth.ts b/src/LegacySocket/auth.ts new file mode 100644 index 0000000..6d551cd --- /dev/null +++ b/src/LegacySocket/auth.ts @@ -0,0 +1,253 @@ +import { Boom } from '@hapi/boom' +import EventEmitter from 'events' +import { ConnectionState, CurveKeyPair, DisconnectReason, LegacyBaileysEventEmitter, LegacySocketConfig, WAInitResponse } from '../Types' +import { bindWaitForConnectionUpdate, computeChallengeResponse, Curve, newLegacyAuthCreds, printQRIfNecessaryListener, validateNewConnection } from '../Utils' +import { makeSocket } from './socket' + +const makeAuthSocket = (config: LegacySocketConfig) => { + const { + logger, + version, + browser, + connectTimeoutMs, + printQRInTerminal, + auth: initialAuthInfo + } = config + const ev = new EventEmitter() as LegacyBaileysEventEmitter + + const authInfo = initialAuthInfo || newLegacyAuthCreds() + + const state: ConnectionState = { + legacy: { + phoneConnected: false, + }, + connection: 'connecting', + } + + const socket = makeSocket(config) + const { ws } = socket + let curveKeys: CurveKeyPair + let initTimeout: NodeJS.Timeout + + ws.on('phone-connection', ({ value: phoneConnected }) => { + updateState({ legacy: { ...state.legacy, phoneConnected } }) + }) + // add close listener + ws.on('ws-close', (error: Boom | Error) => { + logger.info({ error }, 'closed connection to WhatsApp') + initTimeout && clearTimeout(initTimeout) + // if no reconnects occur + // send close event + updateState({ + connection: 'close', + qr: undefined, + lastDisconnect: { + error, + date: new Date() + } + }) + }) + /** Can you login to WA without scanning the QR */ + const canLogin = () => !!authInfo?.encKey && !!authInfo?.macKey + + const updateState = (update: Partial) => { + Object.assign(state, update) + ev.emit('connection.update', update) + } + + /** + * Logs you out from WA + * If connected, invalidates the credentials with the server + */ + const logout = async() => { + if(state.connection === 'open') { + await socket.sendNode({ + json: ['admin', 'Conn', 'disconnect'], + tag: 'goodbye' + }) + } + + // will call state update to close connection + socket?.end( + new Boom('Logged Out', { statusCode: DisconnectReason.loggedOut }) + ) + } + + const updateEncKeys = () => { + // update the keys so we can decrypt traffic + socket.updateKeys({ encKey: authInfo!.encKey, macKey: authInfo!.macKey }) + } + + const generateKeysForAuth = async(ref: string, ttl?: number) => { + curveKeys = Curve.generateKeyPair() + const publicKey = Buffer.from(curveKeys.public).toString('base64') + let qrGens = 0 + + const qrLoop = ttl => { + const qr = [ref, publicKey, authInfo.clientID].join(',') + updateState({ qr }) + + initTimeout = setTimeout(async() => { + if(state.connection !== 'connecting') { + return + } + + logger.debug('regenerating QR') + try { + // request new QR + const { ref: newRef, ttl: newTTL } = await socket.query({ + json: ['admin', 'Conn', 'reref'], + expect200: true, + longTag: true, + requiresPhoneConnection: false + }) + ttl = newTTL + ref = newRef + } catch(error) { + logger.error({ error }, 'error in QR gen') + if(error.output?.statusCode === 429) { // too many QR requests + socket.end(error) + return + } + } + + qrGens += 1 + qrLoop(ttl) + }, ttl || 20_000) // default is 20s, on the off-chance ttl is not present + } + + qrLoop(ttl) + } + + const onOpen = async() => { + const canDoLogin = canLogin() + const initQuery = (async() => { + const { ref, ttl } = await socket.query({ + json: ['admin', 'init', version, browser, authInfo.clientID, true], + expect200: true, + longTag: true, + requiresPhoneConnection: false + }) as WAInitResponse + + if(!canDoLogin) { + generateKeysForAuth(ref, ttl) + } + })() + let loginTag: string + if(canDoLogin) { + updateEncKeys() + // if we have the info to restore a closed session + const json = [ + 'admin', + 'login', + authInfo.clientToken, + authInfo.serverToken, + authInfo.clientID, + 'takeover' + ] + loginTag = socket.generateMessageTag(true) + // send login every 10s + const sendLoginReq = () => { + if(state.connection === 'open') { + logger.warn('Received login timeout req when state=open, ignoring...') + return + } + + logger.info('sending login request') + socket.sendNode({ + json, + tag: loginTag + }) + initTimeout = setTimeout(sendLoginReq, 10_000) + } + + sendLoginReq() + } + + await initQuery + + // wait for response with tag "s1" + let response = await Promise.race( + [ + socket.waitForMessage('s1', false, undefined).promise, + ...(loginTag ? [socket.waitForMessage(loginTag, false, connectTimeoutMs).promise] : []) + ] + ) + initTimeout && clearTimeout(initTimeout) + initTimeout = undefined + + if(response.status && response.status !== 200) { + throw new Boom('Unexpected error in login', { data: response, statusCode: response.status }) + } + + // if its a challenge request (we get it when logging in) + if(response[1]?.challenge) { + const json = computeChallengeResponse(response[1].challenge, authInfo) + logger.info('resolving login challenge') + + await socket.query({ json, expect200: true, timeoutMs: connectTimeoutMs }) + + response = await socket.waitForMessage('s2', true).promise + } + + if(!response || !response[1]) { + throw new Boom('Received unexpected login response', { data: response }) + } + + if(response[1].type === 'upgrade_md_prod') { + throw new Boom('Require multi-device edition', { statusCode: DisconnectReason.multideviceMismatch }) + } + + // validate the new connection + const { user, auth } = validateNewConnection(response[1], authInfo, curveKeys)// validate the connection + const isNewLogin = user.id !== state.legacy!.user?.id + + Object.assign(authInfo, auth) + updateEncKeys() + + logger.info({ user }, 'logged in') + + ev.emit('creds.update', auth) + + updateState({ + connection: 'open', + legacy: { + phoneConnected: true, + user, + }, + isNewLogin, + qr: undefined + }) + } + + ws.once('open', async() => { + try { + await onOpen() + } catch(error) { + socket.end(error) + } + }) + + if(printQRInTerminal) { + printQRIfNecessaryListener(ev, logger) + } + + process.nextTick(() => { + ev.emit('connection.update', { + ...state + }) + }) + + return { + ...socket, + state, + authInfo, + ev, + canLogin, + logout, + /** Waits for the connection to WA to reach a state */ + waitForConnectionUpdate: bindWaitForConnectionUpdate(ev) + } +} + +export default makeAuthSocket \ No newline at end of file diff --git a/src/LegacySocket/chats.ts b/src/LegacySocket/chats.ts new file mode 100644 index 0000000..5e69cf3 --- /dev/null +++ b/src/LegacySocket/chats.ts @@ -0,0 +1,545 @@ +import { BaileysEventMap, Chat, ChatModification, Contact, LegacySocketConfig, PresenceData, WABusinessProfile, WAFlag, WAMessageKey, WAMessageUpdate, WAMetric, WAPresence } from '../Types' +import { debouncedTimeout, unixTimestampSeconds } from '../Utils/generics' +import { BinaryNode, jidNormalizedUser } from '../WABinary' +import makeAuthSocket from './auth' + +const makeChatsSocket = (config: LegacySocketConfig) => { + const { logger } = config + const sock = makeAuthSocket(config) + const { + ev, + ws: socketEvents, + currentEpoch, + setQuery, + query, + sendNode, + state + } = sock + + const chatsDebounceTimeout = debouncedTimeout(10_000, () => sendChatsQuery(1)) + + const sendChatsQuery = (epoch: number) => ( + sendNode({ + json: { + tag: 'query', + attrs: { type: 'chat', epoch: epoch.toString() } + }, + binaryTag: [ WAMetric.queryChat, WAFlag.ignore ] + }) + ) + + const profilePictureUrl = async(jid: string, timeoutMs?: number) => { + const response = await query({ + json: ['query', 'ProfilePicThumb', jid], + expect200: false, + requiresPhoneConnection: false, + timeoutMs + }) + return response.eurl as string | undefined + } + + const executeChatModification = (node: BinaryNode) => { + const { attrs: attributes } = node + const updateType = attributes.type + const jid = jidNormalizedUser(attributes?.jid) + + switch (updateType) { + case 'delete': + ev.emit('chats.delete', [jid]) + break + case 'clear': + if(node.content) { + const ids = (node.content as BinaryNode[]).map( + ({ attrs }) => attrs.index + ) + ev.emit('messages.delete', { keys: ids.map(id => ({ id, remoteJid: jid })) }) + } else { + ev.emit('messages.delete', { jid, all: true }) + } + + break + case 'archive': + ev.emit('chats.update', [ { id: jid, archive: true } ]) + break + case 'unarchive': + ev.emit('chats.update', [ { id: jid, archive: false } ]) + break + case 'pin': + ev.emit('chats.update', [ { id: jid, pin: attributes.pin ? +attributes.pin : null } ]) + break + case 'star': + case 'unstar': + const starred = updateType === 'star' + const updates: WAMessageUpdate[] = (node.content as BinaryNode[]).map( + ({ attrs }) => ({ + key: { + remoteJid: jid, + id: attrs.index, + fromMe: attrs.owner === 'true' + }, + update: { starred } + }) + ) + ev.emit('messages.update', updates) + break + case 'mute': + if(attributes.mute === '0') { + ev.emit('chats.update', [{ id: jid, mute: null }]) + } else { + ev.emit('chats.update', [{ id: jid, mute: +attributes.mute }]) + } + + break + default: + logger.warn({ node }, 'received unrecognized chat update') + break + } + } + + const applyingPresenceUpdate = (update: BinaryNode['attrs']): BaileysEventMap['presence.update'] => { + const id = jidNormalizedUser(update.id) + const participant = jidNormalizedUser(update.participant || update.id) + + const presence: PresenceData = { + lastSeen: update.t ? +update.t : undefined, + lastKnownPresence: update.type as WAPresence + } + return { id, presences: { [participant]: presence } } + } + + const chatRead = async(fromMessage: WAMessageKey, count: number) => { + await setQuery ( + [ + { + tag: 'read', + attrs: { + jid: fromMessage.remoteJid, + count: count.toString(), + index: fromMessage.id, + owner: fromMessage.fromMe ? 'true' : 'false' + } + } + ], + [ WAMetric.read, WAFlag.ignore ] + ) + if(config.emitOwnEvents) { + ev.emit('chats.update', [{ id: fromMessage.remoteJid, unreadCount: count < 0 ? -1 : 0 }]) + } + } + + ev.on('connection.update', async({ connection }) => { + if(connection !== 'open') { + return + } + + try { + await Promise.all([ + sendNode({ + json: { tag: 'query', attrs: { type: 'contacts', epoch: '1' } }, + binaryTag: [ WAMetric.queryContact, WAFlag.ignore ] + }), + sendNode({ + json: { tag: 'query', attrs: { type: 'status', epoch: '1' } }, + binaryTag: [ WAMetric.queryStatus, WAFlag.ignore ] + }), + sendNode({ + json: { tag: 'query', attrs: { type: 'quick_reply', epoch: '1' } }, + binaryTag: [ WAMetric.queryQuickReply, WAFlag.ignore ] + }), + sendNode({ + json: { tag: 'query', attrs: { type: 'label', epoch: '1' } }, + binaryTag: [ WAMetric.queryLabel, WAFlag.ignore ] + }), + sendNode({ + json: { tag: 'query', attrs: { type: 'emoji', epoch: '1' } }, + binaryTag: [ WAMetric.queryEmoji, WAFlag.ignore ] + }), + sendNode({ + json: { + tag: 'action', + attrs: { type: 'set', epoch: '1' }, + content: [ + { tag: 'presence', attrs: { type: 'available' } } + ] + }, + binaryTag: [ WAMetric.presence, WAFlag.available ] + }) + ]) + chatsDebounceTimeout.start() + + logger.debug('sent init queries') + } catch(error) { + logger.error(`error in sending init queries: ${error}`) + } + }) + socketEvents.on('CB:response,type:chat', async({ content: data }: BinaryNode) => { + chatsDebounceTimeout.cancel() + if(Array.isArray(data)) { + const contacts: Contact[] = [] + const chats = data.map(({ attrs }): Chat => { + const id = jidNormalizedUser(attrs.jid) + if(attrs.name) { + contacts.push({ id, name: attrs.name }) + } + + return { + id: jidNormalizedUser(attrs.jid), + conversationTimestamp: attrs.t ? +attrs.t : undefined, + unreadCount: +attrs.count, + archive: attrs.archive === 'true' ? true : undefined, + pin: attrs.pin ? +attrs.pin : undefined, + mute: attrs.mute ? +attrs.mute : undefined, + notSpam: !(attrs.spam === 'true'), + name: attrs.name, + ephemeralExpiration: attrs.ephemeral ? +attrs.ephemeral : undefined, + ephemeralSettingTimestamp: attrs.eph_setting_ts ? +attrs.eph_setting_ts : undefined, + readOnly: attrs.read_only === 'true' ? true : undefined, + } + }) + + logger.info(`got ${chats.length} chats, extracted ${contacts.length} contacts with name`) + ev.emit('chats.set', { chats, isLatest: true }) + } + }) + // got all contacts from phone + socketEvents.on('CB:response,type:contacts', async({ content: data }: BinaryNode) => { + if(Array.isArray(data)) { + const contacts = data.map(({ attrs }): Contact => { + return { + id: jidNormalizedUser(attrs.jid), + name: attrs.name, + notify: attrs.notify, + verifiedName: attrs.vname + } + }) + + logger.info(`got ${contacts.length} contacts`) + ev.emit('contacts.set', { contacts }) + } + }) + // status updates + socketEvents.on('CB:Status,status', json => { + const id = jidNormalizedUser(json[1].id) + ev.emit('contacts.update', [ { id, status: json[1].status } ]) + }) + // User Profile Name Updates + socketEvents.on('CB:Conn,pushname', json => { + const { legacy: { user }, connection } = state + if(connection === 'open' && json[1].pushname !== user.name) { + user.name = json[1].pushname + ev.emit('connection.update', { legacy: { ...state.legacy, user } }) + } + }) + // read updates + socketEvents.on ('CB:action,,read', async({ content }: BinaryNode) => { + if(Array.isArray(content)) { + const { attrs } = content[0] + + const update: Partial = { + id: jidNormalizedUser(attrs.jid) + } + if(attrs.type === 'false') { + update.unreadCount = -1 + } else { + update.unreadCount = 0 + } + + ev.emit('chats.update', [update]) + } + }) + + socketEvents.on('CB:Cmd,type:picture', async json => { + json = json[1] + const id = jidNormalizedUser(json.jid) + const imgUrl = await profilePictureUrl(id).catch(() => '') + + ev.emit('contacts.update', [ { id, imgUrl } ]) + }) + + // chat archive, pin etc. + socketEvents.on('CB:action,,chat', ({ content }: BinaryNode) => { + if(Array.isArray(content)) { + const [node] = content + executeChatModification(node) + } + }) + + socketEvents.on('CB:action,,user', (json: BinaryNode) => { + if(Array.isArray(json.content)) { + const user = json.content[0].attrs + if(user.id) { + user.id = jidNormalizedUser(user.id) + + //ev.emit('contacts.upsert', [user]) + } else { + logger.warn({ json }, 'recv unknown action') + } + } + }) + + // presence updates + socketEvents.on('CB:Presence', json => { + const update = applyingPresenceUpdate(json[1]) + ev.emit('presence.update', update) + }) + + // blocklist updates + socketEvents.on('CB:Blocklist', json => { + json = json[1] + const blocklist = json.blocklist + ev.emit('blocklist.set', { blocklist }) + }) + + socketEvents.on('ws-close', () => { + chatsDebounceTimeout.cancel() + }) + + return { + ...sock, + sendChatsQuery, + profilePictureUrl, + chatRead, + /** + * Modify a given chat (archive, pin etc.) + * @param jid the ID of the person/group you are modifiying + */ + chatModify: async(modification: ChatModification, jid: string, chatInfo: Pick, timestampNow?: number) => { + const chatAttrs: BinaryNode['attrs'] = { jid: jid } + let data: BinaryNode[] | undefined = undefined + + timestampNow = timestampNow || unixTimestampSeconds() + + if('archive' in modification) { + chatAttrs.type = modification.archive ? 'archive' : 'unarchive' + } else if('pin' in modification) { + chatAttrs.type = 'pin' + if(modification.pin) { + chatAttrs.pin = timestampNow.toString() + } else { + chatAttrs.previous = chatInfo.pin!.toString() + } + } else if('mute' in modification) { + chatAttrs.type = 'mute' + if(modification.mute) { + chatAttrs.mute = (timestampNow + modification.mute).toString() + } else { + chatAttrs.previous = chatInfo.mute!.toString() + } + } else if('clear' in modification) { + chatAttrs.type = 'clear' + chatAttrs.modify_tag = Math.round(Math.random()*1000000).toString() + if(modification.clear !== 'all') { + data = modification.clear.messages.map(({ id, fromMe }) => ( + { + tag: 'item', + attrs: { owner: (!!fromMe).toString(), index: id } + } + )) + } + } else if('star' in modification) { + chatAttrs.type = modification.star.star ? 'star' : 'unstar' + data = modification.star.messages.map(({ id, fromMe }) => ( + { + tag: 'item', + attrs: { owner: (!!fromMe).toString(), index: id } + } + )) + } else if('markRead' in modification) { + const indexKey = modification.lastMessages[modification.lastMessages.length-1].key + return chatRead(indexKey, modification.markRead ? 0 : -1) + } else if('delete' in modification) { + chatAttrs.type = 'delete' + } + + if('lastMessages' in modification) { + const indexKey = modification.lastMessages[modification.lastMessages.length-1].key + if(indexKey) { + chatAttrs.index = indexKey.id + chatAttrs.owner = indexKey.fromMe ? 'true' : 'false' + } + } + + const node = { tag: 'chat', attrs: chatAttrs, content: data } + const response = await setQuery([node], [ WAMetric.chat, WAFlag.ignore ]) + if(config.emitOwnEvents) { + // apply it and emit events + executeChatModification(node) + } + + return response + }, + /** + * Query whether a given number is registered on WhatsApp + * @param str phone number/jid you want to check for + * @returns undefined if the number doesn't exists, otherwise the correctly formatted jid + */ + onWhatsApp: async(str: string) => { + const { status, jid, biz } = await query({ + json: ['query', 'exist', str], + requiresPhoneConnection: false + }) + if(status === 200) { + return { + exists: true, + jid: jidNormalizedUser(jid), + isBusiness: biz as boolean + } + } + }, + /** + * Tell someone about your presence -- online, typing, offline etc. + * @param jid the ID of the person/group who you are updating + * @param type your presence + */ + sendPresenceUpdate: (type: WAPresence, jid: string | undefined) => ( + sendNode({ + binaryTag: [WAMetric.presence, WAFlag[type]], // weird stuff WA does + json: { + tag: 'action', + attrs: { epoch: currentEpoch().toString(), type: 'set' }, + content: [ + { + tag: 'presence', + attrs: { type: type, to: jid } + } + ] + } + }) + ), + /** + * Request updates on the presence of a user + * this returns nothing, you'll receive updates in chats.update event + * */ + presenceSubscribe: async(jid: string) => ( + sendNode({ json: ['action', 'presence', 'subscribe', jid] }) + ), + /** Query the status of the person (see groupMetadata() for groups) */ + getStatus: async(jid: string) => { + const status: { status: string } = await query({ json: ['query', 'Status', jid], requiresPhoneConnection: false }) + return status + }, + setStatus: async(status: string) => { + const response = await setQuery( + [ + { + tag: 'status', + attrs: {}, + content: Buffer.from (status, 'utf-8') + } + ] + ) + ev.emit('contacts.update', [{ id: state.legacy!.user!.id, status }]) + return response + }, + /** Updates business profile. */ + updateBusinessProfile: async(profile: WABusinessProfile) => { + if(profile.business_hours?.config) { + profile.business_hours.business_config = profile.business_hours.config + delete profile.business_hours.config + } + + const json = ['action', 'editBusinessProfile', { ...profile, v: 2 }] + await query({ json, expect200: true, requiresPhoneConnection: true }) + }, + updateProfileName: async(name: string) => { + const response = (await setQuery( + [ + { + tag: 'profile', + attrs: { name } + } + ] + )) as any as {status: number, pushname: string} + + if(config.emitOwnEvents) { + const user = { ...state.legacy!.user!, name } + ev.emit('connection.update', { legacy: { + ...state.legacy, user + } }) + ev.emit('contacts.update', [{ id: user.id, name }]) + } + + return response + }, + /** + * Update the profile picture + * @param jid + * @param img + */ + async updateProfilePicture(jid: string, img: Buffer) { + jid = jidNormalizedUser (jid) + const data = { img: Buffer.from([]), preview: Buffer.from([]) } //await generateProfilePicture(img) TODO + const tag = this.generateMessageTag () + const query: BinaryNode = { + tag: 'picture', + attrs: { jid: jid, id: tag, type: 'set' }, + content: [ + { tag: 'image', attrs: {}, content: data.img }, + { tag: 'preview', attrs: {}, content: data.preview } + ] + } + + const user = state.legacy?.user + const { eurl } = await this.setQuery ([query], [WAMetric.picture, 136], tag) as { eurl: string, status: number } + + if(config.emitOwnEvents) { + if(jid === user.id) { + user.imgUrl = eurl + ev.emit('connection.update', { + legacy: { + ...state.legacy, + user + } + }) + } + + ev.emit('contacts.update', [ { id: jid, imgUrl: eurl } ]) + } + }, + /** + * Add or remove user from blocklist + * @param jid the ID of the person who you are blocking/unblocking + * @param type type of operation + */ + blockUser: async(jid: string, type: 'add' | 'remove' = 'add') => { + const json = { + tag: 'block', + attrs: { type }, + content: [ { tag: 'user', attrs: { jid } } ] + } + await setQuery([json], [WAMetric.block, WAFlag.ignore]) + if(config.emitOwnEvents) { + ev.emit('blocklist.update', { blocklist: [jid], type }) + } + }, + /** + * Query Business Profile (Useful for VCards) + * @param jid Business Jid + * @returns profile object or undefined if not business account + */ + getBusinessProfile: async(jid: string) => { + jid = jidNormalizedUser(jid) + const { + profiles: [{ + profile, + wid + }] + } = await query({ + json: [ + 'query', 'businessProfile', + [ { 'wid': jid.replace('@s.whatsapp.net', '@c.us') } ], + 84 + ], + expect200: true, + requiresPhoneConnection: false, + }) + + return { + ...profile, + wid: jidNormalizedUser(wid) + } as WABusinessProfile + } + } +} + +export default makeChatsSocket \ No newline at end of file diff --git a/src/LegacySocket/groups.ts b/src/LegacySocket/groups.ts new file mode 100644 index 0000000..038f21b --- /dev/null +++ b/src/LegacySocket/groups.ts @@ -0,0 +1,257 @@ +import { GroupMetadata, GroupModificationResponse, GroupParticipant, LegacySocketConfig, ParticipantAction, WAFlag, WAGroupCreateResponse, WAMetric } from '../Types' +import { generateMessageID, unixTimestampSeconds } from '../Utils/generics' +import { BinaryNode, jidNormalizedUser } from '../WABinary' +import makeMessagesSocket from './messages' + +const makeGroupsSocket = (config: LegacySocketConfig) => { + const { logger } = config + const sock = makeMessagesSocket(config) + const { + ev, + ws: socketEvents, + query, + generateMessageTag, + currentEpoch, + setQuery, + state + } = sock + + /** Generic function for group queries */ + const groupQuery = async(type: string, jid?: string, subject?: string, participants?: string[], additionalNodes?: BinaryNode[]) => { + const tag = generateMessageTag() + const result = await setQuery ([ + { + tag: 'group', + attrs: { + author: state.legacy?.user?.id, + id: tag, + type: type, + jid: jid, + subject: subject, + }, + content: participants ? + participants.map(jid => ( + { tag: 'participant', attrs: { jid } } + )) : + additionalNodes + } + ], [WAMetric.group, 136], tag) + return result + } + + /** Get the metadata of the group from WA */ + const groupMetadataFull = async(jid: string) => { + const metadata = await query({ + json: ['query', 'GroupMetadata', jid], + expect200: true + }) + + const meta: GroupMetadata = { + id: metadata.id, + subject: metadata.subject, + creation: +metadata.creation, + owner: jidNormalizedUser(metadata.owner), + desc: metadata.desc, + descOwner: metadata.descOwner, + participants: metadata.participants.map( + p => ({ + id: jidNormalizedUser(p.id), + admin: p.isSuperAdmin ? 'super-admin' : p.isAdmin ? 'admin' : undefined + }) + ) + } + + return meta + } + + /** Get the metadata (works after you've left the group also) */ + const groupMetadataMinimal = async(jid: string) => { + const { attrs, content }:BinaryNode = await query({ + json: { + tag: 'query', + attrs: { type: 'group', jid: jid, epoch: currentEpoch().toString() } + }, + binaryTag: [WAMetric.group, WAFlag.ignore], + expect200: true + }) + const participants: GroupParticipant[] = [] + let desc: string | undefined + if(Array.isArray(content) && Array.isArray(content[0].content)) { + const nodes = content[0].content + for(const item of nodes) { + if(item.tag === 'participant') { + participants.push({ + id: item.attrs.jid, + isAdmin: item.attrs.type === 'admin', + isSuperAdmin: false + }) + } else if(item.tag === 'description') { + desc = (item.content as Buffer).toString('utf-8') + } + } + } + + const meta: GroupMetadata = { + id: jid, + owner: attrs?.creator, + creation: +attrs?.create, + subject: null, + desc, + participants + } + return meta + } + + socketEvents.on('CB:Chat,cmd:action', (json: BinaryNode) => { + /*const data = json[1].data + if (data) { + const emitGroupParticipantsUpdate = (action: WAParticipantAction) => this.emitParticipantsUpdate + (json[1].id, data[2].participants.map(whatsappID), action) + const emitGroupUpdate = (data: Partial) => this.emitGroupUpdate(json[1].id, data) + + switch (data[0]) { + case "promote": + emitGroupParticipantsUpdate('promote') + break + case "demote": + emitGroupParticipantsUpdate('demote') + break + case "desc_add": + emitGroupUpdate({ ...data[2], descOwner: data[1] }) + break + default: + this.logger.debug({ unhandled: true }, json) + break + } + }*/ + }) + + return { + ...sock, + groupMetadata: async(jid: string, minimal: boolean) => { + let result: GroupMetadata + + if(minimal) { + result = await groupMetadataMinimal(jid) + } else { + result = await groupMetadataFull(jid) + } + + return result + }, + /** + * Create a group + * @param title like, the title of the group + * @param participants people to include in the group + */ + groupCreate: async(title: string, participants: string[]) => { + const response = await groupQuery('create', null, title, participants) as WAGroupCreateResponse + const gid = response.gid + let metadata: GroupMetadata + try { + metadata = await groupMetadataFull(gid) + } catch(error) { + logger.warn (`error in group creation: ${error}, switching gid & checking`) + // if metadata is not available + const comps = gid.replace ('@g.us', '').split ('-') + response.gid = `${comps[0]}-${+comps[1] + 1}@g.us` + + metadata = await groupMetadataFull(gid) + logger.warn (`group ID switched from ${gid} to ${response.gid}`) + } + + ev.emit('chats.upsert', [ + { + id: response.gid!, + name: title, + conversationTimestamp: unixTimestampSeconds(), + unreadCount: 0 + } + ]) + return metadata + }, + /** + * Leave a group + * @param jid the ID of the group + */ + groupLeave: async(id: string) => { + await groupQuery('leave', id) + ev.emit('chats.update', [ { id, readOnly: true } ]) + }, + /** + * Update the subject of the group + * @param {string} jid the ID of the group + * @param {string} title the new title of the group + */ + groupUpdateSubject: async(id: string, title: string) => { + await groupQuery('subject', id, title) + ev.emit('chats.update', [ { id, name: title } ]) + ev.emit('contacts.update', [ { id, name: title } ]) + ev.emit('groups.update', [ { id: id, subject: title } ]) + }, + /** + * Update the group description + * @param {string} jid the ID of the group + * @param {string} title the new title of the group + */ + groupUpdateDescription: async(jid: string, description: string) => { + const metadata = await groupMetadataFull(jid) + const node: BinaryNode = { + tag: 'description', + attrs: { id: generateMessageID(), prev: metadata?.descId }, + content: Buffer.from(description, 'utf-8') + } + + const response = await groupQuery ('description', jid, null, null, [node]) + ev.emit('groups.update', [ { id: jid, desc: description } ]) + return response + }, + /** + * Update participants in the group + * @param jid the ID of the group + * @param participants the people to add + */ + groupParticipantsUpdate: async(id: string, participants: string[], action: ParticipantAction) => { + const result: GroupModificationResponse = await groupQuery(action, id, null, participants) + const jids = Object.keys(result.participants || {}) + ev.emit('group-participants.update', { id, participants: jids, action }) + return jids + }, + /** Query broadcast list info */ + getBroadcastListInfo: async(jid: string) => { + interface WABroadcastListInfo { + status: number + name: string + recipients?: {id: string}[] + } + + const result = await query({ + json: ['query', 'contact', jid], + expect200: true, + requiresPhoneConnection: true + }) as WABroadcastListInfo + + const metadata: GroupMetadata = { + subject: result.name, + id: jid, + creation: undefined, + owner: state.legacy?.user?.id, + participants: result.recipients!.map(({ id }) => ( + { id: jidNormalizedUser(id), isAdmin: false, isSuperAdmin: false } + )) + } + return metadata + }, + groupInviteCode: async(jid: string) => { + const response = await sock.query({ + json: ['query', 'inviteCode', jid], + expect200: true, + requiresPhoneConnection: false + }) + return response.code as string + } + } + +} + +export default makeGroupsSocket \ No newline at end of file diff --git a/src/LegacySocket/index.ts b/src/LegacySocket/index.ts new file mode 100644 index 0000000..78caed4 --- /dev/null +++ b/src/LegacySocket/index.ts @@ -0,0 +1,12 @@ +import { DEFAULT_LEGACY_CONNECTION_CONFIG } from '../Defaults' +import { LegacySocketConfig } from '../Types' +import _makeLegacySocket from './groups' +// export the last socket layer +const makeLegacySocket = (config: Partial) => ( + _makeLegacySocket({ + ...DEFAULT_LEGACY_CONNECTION_CONFIG, + ...config + }) +) + +export default makeLegacySocket \ No newline at end of file diff --git a/src/LegacySocket/messages.ts b/src/LegacySocket/messages.ts new file mode 100644 index 0000000..2316ddd --- /dev/null +++ b/src/LegacySocket/messages.ts @@ -0,0 +1,568 @@ +import { Boom } from '@hapi/boom' +import { proto } from '../../WAProto' +import { WA_DEFAULT_EPHEMERAL } from '../Defaults' +import { AnyMessageContent, Chat, GroupMetadata, LegacySocketConfig, MediaConnInfo, MessageUpdateType, MessageUserReceipt, MessageUserReceiptUpdate, MiscMessageGenerationOptions, ParticipantAction, WAFlag, WAMessage, WAMessageCursor, WAMessageKey, WAMessageStatus, WAMessageStubType, WAMessageUpdate, WAMetric, WAUrlInfo } from '../Types' +import { decryptMediaMessageBuffer, extractMessageContent, generateWAMessage, getWAUploadToServer, toNumber } from '../Utils' +import { areJidsSameUser, BinaryNode, getBinaryNodeMessages, isJidGroup, jidNormalizedUser } from '../WABinary' +import makeChatsSocket from './chats' + +const STATUS_MAP = { + read: WAMessageStatus.READ, + message: WAMessageStatus.DELIVERY_ACK, + error: WAMessageStatus.ERROR +} as { [_: string]: WAMessageStatus } + +const makeMessagesSocket = (config: LegacySocketConfig) => { + const { logger } = config + const sock = makeChatsSocket(config) + const { + ev, + ws: socketEvents, + query, + generateMessageTag, + currentEpoch, + setQuery, + state + } = sock + + let mediaConn: Promise + const refreshMediaConn = async(forceGet = false) => { + const media = await mediaConn + if(!media || forceGet || (new Date().getTime()-media.fetchDate.getTime()) > media.ttl*1000) { + mediaConn = (async() => { + const { media_conn } = await query({ + json: ['query', 'mediaConn'], + requiresPhoneConnection: false, + expect200: true + }) + media_conn.fetchDate = new Date() + return media_conn as MediaConnInfo + })() + } + + return mediaConn + } + + const fetchMessagesFromWA = async( + jid: string, + count: number, + cursor?: WAMessageCursor + ) => { + let key: WAMessageKey + if(cursor) { + key = 'before' in cursor ? cursor.before : cursor.after + } + + const { content }:BinaryNode = await query({ + json: { + tag: 'query', + attrs: { + epoch: currentEpoch().toString(), + type: 'message', + jid: jid, + kind: !cursor || 'before' in cursor ? 'before' : 'after', + count: count.toString(), + index: key?.id, + owner: key?.fromMe === false ? 'false' : 'true', + } + }, + binaryTag: [WAMetric.queryMessages, WAFlag.ignore], + expect200: false, + requiresPhoneConnection: true + }) + if(Array.isArray(content)) { + return content.map(data => proto.WebMessageInfo.decode(data.content as Buffer)) + } + + return [] + } + + const updateMediaMessage = async(message: WAMessage) => { + const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage + if(!content) { + throw new Boom( + `given message ${message.key.id} is not a media message`, + { statusCode: 400, data: message } + ) + } + + const response: BinaryNode = await query ({ + json: { + tag: 'query', + attrs: { + type: 'media', + index: message.key.id, + owner: message.key.fromMe ? 'true' : 'false', + jid: message.key.remoteJid, + epoch: currentEpoch().toString() + } + }, + binaryTag: [WAMetric.queryMedia, WAFlag.ignore], + expect200: true, + requiresPhoneConnection: true + }) + const attrs = response.attrs + Object.assign(content, attrs) // update message + + ev.emit('messages.upsert', { messages: [message], type: 'replace' }) + + return response + } + + const onMessage = (message: WAMessage, type: MessageUpdateType) => { + const jid = message.key.remoteJid! + // store chat updates in this + const chatUpdate: Partial = { + id: jid, + } + + const emitGroupUpdate = (update: Partial) => { + ev.emit('groups.update', [ { id: jid, ...update } ]) + } + + if(message.message) { + chatUpdate.conversationTimestamp = +toNumber(message.messageTimestamp) + // add to count if the message isn't from me & there exists a message + if(!message.key.fromMe) { + chatUpdate.unreadCount = 1 + const participant = jidNormalizedUser(message.participant || jid) + + ev.emit( + 'presence.update', + { + id: jid, + presences: { [participant]: { lastKnownPresence: 'available' } } + } + ) + } + } + + const protocolMessage = message.message?.protocolMessage || message.message?.ephemeralMessage?.message?.protocolMessage + // if it's a message to delete another message + if(protocolMessage) { + switch (protocolMessage.type) { + case proto.ProtocolMessage.ProtocolMessageType.REVOKE: + const key = protocolMessage.key + const messageStubType = WAMessageStubType.REVOKE + ev.emit('messages.update', [ + { + // the key of the deleted message is updated + update: { message: null, key: message.key, messageStubType }, + key + } + ]) + return + case proto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING: + chatUpdate.ephemeralSettingTimestamp = message.messageTimestamp + chatUpdate.ephemeralExpiration = protocolMessage.ephemeralExpiration + + if(isJidGroup(jid)) { + emitGroupUpdate({ ephemeralDuration: protocolMessage.ephemeralExpiration || null }) + } + + break + default: + break + } + } + + // check if the message is an action + if(message.messageStubType) { + const { user } = state.legacy! + //let actor = jidNormalizedUser (message.participant) + let participants: string[] + const emitParticipantsUpdate = (action: ParticipantAction) => ( + ev.emit('group-participants.update', { id: jid, participants, action }) + ) + + switch (message.messageStubType) { + case WAMessageStubType.CHANGE_EPHEMERAL_SETTING: + chatUpdate.ephemeralSettingTimestamp = message.messageTimestamp + chatUpdate.ephemeralExpiration = +message.messageStubParameters[0] + if(isJidGroup(jid)) { + emitGroupUpdate({ ephemeralDuration: +message.messageStubParameters[0] || null }) + } + + break + case WAMessageStubType.GROUP_PARTICIPANT_LEAVE: + case WAMessageStubType.GROUP_PARTICIPANT_REMOVE: + participants = message.messageStubParameters.map (jidNormalizedUser) + emitParticipantsUpdate('remove') + // mark the chat read only if you left the group + if(participants.includes(user.id)) { + chatUpdate.readOnly = true + } + + break + case WAMessageStubType.GROUP_PARTICIPANT_ADD: + case WAMessageStubType.GROUP_PARTICIPANT_INVITE: + case WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN: + participants = message.messageStubParameters.map (jidNormalizedUser) + if(participants.includes(user.id)) { + chatUpdate.readOnly = null + } + + emitParticipantsUpdate('add') + break + case WAMessageStubType.GROUP_CHANGE_ANNOUNCE: + const announce = message.messageStubParameters[0] === 'on' + emitGroupUpdate({ announce }) + break + case WAMessageStubType.GROUP_CHANGE_RESTRICT: + const restrict = message.messageStubParameters[0] === 'on' + emitGroupUpdate({ restrict }) + break + case WAMessageStubType.GROUP_CHANGE_SUBJECT: + case WAMessageStubType.GROUP_CREATE: + chatUpdate.name = message.messageStubParameters[0] + emitGroupUpdate({ subject: chatUpdate.name }) + break + } + } + + if(Object.keys(chatUpdate).length > 1) { + ev.emit('chats.update', [chatUpdate]) + } + + ev.emit('messages.upsert', { messages: [message], type }) + } + + const waUploadToServer = getWAUploadToServer(config, refreshMediaConn) + + /** Query a string to check if it has a url, if it does, return WAUrlInfo */ + const generateUrlInfo = async(text: string) => { + const response: BinaryNode = await query({ + json: { + tag: 'query', + attrs: { + type: 'url', + url: text, + epoch: currentEpoch().toString() + } + }, + binaryTag: [26, WAFlag.ignore], + expect200: true, + requiresPhoneConnection: false + }) + const urlInfo = { ...response.attrs } as any as WAUrlInfo + if(response && response.content) { + urlInfo.jpegThumbnail = response.content as Buffer + } + + return urlInfo + } + + /** Relay (send) a WAMessage; more advanced functionality to send a built WA Message, you may want to stick with sendMessage() */ + const relayMessage = async(message: WAMessage, { waitForAck } = { waitForAck: true }) => { + const json: BinaryNode = { + tag: 'action', + attrs: { epoch: currentEpoch().toString(), type: 'relay' }, + content: [ + { + tag: 'message', + attrs: {}, + content: proto.WebMessageInfo.encode(message).finish() + } + ] + } + const isMsgToMe = areJidsSameUser(message.key.remoteJid, state.legacy.user?.id || '') + const flag = isMsgToMe ? WAFlag.acknowledge : WAFlag.ignore // acknowledge when sending message to oneself + const mID = message.key.id + const finalState = isMsgToMe ? WAMessageStatus.READ : WAMessageStatus.SERVER_ACK + + message.status = WAMessageStatus.PENDING + const promise = query({ + json, + binaryTag: [WAMetric.message, flag], + tag: mID, + expect200: true, + requiresPhoneConnection: true + }) + + if(waitForAck) { + await promise + message.status = finalState + } else { + const emitUpdate = (status: WAMessageStatus) => { + message.status = status + ev.emit('messages.update', [ { key: message.key, update: { status } } ]) + } + + promise + .then(() => emitUpdate(finalState)) + .catch(() => emitUpdate(WAMessageStatus.ERROR)) + } + + if(config.emitOwnEvents) { + onMessage(message, 'append') + } + } + + // messages received + const messagesUpdate = (node: BinaryNode, isLatest: boolean) => { + const messages = getBinaryNodeMessages(node) + messages.reverse() + ev.emit('messages.set', { messages, isLatest }) + } + + socketEvents.on('CB:action,add:last', json => messagesUpdate(json, true)) + socketEvents.on('CB:action,add:unread', json => messagesUpdate(json, false)) + socketEvents.on('CB:action,add:before', json => messagesUpdate(json, false)) + + // new messages + socketEvents.on('CB:action,add:relay,message', (node: BinaryNode) => { + const msgs = getBinaryNodeMessages(node) + for(const msg of msgs) { + onMessage(msg, 'notify') + } + }) + // If a message has been updated + // usually called when a video message gets its upload url, or live locations or ciphertext message gets fixed + socketEvents.on ('CB:action,add:update,message', (node: BinaryNode) => { + const msgs = getBinaryNodeMessages(node) + for(const msg of msgs) { + onMessage(msg, 'replace') + } + }) + // message status updates + const onMessageStatusUpdate = ({ content }: BinaryNode) => { + if(Array.isArray(content)) { + const updates: WAMessageUpdate[] = [] + for(const { attrs: json } of content) { + const key: WAMessageKey = { + remoteJid: jidNormalizedUser(json.jid), + id: json.index, + fromMe: json.owner === 'true' + } + const status = STATUS_MAP[json.type] + + if(status) { + updates.push({ key, update: { status } }) + } else { + logger.warn({ content, key }, 'got unknown status update for message') + } + } + + ev.emit('messages.update', updates) + } + } + + const onMessageInfoUpdate = ([, attributes]: [string, {[_: string]: any}]) => { + let ids = attributes.id as string[] | string + if(typeof ids === 'string') { + ids = [ids] + } + + let updateKey: keyof MessageUserReceipt + switch (attributes.ack.toString()) { + case '2': + updateKey = 'receiptTimestamp' + break + case '3': + updateKey = 'readTimestamp' + break + case '4': + updateKey = 'playedTimestamp' + break + default: + logger.warn({ attributes }, 'received unknown message info update') + return + } + + const keyPartial = { + remoteJid: jidNormalizedUser(attributes.to), + fromMe: areJidsSameUser(attributes.from, state.legacy?.user?.id || ''), + } + + const userJid = jidNormalizedUser(attributes.participant || attributes.to) + + const updates = ids.map(id => ({ + key: { ...keyPartial, id }, + receipt: { + userJid, + [updateKey]: +attributes.t + } + })) + ev.emit('message-receipt.update', updates) + // for individual messages + // it means the message is marked read/delivered + if(!isJidGroup(keyPartial.remoteJid)) { + ev.emit('messages.update', ids.map(id => ( + { + key: { ...keyPartial, id }, + update: { + status: updateKey === 'receiptTimestamp' ? WAMessageStatus.DELIVERY_ACK : WAMessageStatus.READ + } + } + ))) + } + } + + socketEvents.on('CB:action,add:relay,received', onMessageStatusUpdate) + socketEvents.on('CB:action,,received', onMessageStatusUpdate) + + socketEvents.on('CB:Msg', onMessageInfoUpdate) + socketEvents.on('CB:MsgInfo', onMessageInfoUpdate) + + return { + ...sock, + relayMessage, + generateUrlInfo, + messageInfo: async(jid: string, messageID: string) => { + const { content }: BinaryNode = await query({ + json: { + tag: 'query', + attrs: { + type: 'message_info', + index: messageID, + jid: jid, + epoch: currentEpoch().toString() + } + }, + binaryTag: [WAMetric.queryRead, WAFlag.ignore], + expect200: true, + requiresPhoneConnection: true + }) + const info: { [jid: string]: MessageUserReceipt } = { } + if(Array.isArray(content)) { + for(const { tag, content: innerData } of content) { + const [{ attrs }] = (innerData as BinaryNode[]) + + const jid = jidNormalizedUser(attrs.jid) + const recp = info[jid] || { userJid: jid } + const date = +attrs.t + switch (tag) { + case 'read': + recp.readTimestamp = date + break + case 'delivery': + recp.receiptTimestamp = date + break + } + + info[jid] = recp + } + } + + return Object.values(info) + }, + downloadMediaMessage: async(message: WAMessage, type: 'buffer' | 'stream' = 'buffer') => { + const downloadMediaMessage = async() => { + const mContent = extractMessageContent(message.message) + if(!mContent) { + throw new Boom('No message present', { statusCode: 400, data: message }) + } + + const stream = await decryptMediaMessageBuffer(mContent) + if(type === 'buffer') { + let buffer = Buffer.from([]) + for await (const chunk of stream) { + buffer = Buffer.concat([buffer, chunk]) + } + + return buffer + } + + return stream + } + + try { + const result = await downloadMediaMessage() + return result + } catch(error) { + if(error.message.includes('404')) { // media needs to be updated + logger.info (`updating media of message: ${message.key.id}`) + + await updateMediaMessage(message) + + const result = await downloadMediaMessage() + return result + } + + throw error + } + }, + updateMediaMessage, + fetchMessagesFromWA, + /** Load a single message specified by the ID */ + loadMessageFromWA: async(jid: string, id: string) => { + // load the message before the given message + let messages = (await fetchMessagesFromWA(jid, 1, { before: { id, fromMe: true } })) + if(!messages[0]) { + messages = (await fetchMessagesFromWA(jid, 1, { before: { id, fromMe: false } })) + } + + // the message after the loaded message is the message required + const [actual] = await fetchMessagesFromWA(jid, 1, { after: messages[0] && messages[0].key }) + return actual + }, + searchMessages: async(txt: string, inJid: string | null, count: number, page: number) => { + const node: BinaryNode = await query({ + json: { + tag: 'query', + attrs: { + epoch: currentEpoch().toString(), + type: 'search', + search: txt, + count: count.toString(), + page: page.toString(), + jid: inJid + } + }, + binaryTag: [24, WAFlag.ignore], + expect200: true + }) // encrypt and send off + + return { + last: node.attrs?.last === 'true', + messages: getBinaryNodeMessages(node) + } + }, + sendMessage: async( + jid: string, + content: AnyMessageContent, + options: MiscMessageGenerationOptions & { waitForAck?: boolean } = { waitForAck: true } + ) => { + const userJid = state.legacy.user?.id + if( + typeof content === 'object' && + 'disappearingMessagesInChat' in content && + typeof content['disappearingMessagesInChat'] !== 'undefined' && + isJidGroup(jid) + ) { + const { disappearingMessagesInChat } = content + const value = typeof disappearingMessagesInChat === 'boolean' ? + (disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : + disappearingMessagesInChat + const tag = generateMessageTag(true) + await setQuery([ + { + tag: 'group', + attrs: { id: tag, jid, type: 'prop', author: userJid }, + content: [ + { tag: 'ephemeral', attrs: { value: value.toString() } } + ] + } + ]) + } else { + const msg = await generateWAMessage( + jid, + content, + { + logger, + userJid: userJid, + getUrlInfo: generateUrlInfo, + upload: waUploadToServer, + mediaCache: config.mediaCache, + ...options, + } + ) + + await relayMessage(msg, { waitForAck: options.waitForAck }) + return msg + } + } + } +} + +export default makeMessagesSocket \ No newline at end of file diff --git a/src/LegacySocket/socket.ts b/src/LegacySocket/socket.ts new file mode 100644 index 0000000..333ca99 --- /dev/null +++ b/src/LegacySocket/socket.ts @@ -0,0 +1,431 @@ +import { Boom } from '@hapi/boom' +import { STATUS_CODES } from 'http' +import { promisify } from 'util' +import WebSocket from 'ws' +import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, DEFAULT_ORIGIN, PHONE_CONNECTION_CB } from '../Defaults' +import { DisconnectReason, LegacySocketConfig, SocketQueryOptions, SocketSendMessageOptions, WAFlag, WAMetric, WATag } from '../Types' +import { aesEncrypt, decodeWAMessage, hmacSign, promiseTimeout, unixTimestampSeconds } from '../Utils' +import { BinaryNode, encodeBinaryNodeLegacy } from '../WABinary' + +/** + * Connects to WA servers and performs: + * - simple queries (no retry mechanism, wait for connection establishment) + * - listen to messages and emit events + * - query phone connection + */ +export const makeSocket = ({ + waWebSocketUrl, + connectTimeoutMs, + phoneResponseTimeMs, + logger, + agent, + keepAliveIntervalMs, + expectResponseTimeout, +}: LegacySocketConfig) => { + // for generating tags + const referenceDateSeconds = unixTimestampSeconds(new Date()) + const ws = new WebSocket(waWebSocketUrl, undefined, { + origin: DEFAULT_ORIGIN, + timeout: connectTimeoutMs, + agent, + headers: { + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'en-US,en;q=0.9', + 'Cache-Control': 'no-cache', + 'Host': 'web.whatsapp.com', + 'Pragma': 'no-cache', + 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits', + } + }) + ws.setMaxListeners(0) + let lastDateRecv: Date + let epoch = 0 + let authInfo: { encKey: Buffer, macKey: Buffer } + let keepAliveReq: NodeJS.Timeout + + let phoneCheckInterval: NodeJS.Timeout + let phoneCheckListeners = 0 + + const phoneConnectionChanged = (value: boolean) => { + ws.emit('phone-connection', { value }) + } + + const sendPromise = promisify(ws.send) + /** generate message tag and increment epoch */ + const generateMessageTag = (longTag: boolean = false) => { + const tag = `${longTag ? referenceDateSeconds : (referenceDateSeconds%1000)}.--${epoch}` + epoch += 1 // increment message count, it makes the 'epoch' field when sending binary messages + return tag + } + + const sendRawMessage = (data: Buffer | string) => { + if(ws.readyState !== ws.OPEN) { + throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }) + } + + return sendPromise.call(ws, data) as Promise + } + + /** + * Send a message to the WA servers + * @returns the tag attached in the message + * */ + const sendNode = async( + { json, binaryTag, tag, longTag }: SocketSendMessageOptions + ) => { + tag = tag || generateMessageTag(longTag) + let data: Buffer | string + if(logger.level === 'trace') { + logger.trace({ tag, fromMe: true, json, binaryTag }, 'communication') + } + + if(binaryTag) { + if(Array.isArray(json)) { + throw new Boom('Expected BinaryNode with binary code', { statusCode: 400 }) + } + + if(!authInfo) { + throw new Boom('No encryption/mac keys to encrypt node with', { statusCode: 400 }) + } + + const binary = encodeBinaryNodeLegacy(json) // encode the JSON to the WhatsApp binary format + + const buff = aesEncrypt(binary, authInfo.encKey) // encrypt it using AES and our encKey + const sign = hmacSign(buff, authInfo.macKey) // sign the message using HMAC and our macKey + + data = Buffer.concat([ + Buffer.from(tag + ','), // generate & prefix the message tag + Buffer.from(binaryTag), // prefix some bytes that tell whatsapp what the message is about + sign, // the HMAC sign of the message + buff, // the actual encrypted buffer + ]) + } else { + data = `${tag},${JSON.stringify(json)}` + } + + await sendRawMessage(data) + return tag + } + + const end = (error: Error | undefined) => { + logger.info({ error }, 'connection closed') + + ws.removeAllListeners('close') + ws.removeAllListeners('error') + ws.removeAllListeners('open') + ws.removeAllListeners('message') + + phoneCheckListeners = 0 + clearInterval(keepAliveReq) + clearPhoneCheckInterval() + + if(ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) { + try { + ws.close() + } catch{ } + } + + ws.emit('ws-close', error) + ws.removeAllListeners('ws-close') + } + + const onMessageRecieved = (message: string | Buffer) => { + if(message[0] === '!' || message[0] === '!'.charCodeAt(0)) { + // when the first character in the message is an '!', the server is sending a pong frame + const timestamp = message.slice(1, message.length).toString() + lastDateRecv = new Date(parseInt(timestamp)) + ws.emit('received-pong') + } else { + let messageTag: string + let json: any + try { + const dec = decodeWAMessage(message, authInfo) + messageTag = dec[0] + json = dec[1] + if(!json) { + return + } + } catch(error) { + end(error) + return + } + //if (this.shouldLogMessages) this.messageLog.push ({ tag: messageTag, json: JSON.stringify(json), fromMe: false }) + + if(logger.level === 'trace') { + logger.trace({ tag: messageTag, fromMe: false, json }, 'communication') + } + + let anyTriggered = false + /* Check if this is a response to a message we sent */ + anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${messageTag}`, json) + /* Check if this is a response to a message we are expecting */ + const l0 = json.tag || json[0] || '' + const l1 = json?.attrs || json?.[1] || { } + const l2 = json?.content?.[0]?.tag || json[2]?.[0] || '' + + Object.keys(l1).forEach(key => { + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, json) || anyTriggered + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, json) || anyTriggered + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, json) || anyTriggered + }) + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, json) || anyTriggered + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, json) || anyTriggered + + if(!anyTriggered && logger.level === 'debug') { + logger.debug({ unhandled: true, tag: messageTag, fromMe: false, json }, 'communication recv') + } + } + } + + /** Exits a query if the phone connection is active and no response is still found */ + const exitQueryIfResponseNotExpected = (tag: string, cancel: (error: Boom) => void) => { + let timeout: NodeJS.Timeout + const listener = ([, connected]) => { + if(connected) { + timeout = setTimeout(() => { + logger.info({ tag }, 'cancelling wait for message as a response is no longer expected from the phone') + cancel(new Boom('Not expecting a response', { statusCode: 422 })) + }, expectResponseTimeout) + ws.off(PHONE_CONNECTION_CB, listener) + } + } + + ws.on(PHONE_CONNECTION_CB, listener) + return () => { + ws.off(PHONE_CONNECTION_CB, listener) + timeout && clearTimeout(timeout) + } + } + + /** interval is started when a query takes too long to respond */ + const startPhoneCheckInterval = () => { + phoneCheckListeners += 1 + if(!phoneCheckInterval) { + // if its been a long time and we haven't heard back from WA, send a ping + phoneCheckInterval = setInterval(() => { + if(phoneCheckListeners <= 0) { + logger.warn('phone check called without listeners') + return + } + + logger.info('checking phone connection...') + sendAdminTest() + + phoneConnectionChanged(false) + }, phoneResponseTimeMs) + } + } + + const clearPhoneCheckInterval = () => { + phoneCheckListeners -= 1 + if(phoneCheckListeners <= 0) { + clearInterval(phoneCheckInterval) + phoneCheckInterval = undefined + phoneCheckListeners = 0 + } + } + + /** checks for phone connection */ + const sendAdminTest = () => sendNode({ json: ['admin', 'test'] }) + /** + * Wait for a message with a certain tag to be received + * @param tag the message tag to await + * @param json query that was sent + * @param timeoutMs timeout after which the promise will reject + */ + const waitForMessage = (tag: string, requiresPhoneConnection: boolean, timeoutMs?: number) => { + if(ws.readyState !== ws.OPEN) { + throw new Boom('Connection not open', { statusCode: DisconnectReason.connectionClosed }) + } + + let cancelToken = () => { } + + return { + promise: (async() => { + let onRecv: (json) => void + let onErr: (err) => void + let cancelPhoneChecker: () => void + try { + const result = await promiseTimeout(timeoutMs, + (resolve, reject) => { + onRecv = resolve + onErr = err => { + reject(err || new Boom('Intentional Close', { statusCode: DisconnectReason.connectionClosed })) + } + + cancelToken = () => onErr(new Boom('Cancelled', { statusCode: 500 })) + + if(requiresPhoneConnection) { + startPhoneCheckInterval() + cancelPhoneChecker = exitQueryIfResponseNotExpected(tag, onErr) + } + + ws.on(`TAG:${tag}`, onRecv) + ws.on('ws-close', onErr) // if the socket closes, you'll never receive the message + }, + ) + return result as any + } finally { + requiresPhoneConnection && clearPhoneCheckInterval() + cancelPhoneChecker && cancelPhoneChecker() + + ws.off(`TAG:${tag}`, onRecv) + ws.off('ws-close', onErr) // if the socket closes, you'll never receive the message + } + })(), + cancelToken: () => { + cancelToken() + } + } + } + + /** + * Query something from the WhatsApp servers + * @param json the query itself + * @param binaryTags the tags to attach if the query is supposed to be sent encoded in binary + * @param timeoutMs timeout after which the query will be failed (set to null to disable a timeout) + * @param tag the tag to attach to the message + */ + const query = async( + { json, timeoutMs, expect200, tag, longTag, binaryTag, requiresPhoneConnection }: SocketQueryOptions + ) => { + tag = tag || generateMessageTag(longTag) + const { promise, cancelToken } = waitForMessage(tag, requiresPhoneConnection, timeoutMs) + try { + await sendNode({ json, tag, binaryTag }) + } catch(error) { + cancelToken() + // swallow error + await promise.catch(() => { }) + // throw back the error + throw error + } + + const response = await promise + const responseStatusCode = +(response.status ? response.status : 200) // default status + // read here: http://getstatuscode.com/599 + if(responseStatusCode === 599) { // the connection has gone bad + end(new Boom('WA server overloaded', { statusCode: 599, data: { query: json, response } })) + } + + if(expect200 && Math.floor(responseStatusCode/100) !== 2) { + const message = STATUS_CODES[responseStatusCode] || 'unknown' + throw new Boom( + `Unexpected status in '${Array.isArray(json) ? json[0] : (json?.tag || 'query')}': ${message}(${responseStatusCode})`, + { data: { query: json, response }, statusCode: response.status } + ) + } + + return response + } + + const startKeepAliveRequest = () => ( + keepAliveReq = setInterval(() => { + if(!lastDateRecv) { + lastDateRecv = new Date() + } + + const diff = Date.now() - lastDateRecv.getTime() + /* + check if it's been a suspicious amount of time since the server responded with our last seen + it could be that the network is down + */ + if(diff > keepAliveIntervalMs+5000) { + end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost })) + } else if(ws.readyState === ws.OPEN) { + sendRawMessage('?,,') // if its all good, send a keep alive request + } else { + logger.warn('keep alive called when WS not open') + } + }, keepAliveIntervalMs) + ) + + const waitForSocketOpen = async() => { + if(ws.readyState === ws.OPEN) { + return + } + + if(ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) { + throw new Boom('Connection Already Closed', { statusCode: DisconnectReason.connectionClosed }) + } + + let onOpen: () => void + let onClose: (err: Error) => void + await new Promise((resolve, reject) => { + onOpen = () => resolve(undefined) + onClose = reject + ws.on('open', onOpen) + ws.on('close', onClose) + ws.on('error', onClose) + }) + .finally(() => { + ws.off('open', onOpen) + ws.off('close', onClose) + ws.off('error', onClose) + }) + } + + ws.on('message', onMessageRecieved) + ws.on('open', () => { + startKeepAliveRequest() + logger.info('Opened WS connection to WhatsApp Web') + }) + ws.on('error', end) + ws.on('close', () => end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionLost }))) + + ws.on(PHONE_CONNECTION_CB, json => { + if(!json[1]) { + end(new Boom('Connection terminated by phone', { statusCode: DisconnectReason.connectionLost })) + logger.info('Connection terminated by phone, closing...') + } else { + phoneConnectionChanged(true) + } + }) + ws.on('CB:Cmd,type:disconnect', json => { + const { kind } = json[1] + let reason: DisconnectReason + switch (kind) { + case 'replaced': + reason = DisconnectReason.connectionReplaced + break + default: + reason = DisconnectReason.connectionLost + break + } + + end(new Boom( + `Connection terminated by server: "${kind || 'unknown'}"`, + { statusCode: reason } + )) + }) + + return { + type: 'legacy' as 'legacy', + ws, + sendAdminTest, + updateKeys: (info: { encKey: Buffer, macKey: Buffer }) => authInfo = info, + waitForSocketOpen, + sendNode, + generateMessageTag, + waitForMessage, + query, + /** Generic function for action, set queries */ + setQuery: async(nodes: BinaryNode[], binaryTag: WATag = [WAMetric.group, WAFlag.ignore], tag?: string) => { + const json: BinaryNode = { + tag: 'action', + attrs: { epoch: epoch.toString(), type: 'set' }, + content: nodes + } + + return query({ + json, + binaryTag, + tag, + expect200: true, + requiresPhoneConnection: true + }) as Promise<{ status: number }> + }, + currentEpoch: () => epoch, + end + } +} \ No newline at end of file diff --git a/src/Socket/chats.ts b/src/Socket/chats.ts new file mode 100644 index 0000000..385dd18 --- /dev/null +++ b/src/Socket/chats.ts @@ -0,0 +1,701 @@ +import { Boom } from '@hapi/boom' +import { proto } from '../../WAProto' +import { AppStateChunk, Chat, ChatModification, ChatMutation, Contact, LTHashState, PresenceData, SocketConfig, WABusinessHoursConfig, WABusinessProfile, WAMediaUpload, WAPatchCreate, WAPatchName, WAPresence } from '../Types' +import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, extractSyncdPatches, generateProfilePicture, newLTHashState, toNumber } from '../Utils' +import makeMutex from '../Utils/make-mutex' +import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary' +import { makeMessagesSocket } from './messages-send' + +const MAX_SYNC_ATTEMPTS = 5 + +export const makeChatsSocket = (config: SocketConfig) => { + const { logger } = config + const sock = makeMessagesSocket(config) + const { + ev, + ws, + authState, + generateMessageTag, + sendNode, + query, + fetchPrivacySettings, + } = sock + + const mutationMutex = makeMutex() + + const getAppStateSyncKey = async(keyId: string) => { + const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]) + return key + } + + const interactiveQuery = async(userNodes: BinaryNode[], queryNode: BinaryNode) => { + const result = await query({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'get', + xmlns: 'usync', + }, + content: [ + { + tag: 'usync', + attrs: { + sid: generateMessageTag(), + mode: 'query', + last: 'true', + index: '0', + context: 'interactive', + }, + content: [ + { + tag: 'query', + attrs: { }, + content: [ queryNode ] + }, + { + tag: 'list', + attrs: { }, + content: userNodes + } + ] + } + ], + }) + + const usyncNode = getBinaryNodeChild(result, 'usync') + const listNode = getBinaryNodeChild(usyncNode, 'list') + const users = getBinaryNodeChildren(listNode, 'user') + + return users + } + + const onWhatsApp = async(...jids: string[]) => { + const results = await interactiveQuery( + [ + { + tag: 'user', + attrs: { }, + content: jids.map( + jid => ({ + tag: 'contact', + attrs: { }, + content: `+${jid}` + }) + ) + } + ], + { tag: 'contact', attrs: { } } + ) + + return results.map(user => { + const contact = getBinaryNodeChild(user, 'contact') + return { exists: contact.attrs.type === 'in', jid: user.attrs.jid } + }).filter(item => item.exists) + } + + const fetchStatus = async(jid: string) => { + const [result] = await interactiveQuery( + [{ tag: 'user', attrs: { jid } }], + { tag: 'status', attrs: { } } + ) + if(result) { + const status = getBinaryNodeChild(result, 'status') + return { + status: status.content!.toString(), + setAt: new Date(+status.attrs.t * 1000) + } + } + } + + const updateProfilePicture = async(jid: string, content: WAMediaUpload) => { + const { img } = await generateProfilePicture(content) + await query({ + tag: 'iq', + attrs: { + to: jidNormalizedUser(jid), + type: 'set', + xmlns: 'w:profile:picture' + }, + content: [ + { + tag: 'picture', + attrs: { type: 'image' }, + content: img + } + ] + }) + } + + const fetchBlocklist = async() => { + const result = await query({ + tag: 'iq', + attrs: { + xmlns: 'blocklist', + to: S_WHATSAPP_NET, + type: 'get' + } + }) + const child = result.content?.[0] as BinaryNode + return (child.content as BinaryNode[])?.map(i => i.attrs.jid) + } + + const updateBlockStatus = async(jid: string, action: 'block' | 'unblock') => { + await query({ + tag: 'iq', + attrs: { + xmlns: 'blocklist', + to: S_WHATSAPP_NET, + type: 'set' + }, + content: [ + { + tag: 'item', + attrs: { + action, + jid + } + } + ] + }) + } + + const getBusinessProfile = async(jid: string): Promise => { + const results = await query({ + tag: 'iq', + attrs: { + to: 's.whatsapp.net', + xmlns: 'w:biz', + type: 'get' + }, + content: [{ + tag: 'business_profile', + attrs: { v: '244' }, + content: [{ + tag: 'profile', + attrs: { jid } + }] + }] + }) + const profiles = getBinaryNodeChild(getBinaryNodeChild(results, 'business_profile'), 'profile') + if(!profiles) { + // if not bussines + if(logger.level === 'trace') { + logger.trace({ jid }, 'Not bussines') + } + + return + } + + const address = getBinaryNodeChild(profiles, 'address') + const description = getBinaryNodeChild(profiles, 'description') + const website = getBinaryNodeChild(profiles, 'website') + const email = getBinaryNodeChild(profiles, 'email') + const category = getBinaryNodeChild(getBinaryNodeChild(profiles, 'categories'), 'category') + const business_hours = getBinaryNodeChild(profiles, 'business_hours') + const business_hours_config = business_hours && getBinaryNodeChildren(business_hours, 'business_hours_config') + return { + wid: profiles.attrs?.jid, + address: address?.content.toString(), + description: description?.content.toString(), + website: [website?.content.toString()], + email: email?.content.toString(), + category: category?.content.toString(), + business_hours: { + timezone: business_hours?.attrs?.timezone, + business_config: business_hours_config?.map(({ attrs }) => attrs as unknown as WABusinessHoursConfig) + } + } as unknown as WABusinessProfile + } + + const updateAccountSyncTimestamp = async(fromTimestamp: number | string) => { + logger.info({ fromTimestamp }, 'requesting account sync') + await sendNode({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'set', + xmlns: 'urn:xmpp:whatsapp:dirty', + id: generateMessageTag(), + }, + content: [ + { + tag: 'clean', + attrs: { + type: 'account_sync', + timestamp: fromTimestamp.toString(), + } + } + ] + }) + } + + const resyncAppState = async(collections: WAPatchName[]) => { + const appStateChunk: AppStateChunk = { totalMutations: [], collectionsToHandle: [] } + // we use this to determine which events to fire + // otherwise when we resync from scratch -- all notifications will fire + const initialVersionMap: { [T in WAPatchName]?: number } = { } + + await authState.keys.transaction( + async() => { + const collectionsToHandle = new Set(collections) + // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from + const attemptsMap = { } as { [T in WAPatchName]: number | undefined } + // keep executing till all collections are done + // sometimes a single patch request will not return all the patches (God knows why) + // so we fetch till they're all done (this is determined by the "has_more_patches" flag) + while(collectionsToHandle.size) { + const states = { } as { [T in WAPatchName]: LTHashState } + const nodes: BinaryNode[] = [] + + for(const name of collectionsToHandle) { + const result = await authState.keys.get('app-state-sync-version', [name]) + let state = result[name] + + if(state) { + if(typeof initialVersionMap[name] === 'undefined') { + initialVersionMap[name] = state.version + } + } else { + state = newLTHashState() + } + + states[name] = state + + logger.info(`resyncing ${name} from v${state.version}`) + + nodes.push({ + tag: 'collection', + attrs: { + name, + version: state.version.toString(), + // return snapshot if being synced from scratch + return_snapshot: (!state.version).toString() + } + }) + } + + const result = await query({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + xmlns: 'w:sync:app:state', + type: 'set' + }, + content: [ + { + tag: 'sync', + attrs: { }, + content: nodes + } + ] + }) + + const decoded = await extractSyncdPatches(result) // extract from binary node + for(const key in decoded) { + const name = key as WAPatchName + const { patches, hasMorePatches, snapshot } = decoded[name] + try { + if(snapshot) { + const { state: newState, mutations } = await decodeSyncdSnapshot(name, snapshot, getAppStateSyncKey, initialVersionMap[name]) + states[name] = newState + + logger.info(`restored state of ${name} from snapshot to v${newState.version} with ${mutations.length} mutations`) + + await authState.keys.set({ 'app-state-sync-version': { [name]: newState } }) + + appStateChunk.totalMutations.push(...mutations) + } + + // only process if there are syncd patches + if(patches.length) { + const { newMutations, state: newState } = await decodePatches(name, patches, states[name], getAppStateSyncKey, initialVersionMap[name]) + + await authState.keys.set({ 'app-state-sync-version': { [name]: newState } }) + + logger.info(`synced ${name} to v${newState.version}`) + if(newMutations.length) { + logger.trace({ newMutations, name }, 'recv new mutations') + } + + appStateChunk.totalMutations.push(...newMutations) + } + + if(hasMorePatches) { + logger.info(`${name} has more patches...`) + } else { // collection is done with sync + collectionsToHandle.delete(name) + } + } catch(error) { + logger.info({ name, error: error.stack }, 'failed to sync state from version, removing and trying from scratch') + await authState.keys.set({ 'app-state-sync-version': { [name]: null } }) + // increment number of retries + attemptsMap[name] = (attemptsMap[name] || 0) + 1 + // if retry attempts overshoot + // or key not found + if(attemptsMap[name] >= MAX_SYNC_ATTEMPTS || error.output?.statusCode === 404) { + // stop retrying + collectionsToHandle.delete(name) + } + } + } + } + } + ) + + processSyncActions(appStateChunk.totalMutations) + + return appStateChunk + } + + /** + * fetch the profile picture of a user/group + * type = "preview" for a low res picture + * type = "image for the high res picture" + */ + const profilePictureUrl = async(jid: string, type: 'preview' | 'image' = 'preview', timeoutMs?: number) => { + jid = jidNormalizedUser(jid) + const result = await query({ + tag: 'iq', + attrs: { + to: jid, + type: 'get', + xmlns: 'w:profile:picture' + }, + content: [ + { tag: 'picture', attrs: { type, query: 'url' } } + ] + }, timeoutMs) + const child = getBinaryNodeChild(result, 'picture') + return child?.attrs?.url + } + + const sendPresenceUpdate = async(type: WAPresence, toJid?: string) => { + const me = authState.creds.me! + if(type === 'available' || type === 'unavailable') { + await sendNode({ + tag: 'presence', + attrs: { + name: me!.name, + type + } + }) + } else { + await sendNode({ + tag: 'chatstate', + attrs: { + from: me!.id!, + to: toJid, + }, + content: [ + { tag: type, attrs: { } } + ] + }) + } + } + + const presenceSubscribe = (toJid: string) => ( + sendNode({ + tag: 'presence', + attrs: { + to: toJid, + id: generateMessageTag(), + type: 'subscribe' + } + }) + ) + + const handlePresenceUpdate = ({ tag, attrs, content }: BinaryNode) => { + let presence: PresenceData + const jid = attrs.from + const participant = attrs.participant || attrs.from + if(tag === 'presence') { + presence = { + lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available', + lastSeen: attrs.t ? +attrs.t : undefined + } + } else if(Array.isArray(content)) { + const [firstChild] = content + let type = firstChild.tag as WAPresence + if(type === 'paused') { + type = 'available' + } + + presence = { lastKnownPresence: type } + } else { + logger.error({ tag, attrs, content }, 'recv invalid presence node') + } + + if(presence) { + ev.emit('presence.update', { id: jid, presences: { [participant]: presence } }) + } + } + + const resyncMainAppState = async() => { + + logger.debug('resyncing main app state') + + await ( + mutationMutex.mutex( + () => resyncAppState([ + 'critical_block', + 'critical_unblock_low', + 'regular_high', + 'regular_low', + 'regular' + ]) + ) + .catch(err => ( + logger.warn({ trace: err.stack }, 'failed to sync app state') + )) + ) + } + + const processSyncActions = (actions: ChatMutation[]) => { + const updates: { [jid: string]: Partial } = {} + const contactUpdates: { [jid: string]: Contact } = {} + const msgDeletes: proto.IMessageKey[] = [] + + for(const { syncAction: { value: action }, index: [_, id, msgId, fromMe] } of actions) { + const update: Partial = { id } + if(action?.muteAction) { + update.mute = action.muteAction?.muted ? + toNumber(action.muteAction!.muteEndTimestamp!) : + undefined + } else if(action?.archiveChatAction) { + update.archive = !!action.archiveChatAction?.archived + } else if(action?.markChatAsReadAction) { + update.unreadCount = !!action.markChatAsReadAction?.read ? 0 : -1 + } else if(action?.clearChatAction) { + msgDeletes.push({ + remoteJid: id, + id: msgId, + fromMe: fromMe === '1' + }) + } else if(action?.contactAction) { + contactUpdates[id] = { + ...(contactUpdates[id] || {}), + id, + name: action.contactAction!.fullName + } + } else if(action?.pushNameSetting) { + const me = { + ...authState.creds.me!, + name: action?.pushNameSetting?.name! + } + ev.emit('creds.update', { me }) + } else if(action?.pinAction) { + update.pin = action.pinAction?.pinned ? toNumber(action.timestamp) : undefined + } else { + logger.warn({ action, id }, 'unprocessable update') + } + + if(Object.keys(update).length > 1) { + updates[update.id] = { + ...(updates[update.id] || {}), + ...update + } + } + } + + if(Object.values(updates).length) { + ev.emit('chats.update', Object.values(updates)) + } + + if(Object.values(contactUpdates).length) { + ev.emit('contacts.upsert', Object.values(contactUpdates)) + } + + if(msgDeletes.length) { + ev.emit('messages.delete', { keys: msgDeletes }) + } + } + + const appPatch = async(patchCreate: WAPatchCreate) => { + const name = patchCreate.type + const myAppStateKeyId = authState.creds.myAppStateKeyId + if(!myAppStateKeyId) { + throw new Boom('App state key not present!', { statusCode: 400 }) + } + + await mutationMutex.mutex( + async() => { + logger.debug({ patch: patchCreate }, 'applying app patch') + + await resyncAppState([name]) + const { [name]: initial } = await authState.keys.get('app-state-sync-version', [name]) + const { patch, state } = await encodeSyncdPatch( + patchCreate, + myAppStateKeyId, + initial, + getAppStateSyncKey, + ) + + const node: BinaryNode = { + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'set', + xmlns: 'w:sync:app:state' + }, + content: [ + { + tag: 'sync', + attrs: { }, + content: [ + { + tag: 'collection', + attrs: { + name, + version: (state.version-1).toString(), + return_snapshot: 'false' + }, + content: [ + { + tag: 'patch', + attrs: { }, + content: proto.SyncdPatch.encode(patch).finish() + } + ] + } + ] + } + ] + } + await query(node) + + await authState.keys.set({ 'app-state-sync-version': { [name]: state } }) + + if(config.emitOwnEvents) { + const result = await decodePatches(name, [{ ...patch, version: { version: state.version }, }], initial, getAppStateSyncKey) + processSyncActions(result.newMutations) + } + } + ) + } + + /** sending abt props may fix QR scan fail if server expects */ + const fetchAbt = async() => { + const abtNode = await query({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + xmlns: 'abt', + type: 'get', + id: generateMessageTag(), + }, + content: [ + { tag: 'props', attrs: { protocol: '1' } } + ] + }) + + const propsNode = getBinaryNodeChild(abtNode, 'props') + + let props: { [_: string]: string } = { } + if(propsNode) { + props = reduceBinaryNodeToDictionary(propsNode, 'prop') + } + + logger.debug('fetched abt') + + return props + } + + /** sending non-abt props may fix QR scan fail if server expects */ + const fetchProps = async() => { + const resultNode = await query({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + xmlns: 'w', + type: 'get', + id: generateMessageTag(), + }, + content: [ + { tag: 'props', attrs: { } } + ] + }) + + const propsNode = getBinaryNodeChild(resultNode, 'props') + + let props: { [_: string]: string } = { } + if(propsNode) { + props = reduceBinaryNodeToDictionary(propsNode, 'prop') + } + + logger.debug('fetched props') + + return props + } + + /** + * modify a chat -- mark unread, read etc. + * lastMessages must be sorted in reverse chronologically + * requires the last messages till the last message received; required for archive & unread + */ + const chatModify = (mod: ChatModification, jid: string) => { + const patch = chatModificationToAppPatch(mod, jid) + return appPatch(patch) + } + + ws.on('CB:presence', handlePresenceUpdate) + ws.on('CB:chatstate', handlePresenceUpdate) + + ws.on('CB:ib,,dirty', async(node: BinaryNode) => { + const { attrs } = getBinaryNodeChild(node, 'dirty') + const type = attrs.type + switch (type) { + case 'account_sync': + let { lastAccountSyncTimestamp } = authState.creds + if(lastAccountSyncTimestamp) { + await updateAccountSyncTimestamp(lastAccountSyncTimestamp) + } + + lastAccountSyncTimestamp = +attrs.timestamp + ev.emit('creds.update', { lastAccountSyncTimestamp }) + break + default: + logger.info({ node }, 'received unknown sync') + break + } + }) + + ws.on('CB:notification,type:server_sync', (node: BinaryNode) => { + const update = getBinaryNodeChild(node, 'collection') + if(update) { + const name = update.attrs.name as WAPatchName + mutationMutex.mutex( + async() => { + await resyncAppState([name]) + .catch(err => logger.error({ trace: err.stack, node }, 'failed to sync state')) + } + ) + } + }) + + ev.on('connection.update', ({ connection }) => { + if(connection === 'open') { + sendPresenceUpdate('available') + fetchBlocklist() + fetchPrivacySettings() + fetchAbt() + fetchProps() + } + }) + + return { + ...sock, + appPatch, + sendPresenceUpdate, + presenceSubscribe, + profilePictureUrl, + onWhatsApp, + fetchBlocklist, + fetchStatus, + updateProfilePicture, + updateBlockStatus, + getBusinessProfile, + resyncAppState, + chatModify, + resyncMainAppState, + } +} \ No newline at end of file diff --git a/src/Socket/groups.ts b/src/Socket/groups.ts new file mode 100644 index 0000000..b6e8097 --- /dev/null +++ b/src/Socket/groups.ts @@ -0,0 +1,217 @@ +import { GroupMetadata, ParticipantAction, SocketConfig } from '../Types' +import { generateMessageID } from '../Utils' +import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, jidEncode, jidNormalizedUser } from '../WABinary' +import { makeSocket } from './socket' + +export const makeGroupsSocket = (config: SocketConfig) => { + const sock = makeSocket(config) + const { query } = sock + + const groupQuery = async(jid: string, type: 'get' | 'set', content: BinaryNode[]) => ( + query({ + tag: 'iq', + attrs: { + type, + xmlns: 'w:g2', + to: jid, + }, + content + }) + ) + + const groupMetadata = async(jid: string) => { + const result = await groupQuery( + jid, + 'get', + [ { tag: 'query', attrs: { request: 'interactive' } } ] + ) + return extractGroupMetadata(result) + } + + return { + ...sock, + groupMetadata, + groupCreate: async(subject: string, participants: string[]) => { + const key = generateMessageID() + const result = await groupQuery( + '@g.us', + 'set', + [ + { + tag: 'create', + attrs: { + subject, + key + }, + content: participants.map(jid => ({ + tag: 'participant', + attrs: { jid } + })) + } + ] + ) + return extractGroupMetadata(result) + }, + groupLeave: async(id: string) => { + await groupQuery( + '@g.us', + 'set', + [ + { + tag: 'leave', + attrs: { }, + content: [ + { tag: 'group', attrs: { id } } + ] + } + ] + ) + }, + groupUpdateSubject: async(jid: string, subject: string) => { + await groupQuery( + jid, + 'set', + [ + { + tag: 'subject', + attrs: { }, + content: Buffer.from(subject, 'utf-8') + } + ] + ) + }, + groupParticipantsUpdate: async( + jid: string, + participants: string[], + action: ParticipantAction + ) => { + const result = await groupQuery( + jid, + 'set', + participants.map( + jid => ({ + tag: action, + attrs: { }, + content: [{ tag: 'participant', attrs: { jid } }] + }) + ) + ) + const node = getBinaryNodeChild(result, action) + const participantsAffected = getBinaryNodeChildren(node!, 'participant') + return participantsAffected.map(p => p.attrs.jid) + }, + groupUpdateDescription: async(jid: string, description?: string) => { + const metadata = await groupMetadata(jid) + const prev = metadata.descId ?? null + + await groupQuery( + jid, + 'set', + [ + { + tag: 'description', + attrs: { + ...(description ? { id: generateMessageID() } : { delete: 'true' }), + ...(prev ? { prev } : {}) + }, + content: description ? [{ tag: 'body', attrs: {}, content: Buffer.from(description, 'utf-8') }] : null + } + ] + ) + }, + groupInviteCode: async(jid: string) => { + const result = await groupQuery(jid, 'get', [{ tag: 'invite', attrs: {} }]) + const inviteNode = getBinaryNodeChild(result, 'invite') + return inviteNode.attrs.code + }, + groupRevokeInvite: async(jid: string) => { + const result = await groupQuery(jid, 'set', [{ tag: 'invite', attrs: {} }]) + const inviteNode = getBinaryNodeChild(result, 'invite') + return inviteNode.attrs.code + }, + groupAcceptInvite: async(code: string) => { + const results = await groupQuery('@g.us', 'set', [{ tag: 'invite', attrs: { code } }]) + const result = getBinaryNodeChild(results, 'group') + return result.attrs.jid + }, + groupToggleEphemeral: async(jid: string, ephemeralExpiration: number) => { + const content: BinaryNode = ephemeralExpiration ? + { tag: 'ephemeral', attrs: { ephemeral: ephemeralExpiration.toString() } } : + { tag: 'not_ephemeral', attrs: { } } + await groupQuery(jid, 'set', [content]) + }, + groupSettingUpdate: async(jid: string, setting: 'announcement' | 'not_announcement' | 'locked' | 'unlocked') => { + await groupQuery(jid, 'set', [ { tag: setting, attrs: { } } ]) + }, + groupFetchAllParticipating: async() => { + const result = await query({ + tag: 'iq', + attrs: { + to: '@g.us', + xmlns: 'w:g2', + type: 'get', + }, + content: [ + { + tag: 'participating', + attrs: { }, + content: [ + { tag: 'participants', attrs: { } }, + { tag: 'description', attrs: { } } + ] + } + ] + }) + const data: { [_: string]: GroupMetadata } = { } + const groupsChild = getBinaryNodeChild(result, 'groups') + if(groupsChild) { + const groups = getBinaryNodeChildren(groupsChild, 'group') + for(const groupNode of groups) { + const meta = extractGroupMetadata({ + tag: 'result', + attrs: { }, + content: [groupNode] + }) + data[meta.id] = meta + } + } + + return data + } + } +} + + +export const extractGroupMetadata = (result: BinaryNode) => { + const group = getBinaryNodeChild(result, 'group') + const descChild = getBinaryNodeChild(group, 'description') + let desc: string | undefined + let descId: string | undefined + if(descChild) { + desc = getBinaryNodeChild(descChild, 'body')?.content as string + descId = descChild.attrs.id + } + + const groupId = group.attrs.id.includes('@') ? group.attrs.id : jidEncode(group.attrs.id, 'g.us') + const eph = getBinaryNodeChild(group, 'ephemeral')?.attrs.expiration + const metadata: GroupMetadata = { + id: groupId, + subject: group.attrs.subject, + creation: +group.attrs.creation, + owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined, + desc, + descId, + restrict: !!getBinaryNodeChild(group, 'locked'), + announce: !!getBinaryNodeChild(group, 'announcement'), + participants: getBinaryNodeChildren(group, 'participant').map( + ({ attrs }) => { + return { + id: attrs.jid, + admin: attrs.type || null as any, + } + } + ), + ephemeralDuration: eph ? +eph : undefined + } + return metadata +} diff --git a/src/Socket/index.ts b/src/Socket/index.ts new file mode 100644 index 0000000..b61c19e --- /dev/null +++ b/src/Socket/index.ts @@ -0,0 +1,13 @@ +import { DEFAULT_CONNECTION_CONFIG } from '../Defaults' +import { SocketConfig } from '../Types' +import { makeMessagesRecvSocket as _makeSocket } from './messages-recv' + +// export the last socket layer +const makeWASocket = (config: Partial) => ( + _makeSocket({ + ...DEFAULT_CONNECTION_CONFIG, + ...config + }) +) + +export default makeWASocket \ No newline at end of file diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts new file mode 100644 index 0000000..8dd4e65 --- /dev/null +++ b/src/Socket/messages-recv.ts @@ -0,0 +1,545 @@ + +import { proto } from '../../WAProto' +import { KEY_BUNDLE_TYPE } from '../Defaults' +import { Chat, GroupMetadata, MessageUserReceipt, ParticipantAction, SocketConfig, WAMessageStubType } from '../Types' +import { decodeMessageStanza, downloadAndProcessHistorySyncNotification, encodeBigEndian, generateSignalPubKey, toNumber, xmppPreKey, xmppSignedPreKey } from '../Utils' +import { areJidsSameUser, BinaryNode, BinaryNodeAttributes, getAllBinaryNodeChildren, getBinaryNodeChildren, isJidGroup, jidDecode, jidEncode, jidNormalizedUser } from '../WABinary' +import { makeChatsSocket } from './chats' +import { extractGroupMetadata } from './groups' + +const STATUS_MAP: { [_: string]: proto.WebMessageInfo.WebMessageInfoStatus } = { + 'played': proto.WebMessageInfo.WebMessageInfoStatus.PLAYED, + 'read': proto.WebMessageInfo.WebMessageInfoStatus.READ, + 'read-self': proto.WebMessageInfo.WebMessageInfoStatus.READ +} + +const getStatusFromReceiptType = (type: string | undefined) => { + const status = STATUS_MAP[type] + if(typeof type === 'undefined') { + return proto.WebMessageInfo.WebMessageInfoStatus.DELIVERY_ACK + } + + return status +} + +export const makeMessagesRecvSocket = (config: SocketConfig) => { + const { logger } = config + const sock = makeChatsSocket(config) + const { + ev, + authState, + ws, + assertSessions, + assertingPreKeys, + sendNode, + relayMessage, + sendReceipt, + resyncMainAppState, + } = sock + + const msgRetryMap = config.msgRetryCounterMap || { } + + const historyCache = new Set() + + const sendMessageAck = async({ tag, attrs }: BinaryNode, extraAttrs: BinaryNodeAttributes) => { + const stanza: BinaryNode = { + tag: 'ack', + attrs: { + id: attrs.id, + to: attrs.from, + ...extraAttrs, + } + } + if(!!attrs.participant) { + stanza.attrs.participant = attrs.participant + } + + logger.debug({ recv: attrs, sent: stanza.attrs }, `sent "${tag}" ack`) + await sendNode(stanza) + } + + const sendRetryRequest = async(node: BinaryNode) => { + const msgId = node.attrs.id + const retryCount = msgRetryMap[msgId] || 1 + if(retryCount >= 5) { + logger.debug({ retryCount, msgId }, 'reached retry limit, clearing') + delete msgRetryMap[msgId] + return + } + + msgRetryMap[msgId] = retryCount+1 + + const isGroup = !!node.attrs.participant + const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds + + const deviceIdentity = proto.ADVSignedDeviceIdentity.encode(account).finish() + await assertingPreKeys(1, async preKeys => { + const [keyId] = Object.keys(preKeys) + const key = preKeys[+keyId] + + const decFrom = node.attrs.from ? jidDecode(node.attrs.from) : undefined + const receipt: BinaryNode = { + tag: 'receipt', + attrs: { + id: msgId, + type: 'retry', + to: isGroup ? node.attrs.from : jidEncode(decFrom!.user, 's.whatsapp.net', decFrom!.device, 0) + }, + content: [ + { + tag: 'retry', + attrs: { + count: retryCount.toString(), + id: node.attrs.id, + t: node.attrs.t, + v: '1' + } + }, + { + tag: 'registration', + attrs: { }, + content: encodeBigEndian(authState.creds.registrationId) + } + ] + } + if(node.attrs.recipient) { + receipt.attrs.recipient = node.attrs.recipient + } + + if(node.attrs.participant) { + receipt.attrs.participant = node.attrs.participant + } + + if(retryCount > 1) { + const exec = generateSignalPubKey(Buffer.from(KEY_BUNDLE_TYPE)).slice(0, 1); + + (receipt.content! as BinaryNode[]).push({ + tag: 'keys', + attrs: { }, + content: [ + { tag: 'type', attrs: { }, content: exec }, + { tag: 'identity', attrs: { }, content: identityKey.public }, + xmppPreKey(key, +keyId), + xmppSignedPreKey(signedPreKey), + { tag: 'device-identity', attrs: { }, content: deviceIdentity } + ] + }) + } + + await sendNode(receipt) + + logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt') + }) + } + + const processMessage = async(message: proto.IWebMessageInfo, chatUpdate: Partial) => { + const protocolMsg = message.message?.protocolMessage + if(protocolMsg) { + switch (protocolMsg.type) { + case proto.ProtocolMessage.ProtocolMessageType.HISTORY_SYNC_NOTIFICATION: + const histNotification = protocolMsg!.historySyncNotification + + logger.info({ histNotification, id: message.key.id }, 'got history notification') + const { chats, contacts, messages, isLatest } = await downloadAndProcessHistorySyncNotification(histNotification, historyCache) + + const meJid = authState.creds.me!.id + await sendNode({ + tag: 'receipt', + attrs: { + id: message.key.id, + type: 'hist_sync', + to: jidEncode(jidDecode(meJid).user, 'c.us') + } + }) + + if(chats.length) { + ev.emit('chats.set', { chats, isLatest }) + } + + if(messages.length) { + ev.emit('messages.set', { messages, isLatest }) + } + + if(contacts.length) { + ev.emit('contacts.set', { contacts }) + } + + break + case proto.ProtocolMessage.ProtocolMessageType.APP_STATE_SYNC_KEY_SHARE: + const keys = protocolMsg.appStateSyncKeyShare!.keys + if(keys?.length) { + let newAppStateSyncKeyId = '' + for(const { keyData, keyId } of keys) { + const strKeyId = Buffer.from(keyId.keyId!).toString('base64') + + logger.info({ strKeyId }, 'injecting new app state sync key') + await authState.keys.set({ 'app-state-sync-key': { [strKeyId]: keyData } }) + + newAppStateSyncKeyId = strKeyId + } + + ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId }) + + resyncMainAppState() + } else { + [ + logger.info({ protocolMsg }, 'recv app state sync with 0 keys') + ] + } + + break + case proto.ProtocolMessage.ProtocolMessageType.REVOKE: + ev.emit('messages.update', [ + { + key: { + ...message.key, + id: protocolMsg.key!.id + }, + update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key } + } + ]) + break + case proto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING: + chatUpdate.ephemeralSettingTimestamp = toNumber(message.messageTimestamp) + chatUpdate.ephemeralExpiration = protocolMsg.ephemeralExpiration || null + break + } + } else if(message.messageStubType) { + const meJid = authState.creds.me!.id + const jid = message.key!.remoteJid! + //let actor = whatsappID (message.participant) + let participants: string[] + const emitParticipantsUpdate = (action: ParticipantAction) => ( + ev.emit('group-participants.update', { id: jid, participants, action }) + ) + const emitGroupUpdate = (update: Partial) => { + ev.emit('groups.update', [ { id: jid, ...update } ]) + } + + switch (message.messageStubType) { + case WAMessageStubType.GROUP_PARTICIPANT_LEAVE: + case WAMessageStubType.GROUP_PARTICIPANT_REMOVE: + participants = message.messageStubParameters + emitParticipantsUpdate('remove') + // mark the chat read only if you left the group + if(participants.includes(meJid)) { + chatUpdate.readOnly = true + } + + break + case WAMessageStubType.GROUP_PARTICIPANT_ADD: + case WAMessageStubType.GROUP_PARTICIPANT_INVITE: + case WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN: + participants = message.messageStubParameters + if(participants.includes(meJid)) { + chatUpdate.readOnly = false + } + + emitParticipantsUpdate('add') + break + case WAMessageStubType.GROUP_CHANGE_ANNOUNCE: + const announceValue = message.messageStubParameters[0] + emitGroupUpdate({ announce: announceValue === 'true' || announceValue === 'on' }) + break + case WAMessageStubType.GROUP_CHANGE_RESTRICT: + const restrictValue = message.messageStubParameters[0] + emitGroupUpdate({ restrict: restrictValue === 'true' || restrictValue === 'on' }) + break + case WAMessageStubType.GROUP_CHANGE_SUBJECT: + chatUpdate.name = message.messageStubParameters[0] + emitGroupUpdate({ subject: chatUpdate.name }) + break + } + } + } + + const processNotification = (node: BinaryNode): Partial => { + const result: Partial = { } + const [child] = getAllBinaryNodeChildren(node) + + if(node.attrs.type === 'w:gp2') { + switch (child?.tag) { + case 'create': + const metadata = extractGroupMetadata(child) + + result.messageStubType = WAMessageStubType.GROUP_CREATE + result.messageStubParameters = [metadata.subject] + result.key = { participant: metadata.owner } + + ev.emit('chats.upsert', [{ + id: metadata.id, + name: metadata.subject, + conversationTimestamp: metadata.creation, + }]) + ev.emit('groups.upsert', [metadata]) + break + case 'ephemeral': + case 'not_ephemeral': + result.message = { + protocolMessage: { + type: proto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING, + ephemeralExpiration: +(child.attrs.expiration || 0) + } + } + break + case 'promote': + case 'demote': + case 'remove': + case 'add': + case 'leave': + const stubType = `GROUP_PARTICIPANT_${child.tag!.toUpperCase()}` + result.messageStubType = WAMessageStubType[stubType] + + const participants = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid) + if( + participants.length === 1 && + // if recv. "remove" message and sender removed themselves + // mark as left + areJidsSameUser(participants[0], node.attrs.participant) && + child.tag === 'remove' + ) { + result.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE + } + + result.messageStubParameters = participants + break + case 'subject': + result.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT + result.messageStubParameters = [ child.attrs.subject ] + break + case 'announcement': + case 'not_announcement': + result.messageStubType = WAMessageStubType.GROUP_CHANGE_ANNOUNCE + result.messageStubParameters = [ (child.tag === 'announcement') ? 'on' : 'off' ] + break + case 'locked': + case 'unlocked': + result.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT + result.messageStubParameters = [ (child.tag === 'locked') ? 'on' : 'off' ] + break + + } + } else { + switch (child.tag) { + case 'devices': + const devices = getBinaryNodeChildren(child, 'device') + if(areJidsSameUser(child.attrs.jid, authState.creds!.me!.id)) { + const deviceJids = devices.map(d => d.attrs.jid) + logger.info({ deviceJids }, 'got my own devices') + } + + break + } + } + + if(Object.keys(result).length) { + return result + } + } + + // recv a message + ws.on('CB:message', async(stanza: BinaryNode) => { + const msg = await decodeMessageStanza(stanza, authState) + // message failed to decrypt + if(msg.messageStubType === proto.WebMessageInfo.WebMessageInfoStubType.CIPHERTEXT) { + logger.error( + { msgId: msg.key.id, params: msg.messageStubParameters }, + 'failure in decrypting message' + ) + await sendRetryRequest(stanza) + } else { + await sendMessageAck(stanza, { class: 'receipt' }) + // no type in the receipt => message delivered + await sendReceipt(msg.key.remoteJid!, msg.key.participant, [msg.key.id!], undefined) + logger.debug({ msg: msg.key }, 'sent delivery receipt') + } + + msg.key.remoteJid = jidNormalizedUser(msg.key.remoteJid!) + ev.emit('messages.upsert', { messages: [msg], type: stanza.attrs.offline ? 'append' : 'notify' }) + }) + + ws.on('CB:ack,class:message', async(node: BinaryNode) => { + await sendNode({ + tag: 'ack', + attrs: { + class: 'receipt', + id: node.attrs.id, + from: node.attrs.from + } + }) + logger.debug({ attrs: node.attrs }, 'sending receipt for ack') + }) + + ws.on('CB:call', async(node: BinaryNode) => { + logger.info({ node }, 'recv call') + + const [child] = getAllBinaryNodeChildren(node) + if(!!child?.tag) { + await sendMessageAck(node, { class: 'call', type: child.tag }) + } + }) + + const sendMessagesAgain = async(key: proto.IMessageKey, ids: string[]) => { + const msgs = await Promise.all( + ids.map(id => ( + config.getMessage({ ...key, id }) + )) + ) + + const participant = key.participant || key.remoteJid + await assertSessions([participant], true) + + if(isJidGroup(key.remoteJid)) { + await authState.keys.set({ 'sender-key-memory': { [key.remoteJid]: null } }) + } + + logger.debug({ participant }, 'forced new session for retry recp') + + for(let i = 0; i < msgs.length;i++) { + if(msgs[i]) { + await relayMessage(key.remoteJid, msgs[i], { + messageId: ids[i], + participant + }) + } else { + logger.debug({ jid: key.remoteJid, id: ids[i] }, 'recv retry request, but message not available') + } + } + } + + const handleReceipt = async(node: BinaryNode) => { + let shouldAck = true + + const { attrs, content } = node + const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, authState.creds.me?.id) + const remoteJid = !isNodeFromMe ? attrs.from : attrs.recipient + const fromMe = !attrs.recipient + + const ids = [attrs.id] + if(Array.isArray(content)) { + const items = getBinaryNodeChildren(content[0], 'item') + ids.push(...items.map(i => i.attrs.id)) + } + + const key: proto.IMessageKey = { + remoteJid, + id: '', + fromMe, + participant: attrs.participant + } + + const status = getStatusFromReceiptType(attrs.type) + if( + typeof status !== 'undefined' && + ( + // basically, we only want to know when a message from us has been delivered to/read by the other person + // or another device of ours has read some messages + status > proto.WebMessageInfo.WebMessageInfoStatus.DELIVERY_ACK || + !isNodeFromMe + ) + ) { + if(isJidGroup(remoteJid)) { + const updateKey: keyof MessageUserReceipt = status === proto.WebMessageInfo.WebMessageInfoStatus.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp' + ev.emit( + 'message-receipt.update', + ids.map(id => ({ + key: { ...key, id }, + receipt: { + userJid: jidNormalizedUser(attrs.participant), + [updateKey]: +attrs.t + } + })) + ) + } else { + ev.emit( + 'messages.update', + ids.map(id => ({ + key: { ...key, id }, + update: { status } + })) + ) + } + + } + + if(attrs.type === 'retry') { + // correctly set who is asking for the retry + key.participant = key.participant || attrs.from + if(key.fromMe) { + try { + logger.debug({ attrs }, 'recv retry request') + await sendMessagesAgain(key, ids) + } catch(error) { + logger.error({ key, ids, trace: error.stack }, 'error in sending message again') + shouldAck = false + } + } else { + logger.info({ attrs, key }, 'recv retry for not fromMe message') + } + } + + if(shouldAck) { + await sendMessageAck(node, { class: 'receipt', type: attrs.type }) + } + + } + + ws.on('CB:receipt', handleReceipt) + + ws.on('CB:notification', async(node: BinaryNode) => { + await sendMessageAck(node, { class: 'notification', type: node.attrs.type }) + + const msg = processNotification(node) + if(msg) { + const fromMe = areJidsSameUser(node.attrs.participant || node.attrs.from, authState.creds.me!.id) + msg.key = { + remoteJid: node.attrs.from, + fromMe, + participant: node.attrs.participant, + id: node.attrs.id, + ...(msg.key || {}) + } + msg.messageTimestamp = +node.attrs.t + + const fullMsg = proto.WebMessageInfo.fromObject(msg) + ev.emit('messages.upsert', { messages: [fullMsg], type: 'append' }) + } + }) + + ev.on('messages.upsert', async({ messages, type }) => { + if(type === 'notify' || type === 'append') { + const chat: Partial = { id: messages[0].key.remoteJid } + const contactNameUpdates: { [_: string]: string } = { } + for(const msg of messages) { + if(!!msg.pushName) { + const jid = msg.key.fromMe ? jidNormalizedUser(authState.creds.me!.id) : (msg.key.participant || msg.key.remoteJid) + contactNameUpdates[jid] = msg.pushName + // update our pushname too + if(msg.key.fromMe && authState.creds.me?.name !== msg.pushName) { + ev.emit('creds.update', { me: { ...authState.creds.me!, name: msg.pushName! } }) + } + } + + await processMessage(msg, chat) + if(!!msg.message && !msg.message!.protocolMessage) { + chat.conversationTimestamp = toNumber(msg.messageTimestamp) + if(!msg.key.fromMe) { + chat.unreadCount = (chat.unreadCount || 0) + 1 + } + } + } + + if(Object.keys(chat).length > 1) { + ev.emit('chats.update', [ chat ]) + } + + if(Object.keys(contactNameUpdates).length) { + ev.emit('contacts.update', Object.keys(contactNameUpdates).map( + id => ({ id, notify: contactNameUpdates[id] }) + )) + } + } + }) + + return { ...sock, processMessage, sendMessageAck, sendRetryRequest } +} diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts new file mode 100644 index 0000000..e6b0cfd --- /dev/null +++ b/src/Socket/messages-send.ts @@ -0,0 +1,493 @@ + +import NodeCache from 'node-cache' +import { proto } from '../../WAProto' +import { WA_DEFAULT_EPHEMERAL } from '../Defaults' +import { AnyMessageContent, MediaConnInfo, MessageRelayOptions, MiscMessageGenerationOptions, SocketConfig } from '../Types' +import { encodeWAMessage, encryptSenderKeyMsgSignalProto, encryptSignalProto, extractDeviceJids, generateMessageID, generateWAMessage, getWAUploadToServer, jidToSignalProtocolAddress, parseAndInjectE2ESessions } from '../Utils' +import { BinaryNode, BinaryNodeAttributes, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, jidDecode, jidEncode, jidNormalizedUser, JidWithDevice, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary' +import { makeGroupsSocket } from './groups' + +export const makeMessagesSocket = (config: SocketConfig) => { + const { logger } = config + const sock = makeGroupsSocket(config) + const { + ev, + authState, + query, + generateMessageTag, + sendNode, + groupMetadata, + groupToggleEphemeral + } = sock + + const userDevicesCache = config.userDevicesCache || new NodeCache({ + stdTTL: 300, // 5 minutes + useClones: false + }) + let privacySettings: { [_: string]: string } | undefined + + const fetchPrivacySettings = async(force: boolean = false) => { + if(!privacySettings || force) { + const { content } = await query({ + tag: 'iq', + attrs: { + xmlns: 'privacy', + to: S_WHATSAPP_NET, + type: 'get' + }, + content: [ + { tag: 'privacy', attrs: { } } + ] + }) + privacySettings = reduceBinaryNodeToDictionary(content[0] as BinaryNode, 'category') + } + + return privacySettings + } + + let mediaConn: Promise + const refreshMediaConn = async(forceGet = false) => { + const media = await mediaConn + if(!media || forceGet || (new Date().getTime()-media.fetchDate.getTime()) > media.ttl*1000) { + mediaConn = (async() => { + const result = await query({ + tag: 'iq', + attrs: { + type: 'set', + xmlns: 'w:m', + to: S_WHATSAPP_NET, + }, + content: [ { tag: 'media_conn', attrs: { } } ] + }) + const mediaConnNode = getBinaryNodeChild(result, 'media_conn') + const node: MediaConnInfo = { + hosts: getBinaryNodeChildren(mediaConnNode, 'host').map( + item => item.attrs as any + ), + auth: mediaConnNode.attrs.auth, + ttl: +mediaConnNode.attrs.ttl, + fetchDate: new Date() + } + logger.debug('fetched media conn') + return node + })() + } + + return mediaConn + } + + /** + * generic send receipt function + * used for receipts of phone call, read, delivery etc. + * */ + const sendReceipt = async(jid: string, participant: string | undefined, messageIds: string[], type: 'read' | 'read-self' | undefined) => { + const node: BinaryNode = { + tag: 'receipt', + attrs: { + id: messageIds[0], + t: Date.now().toString(), + to: jid, + }, + } + if(type) { + node.attrs.type = type + } + + if(participant) { + node.attrs.participant = participant + } + + const remainingMessageIds = messageIds.slice(1) + if(remainingMessageIds.length) { + node.content = [ + { + tag: 'list', + attrs: { }, + content: remainingMessageIds.map(id => ({ + tag: 'item', + attrs: { id } + })) + } + ] + } + + logger.debug({ jid, messageIds, type }, 'sending receipt for messages') + await sendNode(node) + } + + const sendReadReceipt = async(jid: string, participant: string | undefined, messageIds: string[]) => { + const privacySettings = await fetchPrivacySettings() + // based on privacy settings, we have to change the read type + const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self' + return sendReceipt(jid, participant, messageIds, readType) + } + + const getUSyncDevices = async(jids: string[], ignoreZeroDevices: boolean) => { + const deviceResults: JidWithDevice[] = [] + + const users: BinaryNode[] = [] + jids = Array.from(new Set(jids)) + for(let jid of jids) { + const user = jidDecode(jid).user + jid = jidNormalizedUser(jid) + if(userDevicesCache.has(user)) { + const devices: JidWithDevice[] = userDevicesCache.get(user) + deviceResults.push(...devices) + + logger.trace({ user }, 'using cache for devices') + } else { + users.push({ tag: 'user', attrs: { jid } }) + } + } + + const iq: BinaryNode = { + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'get', + xmlns: 'usync', + }, + content: [ + { + tag: 'usync', + attrs: { + sid: generateMessageTag(), + mode: 'query', + last: 'true', + index: '0', + context: 'message', + }, + content: [ + { + tag: 'query', + attrs: { }, + content: [ + { + tag: 'devices', + attrs: { version: '2' } + } + ] + }, + { tag: 'list', attrs: { }, content: users } + ] + }, + ], + } + const result = await query(iq) + const extracted = extractDeviceJids(result, authState.creds.me!.id, ignoreZeroDevices) + const deviceMap: { [_: string]: JidWithDevice[] } = {} + + for(const item of extracted) { + deviceMap[item.user] = deviceMap[item.user] || [] + deviceMap[item.user].push(item) + + deviceResults.push(item) + } + + for(const key in deviceMap) { + userDevicesCache.set(key, deviceMap[key]) + } + + return deviceResults + } + + const assertSessions = async(jids: string[], force: boolean) => { + let jidsRequiringFetch: string[] = [] + if(force) { + jidsRequiringFetch = jids + } else { + const addrs = jids.map(jid => jidToSignalProtocolAddress(jid).toString()) + const sessions = await authState.keys.get('session', addrs) + for(const jid of jids) { + const signalId = jidToSignalProtocolAddress(jid).toString() + if(!sessions[signalId]) { + jidsRequiringFetch.push(jid) + } + } + } + + if(jidsRequiringFetch.length) { + logger.debug({ jidsRequiringFetch }, 'fetching sessions') + const result = await query({ + tag: 'iq', + attrs: { + xmlns: 'encrypt', + type: 'get', + to: S_WHATSAPP_NET, + }, + content: [ + { + tag: 'key', + attrs: { }, + content: jidsRequiringFetch.map( + jid => ({ + tag: 'user', + attrs: { jid, reason: 'identity' }, + }) + ) + } + ] + }) + await parseAndInjectE2ESessions(result, authState) + return true + } + + return false + } + + const createParticipantNodes = async(jids: string[], bytes: Buffer) => { + await assertSessions(jids, false) + + if(authState.keys.isInTransaction()) { + await authState.keys.prefetch( + 'session', + jids.map(jid => jidToSignalProtocolAddress(jid).toString()) + ) + } + + const nodes = await Promise.all( + jids.map( + async jid => { + const { type, ciphertext } = await encryptSignalProto(jid, bytes, authState) + const node: BinaryNode = { + tag: 'to', + attrs: { jid }, + content: [{ + tag: 'enc', + attrs: { v: '2', type }, + content: ciphertext + }] + } + return node + } + ) + ) + return nodes + } + + const relayMessage = async( + jid: string, + message: proto.IMessage, + { messageId: msgId, participant, additionalAttributes, cachedGroupMetadata }: MessageRelayOptions + ) => { + const meId = authState.creds.me!.id + + const { user, server } = jidDecode(jid) + const isGroup = server === 'g.us' + msgId = msgId || generateMessageID() + + const encodedMsg = encodeWAMessage(message) + const participants: BinaryNode[] = [] + + const destinationJid = jidEncode(user, isGroup ? 'g.us' : 's.whatsapp.net') + + const binaryNodeContent: BinaryNode[] = [] + + const devices: JidWithDevice[] = [] + if(participant) { + const { user, device } = jidDecode(participant) + devices.push({ user, device }) + } + + await authState.keys.transaction( + async() => { + if(isGroup) { + const { ciphertext, senderKeyDistributionMessageKey } = await encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, meId, authState) + + const [groupData, senderKeyMap] = await Promise.all([ + (async() => { + let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined + if(!groupData) { + groupData = await groupMetadata(jid) + } + + return groupData + })(), + (async() => { + const result = await authState.keys.get('sender-key-memory', [jid]) + return result[jid] || { } + })() + ]) + + if(!participant) { + const participantsList = groupData.participants.map(p => p.id) + const additionalDevices = await getUSyncDevices(participantsList, false) + devices.push(...additionalDevices) + } + + const senderKeyJids: string[] = [] + // ensure a connection is established with every device + for(const { user, device } of devices) { + const jid = jidEncode(user, 's.whatsapp.net', device) + if(!senderKeyMap[jid]) { + senderKeyJids.push(jid) + // store that this person has had the sender keys sent to them + senderKeyMap[jid] = true + } + } + + // if there are some participants with whom the session has not been established + // if there are, we re-send the senderkey + if(senderKeyJids.length) { + logger.debug({ senderKeyJids }, 'sending new sender key') + + const encSenderKeyMsg = encodeWAMessage({ + senderKeyDistributionMessage: { + axolotlSenderKeyDistributionMessage: senderKeyDistributionMessageKey, + groupId: destinationJid + } + }) + + participants.push( + ...(await createParticipantNodes(senderKeyJids, encSenderKeyMsg)) + ) + } + + binaryNodeContent.push({ + tag: 'enc', + attrs: { v: '2', type: 'skmsg' }, + content: ciphertext + }) + + await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } }) + } else { + const { user: meUser } = jidDecode(meId) + + const encodedMeMsg = encodeWAMessage({ + deviceSentMessage: { + destinationJid, + message + } + }) + + if(!participant) { + devices.push({ user }) + devices.push({ user: meUser }) + + const additionalDevices = await getUSyncDevices([ meId, jid ], true) + devices.push(...additionalDevices) + } + + const meJids: string[] = [] + const otherJids: string[] = [] + for(const { user, device } of devices) { + const jid = jidEncode(user, 's.whatsapp.net', device) + const isMe = user === meUser + if(isMe) { + meJids.push(jid) + } else { + otherJids.push(jid) + } + } + + const [meNodes, otherNodes] = await Promise.all([ + createParticipantNodes(meJids, encodedMeMsg), + createParticipantNodes(otherJids, encodedMsg) + ]) + participants.push(...meNodes) + participants.push(...otherNodes) + } + + if(participants.length) { + binaryNodeContent.push({ + tag: 'participants', + attrs: { }, + content: participants + }) + } + + const stanza: BinaryNode = { + tag: 'message', + attrs: { + id: msgId, + type: 'text', + to: destinationJid, + ...(additionalAttributes || {}) + }, + content: binaryNodeContent + } + + const shouldHaveIdentity = !!participants.find( + participant => (participant.content! as BinaryNode[]).find(n => n.attrs.type === 'pkmsg') + ) + + if(shouldHaveIdentity) { + (stanza.content as BinaryNode[]).push({ + tag: 'device-identity', + attrs: { }, + content: proto.ADVSignedDeviceIdentity.encode(authState.creds.account).finish() + }) + + logger.debug({ jid }, 'adding device identity') + } + + logger.debug({ msgId }, `sending message to ${participants.length} devices`) + + await sendNode(stanza) + } + ) + + return msgId + } + + const waUploadToServer = getWAUploadToServer(config, refreshMediaConn) + + return { + ...sock, + assertSessions, + relayMessage, + sendReceipt, + sendReadReceipt, + refreshMediaConn, + waUploadToServer, + fetchPrivacySettings, + sendMessage: async( + jid: string, + content: AnyMessageContent, + options: MiscMessageGenerationOptions = { } + ) => { + const userJid = authState.creds.me!.id + if( + typeof content === 'object' && + 'disappearingMessagesInChat' in content && + typeof content['disappearingMessagesInChat'] !== 'undefined' && + isJidGroup(jid) + ) { + const { disappearingMessagesInChat } = content + const value = typeof disappearingMessagesInChat === 'boolean' ? + (disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : + disappearingMessagesInChat + await groupToggleEphemeral(jid, value) + } else { + const fullMsg = await generateWAMessage( + jid, + content, + { + logger, + userJid, + // multi-device does not have this yet + //getUrlInfo: generateUrlInfo, + upload: waUploadToServer, + mediaCache: config.mediaCache, + ...options, + } + ) + const isDeleteMsg = 'delete' in content && !!content.delete + const additionalAttributes: BinaryNodeAttributes = { } + // required for delete + if(isDeleteMsg) { + additionalAttributes.edit = '7' + } + + await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id!, additionalAttributes }) + if(config.emitOwnEvents) { + process.nextTick(() => { + ev.emit('messages.upsert', { messages: [fullMsg], type: 'append' }) + }) + } + + return fullMsg + } + } + } +} diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts new file mode 100644 index 0000000..d31df02 --- /dev/null +++ b/src/Socket/socket.ts @@ -0,0 +1,563 @@ +import { Boom } from '@hapi/boom' +import { randomBytes } from 'crypto' +import EventEmitter from 'events' +import { promisify } from 'util' +import WebSocket from 'ws' +import { proto } from '../../WAProto' +import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, DEFAULT_ORIGIN, KEY_BUNDLE_TYPE } from '../Defaults' +import { AuthenticationCreds, BaileysEventEmitter, DisconnectReason, SocketConfig } from '../Types' +import { addTransactionCapability, bindWaitForConnectionUpdate, configureSuccessfulPairing, Curve, encodeBigEndian, generateLoginNode, generateOrGetPreKeys, generateRegistrationNode, getPreKeys, makeNoiseHandler, printQRIfNecessaryListener, promiseTimeout, useSingleFileAuthState, xmppPreKey, xmppSignedPreKey } from '../Utils' +import { assertNodeErrorFree, BinaryNode, encodeBinaryNode, getBinaryNodeChild, S_WHATSAPP_NET } from '../WABinary' + +/** + * Connects to WA servers and performs: + * - simple queries (no retry mechanism, wait for connection establishment) + * - listen to messages and emit events + * - query phone connection + */ +export const makeSocket = ({ + waWebSocketUrl, + connectTimeoutMs, + logger, + agent, + keepAliveIntervalMs, + version, + browser, + auth: initialAuthState, + printQRInTerminal, + defaultQueryTimeoutMs +}: SocketConfig) => { + const ws = new WebSocket(waWebSocketUrl, undefined, { + origin: DEFAULT_ORIGIN, + timeout: connectTimeoutMs, + agent, + headers: { + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'en-US,en;q=0.9', + 'Cache-Control': 'no-cache', + 'Host': 'web.whatsapp.com', + 'Pragma': 'no-cache', + 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits' + } + }) + ws.setMaxListeners(0) + const ev = new EventEmitter() as BaileysEventEmitter + /** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */ + const ephemeralKeyPair = Curve.generateKeyPair() + /** WA noise protocol wrapper */ + const noise = makeNoiseHandler(ephemeralKeyPair) + let authState = initialAuthState + if(!authState) { + authState = useSingleFileAuthState('./auth-info-multi.json').state + + logger.warn(` + Baileys just created a single file state for your credentials. + This will not be supported soon. + Please pass the credentials in the config itself + `) + } + + const { creds } = authState + + let lastDateRecv: Date + let epoch = 0 + let keepAliveReq: NodeJS.Timeout + let qrTimer: NodeJS.Timeout + + const uqTagId = `${randomBytes(1).toString('hex')[0]}.${randomBytes(1).toString('hex')[0]}-` + const generateMessageTag = () => `${uqTagId}${epoch++}` + + const sendPromise = promisify(ws.send) + /** send a raw buffer */ + const sendRawMessage = async(data: Buffer | Uint8Array) => { + if(ws.readyState !== ws.OPEN) { + throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }) + } + + const bytes = noise.encodeFrame(data) + await sendPromise.call(ws, bytes) as Promise + } + + /** send a binary node */ + const sendNode = (node: BinaryNode) => { + const buff = encodeBinaryNode(node) + return sendRawMessage(buff) + } + + /** await the next incoming message */ + const awaitNextMessage = async(sendMsg?: Uint8Array) => { + if(ws.readyState !== ws.OPEN) { + throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }) + } + + let onOpen: (data: any) => void + let onClose: (err: Error) => void + + const result = new Promise((resolve, reject) => { + onOpen = (data: any) => resolve(data) + onClose = reject + ws.on('frame', onOpen) + ws.on('close', onClose) + ws.on('error', onClose) + }) + .finally(() => { + ws.off('frame', onOpen) + ws.off('close', onClose) + ws.off('error', onClose) + }) + + if(sendMsg) { + sendRawMessage(sendMsg).catch(onClose) + } + + return result + } + + /** + * Wait for a message with a certain tag to be received + * @param tag the message tag to await + * @param json query that was sent + * @param timeoutMs timeout after which the promise will reject + */ + const waitForMessage = async(msgId: string, timeoutMs = defaultQueryTimeoutMs) => { + let onRecv: (json) => void + let onErr: (err) => void + try { + const result = await promiseTimeout(timeoutMs, + (resolve, reject) => { + onRecv = resolve + onErr = err => { + reject(err || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })) + } + + ws.on(`TAG:${msgId}`, onRecv) + ws.on('close', onErr) // if the socket closes, you'll never receive the message + ws.off('error', onErr) + }, + ) + return result as any + } finally { + ws.off(`TAG:${msgId}`, onRecv) + ws.off('close', onErr) // if the socket closes, you'll never receive the message + ws.off('error', onErr) + } + } + + /** send a query, and wait for its response. auto-generates message ID if not provided */ + const query = async(node: BinaryNode, timeoutMs?: number) => { + if(!node.attrs.id) { + node.attrs.id = generateMessageTag() + } + + const msgId = node.attrs.id + const wait = waitForMessage(msgId, timeoutMs) + + await sendNode(node) + + const result = await (wait as Promise) + if('tag' in result) { + assertNodeErrorFree(result) + } + + return result + } + + /** connection handshake */ + const validateConnection = async() => { + logger.info('connected to WA Web') + + const init = proto.HandshakeMessage.encode({ + clientHello: { ephemeral: ephemeralKeyPair.public } + }).finish() + + const result = await awaitNextMessage(init) + const handshake = proto.HandshakeMessage.decode(result) + + logger.debug('handshake recv from WA Web') + + const keyEnc = noise.processHandshake(handshake, creds.noiseKey) + logger.info('handshake complete') + + let node: Uint8Array + if(!creds.me) { + logger.info('not logged in, attempting registration...') + node = generateRegistrationNode(creds, { version, browser }) + } else { + logger.info('logging in...') + node = generateLoginNode(creds.me!.id, { version, browser }) + } + + const payloadEnc = noise.encrypt(node) + await sendRawMessage( + proto.HandshakeMessage.encode({ + clientFinish: { + static: new Uint8Array(keyEnc), + payload: new Uint8Array(payloadEnc), + }, + }).finish() + ) + noise.finishInit() + startKeepAliveRequest() + } + + /** get some pre-keys and do something with them */ + const assertingPreKeys = async(range: number, execute: (keys: { [_: number]: any }) => Promise) => { + const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(authState.creds, range) + + const update: Partial = { + nextPreKeyId: Math.max(lastPreKeyId+1, creds.nextPreKeyId), + firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId+1) + } + if(!creds.serverHasPreKeys) { + update.serverHasPreKeys = true + } + + await authState.keys.set({ 'pre-key': newPreKeys }) + + const preKeys = await getPreKeys(authState.keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1]) + await execute(preKeys) + + ev.emit('creds.update', update) + } + + /** generates and uploads a set of pre-keys */ + const uploadPreKeys = async() => { + await assertingPreKeys(30, async preKeys => { + const node: BinaryNode = { + tag: 'iq', + attrs: { + id: generateMessageTag(), + xmlns: 'encrypt', + type: 'set', + to: S_WHATSAPP_NET, + }, + content: [ + { tag: 'registration', attrs: { }, content: encodeBigEndian(creds.registrationId) }, + { tag: 'type', attrs: { }, content: KEY_BUNDLE_TYPE }, + { tag: 'identity', attrs: { }, content: creds.signedIdentityKey.public }, + { tag: 'list', attrs: { }, content: Object.keys(preKeys).map(k => xmppPreKey(preKeys[+k], +k)) }, + xmppSignedPreKey(creds.signedPreKey) + ] + } + await sendNode(node) + + logger.info('uploaded pre-keys') + }) + } + + const onMessageRecieved = (data: Buffer) => { + noise.decodeFrame(data, frame => { + ws.emit('frame', frame) + // if it's a binary node + if(!(frame instanceof Uint8Array)) { + const msgId = frame.attrs.id + + if(logger.level === 'trace') { + logger.trace({ msgId, fromMe: false, frame }, 'communication') + } + + let anyTriggered = false + /* Check if this is a response to a message we sent */ + anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) + /* Check if this is a response to a message we are expecting */ + const l0 = frame.tag + const l1 = frame.attrs || { } + const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : '' + + Object.keys(l1).forEach(key => { + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered + }) + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered + anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered + anyTriggered = ws.emit('frame', frame) || anyTriggered + + if(!anyTriggered && logger.level === 'debug') { + logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv') + } + } + }) + } + + const end = (error: Error | undefined) => { + logger.info({ error }, 'connection closed') + + clearInterval(keepAliveReq) + clearInterval(qrTimer) + + ws.removeAllListeners('close') + ws.removeAllListeners('error') + ws.removeAllListeners('open') + ws.removeAllListeners('message') + + if(ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) { + try { + ws.close() + } catch{ } + } + + ev.emit('connection.update', { + connection: 'close', + lastDisconnect: { + error, + date: new Date() + } + }) + ev.removeAllListeners('connection.update') + } + + const waitForSocketOpen = async() => { + if(ws.readyState === ws.OPEN) { + return + } + + if(ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) { + throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }) + } + + let onOpen: () => void + let onClose: (err: Error) => void + await new Promise((resolve, reject) => { + onOpen = () => resolve(undefined) + onClose = reject + ws.on('open', onOpen) + ws.on('close', onClose) + ws.on('error', onClose) + }) + .finally(() => { + ws.off('open', onOpen) + ws.off('close', onClose) + ws.off('error', onClose) + }) + } + + const startKeepAliveRequest = () => ( + keepAliveReq = setInterval(() => { + if(!lastDateRecv) { + lastDateRecv = new Date() + } + + const diff = Date.now() - lastDateRecv.getTime() + /* + check if it's been a suspicious amount of time since the server responded with our last seen + it could be that the network is down + */ + if(diff > keepAliveIntervalMs+5000) { + end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost })) + } else if(ws.readyState === ws.OPEN) { + // if its all good, send a keep alive request + query( + { + tag: 'iq', + attrs: { + id: generateMessageTag(), + to: S_WHATSAPP_NET, + type: 'get', + xmlns: 'w:p', + }, + content: [{ tag: 'ping', attrs: { } }] + }, + keepAliveIntervalMs + ) + .then(() => { + lastDateRecv = new Date() + logger.trace('recv keep alive') + }) + .catch(err => end(err)) + } else { + logger.warn('keep alive called when WS not open') + } + }, keepAliveIntervalMs) + ) + /** i have no idea why this exists. pls enlighten me */ + const sendPassiveIq = (tag: 'passive' | 'active') => ( + sendNode({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + xmlns: 'passive', + type: 'set', + id: generateMessageTag(), + }, + content: [ + { tag, attrs: { } } + ] + }) + ) + /** logout & invalidate connection */ + const logout = async() => { + const jid = authState.creds.me?.id + if(jid) { + await sendNode({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'set', + id: generateMessageTag(), + xmlns: 'md' + }, + content: [ + { + tag: 'remove-companion-device', + attrs: { + jid: jid, + reason: 'user_initiated' + } + } + ] + }) + } + + end(new Boom('Intentional Logout', { statusCode: DisconnectReason.loggedOut })) + } + + ws.on('message', onMessageRecieved) + ws.on('open', validateConnection) + ws.on('error', end) + ws.on('close', () => end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed }))) + // the server terminated the connection + ws.on('CB:xmlstreamend', () => { + end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })) + }) + // QR gen + ws.on('CB:iq,type:set,pair-device', async(stanza: BinaryNode) => { + const iq: BinaryNode = { + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'result', + id: stanza.attrs.id, + } + } + await sendNode(iq) + + const refs = ((stanza.content[0] as BinaryNode).content as BinaryNode[]).map(n => n.content as string) + const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64') + const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64') + const advB64 = creds.advSecretKey + + let qrMs = 60_000 // time to let a QR live + const genPairQR = () => { + const ref = refs.shift() + if(!ref) { + end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut })) + return + } + + const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',') + + ev.emit('connection.update', { qr }) + + qrTimer = setTimeout(genPairQR, qrMs) + qrMs = 20_000 // shorter subsequent qrs + } + + genPairQR() + }) + // device paired for the first time + // if device pairs successfully, the server asks to restart the connection + ws.on('CB:iq,,pair-success', async(stanza: BinaryNode) => { + logger.debug('pair success recv') + try { + const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds) + + logger.debug('pairing configured successfully') + + const waiting = awaitNextMessage() + await sendNode(reply) + + const value = (await waiting) as BinaryNode + if(value.tag === 'stream:error') { + if(value.attrs?.code !== '515') { + throw new Boom('Authentication failed', { statusCode: +(value.attrs.code || 500) }) + } + } + + logger.info({ jid: updatedCreds.me!.id }, 'registered connection, restart server') + + ev.emit('creds.update', updatedCreds) + ev.emit('connection.update', { isNewLogin: true, qr: undefined }) + + end(new Boom('Restart Required', { statusCode: DisconnectReason.restartRequired })) + } catch(error) { + logger.info({ trace: error.stack }, 'error in pairing') + end(error) + } + }) + // login complete + ws.on('CB:success', async() => { + if(!creds.serverHasPreKeys) { + await uploadPreKeys() + } + + await sendPassiveIq('active') + + logger.info('opened connection to WA') + clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try + + ev.emit('connection.update', { connection: 'open' }) + }) + + ws.on('CB:ib,,offline', (node: BinaryNode) => { + const child = getBinaryNodeChild(node, 'offline') + const offlineCount = +child.attrs.count + + logger.info(`got ${offlineCount} offline messages/notifications`) + + ev.emit('connection.update', { receivedPendingNotifications: true }) + }) + + ws.on('CB:stream:error', (node: BinaryNode) => { + logger.error({ error: node }, 'stream errored out') + + const statusCode = +(node.attrs.code || DisconnectReason.restartRequired) + end(new Boom('Stream Errored', { statusCode, data: node })) + }) + // stream fail, possible logout + ws.on('CB:failure', (node: BinaryNode) => { + const reason = +(node.attrs.reason || 500) + end(new Boom('Connection Failure', { statusCode: reason, data: node.attrs })) + }) + + ws.on('CB:ib,,downgrade_webclient', () => { + end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch })) + }) + + process.nextTick(() => { + ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined }) + }) + // update credentials when required + ev.on('creds.update', update => Object.assign(creds, update)) + + if(printQRInTerminal) { + printQRIfNecessaryListener(ev, logger) + } + + return { + type: 'md' as 'md', + ws, + ev, + authState: { + creds, + // add capability + keys: addTransactionCapability(authState.keys, logger) + }, + get user() { + return authState.creds.me + }, + assertingPreKeys, + generateMessageTag, + query, + waitForMessage, + waitForSocketOpen, + sendRawMessage, + sendNode, + logout, + end, + /** Waits for the connection to WA to reach a state */ + waitForConnectionUpdate: bindWaitForConnectionUpdate(ev) + } +} + +export type Socket = ReturnType \ No newline at end of file diff --git a/src/Store/index.ts b/src/Store/index.ts new file mode 100644 index 0000000..f744671 --- /dev/null +++ b/src/Store/index.ts @@ -0,0 +1,2 @@ +import makeInMemoryStore from './make-in-memory-store' +export { makeInMemoryStore } \ No newline at end of file diff --git a/src/Store/make-in-memory-store.ts b/src/Store/make-in-memory-store.ts new file mode 100644 index 0000000..90a766d --- /dev/null +++ b/src/Store/make-in-memory-store.ts @@ -0,0 +1,381 @@ +import type KeyedDB from '@adiwajshing/keyed-db' +import type { Comparable } from '@adiwajshing/keyed-db/lib/Types' +import type { Logger } from 'pino' +import { proto } from '../../WAProto' +import { DEFAULT_CONNECTION_CONFIG } from '../Defaults' +import type makeLegacySocket from '../LegacySocket' +import type makeMDSocket from '../Socket' +import type { BaileysEventEmitter, Chat, ConnectionState, Contact, GroupMetadata, PresenceData, WAMessage, WAMessageCursor, WAMessageKey } from '../Types' +import { toNumber } from '../Utils' +import { jidNormalizedUser } from '../WABinary' +import makeOrderedDictionary from './make-ordered-dictionary' + +type LegacyWASocket = ReturnType +type AnyWASocket = ReturnType + +export const waChatKey = (pin: boolean) => ({ + key: (c: Chat) => (pin ? (c.pin ? '1' : '0') : '') + (c.archive ? '0' : '1') + c.conversationTimestamp.toString(16).padStart(8, '0') + c.id, + compare: (k1: string, k2: string) => k2.localeCompare (k1) +}) + +export const waMessageID = (m: WAMessage) => m.key.id + +export type BaileysInMemoryStoreConfig = { + chatKey?: Comparable + logger?: Logger +} + +const makeMessagesDictionary = () => makeOrderedDictionary(waMessageID) + +export default ( + { logger, chatKey }: BaileysInMemoryStoreConfig +) => { + logger = logger || DEFAULT_CONNECTION_CONFIG.logger.child({ stream: 'in-mem-store' }) + chatKey = chatKey || waChatKey(true) + const KeyedDB = require('@adiwajshing/keyed-db').default as new (...args: any[]) => KeyedDB + + const chats = new KeyedDB(chatKey, c => c.id) + const messages: { [_: string]: ReturnType } = { } + const contacts: { [_: string]: Contact } = { } + const groupMetadata: { [_: string]: GroupMetadata } = { } + const presences: { [id: string]: { [participant: string]: PresenceData } } = { } + const state: ConnectionState = { connection: 'close' } + + const assertMessageList = (jid: string) => { + if(!messages[jid]) { + messages[jid] = makeMessagesDictionary() + } + + return messages[jid] + } + + const contactsUpsert = (newContacts: Contact[]) => { + const oldContacts = new Set(Object.keys(contacts)) + for(const contact of newContacts) { + oldContacts.delete(contact.id) + contacts[contact.id] = Object.assign( + contacts[contact.id] || {}, + contact + ) + } + + return oldContacts + } + + /** + * binds to a BaileysEventEmitter. + * It listens to all events and constructs a state that you can query accurate data from. + * Eg. can use the store to fetch chats, contacts, messages etc. + * @param ev typically the event emitter from the socket connection + */ + const bind = (ev: BaileysEventEmitter) => { + ev.on('connection.update', update => { + Object.assign(state, update) + }) + ev.on('chats.set', ({ chats: newChats, isLatest }) => { + if(isLatest) { + chats.clear() + } + + const chatsAdded = chats.insertIfAbsent(...newChats).length + logger.debug({ chatsAdded }, 'synced chats') + }) + ev.on('contacts.set', ({ contacts: newContacts }) => { + const oldContacts = contactsUpsert(newContacts) + for(const jid of oldContacts) { + delete contacts[jid] + } + + logger.debug({ deletedContacts: oldContacts.size, newContacts }, 'synced contacts') + }) + ev.on('messages.set', ({ messages: newMessages, isLatest }) => { + if(isLatest) { + for(const id in messages) { + delete messages[id] + } + } + + for(const msg of newMessages) { + const jid = msg.key.remoteJid! + const list = assertMessageList(jid) + list.upsert(msg, 'prepend') + } + + logger.debug({ messages: newMessages.length }, 'synced messages') + }) + ev.on('contacts.update', updates => { + for(const update of updates) { + if(contacts[update.id!]) { + Object.assign(contacts[update.id!], update) + } else { + logger.debug({ update }, 'got update for non-existant contact') + } + } + }) + ev.on('chats.upsert', newChats => { + chats.upsert(...newChats) + }) + ev.on('chats.update', updates => { + for(let update of updates) { + const result = chats.update(update.id!, chat => { + if(update.unreadCount > 0) { + update = { ...update } + update.unreadCount = chat.unreadCount + update.unreadCount + } + + Object.assign(chat, update) + }) + if(!result) { + logger.debug({ update }, 'got update for non-existant chat') + } + } + }) + ev.on('presence.update', ({ id, presences: update }) => { + presences[id] = presences[id] || {} + Object.assign(presences[id], update) + }) + ev.on('chats.delete', deletions => { + for(const item of deletions) { + chats.deleteById(item) + } + }) + ev.on('messages.upsert', ({ messages: newMessages, type }) => { + switch (type) { + case 'append': + case 'notify': + for(const msg of newMessages) { + const jid = jidNormalizedUser(msg.key.remoteJid!) + const list = assertMessageList(jid) + list.upsert(msg, 'append') + + if(type === 'notify') { + if(!chats.get(jid)) { + ev.emit('chats.upsert', [ + { + id: jid, + conversationTimestamp: toNumber(msg.messageTimestamp), + unreadCount: 1 + } + ]) + } + } + } + + break + } + }) + ev.on('messages.update', updates => { + for(const { update, key } of updates) { + const list = assertMessageList(key.remoteJid) + const result = list.updateAssign(key.id, update) + if(!result) { + logger.debug({ update }, 'got update for non-existent message') + } + } + }) + ev.on('messages.delete', item => { + if('all' in item) { + const list = messages[item.jid] + list?.clear() + } else { + const jid = item.keys[0].remoteJid + const list = messages[jid] + if(list) { + const idSet = new Set(item.keys.map(k => k.id)) + list.filter(m => !idSet.has(m.key.id)) + } + } + }) + + ev.on('groups.update', updates => { + for(const update of updates) { + if(groupMetadata[update.id]) { + Object.assign(groupMetadata[update.id!], update) + } else { + logger.debug({ update }, 'got update for non-existant group metadata') + } + } + }) + + ev.on('group-participants.update', ({ id, participants, action }) => { + const metadata = groupMetadata[id] + if(metadata) { + switch (action) { + case 'add': + metadata.participants.push(...participants.map(id => ({ id, isAdmin: false, isSuperAdmin: false }))) + break + case 'demote': + case 'promote': + for(const participant of metadata.participants) { + if(participants.includes(participant.id)) { + participant.isAdmin = action === 'promote' + } + } + + break + case 'remove': + metadata.participants = metadata.participants.filter(p => !participants.includes(p.id)) + break + } + } + }) + + ev.on('message-receipt.update', updates => { + for(const { key, receipt } of updates) { + const obj = messages[key.remoteJid!] + const msg = obj?.get(key.id) + if(msg) { + msg.userReceipt = msg.userReceipt || [] + const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid) + if(recp) { + Object.assign(recp, receipt) + } else { + msg.userReceipt.push(receipt) + } + } + } + }) + } + + const toJSON = () => ({ + chats, + contacts, + messages + }) + + const fromJSON = (json: { chats: Chat[], contacts: { [id: string]: Contact }, messages: { [id: string]: WAMessage[] } }) => { + chats.upsert(...json.chats) + contactsUpsert(Object.values(contacts)) + for(const jid in json.messages) { + const list = assertMessageList(jid) + for(const msg of json.messages[jid]) { + list.upsert(proto.WebMessageInfo.fromObject(msg), 'append') + } + } + } + + + return { + chats, + contacts, + messages, + groupMetadata, + state, + presences, + bind, + /** loads messages from the store, if not found -- uses the legacy connection */ + loadMessages: async(jid: string, count: number, cursor: WAMessageCursor, sock: LegacyWASocket | undefined) => { + const list = assertMessageList(jid) + const retrieve = async(count: number, cursor: WAMessageCursor) => { + const result = await sock?.fetchMessagesFromWA(jid, count, cursor) + return result || [] + } + + const mode = !cursor || 'before' in cursor ? 'before' : 'after' + const cursorKey = !!cursor ? ('before' in cursor ? cursor.before : cursor.after) : undefined + const cursorValue = cursorKey ? list.get(cursorKey.id) : undefined + + let messages: WAMessage[] + if(list && mode === 'before' && (!cursorKey || cursorValue)) { + if(cursorValue) { + const msgIdx = list.array.findIndex(m => m.key.id === cursorKey.id) + messages = list.array.slice(0, msgIdx) + } else { + messages = list.array + } + + const diff = count - messages.length + if(diff < 0) { + messages = messages.slice(-count) // get the last X messages + } else if(diff > 0) { + const [fMessage] = messages + const cursor = { before: fMessage?.key || cursorKey } + const extra = await retrieve (diff, cursor) + // add to DB + for(let i = extra.length-1; i >= 0;i--) { + list.upsert(extra[i], 'prepend') + } + + messages.splice(0, 0, ...extra) + } + } else { + messages = await retrieve(count, cursor) + } + + return messages + }, + loadMessage: async(jid: string, id: string, sock: LegacyWASocket | undefined) => { + let message = messages[jid]?.get(id) + if(!message) { + message = await sock?.loadMessageFromWA(jid, id) + } + + return message + }, + mostRecentMessage: async(jid: string, sock: LegacyWASocket | undefined) => { + let message = messages[jid]?.array.slice(-1)[0] + if(!message) { + const [result] = await sock?.fetchMessagesFromWA(jid, 1, undefined) + message = result + } + + return message + }, + fetchImageUrl: async(jid: string, sock: AnyWASocket | undefined) => { + const contact = contacts[jid] + if(!contact) { + return sock?.profilePictureUrl(jid) + } + + if(typeof contact.imgUrl === 'undefined') { + contact.imgUrl = await sock?.profilePictureUrl(jid) + } + + return contact.imgUrl + }, + fetchGroupMetadata: async(jid: string, sock: AnyWASocket | undefined) => { + if(!groupMetadata[jid]) { + groupMetadata[jid] = await sock?.groupMetadata(jid) + } + + return groupMetadata[jid] + }, + fetchBroadcastListInfo: async(jid: string, sock: LegacyWASocket | undefined) => { + if(!groupMetadata[jid]) { + groupMetadata[jid] = await sock?.getBroadcastListInfo(jid) + } + + return groupMetadata[jid] + }, + fetchMessageReceipts: async({ remoteJid, id }: WAMessageKey, sock: LegacyWASocket | undefined) => { + const list = messages[remoteJid] + const msg = list?.get(id) + let receipts = msg.userReceipt + if(!receipts) { + receipts = await sock?.messageInfo(remoteJid, id) + if(msg) { + msg.userReceipt = receipts + } + } + + return receipts + }, + toJSON, + fromJSON, + writeToFile: (path: string) => { + // require fs here so that in case "fs" is not available -- the app does not crash + const { writeFileSync } = require('fs') + writeFileSync(path, JSON.stringify(toJSON())) + }, + readFromFile: (path: string) => { + // require fs here so that in case "fs" is not available -- the app does not crash + const { readFileSync, existsSync } = require('fs') + if(existsSync(path)) { + logger.debug({ path }, 'reading from file') + const jsonStr = readFileSync(path, { encoding: 'utf-8' }) + const json = JSON.parse(jsonStr) + fromJSON(json) + } + } + } +} \ No newline at end of file diff --git a/src/Store/make-ordered-dictionary.ts b/src/Store/make-ordered-dictionary.ts new file mode 100644 index 0000000..b66d760 --- /dev/null +++ b/src/Store/make-ordered-dictionary.ts @@ -0,0 +1,86 @@ +function makeOrderedDictionary(idGetter: (item: T) => string) { + const array: T[] = [] + const dict: { [_: string]: T } = { } + + const get = (id: string) => dict[id] + + const update = (item: T) => { + const id = idGetter(item) + const idx = array.findIndex(i => idGetter(i) === id) + if(idx >= 0) { + array[idx] = item + dict[id] = item + } + + return false + } + + const upsert = (item: T, mode: 'append' | 'prepend') => { + const id = idGetter(item) + if(get(id)) { + update(item) + } else { + if(mode === 'append') { + array.push(item) + } else { + array.splice(0, 0, item) + } + + dict[id] = item + } + } + + const remove = (item: T) => { + const id = idGetter(item) + const idx = array.findIndex(i => idGetter(i) === id) + if(idx >= 0) { + array.splice(idx, 1) + delete dict[id] + return true + } + + return false + } + + return { + array, + get, + upsert, + update, + remove, + updateAssign: (id: string, update: Partial) => { + const item = get(id) + if(item) { + Object.assign(item, update) + delete dict[id] + dict[idGetter(item)] = item + return true + } + + return false + }, + clear: () => { + array.splice(0, array.length) + Object.keys(dict).forEach(key => { + delete dict[key] + }) + }, + filter: (contain: (item: T) => boolean) => { + let i = 0 + while(i < array.length) { + if(!contain(array[i])) { + delete dict[idGetter(array[i])] + array.splice(i, 1) + } else { + i += 1 + } + } + }, + toJSON: () => array, + fromJSON: (newItems: T[]) => { + array.splice(0, array.length, ...newItems) + } + } +} + +export default makeOrderedDictionary \ No newline at end of file diff --git a/src/Tests/Common.ts b/src/Tests/Common.ts deleted file mode 100644 index 08e46ef..0000000 --- a/src/Tests/Common.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { WAConnection, MessageOptions, MessageType, unixTimestampSeconds, toNumber, GET_MESSAGE_ID, waMessageKey } from '../WAConnection' -import * as assert from 'assert' -import {promises as fs} from 'fs' - -require ('dotenv').config () // dotenv to load test jid -export const testJid = process.env.TEST_JID || '1234@s.whatsapp.net' // set TEST_JID=xyz@s.whatsapp.net in a .env file in the root directory - -export const makeConnection = () => { - const conn = new WAConnection() - conn.connectOptions.maxIdleTimeMs = 15_000 - conn.logger.level = 'debug' - - let evCounts = {} - - conn.on ('close', ({ isReconnecting }) => { - !isReconnecting && console.log ('Events registered: ', evCounts) - }) - - const onM = conn.on - conn.on = (...args: any[]) => { - evCounts[args[0]] = (evCounts[args[0]] || 0) + 1 - return onM.apply (conn, args) - } - const offM = conn.off - conn.off = (...args: any[]) => { - evCounts[args[0]] = (evCounts[args[0]] || 0) - 1 - if (evCounts[args[0]] <= 0) delete evCounts[args[0]] - return offM.apply (conn, args) - } - return conn -} - -export async function sendAndRetrieveMessage(conn: WAConnection, content, type: MessageType, options: MessageOptions = {}, recipientJid = testJid) { - const response = await conn.sendMessage(recipientJid, content, type, options) - const {messages} = await conn.loadMessages(recipientJid, 10) - const message = messages.find (m => m.key.id === response.key.id) - assert.ok(message) - - const chat = conn.chats.get(recipientJid) - - assert.ok (chat.messages.get(GET_MESSAGE_ID(message.key))) - assert.ok (chat.t >= (unixTimestampSeconds()-5), `expected: ${chat.t} > ${(unixTimestampSeconds()-5)}`) - return message -} -export const WAConnectionTest = (name: string, func: (conn: WAConnection) => void) => ( - describe(name, () => { - const conn = new WAConnection() - conn.connectOptions.maxIdleTimeMs = 30_000 - conn.logger.level = 'debug' - - before(async () => { - const file = './auth_info.json' - await conn.loadAuthInfo(file).connect() - await fs.writeFile(file, JSON.stringify(conn.base64EncodedAuthInfo(), null, '\t')) - }) - after(() => conn.close()) - - afterEach (() => assertChatDBIntegrity (conn)) - - func(conn) - }) -) -export const assertChatDBIntegrity = (conn: WAConnection) => { - conn.chats.all ().forEach (chat => ( - assert.deepStrictEqual ( - [...chat.messages.all()].sort ((m1, m2) => waMessageKey.compare(waMessageKey.key(m1), waMessageKey.key(m2))), - chat.messages.all() - ) - )) - conn.chats.all ().forEach (chat => ( - assert.deepStrictEqual ( - chat.messages.all().filter (m => chat.messages.all().filter(m1 => m1.key.id === m.key.id).length > 1), - [] - ) - )) -} diff --git a/src/Tests/Tests.Binary.ts b/src/Tests/Tests.Binary.ts deleted file mode 100644 index f373d61..0000000 --- a/src/Tests/Tests.Binary.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { strict as assert } from 'assert' -import Encoder from '../Binary/Encoder' -import Decoder from '../Binary/Decoder' - -describe('Binary Coding Tests', () => { - const testVectors: [string, Object][] = [ - [ - 'f806092f5a0a10f804f80234fc6c0a350a1b39313735323938373131313740732e77686174736170702e6e657410011a143345423030393637354537454433374141424632122b0a292a7069616e6f20726f6f6d2074696d696e6773206172653a2a0a20363a3030414d2d31323a3030414d18b3faa7f3052003f80234fc4c0a410a1b39313735323938373131313740732e77686174736170702e6e657410001a20304643454335333330463634393239433645394132434646443242433845414418bdfaa7f305c00101f80234fc930a350a1b39313735323938373131313740732e77686174736170702e6e657410011a14334542303033433742353339414644303937353312520a50536f727279206672656e2c204920636f756c646e277420756e6465727374616e6420274c69627261272e2054797065202768656c702720746f206b6e6f77207768617420616c6c20492063616e20646f18c1faa7f3052003f80234fc540a410a1b39313735323938373131313740732e77686174736170702e6e657410001a20413132333042384436423041314437393345433241453245413043313638443812090a076c69627261727918c2faa7f305', - [ - 'action', - { last: 'true', add: 'before' }, - [ - [ - 'message', - null, - { - key: { remoteJid: '917529871117@s.whatsapp.net', fromMe: true, id: '3EB009675E7ED37AABF2' }, - message: { conversation: '*piano room timings are:*\n 6:00AM-12:00AM' }, - messageTimestamp: '1584004403', - status: 'DELIVERY_ACK', - }, - ], - [ - 'message', - null, - { - key: { - remoteJid: '917529871117@s.whatsapp.net', - fromMe: false, - id: '0FCEC5330F64929C6E9A2CFFD2BC8EAD', - }, - messageTimestamp: '1584004413', - messageStubType: 'REVOKE', - }, - ], - [ - 'message', - null, - { - key: { remoteJid: '917529871117@s.whatsapp.net', fromMe: true, id: '3EB003C7B539AFD09753' }, - message: { - conversation: - "Sorry fren, I couldn't understand 'Libra'. Type 'help' to know what all I can do", - }, - messageTimestamp: '1584004417', - status: 'DELIVERY_ACK', - }, - ], - [ - 'message', - null, - { - key: { - remoteJid: '917529871117@s.whatsapp.net', - fromMe: false, - id: 'A1230B8D6B0A1D793EC2AE2EA0C168D8', - }, - message: { conversation: 'library' }, - messageTimestamp: '1584004418', - }, - ], - ], - ], - ], - [ - 'f8063f2dfafc0831323334353637385027fc0431323334f801f80228fc0701020304050607', - [ - 'picture', - {jid: '12345678@c.us', id: '1234'}, - [['image', null, Buffer.from([1,2,3,4,5,6,7])]] - ] - ] - ] - const encoder = new Encoder() - const decoder = new Decoder() - - it('should decode strings', () => { - testVectors.forEach(pair => { - const buff = Buffer.from(pair[0], 'hex') - const decoded = decoder.read(buff) - //console.log((decoded[2][0][2])) - assert.deepStrictEqual(decoded, pair[1]) - - const encoded = encoder.write(decoded) - assert.deepStrictEqual(encoded, buff) - }) - console.log('all coding tests passed') - }) -}) diff --git a/src/Tests/Tests.Connect.ts b/src/Tests/Tests.Connect.ts deleted file mode 100644 index 2608ffb..0000000 --- a/src/Tests/Tests.Connect.ts +++ /dev/null @@ -1,407 +0,0 @@ -import * as assert from 'assert' -import {WAConnection} from '../WAConnection' -import { AuthenticationCredentialsBase64, BaileysError, ReconnectMode, DisconnectReason, WAChat, WAContact } from '../WAConnection/Constants' -import { delay } from '../WAConnection/Utils' -import { assertChatDBIntegrity, makeConnection, testJid } from './Common' - -describe('QR Generation', () => { - it('should generate QR', async () => { - const conn = makeConnection () - conn.connectOptions.maxRetries = 0 - - let calledQR = 0 - conn.removeAllListeners ('qr') - conn.on ('qr', () => calledQR += 1) - - await conn.connect() - .then (() => assert.fail('should not have succeeded')) - .catch (error => {}) - assert.deepStrictEqual ( - Object.keys(conn.eventNames()).filter(key => key.startsWith('TAG:')), - [] - ) - assert.ok(calledQR >= 2, 'QR not called') - }) -}) - -describe('Test Connect', () => { - let auth: AuthenticationCredentialsBase64 - it('should connect', async () => { - console.log('please be ready to scan with your phone') - - const conn = makeConnection () - - let credentialsUpdateCalled = false - conn.on ('credentials-updated', () => credentialsUpdateCalled = true) - - await conn.connect () - assert.ok(conn.user?.jid) - assert.ok(conn.user?.phone) - assert.ok (conn.user?.imgUrl || conn.user.imgUrl === '') - assert.ok (credentialsUpdateCalled) - - assertChatDBIntegrity (conn) - - conn.close() - auth = conn.base64EncodedAuthInfo() - }) - it('should restore session', async () => { - const conn = makeConnection () - - let credentialsUpdateCalled = false - conn.on ('credentials-updated', () => credentialsUpdateCalled = true) - - await conn.loadAuthInfo (auth).connect () - assert.ok(conn.user) - assert.ok(conn.user.jid) - assert.ok (credentialsUpdateCalled) - - assertChatDBIntegrity (conn) - await conn.logout() - conn.loadAuthInfo(auth) - - await conn.connect() - .then (() => assert.fail('should not have reconnected')) - .catch (err => { - assert.ok (err instanceof BaileysError) - assert.ok ((err as BaileysError).status >= 400) - }) - conn.close() - }) - it ('should disconnect & reconnect phone', async () => { - const conn = makeConnection () - conn.logger.level = 'debug' - await conn.loadAuthInfo('./auth_info.json').connect () - assert.strictEqual (conn.phoneConnected, true) - - try { - const waitForEvent = expect => new Promise (resolve => { - conn.on ('connection-phone-change', ({connected}) => { - if (connected === expect) { - conn.removeAllListeners ('connection-phone-change') - resolve(undefined) - } - }) - }) - - console.log ('disconnect your phone from the internet') - await delay (10_000) - console.log ('phone should be disconnected now, testing...') - - const messagesPromise = Promise.all ( - [ - conn.loadMessages (testJid, 50), - conn.getStatus (testJid), - conn.getProfilePicture (testJid).catch (() => '') - ] - ) - - await waitForEvent (false) - - console.log ('reconnect your phone to the internet') - await waitForEvent (true) - - console.log ('reconnected successfully') - - const final = await messagesPromise - assert.ok (final) - } finally { - conn.close () - } - }) -}) -describe ('Reconnects', () => { - const verifyConnectionOpen = async (conn: WAConnection) => { - assert.ok (conn.user.jid) - let failed = false - // check that the connection stays open - conn.on ('close', ({reason}) => { - if(reason !== DisconnectReason.intentional) failed = true - }) - await delay (60*1000) - - const status = await conn.getStatus () - assert.ok (status) - assert.ok (!conn['debounceTimeout']) // this should be null - - conn.close () - - if (failed) assert.fail ('should not have closed again') - } - it('should dispose correctly on bad_session', async () => { - const conn = makeConnection () - conn.autoReconnect = ReconnectMode.onAllErrors - conn.loadAuthInfo ('./auth_info.json') - - let gotClose0 = false - let gotClose1 = false - - conn.on ('ws-close', ({ reason }) => { - gotClose0 = true - }) - conn.on ('close', ({ reason }) => { - if (reason === DisconnectReason.badSession) gotClose1 = true - }) - setTimeout (() => conn['conn'].emit ('message', Buffer.from('some-tag,sdjjij1jo2ejo1je')), 1500) - await conn.connect () - - setTimeout (() => conn['conn'].emit ('message', Buffer.from('some-tag,sdjjij1jo2ejo1je')), 1500) - - await new Promise (resolve => { - conn.on ('open', resolve) - }) - - assert.ok (gotClose0, 'did not receive bad_session close initially') - assert.ok (gotClose1, 'did not receive bad_session close') - - conn.close () - }) - /** - * the idea is to test closing the connection at multiple points in the connection - * and see if the library cleans up resources correctly - */ - it('should cleanup correctly', async () => { - const conn = makeConnection () - conn.autoReconnect = ReconnectMode.onAllErrors - conn.loadAuthInfo ('./auth_info.json') - - let timeout = 0.1 - while (true) { - let tmout = setTimeout (() => conn.close(), timeout*1000) - try { - await conn.connect () - clearTimeout (tmout) - break - } catch (error) { - - } - // exponentially increase the timeout disconnect - timeout *= 2 - } - await verifyConnectionOpen (conn) - }) - /** - * the idea is to test closing the connection at multiple points in the connection - * and see if the library cleans up resources correctly - */ - it('should disrupt connect loop', async () => { - const conn = makeConnection () - - conn.autoReconnect = ReconnectMode.onAllErrors - conn.loadAuthInfo ('./auth_info.json') - - let timeout = 1000 - let tmout - const endConnection = async () => { - while (!conn['conn']) { - await delay(100) - } - conn['conn'].close () - - while (conn['conn']) { - await delay(100) - } - - timeout *= 2 - tmout = setTimeout (endConnection, timeout) - } - tmout = setTimeout (endConnection, timeout) - - await conn.connect () - clearTimeout (tmout) - - await verifyConnectionOpen (conn) - }) - it ('should reconnect on broken connection', async () => { - const conn = makeConnection () - conn.autoReconnect = ReconnectMode.onConnectionLost - - await conn.loadAuthInfo('./auth_info.json').connect () - assert.strictEqual (conn.phoneConnected, true) - - try { - const closeConn = () => conn['conn']?.terminate () - - const task = new Promise (resolve => { - let closes = 0 - conn.on ('close', ({reason, isReconnecting}) => { - console.log (`closed: ${reason}`) - assert.ok (reason) - assert.ok (isReconnecting) - closes += 1 - - // let it fail reconnect a few times - if (closes >= 1) { - conn.removeAllListeners ('close') - conn.removeAllListeners ('connecting') - resolve(undefined) - } - }) - conn.on ('connecting', () => { - // close again - delay (3500).then (closeConn) - }) - }) - - closeConn () - await task - - await new Promise (resolve => { - conn.on ('open', () => { - conn.removeAllListeners ('open') - resolve(undefined) - }) - }) - - conn.close () - - conn.on ('connecting', () => assert.fail('should not connect')) - await delay (2000) - } finally { - conn.removeAllListeners ('connecting') - conn.removeAllListeners ('close') - conn.removeAllListeners ('open') - conn.close () - } - }) - it ('should reconnect & stay connected', async () => { - const conn = makeConnection () - conn.autoReconnect = ReconnectMode.onConnectionLost - - await conn.loadAuthInfo('./auth_info.json').connect () - assert.strictEqual (conn.phoneConnected, true) - - await delay (30*1000) - - conn['conn']?.terminate () - - conn.on ('close', () => { - assert.ok (!conn['lastSeen']) - console.log ('connection closed') - }) - await new Promise (resolve => conn.on ('open', resolve)) - await verifyConnectionOpen (conn) - }) -}) - -describe ('Pending Requests', () => { - it ('should correctly send updates for chats', async () => { - const conn = makeConnection () - conn.pendingRequestTimeoutMs = null - conn.loadAuthInfo('./auth_info.json') - - const task = new Promise(resolve => conn.once('chats-received', resolve)) - await conn.connect () - await task - - conn.close () - - const oldChat = conn.chats.all()[0] - oldChat.archive = 'true' // mark the first chat as archived - oldChat.modify_tag = '1234' // change modify tag to detect change - - const promise = new Promise(resolve => conn.once('chats-update', resolve)) - - const result = await conn.connect () - assert.ok (!result.newConnection) - - const chats = await promise as Partial[] - const chat = chats.find (c => c.jid === oldChat.jid) - assert.ok (chat) - - assert.ok ('archive' in chat) - assert.strictEqual (Object.keys(chat).length, 3) - assert.strictEqual (Object.keys(chats).length, 1) - - conn.close () - }) - it ('should correctly send updates for contacts', async () => { - const conn = makeConnection () - conn.pendingRequestTimeoutMs = null - conn.loadAuthInfo('./auth_info.json') - - const task: any = new Promise(resolve => conn.once('contacts-received', resolve)) - await conn.connect () - const initialResult = await task - assert.strictEqual( - initialResult.updatedContacts.length, - Object.keys(conn.contacts).length - ) - - - conn.close () - - const [jid] = Object.keys(conn.contacts) - const oldContact = conn.contacts[jid] - oldContact.name = 'Lol' - oldContact.index = 'L' - - const promise = new Promise(resolve => conn.once('contacts-received', resolve)) - - const result = await conn.connect () - assert.ok (!result.newConnection) - - const {updatedContacts} = await promise as { updatedContacts: Partial[] } - const contact = updatedContacts.find (c => c.jid === jid) - assert.ok (contact) - - assert.ok ('name' in contact) - assert.strictEqual (Object.keys(contact).length, 3) - assert.strictEqual (Object.keys(updatedContacts).length, 1) - - conn.close () - }) - it('should queue requests when closed', async () => { - const conn = makeConnection () - //conn.pendingRequestTimeoutMs = null - - await conn.loadAuthInfo('./auth_info.json').connect () - - await delay (2000) - - conn.close () - - const task: Promise = conn.query({json: ['query', 'Status', conn.user.jid]}) - - await delay (2000) - - conn.connect () - const json = await task - - assert.ok (json.status) - - conn.close () - }) - it('[MANUAL] should receive query response after phone disconnect', async () => { - const conn = makeConnection () - await conn.loadAuthInfo('./auth_info.json').connect () - - console.log(`disconnect your phone from the internet!`) - await delay(5000) - const task = conn.loadMessages(testJid, 50) - setTimeout(() => console.log('reconnect your phone!'), 20_000) - - const result = await task - assert.ok(result.messages[0]) - assert.ok(!conn['phoneCheckInterval']) // should be undefined - - conn.close () - }) - it('should re-execute query on connection closed error', async () => { - const conn = makeConnection () - //conn.pendingRequestTimeoutMs = 10_000 - await conn.loadAuthInfo('./auth_info.json').connect () - const task: Promise = conn.query({json: ['query', 'Status', conn.user.jid], waitForOpen: true}) - - await delay(20) - conn['onMessageRecieved']('1234,["Pong",false]') // fake cancel the connection - - await delay(2000) - - const json = await task - - assert.ok (json.status) - - conn.close () - }) -}) \ No newline at end of file diff --git a/src/Tests/Tests.Groups.ts b/src/Tests/Tests.Groups.ts deleted file mode 100644 index 6e2339a..0000000 --- a/src/Tests/Tests.Groups.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { MessageType, GroupSettingChange, delay, ChatModification, whatsappID } from '../WAConnection' -import * as assert from 'assert' -import { WAConnectionTest, testJid, sendAndRetrieveMessage } from './Common' - -WAConnectionTest('Groups', (conn) => { - let gid: string - it('should create a group', async () => { - const response = await conn.groupCreate('Cool Test Group', [testJid]) - assert.ok (conn.chats.get(response.gid)) - - const {chats} = await conn.loadChats(10, null) - assert.strictEqual (chats[0].jid, response.gid) // first chat should be new group - - gid = response.gid - - console.log('created group: ' + JSON.stringify(response)) - }) - it('should retrieve group invite code', async () => { - const code = await conn.groupInviteCode(gid) - assert.ok(code) - assert.strictEqual(typeof code, 'string') - }) - it('should joined group via invite code', async () => { - const response = await conn.acceptInvite(gid) - assert.ok(response.status) - assert.strictEqual(response.status, response.gid) - }) - it('should retrieve group metadata', async () => { - const metadata = await conn.groupMetadata(gid) - assert.strictEqual(metadata.id, gid) - assert.strictEqual(metadata.participants.filter((obj) => obj.jid.split('@')[0] === testJid.split('@')[0]).length, 1) - assert.ok(conn.chats.get(gid)) - assert.ok(conn.chats.get(gid).metadata) - }) - it('should update the group description', async () => { - const newDesc = 'Wow this was set from Baileys' - - const waitForEvent = new Promise (resolve => ( - conn.once ('group-update', ({jid, desc}) => { - if (jid === gid && desc) { - assert.strictEqual(desc, newDesc) - assert.strictEqual( - conn.chats.get(jid).metadata.desc, - newDesc - ) - resolve(undefined) - } - }) - )) - await conn.groupUpdateDescription (gid, newDesc) - await waitForEvent - - const metadata = await conn.groupMetadata(gid) - assert.strictEqual(metadata.desc, newDesc) - }) - it('should send a message on the group', async () => { - await sendAndRetrieveMessage(conn, 'Hello!', MessageType.text, {}, gid) - }) - it('should delete a message on the group', async () => { - const message = await sendAndRetrieveMessage(conn, 'Hello!', MessageType.text, {}, gid) - await delay(1500) - await conn.deleteMessage(message.key) - }) - it('should quote a message on the group', async () => { - const {messages} = await conn.loadMessages (gid, 100) - const quotableMessage = messages.find (m => m.message) - assert.ok (quotableMessage, 'need at least one message') - - const response = await conn.sendMessage(gid, 'hello', MessageType.extendedText, {quoted: quotableMessage}) - const loaded = await conn.loadMessages(gid, 10) - const message = loaded.messages.find (m => m.key.id === response.key.id)?.message?.extendedTextMessage - assert.ok(message) - assert.strictEqual (message.contextInfo.stanzaId, quotableMessage.key.id) - }) - it('should update the subject', async () => { - const subject = 'Baileyz ' + Math.floor(Math.random()*5) - const waitForEvent = new Promise (resolve => { - conn.once ('chat-update', ({jid, name}) => { - if (jid === gid) { - assert.strictEqual(name, subject) - assert.strictEqual(conn.chats.get(jid).name, subject) - resolve(undefined) - } - }) - }) - await conn.groupUpdateSubject(gid, subject) - await waitForEvent - - const metadata = await conn.groupMetadata(gid) - assert.strictEqual(metadata.subject, subject) - }) - - it('should update the group settings', async () => { - const waitForEvent = new Promise (resolve => { - conn.once ('group-update', ({jid, announce}) => { - if (jid === gid) { - assert.strictEqual (announce, 'true') - assert.strictEqual(conn.chats.get(gid).metadata.announce, announce) - resolve(undefined) - } - }) - }) - await conn.groupSettingChange (gid, GroupSettingChange.messageSend, true) - - await waitForEvent - conn.removeAllListeners ('group-update') - - await delay (2000) - await conn.groupSettingChange (gid, GroupSettingChange.settingsChange, true) - }) - - it('should promote someone', async () => { - const waitForEvent = new Promise (resolve => { - conn.once ('group-participants-update', ({ jid, action, participants }) => { - if (jid === gid) { - assert.strictEqual (action, 'promote') - console.log(participants) - console.log(conn.chats.get(jid).metadata) - assert.ok( - conn.chats.get(jid).metadata.participants.find(({ jid, isAdmin }) => ( - whatsappID(jid) === whatsappID(participants[0]) && isAdmin - )), - ) - resolve(undefined) - } - - }) - }) - await conn.groupMakeAdmin(gid, [ testJid ]) - await waitForEvent - }) - - it('should remove someone from a group', async () => { - const metadata = await conn.groupMetadata (gid) - if (metadata.participants.find(({jid}) => whatsappID(jid) === testJid)) { - const waitForEvent = new Promise (resolve => { - conn.once ('group-participants-update', ({jid, participants, action}) => { - if (jid === gid) { - assert.strictEqual (participants[0], testJid) - assert.strictEqual (action, 'remove') - assert.deepStrictEqual( - conn.chats.get(jid).metadata.participants.find(p => whatsappID(p.jid) === whatsappID(participants[0])), - undefined - ) - resolve(undefined) - } - }) - }) - - await conn.groupRemove(gid, [testJid]) - await waitForEvent - } else console.log(`could not find testJid`) - }) - - it('should leave the group', async () => { - const waitForEvent = new Promise (resolve => { - conn.once ('chat-update', ({jid, read_only}) => { - if (jid === gid) { - assert.strictEqual (read_only, 'true') - resolve(undefined) - } - }) - }) - await conn.groupLeave(gid) - await waitForEvent - - await conn.groupMetadataMinimal (gid) - }) - it('should archive the group', async () => { - const waitForEvent = new Promise (resolve => { - conn.once ('chat-update', ({jid, archive}) => { - if (jid === gid) { - assert.strictEqual (archive, 'true') - resolve(undefined) - } - }) - }) - await conn.modifyChat(gid, ChatModification.archive) - await waitForEvent - }) - it('should delete the group', async () => { - const waitForEvent = new Promise (resolve => { - conn.once ('chat-update', (chat) => { - if (chat.jid === gid) { - assert.strictEqual (chat['delete'], 'true') - resolve(undefined) - } - }) - }) - await conn.modifyChat(gid, 'delete') - await waitForEvent - }) -}) diff --git a/src/Tests/Tests.Media.ts b/src/Tests/Tests.Media.ts deleted file mode 100644 index 34426ad..0000000 --- a/src/Tests/Tests.Media.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { deepStrictEqual, strictEqual } from 'assert' -import { createWriteStream } from 'fs' -import { readFile } from 'fs/promises' -import { proto } from '../../WAMessage/WAMessage' -import { MessageType } from '../WAConnection' -import { aesEncrypWithIV, decryptMediaMessageBuffer, encryptedStream, getMediaKeys, getStream, hmacSign, sha256 } from '../WAConnection/Utils' -import { WAConnectionTest } from './Common' - -describe('Media Download Tests', () => { - - it('should encrypt media streams correctly', async function() { - const url = './Media/meme.jpeg' - const streamValues = await encryptedStream({ url }, MessageType.image) - - const buffer = await readFile(url) - const mediaKeys = getMediaKeys(streamValues.mediaKey, MessageType.image) - - const enc = aesEncrypWithIV(buffer, mediaKeys.cipherKey, mediaKeys.iv) - const mac = hmacSign(Buffer.concat([mediaKeys.iv, enc]), mediaKeys.macKey).slice(0, 10) - const body = Buffer.concat([enc, mac]) // body is enc + mac - const fileSha256 = sha256(buffer) - const fileEncSha256 = sha256(body) - - deepStrictEqual(streamValues.fileSha256, fileSha256) - strictEqual(streamValues.fileLength, buffer.length) - deepStrictEqual(streamValues.mac, mac) - deepStrictEqual(await readFile(streamValues.encBodyPath), body) - deepStrictEqual(streamValues.fileEncSha256, fileEncSha256) - - }) -}) -/* -WAConnectionTest('Media Upload', conn => { - - it('should upload the same file', async () => { - const FILES = [ - { url: './Media/meme.jpeg', type: MessageType.image }, - { url: './Media/ma_gif.mp4', type: MessageType.video }, - { url: './Media/sonata.mp3', type: MessageType.audio }, - ] - }) - -})*/ \ No newline at end of file diff --git a/src/Tests/Tests.Messages.ts b/src/Tests/Tests.Messages.ts deleted file mode 100644 index a9b43fc..0000000 --- a/src/Tests/Tests.Messages.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { MessageType, Mimetype, delay, promiseTimeout, WA_MESSAGE_STATUS_TYPE, generateMessageID, WAMessage } from '../WAConnection' -import { promises as fs } from 'fs' -import * as assert from 'assert' -import { WAConnectionTest, testJid, sendAndRetrieveMessage } from './Common' - -WAConnectionTest('Messages', conn => { - - it('should send a text message', async () => { - const message = await sendAndRetrieveMessage(conn, 'hello fren', MessageType.text) - assert.strictEqual(message.message.conversation || message.message.extendedTextMessage?.text, 'hello fren') - }) - it('should send a pending message', async () => { - const message = await sendAndRetrieveMessage(conn, 'hello fren', MessageType.text, { waitForAck: false }) - - await new Promise(resolve => conn.on('chat-update', update => { - if (update.jid === testJid && - update.messages && - update.messages.first.key.id === message.key.id && - update.messages.first.status === WA_MESSAGE_STATUS_TYPE.SERVER_ACK) { - resolve(undefined) - } - })) - - }) - it('should forward a message', async () => { - let {messages} = await conn.loadMessages (testJid, 1) - await conn.forwardMessage (testJid, messages[0], true) - - messages = (await conn.loadMessages (testJid, 1)).messages - const message = messages.slice (-1)[0] - const content = message.message[ Object.keys(message.message)[0] ] - assert.strictEqual (content?.contextInfo?.isForwarded, true) - }) - it('should send a link preview', async () => { - const text = 'hello this is from https://www.github.com/adiwajshing/Baileys' - const message = await sendAndRetrieveMessage(conn, text, MessageType.text, { detectLinks: true }) - const received = message.message.extendedTextMessage - - assert.strictEqual(received.text, text) - assert.ok (received.canonicalUrl) - assert.ok (received.title) - assert.ok (received.description) - }) - it('should quote a message', async () => { - const quoted = (await conn.loadMessages(testJid, 2)).messages[0] - const message = await sendAndRetrieveMessage(conn, 'hello fren 2', MessageType.extendedText, { quoted }) - assert.strictEqual( - message.message.extendedTextMessage.contextInfo.stanzaId, - quoted.key.id - ) - assert.strictEqual( - message.message.extendedTextMessage.contextInfo.participant, - quoted.key.fromMe ? conn.user.jid : quoted.key.id - ) - }) - it('should upload media successfully', async () => { - const content = await fs.readFile('./Media/sonata.mp3') - // run 10 uploads - for (let i = 0; i < 10;i++) { - await conn.prepareMessageContent (content, MessageType.audio, { filename: 'audio.mp3', mimetype: Mimetype.mp4Audio }) - } - }) - it('should send a gif', async () => { - const message = await sendAndRetrieveMessage(conn, { url: './Media/ma_gif.mp4' }, MessageType.video, { mimetype: Mimetype.gif }) - - await conn.downloadAndSaveMediaMessage(message,'./Media/received_vid') - }) - it('should send an audio', async () => { - const content = await fs.readFile('./Media/sonata.mp3') - const message = await sendAndRetrieveMessage(conn, content, MessageType.audio, { mimetype: Mimetype.mp4Audio }) - // check duration was okay - assert.ok (message.message.audioMessage.seconds > 0) - await conn.downloadAndSaveMediaMessage(message,'./Media/received_aud') - }) - it('should send a voice note', async () => { - const content = await fs.readFile('./Media/sonata.mp3') - const message = await sendAndRetrieveMessage(conn, content, MessageType.audio, { mimetype: Mimetype.mp4Audio, ptt: true }) - - assert.ok (message.message.audioMessage.seconds > 0) - assert.strictEqual (message.message?.audioMessage?.ptt, true) - await conn.downloadAndSaveMediaMessage(message,'./Media/received_aud') - }) - it('should send a jpeg image', async () => { - const message = await sendAndRetrieveMessage(conn, { url: './Media/meme.jpeg' }, MessageType.image) - assert.ok(message.message.imageMessage.jpegThumbnail.length > 0) - const msg = await conn.downloadMediaMessage(message) - assert.deepStrictEqual(msg, await fs.readFile('./Media/meme.jpeg')) - }) - it('should send a remote jpeg image', async () => { - const message = await sendAndRetrieveMessage( - conn, - { url: 'https://www.memestemplates.com/wp-content/uploads/2020/05/tom-with-phone.jpg' }, - MessageType.image - ) - assert.ok (message.message?.imageMessage?.jpegThumbnail) - await conn.downloadMediaMessage(message) - }) - it('should send a png image', async () => { - const content = await fs.readFile('./Media/icon.png') - const message = await sendAndRetrieveMessage(conn, content, MessageType.image, { mimetype: 'image/png' }) - assert.ok (message.message?.imageMessage?.jpegThumbnail) - await conn.downloadMediaMessage(message) - }) - it('should send a sticker', async () => { - const content = await fs.readFile('./Media/octopus.webp') - const message = await sendAndRetrieveMessage(conn, content, MessageType.sticker) - - await conn.downloadMediaMessage(message) - }) - /*it('should send an interactive message', async () => { - - console.log ( - JSON.stringify(await conn.loadMessages (testJid, 5), null, '\t') - ) - const message = conn.prepareMessageFromContent ( - testJid, - { - templateMessage: { - fourRowTemplate: { - content: { - namespace: 'my-namespace', - localizableParams: [ - - ], - params: ['hello!'] - }, - buttons: [ - { - index: 0, - quickReplyButton: { - displayText: { - params: ['my name jeff'] - } - } - }, - { - index: 1, - quickReplyButton: { - displayText: { - params: ['my name NOT jeff'], - } - } - } - ] - } - } - }, - {} - ) - await conn.relayWAMessage (message) - })*/ - it('should send an image & quote', async () => { - const quoted = (await conn.loadMessages(testJid, 2)).messages[0] - const content = await fs.readFile('./Media/meme.jpeg') - const message = await sendAndRetrieveMessage(conn, content, MessageType.image, { quoted }) - - await conn.downloadMediaMessage(message) // check for successful decoding - assert.strictEqual(message.message.imageMessage.contextInfo.stanzaId, quoted.key.id) - }) - it('should send a message & delete it', async () => { - const message = await sendAndRetrieveMessage(conn, 'hello fren', MessageType.text) - await delay (2000) - await conn.deleteMessage (testJid, message.key) - }) - it('should clear the most recent message', async () => { - const {messages} = await conn.loadMessages (testJid, 1) - await delay (2000) - await conn.clearMessage (messages[0].key) - }) - it('should send media after close', async () => { - const content = await fs.readFile('./Media/octopus.webp') - await sendAndRetrieveMessage(conn, content, MessageType.sticker) - - conn.close () - - await conn.connect () - - const content2 = await fs.readFile('./Media/cat.jpeg') - await sendAndRetrieveMessage(conn, content2, MessageType.image) - }) - it('should fail to send a text message', async () => { - const JID = '1234-1234@g.us' - const messageId = generateMessageID() - conn.sendMessage(JID, 'hello', MessageType.text, { messageId }) - - await new Promise(resolve => ( - conn.on ('chat-update', async update => { - console.log(messageId, update.messages?.first) - if ( - update.jid === JID && - update.messages?.first.key.id === messageId && - update.messages.first.status === WA_MESSAGE_STATUS_TYPE.ERROR) { - resolve(undefined) - } - }) - )) - conn.removeAllListeners('chat-update') - }) - it('should maintain message integrity', async () => { - // loading twice does not alter the results - const results = await Promise.all ([ - conn.loadMessages (testJid, 50), - conn.loadMessages (testJid, 50) - ]) - assert.strictEqual (results[0].messages.length, results[1].messages.length) - for (let i = 0; i < results[1].messages.length;i++) { - assert.deepStrictEqual ( - results[0].messages[i].key, - results[1].messages[i].key, - `failed equal at ${i}` - ) - } - assert.ok (results[0].messages.length <= 50) - - // check if messages match server - let msgs = await conn.fetchMessagesFromWA (testJid, 50) - for (let i = 0; i < results[1].messages.length;i++) { - assert.deepStrictEqual ( - results[0].messages[i].key, - msgs[i].key, - `failed equal at ${i}` - ) - } - // check with some arbitary cursors - let cursor = results[0].messages.slice(-1)[0].key - - msgs = await conn.fetchMessagesFromWA (testJid, 20, cursor) - let {messages} = await conn.loadMessages (testJid, 20, cursor) - for (let i = 0; i < messages.length;i++) { - assert.deepStrictEqual ( - messages[i].key, - msgs[i].key, - `failed equal at ${i}` - ) - } - for (let i = 0; i < 3;i++) { - cursor = results[0].messages[i].key - - msgs = await conn.fetchMessagesFromWA (testJid, 20, cursor) - messages = (await conn.loadMessages (testJid, 20, cursor)).messages - for (let i = 0; i < messages.length;i++) { - assert.deepStrictEqual (messages[i].key, msgs[i].key, `failed equal at ${i}`) - } - - cursor = msgs[0].key - - msgs = await conn.fetchMessagesFromWA (testJid, 20, cursor) - messages = (await conn.loadMessages (testJid, 20, cursor)).messages - for (let i = 0; i < messages.length;i++) { - assert.deepStrictEqual (messages[i].key, msgs[i].key, `failed equal at ${i}`) - } - } - }) - it('should deliver a message', async () => { - const response = await conn.sendMessage(testJid, 'My Name Jeff', MessageType.text) - const waitForUpdate = - promiseTimeout(15000, resolve => { - conn.on('chat-update', update => { - if (update.messages?.first.key.id === response.key.id) { - resolve(update.messages.first) - } - }) - }) as Promise - - const m = await waitForUpdate - assert.ok (m.status >= WA_MESSAGE_STATUS_TYPE.DELIVERY_ACK) - }) -}) diff --git a/src/Tests/Tests.Misc.ts b/src/Tests/Tests.Misc.ts deleted file mode 100644 index 8b09bc6..0000000 --- a/src/Tests/Tests.Misc.ts +++ /dev/null @@ -1,430 +0,0 @@ -import { Presence, ChatModification, delay, newMessagesDB, WA_DEFAULT_EPHEMERAL, MessageType, WAMessage } from '../WAConnection' -import { promises as fs } from 'fs' -import * as assert from 'assert' -import got from 'got' -import { WAConnectionTest, testJid, sendAndRetrieveMessage } from './Common' - -WAConnectionTest('Misc', conn => { - - it('should tell if someone has an account on WhatsApp', async () => { - const response = await conn.isOnWhatsApp(testJid) - assert.strictEqual(response, true) - - const responseFail = await conn.isOnWhatsApp('abcd@s.whatsapp.net') - assert.strictEqual(responseFail, false) - }) - it('should return the status', async () => { - const response = await conn.getStatus(testJid) - assert.strictEqual(typeof response.status, 'string') - }) - it('should update status', async () => { - const newStatus = 'v cool status' - - const waitForEvent = new Promise (resolve => { - conn.on ('contact-update', ({jid, status}) => { - if (jid === conn.user.jid) { - assert.strictEqual (status, newStatus) - conn.removeAllListeners ('contact-update') - resolve(undefined) - } - }) - }) - - const response = await conn.getStatus() - assert.strictEqual(typeof response.status, 'string') - - await delay (1000) - - await conn.setStatus (newStatus) - const response2 = await conn.getStatus() - assert.strictEqual (response2.status, newStatus) - - await waitForEvent - - await delay (1000) - - await conn.setStatus (response.status) // update back - }) - it('should update profile name', async () => { - const newName = 'v cool name' - - await delay (1000) - - const originalName = conn.user.name! - - const waitForEvent = new Promise (resolve => { - conn.on ('contact-update', ({name}) => { - assert.strictEqual (name, newName) - conn.removeAllListeners ('contact-update') - resolve () - }) - }) - - await conn.updateProfileName (newName) - - await waitForEvent - - await delay (1000) - - assert.strictEqual (conn.user.name, newName) - - await delay (1000) - - await conn.updateProfileName (originalName) // update back - }) - it('should return the stories', async () => { - await conn.getStories() - }) - - it('should return the profile picture correctly', async () => { - // wait for chats - await new Promise(resolve => ( - conn.once('initial-data-received', resolve) - )) - const pictures = await Promise.all( - conn.chats.all().slice(0, 15).map(({ jid }) => ( - conn.getProfilePicture(jid) - .catch(err => '') - )) - ) - // pictures should return correctly - const truePictures = pictures.filter(pp => !!pp) - assert.strictEqual( - new Set(truePictures).size, - truePictures.length - ) - }) - - it('should change the profile picture', async () => { - await delay (5000) - - const ppUrl = await conn.getProfilePicture(conn.user.jid) - const {rawBody: oldPP} = await got(ppUrl) - - const newPP = await fs.readFile('./Media/cat.jpeg') - await conn.updateProfilePicture(conn.user.jid, newPP) - - await delay (10000) - - await conn.updateProfilePicture (conn.user.jid, oldPP) // revert back - }) - it('should send typing indicator', async () => { - const response = await conn.updatePresence(testJid, Presence.composing) - assert.ok(response) - }) - it('should change a chat read status', async () => { - const jids = conn.chats.all ().map (c => c.jid) - for (let jid of jids.slice(0, 5)) { - console.log (`changing read status for ${jid}`) - const waitForEvent = new Promise (resolve => { - conn.once ('chat-update', ({jid: tJid, count}) => { - if (jid === tJid) { - assert.ok (count < 0) - resolve(undefined) - } - }) - }) - await conn.chatRead (jid, 'unread') - await waitForEvent - - await delay (5000) - - await conn.chatRead (jid, 'read') - } - }) - it('should archive & unarchive', async () => { - // wait for chats - await new Promise(resolve => ( - conn.once('chats-received', ({ }) => resolve(undefined)) - )) - - const idx = conn.chats.all().findIndex(chat => chat.jid === testJid) - await conn.modifyChat (testJid, ChatModification.archive) - const idx2 = conn.chats.all().findIndex(chat => chat.jid === testJid) - assert.ok(idx < idx2) // should move further down the array - - await delay (2000) - await conn.modifyChat (testJid, ChatModification.unarchive) - const idx3 = conn.chats.all().findIndex(chat => chat.jid === testJid) - assert.strictEqual(idx, idx3) // should be back there - }) - it('should archive & unarchive on new message', async () => { - // wait for chats - await new Promise(resolve => ( - conn.once('chats-received', ({ }) => resolve(undefined)) - )) - - const idx = conn.chats.all().findIndex(chat => chat.jid === testJid) - await conn.modifyChat (testJid, ChatModification.archive) - const idx2 = conn.chats.all().findIndex(chat => chat.jid === testJid) - assert.ok(idx < idx2) // should move further down the array - - await delay (2000) - await sendAndRetrieveMessage(conn, 'test', MessageType.text) - // should be unarchived - const idx3 = conn.chats.all().findIndex(chat => chat.jid === testJid) - assert.strictEqual(idx, idx3) // should be back there - }) - it('should pin & unpin a chat', async () => { - await conn.modifyChat (testJid, ChatModification.pin) - await delay (2000) - await conn.modifyChat (testJid, ChatModification.unpin) - }) - it('should mute & unmute a chat', async () => { - const waitForEvent = new Promise (resolve => { - conn.on ('chat-update', ({jid, mute}) => { - if (jid === testJid ) { - assert.ok (mute) - conn.removeAllListeners ('chat-update') - resolve(undefined) - } - }) - }) - await conn.modifyChat (testJid, ChatModification.mute, 8*60*60*1000) // 8 hours in the future - await waitForEvent - await delay (2000) - await conn.modifyChat (testJid, ChatModification.unmute) - }) - it('should star/unstar messages', async () => { - for (let i = 1; i <= 5; i++) { - await conn.sendMessage(testJid, `Message ${i}`, MessageType.text) - await delay(1000) - } - - let response = await conn.loadMessages(testJid, 5) - let starred = response.messages.filter(m => m.starred) - assert.strictEqual(starred.length, 0) - - conn.starMessage(response.messages[2].key) - await delay(2000) - conn.starMessage(response.messages[4].key) - await delay(2000) - - response = await conn.loadMessages(testJid, 5) - starred = response.messages.filter(m => m.starred) - assert.strictEqual(starred.length, 2) - await delay(2000) - - conn.starMessage(response.messages[2].key, 'unstar') - await delay(2000) - - response = await conn.loadMessages(testJid, 5) - starred = response.messages.filter(m => m.starred) - assert.strictEqual(starred.length, 1) - }) - it('should clear a chat', async () => { - // Uses chat with yourself to avoid losing chats - const selfJid = conn.user.jid - - for (let i = 1; i <= 5; i++) { - await conn.sendMessage(selfJid, `Message ${i}`, MessageType.text) - await delay(1000) - } - - let response = await conn.loadMessages(selfJid, 50) - const initialCount = response.messages.length - - assert.ok(response.messages.length >= 0) - - conn.starMessage(response.messages[2].key) - await delay(2000) - conn.starMessage(response.messages[4].key) - await delay(2000) - - await conn.modifyChat(selfJid, ChatModification.clear) - await delay(2000) - - response = await conn.loadMessages(selfJid, 50) - await delay(2000) - assert.ok(response.messages.length < initialCount) - assert.ok(response.messages.length > 1) - - await conn.modifyChat(selfJid, ChatModification.clear, true) - await delay(2000) - - response = await conn.loadMessages(selfJid, 50) - assert.strictEqual(response.messages.length, 1) - }) - it('should return search results', async () => { - const jids = [null, testJid] - for (let i in jids) { - let response = await conn.searchMessages('Hello', jids[i], 25, 1) - assert.ok (response.messages) - assert.ok (response.messages.length >= 0) - - response = await conn.searchMessages('剛剛試咗😋一個字', jids[i], 25, 1) - assert.ok (response.messages) - assert.ok (response.messages.length >= 0) - } - }) - - it('should load a single message', async () => { - const {messages} = await conn.loadMessages (testJid, 25) - for (var message of messages) { - const loaded = await conn.loadMessage (testJid, message.key.id) - assert.strictEqual (loaded.key.id, message.key.id, `loaded message ${JSON.stringify(message)} incorrectly`) - await delay (500) - } - }) - // open the other phone and look at the updates to really verify stuff - it('should send presence updates', async () => { - conn.shouldLogMessages = true - conn.requestPresenceUpdate(testJid) - - const sequence = [ Presence.available, Presence.composing, Presence.paused, Presence.recording, Presence.paused, Presence.unavailable ] - for (const presence of sequence) { - await delay(5000) - await conn.updatePresence(presence !== Presence.unavailable ? testJid : null, presence) - //console.log(conn.messageLog.slice(-1)) - console.log('sent update ', presence) - } - }) - it('should generate link previews correctly', async () => { - await conn.generateLinkPreview ('hello this is from https://www.github.com/adiwajshing/Baileys') - // two links should fail - await assert.rejects ( - conn.generateLinkPreview ('I sent links to https://teachyourselfcs.com/ and https://www.fast.ai/') - ) - }) - // this test requires quite a few messages with the test JID - it('should detect overlaps and clear messages accordingly', async () => { - // wait for chats - await new Promise(resolve => ( - conn.once('initial-data-received', resolve) - )) - - conn.maxCachedMessages = 100 - - const chat = conn.chats.get(testJid) - const oldCount = chat.messages.length - console.log(`test chat has ${oldCount} pre-loaded messages`) - // load 100 messages - await conn.loadMessages(testJid, 100, undefined) - assert.strictEqual(chat.messages.length, 100) - - conn.close() - // remove all latest messages - chat.messages = newMessagesDB( chat.messages.all().slice(0, 20) ) - - const task = new Promise(resolve => ( - conn.on('initial-data-received', ({ chatsWithMissingMessages }) => { - assert.strictEqual(Object.keys(chatsWithMissingMessages).length, 1) - const missing = chatsWithMissingMessages.find(({ jid }) => jid === testJid) - assert.ok(missing, 'missing message not detected') - assert.strictEqual( - conn.chats.get(testJid).messages.length, - missing.count - ) - assert.strictEqual(missing.count, oldCount) - resolve(undefined) - }) - )) - - await conn.connect() - - await task - }) - - it('should toggle disappearing messages', async () => { - let chat = conn.chats.get(testJid) - if (!chat) { - // wait for chats - await new Promise(resolve => ( - conn.once('chats-received', resolve) - )) - chat = conn.chats.get(testJid) - } - - const waitForChatUpdate = (ephemeralOn: boolean) => ( - new Promise(resolve => ( - conn.on('chat-update', ({ jid, ephemeral }) => { - if (jid === testJid && typeof ephemeral !== 'undefined') { - assert.strictEqual(!!(+ephemeral), ephemeralOn) - assert.strictEqual(!!(+chat.ephemeral), ephemeralOn) - resolve(undefined) - conn.removeAllListeners('chat-update') - } - }) - )) - ) - const toggleDisappearingMessages = async (on: boolean) => { - const update = waitForChatUpdate(on) - await conn.toggleDisappearingMessages(testJid, on ? WA_DEFAULT_EPHEMERAL : 0) - await update - } - - if (!chat.eph_setting_ts) { - await toggleDisappearingMessages(true) - } - - await delay(1000) - - let msg = await sendAndRetrieveMessage( - conn, - 'This will go poof 😱', - MessageType.text - ) - assert.ok(msg.message?.ephemeralMessage) - - const contextInfo = msg.message?.ephemeralMessage?.message?.extendedTextMessage?.contextInfo - assert.strictEqual(contextInfo.expiration, chat.ephemeral) - assert.strictEqual(+contextInfo.ephemeralSettingTimestamp, +chat.eph_setting_ts) - // test message deletion - await conn.deleteMessage(testJid, msg.key) - - await delay(1000) - - await toggleDisappearingMessages(false) - - await delay(1000) - - msg = await sendAndRetrieveMessage( - conn, - 'This will not go poof 😔', - MessageType.text - ) - assert.ok(msg.message.extendedTextMessage) - }) - it('should block & unblock a user', async () => { - const blockedCount = conn.blocklist.length; - - const waitForEventAdded = new Promise (resolve => { - conn.once ('blocklist-update', ({added}) => { - assert.ok (added.length) - resolve () - }) - }) - - await conn.blockUser (testJid, 'add') - assert.strictEqual(conn.blocklist.length, blockedCount + 1); - await waitForEventAdded - - await delay (2000) - const waitForEventRemoved = new Promise (resolve => { - conn.once ('blocklist-update', ({removed}) => { - assert.ok (removed.length) - resolve () - }) - }) - - await conn.blockUser (testJid, 'remove') - assert.strictEqual(conn.blocklist.length, blockedCount); - await waitForEventRemoved - }) - it('should exit an invalid query', async () => { - // try and send an already sent message - let msg: WAMessage - await conn.findMessage(testJid, 5, m => { - if(m.key.fromMe) { - msg = m - return true - } - }) - try { - await conn.relayWAMessage(msg) - assert.fail('should not have sent') - } catch(error) { - assert.strictEqual(error.status, 422) - } - }) -}) diff --git a/src/Tests/Tests.Mutex.ts b/src/Tests/Tests.Mutex.ts deleted file mode 100644 index d4e2992..0000000 --- a/src/Tests/Tests.Mutex.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { strict as assert } from 'assert' -import { Mutex } from '../WAConnection/Mutex' - -const DEFAULT_WAIT = 1000 - -class MyClass { - didDoWork = false - values: { [k: string]: number } = {} - counter = 0 - - @Mutex () - async myFunction () { - if (this.didDoWork) return - - await new Promise (resolve => setTimeout(resolve, DEFAULT_WAIT)) - if (this.didDoWork) { - throw new Error ('work already done') - } - this.didDoWork = true - } - @Mutex (key => key) - async myKeyedFunction (key: string) { - if (!this.values[key]) { - await new Promise (resolve => setTimeout(resolve, DEFAULT_WAIT)) - if (this.values[key]) throw new Error ('value already set for ' + key) - this.values[key] = Math.floor(Math.random ()*100) - } - return this.values[key] - } - @Mutex (key => key) - async myQueingFunction (key: string) { - await new Promise (resolve => setTimeout(resolve, DEFAULT_WAIT)) - } - @Mutex () - async myErrorFunction () { - await new Promise (resolve => setTimeout(resolve, 100)) - this.counter += 1 - if (this.counter % 2 === 0) { - throw new Error ('failed') - } - } -} - -describe ('garbage', () => { - it ('should only execute once', async () => { - const stuff = new MyClass () - const start = new Date () - await Promise.all ([...Array(1000)].map(() => stuff.myFunction ())) - const diff = new Date ().getTime()-start.getTime() - assert.ok (diff < DEFAULT_WAIT*1.25) - }) - it ('should only execute once based on the key', async () => { - const stuff = new MyClass () - const start = new Date () - /* - In this test, the mutex will lock the function based on the key. - - So, if the function with argument `key1` is underway - and another function with key `key1` is called, - the call is blocked till the first function completes. - However, if argument `key2` is passed, the function is allowed to pass. - */ - const keys = ['key1', 'key2', 'key3'] - const duplicates = 1000 - const results = await Promise.all ( - keys.flatMap (key => ( - [...Array(duplicates)].map(() => stuff.myKeyedFunction (key)) - )) - ) - assert.deepStrictEqual ( - results.slice(0, duplicates).filter (r => r !== results[0]), - [] - ) - - const diff = new Date ().getTime()-start.getTime() - assert.ok (diff < DEFAULT_WAIT*1.25) - }) - it ('should execute operations in a queue', async () => { - const stuff = new MyClass () - const start = new Date () - - const keys = ['key1', 'key2', 'key3'] - - await Promise.all ( - keys.flatMap (key => ( - [...Array(2)].map(() => stuff.myQueingFunction (key)) - )) - ) - - const diff = new Date ().getTime()-start.getTime() - assert.ok (diff < DEFAULT_WAIT*2.2 && diff > DEFAULT_WAIT*1.5) - }) - it ('should throw an error on selected items', async () => { - const stuff = new MyClass () - const start = new Date () - - const WAIT = 100 - const FUNCS = 40 - const results = await Promise.all ( - [...Array(FUNCS)].map(() => stuff.myErrorFunction ().catch(err => err.message)) - ) - - const diff = new Date ().getTime()-start.getTime() - assert.ok (diff < WAIT*FUNCS*1.1) - - assert.strictEqual ( - results.filter (r => r === 'failed').length, - FUNCS/2 // half should fail - ) - }) -}) \ No newline at end of file diff --git a/src/Tests/test.media-download.ts b/src/Tests/test.media-download.ts new file mode 100644 index 0000000..81e7b4a --- /dev/null +++ b/src/Tests/test.media-download.ts @@ -0,0 +1,76 @@ +import { readFileSync } from 'fs' +import { proto } from '../../WAProto' +import { DownloadableMessage, MediaType } from '../Types' +import { downloadContentFromMessage } from '../Utils' + +jest.setTimeout(20_000) + +type TestVector = { + type: MediaType + message: DownloadableMessage + plaintext: Buffer +} + +const TEST_VECTORS: TestVector[] = [ + { + type: 'image', + message: proto.ImageMessage.decode( + Buffer.from( + 'Ck1odHRwczovL21tZy53aGF0c2FwcC5uZXQvZC9mL0FwaHR4WG9fWXZZcDZlUVNSa0tjOHE5d2ozVUpleWdoY3poM3ExX3I0ektnLmVuYxIKaW1hZ2UvanBlZyIgKTuVFyxDc6mTm4GXPlO3Z911Wd8RBeTrPLSWAEdqW8MomcUBQiB7wH5a4nXMKyLOT0A2nFgnnM/DUH8YjQf8QtkCIekaSkogTB+BXKCWDFrmNzozY0DCPn0L4VKd7yG1ZbZwbgRhzVc=', + 'base64' + ) + ), + plaintext: readFileSync('./Media/cat.jpeg') + }, + { + type: 'image', + message: proto.ImageMessage.decode( + Buffer.from( + 'Ck1odHRwczovL21tZy53aGF0c2FwcC5uZXQvZC9mL0Ftb2tnWkphNWF6QWZxa3dVRzc0eUNUdTlGeWpjMmd5akpqcXNmMUFpZEU5LmVuYxIKaW1hZ2UvanBlZyIg8IS5TQzdzcuvcR7F8HMhWnXmlsV+GOo9JE1/t2k+o9Yoz6o6QiA7kDk8j5KOEQC0kDFE1qW7lBBDYhm5z06N3SirfUj3CUog/CjYF8e670D5wUJwWv2B2mKzDEo8IJLStDv76YmtPfs=', + 'base64' + ) + ), + plaintext: readFileSync('./Media/icon.png') + }, +] + +describe('Media Download Tests', () => { + + it('should download a full encrypted media correctly', async() => { + for(const { type, message, plaintext } of TEST_VECTORS) { + const readPipe = await downloadContentFromMessage(message, type) + + let buffer = Buffer.alloc(0) + for await (const read of readPipe) { + buffer = Buffer.concat([ buffer, read ]) + } + + expect(buffer).toEqual(plaintext) + } + }) + + it('should download an encrypted media correctly piece', async() => { + for(const { type, message, plaintext } of TEST_VECTORS) { + // check all edge cases + const ranges = [ + { startByte: 51, endByte: plaintext.length-100 }, // random numbers + { startByte: 1024, endByte: 2038 }, // larger random multiples of 16 + { startByte: 1, endByte: plaintext.length-1 } // borders + ] + for(const range of ranges) { + const readPipe = await downloadContentFromMessage(message, type, range) + + let buffer = Buffer.alloc(0) + for await (const read of readPipe) { + buffer = Buffer.concat([ buffer, read ]) + } + + const hex = buffer.toString('hex') + const expectedHex = plaintext.slice(range.startByte || 0, range.endByte || undefined).toString('hex') + expect(hex).toBe(expectedHex) + + console.log('success on ', range) + } + } + }) +}) \ No newline at end of file diff --git a/src/Types/Auth.ts b/src/Types/Auth.ts new file mode 100644 index 0000000..30cc1ce --- /dev/null +++ b/src/Types/Auth.ts @@ -0,0 +1,77 @@ +import type { proto } from '../../WAProto' +import type { Contact } from './Contact' + +export type KeyPair = { public: Uint8Array, private: Uint8Array } +export type SignedKeyPair = { keyPair: KeyPair, signature: Uint8Array, keyId: number } + +export type ProtocolAddress = { + name: string // jid + deviceId: number +} +export type SignalIdentity = { + identifier: ProtocolAddress + identifierKey: Uint8Array +} + +export type LTHashState = { + version: number + hash: Buffer + indexValueMap: { + [indexMacBase64: string]: { valueMac: Uint8Array | Buffer } + } +} + +export type SignalCreds = { + readonly signedIdentityKey: KeyPair + readonly signedPreKey: SignedKeyPair + readonly registrationId: number +} + +export type AuthenticationCreds = SignalCreds & { + readonly noiseKey: KeyPair + readonly advSecretKey: string + + me?: Contact + account?: proto.IADVSignedDeviceIdentity + signalIdentities?: SignalIdentity[] + myAppStateKeyId?: string + firstUnuploadedPreKeyId: number + serverHasPreKeys: boolean + nextPreKeyId: number + + lastAccountSyncTimestamp?: number +} + +export type SignalDataTypeMap = { + 'pre-key': KeyPair + 'session': any + 'sender-key': any + 'sender-key-memory': { [jid: string]: boolean } + 'app-state-sync-key': proto.IAppStateSyncKeyData + 'app-state-sync-version': LTHashState +} + +export type SignalDataSet = { [T in keyof SignalDataTypeMap]?: { [id: string]: SignalDataTypeMap[T] | null } } + +type Awaitable = T | Promise + +export type SignalKeyStore = { + get(type: T, ids: string[]): Awaitable<{ [id: string]: SignalDataTypeMap[T] }> + set(data: SignalDataSet): Awaitable +} + +export type SignalKeyStoreWithTransaction = SignalKeyStore & { + isInTransaction: () => boolean + transaction(exec: () => Promise): Promise + prefetch(type: T, ids: string[]): Promise +} + +export type SignalAuthState = { + creds: SignalCreds + keys: SignalKeyStore +} + +export type AuthenticationState = { + creds: AuthenticationCreds + keys: SignalKeyStore +} \ No newline at end of file diff --git a/src/Types/Chat.ts b/src/Types/Chat.ts new file mode 100644 index 0000000..3b98d84 --- /dev/null +++ b/src/Types/Chat.ts @@ -0,0 +1,66 @@ +import type { proto } from '../../WAProto' + +/** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */ +export type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused' + +export type WAPatchName = 'critical_block' | 'critical_unblock_low' | 'regular_low' | 'regular_high' | 'regular' + +export interface PresenceData { + lastKnownPresence: WAPresence + lastSeen?: number +} + +export type ChatMutation = { + syncAction: proto.ISyncActionData + index: string[] +} + +export type AppStateChunk = { totalMutations : ChatMutation[], collectionsToHandle: WAPatchName[] } + +export type WAPatchCreate = { + syncAction: proto.ISyncActionValue + index: string[] + type: WAPatchName + apiVersion: number + operation: proto.SyncdMutation.SyncdMutationSyncdOperation +} + +export type Chat = Omit & { + /** unix timestamp of date when mute ends, if applicable */ + mute?: number | null + /** timestamp of when pinned */ + pin?: number | null + archive?: boolean +} +/** + * the last messages in a chat, sorted reverse-chronologically + * for MD modifications, the last message in the array must be the last message recv in the chat + * */ +export type LastMessageList = Pick[] + +export type ChatModification = + { + archive: boolean + lastMessages: LastMessageList + } | + { + pin: boolean + } | + { + /** mute for duration, or provide timestamp of mute to remove*/ + mute: number | null + } | + { + clear: 'all' | { messages: {id: string, fromMe?: boolean}[] } + } | + { + star: { + messages: { id: string, fromMe?: boolean }[], + star: boolean + } + } | + { + markRead: boolean + lastMessages: LastMessageList + } | + { delete: true, lastMessages: LastMessageList } \ No newline at end of file diff --git a/src/Types/Contact.ts b/src/Types/Contact.ts new file mode 100644 index 0000000..f5e74b3 --- /dev/null +++ b/src/Types/Contact.ts @@ -0,0 +1,12 @@ +export interface Contact { + id: string + /** name of the contact, you have saved on your WA */ + name?: string + /** name of the contact, the contact has set on their own on WA */ + notify?: string + /** I have no idea */ + verifiedName?: string + // Baileys Added + imgUrl?: string + status?: string +} \ No newline at end of file diff --git a/src/Types/Events.ts b/src/Types/Events.ts new file mode 100644 index 0000000..889040d --- /dev/null +++ b/src/Types/Events.ts @@ -0,0 +1,58 @@ +import type EventEmitter from 'events' +import { AuthenticationCreds } from './Auth' +import { Chat, PresenceData } from './Chat' +import { Contact } from './Contact' +import { GroupMetadata, ParticipantAction } from './GroupMetadata' +import { MessageUpdateType, MessageUserReceiptUpdate, WAMessage, WAMessageKey, WAMessageUpdate } from './Message' +import { ConnectionState } from './State' + +export type BaileysEventMap = { + /** connection state has been updated -- WS closed, opened, connecting etc. */ + 'connection.update': Partial + /** credentials updated -- some metadata, keys or something */ + 'creds.update': Partial + /** set chats (history sync), chats are reverse chronologically sorted */ + 'chats.set': { chats: Chat[], isLatest: boolean } + /** set messages (history sync), messages are reverse chronologically sorted */ + 'messages.set': { messages: WAMessage[], isLatest: boolean } + /** set contacts (history sync) */ + 'contacts.set': { contacts: Contact[] } + /** upsert chats */ + 'chats.upsert': Chat[] + /** update the given chats */ + 'chats.update': Partial[] + /** delete chats with given ID */ + 'chats.delete': string[] + /** presence of contact in a chat updated */ + 'presence.update': { id: string, presences: { [participant: string]: PresenceData } } + + 'contacts.upsert': Contact[] + 'contacts.update': Partial[] + + 'messages.delete': { keys: WAMessageKey[] } | { jid: string, all: true } + 'messages.update': WAMessageUpdate[] + /** + * add/update the given messages. If they were received while the connection was online, + * the update will have type: "notify" + * */ + 'messages.upsert': { messages: WAMessage[], type: MessageUpdateType } + + 'message-receipt.update': MessageUserReceiptUpdate[] + + 'groups.upsert': GroupMetadata[] + 'groups.update': Partial[] + /** apply an action to participants in a group */ + 'group-participants.update': { id: string, participants: string[], action: ParticipantAction } + + 'blocklist.set': { blocklist: string[] } + 'blocklist.update': { blocklist: string[], type: 'add' | 'remove' } +} + +export interface CommonBaileysEventEmitter extends EventEmitter { + on>(event: T, listener: (arg: BaileysEventMap[T]) => void): this + off>(event: T, listener: (arg: BaileysEventMap[T]) => void): this + removeAllListeners>(event: T): this + emit>(event: T, arg: BaileysEventMap[T]): boolean +} + +export type BaileysEventEmitter = CommonBaileysEventEmitter \ No newline at end of file diff --git a/src/Types/GroupMetadata.ts b/src/Types/GroupMetadata.ts new file mode 100644 index 0000000..c4c0ff7 --- /dev/null +++ b/src/Types/GroupMetadata.ts @@ -0,0 +1,34 @@ +import { Contact } from './Contact' + +export type GroupParticipant = (Contact & { isAdmin?: boolean; isSuperAdmin?: boolean, admin?: 'admin' | 'superadmin' | null }) + +export type ParticipantAction = 'add' | 'remove' | 'promote' | 'demote' + +export interface GroupMetadata { + id: string + owner: string | undefined + subject: string + creation: number + desc?: string + descOwner?: string + descId?: string + /** is set when the group only allows admins to change group settings */ + restrict?: boolean + /** is set when the group only allows admins to write messages */ + announce?: boolean + // Baileys modified array + participants: GroupParticipant[] + ephemeralDuration?: number +} + + +export interface WAGroupCreateResponse { + status: number + gid?: string + participants?: [{ [key: string]: any }] +} + +export interface GroupModificationResponse { + status: number + participants?: { [key: string]: any } +} \ No newline at end of file diff --git a/src/Types/Legacy.ts b/src/Types/Legacy.ts new file mode 100644 index 0000000..ad86ca7 --- /dev/null +++ b/src/Types/Legacy.ts @@ -0,0 +1,81 @@ +import { BinaryNode } from '../WABinary' +import { CommonBaileysEventEmitter } from './Events' +import { CommonSocketConfig } from './Socket' + +export interface LegacyAuthenticationCreds { + clientID: string + serverToken: string + clientToken: string + encKey: Buffer + macKey: Buffer +} + +/** used for binary messages */ +export enum WAMetric { + debugLog = 1, + queryResume = 2, + liveLocation = 3, + queryMedia = 4, + queryChat = 5, + queryContact = 6, + queryMessages = 7, + presence = 8, + presenceSubscribe = 9, + group = 10, + read = 11, + chat = 12, + received = 13, + picture = 14, + status = 15, + message = 16, + queryActions = 17, + block = 18, + queryGroup = 19, + queryPreview = 20, + queryEmoji = 21, + queryRead = 22, + queryVCard = 29, + queryStatus = 30, + queryStatusUpdate = 31, + queryLiveLocation = 33, + queryLabel = 36, + queryQuickReply = 39 +} + +/** used for binary messages */ +export enum WAFlag { + available = 160, + other = 136, // don't know this one + ignore = 1 << 7, + acknowledge = 1 << 6, + unavailable = 1 << 4, + expires = 1 << 3, + composing = 1 << 2, + recording = 1 << 2, + paused = 1 << 2 +} + +/** Tag used with binary queries */ +export type WATag = [WAMetric, WAFlag] + +export type SocketSendMessageOptions = { + json: BinaryNode | any[] + binaryTag?: WATag + tag?: string + longTag?: boolean +} + +export type SocketQueryOptions = SocketSendMessageOptions & { + timeoutMs?: number + expect200?: boolean + requiresPhoneConnection?: boolean +} + +export type LegacySocketConfig = CommonSocketConfig & { + /** max time for the phone to respond to a connectivity test */ + phoneResponseTimeMs: number + /** max time for WA server to respond before error with 422 */ + expectResponseTimeout: number +} + +export type LegacyBaileysEventEmitter = CommonBaileysEventEmitter \ No newline at end of file diff --git a/src/Types/Message.ts b/src/Types/Message.ts new file mode 100644 index 0000000..5cbf760 --- /dev/null +++ b/src/Types/Message.ts @@ -0,0 +1,175 @@ +import type NodeCache from 'node-cache' +import type { Logger } from 'pino' +import type { Readable } from 'stream' +import type { URL } from 'url' +import { proto } from '../../WAProto' +import type { GroupMetadata } from './GroupMetadata' + +// export the WAMessage Prototypes +export { proto as WAProto } +export type WAMessage = proto.IWebMessageInfo +export type WAMessageContent = proto.IMessage +export type WAContactMessage = proto.IContactMessage +export type WAContactsArrayMessage = proto.IContactsArrayMessage +export type WAMessageKey = proto.IMessageKey +export type WATextMessage = proto.IExtendedTextMessage +export type WAContextInfo = proto.IContextInfo +export type WALocationMessage = proto.ILocationMessage +export type WAGenericMediaMessage = proto.IVideoMessage | proto.IImageMessage | proto.IAudioMessage | proto.IDocumentMessage | proto.IStickerMessage +export import WAMessageStubType = proto.WebMessageInfo.WebMessageInfoStubType +export import WAMessageStatus = proto.WebMessageInfo.WebMessageInfoStatus +export type WAMediaUpload = Buffer | { url: URL | string } | { stream: Readable } +/** Set of message types that are supported by the library */ +export type MessageType = keyof proto.Message + +export type DownloadableMessage = { mediaKey?: Uint8Array, directPath?: string, url?: string } + +export type MediaConnInfo = { + auth: string + ttl: number + hosts: { hostname: string, maxContentLengthBytes: number }[] + fetchDate: Date +} + +export interface WAUrlInfo { + 'canonical-url': string + 'matched-text': string + title: string + description: string + jpegThumbnail?: Buffer +} + +// types to generate WA messages +type Mentionable = { + /** list of jids that are mentioned in the accompanying text */ + mentions?: string[] +} +type ViewOnce = { + viewOnce?: boolean +} +type Buttonable = { + /** add buttons to the message */ + buttons?: proto.IButton[] +} +type Templatable = { + /** add buttons to the message (conflicts with normal buttons)*/ + templateButtons?: proto.IHydratedTemplateButton[] + + footer?: string +} +type Listable = { + /** Sections of the List */ + sections?: proto.ISection[] + + /** Title of a List Message only */ + title?: string + + /** Text of the bnutton on the list (required) */ + buttonText?: string +} +type WithDimensions = { + width?: number + height?: number +} +export type MediaType = 'image' | 'video' | 'sticker' | 'audio' | 'document' | 'history' | 'md-app-state' +export type AnyMediaMessageContent = ( + ({ + image: WAMediaUpload + caption?: string + jpegThumbnail?: string + } & Mentionable & Buttonable & Templatable & WithDimensions) | + ({ + video: WAMediaUpload + caption?: string + gifPlayback?: boolean + jpegThumbnail?: string + } & Mentionable & Buttonable & Templatable & WithDimensions) | { + audio: WAMediaUpload + /** if set to true, will send as a `voice note` */ + ptt?: boolean + /** optionally tell the duration of the audio */ + seconds?: number + } | ({ + sticker: WAMediaUpload + } & WithDimensions) | ({ + document: WAMediaUpload + mimetype: string + fileName?: string + } & Buttonable & Templatable)) & + { mimetype?: string } + +export type AnyRegularMessageContent = ( + ({ + text: string + } + & Mentionable & Buttonable & Templatable & Listable) | + AnyMediaMessageContent | + { + contacts: { + displayName?: string + contacts: proto.IContactMessage[] + } + } | + { + location: WALocationMessage + } +) & ViewOnce + +export type AnyMessageContent = AnyRegularMessageContent | { + forward: WAMessage + force?: boolean +} | { + delete: WAMessageKey +} | { + disappearingMessagesInChat: boolean | number +} + +export type MessageRelayOptions = { + messageId?: string + /** only send to a specific participant */ + participant?: string + additionalAttributes?: { [_: string]: string } + cachedGroupMetadata?: (jid: string) => Promise + //cachedDevices?: (jid: string) => Promise +} + +export type MiscMessageGenerationOptions = { + /** Force message id */ + messageId?: string + /** optional, if you want to manually set the timestamp of the message */ + timestamp?: Date + /** the message you want to quote */ + quoted?: WAMessage + /** disappearing messages settings */ + ephemeralExpiration?: number | string + + mediaUploadTimeoutMs?: number +} +export type MessageGenerationOptionsFromContent = MiscMessageGenerationOptions & { + userJid: string +} + +export type WAMediaUploadFunction = (readStream: Readable, opts: { fileEncSha256B64: string, mediaType: MediaType, timeoutMs?: number }) => Promise<{ mediaUrl: string, directPath: string }> + +export type MediaGenerationOptions = { + logger?: Logger + upload: WAMediaUploadFunction + /** cache media so it does not have to be uploaded again */ + mediaCache?: NodeCache + + mediaUploadTimeoutMs?: number +} +export type MessageContentGenerationOptions = MediaGenerationOptions & { + getUrlInfo?: (text: string) => Promise +} +export type MessageGenerationOptions = MessageContentGenerationOptions & MessageGenerationOptionsFromContent + +export type MessageUpdateType = 'append' | 'notify' | 'replace' + +export type MessageUserReceipt = proto.IUserReceipt + +export type WAMessageUpdate = { update: Partial, key: proto.IMessageKey } + +export type WAMessageCursor = { before: WAMessageKey | undefined } | { after: WAMessageKey | undefined } + +export type MessageUserReceiptUpdate = { key: proto.IMessageKey, receipt: MessageUserReceipt } diff --git a/src/Types/Socket.ts b/src/Types/Socket.ts new file mode 100644 index 0000000..aee0639 --- /dev/null +++ b/src/Types/Socket.ts @@ -0,0 +1,40 @@ + +import type { Agent } from 'https' +import type NodeCache from 'node-cache' +import type { Logger } from 'pino' +import type { URL } from 'url' +import { MediaConnInfo } from './Message' + +export type WAVersion = [number, number, number] +export type WABrowserDescription = [string, string, string] + +export type CommonSocketConfig = { + /** provide an auth state object to maintain the auth state */ + auth?: T + /** the WS url to connect to WA */ + waWebSocketUrl: string | URL + /** Fails the connection if the socket times out in this interval */ + connectTimeoutMs: number + /** Default timeout for queries, undefined for no timeout */ + defaultQueryTimeoutMs: number | undefined + /** ping-pong interval for WS connection */ + keepAliveIntervalMs: number + /** proxy agent */ + agent?: Agent + /** pino logger */ + logger: Logger + /** version to connect with */ + version: WAVersion + /** override browser config */ + browser: WABrowserDescription + /** agent used for fetch requests -- uploading/downloading media */ + fetchAgent?: Agent + /** should the QR be printed in the terminal */ + printQRInTerminal: boolean + /** should events be emitted for actions done by this socket connection */ + emitOwnEvents: boolean + /** provide a cache to store media, so does not have to be re-uploaded */ + mediaCache?: NodeCache + + customUploadHosts: MediaConnInfo['hosts'] +} diff --git a/src/Types/State.ts b/src/Types/State.ts new file mode 100644 index 0000000..5774c50 --- /dev/null +++ b/src/Types/State.ts @@ -0,0 +1,25 @@ +import { Contact } from './Contact' + +export type WAConnectionState = 'open' | 'connecting' | 'close' + +export type ConnectionState = { + /** connection is now open, connecting or closed */ + connection: WAConnectionState + /** the error that caused the connection to close */ + lastDisconnect?: { + error: Error + date: Date + } + /** is this a new login */ + isNewLogin?: boolean + /** the current QR code */ + qr?: string + /** has the device received all pending notifications while it was offline */ + receivedPendingNotifications?: boolean + /** legacy connection options */ + legacy?: { + phoneConnected: boolean + user?: Contact + } + +} \ No newline at end of file diff --git a/src/Types/index.ts b/src/Types/index.ts new file mode 100644 index 0000000..c0c0a11 --- /dev/null +++ b/src/Types/index.ts @@ -0,0 +1,69 @@ +export * from './Auth' +export * from './GroupMetadata' +export * from './Chat' +export * from './Contact' +export * from './State' +export * from './Message' +export * from './Legacy' +export * from './Socket' +export * from './Events' + +import type NodeCache from 'node-cache' +import { proto } from '../../WAProto' +import { AuthenticationState } from './Auth' +import { CommonSocketConfig } from './Socket' + +export type SocketConfig = CommonSocketConfig & { + /** provide a cache to store a user's device list */ + userDevicesCache?: NodeCache + /** map to store the retry counts for failed messages */ + msgRetryCounterMap?: { [msgId: string]: number } + /** + * fetch a message from your store + * implement this so that messages failed to send (solves the "this message can take a while" issue) can be retried + * */ + getMessage: (key: proto.IMessageKey) => Promise +} + +export enum DisconnectReason { + connectionClosed = 428, + connectionLost = 408, + connectionReplaced = 440, + timedOut = 408, + loggedOut = 401, + badSession = 500, + restartRequired = 410, + multideviceMismatch = 411 +} + +export type WAInitResponse = { + ref: string + ttl: number + status: 200 +} + +export type WABusinessHoursConfig = { + day_of_week: string + mode: string + open_time?: number + close_time?: number +} + +export type WABusinessProfile = { + description: string + email: string + business_hours: { + timezone?: string + config?: WABusinessHoursConfig[] + business_config?: WABusinessHoursConfig[] + } + website: string[] + categories: { + id: string + localized_display_name: string + }[] + wid?: string +} + + +export type CurveKeyPair = { private: Uint8Array; public: Uint8Array } \ No newline at end of file diff --git a/src/Utils/auth-utils.ts b/src/Utils/auth-utils.ts new file mode 100644 index 0000000..9013859 --- /dev/null +++ b/src/Utils/auth-utils.ts @@ -0,0 +1,178 @@ +import { Boom } from '@hapi/boom' +import { randomBytes } from 'crypto' +import type { Logger } from 'pino' +import { proto } from '../../WAProto' +import type { AuthenticationCreds, AuthenticationState, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction } from '../Types' +import { Curve, signedKeyPair } from './crypto' +import { BufferJSON, generateRegistrationId } from './generics' + +const KEY_MAP: { [T in keyof SignalDataTypeMap]: string } = { + 'pre-key': 'preKeys', + 'session': 'sessions', + 'sender-key': 'senderKeys', + 'app-state-sync-key': 'appStateSyncKeys', + 'app-state-sync-version': 'appStateVersions', + 'sender-key-memory': 'senderKeyMemory' +} + +export const addTransactionCapability = (state: SignalKeyStore, logger: Logger): SignalKeyStoreWithTransaction => { + let inTransaction = false + let transactionCache: SignalDataSet = { } + let mutations: SignalDataSet = { } + + const prefetch = async(type: keyof SignalDataTypeMap, ids: string[]) => { + if(!inTransaction) { + throw new Boom('Cannot prefetch without transaction') + } + + const dict = transactionCache[type] + const idsRequiringFetch = dict ? ids.filter(item => !(item in dict)) : ids + // only fetch if there are any items to fetch + if(idsRequiringFetch.length) { + const result = await state.get(type, idsRequiringFetch) + + transactionCache[type] = transactionCache[type] || { } + Object.assign(transactionCache[type], result) + } + } + + return { + get: async(type, ids) => { + if(inTransaction) { + await prefetch(type, ids) + return ids.reduce( + (dict, id) => { + const value = transactionCache[type]?.[id] + if(value) { + dict[id] = value + } + + return dict + }, { } + ) + } else { + return state.get(type, ids) + } + }, + set: data => { + if(inTransaction) { + logger.trace({ types: Object.keys(data) }, 'caching in transaction') + for(const key in data) { + transactionCache[key] = transactionCache[key] || { } + Object.assign(transactionCache[key], data[key]) + + mutations[key] = mutations[key] || { } + Object.assign(mutations[key], data[key]) + } + } else { + return state.set(data) + } + }, + isInTransaction: () => inTransaction, + prefetch: (type, ids) => { + logger.trace({ type, ids }, 'prefetching') + return prefetch(type, ids) + }, + transaction: async(work) => { + if(inTransaction) { + await work() + } else { + logger.debug('entering transaction') + inTransaction = true + try { + await work() + if(Object.keys(mutations).length) { + logger.debug('committing transaction') + await state.set(mutations) + } else { + logger.debug('no mutations in transaction') + } + } finally { + inTransaction = false + transactionCache = { } + mutations = { } + } + } + } + } +} + +export const initAuthCreds = (): AuthenticationCreds => { + const identityKey = Curve.generateKeyPair() + return { + noiseKey: Curve.generateKeyPair(), + signedIdentityKey: identityKey, + signedPreKey: signedKeyPair(identityKey, 1), + registrationId: generateRegistrationId(), + advSecretKey: randomBytes(32).toString('base64'), + + nextPreKeyId: 1, + firstUnuploadedPreKeyId: 1, + serverHasPreKeys: false + } +} + +/** stores the full authentication state in a single JSON file */ +export const useSingleFileAuthState = (filename: string, logger?: Logger): { state: AuthenticationState, saveState: () => void } => { + // require fs here so that in case "fs" is not available -- the app does not crash + const { readFileSync, writeFileSync, existsSync } = require('fs') + let creds: AuthenticationCreds + let keys: any = { } + + // save the authentication state to a file + const saveState = () => { + logger && logger.trace('saving auth state') + writeFileSync( + filename, + // BufferJSON replacer utility saves buffers nicely + JSON.stringify({ creds, keys }, BufferJSON.replacer, 2) + ) + } + + if(existsSync(filename)) { + const result = JSON.parse( + readFileSync(filename, { encoding: 'utf-8' }), + BufferJSON.reviver + ) + creds = result.creds + keys = result.keys + } else { + creds = initAuthCreds() + keys = { } + } + + return { + state: { + creds, + keys: { + get: (type, ids) => { + const key = KEY_MAP[type] + return ids.reduce( + (dict, id) => { + let value = keys[key]?.[id] + if(value) { + if(type === 'app-state-sync-key') { + value = proto.AppStateSyncKeyData.fromObject(value) + } + + dict[id] = value + } + + return dict + }, { } + ) + }, + set: (data) => { + for(const _key in data) { + const key = KEY_MAP[_key as keyof SignalDataTypeMap] + keys[key] = keys[key] || { } + Object.assign(keys[key], data[_key]) + } + + saveState() + } + } + }, + saveState + } +} \ No newline at end of file diff --git a/src/Utils/chat-utils.ts b/src/Utils/chat-utils.ts new file mode 100644 index 0000000..1ae606c --- /dev/null +++ b/src/Utils/chat-utils.ts @@ -0,0 +1,541 @@ +import { Boom } from '@hapi/boom' +import { proto } from '../../WAProto' +import { ChatModification, ChatMutation, LastMessageList, LTHashState, WAPatchCreate, WAPatchName } from '../Types' +import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren } from '../WABinary' +import { aesDecrypt, aesEncrypt, hkdf, hmacSign } from './crypto' +import { toNumber } from './generics' +import { LT_HASH_ANTI_TAMPERING } from './lt-hash' +import { downloadContentFromMessage, } from './messages-media' + +type FetchAppStateSyncKey = (keyId: string) => Promise | proto.IAppStateSyncKeyData + +const mutationKeys = (keydata: Uint8Array) => { + const expanded = hkdf(keydata, 160, { info: 'WhatsApp Mutation Keys' }) + return { + indexKey: expanded.slice(0, 32), + valueEncryptionKey: expanded.slice(32, 64), + valueMacKey: expanded.slice(64, 96), + snapshotMacKey: expanded.slice(96, 128), + patchMacKey: expanded.slice(128, 160) + } +} + +const generateMac = (operation: proto.SyncdMutation.SyncdMutationSyncdOperation, data: Buffer, keyId: Uint8Array | string, key: Buffer) => { + const getKeyData = () => { + let r: number + switch (operation) { + case proto.SyncdMutation.SyncdMutationSyncdOperation.SET: + r = 0x01 + break + case proto.SyncdMutation.SyncdMutationSyncdOperation.REMOVE: + r = 0x02 + break + } + + const buff = Buffer.from([r]) + return Buffer.concat([ buff, Buffer.from(keyId as any, 'base64') ]) + } + + const keyData = getKeyData() + + const last = Buffer.alloc(8) // 8 bytes + last.set([ keyData.length ], last.length-1) + + const total = Buffer.concat([ keyData, data, last ]) + const hmac = hmacSign(total, key, 'sha512') + + return hmac.slice(0, 32) +} + +const to64BitNetworkOrder = (e: number) => { + const t = new ArrayBuffer(8) + new DataView(t).setUint32(4, e, !1) + return Buffer.from(t) +} + +type Mac = { indexMac: Uint8Array, valueMac: Uint8Array, operation: proto.SyncdMutation.SyncdMutationSyncdOperation } + +const makeLtHashGenerator = ({ indexValueMap, hash }: Pick) => { + indexValueMap = { ...indexValueMap } + const addBuffs: ArrayBuffer[] = [] + const subBuffs: ArrayBuffer[] = [] + + return { + mix: ({ indexMac, valueMac, operation }: Mac) => { + const indexMacBase64 = Buffer.from(indexMac).toString('base64') + const prevOp = indexValueMap[indexMacBase64] + if(operation === proto.SyncdMutation.SyncdMutationSyncdOperation.REMOVE) { + if(!prevOp) { + throw new Boom('tried remove, but no previous op', { data: { indexMac, valueMac } }) + } + + // remove from index value mac, since this mutation is erased + delete indexValueMap[indexMacBase64] + } else { + addBuffs.push(new Uint8Array(valueMac).buffer) + // add this index into the history map + indexValueMap[indexMacBase64] = { valueMac } + } + + if(prevOp) { + subBuffs.push(new Uint8Array(prevOp.valueMac).buffer) + } + }, + finish: () => { + const result = LT_HASH_ANTI_TAMPERING.subtractThenAdd(new Uint8Array(hash).buffer, addBuffs, subBuffs) + const buffer = Buffer.from(result) + + return { + hash: buffer, + indexValueMap + } + } + } +} + +const generateSnapshotMac = (lthash: Uint8Array, version: number, name: WAPatchName, key: Buffer) => { + const total = Buffer.concat([ + lthash, + to64BitNetworkOrder(version), + Buffer.from(name, 'utf-8') + ]) + return hmacSign(total, key, 'sha256') +} + +const generatePatchMac = (snapshotMac: Uint8Array, valueMacs: Uint8Array[], version: number, type: WAPatchName, key: Buffer) => { + const total = Buffer.concat([ + snapshotMac, + ...valueMacs, + to64BitNetworkOrder(version), + Buffer.from(type, 'utf-8') + ]) + return hmacSign(total, key) +} + +export const newLTHashState = (): LTHashState => ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} }) + +export const encodeSyncdPatch = async( + { type, index, syncAction, apiVersion, operation }: WAPatchCreate, + myAppStateKeyId: string, + state: LTHashState, + getAppStateSyncKey: FetchAppStateSyncKey +) => { + const key = !!myAppStateKeyId ? await getAppStateSyncKey(myAppStateKeyId) : undefined + if(!key) { + throw new Boom(`myAppStateKey ("${myAppStateKeyId}") not present`, { statusCode: 404 }) + } + + const encKeyId = Buffer.from(myAppStateKeyId, 'base64') + + state = { ...state, indexValueMap: { ...state.indexValueMap } } + + const indexBuffer = Buffer.from(JSON.stringify(index)) + const dataProto = proto.SyncActionData.fromObject({ + index: indexBuffer, + value: syncAction, + padding: new Uint8Array(0), + version: apiVersion + }) + const encoded = proto.SyncActionData.encode(dataProto).finish() + + const keyValue = mutationKeys(key!.keyData!) + + const encValue = aesEncrypt(encoded, keyValue.valueEncryptionKey) + const valueMac = generateMac(operation, encValue, encKeyId, keyValue.valueMacKey) + const indexMac = hmacSign(indexBuffer, keyValue.indexKey) + + // update LT hash + const generator = makeLtHashGenerator(state) + generator.mix({ indexMac, valueMac, operation }) + Object.assign(state, generator.finish()) + + state.version += 1 + + const snapshotMac = generateSnapshotMac(state.hash, state.version, type, keyValue.snapshotMacKey) + + const patch: proto.ISyncdPatch = { + patchMac: generatePatchMac(snapshotMac, [valueMac], state.version, type, keyValue.patchMacKey), + snapshotMac: snapshotMac, + keyId: { id: encKeyId }, + mutations: [ + { + operation: operation, + record: { + index: { + blob: indexMac + }, + value: { + blob: Buffer.concat([ encValue, valueMac ]) + }, + keyId: { id: encKeyId } + } + } + ] + } + + const base64Index = indexMac.toString('base64') + state.indexValueMap[base64Index] = { valueMac } + + return { patch, state } +} + +export const decodeSyncdMutations = async( + msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[], + initialState: LTHashState, + getAppStateSyncKey: FetchAppStateSyncKey, + validateMacs: boolean +) => { + const keyCache: { [_: string]: ReturnType } = { } + const getKey = async(keyId: Uint8Array) => { + const base64Key = Buffer.from(keyId!).toString('base64') + let key = keyCache[base64Key] + if(!key) { + const keyEnc = await getAppStateSyncKey(base64Key) + if(!keyEnc) { + throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 404, data: { msgMutations } }) + } + + const result = mutationKeys(keyEnc.keyData!) + keyCache[base64Key] = result + key = result + } + + return key + } + + const ltGenerator = makeLtHashGenerator(initialState) + + const mutations: ChatMutation[] = [] + // indexKey used to HMAC sign record.index.blob + // valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32] + // the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId + for(const msgMutation of msgMutations!) { + // if it's a syncdmutation, get the operation property + // otherwise, if it's only a record -- it'll be a SET mutation + const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdMutationSyncdOperation.SET + const record = ('record' in msgMutation && !!msgMutation.record) ? msgMutation.record : msgMutation as proto.ISyncdRecord + + const key = await getKey(record.keyId!.id!) + const content = Buffer.from(record.value!.blob!) + const encContent = content.slice(0, -32) + const ogValueMac = content.slice(-32) + if(validateMacs) { + const contentHmac = generateMac(operation, encContent, record.keyId!.id!, key.valueMacKey) + if(Buffer.compare(contentHmac, ogValueMac) !== 0) { + throw new Boom('HMAC content verification failed') + } + } + + const result = aesDecrypt(encContent, key.valueEncryptionKey) + const syncAction = proto.SyncActionData.decode(result) + + if(validateMacs) { + const hmac = hmacSign(syncAction.index, key.indexKey) + if(Buffer.compare(hmac, record.index!.blob) !== 0) { + throw new Boom('HMAC index verification failed') + } + } + + const indexStr = Buffer.from(syncAction.index).toString() + mutations.push({ + syncAction, + index: JSON.parse(indexStr), + }) + ltGenerator.mix({ + indexMac: record.index!.blob!, + valueMac: ogValueMac, + operation: operation + }) + } + + return { mutations, ...ltGenerator.finish() } +} + +export const decodeSyncdPatch = async( + msg: proto.ISyncdPatch, + name: WAPatchName, + initialState: LTHashState, + getAppStateSyncKey: FetchAppStateSyncKey, + validateMacs: boolean +) => { + if(validateMacs) { + const base64Key = Buffer.from(msg.keyId!.id).toString('base64') + const mainKeyObj = await getAppStateSyncKey(base64Key) + const mainKey = mutationKeys(mainKeyObj.keyData!) + const mutationmacs = msg.mutations!.map(mutation => mutation.record!.value!.blob!.slice(-32)) + + const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs, toNumber(msg.version!.version), name, mainKey.patchMacKey) + if(Buffer.compare(patchMac, msg.patchMac) !== 0) { + throw new Boom('Invalid patch mac') + } + } + + const result = await decodeSyncdMutations(msg!.mutations!, initialState, getAppStateSyncKey, validateMacs) + return result +} + +export const extractSyncdPatches = async(result: BinaryNode) => { + const syncNode = getBinaryNodeChild(result, 'sync') + const collectionNodes = getBinaryNodeChildren(syncNode, 'collection') + + const final = { } as { [T in WAPatchName]: { patches: proto.ISyncdPatch[], hasMorePatches: boolean, snapshot?: proto.ISyncdSnapshot } } + await Promise.all( + collectionNodes.map( + async collectionNode => { + const patchesNode = getBinaryNodeChild(collectionNode, 'patches') + + const patches = getBinaryNodeChildren(patchesNode || collectionNode, 'patch') + const snapshotNode = getBinaryNodeChild(collectionNode, 'snapshot') + + const syncds: proto.ISyncdPatch[] = [] + const name = collectionNode.attrs.name as WAPatchName + + const hasMorePatches = collectionNode.attrs.has_more_patches === 'true' + + let snapshot: proto.ISyncdSnapshot | undefined = undefined + if(snapshotNode && !!snapshotNode.content) { + if(!Buffer.isBuffer(snapshotNode)) { + snapshotNode.content = Buffer.from(Object.values(snapshotNode.content)) + } + + const blobRef = proto.ExternalBlobReference.decode( + snapshotNode.content! as Buffer + ) + const data = await downloadExternalBlob(blobRef) + snapshot = proto.SyncdSnapshot.decode(data) + } + + for(let { content } of patches) { + if(content) { + if(!Buffer.isBuffer(content)) { + content = Buffer.from(Object.values(content)) + } + + const syncd = proto.SyncdPatch.decode(content! as Uint8Array) + if(!syncd.version) { + syncd.version = { version: +collectionNode.attrs.version+1 } + } + + syncds.push(syncd) + } + } + + final[name] = { patches: syncds, hasMorePatches, snapshot } + } + ) + ) + + return final +} + + +export const downloadExternalBlob = async(blob: proto.IExternalBlobReference) => { + const stream = await downloadContentFromMessage(blob, 'md-app-state') + let buffer = Buffer.from([]) + for await (const chunk of stream) { + buffer = Buffer.concat([buffer, chunk]) + } + + return buffer +} + +export const downloadExternalPatch = async(blob: proto.IExternalBlobReference) => { + const buffer = await downloadExternalBlob(blob) + const syncData = proto.SyncdMutations.decode(buffer) + return syncData +} + +export const decodeSyncdSnapshot = async( + name: WAPatchName, + snapshot: proto.ISyncdSnapshot, + getAppStateSyncKey: FetchAppStateSyncKey, + minimumVersionNumber: number | undefined, + validateMacs: boolean = true +) => { + const newState = newLTHashState() + newState.version = toNumber(snapshot.version!.version!) + + const { hash, indexValueMap, mutations } = await decodeSyncdMutations(snapshot.records!, newState, getAppStateSyncKey, validateMacs) + newState.hash = hash + newState.indexValueMap = indexValueMap + + if(validateMacs) { + const base64Key = Buffer.from(snapshot.keyId!.id!).toString('base64') + const keyEnc = await getAppStateSyncKey(base64Key) + if(!keyEnc) { + throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 500 }) + } + + const result = mutationKeys(keyEnc.keyData!) + const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey) + if(Buffer.compare(snapshot.mac!, computedSnapshotMac) !== 0) { + throw new Boom(`failed to verify LTHash at ${newState.version} of ${name} from snapshot`, { statusCode: 500 }) + } + } + + const areMutationsRequired = typeof minimumVersionNumber === 'undefined' || newState.version > minimumVersionNumber + if(!areMutationsRequired) { + // clear array + mutations.splice(0, mutations.length) + } + + return { + state: newState, + mutations + } +} + +export const decodePatches = async( + name: WAPatchName, + syncds: proto.ISyncdPatch[], + initial: LTHashState, + getAppStateSyncKey: FetchAppStateSyncKey, + minimumVersionNumber?: number, + validateMacs: boolean = true +) => { + const successfulMutations: ChatMutation[] = [] + + const newState: LTHashState = { + ...initial, + indexValueMap: { ...initial.indexValueMap } + } + + for(const syncd of syncds) { + const { version, keyId, snapshotMac } = syncd + if(syncd.externalMutations) { + const ref = await downloadExternalPatch(syncd.externalMutations) + syncd.mutations.push(...ref.mutations) + } + + const patchVersion = toNumber(version.version!) + + newState.version = patchVersion + + const decodeResult = await decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, validateMacs) + + newState.hash = decodeResult.hash + newState.indexValueMap = decodeResult.indexValueMap + if(typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber) { + successfulMutations.push(...decodeResult.mutations) + } + + if(validateMacs) { + const base64Key = Buffer.from(keyId!.id!).toString('base64') + const keyEnc = await getAppStateSyncKey(base64Key) + if(!keyEnc) { + throw new Boom(`failed to find key "${base64Key}" to decode mutation`) + } + + const result = mutationKeys(keyEnc.keyData!) + const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey) + if(Buffer.compare(snapshotMac, computedSnapshotMac) !== 0) { + throw new Boom(`failed to verify LTHash at ${newState.version} of ${name}`) + } + } + } + + return { + newMutations: successfulMutations, + state: newState + } +} + +export const chatModificationToAppPatch = ( + mod: ChatModification, + jid: string +) => { + const OP = proto.SyncdMutation.SyncdMutationSyncdOperation + const getMessageRange = (lastMessages: LastMessageList) => { + if(!lastMessages?.length) { + throw new Boom('Expected last message to be not from me', { statusCode: 400 }) + } + + const lastMsg = lastMessages[lastMessages.length-1] + if(lastMsg.key.fromMe) { + throw new Boom('Expected last message in array to be not from me', { statusCode: 400 }) + } + + const messageRange: proto.ISyncActionMessageRange = { + lastMessageTimestamp: lastMsg?.messageTimestamp, + messages: lastMessages + } + return messageRange + } + + let patch: WAPatchCreate + if('mute' in mod) { + patch = { + syncAction: { + muteAction: { + muted: !!mod.mute, + muteEndTimestamp: mod.mute || undefined + } + }, + index: ['mute', jid], + type: 'regular_high', + apiVersion: 2, + operation: OP.SET + } + } else if('archive' in mod) { + patch = { + syncAction: { + archiveChatAction: { + archived: !!mod.archive, + messageRange: getMessageRange(mod.lastMessages) + } + }, + index: ['archive', jid], + type: 'regular_low', + apiVersion: 3, + operation: OP.SET + } + } else if('markRead' in mod) { + patch = { + syncAction: { + markChatAsReadAction: { + read: mod.markRead, + messageRange: getMessageRange(mod.lastMessages) + } + }, + index: ['markChatAsRead', jid], + type: 'regular_low', + apiVersion: 3, + operation: OP.SET + } + } else if('clear' in mod) { + if(mod.clear === 'all') { + throw new Boom('not supported') + } else { + const key = mod.clear.messages[0] + patch = { + syncAction: { + deleteMessageForMeAction: { + deleteMedia: false + } + }, + index: ['deleteMessageForMe', jid, key.id, key.fromMe ? '1' : '0', '0'], + type: 'regular_high', + apiVersion: 3, + operation: OP.SET + } + } + } else if('pin' in mod) { + patch = { + syncAction: { + pinAction: { + pinned: !!mod.pin + } + }, + index: ['pin_v1', jid], + type: 'regular_low', + apiVersion: 5, + operation: OP.SET + } + } else { + throw new Boom('not supported') + } + + patch.syncAction.timestamp = Date.now() + + return patch +} \ No newline at end of file diff --git a/src/Utils/crypto.ts b/src/Utils/crypto.ts new file mode 100644 index 0000000..81b4e74 --- /dev/null +++ b/src/Utils/crypto.ts @@ -0,0 +1,99 @@ +import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from 'crypto' +import * as curveJs from 'curve25519-js' +import { KeyPair } from '../Types' + +export const Curve = { + generateKeyPair: (): KeyPair => { + const { public: pubKey, private: privKey } = curveJs.generateKeyPair(randomBytes(32)) + return { + private: Buffer.from(privKey), + public: Buffer.from(pubKey) + } + }, + sharedKey: (privateKey: Uint8Array, publicKey: Uint8Array) => { + const shared = curveJs.sharedKey(privateKey, publicKey) + return Buffer.from(shared) + }, + sign: (privateKey: Uint8Array, buf: Uint8Array) => ( + Buffer.from(curveJs.sign(privateKey, buf, null)) + ), + verify: (pubKey: Uint8Array, message: Uint8Array, signature: Uint8Array) => { + return curveJs.verify(pubKey, message, signature) + } +} + +export const signedKeyPair = (keyPair: KeyPair, keyId: number) => { + const signKeys = Curve.generateKeyPair() + const pubKey = new Uint8Array(33) + pubKey.set([5], 0) + pubKey.set(signKeys.public, 1) + + const signature = Curve.sign(keyPair.private, pubKey) + + return { keyPair: signKeys, signature, keyId } +} + +/** decrypt AES 256 CBC; where the IV is prefixed to the buffer */ +export function aesDecrypt(buffer: Buffer, key: Buffer) { + return aesDecryptWithIV(buffer.slice(16, buffer.length), key, buffer.slice(0, 16)) +} + +/** decrypt AES 256 CBC */ +export function aesDecryptWithIV(buffer: Buffer, key: Buffer, IV: Buffer) { + const aes = 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 +export function aesEncrypt(buffer: Buffer | Uint8Array, key: Buffer) { + const IV = randomBytes(16) + const aes = 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 +export function aesEncrypWithIV(buffer: Buffer, key: Buffer, IV: Buffer) { + const aes = createCipheriv('aes-256-cbc', key, IV) + return Buffer.concat([aes.update(buffer), aes.final()]) // prefix IV to the buffer +} + +// sign HMAC using SHA 256 +export function hmacSign(buffer: Buffer | Uint8Array, key: Buffer | Uint8Array, variant: 'sha256' | 'sha512' = 'sha256') { + return createHmac(variant, key).update(buffer).digest() +} + +export function sha256(buffer: Buffer) { + return createHash('sha256').update(buffer).digest() +} + +// HKDF key expansion +// from: https://github.com/benadida/node-hkdf +export function hkdf(buffer: Uint8Array, expandedLength: number, { info, salt }: { salt?: Buffer, info?: string }) { + const hashAlg = 'sha256' + const hashLength = 32 + salt = salt || Buffer.alloc(hashLength) + // now we compute the PRK + const prk = createHmac(hashAlg, salt).update(buffer).digest() + + let prev = Buffer.from([]) + const buffers = [] + const num_blocks = Math.ceil(expandedLength / hashLength) + + const infoBuff = Buffer.from(info || []) + + for(var i=0; i { + //const deviceIdentity = (stanza.content as BinaryNodeM[])?.find(m => m.tag === 'device-identity') + //const deviceIdentityBytes = deviceIdentity ? deviceIdentity.content as Buffer : undefined + + let msgType: MessageType + let chatId: string + let author: string + + const msgId: string = stanza.attrs.id + const from: string = stanza.attrs.from + const participant: string | undefined = stanza.attrs.participant + const recipient: string | undefined = stanza.attrs.recipient + + const isMe = (jid: string) => areJidsSameUser(jid, auth.creds.me!.id) + + if(isJidUser(from)) { + if(recipient) { + if(!isMe(from)) { + throw new Boom('') + } + + chatId = recipient + } else { + chatId = from + } + + msgType = 'chat' + author = from + } else if(isJidGroup(from)) { + if(!participant) { + throw new Boom('No participant in group message') + } + + msgType = 'group' + author = participant + chatId = from + } else if(isJidBroadcast(from)) { + if(!participant) { + throw new Boom('No participant in group message') + } + + const isParticipantMe = isMe(participant) + if(isJidStatusBroadcast(from)) { + msgType = isParticipantMe ? 'direct_peer_status' : 'other_status' + } else { + msgType = isParticipantMe ? 'peer_broadcast' : 'other_broadcast' + } + + chatId = from + author = participant + } + + const sender = msgType === 'chat' ? author : chatId + + const fromMe = isMe(stanza.attrs.participant || stanza.attrs.from) + const pushname = stanza.attrs.notify + + const key: WAMessageKey = { + remoteJid: chatId, + fromMe, + id: msgId, + participant + } + + const fullMessage: proto.IWebMessageInfo = { + key, + messageTimestamp: +stanza.attrs.t, + pushName: pushname + } + + if(key.fromMe) { + fullMessage.status = proto.WebMessageInfo.WebMessageInfoStatus.SERVER_ACK + } + + if(Array.isArray(stanza.content)) { + for(const { tag, attrs, content } of stanza.content) { + if(tag !== 'enc') { + continue + } + + if(!(content instanceof Uint8Array)) { + continue + } + + let msgBuffer: Buffer + + try { + const e2eType = attrs.type + switch (e2eType) { + case 'skmsg': + msgBuffer = await decryptGroupSignalProto(sender, author, content, auth) + break + case 'pkmsg': + case 'msg': + const user = isJidUser(sender) ? sender : author + msgBuffer = await decryptSignalProto(user, e2eType, content as Buffer, auth) + break + } + + let msg: proto.IMessage = proto.Message.decode(unpadRandomMax16(msgBuffer)) + msg = msg.deviceSentMessage?.message || msg + if(msg.senderKeyDistributionMessage) { + await processSenderKeyMessage(author, msg.senderKeyDistributionMessage, auth) + } + + if(fullMessage.message) { + Object.assign(fullMessage.message, msg) + } else { + fullMessage.message = msg + } + } catch(error) { + fullMessage.messageStubType = proto.WebMessageInfo.WebMessageInfoStubType.CIPHERTEXT + fullMessage.messageStubParameters = [error.message] + } + } + } + + return fullMessage +} \ No newline at end of file diff --git a/src/Utils/generics.ts b/src/Utils/generics.ts new file mode 100644 index 0000000..566c377 --- /dev/null +++ b/src/Utils/generics.ts @@ -0,0 +1,234 @@ +import { Boom } from '@hapi/boom' +import { randomBytes } from 'crypto' +import { platform, release } from 'os' +import { Logger } from 'pino' +import { proto } from '../../WAProto' +import { CommonBaileysEventEmitter, DisconnectReason } from '../Types' +import { Binary } from '../WABinary' +import { ConnectionState } from '..' + +const PLATFORM_MAP = { + 'aix': 'AIX', + 'darwin': 'Mac OS', + 'win32': 'Windows', + 'android': 'Android' +} + +export const Browsers = { + ubuntu: browser => ['Ubuntu', browser, '18.04'] as [string, string, string], + macOS: browser => ['Mac OS', browser, '10.15.3'] as [string, string, string], + baileys: browser => ['Baileys', browser, '4.0.0'] as [string, string, string], + /** The appropriate browser based on your OS & release */ + appropriate: browser => [ PLATFORM_MAP[platform()] || 'Ubuntu', browser, release() ] as [string, string, string] +} + +export const BufferJSON = { + replacer: (k, value: any) => { + if(Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') { + return { type: 'Buffer', data: Buffer.from(value?.data || value).toString('base64') } + } + + return value + }, + reviver: (_, value: any) => { + if(typeof value === 'object' && !!value && (value.buffer === true || value.type === 'Buffer')) { + const val = value.data || value.value + return typeof val === 'string' ? Buffer.from(val, 'base64') : Buffer.from(val) + } + + return value + } +} + + +export const writeRandomPadMax16 = (e: Binary) => { + function r(e: Binary, t: number) { + for(var r = 0; r < t; r++) { + e.writeUint8(t) + } + } + + var t = randomBytes(1) + r(e, 1 + (15 & t[0])) + return e +} + +export const unpadRandomMax16 = (e: Uint8Array | Buffer) => { + const t = new Uint8Array(e) + if(0 === t.length) { + throw new Error('unpadPkcs7 given empty bytes') + } + + var r = t[t.length - 1] + if(r > t.length) { + throw new Error(`unpad given ${t.length} bytes, but pad is ${r}`) + } + + return new Uint8Array(t.buffer, t.byteOffset, t.length - r) +} + +export const encodeWAMessage = (message: proto.IMessage) => ( + Buffer.from( + writeRandomPadMax16( + new Binary(proto.Message.encode(message).finish()) + ).readByteArray() + ) +) + +export const generateRegistrationId = () => ( + Uint16Array.from(randomBytes(2))[0] & 0x3fff +) + +export const encodeInt = (e: number, t: number) => { + for(var r = t, a = new Uint8Array(e), i = e - 1; i >= 0; i--) { + a[i] = 255 & r + r >>>= 8 + } + + return a +} + +export const encodeBigEndian = (e: number, t=4) => { + let r = e + const a = new Uint8Array(t) + for(let i = t - 1; i >= 0; i--) { + a[i] = 255 & r + r >>>= 8 + } + + return a +} + +export const toNumber = (t: Long | number) => ((typeof t === 'object' && 'toNumber' in t) ? t.toNumber() : t) + +export function shallowChanges (old: T, current: T, { lookForDeletedKeys }: {lookForDeletedKeys: boolean}): Partial { + const changes: Partial = {} + for(const key in current) { + if(old[key] !== current[key]) { + changes[key] = current[key] || null + } + } + + if(lookForDeletedKeys) { + for(const key in old) { + if(!changes[key] && old[key] !== current[key]) { + changes[key] = current[key] || null + } + } + } + + return changes +} + +/** unix timestamp of a date in seconds */ +export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime()/1000) + +export type DebouncedTimeout = ReturnType + +export const debouncedTimeout = (intervalMs: number = 1000, task: () => void = undefined) => { + let timeout: NodeJS.Timeout + return { + start: (newIntervalMs?: number, newTask?: () => void) => { + task = newTask || task + intervalMs = newIntervalMs || intervalMs + timeout && clearTimeout(timeout) + timeout = setTimeout(task, intervalMs) + }, + cancel: () => { + timeout && clearTimeout(timeout) + timeout = undefined + }, + setTask: (newTask: () => void) => task = newTask, + setInterval: (newInterval: number) => intervalMs = newInterval + } +} + +export const delay = (ms: number) => delayCancellable (ms).delay +export const delayCancellable = (ms: number) => { + const stack = new Error().stack + let timeout: NodeJS.Timeout + let reject: (error) => void + const delay: Promise = new Promise((resolve, _reject) => { + timeout = setTimeout(resolve, ms) + reject = _reject + }) + const cancel = () => { + clearTimeout (timeout) + reject( + new Boom('Cancelled', { + statusCode: 500, + data: { + stack + } + }) + ) + } + + return { delay, cancel } +} + +export async function promiseTimeout(ms: number, promise: (resolve: (v?: T)=>void, reject: (error) => void) => void) { + if(!ms) { + return new Promise (promise) + } + + const stack = new Error().stack + // Create a promise that rejects in milliseconds + const { delay, cancel } = delayCancellable (ms) + const p = new Promise ((resolve, reject) => { + delay + .then(() => reject( + new Boom('Timed Out', { + statusCode: DisconnectReason.timedOut, + data: { + stack + } + }) + )) + .catch (err => reject(err)) + + promise (resolve, reject) + }) + .finally (cancel) + return p as Promise +} + +// generate a random ID to attach to a message +export const generateMessageID = () => 'BAE5' + randomBytes(6).toString('hex').toUpperCase() + +export const bindWaitForConnectionUpdate = (ev: CommonBaileysEventEmitter) => ( + async(check: (u: Partial) => boolean, timeoutMs?: number) => { + let listener: (item: Partial) => void + await ( + promiseTimeout( + timeoutMs, + (resolve, reject) => { + listener = (update) => { + if(check(update)) { + resolve() + } else if(update.connection === 'close') { + reject(update.lastDisconnect?.error || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })) + } + } + + ev.on('connection.update', listener) + } + ) + .finally(() => ( + ev.off('connection.update', listener) + )) + ) + } +) + +export const printQRIfNecessaryListener = (ev: CommonBaileysEventEmitter, logger: Logger) => { + ev.on('connection.update', async({ qr }) => { + if(qr) { + const QR = await import('qrcode-terminal') + .catch(err => { + logger.error('QR code terminal not added as dependency') + }) + QR?.generate(qr, { small: true }) + } + }) +} \ No newline at end of file diff --git a/src/Utils/history.ts b/src/Utils/history.ts new file mode 100644 index 0000000..1caf4d5 --- /dev/null +++ b/src/Utils/history.ts @@ -0,0 +1,83 @@ +import { promisify } from 'util' +import { inflate } from 'zlib' +import { proto } from '../../WAProto' +import { Chat, Contact } from '../Types' +import { downloadContentFromMessage } from './messages-media' + +const inflatePromise = promisify(inflate) + +export const downloadHistory = async(msg: proto.IHistorySyncNotification) => { + const stream = await downloadContentFromMessage(msg, 'history') + let buffer = Buffer.from([]) + for await (const chunk of stream) { + buffer = Buffer.concat([buffer, chunk]) + } + + // decompress buffer + buffer = await inflatePromise(buffer) + + const syncData = proto.HistorySync.decode(buffer) + return syncData +} + +export const processHistoryMessage = (item: proto.IHistorySync, historyCache: Set) => { + const isLatest = historyCache.size === 0 + const messages: proto.IWebMessageInfo[] = [] + const contacts: Contact[] = [] + const chats: Chat[] = [] + switch (item.syncType) { + case proto.HistorySync.HistorySyncHistorySyncType.INITIAL_BOOTSTRAP: + case proto.HistorySync.HistorySyncHistorySyncType.RECENT: + for(const chat of item.conversations) { + const contactId = `c:${chat.id}` + if(chat.name && !historyCache.has(contactId)) { + contacts.push({ + id: chat.id, + name: chat.name + }) + historyCache.add(contactId) + } + + for(const { message } of chat.messages || []) { + const uqId = `${message?.key.remoteJid}:${message.key.id}` + if(message && !historyCache.has(uqId)) { + messages.push(message) + historyCache.add(uqId) + } + } + + delete chat.messages + if(!historyCache.has(chat.id)) { + chats.push(chat) + historyCache.add(chat.id) + } + } + + break + case proto.HistorySync.HistorySyncHistorySyncType.PUSH_NAME: + for(const c of item.pushnames) { + const contactId = `c:${c.id}` + if(historyCache.has(contactId)) { + contacts.push({ notify: c.pushname, id: c.id }) + historyCache.add(contactId) + } + } + + break + case proto.HistorySync.HistorySyncHistorySyncType.INITIAL_STATUS_V3: + // TODO + break + } + + return { + chats, + contacts, + messages, + isLatest, + } +} + +export const downloadAndProcessHistorySyncNotification = async(msg: proto.IHistorySyncNotification, historyCache: Set) => { + const historyMsg = await downloadHistory(msg) + return processHistoryMessage(historyMsg, historyCache) +} \ No newline at end of file diff --git a/src/Utils/index.ts b/src/Utils/index.ts new file mode 100644 index 0000000..76448aa --- /dev/null +++ b/src/Utils/index.ts @@ -0,0 +1,13 @@ +export * from './decode-wa-message' +export * from './generics' +export * from './messages' +export * from './messages-media' +export * from './validate-connection' +export * from './crypto' +export * from './signal' +export * from './noise-handler' +export * from './history' +export * from './chat-utils' +export * from './lt-hash' +export * from './auth-utils' +export * from './legacy-msgs' \ No newline at end of file diff --git a/src/Utils/legacy-msgs.ts b/src/Utils/legacy-msgs.ts new file mode 100644 index 0000000..3c874da --- /dev/null +++ b/src/Utils/legacy-msgs.ts @@ -0,0 +1,198 @@ +import { Boom } from '@hapi/boom' +import { randomBytes } from 'crypto' +import { AuthenticationCreds, Contact, CurveKeyPair, DisconnectReason, LegacyAuthenticationCreds, WATag } from '../Types' +import { decodeBinaryNodeLegacy, jidNormalizedUser } from '../WABinary' +import { aesDecrypt, Curve, hkdf, hmacSign } from './crypto' +import { BufferJSON } from './generics' + +export const newLegacyAuthCreds = () => ({ + clientID: randomBytes(16).toString('base64') +}) as LegacyAuthenticationCreds + +export const decodeWAMessage = ( + message: Buffer | string, + auth: { macKey: Buffer, encKey: Buffer }, + fromMe: boolean=false +) => { + let commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message + if(commaIndex < 0) { + throw new Boom('invalid message', { data: message }) + } // if there was no comma, then this message must be not be valid + + if(message[commaIndex+1] === ',') { + commaIndex += 1 + } + + let data = message.slice(commaIndex+1, message.length) + + // 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: string = message.slice(0, commaIndex).toString() + let json: any + let tags: WATag + if(data.length) { + const possiblyEnc = (data.length > 32 && data.length % 16 === 0) + if(typeof data === 'string' || !possiblyEnc) { + json = JSON.parse(data.toString()) // parse the JSON + } else { + try { + json = JSON.parse(data.toString()) + } catch{ + const { macKey, encKey } = auth || {} + if(!macKey || !encKey) { + throw new Boom('recieved encrypted buffer when auth creds unavailable', { data: message, statusCode: DisconnectReason.badSession }) + } + + /* + If the data recieved was not a JSON, then it must be an encrypted message. + Such a message can only be decrypted if we're connected successfully to the servers & have encryption keys + */ + if(fromMe) { + tags = [data[0], data[1]] + data = data.slice(2, data.length) + } + + const checksum = data.slice(0, 32) // the first 32 bytes of the buffer are the HMAC sign of the message + data = data.slice(32, data.length) // the actual message + const computedChecksum = hmacSign(data, macKey) // compute the sign of the message we recieved using our macKey + + if(checksum.equals(computedChecksum)) { + // the checksum the server sent, must match the one we computed for the message to be valid + const decrypted = aesDecrypt(data, encKey) // decrypt using AES + json = decodeBinaryNodeLegacy(decrypted, { index: 0 }) // decode the binary message into a JSON array + } else { + throw new Boom('Bad checksum', { + data: { + received: checksum.toString('hex'), + computed: computedChecksum.toString('hex'), + data: data.slice(0, 80).toString(), + tag: messageTag, + message: message.slice(0, 80).toString() + }, + statusCode: DisconnectReason.badSession + }) + } + } + } + } + + return [messageTag, json, tags] as const +} + +/** +* Once the QR code is scanned and we can validate our connection, or we resolved the challenge when logging back in +* @private +* @param json +*/ +export const validateNewConnection = ( + json: { [_: string]: any }, + auth: LegacyAuthenticationCreds, + curveKeys: CurveKeyPair +) => { + // set metadata: one's WhatsApp ID [cc][number]@s.whatsapp.net, name on WhatsApp, info about the phone + const onValidationSuccess = () => { + const user: Contact = { + id: jidNormalizedUser(json.wid), + name: json.pushname + } + return { user, auth, phone: json.phone } + } + + if(!json.secret) { + // if we didn't get a secret, we don't need it, we're validated + if(json.clientToken && json.clientToken !== auth.clientToken) { + auth = { ...auth, clientToken: json.clientToken } + } + + if(json.serverToken && json.serverToken !== auth.serverToken) { + auth = { ...auth, serverToken: json.serverToken } + } + + return onValidationSuccess() + } + + const secret = Buffer.from(json.secret, 'base64') + if(secret.length !== 144) { + throw new Error ('incorrect secret length received: ' + secret.length) + } + + // generate shared key from our private key & the secret shared by the server + const sharedKey = Curve.sharedKey(curveKeys.private, secret.slice(0, 32)) + // expand the key to 80 bytes using HKDF + const expandedKey = hkdf(sharedKey as Buffer, 80, { }) + + // perform HMAC validation. + const hmacValidationKey = expandedKey.slice(32, 64) + const hmacValidationMessage = Buffer.concat([secret.slice(0, 32), secret.slice(64, secret.length)]) + + const hmac = hmacSign(hmacValidationMessage, hmacValidationKey) + + if(!hmac.equals(secret.slice(32, 64))) { + // if the checksums didn't match + throw new Boom('HMAC validation failed', { statusCode: 400 }) + } + + // computed HMAC should equal secret[32:64] + // expandedKey[64:] + secret[64:] are the keys, encrypted using AES, that are used to encrypt/decrypt the messages recieved from WhatsApp + // they are encrypted using key: expandedKey[0:32] + const encryptedAESKeys = Buffer.concat([ + expandedKey.slice(64, expandedKey.length), + secret.slice(64, secret.length), + ]) + const decryptedKeys = aesDecrypt(encryptedAESKeys, expandedKey.slice(0, 32)) + // set the credentials + auth = { + encKey: decryptedKeys.slice(0, 32), // first 32 bytes form the key to encrypt/decrypt messages + macKey: decryptedKeys.slice(32, 64), // last 32 bytes from the key to sign messages + clientToken: json.clientToken, + serverToken: json.serverToken, + clientID: auth.clientID, + } + return onValidationSuccess() +} + +export const computeChallengeResponse = (challenge: string, auth: LegacyAuthenticationCreds) => { + const bytes = Buffer.from(challenge, 'base64') // decode the base64 encoded challenge string + const signed = hmacSign(bytes, auth.macKey).toString('base64') // sign the challenge string with our macKey + return ['admin', 'challenge', signed, auth.serverToken, auth.clientID] // prepare to send this signed string with the serverToken & clientID +} + +export const useSingleFileLegacyAuthState = (file: string) => { + // require fs here so that in case "fs" is not available -- the app does not crash + const { readFileSync, writeFileSync, existsSync } = require('fs') + let state: LegacyAuthenticationCreds + + if(existsSync(file)) { + state = JSON.parse( + readFileSync(file, { encoding: 'utf-8' }), + BufferJSON.reviver + ) + if(typeof state.encKey === 'string') { + state.encKey = Buffer.from(state.encKey, 'base64') + } + + if(typeof state.macKey === 'string') { + state.macKey = Buffer.from(state.macKey, 'base64') + } + } else { + state = newLegacyAuthCreds() + } + + return { + state, + saveState: () => { + const str = JSON.stringify(state, BufferJSON.replacer, 2) + writeFileSync(file, str) + } + } +} + +export const getAuthenticationCredsType = (creds: LegacyAuthenticationCreds | AuthenticationCreds) => { + if('clientID' in creds && !!creds.clientID) { + return 'legacy' + } + + if('noiseKey' in creds && !!creds.noiseKey) { + return 'md' + } +} \ No newline at end of file diff --git a/src/Utils/lt-hash.ts b/src/Utils/lt-hash.ts new file mode 100644 index 0000000..bc9b057 --- /dev/null +++ b/src/Utils/lt-hash.ts @@ -0,0 +1,61 @@ +import { hkdf } from './crypto' + +/** + * LT Hash is a summation based hash algorithm that maintains the integrity of a piece of data + * over a series of mutations. You can add/remove mutations and it'll return a hash equal to + * if the same series of mutations was made sequentially. + */ + +const o = 128 + +class d { + + salt: string + + constructor(e: string) { + this.salt = e + } + add(e, t) { + var r = this + for(const item of t) { + e = r._addSingle(e, item) + } + + return e + } + subtract(e, t) { + var r = this + for(const item of t) { + e = r._subtractSingle(e, item) + } + + return e + } + subtractThenAdd(e, t, r) { + var n = this + return n.add(n.subtract(e, r), t) + } + _addSingle(e, t) { + var r = this + const n = new Uint8Array(hkdf(Buffer.from(t), o, { info: r.salt })).buffer + return r.performPointwiseWithOverflow(e, n, ((e, t) => e + t)) + } + _subtractSingle(e, t) { + var r = this + + const n = new Uint8Array(hkdf(Buffer.from(t), o, { info: r.salt })).buffer + return r.performPointwiseWithOverflow(e, n, ((e, t) => e - t)) + } + performPointwiseWithOverflow(e, t, r) { + const n = new DataView(e) + , i = new DataView(t) + , a = new ArrayBuffer(n.byteLength) + , s = new DataView(a) + for(let e = 0; e < n.byteLength; e += 2) { + s.setUint16(e, r(n.getUint16(e, !0), i.getUint16(e, !0)), !0) + } + + return a + } +} +export const LT_HASH_ANTI_TAMPERING = new d('WhatsApp Patch Integrity') \ No newline at end of file diff --git a/src/Utils/make-mutex.ts b/src/Utils/make-mutex.ts new file mode 100644 index 0000000..8903b26 --- /dev/null +++ b/src/Utils/make-mutex.ts @@ -0,0 +1,22 @@ + +export default () => { + let task = Promise.resolve() as Promise + return { + mutex(code: () => Promise):Promise { + task = (async() => { + // wait for the previous task to complete + // if there is an error, we swallow so as to not block the queue + try { + await task + } catch{ } + + // execute the current task + return code() + })() + // we replace the existing task, appending the new piece of execution to it + // so the next task will have to wait for this one to finish + return task + }, + } +} + \ No newline at end of file diff --git a/src/Utils/messages-media.ts b/src/Utils/messages-media.ts new file mode 100644 index 0000000..fab4930 --- /dev/null +++ b/src/Utils/messages-media.ts @@ -0,0 +1,588 @@ +import { Boom } from '@hapi/boom' +import { AxiosRequestConfig } from 'axios' +import { exec } from 'child_process' +import * as Crypto from 'crypto' +import { once } from 'events' +import { createReadStream, createWriteStream, promises as fs, WriteStream } from 'fs' +import type { IAudioMetadata } from 'music-metadata' +import { tmpdir } from 'os' +import { join } from 'path' +import type { Logger } from 'pino' +import { Readable, Transform } from 'stream' +import { URL } from 'url' +import { DEFAULT_ORIGIN, MEDIA_PATH_MAP } from '../Defaults' +import { CommonSocketConfig, DownloadableMessage, MediaConnInfo, MediaType, MessageType, WAGenericMediaMessage, WAMediaUpload, WAMediaUploadFunction, WAMessageContent, WAProto } from '../Types' +import { hkdf } from './crypto' +import { generateMessageID } from './generics' + +const getTmpFilesDirectory = () => tmpdir() + +const getImageProcessingLibrary = async() => { + const [jimp, sharp] = await Promise.all([ + (async() => { + const jimp = await ( + import('jimp') + .catch(() => { }) + ) + return jimp + })(), + (async() => { + const sharp = await ( + import('sharp') + .catch(() => { }) + ) + return sharp + })() + ]) + if(sharp) { + return { sharp } + } + + if(jimp) { + return { jimp } + } + + throw new Boom('No image processing library available') +} + +export const hkdfInfoKey = (type: MediaType) => { + let str: string = type + if(type === 'sticker') { + str = 'image' + } + + if(type === 'md-app-state') { + str = 'App State' + } + + const hkdfInfo = str[0].toUpperCase() + str.slice(1) + return `WhatsApp ${hkdfInfo} Keys` +} + +/** generates all the keys required to encrypt/decrypt & sign a media message */ +export function getMediaKeys(buffer, mediaType: MediaType) { + if(typeof buffer === 'string') { + buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64') + } + + // expand using HKDF to 112 bytes, also pass in the relevant app info + const expandedMediaKey = hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) }) + return { + iv: expandedMediaKey.slice(0, 16), + cipherKey: expandedMediaKey.slice(16, 48), + macKey: expandedMediaKey.slice(48, 80), + } +} + +/** Extracts video thumb using FFMPEG */ +const extractVideoThumb = async( + path: string, + destPath: string, + time: string, + size: { width: number; height: number }, +) => new Promise((resolve, reject) => { + const cmd = `ffmpeg -ss ${time} -i ${path} -y -s ${size.width}x${size.height} -vframes 1 -f image2 ${destPath}` + exec(cmd, (err) => { + if(err) { + reject(err) + } else { + resolve() + } + }) +}) as Promise + +export const extractImageThumb = async(bufferOrFilePath: Readable | Buffer | string) => { + if(bufferOrFilePath instanceof Readable) { + bufferOrFilePath = await toBuffer(bufferOrFilePath) + } + + const lib = await getImageProcessingLibrary() + if('sharp' in lib) { + const result = await lib.sharp!.default(bufferOrFilePath) + .resize(32, 32) + .jpeg({ quality: 50 }) + .toBuffer() + return result + } else { + const { read, MIME_JPEG, RESIZE_BILINEAR } = lib.jimp + + const jimp = await read(bufferOrFilePath as any) + const result = await jimp + .quality(50) + .resize(32, 32, RESIZE_BILINEAR) + .getBufferAsync(MIME_JPEG) + return result + } +} + +export const generateProfilePicture = async(mediaUpload: WAMediaUpload) => { + let bufferOrFilePath: Buffer | string + if(Buffer.isBuffer(mediaUpload)) { + bufferOrFilePath = mediaUpload + } else if('url' in mediaUpload) { + bufferOrFilePath = mediaUpload.url.toString() + } else { + bufferOrFilePath = await toBuffer(mediaUpload.stream) + } + + const lib = await getImageProcessingLibrary() + let img: Promise + if('sharp' in lib) { + img = lib.sharp!.default(bufferOrFilePath) + .resize(640, 640) + .jpeg({ + quality: 50, + }) + .toBuffer() + } else { + const { read, MIME_JPEG, RESIZE_BILINEAR } = lib.jimp + const jimp = await read(bufferOrFilePath as any) + const min = Math.min(jimp.getWidth(), jimp.getHeight()) + const cropped = jimp.crop(0, 0, min, min) + + img = cropped + .quality(50) + .resize(640, 640, RESIZE_BILINEAR) + .getBufferAsync(MIME_JPEG) + } + + return { + img: await img, + } +} + +/** gets the SHA256 of the given media message */ +export const mediaMessageSHA256B64 = (message: WAMessageContent) => { + const media = Object.values(message)[0] as WAGenericMediaMessage + return media?.fileSha256 && Buffer.from(media.fileSha256).toString ('base64') +} + +export async function getAudioDuration(buffer: Buffer | string | Readable) { + const musicMetadata = await import('music-metadata') + let metadata: IAudioMetadata + if(Buffer.isBuffer(buffer)) { + metadata = await musicMetadata.parseBuffer(buffer, null, { duration: true }) + } else if(typeof buffer === 'string') { + const rStream = createReadStream(buffer) + metadata = await musicMetadata.parseStream(rStream, null, { duration: true }) + rStream.close() + } else { + metadata = await musicMetadata.parseStream(buffer, null, { duration: true }) + } + + return metadata.format.duration +} + +export const toReadable = (buffer: Buffer) => { + const readable = new Readable({ read: () => {} }) + readable.push(buffer) + readable.push(null) + return readable +} + +export const toBuffer = async(stream: Readable) => { + let buff = Buffer.alloc(0) + for await (const chunk of stream) { + buff = Buffer.concat([ buff, chunk ]) + } + + return buff +} + +export const getStream = async(item: WAMediaUpload) => { + if(Buffer.isBuffer(item)) { + return { stream: toReadable(item), type: 'buffer' } + } + + if('stream' in item) { + return { stream: item.stream, type: 'readable' } + } + + if(item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) { + return { stream: await getHttpStream(item.url), type: 'remote' } + } + + return { stream: createReadStream(item.url), type: 'file' } +} + +/** generates a thumbnail for a given media, if required */ +export async function generateThumbnail( + file: string, + mediaType: 'video' | 'image', + options: { + logger?: Logger + } +) { + let thumbnail: string + if(mediaType === 'image') { + const buff = await extractImageThumb(file) + thumbnail = buff.toString('base64') + } else if(mediaType === 'video') { + const imgFilename = join(getTmpFilesDirectory(), generateMessageID() + '.jpg') + try { + await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 }) + const buff = await fs.readFile(imgFilename) + thumbnail = buff.toString('base64') + + await fs.unlink(imgFilename) + } catch(err) { + options.logger?.debug('could not generate video thumb: ' + err) + } + } + + return thumbnail +} + +export const getHttpStream = async(url: string | URL, options: AxiosRequestConfig & { isStream?: true } = {}) => { + const { default: axios } = await import('axios') + const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' }) + return fetched.data as Readable +} + +export const encryptedStream = async( + media: WAMediaUpload, + mediaType: MediaType, + saveOriginalFileIfRequired = true, + logger?: Logger +) => { + const { stream, type } = await getStream(media) + + logger?.debug('fetched media stream') + + const mediaKey = Crypto.randomBytes(32) + const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType) + // random name + //const encBodyPath = join(getTmpFilesDirectory(), mediaType + generateMessageID() + '.enc') + // const encWriteStream = createWriteStream(encBodyPath) + const encWriteStream = new Readable({ read: () => {} }) + + let bodyPath: string + let writeStream: WriteStream + let didSaveToTmpPath = false + if(type === 'file') { + bodyPath = (media as any).url + } else if(saveOriginalFileIfRequired) { + bodyPath = join(getTmpFilesDirectory(), mediaType + generateMessageID()) + writeStream = createWriteStream(bodyPath) + didSaveToTmpPath = true + } + + let fileLength = 0 + const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv) + let hmac = Crypto.createHmac('sha256', macKey).update(iv) + let sha256Plain = Crypto.createHash('sha256') + let sha256Enc = Crypto.createHash('sha256') + + const onChunk = (buff: Buffer) => { + sha256Enc = sha256Enc.update(buff) + hmac = hmac.update(buff) + encWriteStream.push(buff) + } + + try { + for await (const data of stream) { + fileLength += data.length + sha256Plain = sha256Plain.update(data) + if(writeStream) { + if(!writeStream.write(data)) { + await once(writeStream, 'drain') + } + } + + onChunk(aes.update(data)) + } + + onChunk(aes.final()) + + const mac = hmac.digest().slice(0, 10) + sha256Enc = sha256Enc.update(mac) + + const fileSha256 = sha256Plain.digest() + const fileEncSha256 = sha256Enc.digest() + + encWriteStream.push(mac) + encWriteStream.push(null) + + writeStream && writeStream.end() + stream.destroy() + + logger?.debug('encrypted data successfully') + + return { + mediaKey, + encWriteStream, + bodyPath, + mac, + fileEncSha256, + fileSha256, + fileLength, + didSaveToTmpPath + } + } catch(error) { + encWriteStream.destroy(error) + writeStream.destroy(error) + aes.destroy(error) + hmac.destroy(error) + sha256Plain.destroy(error) + sha256Enc.destroy(error) + stream.destroy(error) + + throw error + } +} + +const DEF_HOST = 'mmg.whatsapp.net' +const AES_CHUNK_SIZE = 16 + +const toSmallestChunkSize = (num: number) => { + return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE +} + +type MediaDownloadOptions = { + startByte?: number + endByte?: number +} + +export const downloadContentFromMessage = async( + { mediaKey, directPath, url }: DownloadableMessage, + type: MediaType, + { startByte, endByte }: MediaDownloadOptions = { } +) => { + const downloadUrl = url || `https://${DEF_HOST}${directPath}` + let bytesFetched = 0 + let startChunk = 0 + let firstBlockIsIV = false + // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV + if(startByte) { + const chunk = toSmallestChunkSize(startByte || 0) + if(chunk) { + startChunk = chunk-AES_CHUNK_SIZE + bytesFetched = chunk + + firstBlockIsIV = true + } + } + + const endChunk = endByte ? toSmallestChunkSize(endByte || 0)+AES_CHUNK_SIZE : undefined + + const headers: { [_: string]: string } = { + Origin: DEFAULT_ORIGIN, + } + if(startChunk || endChunk) { + headers.Range = `bytes=${startChunk}-` + if(endChunk) { + headers.Range += endChunk + } + } + + // download the message + const fetched = await getHttpStream( + downloadUrl, + { + headers, + maxBodyLength: Infinity, + maxContentLength: Infinity, + } + ) + + let remainingBytes = Buffer.from([]) + const { cipherKey, iv } = getMediaKeys(mediaKey, type) + + let aes: Crypto.Decipher + + const pushBytes = (bytes: Buffer, push: (bytes: Buffer) => void) => { + if(startByte || endByte) { + const start = bytesFetched >= startByte ? undefined : Math.max(startByte-bytesFetched, 0) + const end = bytesFetched+bytes.length < endByte ? undefined : Math.max(endByte-bytesFetched, 0) + + push(bytes.slice(start, end)) + + bytesFetched += bytes.length + } else { + push(bytes) + } + } + + const output = new Transform({ + transform(chunk, _, callback) { + let data = Buffer.concat([remainingBytes, chunk]) + + const decryptLength = toSmallestChunkSize(data.length) + remainingBytes = data.slice(decryptLength) + data = data.slice(0, decryptLength) + + if(!aes) { + let ivValue = iv + if(firstBlockIsIV) { + ivValue = data.slice(0, AES_CHUNK_SIZE) + data = data.slice(AES_CHUNK_SIZE) + } + + aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue) + // if an end byte that is not EOF is specified + // stop auto padding (PKCS7) -- otherwise throws an error for decryption + if(endByte) { + aes.setAutoPadding(false) + } + + } + + try { + pushBytes(aes.update(data), b => this.push(b)) + callback() + } catch(error) { + callback(error) + } + }, + final(callback) { + try { + pushBytes(aes.final(), b => this.push(b)) + callback() + } catch(error) { + callback(error) + } + }, + }) + return fetched.pipe(output, { end: true }) +} + +/** + * Decode a media message (video, image, document, audio) & return decrypted buffer + * @param message the media message you want to decode + */ +export async function decryptMediaMessageBuffer(message: WAMessageContent): Promise { + /* + One can infer media type from the key in the message + it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc. + */ + const type = Object.keys(message)[0] as MessageType + if( + !type || + type === 'conversation' || + type === 'extendedTextMessage' + ) { + throw new Boom(`no media message for "${type}"`, { statusCode: 400 }) + } + + if(type === 'locationMessage' || type === 'liveLocationMessage') { + const buffer = Buffer.from(message[type].jpegThumbnail) + const readable = new Readable({ read: () => {} }) + readable.push(buffer) + readable.push(null) + return readable + } + + let messageContent: WAGenericMediaMessage + if(message.productMessage) { + const product = message.productMessage.product?.productImage + if(!product) { + throw new Boom('product has no image', { statusCode: 400 }) + } + + messageContent = product + } else { + messageContent = message[type] + } + + return downloadContentFromMessage(messageContent, type.replace('Message', '') as MediaType) +} + +export function extensionForMediaMessage(message: WAMessageContent) { + const getExtension = (mimetype: string) => mimetype.split(';')[0].split('/')[1] + const type = Object.keys(message)[0] as MessageType + let extension: string + if( + type === 'locationMessage' || + type === 'liveLocationMessage' || + type === 'productMessage' + ) { + extension = '.jpeg' + } else { + const messageContent = message[type] as + | WAProto.VideoMessage + | WAProto.ImageMessage + | WAProto.AudioMessage + | WAProto.DocumentMessage + extension = getExtension (messageContent.mimetype) + } + + return extension +} + +export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: CommonSocketConfig, refreshMediaConn: (force: boolean) => Promise): WAMediaUploadFunction => { + return async(stream, { mediaType, fileEncSha256B64, timeoutMs }) => { + const { default: axios } = await import('axios') + // send a query JSON to obtain the url & auth token to upload our media + let uploadInfo = await refreshMediaConn(false) + + let urls: { mediaUrl: string, directPath: string } + const hosts = [ ...customUploadHosts, ...uploadInfo.hosts ] + + const chunks: Buffer[] = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + let reqBody = Buffer.concat(chunks) + + for(const { hostname, maxContentLengthBytes } of hosts) { + logger.debug(`uploading to "${hostname}"`) + + const auth = encodeURIComponent(uploadInfo.auth) // the auth token + const url = `https://${hostname}${MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}` + let result: any + try { + if(maxContentLengthBytes && reqBody.length > maxContentLengthBytes) { + throw new Boom(`Body too large for "${hostname}"`, { statusCode: 413 }) + } + + const body = await axios.post( + url, + reqBody, + { + headers: { + 'Content-Type': 'application/octet-stream', + 'Origin': DEFAULT_ORIGIN + }, + httpsAgent: fetchAgent, + timeout: timeoutMs, + responseType: 'json', + maxBodyLength: Infinity, + maxContentLength: Infinity, + } + ) + result = body.data + + if(result?.url || result?.directPath) { + urls = { + mediaUrl: result.url, + directPath: result.direct_path + } + break + } else { + uploadInfo = await refreshMediaConn(true) + throw new Error(`upload failed, reason: ${JSON.stringify(result)}`) + } + } catch(error) { + if(axios.isAxiosError(error)) { + result = error.response?.data + } + + const isLast = hostname === hosts[uploadInfo.hosts.length-1]?.hostname + logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`) + } + } + + // clear buffer just to be sure we're releasing the memory + reqBody = undefined + + if(!urls) { + throw new Boom( + 'Media upload failed on all hosts', + { statusCode: 500 } + ) + } + + return urls + } +} \ No newline at end of file diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts new file mode 100644 index 0000000..9b4cc0a --- /dev/null +++ b/src/Utils/messages.ts @@ -0,0 +1,495 @@ +import { Boom } from '@hapi/boom' +import { promises as fs } from 'fs' +import { proto } from '../../WAProto' +import { MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults' +import { + AnyMediaMessageContent, + AnyMessageContent, + MediaGenerationOptions, + MediaType, + MessageContentGenerationOptions, + MessageGenerationOptions, + MessageGenerationOptionsFromContent, + MessageType, + WAMediaUpload, + WAMessage, + WAMessageContent, + WAMessageStatus, + WAProto, + WATextMessage } from '../Types' +import { generateMessageID, unixTimestampSeconds } from './generics' +import { encryptedStream, generateThumbnail, getAudioDuration } from './messages-media' + +type MediaUploadData = { + media: WAMediaUpload + caption?: string + ptt?: boolean + seconds?: number + gifPlayback?: boolean + fileName?: string + jpegThumbnail?: string + mimetype?: string +} + +const MIMETYPE_MAP: { [T in MediaType]: string } = { + image: 'image/jpeg', + video: 'video/mp4', + document: 'application/pdf', + audio: 'audio/ogg; codecs=opus', + sticker: 'image/webp', + history: 'application/x-protobuf', + 'md-app-state': 'application/x-protobuf', +} + +const MessageTypeProto = { + 'image': WAProto.ImageMessage, + 'video': WAProto.VideoMessage, + 'audio': WAProto.AudioMessage, + 'sticker': WAProto.StickerMessage, + 'document': WAProto.DocumentMessage, +} as const + +const ButtonType = proto.ButtonsMessage.ButtonsMessageHeaderType + +export const prepareWAMessageMedia = async( + message: AnyMediaMessageContent, + options: MediaGenerationOptions +) => { + const logger = options.logger + + let mediaType: typeof MEDIA_KEYS[number] + for(const key of MEDIA_KEYS) { + if(key in message) { + mediaType = key + } + } + + const uploadData: MediaUploadData = { + ...message, + media: message[mediaType] + } + delete uploadData[mediaType] + // check if cacheable + generate cache key + const cacheableKey = typeof uploadData.media === 'object' && + ('url' in uploadData.media) && + !!uploadData.media.url && + !!options.mediaCache && ( + // generate the key + mediaType + ':' + uploadData.media.url!.toString() + ) + + if(mediaType === 'document' && !uploadData.fileName) { + uploadData.fileName = 'file' + } + + if(!uploadData.mimetype) { + uploadData.mimetype = MIMETYPE_MAP[mediaType] + } + + // check for cache hit + if(cacheableKey) { + const mediaBuff: Buffer = options.mediaCache!.get(cacheableKey) + if(mediaBuff) { + logger?.debug({ cacheableKey }, 'got media cache hit') + + const obj = WAProto.Message.decode(mediaBuff) + const key = `${mediaType}Message` + + delete uploadData.media + Object.assign(obj[key], { ...uploadData }) + + return obj + } + } + + const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined' + const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && + (typeof uploadData['jpegThumbnail'] === 'undefined') + const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation + const { + mediaKey, + encWriteStream, + bodyPath, + fileEncSha256, + fileSha256, + fileLength, + didSaveToTmpPath + } = await encryptedStream(uploadData.media, mediaType, requiresOriginalForSomeProcessing) + // url safe Base64 encode the SHA256 hash of the body + const fileEncSha256B64 = encodeURIComponent( + fileEncSha256.toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/\=+$/, '') + ) + + const [{ mediaUrl, directPath }] = await Promise.all([ + (async() => { + const result = await options.upload( + encWriteStream, + { fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs } + ) + logger?.debug('uploaded media') + return result + })(), + (async() => { + try { + if(requiresThumbnailComputation) { + uploadData.jpegThumbnail = await generateThumbnail(bodyPath, mediaType as any, options) + logger?.debug('generated thumbnail') + } + + if(requiresDurationComputation) { + uploadData.seconds = await getAudioDuration(bodyPath) + logger?.debug('computed audio duration') + } + } catch(error) { + logger?.warn({ trace: error.stack }, 'failed to obtain extra info') + } + })(), + ]) + .finally( + async() => { + encWriteStream.destroy() + // remove tmp files + if(didSaveToTmpPath && bodyPath) { + await fs.unlink(bodyPath) + logger?.debug('removed tmp files') + } + } + ) + + delete uploadData.media + + const obj = WAProto.Message.fromObject({ + [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject( + { + url: mediaUrl, + directPath, + mediaKey, + fileEncSha256, + fileSha256, + fileLength, + mediaKeyTimestamp: unixTimestampSeconds(), + ...uploadData + } + ) + }) + + if(cacheableKey) { + logger.debug({ cacheableKey }, 'set cache') + options.mediaCache!.set(cacheableKey, WAProto.Message.encode(obj).finish()) + } + + return obj +} + +export const prepareDisappearingMessageSettingContent = (ephemeralExpiration?: number) => { + ephemeralExpiration = ephemeralExpiration || 0 + const content: WAMessageContent = { + ephemeralMessage: { + message: { + protocolMessage: { + type: WAProto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING, + ephemeralExpiration + } + } + } + } + return WAProto.Message.fromObject(content) +} + +/** + * Generate forwarded message content like WA does + * @param message the message to forward + * @param options.forceForward will show the message as forwarded even if it is from you + */ +export const generateForwardMessageContent = ( + message: WAMessage, + forceForward?: boolean +) => { + let content = message.message + if(!content) { + throw new Boom('no content in message', { statusCode: 400 }) + } + + // hacky copy + content = proto.Message.decode(proto.Message.encode(message.message).finish()) + + let key = Object.keys(content)[0] as MessageType + + let score = content[key].contextInfo?.forwardingScore || 0 + score += message.key.fromMe && !forceForward ? 0 : 1 + if(key === 'conversation') { + content.extendedTextMessage = { text: content[key] } + delete content.conversation + + key = 'extendedTextMessage' + } + + if(score > 0) { + content[key].contextInfo = { forwardingScore: score, isForwarded: true } + } else { + content[key].contextInfo = {} + } + + return content +} + +export const generateWAMessageContent = async( + message: AnyMessageContent, + options: MessageContentGenerationOptions +) => { + let m: WAMessageContent = {} + if('text' in message) { + const extContent = { ...message } as WATextMessage + if(!!options.getUrlInfo && message.text.match(URL_REGEX)) { + try { + const data = await options.getUrlInfo(message.text) + extContent.canonicalUrl = data['canonical-url'] + extContent.matchedText = data['matched-text'] + extContent.jpegThumbnail = data.jpegThumbnail + extContent.description = data.description + extContent.title = data.title + extContent.previewType = 0 + } catch(error) { // ignore if fails + options.logger?.warn({ trace: error.stack }, 'url generation failed') + } + } + + m.extendedTextMessage = extContent + } else if('contacts' in message) { + const contactLen = message.contacts.contacts.length + if(!contactLen) { + throw new Boom('require atleast 1 contact', { statusCode: 400 }) + } + + if(contactLen === 1) { + m.contactMessage = WAProto.ContactMessage.fromObject(message.contacts.contacts[0]) + } else { + m.contactsArrayMessage = WAProto.ContactsArrayMessage.fromObject(message.contacts) + } + } else if('location' in message) { + m.locationMessage = WAProto.LocationMessage.fromObject(message.location) + } else if('delete' in message) { + m.protocolMessage = { + key: message.delete, + type: WAProto.ProtocolMessage.ProtocolMessageType.REVOKE + } + } else if('forward' in message) { + m = generateForwardMessageContent( + message.forward, + message.force + ) + } else if('disappearingMessagesInChat' in message) { + const exp = typeof message.disappearingMessagesInChat === 'boolean' ? + (message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : + message.disappearingMessagesInChat + m = prepareDisappearingMessageSettingContent(exp) + } else { + m = await prepareWAMessageMedia( + message, + options + ) + } + + if('buttons' in message && !!message.buttons) { + const buttonsMessage: proto.IButtonsMessage = { + buttons: message.buttons!.map(b => ({ ...b, type: proto.Button.ButtonType.RESPONSE })) + } + if('text' in message) { + buttonsMessage.contentText = message.text + buttonsMessage.headerType = ButtonType.EMPTY + } else { + if('caption' in message) { + buttonsMessage.contentText = message.caption + } + + const type = Object.keys(m)[0].replace('Message', '').toUpperCase() + buttonsMessage.headerType = ButtonType[type] + + Object.assign(buttonsMessage, m) + } + + if('footer' in message && !!message.footer) { + buttonsMessage.footerText = message.footer + } + + m = { buttonsMessage } + } else if('templateButtons' in message && !!message.templateButtons) { + const templateMessage: proto.ITemplateMessage = { + hydratedTemplate: { + hydratedButtons: message.templateButtons + } + } + + if('text' in message) { + templateMessage.hydratedTemplate.hydratedContentText = message.text + } else { + + if('caption' in message) { + templateMessage.hydratedTemplate.hydratedContentText = message.caption + } + + Object.assign(templateMessage.hydratedTemplate, m) + } + + if('footer' in message && !!message.footer) { + templateMessage.hydratedTemplate.hydratedFooterText = message.footer + } + + m = { templateMessage } + } + + if('sections' in message && !!message.sections) { + const listMessage: proto.IListMessage = { + sections: message.sections, + buttonText: message.buttonText, + title: message.title, + footerText: message.footer, + description: message.text, + listType: proto.ListMessage.ListMessageListType['SINGLE_SELECT'] + } + + m = { listMessage } + } + + if('viewOnce' in message && !!message.viewOnce) { + m = { viewOnceMessage: { message: m } } + } + + if('mentions' in message && message.mentions?.length) { + const [messageType] = Object.keys(m) + m[messageType].contextInfo = m[messageType] || { } + m[messageType].contextInfo.mentionedJid = message.mentions + } + + return WAProto.Message.fromObject(m) +} + +export const generateWAMessageFromContent = ( + jid: string, + message: WAMessageContent, + options: MessageGenerationOptionsFromContent +) => { + if(!options.timestamp) { + options.timestamp = new Date() + } // set timestamp to now + + const key = Object.keys(message)[0] + const timestamp = unixTimestampSeconds(options.timestamp) + const { quoted, userJid } = options + + if(quoted) { + const participant = quoted.key.fromMe ? userJid : (quoted.participant || quoted.key.participant || quoted.key.remoteJid) + + message[key].contextInfo = message[key].contextInfo || { } + message[key].contextInfo.participant = participant + message[key].contextInfo.stanzaId = quoted.key.id + message[key].contextInfo.quotedMessage = quoted.message + + // if a participant is quoted, then it must be a group + // hence, remoteJid of group must also be entered + if(quoted.key.participant || quoted.participant) { + message[key].contextInfo.remoteJid = quoted.key.remoteJid + } + } + + if( + // if we want to send a disappearing message + !!options?.ephemeralExpiration && + // and it's not a protocol message -- delete, toggle disappear message + key !== 'protocolMessage' && + // already not converted to disappearing message + key !== 'ephemeralMessage' + ) { + message[key].contextInfo = { + ...(message[key].contextInfo || {}), + expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL, + //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString() + } + message = { + ephemeralMessage: { + message + } + } + } + + message = WAProto.Message.fromObject(message) + + const messageJSON = { + key: { + remoteJid: jid, + fromMe: true, + id: options?.messageId || generateMessageID(), + }, + message: message, + messageTimestamp: timestamp, + messageStubParameters: [], + participant: jid.includes('@g.us') ? userJid : undefined, + status: WAMessageStatus.PENDING + } + return WAProto.WebMessageInfo.fromObject(messageJSON) +} + +export const generateWAMessage = async( + jid: string, + content: AnyMessageContent, + options: MessageGenerationOptions, +) => { + // ensure msg ID is with every log + options.logger = options?.logger?.child({ msgId: options.messageId }) + return generateWAMessageFromContent( + jid, + await generateWAMessageContent( + content, + options + ), + options + ) +} + +/** Get the key to access the true type of content */ +export const getContentType = (content: WAProto.IMessage | undefined) => { + if(content) { + const keys = Object.keys(content) + const key = keys.find(k => (k === 'conversation' || k.endsWith('Message')) && k !== 'senderKeyDistributionMessage') + return key as keyof typeof content + } +} + +/** + * Extract the true message content from a message + * Eg. extracts the inner message from a disappearing message/view once message + */ +export const extractMessageContent = (content: WAMessageContent | undefined | null): WAMessageContent | undefined => { + content = content?.ephemeralMessage?.message || + content?.viewOnceMessage?.message || + content || + undefined + + if(content?.buttonsMessage) { + const { buttonsMessage } = content + if(buttonsMessage.imageMessage) { + return { imageMessage: buttonsMessage.imageMessage } + } else if(buttonsMessage.documentMessage) { + return { documentMessage: buttonsMessage.documentMessage } + } else if(buttonsMessage.videoMessage) { + return { videoMessage: buttonsMessage.videoMessage } + } else if(buttonsMessage.locationMessage) { + return { locationMessage: buttonsMessage.locationMessage } + } else { + return { conversation: buttonsMessage.contentText } + } + } + + return content +} + +/** + * Returns the device predicted by message ID + */ +export const getDevice = (id: string) => { + const deviceType = id.length > 21 ? 'android' : id.substring(0, 2) === '3A' ? 'ios' : 'web' + return deviceType +} diff --git a/src/Utils/noise-handler.ts b/src/Utils/noise-handler.ts new file mode 100644 index 0000000..def4be1 --- /dev/null +++ b/src/Utils/noise-handler.ts @@ -0,0 +1,179 @@ +import { Boom } from '@hapi/boom' +import { createCipheriv, createDecipheriv } from 'crypto' +import { proto } from '../../WAProto' +import { NOISE_MODE, NOISE_WA_HEADER } from '../Defaults' +import { KeyPair } from '../Types' +import { Binary } from '../WABinary' +import { BinaryNode, decodeBinaryNode } from '../WABinary' +import { Curve, hkdf, sha256 } from './crypto' + +const generateIV = (counter: number) => { + const iv = new ArrayBuffer(12) + new DataView(iv).setUint32(8, counter) + + return new Uint8Array(iv) +} + +export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: KeyPair) => { + + const authenticate = (data: Uint8Array) => { + if(!isFinished) { + hash = sha256(Buffer.from(Binary.build(hash, data).readByteArray())) + } + } + + const encrypt = (plaintext: Uint8Array) => { + const authTagLength = 128 >> 3 + const cipher = createCipheriv('aes-256-gcm', encKey, generateIV(writeCounter), { authTagLength }) + cipher.setAAD(hash) + + const result = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()]) + + writeCounter += 1 + + authenticate(result) + return result + } + + const decrypt = (ciphertext: Uint8Array) => { + // before the handshake is finished, we use the same counter + // after handshake, the counters are different + const iv = generateIV(isFinished ? readCounter : writeCounter) + const cipher = createDecipheriv('aes-256-gcm', decKey, iv) + // decrypt additional adata + const tagLength = 128 >> 3 + const enc = ciphertext.slice(0, ciphertext.length-tagLength) + const tag = ciphertext.slice(ciphertext.length-tagLength) + // set additional data + cipher.setAAD(hash) + cipher.setAuthTag(tag) + + const result = Buffer.concat([cipher.update(enc), cipher.final()]) + + if(isFinished) { + readCounter += 1 + } else { + writeCounter += 1 + } + + authenticate(ciphertext) + return result + } + + const localHKDF = (data: Uint8Array) => { + const key = hkdf(Buffer.from(data), 64, { salt, info: '' }) + return [key.slice(0, 32), key.slice(32)] + } + + const mixIntoKey = (data: Uint8Array) => { + const [write, read] = localHKDF(data) + salt = write + encKey = read + decKey = read + readCounter = 0 + writeCounter = 0 + } + + const finishInit = () => { + const [write, read] = localHKDF(new Uint8Array(0)) + encKey = write + decKey = read + hash = Buffer.from([]) + readCounter = 0 + writeCounter = 0 + isFinished = true + } + + const data = Binary.build(NOISE_MODE).readBuffer() + let hash = Buffer.from(data.byteLength === 32 ? data : sha256(Buffer.from(data))) + let salt = hash + let encKey = hash + let decKey = hash + let readCounter = 0 + let writeCounter = 0 + let isFinished = false + let sentIntro = false + + const outBinary = new Binary() + const inBinary = new Binary() + + authenticate(NOISE_WA_HEADER) + authenticate(publicKey) + + return { + encrypt, + decrypt, + authenticate, + mixIntoKey, + finishInit, + processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => { + authenticate(serverHello!.ephemeral!) + mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral!)) + + const decStaticContent = decrypt(serverHello!.static!) + mixIntoKey(Curve.sharedKey(privateKey, decStaticContent)) + + const certDecoded = decrypt(serverHello!.payload!) + const { details: certDetails, signature: certSignature } = proto.NoiseCertificate.decode(certDecoded) + + const { issuer: certIssuer, key: certKey } = proto.Details.decode(certDetails) + + if(Buffer.compare(decStaticContent, certKey) !== 0) { + throw new Boom('certification match failed', { statusCode: 400 }) + } + + const keyEnc = encrypt(noiseKey.public) + mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!)) + + return keyEnc + }, + encodeFrame: (data: Buffer | Uint8Array) => { + if(isFinished) { + data = encrypt(data) + } + + const introSize = sentIntro ? 0 : NOISE_WA_HEADER.length + + outBinary.ensureAdditionalCapacity(introSize + 3 + data.byteLength) + + if(!sentIntro) { + outBinary.writeByteArray(NOISE_WA_HEADER) + sentIntro = true + } + + outBinary.writeUint8(data.byteLength >> 16) + outBinary.writeUint16(65535 & data.byteLength) + outBinary.write(data) + + const bytes = outBinary.readByteArray() + return bytes as Uint8Array + }, + decodeFrame: (newData: Buffer | Uint8Array, onFrame: (buff: Uint8Array | BinaryNode) => void) => { + // the binary protocol uses its own framing mechanism + // on top of the WS frames + // so we get this data and separate out the frames + const getBytesSize = () => { + return (inBinary.readUint8() << 16) | inBinary.readUint16() + } + + const peekSize = () => { + return !(inBinary.size() < 3) && getBytesSize() <= inBinary.size() + } + + inBinary.writeByteArray(newData) + while(inBinary.peek(peekSize)) { + const bytes = getBytesSize() + let frame: Uint8Array | BinaryNode = inBinary.readByteArray(bytes) + if(isFinished) { + const result = decrypt(frame as Uint8Array) + const unpacked = new Binary(result).decompressed() + frame = decodeBinaryNode(unpacked) + } + + onFrame(frame) + } + + inBinary.peek(peekSize) + } + } +} \ No newline at end of file diff --git a/src/Utils/signal.ts b/src/Utils/signal.ts new file mode 100644 index 0000000..7b0dfa2 --- /dev/null +++ b/src/Utils/signal.ts @@ -0,0 +1,276 @@ +import * as libsignal from 'libsignal' +import { proto } from '../../WAProto' +import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage, SenderKeyName, SenderKeyRecord } from '../../WASignalGroup' +import { AuthenticationCreds, KeyPair, SignalAuthState, SignalIdentity, SignalKeyStore, SignedKeyPair } from '../Types/Auth' +import { assertNodeErrorFree, BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildUInt, jidDecode, JidWithDevice } from '../WABinary' +import { Curve } from './crypto' +import { encodeBigEndian } from './generics' + +export const generateSignalPubKey = (pubKey: Uint8Array | Buffer) => { + const newPub = Buffer.alloc(33) + newPub.set([5], 0) + newPub.set(pubKey, 1) + return newPub +} + +const jidToSignalAddress = (jid: string) => jid.split('@')[0] + +export const jidToSignalProtocolAddress = (jid: string) => { + return new libsignal.ProtocolAddress(jidToSignalAddress(jid), 0) +} + +export const jidToSignalSenderKeyName = (group: string, user: string): string => { + return new SenderKeyName(group, jidToSignalProtocolAddress(user)).toString() +} + +export const createSignalIdentity = ( + wid: string, + accountSignatureKey: Uint8Array +): SignalIdentity => { + return { + identifier: { name: wid, deviceId: 0 }, + identifierKey: generateSignalPubKey(accountSignatureKey) + } +} + +export const getPreKeys = async({ get }: SignalKeyStore, min: number, limit: number) => { + const idList: string[] = [] + for(let id = min; id < limit;id++) { + idList.push(id.toString()) + } + + return get('pre-key', idList) +} + +export const generateOrGetPreKeys = (creds: AuthenticationCreds, range: number) => { + const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId + const remaining = range - avaliable + const lastPreKeyId = creds.nextPreKeyId + remaining - 1 + const newPreKeys: { [id: number]: KeyPair } = { } + if(remaining > 0) { + for(let i = creds.nextPreKeyId;i <= lastPreKeyId;i++) { + newPreKeys[i] = Curve.generateKeyPair() + } + } + + return { + newPreKeys, + lastPreKeyId, + preKeysRange: [creds.firstUnuploadedPreKeyId, range] as const, + } +} + + +export const xmppSignedPreKey = (key: SignedKeyPair): BinaryNode => ( + { + tag: 'skey', + attrs: { }, + content: [ + { tag: 'id', attrs: { }, content: encodeBigEndian(key.keyId, 3) }, + { tag: 'value', attrs: { }, content: key.keyPair.public }, + { tag: 'signature', attrs: { }, content: key.signature } + ] + } +) + +export const xmppPreKey = (pair: KeyPair, id: number): BinaryNode => ( + { + tag: 'key', + attrs: { }, + content: [ + { tag: 'id', attrs: { }, content: encodeBigEndian(id, 3) }, + { tag: 'value', attrs: { }, content: pair.public } + ] + } +) + +export const signalStorage = ({ creds, keys }: SignalAuthState) => ({ + loadSession: async(id: string) => { + const { [id]: sess } = await keys.get('session', [id]) + if(sess) { + return libsignal.SessionRecord.deserialize(sess) + } + }, + storeSession: async(id, session) => { + await keys.set({ 'session': { [id]: session.serialize() } }) + }, + isTrustedIdentity: () => { + return true + }, + loadPreKey: async(id: number | string) => { + const keyId = id.toString() + const { [keyId]: key } = await keys.get('pre-key', [keyId]) + if(key) { + return { + privKey: Buffer.from(key.private), + pubKey: Buffer.from(key.public) + } + } + }, + removePreKey: (id: number) => keys.set({ 'pre-key': { [id]: null } }), + loadSignedPreKey: (keyId: number) => { + const key = creds.signedPreKey + return { + privKey: Buffer.from(key.keyPair.private), + pubKey: Buffer.from(key.keyPair.public) + } + }, + loadSenderKey: async(keyId: string) => { + const { [keyId]: key } = await keys.get('sender-key', [keyId]) + if(key) { + return new SenderKeyRecord(key) + } + }, + storeSenderKey: async(keyId, key) => { + await keys.set({ 'sender-key': { [keyId]: key.serialize() } }) + }, + getOurRegistrationId: () => ( + creds.registrationId + ), + getOurIdentity: () => { + const { signedIdentityKey } = creds + return { + privKey: Buffer.from(signedIdentityKey.private), + pubKey: generateSignalPubKey(signedIdentityKey.public), + } + } +}) + +export const decryptGroupSignalProto = (group: string, user: string, msg: Buffer | Uint8Array, auth: SignalAuthState) => { + const senderName = jidToSignalSenderKeyName(group, user) + const cipher = new GroupCipher(signalStorage(auth), senderName) + + return cipher.decrypt(Buffer.from(msg)) +} + +export const processSenderKeyMessage = async( + authorJid: string, + item: proto.ISenderKeyDistributionMessage, + auth: SignalAuthState +) => { + const builder = new GroupSessionBuilder(signalStorage(auth)) + const senderName = jidToSignalSenderKeyName(item.groupId, authorJid) + + const senderMsg = new SenderKeyDistributionMessage(null, null, null, null, item.axolotlSenderKeyDistributionMessage) + const { [senderName]: senderKey } = await auth.keys.get('sender-key', [senderName]) + if(!senderKey) { + const record = new SenderKeyRecord() + await auth.keys.set({ 'sender-key': { [senderName]: record } }) + } + + await builder.process(senderName, senderMsg) +} + +export const decryptSignalProto = async(user: string, type: 'pkmsg' | 'msg', msg: Buffer | Uint8Array, auth: SignalAuthState) => { + const addr = jidToSignalProtocolAddress(user) + const session = new libsignal.SessionCipher(signalStorage(auth), addr) + let result: Buffer + switch (type) { + case 'pkmsg': + result = await session.decryptPreKeyWhisperMessage(msg) + break + case 'msg': + result = await session.decryptWhisperMessage(msg) + break + } + + return result +} + + +export const encryptSignalProto = async(user: string, buffer: Buffer, auth: SignalAuthState) => { + const addr = jidToSignalProtocolAddress(user) + const cipher = new libsignal.SessionCipher(signalStorage(auth), addr) + + const { type, body } = await cipher.encrypt(buffer) + return { + type: type === 3 ? 'pkmsg' : 'msg', + ciphertext: Buffer.from(body, 'binary') + } +} + +export const encryptSenderKeyMsgSignalProto = async(group: string, data: Uint8Array | Buffer, meId: string, auth: SignalAuthState) => { + const storage = signalStorage(auth) + const senderName = jidToSignalSenderKeyName(group, meId) + const builder = new GroupSessionBuilder(storage) + + const { [senderName]: senderKey } = await auth.keys.get('sender-key', [senderName]) + if(!senderKey) { + const record = new SenderKeyRecord() + await auth.keys.set({ 'sender-key': { [senderName]: record } }) + } + + const senderKeyDistributionMessage = await builder.create(senderName) + const session = new GroupCipher(storage, senderName) + return { + ciphertext: await session.encrypt(data) as Uint8Array, + senderKeyDistributionMessageKey: senderKeyDistributionMessage.serialize() as Buffer, + } +} + +export const parseAndInjectE2ESessions = async(node: BinaryNode, auth: SignalAuthState) => { + const extractKey = (key: BinaryNode) => ( + key ? ({ + keyId: getBinaryNodeChildUInt(key, 'id', 3), + publicKey: generateSignalPubKey( + getBinaryNodeChildBuffer(key, 'value') + ), + signature: getBinaryNodeChildBuffer(key, 'signature'), + }) : undefined + ) + const nodes = getBinaryNodeChildren(getBinaryNodeChild(node, 'list'), 'user') + for(const node of nodes) { + assertNodeErrorFree(node) + } + + await Promise.all( + nodes.map( + async node => { + const signedKey = getBinaryNodeChild(node, 'skey') + const key = getBinaryNodeChild(node, 'key') + const identity = getBinaryNodeChildBuffer(node, 'identity') + const jid = node.attrs.jid + const registrationId = getBinaryNodeChildUInt(node, 'registration', 4) + + const device = { + registrationId, + identityKey: generateSignalPubKey(identity), + signedPreKey: extractKey(signedKey), + preKey: extractKey(key) + } + const cipher = new libsignal.SessionBuilder(signalStorage(auth), jidToSignalProtocolAddress(jid)) + await cipher.initOutgoing(device) + } + ) + ) +} + +export const extractDeviceJids = (result: BinaryNode, myJid: string, excludeZeroDevices: boolean) => { + const { user: myUser, device: myDevice } = jidDecode(myJid) + const extracted: JidWithDevice[] = [] + for(const node of result.content as BinaryNode[]) { + const list = getBinaryNodeChild(node, 'list')?.content + if(list && Array.isArray(list)) { + for(const item of list) { + const { user } = jidDecode(item.attrs.jid) + const devicesNode = getBinaryNodeChild(item, 'devices') + const deviceListNode = getBinaryNodeChild(devicesNode, 'device-list') + if(Array.isArray(deviceListNode?.content)) { + for(const { tag, attrs } of deviceListNode!.content) { + const device = +attrs.id + if( + tag === 'device' && // ensure the "device" tag + (!excludeZeroDevices || device !== 0) && // if zero devices are not-excluded, or device is non zero + (myUser !== user || myDevice !== device) && // either different user or if me user, not this device + (device === 0 || !!attrs['key-index']) // ensure that "key-index" is specified for "non-zero" devices, produces a bad req otherwise + ) { + extracted.push({ user, device }) + } + } + } + } + } + } + + return extracted +} \ No newline at end of file diff --git a/src/Utils/validate-connection.ts b/src/Utils/validate-connection.ts new file mode 100644 index 0000000..8656493 --- /dev/null +++ b/src/Utils/validate-connection.ts @@ -0,0 +1,152 @@ +import { Boom } from '@hapi/boom' +import { proto } from '../../WAProto' +import type { AuthenticationCreds, SignalCreds, SocketConfig } from '../Types' +import { Binary, BinaryNode, getAllBinaryNodeChildren, jidDecode, S_WHATSAPP_NET } from '../WABinary' +import { Curve, hmacSign } from './crypto' +import { encodeInt } from './generics' +import { createSignalIdentity } from './signal' + +const ENCODED_VERSION = 'S9Kdc4pc4EJryo21snc5cg==' +const getUserAgent = ({ version, browser }: Pick) => ({ + appVersion: { + primary: version[0], + secondary: version[1], + tertiary: version[2], + }, + platform: 14, + releaseChannel: 0, + mcc: '000', + mnc: '000', + osVersion: browser[2], + manufacturer: '', + device: browser[1], + osBuildNumber: '0.1', + localeLanguageIso6391: 'en', + localeCountryIso31661Alpha2: 'en', +}) + +export const generateLoginNode = (userJid: string, config: Pick) => { + const { user, device } = jidDecode(userJid) + const payload = { + passive: true, + connectType: 1, + connectReason: 1, + userAgent: getUserAgent(config), + webInfo: { webSubPlatform: 0 }, + username: parseInt(user, 10), + device: device, + } + return proto.ClientPayload.encode(payload).finish() +} + +export const generateRegistrationNode = ( + { registrationId, signedPreKey, signedIdentityKey }: SignalCreds, + config: Pick +) => { + const appVersionBuf = new Uint8Array(Buffer.from(ENCODED_VERSION, 'base64')) + + const companion = { + os: config.browser[0], + version: { + primary: 10, + secondary: undefined, + tertiary: undefined, + }, + platformType: 1, + requireFullSync: false, + } + + const companionProto = proto.CompanionProps.encode(companion).finish() + + const registerPayload = { + connectReason: 1, + connectType: 1, + passive: false, + regData: { + buildHash: appVersionBuf, + companionProps: companionProto, + eRegid: encodeInt(4, registrationId), + eKeytype: encodeInt(1, 5), + eIdent: signedIdentityKey.public, + eSkeyId: encodeInt(3, signedPreKey.keyId), + eSkeyVal: signedPreKey.keyPair.public, + eSkeySig: signedPreKey.signature, + }, + userAgent: getUserAgent(config), + webInfo: { + webSubPlatform: 0, + }, + } + + return proto.ClientPayload.encode(registerPayload).finish() +} + +export const configureSuccessfulPairing = ( + stanza: BinaryNode, + { advSecretKey, signedIdentityKey, signalIdentities }: Pick +) => { + const [pair] = getAllBinaryNodeChildren(stanza) + const pairContent = Array.isArray(pair.content) ? pair.content : [] + + const msgId = stanza.attrs.id + const deviceIdentity = pairContent.find(m => m.tag === 'device-identity')?.content + const businessName = pairContent.find(m => m.tag === 'biz')?.attrs?.name + const verifiedName = businessName || '' + const jid = pairContent.find(m => m.tag === 'device')?.attrs?.jid + + const { details, hmac } = proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentity as Buffer) + + const advSign = hmacSign(details, Buffer.from(advSecretKey, 'base64')) + + if(Buffer.compare(hmac, advSign) !== 0) { + throw new Boom('Invalid pairing') + } + + const account = proto.ADVSignedDeviceIdentity.decode(details) + const { accountSignatureKey, accountSignature } = account + + const accountMsg = Binary.build(new Uint8Array([6, 0]), account.details, signedIdentityKey.public).readByteArray() + if(!Curve.verify(accountSignatureKey, accountMsg, accountSignature)) { + throw new Boom('Failed to verify account signature') + } + + const deviceMsg = Binary.build(new Uint8Array([6, 1]), account.details, signedIdentityKey.public, account.accountSignatureKey).readByteArray() + account.deviceSignature = Curve.sign(signedIdentityKey.private, deviceMsg) + + const identity = createSignalIdentity(jid, accountSignatureKey) + + const keyIndex = proto.ADVDeviceIdentity.decode(account.details).keyIndex + + const accountEnc = proto.ADVSignedDeviceIdentity.encode({ + ...account.toJSON(), + accountSignatureKey: undefined + }).finish() + + const reply: BinaryNode = { + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'result', + id: msgId, + }, + content: [ + { + tag: 'pair-device-sign', + attrs: { }, + content: [ + { tag: 'device-identity', attrs: { 'key-index': `${keyIndex}` }, content: accountEnc } + ] + } + ] + } + + const authUpdate: Partial = { + account, + me: { id: jid, verifiedName }, + signalIdentities: [...(signalIdentities || []), identity] + } + return { + creds: authUpdate, + reply + } +} \ No newline at end of file diff --git a/src/WABinary/Legacy/constants.ts b/src/WABinary/Legacy/constants.ts new file mode 100644 index 0000000..a9be6d6 --- /dev/null +++ b/src/WABinary/Legacy/constants.ts @@ -0,0 +1,198 @@ + +export const Tags = { + LIST_EMPTY: 0, + STREAM_END: 2, + DICTIONARY_0: 236, + DICTIONARY_1: 237, + DICTIONARY_2: 238, + DICTIONARY_3: 239, + LIST_8: 248, + LIST_16: 249, + JID_PAIR: 250, + HEX_8: 251, + BINARY_8: 252, + BINARY_20: 253, + BINARY_32: 254, + NIBBLE_8: 255, + SINGLE_BYTE_MAX: 256, + PACKED_MAX: 254, +} +export const DoubleByteTokens = [] +export const SingleByteTokens = [ + null, + null, + null, + '200', + '400', + '404', + '500', + '501', + '502', + 'action', + 'add', + 'after', + 'archive', + 'author', + 'available', + 'battery', + 'before', + 'body', + 'broadcast', + 'chat', + 'clear', + 'code', + 'composing', + 'contacts', + 'count', + 'create', + 'debug', + 'delete', + 'demote', + 'duplicate', + 'encoding', + 'error', + 'false', + 'filehash', + 'from', + 'g.us', + 'group', + 'groups_v2', + 'height', + 'id', + 'image', + 'in', + 'index', + 'invis', + 'item', + 'jid', + 'kind', + 'last', + 'leave', + 'live', + 'log', + 'media', + 'message', + 'mimetype', + 'missing', + 'modify', + 'name', + 'notification', + 'notify', + 'out', + 'owner', + 'participant', + 'paused', + 'picture', + 'played', + 'presence', + 'preview', + 'promote', + 'query', + 'raw', + 'read', + 'receipt', + 'received', + 'recipient', + 'recording', + 'relay', + 'remove', + 'response', + 'resume', + 'retry', + 's.whatsapp.net', + 'seconds', + 'set', + 'size', + 'status', + 'subject', + 'subscribe', + 't', + 'text', + 'to', + 'true', + 'type', + 'unarchive', + 'unavailable', + 'url', + 'user', + 'value', + 'web', + 'width', + 'mute', + 'read_only', + 'admin', + 'creator', + 'short', + 'update', + 'powersave', + 'checksum', + 'epoch', + 'block', + 'previous', + '409', + 'replaced', + 'reason', + 'spam', + 'modify_tag', + 'message_info', + 'delivery', + 'emoji', + 'title', + 'description', + 'canonical-url', + 'matched-text', + 'star', + 'unstar', + 'media_key', + 'filename', + 'identity', + 'unread', + 'page', + 'page_count', + 'search', + 'media_message', + 'security', + 'call_log', + 'profile', + 'ciphertext', + 'invite', + 'gif', + 'vcard', + 'frequent', + 'privacy', + 'blacklist', + 'whitelist', + 'verify', + 'location', + 'document', + 'elapsed', + 'revoke_invite', + 'expiration', + 'unsubscribe', + 'disable', + 'vname', + 'old_jid', + 'new_jid', + 'announcement', + 'locked', + 'prop', + 'label', + 'color', + 'call', + 'offer', + 'call-id', + 'quick_reply', + 'sticker', + 'pay_t', + 'accept', + 'reject', + 'sticker_pack', + 'invalid', + 'canceled', + 'missed', + 'connected', + 'result', + 'audio', + 'video', + 'recent', +] \ No newline at end of file diff --git a/src/WABinary/Legacy/index.ts b/src/WABinary/Legacy/index.ts new file mode 100644 index 0000000..b73cf28 --- /dev/null +++ b/src/WABinary/Legacy/index.ts @@ -0,0 +1,376 @@ + +import { BinaryNode } from '../types' +import { DoubleByteTokens, SingleByteTokens, Tags } from './constants' + +export const isLegacyBinaryNode = (buffer: Buffer) => { + switch (buffer[0]) { + case Tags.LIST_EMPTY: + case Tags.LIST_8: + case Tags.LIST_16: + return true + default: + return false + } +} + +function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode { + + const checkEOS = (length: number) => { + if(indexRef.index + length > buffer.length) { + throw new Error('end of stream') + } + } + + const next = () => { + const value = buffer[indexRef.index] + indexRef.index += 1 + return value + } + + const readByte = () => { + checkEOS(1) + return next() + } + + const readStringFromChars = (length: number) => { + checkEOS(length) + const value = buffer.slice(indexRef.index, indexRef.index + length) + + indexRef.index += length + return value.toString('utf-8') + } + + const readBytes = (n: number) => { + checkEOS(n) + const value = buffer.slice(indexRef.index, indexRef.index + n) + indexRef.index += n + return value + } + + const readInt = (n: number, littleEndian = false) => { + checkEOS(n) + let val = 0 + for(let i = 0; i < n; i++) { + const shift = littleEndian ? i : n - 1 - i + val |= next() << (shift * 8) + } + + return val + } + + const readInt20 = () => { + checkEOS(3) + return ((next() & 15) << 16) + (next() << 8) + next() + } + + const unpackHex = (value: number) => { + if(value >= 0 && value < 16) { + return value < 10 ? '0'.charCodeAt(0) + value : 'A'.charCodeAt(0) + value - 10 + } + + throw new Error('invalid hex: ' + value) + } + + const unpackNibble = (value: number) => { + if(value >= 0 && value <= 9) { + return '0'.charCodeAt(0) + value + } + + switch (value) { + case 10: + return '-'.charCodeAt(0) + case 11: + return '.'.charCodeAt(0) + case 15: + return '\0'.charCodeAt(0) + default: + throw new Error('invalid nibble: ' + value) + } + } + + const unpackByte = (tag: number, value: number) => { + if(tag === Tags.NIBBLE_8) { + return unpackNibble(value) + } else if(tag === Tags.HEX_8) { + return unpackHex(value) + } else { + throw new Error('unknown tag: ' + tag) + } + } + + const readPacked8 = (tag: number) => { + const startByte = readByte() + let value = '' + + for(let i = 0; i < (startByte & 127); i++) { + const curByte = readByte() + value += String.fromCharCode(unpackByte(tag, (curByte & 0xf0) >> 4)) + value += String.fromCharCode(unpackByte(tag, curByte & 0x0f)) + } + + if(startByte >> 7 !== 0) { + value = value.slice(0, -1) + } + + return value + } + + const isListTag = (tag: number) => { + return tag === Tags.LIST_EMPTY || tag === Tags.LIST_8 || tag === Tags.LIST_16 + } + + const readListSize = (tag: number) => { + switch (tag) { + case Tags.LIST_EMPTY: + return 0 + case Tags.LIST_8: + return readByte() + case Tags.LIST_16: + return readInt(2) + default: + throw new Error('invalid tag for list size: ' + tag) + } + } + + const getToken = (index: number) => { + if(index < 3 || index >= SingleByteTokens.length) { + throw new Error('invalid token index: ' + index) + } + + return SingleByteTokens[index] + } + + const readString = (tag: number) => { + if(tag >= 3 && tag <= 235) { + const token = getToken(tag) + return token// === 's.whatsapp.net' ? 'c.us' : token + } + + switch (tag) { + case Tags.DICTIONARY_0: + case Tags.DICTIONARY_1: + case Tags.DICTIONARY_2: + case Tags.DICTIONARY_3: + return getTokenDouble(tag - Tags.DICTIONARY_0, readByte()) + case Tags.LIST_EMPTY: + return null + case Tags.BINARY_8: + return readStringFromChars(readByte()) + case Tags.BINARY_20: + return readStringFromChars(readInt20()) + case Tags.BINARY_32: + return readStringFromChars(readInt(4)) + case Tags.JID_PAIR: + const i = readString(readByte()) + const j = readString(readByte()) + if(typeof i === 'string' && j) { + return i + '@' + j + } + + throw new Error('invalid jid pair: ' + i + ', ' + j) + case Tags.HEX_8: + case Tags.NIBBLE_8: + return readPacked8(tag) + default: + throw new Error('invalid string with tag: ' + tag) + } + } + + const readList = (tag: number) => ( + [...new Array(readListSize(tag))].map(() => decode(buffer, indexRef)) + ) + const getTokenDouble = (index1: number, index2: number) => { + const n = 256 * index1 + index2 + if(n < 0 || n > DoubleByteTokens.length) { + throw new Error('invalid double token index: ' + n) + } + + return DoubleByteTokens[n] + } + + const listSize = readListSize(readByte()) + const descrTag = readByte() + if(descrTag === Tags.STREAM_END) { + throw new Error('unexpected stream end') + } + + const header = readString(descrTag) + const attrs: BinaryNode['attrs'] = { } + let data: BinaryNode['content'] + if(listSize === 0 || !header) { + throw new Error('invalid node') + } + // read the attributes in + + const attributesLength = (listSize - 1) >> 1 + for(let i = 0; i < attributesLength; i++) { + const key = readString(readByte()) + const b = readByte() + + attrs[key] = readString(b) + } + + if(listSize % 2 === 0) { + const tag = readByte() + if(isListTag(tag)) { + data = readList(tag) + } else { + let decoded: Buffer | string + switch (tag) { + case Tags.BINARY_8: + decoded = readBytes(readByte()) + break + case Tags.BINARY_20: + decoded = readBytes(readInt20()) + break + case Tags.BINARY_32: + decoded = readBytes(readInt(4)) + break + default: + decoded = readString(tag) + break + } + + data = decoded + } + } + + return { + tag: header, + attrs, + content: data + } +} + +const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => { + + const pushByte = (value: number) => buffer.push(value & 0xff) + + const pushInt = (value: number, n: number, littleEndian=false) => { + for(let i = 0; i < n; i++) { + const curShift = littleEndian ? i : n - 1 - i + buffer.push((value >> (curShift * 8)) & 0xff) + } + } + + const pushBytes = (bytes: Uint8Array | Buffer | number[]) => ( + bytes.forEach (b => buffer.push(b)) + ) + const pushInt20 = (value: number) => ( + pushBytes([(value >> 16) & 0x0f, (value >> 8) & 0xff, value & 0xff]) + ) + const writeByteLength = (length: number) => { + if(length >= 4294967296) { + throw new Error('string too large to encode: ' + length) + } + + if(length >= 1 << 20) { + pushByte(Tags.BINARY_32) + pushInt(length, 4) // 32 bit integer + } else if(length >= 256) { + pushByte(Tags.BINARY_20) + pushInt20(length) + } else { + pushByte(Tags.BINARY_8) + pushByte(length) + } + } + + const writeStringRaw = (str: string) => { + const bytes = Buffer.from (str, 'utf-8') + writeByteLength(bytes.length) + pushBytes(bytes) + } + + const writeToken = (token: number) => { + if(token < 245) { + pushByte(token) + } else if(token <= 500) { + throw new Error('invalid token') + } + } + + const writeString = (token: string, i?: boolean) => { + if(token === 'c.us') { + token = 's.whatsapp.net' + } + + const tokenIndex = SingleByteTokens.indexOf(token) + if(!i && token === 's.whatsapp.net') { + writeToken(tokenIndex) + } else if(tokenIndex >= 0) { + if(tokenIndex < Tags.SINGLE_BYTE_MAX) { + writeToken(tokenIndex) + } else { + const overflow = tokenIndex - Tags.SINGLE_BYTE_MAX + const dictionaryIndex = overflow >> 8 + if(dictionaryIndex < 0 || dictionaryIndex > 3) { + throw new Error('double byte dict token out of range: ' + token + ', ' + tokenIndex) + } + + writeToken(Tags.DICTIONARY_0 + dictionaryIndex) + writeToken(overflow % 256) + } + } else if(token) { + const jidSepIndex = token.indexOf('@') + if(jidSepIndex <= 0) { + writeStringRaw(token) + } else { + writeJid(token.slice(0, jidSepIndex), token.slice(jidSepIndex + 1, token.length)) + } + } + } + + const writeJid = (left: string, right: string) => { + pushByte(Tags.JID_PAIR) + left && left.length > 0 ? writeString(left) : writeToken(Tags.LIST_EMPTY) + writeString(right) + } + + const writeListStart = (listSize: number) => { + if(listSize === 0) { + pushByte(Tags.LIST_EMPTY) + } else if(listSize < 256) { + pushBytes([Tags.LIST_8, listSize]) + } else { + pushBytes([Tags.LIST_16, listSize]) + } + } + + const validAttributes = Object.keys(attrs).filter(k => ( + typeof attrs[k] !== 'undefined' && attrs[k] !== null + )) + + writeListStart(2*validAttributes.length + 1 + (typeof content !== 'undefined' && content !== null ? 1 : 0)) + writeString(tag) + + validAttributes.forEach((key) => { + if(typeof attrs[key] === 'string') { + writeString(key) + writeString(attrs[key]) + } + }) + + if(typeof content === 'string') { + writeString(content, true) + } else if(Buffer.isBuffer(content)) { + writeByteLength(content.length) + pushBytes(content) + } else if(Array.isArray(content)) { + writeListStart(content.length) + for(const item of content) { + if(item) { + encode(item, buffer) + } + } + } else if(typeof content === 'undefined' || content === null) { + + } else { + throw new Error(`invalid children for header "${tag}": ${content} (${typeof content})`) + } + + return Buffer.from(buffer) +} + +export const encodeBinaryNodeLegacy = encode +export const decodeBinaryNodeLegacy = decode diff --git a/src/WABinary/generic-utils.ts b/src/WABinary/generic-utils.ts new file mode 100644 index 0000000..82d601c --- /dev/null +++ b/src/WABinary/generic-utils.ts @@ -0,0 +1,81 @@ +import { Boom } from '@hapi/boom' +import { proto } from '../../WAProto' +import { BinaryNode } from './types' + +// some extra useful utilities + +export const getBinaryNodeChildren = ({ content }: BinaryNode, childTag: string) => { + if(Array.isArray(content)) { + return content.filter(item => item.tag === childTag) + } + + return [] +} + +export const getAllBinaryNodeChildren = ({ content }: BinaryNode) => { + if(Array.isArray(content)) { + return content + } + + return [] +} + +export const getBinaryNodeChild = ({ content }: BinaryNode, childTag: string) => { + if(Array.isArray(content)) { + return content.find(item => item.tag === childTag) + } +} + +export const getBinaryNodeChildBuffer = (node: BinaryNode, childTag: string) => { + const child = getBinaryNodeChild(node, childTag)?.content + if(Buffer.isBuffer(child) || child instanceof Uint8Array) { + return child + } +} + +export const getBinaryNodeChildUInt = (node: BinaryNode, childTag: string, length: number) => { + const buff = getBinaryNodeChildBuffer(node, childTag) + if(buff) { + return bufferToUInt(buff, length) + } +} + +export const assertNodeErrorFree = (node: BinaryNode) => { + const errNode = getBinaryNodeChild(node, 'error') + if(errNode) { + throw new Boom(errNode.attrs.text || 'Unknown error', { data: +errNode.attrs.code }) + } +} + +export const reduceBinaryNodeToDictionary = (node: BinaryNode, tag: string) => { + const nodes = getBinaryNodeChildren(node, tag) + const dict = nodes.reduce( + (dict, { attrs }) => { + dict[attrs.name || attrs.config_code] = attrs.value || attrs.config_value + return dict + }, { } as { [_: string]: string } + ) + return dict +} + +export const getBinaryNodeMessages = ({ content }: BinaryNode) => { + const msgs: proto.WebMessageInfo[] = [] + if(Array.isArray(content)) { + for(const item of content) { + if(item.tag === 'message') { + msgs.push(proto.WebMessageInfo.decode(item.content as Buffer)) + } + } + } + + return msgs +} + +function bufferToUInt(e: Uint8Array | Buffer, t: number) { + let a = 0 + for(let i = 0; i < t; i++) { + a = 256 * a + e[i] + } + + return a +} \ No newline at end of file diff --git a/src/WABinary/index.ts b/src/WABinary/index.ts new file mode 100644 index 0000000..3595df2 --- /dev/null +++ b/src/WABinary/index.ts @@ -0,0 +1,326 @@ +import { DICTIONARIES_MAP, SINGLE_BYTE_TOKEN, SINGLE_BYTE_TOKEN_MAP, DICTIONARIES } from '../../WABinary/Constants'; +import { jidDecode, jidEncode } from './jid-utils'; +import { Binary, numUtf8Bytes } from '../../WABinary/Binary'; +import { Boom } from '@hapi/boom'; +import { proto } from '../../WAProto'; +import { BinaryNode } from './types'; + +const LIST1 = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', '�', '�', '�', '�']; +const LIST2 = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; + +function k(data: Binary, uint: number) { + let arr = []; + for (let a = 0; a < uint; a++) { + arr.push(decodeBinaryNode(data)); + } + return arr; +} + +function x(data: Binary, t, r, a) { + const arr = new Array(2 * a - r); + for (let n = 0; n < arr.length - 1; n += 2) { + var s = data.readUint8(); + (arr[n] = t[s >>> 4]), (arr[n + 1] = t[15 & s]); + } + + if (r) { + arr[arr.length - 1] = t[data.readUint8() >>> 4]; + } + + return arr.join(''); +} + +function D(e, t, r) { + var a = e.length % 2 == 1; + r.writeUint8(t); + var i = Math.ceil(e.length / 2); + a && (i |= 128), r.writeUint8(i); + for (var n = 0, s = 0; s < e.length; s++) { + var o = e.charCodeAt(s), + l = null; + if ((48 <= o && o <= 57 ? (l = o - 48) : 255 === t ? (45 === o ? (l = 10) : 46 === o && (l = 11)) : 251 === t && 65 <= o && o <= 70 && (l = o - 55), null == l)) + throw new Error(`Cannot nibble encode ${o}`); + s % 2 == 0 ? ((n = l << 4), s === e.length - 1 && ((n |= 15), r.writeUint8(n))) : ((n |= l), r.writeUint8(n)); + } +} + +function N(e, t) { + if (e < 256) t.writeUint8(252), t.writeUint8(e); + else if (e < 1048576) t.writeUint8(253), t.writeUint8((e >>> 16) & 255), t.writeUint8((e >>> 8) & 255), t.writeUint8(255 & e); + else { + if (!(e < 4294967296)) throw new Error(`Binary with length ${e} is too big for WAP protocol`); + t.writeUint8(254), t.writeUint32(e); + } +} + +function R(e: any, t: Binary) { + var w = null; + if ('' === e) return t.writeUint8(252), void t.writeUint8(0); + var b = SINGLE_BYTE_TOKEN_MAP; + var r = b.get(e); + var c = [236, 237, 238, 239]; + if (null == r) { + if (null == w) { + w = []; + for (var a = 0; a < DICTIONARIES_MAP.length; ++a) w.push(DICTIONARIES_MAP[a]); + } + for (var n = 0; n < w.length; ++n) { + var s = w[n].get(e); + if (null != s) return t.writeUint8(c[n]), void t.writeUint8(s); + } + var o = numUtf8Bytes(e); + if (o < 128) { + if (!/[^0-9.-]+?/.exec(e)) return void D(e, 255, t); + if (!/[^0-9A-F]+?/.exec(e)) return void D(e, 251, t); + } + N(o, t), t.writeString(e); + } else t.writeUint8(r + 1); +} + +function M(e: any, t: Binary) { + var p = 248; + var f = 249; + if (void 0 === e.tag) return t.writeUint8(p), void t.writeUint8(0); + var r = 1; + e.attrs && (r += 2 * Object.keys(e.attrs).length), + e.content && r++, + r < 256 ? (t.writeUint8(p), t.writeUint8(r)) : r < 65536 && (t.writeUint8(f), t.writeUint16(r)), + O(e.tag, t), + e.attrs && + Object.keys(e.attrs).forEach((r) => { + R(r, t), O(e.attrs[r], t); + }); + var a = e.content; + if (Array.isArray(a)) { + a.length < 256 ? (t.writeUint8(p), t.writeUint8(a.length)) : a.length < 65536 && (t.writeUint8(f), t.writeUint16(a.length)); + for (var i = 0; i < a.length; i++) M(a[i], t); + } else a && O(a, t); +} + +function L(data: Binary, t: boolean) { + const n = data.readUint8(); + if (n === 0) { + return null; + } + + if (n === 248) { + return k(data, data.readUint8()); + } + + if (n === 249) { + return k(data, data.readUint16()); + } + + if (n === 252) { + return t ? data.readString(data.readUint8()) : data.readByteArray(data.readUint8()); + } + + if (n === 253) { + const size = ((15 & data.readUint8()) << 16) + (data.readUint8() << 8) + data.readUint8(); + return t ? data.readString(size) : data.readByteArray(size); + } + + if (n === 254) { + return t ? data.readString(data.readUint32()) : data.readByteArray(data.readUint32()); + } + + if (n === 250) { + const user = L(data, true); + if (null != user && 'string' != typeof user) throw new Error(`Decode string got invalid value ${String(t)}, string expected`); + const server = decodeStanzaString(data) + return jidEncode(user, server) + } + + if (n === 247) { + const agent = data.readUint8(); + const device = data.readUint8(); + const user = decodeStanzaString(data); + + return jidEncode(user, 's.whatsapp.net', device, agent); + } + + if (n === 255) { + const number = data.readUint8(); + return x(data, LIST1, number >>> 7, 127 & number); + } + + if (n === 251) { + const number = data.readUint8(); + return x(data, LIST2, number >>> 7, 127 & number); + } + + if (n <= 0 || n >= 240) { + throw new Error('Unable to decode WAP buffer'); + } + + if (n >= 236 && n <= 239) { + const dict = DICTIONARIES[n - 236]; + if (!dict) { + throw new Error(`Missing WAP dictionary ${n - 236}`); + } + + const index = data.readUint8(); + const value = dict[index]; + if (!value) { + throw new Error(`Invalid value index ${index} in dict ${n - 236}`); + } + + return value; + } + + const singleToken = SINGLE_BYTE_TOKEN[n - 1]; + if (!singleToken) throw new Error(`Undefined token with index ${n}`); + + return singleToken; +} + +function O(e: any, t: Binary) { + if (null == e) t.writeUint8(0); + else if (typeof e === 'object' && !(e instanceof Uint8Array) && !Buffer.isBuffer(e) && !Array.isArray(e)) M(e, t); + else if ('string' == typeof e) { + const jid = jidDecode(e) + if(jid) { + if(typeof jid.agent !== 'undefined' || typeof jid.device !== 'undefined') { + var { user: a, agent: i, device: n } = jid; + t.writeUint8(247), t.writeUint8(i || 0), t.writeUint8(n || 0), O(a, t); + } else { + var { user: s, server: l } = jid; + t.writeUint8(250), null != s ? O(s, t) : t.writeUint8(0), O(l, t); + } + } else { + R(e, t); + } + } else { + if (!(e instanceof Uint8Array)) throw new Error('Invalid payload type ' + typeof e); + N(e.length, t), t.writeByteArray(e); + } +} + +function decodeStanzaString(data: Binary) { + // G + const t = L(data, true); + if (typeof t != 'string') { + throw new Error(`Decode string got invalid value ${String(t)}, string expected`); + } + + return t; +} + +function bufferToUInt(e: Uint8Array | Buffer, t: number) { + let a = 0 + for (let i = 0; i < t; i++) a = 256 * a + e[i] + return a +} + +export const decodeBinaryNode = (data: Binary): BinaryNode => { + //U + let r = data.readUint8(); + let t = r === 248 ? data.readUint8() : data.readUint16(); + + if (!t) { + throw new Error('Failed to decode node, list cannot be empty'); + } + + const a = {}; + + const n = decodeStanzaString(data); + for (t -= 1; t > 1; ) { + const s = decodeStanzaString(data); + const l = L(data, true); + a[s] = l; + t -= 2; + } + + let i = null; + 1 === t && jidDecode(i = L(data, !1)) && (i = String(i)); + + return { + tag: n, + attrs: a, + content: i + } +} + +export const encodeBinaryNode = (node: BinaryNode) => { + const data = new Binary(); + + O(node, data); + + const dataArr = data.readByteArray(); + const result = new Uint8Array(1 + dataArr.length); + + result[0] = 0; + result.set(dataArr, 1); + + return result; +} + +// some extra useful utilities + +export const getBinaryNodeChildren = ({ content }: BinaryNode, childTag: string) => { + if(Array.isArray(content)) { + return content.filter(item => item.tag == childTag) + } + return [] +} + +export const getAllBinaryNodeChildren = ({ content }: BinaryNode) => { + if(Array.isArray(content)) { + return content + } + return [] +} + +export const getBinaryNodeChild = ({ content }: BinaryNode, childTag: string) => { + if(Array.isArray(content)) { + return content.find(item => item.tag == childTag) + } +} + +export const getBinaryNodeChildBuffer = (node: BinaryNode, childTag: string) => { + const child = getBinaryNodeChild(node, childTag)?.content + if(Buffer.isBuffer(child) || child instanceof Uint8Array) { + return child + } +} + +export const getBinaryNodeChildUInt = (node: BinaryNode, childTag: string, length: number) => { + const buff = getBinaryNodeChildBuffer(node, childTag) + if(buff) return bufferToUInt(buff, length) +} + +export const assertNodeErrorFree = (node: BinaryNode) => { + const errNode = getBinaryNodeChild(node, 'error') + if(errNode) { + throw new Boom(errNode.attrs.text || 'Unknown error', { data: +errNode.attrs.code }) + } +} + +export const reduceBinaryNodeToDictionary = (node: BinaryNode, tag: string) => { + const nodes = getBinaryNodeChildren(node, tag) + const dict = nodes.reduce( + (dict, { attrs }) => { + dict[attrs.name || attrs.config_code] = attrs.value || attrs.config_value + return dict + }, { } as { [_: string]: string } + ) + return dict +} + +export const getBinaryNodeMessages = ({ content }: BinaryNode) => { + const msgs: proto.WebMessageInfo[] = [] + if(Array.isArray(content)) { + for(const item of content) { + if(item.tag === 'message') { + msgs.push(proto.WebMessageInfo.decode(item.content as Buffer)) + } + } + } + return msgs +} + +export * from './generic-utils' +export * from './jid-utils' +export { Binary } from '../../WABinary/Binary' +export * from './types' +export * from './Legacy' \ No newline at end of file diff --git a/src/WABinary/jid-utils.ts b/src/WABinary/jid-utils.ts new file mode 100644 index 0000000..266482f --- /dev/null +++ b/src/WABinary/jid-utils.ts @@ -0,0 +1,54 @@ +export const S_WHATSAPP_NET = '@s.whatsapp.net' +export const OFFICIAL_BIZ_JID = '16505361212@c.us' +export const SERVER_JID = 'server@c.us' +export const PSA_WID = '0@c.us' +export const STORIES_JID = 'status@broadcast' + +export type JidServer = 'c.us' | 'g.us' | 'broadcast' | 's.whatsapp.net' | 'call' + +export type JidWithDevice = { + user: string + device?: number +} + +export const jidEncode = (user: string | number | null, server: JidServer, device?: number, agent?: number) => { + return `${user || ''}${!!agent ? `_${agent}` : ''}${!!device ? `:${device}` : ''}@${server}` +} + +export const jidDecode = (jid: string) => { + const sepIdx = typeof jid === 'string' ? jid.indexOf('@') : -1 + if(sepIdx < 0) { + return undefined + } + + const server = jid.slice(sepIdx+1) + const userCombined = jid.slice(0, sepIdx) + + const [userAgent, device] = userCombined.split(':') + const [user, agent] = userAgent.split('_') + + return { + server, + user, + agent: agent ? +agent : undefined, + device: device ? +device : undefined + } +} + +/** is the jid a user */ +export const areJidsSameUser = (jid1: string, jid2: string) => ( + jidDecode(jid1)?.user === jidDecode(jid2)?.user +) +/** is the jid a user */ +export const isJidUser = (jid: string) => (jid?.endsWith('@s.whatsapp.net')) +/** is the jid a broadcast */ +export const isJidBroadcast = (jid: string) => (jid?.endsWith('@broadcast')) +/** is the jid a broadcast */ +export const isJidGroup = (jid: string) => (jid?.endsWith('@g.us')) +/** is the jid the status broadcast */ +export const isJidStatusBroadcast = (jid: string) => jid === 'status@broadcast' + +export const jidNormalizedUser = (jid: string) => { + const { user, server } = jidDecode(jid) + return jidEncode(user, server === 'c.us' ? 's.whatsapp.net' : server as JidServer) +} \ No newline at end of file diff --git a/src/WABinary/types.ts b/src/WABinary/types.ts new file mode 100644 index 0000000..66714f9 --- /dev/null +++ b/src/WABinary/types.ts @@ -0,0 +1,14 @@ +/** + * the binary node WA uses internally for communication + * + * this is manipulated soley as an object and it does not have any functions. + * This is done for easy serialization, to prevent running into issues with prototypes & + * to maintain functional code structure + * */ +export type BinaryNode = { + tag: string + attrs: { [key: string]: string } + content?: BinaryNode[] | string | Uint8Array +} +export type BinaryNodeAttributes = BinaryNode['attrs'] +export type BinaryNodeData = BinaryNode['content'] \ No newline at end of file diff --git a/src/WAConnection/0.Base.ts b/src/WAConnection/0.Base.ts deleted file mode 100644 index d320304..0000000 --- a/src/WAConnection/0.Base.ts +++ /dev/null @@ -1,478 +0,0 @@ -import WS from 'ws' -import * as fs from 'fs' -import * as Utils from './Utils' -import Encoder from '../Binary/Encoder' -import Decoder from '../Binary/Decoder' -import got, { Method } from 'got' -import { - AuthenticationCredentials, - WAUser, - WANode, - WATag, - BaileysError, - WAMetric, - WAFlag, - DisconnectReason, - WAConnectionState, - AnyAuthenticationCredentials, - WAContact, - WAQuery, - ReconnectMode, - WAConnectOptions, - MediaConnInfo, - DEFAULT_ORIGIN, -} from './Constants' -import { EventEmitter } from 'events' -import KeyedDB from '@adiwajshing/keyed-db' -import { STATUS_CODES } from 'http' -import { Agent } from 'https' -import pino from 'pino' - -const logger = pino({ prettyPrint: { levelFirst: true, ignore: 'hostname', translateTime: true }, prettifier: require('pino-pretty') }) - -export class WAConnection extends EventEmitter { - /** The version of WhatsApp Web we're telling the servers we are */ - version: [number, number, number] = [2, 2149, 4] - /** The Browser we're telling the WhatsApp Web servers we are */ - browserDescription: [string, string, string] = Utils.Browsers.baileys ('Chrome') - /** Metadata like WhatsApp id, name set on WhatsApp etc. */ - user: WAUser - /** Should requests be queued when the connection breaks in between; if 0, then an error will be thrown */ - pendingRequestTimeoutMs: number = null - /** The connection state */ - state: WAConnectionState = 'close' - connectOptions: WAConnectOptions = { - maxIdleTimeMs: 60_000, - maxRetries: 10, - connectCooldownMs: 4000, - phoneResponseTime: 15_000, - maxQueryResponseTime: 10_000, - alwaysUseTakeover: true, - queryChatsTillReceived: true, - logQR: true - } - /** When to auto-reconnect */ - autoReconnect = ReconnectMode.onConnectionLost - /** Whether the phone is connected */ - phoneConnected: boolean = false - /** key to use to order chats */ - chatOrderingKey = Utils.waChatKey(false) - - logger = logger.child ({ class: 'Baileys' }) - - /** log messages */ - shouldLogMessages = false - messageLog: { tag: string, json: string, fromMe: boolean, binaryTags?: any[] }[] = [] - - maxCachedMessages = 50 - - lastChatsReceived: Date - chats = new KeyedDB (Utils.waChatKey(false), value => value.jid) - contacts: { [k: string]: WAContact } = {} - blocklist: string[] = [] - - /** Data structure of tokens & IDs used to establish one's identiy to WhatsApp Web */ - protected authInfo: AuthenticationCredentials - /** Curve keys to initially authenticate */ - protected curveKeys: { private: Uint8Array; public: Uint8Array } - /** The websocket connection */ - protected conn: WS - protected msgCount = 0 - protected keepAliveReq: NodeJS.Timeout - protected encoder = new Encoder() - protected decoder = new Decoder() - protected phoneCheckInterval - protected phoneCheckListeners = 0 - - protected referenceDate = new Date () // used for generating tags - protected lastSeen: Date = null // last keep alive received - protected initTimeout: NodeJS.Timeout - - protected lastDisconnectTime: Date = null - protected lastDisconnectReason: DisconnectReason - - protected mediaConn: MediaConnInfo - protected connectionDebounceTimeout = Utils.debouncedTimeout( - 1000, - () => this.state === 'connecting' && this.endConnection(DisconnectReason.timedOut) - ) - // timeout to know when we're done recieving messages - protected messagesDebounceTimeout = Utils.debouncedTimeout(2000) - // ping chats till recieved - protected chatsDebounceTimeout = Utils.debouncedTimeout(10_000) - /** - * Connect to WhatsAppWeb - * @param options the connect options - */ - async connect() { - return null - } - async unexpectedDisconnect (error: DisconnectReason) { - if (this.state === 'open') { - const willReconnect = - (this.autoReconnect === ReconnectMode.onAllErrors || - (this.autoReconnect === ReconnectMode.onConnectionLost && error !== DisconnectReason.replaced)) && - error !== DisconnectReason.invalidSession // do not reconnect if credentials have been invalidated - - this.closeInternal(error, willReconnect) - willReconnect && ( - this.connect() - .catch(err => {}) // prevent unhandled exeception - ) - } else { - this.endConnection(error) - } - } - /** - * base 64 encode the authentication credentials and return them - * these can then be used to login again by passing the object to the connect () function. - * @see connect () in WhatsAppWeb.Session - */ - base64EncodedAuthInfo() { - return { - clientID: this.authInfo.clientID, - serverToken: this.authInfo.serverToken, - clientToken: this.authInfo.clientToken, - encKey: this.authInfo.encKey.toString('base64'), - macKey: this.authInfo.macKey.toString('base64'), - } - } - /** Can you login to WA without scanning the QR */ - canLogin () { - return !!this.authInfo?.encKey && !!this.authInfo?.macKey - } - /** Clear authentication info so a new connection can be created */ - clearAuthInfo () { - this.authInfo = null - return this - } - /** - * Load in the authentication credentials - * @param authInfo the authentication credentials or file path to auth credentials - */ - loadAuthInfo(authInfo: AnyAuthenticationCredentials | string) { - if (!authInfo) throw new Error('given authInfo is null') - - if (typeof authInfo === 'string') { - this.logger.info(`loading authentication credentials from ${authInfo}`) - const file = fs.readFileSync(authInfo, { encoding: 'utf-8' }) // load a closed session back if it exists - authInfo = JSON.parse(file) as AnyAuthenticationCredentials - } - if ('clientID' in authInfo) { - this.authInfo = { - clientID: authInfo.clientID, - serverToken: authInfo.serverToken, - clientToken: authInfo.clientToken, - encKey: Buffer.isBuffer(authInfo.encKey) ? authInfo.encKey : Buffer.from(authInfo.encKey, 'base64'), - macKey: Buffer.isBuffer(authInfo.macKey) ? authInfo.macKey : Buffer.from(authInfo.macKey, 'base64'), - } - } else { - const secretBundle: {encKey: string, macKey: string} = typeof authInfo.WASecretBundle === 'string' ? JSON.parse (authInfo.WASecretBundle): authInfo.WASecretBundle - this.authInfo = { - clientID: authInfo.WABrowserId.replace(/\"/g, ''), - serverToken: authInfo.WAToken2.replace(/\"/g, ''), - clientToken: authInfo.WAToken1.replace(/\"/g, ''), - encKey: Buffer.from(secretBundle.encKey, 'base64'), // decode from base64 - macKey: Buffer.from(secretBundle.macKey, 'base64'), // decode from base64 - } - } - return this - } - /** - * Wait for a message with a certain tag to be received - * @param tag the message tag to await - * @param json query that was sent - * @param timeoutMs timeout after which the promise will reject - */ - async waitForMessage(tag: string, requiresPhoneConnection: boolean, timeoutMs?: number) { - let onRecv: (json) => void - let onErr: (err) => void - let cancelPhoneChecker: () => void - if (requiresPhoneConnection) { - this.startPhoneCheckInterval() - cancelPhoneChecker = this.exitQueryIfResponseNotExpected(tag, err => onErr(err)) - } - try { - const result = await Utils.promiseTimeout(timeoutMs, - (resolve, reject) => { - onRecv = resolve - onErr = ({ reason, status }) => reject(new BaileysError(reason, { status })) - this.on (`TAG:${tag}`, onRecv) - this.on ('ws-close', onErr) // if the socket closes, you'll never receive the message - }, - ) - return result as any - } finally { - requiresPhoneConnection && this.clearPhoneCheckInterval() - this.off (`TAG:${tag}`, onRecv) - this.off (`ws-close`, onErr) - cancelPhoneChecker && cancelPhoneChecker() - } - } - /** Generic function for action, set queries */ - async setQuery (nodes: WANode[], binaryTags: WATag = [WAMetric.group, WAFlag.ignore], tag?: string) { - const json = ['action', {epoch: this.msgCount.toString(), type: 'set'}, nodes] - const result = await this.query({ json, binaryTags, tag, expect200: true, requiresPhoneConnection: true }) as Promise<{status: number}> - return result - } - /** - * Query something from the WhatsApp servers - * @param json the query itself - * @param binaryTags the tags to attach if the query is supposed to be sent encoded in binary - * @param timeoutMs timeout after which the query will be failed (set to null to disable a timeout) - * @param tag the tag to attach to the message - */ - async query(q: WAQuery): Promise { - let {json, binaryTags, tag, timeoutMs, expect200, waitForOpen, longTag, requiresPhoneConnection, startDebouncedTimeout, maxRetries} = q - requiresPhoneConnection = requiresPhoneConnection !== false - waitForOpen = waitForOpen !== false - let triesLeft = maxRetries || 2 - tag = tag || this.generateMessageTag(longTag) - - while (triesLeft >= 0) { - if (waitForOpen) await this.waitForConnection() - - const promise = this.waitForMessage(tag, requiresPhoneConnection, timeoutMs) - - if (this.logger.level === 'trace') { - this.logger.trace ({ fromMe: true },`${tag},${JSON.stringify(json)}`) - } - - if (binaryTags) tag = await this.sendBinary(json as WANode, binaryTags, tag) - else tag = await this.sendJSON(json, tag) - - try { - const response = await promise - if (expect200 && response.status && Math.floor(+response.status / 100) !== 2) { - const message = STATUS_CODES[response.status] || 'unknown' - throw new BaileysError ( - `Unexpected status in '${json[0] || 'query'}': ${STATUS_CODES[response.status]}(${response.status})`, - {query: json, message, status: response.status} - ) - } - if (startDebouncedTimeout) { - this.connectionDebounceTimeout.start() - } - return response - } catch (error) { - if (triesLeft === 0) { - throw error - } - // read here: http://getstatuscode.com/599 - if (error.status === 599) { - this.unexpectedDisconnect (DisconnectReason.badSession) - } else if ( - (error.message === 'close' || error.message === 'lost') && - waitForOpen && - this.state !== 'close' && - (this.pendingRequestTimeoutMs === null || - this.pendingRequestTimeoutMs > 0)) { - // nothing here - } else throw error - - triesLeft -= 1 - this.logger.debug(`query failed due to ${error}, retrying...`) - } - } - } - protected exitQueryIfResponseNotExpected(tag: string, cancel: ({ reason, status }) => void) { - let timeout: NodeJS.Timeout - const listener = ({ connected }) => { - if(connected) { - timeout = setTimeout(() => { - this.logger.info({ tag }, `cancelling wait for message as a response is no longer expected from the phone`) - cancel({ reason: 'Not expecting a response', status: 422 }) - }, this.connectOptions.maxQueryResponseTime) - this.off('connection-phone-change', listener) - } - } - this.on('connection-phone-change', listener) - return () => { - this.off('connection-phone-change', listener) - timeout && clearTimeout(timeout) - } - } - /** interval is started when a query takes too long to respond */ - protected startPhoneCheckInterval () { - this.phoneCheckListeners += 1 - if (!this.phoneCheckInterval) { - // if its been a long time and we haven't heard back from WA, send a ping - this.phoneCheckInterval = setInterval (() => { - if (!this.conn) return // if disconnected, then don't do anything - - this.logger.info('checking phone connection...') - this.sendAdminTest () - if(this.phoneConnected !== false) { - this.phoneConnected = false - this.emit ('connection-phone-change', { connected: false }) - } - }, this.connectOptions.phoneResponseTime) - } - - } - protected clearPhoneCheckInterval () { - this.phoneCheckListeners -= 1 - if (this.phoneCheckListeners <= 0) { - this.phoneCheckInterval && clearInterval (this.phoneCheckInterval) - this.phoneCheckInterval = undefined - this.phoneCheckListeners = 0 - } - - } - /** checks for phone connection */ - protected async sendAdminTest () { - return this.sendJSON (['admin', 'test']) - } - /** - * Send a binary encoded message - * @param json the message to encode & send - * @param tags the binary tags to tell WhatsApp what the message is all about - * @param tag the tag to attach to the message - * @return the message tag - */ - protected async sendBinary(json: WANode, tags: WATag, tag: string = null, longTag: boolean = false) { - const binary = this.encoder.write(json) // encode the JSON to the WhatsApp binary format - - let 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 - tag = tag || this.generateMessageTag(longTag) - - if (this.shouldLogMessages) this.messageLog.push ({ tag, json: JSON.stringify(json), fromMe: true, binaryTags: tags }) - - buff = Buffer.concat([ - 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 - ]) - await this.send(buff) // send it off - return tag - } - /** - * Send a plain JSON message to the WhatsApp servers - * @param json the message to send - * @param tag the tag to attach to the message - * @returns the message tag - */ - protected async sendJSON(json: any[] | WANode, tag: string = null, longTag: boolean = false) { - tag = tag || this.generateMessageTag(longTag) - if (this.shouldLogMessages) this.messageLog.push ({ tag, json: JSON.stringify(json), fromMe: true }) - await this.send(`${tag},${JSON.stringify(json)}`) - return tag - } - /** Send some message to the WhatsApp servers */ - protected async send(m) { - this.conn.send(m) - } - protected async waitForConnection () { - if (this.state === 'open') return - - let onOpen: () => void - let onClose: ({ reason }) => void - - if (this.pendingRequestTimeoutMs !== null && this.pendingRequestTimeoutMs <= 0) { - throw new BaileysError(DisconnectReason.close, { status: 428 }) - } - await ( - Utils.promiseTimeout ( - this.pendingRequestTimeoutMs, - (resolve, reject) => { - onClose = ({ reason }) => { - if (reason === DisconnectReason.invalidSession || reason === DisconnectReason.intentional) { - reject (new Error(reason)) - } - } - onOpen = resolve - this.on ('close', onClose) - this.on ('open', onOpen) - } - ) - .finally(() => { - this.off ('open', onOpen) - this.off ('close', onClose) - }) - ) - } - /** - * Disconnect from the phone. Your auth credentials become invalid after sending a disconnect request. - * @see close() if you just want to close the connection - */ - async logout () { - this.authInfo = null - if (this.state === 'open') { - //throw new Error("You're not even connected, you can't log out") - await new Promise(resolve => this.conn.send('goodbye,["admin","Conn","disconnect"]', null, resolve)) - } - this.user = undefined - this.chats.clear() - this.contacts = {} - this.close() - } - /** Close the connection to WhatsApp Web */ - close () { - this.closeInternal (DisconnectReason.intentional) - } - protected closeInternal (reason?: DisconnectReason, isReconnecting: boolean=false) { - this.logger.info (`closed connection, reason ${reason}${isReconnecting ? ', reconnecting in a few seconds...' : ''}`) - - this.state = 'close' - this.phoneConnected = false - this.lastDisconnectReason = reason - this.lastDisconnectTime = new Date () - - this.endConnection(reason) - // reconnecting if the timeout is active for the reconnect loop - this.emit ('close', { reason, isReconnecting }) - } - protected endConnection (reason: DisconnectReason) { - this.conn?.removeAllListeners ('close') - this.conn?.removeAllListeners ('error') - this.conn?.removeAllListeners ('open') - this.conn?.removeAllListeners ('message') - - this.initTimeout && clearTimeout (this.initTimeout) - this.connectionDebounceTimeout.cancel() - this.messagesDebounceTimeout.cancel() - this.chatsDebounceTimeout.cancel() - this.keepAliveReq && clearInterval(this.keepAliveReq) - this.phoneCheckListeners = 0 - this.clearPhoneCheckInterval () - - this.emit ('ws-close', { reason }) - - try { - this.conn?.close() - //this.conn?.terminate() - } catch { - - } - this.conn = undefined - this.lastSeen = undefined - this.msgCount = 0 - } - /** - * Does a fetch request with the configuration of the connection - */ - protected fetchRequest = ( - endpoint: string, - method: Method = 'GET', - body?: any, - agent?: Agent, - headers?: {[k: string]: string}, - followRedirect = true - ) => ( - got(endpoint, { - method, - body, - followRedirect, - headers: { Origin: DEFAULT_ORIGIN, ...(headers || {}) }, - agent: { https: agent || this.connectOptions.fetchAgent } - }) - ) - generateMessageTag (longTag: boolean = false) { - const seconds = Utils.unixTimestampSeconds(this.referenceDate) - const tag = `${longTag ? seconds : (seconds%1000)}.--${this.msgCount}` - this.msgCount += 1 // increment message count, it makes the 'epoch' field when sending binary messages - return tag - } -} diff --git a/src/WAConnection/1.Validation.ts b/src/WAConnection/1.Validation.ts deleted file mode 100644 index 7bbaaec..0000000 --- a/src/WAConnection/1.Validation.ts +++ /dev/null @@ -1,224 +0,0 @@ -import * as Curve from 'curve25519-js' -import * as Utils from './Utils' -import {WAConnection as Base} from './0.Base' -import { WAMetric, WAFlag, BaileysError, Presence, WAUser, WAInitResponse, WAOpenResult } from './Constants' - -export class WAConnection extends Base { - - /** Authenticate the connection */ - protected async authenticate (reconnect?: string) { - // if no auth info is present, that is, a new session has to be established - // generate a client ID - if (!this.authInfo?.clientID) { - this.authInfo = { clientID: Utils.generateClientID() } as any - } - const canLogin = this.canLogin() - this.referenceDate = new Date () // refresh reference date - - this.connectionDebounceTimeout.start() - - const initQuery = (async () => { - const {ref, ttl} = await this.query({ - json: ['admin', 'init', this.version, this.browserDescription, this.authInfo?.clientID, true], - expect200: true, - waitForOpen: false, - longTag: true, - requiresPhoneConnection: false, - startDebouncedTimeout: true - }) as WAInitResponse - - if (!canLogin) { - this.connectionDebounceTimeout.cancel() // stop the debounced timeout for QR gen - this.generateKeysForAuth (ref, ttl) - } - })(); - let loginTag: string - if (canLogin) { - // if we have the info to restore a closed session - const json = [ - 'admin', - 'login', - this.authInfo?.clientToken, - this.authInfo?.serverToken, - this.authInfo?.clientID, - ] - loginTag = this.generateMessageTag(true) - - if (reconnect) json.push(...['reconnect', reconnect.replace('@s.whatsapp.net', '@c.us')]) - else json.push ('takeover') - // send login every 10s - const sendLoginReq = () => { - if (!this.conn || this.conn?.readyState !== this.conn.OPEN) { - this.logger.warn('Received login timeout req when WS not open, ignoring...') - return - } - if (this.state === 'open') { - this.logger.warn('Received login timeout req when state=open, ignoring...') - return - } - this.logger.debug('sending login request') - this.sendJSON(json, loginTag) - this.initTimeout = setTimeout(sendLoginReq, 10_000) - } - sendLoginReq() - } - - await initQuery - - // wait for response with tag "s1" - let response = await Promise.race( - [ - this.waitForMessage('s1', false, undefined), - loginTag && this.waitForMessage(loginTag, false, undefined) - ] - .filter(Boolean) - ) - this.connectionDebounceTimeout.start() - this.initTimeout && clearTimeout (this.initTimeout) - this.initTimeout = null - - if (response.status && response.status !== 200) { - throw new BaileysError(`Unexpected error in login`, { response, status: response.status }) - } - // if its a challenge request (we get it when logging in) - if (response[1]?.challenge) { - await this.respondToChallenge(response[1].challenge) - response = await this.waitForMessage('s2', true) - } - - const result = this.validateNewConnection(response[1])// validate the connection - if (result.user.jid !== this.user?.jid) { - result.isNewUser = true - // clear out old data - this.chats.clear() - this.contacts = {} - } - this.user = result.user - - this.logger.info('validated connection successfully') - - return result - } - /** - * Refresh QR Code - * @returns the new ref - */ - async requestNewQRCodeRef() { - const response = await this.query({ - json: ['admin', 'Conn', 'reref'], - expect200: true, - waitForOpen: false, - longTag: true, - requiresPhoneConnection: false - }) - return response as WAInitResponse - } - /** - * Once the QR code is scanned and we can validate our connection, or we resolved the challenge when logging back in - * @private - * @param {object} json - */ - private validateNewConnection(json) { - // set metadata: one's WhatsApp ID [cc][number]@s.whatsapp.net, name on WhatsApp, info about the phone - const onValidationSuccess = () => ({ - user: { - jid: Utils.whatsappID(json.wid), - name: json.pushname, - phone: json.phone, - imgUrl: null - }, - auth: this.authInfo - }) as WAOpenResult - - if (!json.secret) { - // if we didn't get a secret, we don't need it, we're validated - if (json.clientToken && json.clientToken !== this.authInfo.clientToken) { - this.authInfo = { ...this.authInfo, clientToken: json.clientToken } - } - if (json.serverToken && json.serverToken !== this.authInfo.serverToken) { - this.authInfo = { ...this.authInfo, serverToken: json.serverToken } - } - return onValidationSuccess() - } - const secret = Buffer.from(json.secret, 'base64') - if (secret.length !== 144) { - throw new Error ('incorrect secret length received: ' + secret.length) - } - - // 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 = Utils.hkdf(sharedKey as Buffer, 80) - - // perform HMAC validation. - const hmacValidationKey = expandedKey.slice(32, 64) - const hmacValidationMessage = Buffer.concat([secret.slice(0, 32), secret.slice(64, secret.length)]) - - const hmac = Utils.hmacSign(hmacValidationMessage, hmacValidationKey) - - if (!hmac.equals(secret.slice(32, 64))) { - // if the checksums didn't match - throw new BaileysError ('HMAC validation failed', json) - } - - // computed HMAC should equal secret[32:64] - // expandedKey[64:] + secret[64:] are the keys, encrypted using AES, that are used to encrypt/decrypt the messages recieved from WhatsApp - // they are encrypted using key: expandedKey[0:32] - const encryptedAESKeys = Buffer.concat([ - expandedKey.slice(64, expandedKey.length), - secret.slice(64, secret.length), - ]) - const decryptedKeys = Utils.aesDecrypt(encryptedAESKeys, expandedKey.slice(0, 32)) - // set the credentials - this.authInfo = { - encKey: decryptedKeys.slice(0, 32), // first 32 bytes form the key to encrypt/decrypt messages - macKey: decryptedKeys.slice(32, 64), // last 32 bytes from the key to sign messages - clientToken: json.clientToken, - serverToken: json.serverToken, - clientID: this.authInfo.clientID, - } - return onValidationSuccess() - } - /** - * When logging back in (restoring a previously closed session), WhatsApp may challenge one to check if one still has the encryption keys - * WhatsApp does that by asking for us to sign a string it sends with our macKey - */ - protected respondToChallenge(challenge: string) { - const bytes = Buffer.from(challenge, 'base64') // decode the base64 encoded challenge string - const signed = Utils.hmacSign(bytes, this.authInfo.macKey).toString('base64') // sign the challenge string with our macKey - const json = ['admin', 'challenge', signed, this.authInfo.serverToken, this.authInfo.clientID] // prepare to send this signed string with the serverToken & clientID - - this.logger.info('resolving login challenge') - return this.query({json, expect200: true, waitForOpen: false, startDebouncedTimeout: true}) - } - /** When starting a new session, generate a QR code by generating a private/public key pair & the keys the server sends */ - protected generateKeysForAuth(ref: string, ttl?: number) { - this.curveKeys = Curve.generateKeyPair(Utils.randomBytes(32)) - const publicKey = Buffer.from(this.curveKeys.public).toString('base64') - - const qrLoop = ttl => { - const qr = [ref, publicKey, this.authInfo.clientID].join(',') - this.emit ('qr', qr) - - this.initTimeout = setTimeout (async () => { - if (this.state === 'open') return - - this.logger.debug ('regenerating QR') - try { - const {ref: newRef, ttl: newTTL} = await this.requestNewQRCodeRef() - ttl = newTTL - ref = newRef - } catch (error) { - this.logger.warn ({ error }, `error in QR gen`) - // @ts-ignore - if (error.status === 429 && this.state !== 'open') { // too many QR requests - this.endConnection(error.message) - return - } - } - qrLoop (ttl) - }, ttl || 20_000) // default is 20s, on the off-chance ttl is not present - } - qrLoop (ttl) - } -} diff --git a/src/WAConnection/3.Connect.ts b/src/WAConnection/3.Connect.ts deleted file mode 100644 index 0dc4f56..0000000 --- a/src/WAConnection/3.Connect.ts +++ /dev/null @@ -1,196 +0,0 @@ -import * as Utils from './Utils' -import { KEEP_ALIVE_INTERVAL_MS, BaileysError, WAConnectOptions, DisconnectReason, UNAUTHORIZED_CODES, CancelledError, WAOpenResult, DEFAULT_ORIGIN, WS_URL } from './Constants' -import {WAConnection as Base} from './1.Validation' -import Decoder from '../Binary/Decoder' -import WS from 'ws' - -const DEF_CALLBACK_PREFIX = 'CB:' -const DEF_TAG_PREFIX = 'TAG:' - -export class WAConnection extends Base { - /** Connect to WhatsApp Web */ - async connect () { - // if we're already connected, throw an error - if (this.state !== 'close') { - throw new BaileysError('cannot connect when state=' + this.state, { status: 409 }) - } - - const options = this.connectOptions - const newConnection = !this.authInfo - - this.state = 'connecting' - this.emit ('connecting') - - let tries = 0 - let lastConnect = this.lastDisconnectTime - let result: WAOpenResult - while (this.state === 'connecting') { - tries += 1 - try { - const diff = lastConnect ? new Date().getTime()-lastConnect.getTime() : Infinity - result = await this.connectInternal ( - options, - diff > this.connectOptions.connectCooldownMs ? 0 : this.connectOptions.connectCooldownMs - ) - this.phoneConnected = true - this.state = 'open' - } catch (error) { - lastConnect = new Date() - - const loggedOut = error instanceof BaileysError && UNAUTHORIZED_CODES.includes(error.status) - const willReconnect = !loggedOut && (tries < options?.maxRetries) && (this.state === 'connecting') - const reason = loggedOut ? DisconnectReason.invalidSession : error.message - - this.logger.warn ({ error }, `connect attempt ${tries} failed: ${error}${ willReconnect ? ', retrying...' : ''}`) - - if ((this.state as string) !== 'close' && !willReconnect) { - this.closeInternal (reason) - } - if (!willReconnect) throw error - } - } - if (newConnection) result.newConnection = newConnection - this.emit ('open', result) - - this.logger.info ('opened connection to WhatsApp Web') - - this.conn.on ('close', () => this.unexpectedDisconnect (DisconnectReason.close)) - - return result - } - /** Meat of the connect logic */ - protected async connectInternal (options: WAConnectOptions, delayMs?: number) { - const rejections: ((e?: Error) => void)[] = [] - const rejectAll = (e: Error) => rejections.forEach (r => r(e)) - const rejectAllOnWSClose = ({ reason }) => rejectAll(new Error(reason)) - // actual connect - const connect = () => ( - new Promise((resolve, reject) => { - rejections.push (reject) - // determine whether reconnect should be used or not - const shouldUseReconnect = (this.lastDisconnectReason === DisconnectReason.close || - this.lastDisconnectReason === DisconnectReason.lost) && - !this.connectOptions.alwaysUseTakeover - const reconnectID = shouldUseReconnect && this.user.jid.replace ('@s.whatsapp.net', '@c.us') - - this.conn = new WS(WS_URL, null, { - origin: DEFAULT_ORIGIN, - timeout: this.connectOptions.maxIdleTimeMs, - agent: options.agent, - headers: { - 'Accept-Encoding': 'gzip, deflate, br', - 'Accept-Language': 'en-US,en;q=0.9', - 'Cache-Control': 'no-cache', - 'Host': 'web.whatsapp.com', - 'Pragma': 'no-cache', - 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits', - } - }) - - this.conn.on('message', data => this.onMessageRecieved(data as any)) - - this.conn.once('open', async () => { - this.startKeepAliveRequest() - this.logger.info(`connected to WhatsApp Web server, authenticating via ${reconnectID ? 'reconnect' : 'takeover'}`) - - try { - this.connectionDebounceTimeout.setInterval(this.connectOptions.maxIdleTimeMs) - const authResult = await this.authenticate(reconnectID) - - this.conn - .removeAllListeners('error') - .removeAllListeners('close') - this.connectionDebounceTimeout.start() - resolve(authResult as WAOpenResult) - } catch (error) { - reject(error) - } - }) - this.conn.on('error', rejectAll) - this.conn.on('close', () => rejectAll(new Error(DisconnectReason.close))) - }) as Promise - ) - - this.on ('ws-close', rejectAllOnWSClose) - try { - if (delayMs) { - const {delay, cancel} = Utils.delayCancellable (delayMs) - rejections.push (cancel) - await delay - } - const result = await connect () - return result - } catch (error) { - if (this.conn) { - this.endConnection(error.message) - } - throw error - } finally { - this.off ('ws-close', rejectAllOnWSClose) - } - } - private onMessageRecieved(message: string | Buffer) { - if (message[0] === '!') { - // when the first character in the message is an '!', the server is sending a pong frame - const timestamp = message.slice(1, message.length).toString ('utf-8') - this.lastSeen = new Date(parseInt(timestamp)) - this.emit ('received-pong') - } else { - let messageTag: string - let json: any - try { - const dec = Utils.decryptWA (message, this.authInfo?.macKey, this.authInfo?.encKey, new Decoder()) - messageTag = dec[0] - json = dec[1] - } catch (error) { - this.logger.error ({ error }, `encountered error in decrypting message, closing: ${error}`) - - this.unexpectedDisconnect(DisconnectReason.badSession) - } - - if (this.shouldLogMessages) this.messageLog.push ({ tag: messageTag, json: JSON.stringify(json), fromMe: false }) - if (!json) return - - if (this.logger.level === 'trace') { - this.logger.trace(messageTag + ',' + JSON.stringify(json)) - } - - let anyTriggered = false - /* Check if this is a response to a message we sent */ - anyTriggered = this.emit (`${DEF_TAG_PREFIX}${messageTag}`, json) - /* Check if this is a response to a message we are expecting */ - const l0 = json[0] || '' - const l1 = typeof json[1] !== 'object' || json[1] === null ? {} : json[1] - const l2 = ((json[2] || [])[0] || [])[0] || '' - - Object.keys(l1).forEach(key => { - anyTriggered = this.emit (`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, json) || anyTriggered; - anyTriggered = this.emit (`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, json) || anyTriggered; - anyTriggered = this.emit (`${DEF_CALLBACK_PREFIX}${l0},${key}`, json) || anyTriggered; - }) - anyTriggered = this.emit (`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, json) || anyTriggered; - anyTriggered = this.emit (`${DEF_CALLBACK_PREFIX}${l0}`, json) || anyTriggered; - - if (anyTriggered) return - - if (this.logger.level === 'debug') { - this.logger.debug({ unhandled: true }, messageTag + ',' + JSON.stringify(json)) - } - } - } - /** Send a keep alive request every X seconds, server updates & responds with last seen */ - private startKeepAliveRequest() { - this.keepAliveReq && clearInterval (this.keepAliveReq) - - this.keepAliveReq = setInterval(() => { - if (!this.lastSeen) this.lastSeen = new Date () - const diff = new Date().getTime() - this.lastSeen.getTime() - /* - check if it's been a suspicious amount of time since the server responded with our last seen - it could be that the network is down - */ - if (diff > KEEP_ALIVE_INTERVAL_MS+5000) this.unexpectedDisconnect(DisconnectReason.lost) - else if (this.conn) this.send('?,,') // if its all good, send a keep alive request - }, KEEP_ALIVE_INTERVAL_MS) - } -} diff --git a/src/WAConnection/4.Events.ts b/src/WAConnection/4.Events.ts deleted file mode 100644 index 03ec04e..0000000 --- a/src/WAConnection/4.Events.ts +++ /dev/null @@ -1,713 +0,0 @@ -import * as QR from 'qrcode-terminal' -import { WAConnection as Base } from './3.Connect' -import { WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, PresenceUpdate, BaileysEvent, DisconnectReason, WAOpenResult, Presence, WAParticipantAction, WAGroupMetadata, WANode, WAPresenceData, WAChatUpdate, BlocklistUpdate, WAContactUpdate, WAMetric, WAFlag } from './Constants' -import { whatsappID, unixTimestampSeconds, GET_MESSAGE_ID, WA_MESSAGE_ID, newMessagesDB, shallowChanges, toNumber, isGroupID } from './Utils' -import KeyedDB from '@adiwajshing/keyed-db' -import { Mutex } from './Mutex' - -export class WAConnection extends Base { - - constructor () { - super () - this.setMaxListeners (30) - this.chatsDebounceTimeout.setTask(() => { - this.logger.debug('pinging with chats query') - this.sendChatsQuery(this.msgCount) - - this.chatsDebounceTimeout.start() - }) - this.on('open', () => { - // send queries WA Web expects - this.sendBinary (['query', {type: 'contacts', epoch: '1'}, null], [ WAMetric.queryContact, WAFlag.ignore ]) - this.sendBinary (['query', {type: 'status', epoch: '1'}, null], [ WAMetric.queryStatus, WAFlag.ignore ]) - this.sendBinary (['query', {type: 'quick_reply', epoch: '1'}, null], [ WAMetric.queryQuickReply, WAFlag.ignore ]) - this.sendBinary (['query', {type: 'label', epoch: '1'}, null], [ WAMetric.queryLabel, WAFlag.ignore ]) - this.sendBinary (['query', {type: 'emoji', epoch: '1'}, null], [ WAMetric.queryEmoji, WAFlag.ignore ]) - this.sendBinary (['action', {type: 'set', epoch: '1'}, [['presence', {type: Presence.available}, null]] ], [ WAMetric.presence, WAFlag.available ]) - - if(this.connectOptions.queryChatsTillReceived) { - this.chatsDebounceTimeout.start() - } else { - this.sendChatsQuery(1) - } - - this.logger.debug('sent init queries') - }) - // on disconnects - this.on('CB:Cmd,type:disconnect', json => ( - this.state === 'open' && this.unexpectedDisconnect(json[1].kind || 'unknown') - )) - this.on('CB:Pong', json => { - if (!json[1]) { - this.unexpectedDisconnect(DisconnectReason.close) - this.logger.info('Connection terminated by phone, closing...') - } else if (this.phoneConnected !== json[1]) { - this.phoneConnected = json[1] - this.emit ('connection-phone-change', { connected: this.phoneConnected }) - } - }) - // chats received - this.on('CB:response,type:chat', json => { - if (json[1].duplicate || !json[2]) return - - this.chatsDebounceTimeout.cancel() - const chats = new KeyedDB(this.chatOrderingKey, c => c.jid) - - json[2].forEach(([item, chat]: [any, WAChat]) => { - if (!chat) { - this.logger.warn (`unexpectedly got null chat: ${item}`, chat) - return - } - chat.jid = whatsappID (chat.jid) - chat.t = +chat.t - chat.count = +chat.count - chat.messages = newMessagesDB() - // chats data (log json to see what it looks like) - chats.insertIfAbsent(chat) - }) - this.logger.info (`received ${json[2].length} chats`) - - const oldChats = this.chats - const updatedChats = [] - let hasNewChats = false - - chats.all().forEach (chat => { - const respectiveContact = this.contacts[chat.jid] - chat.name = respectiveContact?.name || respectiveContact?.notify || chat.name - - const oldChat = oldChats.get(chat.jid) - if (!oldChat) { - hasNewChats = true - } else { - chat.messages = oldChat.messages - if (oldChat.t !== chat.t || oldChat.modify_tag !== chat.modify_tag) { - const changes = shallowChanges (oldChat, chat, { lookForDeletedKeys: true }) - delete chat.metadata // remove group metadata as that may have changed; TODO, write better mechanism for this - delete changes.messages - - updatedChats.push({ ...changes, jid: chat.jid }) - } - } - }) - this.chats = chats - this.lastChatsReceived = new Date() - - updatedChats.length > 0 && this.emit('chats-update', updatedChats) - - this.emit('chats-received', { hasNewChats }) - }) - // we store these last messages - const lastMessages = {} - // keep track of overlaps, - // if there are no overlaps of messages and we had messages present, we clear the previous messages - // this prevents missing messages in conversations - let overlaps: { [_: string]: { requiresOverlap: boolean, didOverlap?: boolean } } = {} - const onLastBatchOfDataReceived = () => { - // find which chats had missing messages - // list out all the jids, and how many messages we've cached now - const chatsWithMissingMessages = Object.keys(overlaps).map(jid => { - // if there was no overlap, delete previous messages - if (!overlaps[jid].didOverlap && overlaps[jid].requiresOverlap) { - this.logger.debug(`received messages for ${jid}, but did not overlap with previous messages, clearing...`) - const chat = this.chats.get(jid) - if (chat) { - const message = chat.messages.get(lastMessages[jid]) - const remainingMessages = chat.messages.paginatedByValue(message, this.maxCachedMessages, undefined, 'after') - chat.messages = newMessagesDB([message, ...remainingMessages]) - return { jid, count: chat.messages.length } // return number of messages we've left - } - } - }).filter(Boolean) - this.emit('initial-data-received', { chatsWithMissingMessages }) - } - // messages received - const messagesUpdate = (json, style: 'previous' | 'last') => { - //console.log('msg ', json[1]) - this.messagesDebounceTimeout.start(undefined, onLastBatchOfDataReceived) - if (style === 'last') { - overlaps = {} - } - const messages = json[2] as WANode[] - if (messages) { - const updates: { [k: string]: KeyedDB } = {} - messages.reverse().forEach (([,, message]: ['message', null, WAMessage]) => { - const jid = message.key.remoteJid - const chat = this.chats.get(jid) - - const mKeyID = WA_MESSAGE_ID(message) - if (chat) { - if (style === 'previous') { - const fm = chat.messages.get(lastMessages[jid]) - if (!fm) return - const prevEpoch = fm['epoch'] - message['epoch'] = prevEpoch-1 - } else if (style === 'last') { - // no overlap required, if there were no previous messages - overlaps[jid] = { requiresOverlap: chat.messages.length > 0 } - - const lm = chat.messages.all()[chat.messages.length-1] - const prevEpoch = (lm && lm['epoch']) || 0 - // hacky way to allow more previous messages - message['epoch'] = prevEpoch+1000 - } - if (chat.messages.upsert(message).length > 0) { - overlaps[jid] = { ...(overlaps[jid] || { requiresOverlap: true }), didOverlap: true } - } - updates[jid] = updates[jid] || newMessagesDB() - updates[jid].upsert(message) - - lastMessages[jid] = mKeyID - } else if (!chat) this.logger.debug({ jid }, `chat not found`) - }) - if (Object.keys(updates).length > 0) { - this.emit ('chats-update', - Object.keys(updates).map(jid => ({ jid, messages: updates[jid] })) - ) - } - } - } - this.on('CB:action,add:last', json => messagesUpdate(json, 'last')) - this.on('CB:action,add:before', json => messagesUpdate(json, 'previous')) - this.on('CB:action,add:unread', json => messagesUpdate(json, 'previous')) - - // contacts received - this.on('CB:response,type:contacts', json => { - if (json[1].duplicate || !json[2]) return - const contacts = this.contacts - const updatedContacts: WAContact[] = [] - - json[2].forEach(([type, contact]: ['user', WAContact]) => { - if (!contact) return this.logger.info (`unexpectedly got null contact: ${type}`, contact) - - contact.jid = whatsappID (contact.jid) - const presentContact = contacts[contact.jid] - if (presentContact) { - const changes = shallowChanges(presentContact, contact, { lookForDeletedKeys: false }) - if (changes && Object.keys(changes).length > 0) { - updatedContacts.push({ ...changes, jid: contact.jid }) - } - } else updatedContacts.push(contact) - - contacts[contact.jid] = { ...(presentContact || {}), ...contact } - }) - // update chat names - const updatedChats = [] - this.chats.all().forEach(c => { - const contact = contacts[c.jid] - if (contact) { - const name = contact?.name || contact?.notify || c.name - if (name !== c.name) { - updatedChats.push({ jid: c.jid, name }) - } - } - }) - updatedChats.length > 0 && this.emit('chats-update', updatedChats) - - this.logger.info (`received ${json[2].length} contacts`) - this.contacts = contacts - - this.emit('contacts-received', { updatedContacts }) - }) - // new messages - this.on('CB:action,add:relay,message', json => { - const message = json[2][0][2] as WAMessage - this.chatAddMessageAppropriate (message) - }) - this.on('CB:Chat,cmd:action', json => { - const data = json[1].data - if (data) { - const emitGroupParticipantsUpdate = (action: WAParticipantAction) => this.emitParticipantsUpdate - (json[1].id, data[2].participants.map(whatsappID), action) - const emitGroupUpdate = (data: Partial) => this.emitGroupUpdate(json[1].id, data) - - switch (data[0]) { - case "promote": - emitGroupParticipantsUpdate('promote') - break - case "demote": - emitGroupParticipantsUpdate('demote') - break - case "desc_add": - emitGroupUpdate({ ...data[2], descOwner: data[1] }) - break - default: - this.logger.debug({ unhandled: true }, json) - break - } - } - }) - // presence updates - this.on('CB:Presence', json => { - const chatUpdate = this.applyingPresenceUpdate(json[1]) - chatUpdate && this.emit('chat-update', chatUpdate) - }) - // If a message has been updated (usually called when a video message gets its upload url, or live locations) - this.on ('CB:action,add:update,message', json => { - const message: WAMessage = json[2][0][2] - const jid = whatsappID(message.key.remoteJid) - const chat = this.chats.get(jid) - if (!chat) return - // reinsert to update - const oldMessage = chat.messages.get (WA_MESSAGE_ID(message)) - if (oldMessage) { - message['epoch'] = oldMessage['epoch'] - if (chat.messages.upsert(message).length) { - const chatUpdate: Partial = { jid, messages: newMessagesDB([ message ]) } - this.emit ('chat-update', chatUpdate) - } - } else { - this.logger.debug ({ unhandled: true }, 'received message update for non-present message from ' + jid) - } - }) - // message status updates - const onMessageStatusUpdate = json => { - json = json[2][0][1] - const MAP = { - read: WA_MESSAGE_STATUS_TYPE.READ, - message: WA_MESSAGE_STATUS_TYPE.DELIVERY_ACK, - error: WA_MESSAGE_STATUS_TYPE.ERROR - } - this.onMessageStatusUpdate( - whatsappID(json.jid), - { id: json.index, fromMe: json.owner === 'true' }, - MAP[json.type] - ) - } - this.on('CB:action,add:relay,received', onMessageStatusUpdate) - this.on('CB:action,,received', onMessageStatusUpdate) - - this.on('CB:Msg,cmd:ack', json => ( - this.onMessageStatusUpdate( - whatsappID(json[1].to), - { id: json[1].id, fromMe: true }, - +json[1].ack + 1 - ) - )) - - // If a user's contact has changed - this.on ('CB:action,,user', json => { - const node = json[2][0] - if (node) { - const user = node[1] as WAContact - user.jid = whatsappID(user.jid) - - this.contacts[user.jid] = user - this.emit('contact-update', user) - - const chat = this.chats.get (user.jid) - if (chat) { - chat.name = user.name || user.notify || chat.name - this.emit ('chat-update', { jid: chat.jid, name: chat.name }) - } - } - }) - // chat archive, pin etc. - this.on('CB:action,,chat', json => { - json = json[2][0] - - const updateType = json[1].type - const jid = whatsappID(json[1]?.jid) - - const chat = this.chats.get(jid) - if (!chat) return - - const FUNCTIONS = { - 'delete': () => { - chat['delete'] = 'true' - this.chats.deleteById(chat.jid) - return 'delete' - }, - 'clear': () => { - if (!json[2]) chat.messages.clear () - else json[2].forEach(item => chat.messages.filter(m => m.key.id !== item[1].index)) - return 'clear' - }, - 'archive': () => { - this.chats.update(chat.jid, chat => chat.archive = 'true') - return 'archive' - }, - 'unarchive': () => { - delete chat.archive - return 'archive' - }, - 'pin': () => { - chat.pin = json[1].pin - return 'pin' - } - } - const func = FUNCTIONS [updateType] - - if (func) { - const property = func () - this.emit ('chat-update', { jid, [property]: chat[property] || 'false' }) - } - }) - // profile picture updates - this.on('CB:Cmd,type:picture', async json => { - json = json[1] - const jid = whatsappID(json.jid) - const imgUrl = await this.getProfilePicture(jid).catch(() => '') - const contact = this.contacts[jid] - if (contact) { - contact.imgUrl = imgUrl - this.emit('contact-update', { jid, imgUrl }) - } - const chat = this.chats.get(jid) - if (chat) { - chat.imgUrl = imgUrl - this.emit ('chat-update', { jid, imgUrl }) - } - }) - // status updates - this.on('CB:Status,status', async json => { - const jid = whatsappID(json[1].id) - this.emit ('contact-update', { jid, status: json[1].status }) - }) - // User Profile Name Updates - this.on ('CB:Conn,pushname', json => { - if (this.user) { - const name = json[1].pushname - if(this.user.name !== name) { - this.user.name = name // update on client too - this.emit ('contact-update', { jid: this.user.jid, name }) - } - } - }) - // read updates - this.on ('CB:action,,read', async json => { - const update = json[2][0][1] - const jid = whatsappID(update.jid) - const chat = this.chats.get (jid) - if(chat) { - if (update.type === 'false') chat.count = -1 - else chat.count = 0 - - this.emit ('chat-update', { jid: chat.jid, count: chat.count }) - } else { - this.logger.warn('recieved read update for unknown chat ' + jid) - } - }) - this.on('qr', qr => { - if (this.connectOptions.logQR) { - QR.generate(qr, { small: true }) - } - }); - - // blocklist updates - this.on('CB:Blocklist', json => { - json = json[1] - const initial = this.blocklist - this.blocklist = json.blocklist - - const added = this.blocklist.filter(id => !initial.includes(id)) - const removed = initial.filter(id => !this.blocklist.includes(id)) - - const update: BlocklistUpdate = { added, removed } - - this.emit('blocklist-update', update) - }) - } - protected sendChatsQuery(epoch: number) { - return this.sendBinary(['query', {type: 'chat', epoch: epoch.toString()}, null], [ WAMetric.queryChat, WAFlag.ignore ]) - } - /** Get the URL to download the profile picture of a person/group */ - @Mutex (jid => jid) - async getProfilePicture(jid: string | null) { - const response = await this.query({ - json: ['query', 'ProfilePicThumb', jid || this.user.jid], - expect200: true, - requiresPhoneConnection: false - }) - return response.eurl as string - } - protected applyingPresenceUpdate(update: PresenceUpdate) { - const chatId = whatsappID(update.id) - const jid = whatsappID(update.participant || update.id) - - const chat = this.chats.get(chatId) - if (chat && jid.endsWith('@s.whatsapp.net')) { // if its a single chat - chat.presences = chat.presences || {} - - const presence = { ...(chat.presences[jid] || {}) } as WAPresenceData - - if (update.t) presence.lastSeen = +update.t - else if (update.type === Presence.unavailable && (presence.lastKnownPresence === Presence.available || presence.lastKnownPresence === Presence.composing)) { - presence.lastSeen = unixTimestampSeconds() - } - presence.lastKnownPresence = update.type - // no update - if(presence.lastKnownPresence === chat.presences[jid]?.lastKnownPresence && presence.lastSeen === chat.presences[jid]?.lastSeen) { - return - } - - const contact = this.contacts[jid] - if (contact) { - presence.name = contact.name || contact.notify || contact.vname - } - - chat.presences[jid] = presence - return { jid: chatId, presences: { [jid]: presence } } as Partial - } - } - /** inserts an empty chat into the DB */ - protected chatAdd (jid: string, name?: string, properties: Partial = {}) { - const chat: WAChat = { - jid, - name, - t: unixTimestampSeconds(), - messages: newMessagesDB(), - count: 0, - ...(properties || {}) - } - if(this.chats.insertIfAbsent(chat).length) { - this.emit('chat-new', chat) - return chat - } - } - protected onMessageStatusUpdate(jid: string, key: { id: string, fromMe: boolean }, status: WA_MESSAGE_STATUS_TYPE) { - const chat = this.chats.get( whatsappID(jid) ) - const msg = chat?.messages.get(GET_MESSAGE_ID(key)) - if (msg) { - if (typeof status !== 'undefined') { - if (status > msg.status || status === WA_MESSAGE_STATUS_TYPE.ERROR) { - msg.status = status - this.emit('chat-update', { jid: chat.jid, messages: newMessagesDB([ msg ]) }) - } - } else { - this.logger.warn({ update: status }, 'received unknown message status update') - } - } else { - this.logger.debug ({ unhandled: true, update: status, key }, 'received message status update for non-present message') - } - } - protected contactAddOrGet (jid: string) { - jid = whatsappID(jid) - if (!this.contacts[jid]) this.contacts[jid] = { jid } - return this.contacts[jid] - } - /** find a chat or return an error */ - protected assertChatGet = jid => { - const chat = this.chats.get (jid) - if (!chat) throw new Error (`chat '${jid}' not found`) - return chat - } - /** Adds the given message to the appropriate chat, if the chat doesn't exist, it is created */ - protected async chatAddMessageAppropriate (message: WAMessage) { - const jid = whatsappID(message.key.remoteJid) - const chat = this.chats.get(jid) || await this.chatAdd (jid) - this.chatAddMessage (message, chat) - } - protected chatAddMessage (message: WAMessage, chat: WAChat) { - // store updates in this - const chatUpdate: WAChatUpdate = { jid: chat.jid } - // add to count if the message isn't from me & there exists a message - if (!message.key.fromMe && message.message) { - chat.count += 1 - chatUpdate.count = chat.count - - const participant = whatsappID(message.participant || chat.jid) - const contact = chat.presences && chat.presences[participant] - if (contact?.lastKnownPresence === Presence.composing) { // update presence - const update = this.applyingPresenceUpdate({ id: chat.jid, participant, type: Presence.available }) - update && Object.assign(chatUpdate, update) - } - } - - const ephemeralProtocolMsg = message.message?.ephemeralMessage?.message?.protocolMessage - if ( - ephemeralProtocolMsg && - ephemeralProtocolMsg.type === WAMessageProto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING - ) { - chatUpdate.eph_setting_ts = message.messageTimestamp.toString() - chatUpdate.ephemeral = ephemeralProtocolMsg.ephemeralExpiration.toString() - - if (ephemeralProtocolMsg.ephemeralExpiration) { - chat.eph_setting_ts = chatUpdate.eph_setting_ts - chat.ephemeral = chatUpdate.ephemeral - } else { - delete chat.eph_setting_ts - delete chat.ephemeral - } - } - - const messages = chat.messages - const protocolMessage = message.message?.protocolMessage - // if it's a message to delete another message - if (protocolMessage) { - switch (protocolMessage.type) { - case WAMessageProto.ProtocolMessage.ProtocolMessageType.REVOKE: - const found = chat.messages.get (GET_MESSAGE_ID(protocolMessage.key)) - if (found?.message) { - this.logger.info ('deleting message: ' + protocolMessage.key.id + ' in chat: ' + protocolMessage.key.remoteJid) - - found.messageStubType = WA_MESSAGE_STUB_TYPE.REVOKE - delete found.message - chatUpdate.messages = newMessagesDB([ found ]) - } - break - default: - break - } - } else if (!messages.get(WA_MESSAGE_ID(message))) { // if the message is not already there - - const lastEpoch = (messages.last && messages.last['epoch']) || 0 - message['epoch'] = lastEpoch+1 - - messages.insert (message) - while (messages.length > this.maxCachedMessages) { - messages.delete (messages.all()[0]) // delete oldest messages - } - // only update if it's an actual message - if (message.message && !ephemeralProtocolMsg) { - this.chats.update(chat.jid, chat => { - chat.t = +toNumber(message.messageTimestamp) - chatUpdate.t = chat.t - // a new message unarchives the chat - if (chat.archive) { - delete chat.archive - chatUpdate.archive = 'false' - } - }) - } - chatUpdate.hasNewMessage = true - chatUpdate.messages = newMessagesDB([ message ]) - // check if the message is an action - if (message.messageStubType) { - const jid = chat.jid - //let actor = whatsappID (message.participant) - let participants: string[] - const emitParticipantsUpdate = (action: WAParticipantAction) => ( - this.emitParticipantsUpdate(jid, participants, action) - ) - const emitGroupUpdate = (update: Partial) => this.emitGroupUpdate(jid, update) - - switch (message.messageStubType) { - case WA_MESSAGE_STUB_TYPE.CHANGE_EPHEMERAL_SETTING: - chatUpdate.eph_setting_ts = message.messageTimestamp.toString() - chatUpdate.ephemeral = message.messageStubParameters[0] - - if (+chatUpdate.ephemeral) { - chat.eph_setting_ts = chatUpdate.eph_setting_ts - chat.ephemeral = chatUpdate.ephemeral - } else { - delete chat.eph_setting_ts - delete chat.ephemeral - } - break - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_LEAVE: - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_REMOVE: - participants = message.messageStubParameters.map (whatsappID) - emitParticipantsUpdate('remove') - // mark the chat read only if you left the group - if (participants.includes(this.user.jid)) { - chat.read_only = 'true' - chatUpdate.read_only = 'true' - } - break - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_ADD: - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_INVITE: - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_ADD_REQUEST_JOIN: - participants = message.messageStubParameters.map (whatsappID) - if (participants.includes(this.user.jid) && chat.read_only === 'true') { - delete chat.read_only - chatUpdate.read_only = 'false' - } - emitParticipantsUpdate('add') - break - case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_ANNOUNCE: - const announce = message.messageStubParameters[0] === 'on' ? 'true' : 'false' - emitGroupUpdate({ announce }) - break - case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_RESTRICT: - const restrict = message.messageStubParameters[0] === 'on' ? 'true' : 'false' - emitGroupUpdate({ restrict }) - break - case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_SUBJECT: - case WA_MESSAGE_STUB_TYPE.GROUP_CREATE: - chat.name = message.messageStubParameters[0] - chatUpdate.name = chat.name - if (chat.metadata) chat.metadata.subject = chat.name - break - } - } - } - - this.emit('chat-update', chatUpdate) - } - protected emitParticipantsUpdate = (jid: string, participants: string[], action: WAParticipantAction) => { - const chat = this.chats.get(jid) - const meta = chat?.metadata - if (meta) { - switch (action) { - case 'add': - participants.forEach(jid => ( - meta.participants.push({ ...this.contactAddOrGet(jid), isAdmin: false, isSuperAdmin: false }) - )) - break - case 'remove': - meta.participants = meta.participants.filter(p => !participants.includes(p.jid)) - break - case 'promote': - case 'demote': - const isAdmin = action==='promote' - meta.participants.forEach(p => { - if (participants.includes( p.jid )) p.isAdmin = isAdmin - }) - break - } - } - this.emit ('group-participants-update', { jid, participants, action }) - } - protected emitGroupUpdate = (jid: string, update: Partial) => { - const chat = this.chats.get(jid) - if (chat && chat.metadata) Object.assign(chat.metadata, update) - this.emit ('group-update', { jid, ...update }) - } - protected chatUpdateTime = (chat, stamp: number) => this.chats.update (chat.jid, c => c.t = stamp) - /** sets the profile picture of a chat */ - protected async setProfilePicture (chat: WAChat) { - chat.imgUrl = await this.getProfilePicture (chat.jid).catch (err => '') - } - - // Add all event types - - /** when the connection has opened successfully */ - on (event: 'open', listener: (result: WAOpenResult) => void): this - /** when the connection is opening */ - on (event: 'connecting', listener: () => void): this - /** when the connection has closed */ - on (event: 'close', listener: (err: {reason?: DisconnectReason | string, isReconnecting: boolean}) => void): this - /** when the socket is closed */ - on (event: 'ws-close', listener: (err: {reason?: DisconnectReason | string}) => void): this - /** when a new QR is generated, ready for scanning */ - on (event: 'qr', listener: (qr: string) => void): this - /** when the connection to the phone changes */ - on (event: 'connection-phone-change', listener: (state: {connected: boolean}) => void): this - /** when a contact is updated */ - on (event: 'contact-update', listener: (update: WAContactUpdate) => void): this - /** when a new chat is added */ - on (event: 'chat-new', listener: (chat: WAChat) => void): this - /** when contacts are sent by WA */ - on (event: 'contacts-received', listener: (u: { updatedContacts: Partial[] }) => void): this - /** when chats are sent by WA, and when all messages are received */ - on (event: 'chats-received', listener: (update: {hasNewChats?: boolean}) => void): this - /** when all initial messages are received from WA */ - on (event: 'initial-data-received', listener: (update: {chatsWithMissingMessages: { jid: string, count: number }[] }) => void): this - /** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */ - on (event: 'chats-update', listener: (chats: WAChatUpdate[]) => void): this - /** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */ - on (event: 'chat-update', listener: (chat: WAChatUpdate) => void): this - /** when participants are added to a group */ - on (event: 'group-participants-update', listener: (update: {jid: string, participants: string[], actor?: string, action: WAParticipantAction}) => void): this - /** when the group is updated */ - on (event: 'group-update', listener: (update: Partial & {jid: string, actor?: string}) => void): this - /** when WA sends back a pong */ - on (event: 'received-pong', listener: () => void): this - /** when a user is blocked or unblockd */ - on (event: 'blocklist-update', listener: (update: BlocklistUpdate) => void): this - - on (event: BaileysEvent | string, listener: (json: any) => void): this - - on (event: BaileysEvent | string, listener: (...args: any[]) => void) { return super.on (event, listener) } - emit (event: BaileysEvent | string, ...args: any[]) { return super.emit (event, ...args) } -} diff --git a/src/WAConnection/5.User.ts b/src/WAConnection/5.User.ts deleted file mode 100644 index 5509ac2..0000000 --- a/src/WAConnection/5.User.ts +++ /dev/null @@ -1,231 +0,0 @@ -import {WAConnection as Base} from './4.Events' -import { Presence, WABroadcastListInfo, WAProfilePictureChange, WALoadChatOptions, WAChatIndex, BlocklistUpdate, WABusinessProfile } from './Constants' -import { - WAMessage, - WANode, - WAMetric, - WAFlag, -} from '../WAConnection/Constants' -import { generateProfilePicture, whatsappID } from './Utils' -import { Mutex } from './Mutex' -import { URL } from 'url' - -// All user related functions -- get profile picture, set status etc. - -export class WAConnection extends Base { - /** - * Query whether a given number is registered on WhatsApp - * @param str phone number/jid you want to check for - * @returns undefined if the number doesn't exists, otherwise the correctly formatted jid - */ - isOnWhatsApp = async (str: string) => { - const { status, jid, biz } = await this.query({json: ['query', 'exist', str], requiresPhoneConnection: false}) - if (status === 200) return { exists: true, jid: whatsappID(jid), isBusiness: biz as boolean} - } - /** - * Tell someone about your presence -- online, typing, offline etc. - * @param jid the ID of the person/group who you are updating - * @param type your presence - */ - updatePresence = (jid: string | null, type: Presence) => this.sendBinary( - [ 'action', - {epoch: this.msgCount.toString(), type: 'set'}, - [ ['presence', { type: type, to: jid }, null] ] - ], - [WAMetric.presence, WAFlag[type] ], // weird stuff WA does - undefined, - true - ) - /** Request an update on the presence of a user */ - requestPresenceUpdate = async (jid: string) => this.query({ json: ['action', 'presence', 'subscribe', jid] }) - /** Query the status of the person (see groupMetadata() for groups) */ - async getStatus (jid?: string) { - const status: { status: string } = await this.query({ json: ['query', 'Status', jid || this.user.jid], requiresPhoneConnection: false }) - return status - } - async setStatus (status: string) { - const response = await this.setQuery ( - [ - [ - 'status', - null, - Buffer.from (status, 'utf-8') - ] - ] - ) - this.emit ('contact-update', { jid: this.user.jid, status }) - return response - } - /** Updates business profile. */ - async updateBusinessProfile(profile: WABusinessProfile) { - if (profile.business_hours?.config) { - profile.business_hours.business_config = profile.business_hours.config - delete profile.business_hours.config - } - const json = ['action', "editBusinessProfile", {...profile, v: 2}] - let response; - try { - response = await this.query({ json, expect200: true, requiresPhoneConnection: true }) - } catch (_) { - return {status: 400} - } - return { status: response.status } - } - async updateProfileName (name: string) { - const response = (await this.setQuery ( - [ - [ - 'profile', - { - name - }, - null - ] - ] - )) as any as {status: number, pushname: string} - if (response.status === 200) { - this.user.name = response.pushname; - this.emit ('contact-update', { jid: this.user.jid, name }) - } - return response - } - /** Get your contacts */ - async getContacts() { - const json = ['query', { epoch: this.msgCount.toString(), type: 'contacts' }, null] - const response = await this.query({ json, binaryTags: [WAMetric.queryContact, WAFlag.ignore], expect200: true, requiresPhoneConnection: true }) // this has to be an encrypted query - return response - } - /** Get the stories of your contacts */ - async getStories() { - const json = ['query', { epoch: this.msgCount.toString(), type: 'status' }, null] - const response = await this.query({json, binaryTags: [WAMetric.queryStatus, WAFlag.ignore], expect200: true, requiresPhoneConnection: true }) as WANode - if (Array.isArray(response[2])) { - return response[2].map (row => ( - { - unread: row[1]?.unread, - count: row[1]?.count, - messages: Array.isArray(row[2]) ? row[2].map (m => m[2]) : [] - } as {unread: number, count: number, messages: WAMessage[]} - )) - } - return [] - } - /** Fetch your chats */ - async getChats() { - const json = ['query', { epoch: this.msgCount.toString(), type: 'chat' }, null] - return this.query({ json, binaryTags: [5, WAFlag.ignore], expect200: true }) // this has to be an encrypted query - } - /** Query broadcast list info */ - async getBroadcastListInfo(jid: string) { - return this.query({ - json: ['query', 'contact', jid], - expect200: true, - requiresPhoneConnection: true - }) as Promise - } - /** - * Load chats in a paginated manner + gets the profile picture - * @param before chats before the given cursor - * @param count number of results to return - * @param searchString optionally search for users - * @returns the chats & the cursor to fetch the next page - */ - loadChats (count: number, before: string | null, options: WALoadChatOptions = {}) { - const searchString = options.searchString?.toLowerCase() - const chats = this.chats.paginated (before, count, options && (chat => ( - (typeof options?.custom !== 'function' || options?.custom(chat)) && - (typeof searchString === 'undefined' || chat.name?.toLowerCase().includes (searchString) || chat.jid?.includes(searchString)) - ))) - const cursor = (chats[chats.length-1] && chats.length >= count) && this.chatOrderingKey.key (chats[chats.length-1]) - return { chats, cursor } - } - /** - * Update the profile picture - * @param jid - * @param img - */ - @Mutex (jid => jid) - async updateProfilePicture (jid: string, img: Buffer) { - jid = whatsappID (jid) - const data = await generateProfilePicture (img) - const tag = this.generateMessageTag () - const query: WANode = [ - 'picture', - { jid: jid, id: tag, type: 'set' }, - [ - ['image', null, data.img], - ['preview', null, data.preview] - ] - ] - const response = await (this.setQuery ([query], [WAMetric.picture, 136], tag) as Promise) - if (jid === this.user.jid) this.user.imgUrl = response.eurl - else if (this.chats.get(jid)) { - this.chats.get(jid).imgUrl = response.eurl - this.emit ('chat-update', { jid, imgUrl: response.eurl }) - } - return response - } - /** - * Add or remove user from blocklist - * @param jid the ID of the person who you are blocking/unblocking - * @param type type of operation - */ - @Mutex (jid => jid) - async blockUser (jid: string, type: 'add' | 'remove' = 'add') { - const json: WANode = [ - 'block', - { - type: type, - }, - [ - ['user', { jid }, null] - ], - ] - const result = await this.setQuery ([json], [WAMetric.block, WAFlag.ignore]) - - if (result.status === 200) { - if (type === 'add') { - this.blocklist.push(jid) - } else { - const index = this.blocklist.indexOf(jid); - if (index !== -1) { - this.blocklist.splice(index, 1); - } - } - - // Blocklist update event - const update: BlocklistUpdate = { added: [], removed: [] } - let key = type === 'add' ? 'added' : 'removed' - update[key] = [ jid ] - this.emit('blocklist-update', update) - } - - return result - } - /** - * Query Business Profile (Useful for VCards) - * @param jid Business Jid - * @returns {WABusinessProfile} profile object or undefined if not business account - */ - async getBusinessProfile(jid: string) { - jid = whatsappID(jid) - const { - profiles: [{ - profile, - wid - }] - } = await this.query({ - json: ["query", "businessProfile", [ - { - "wid": jid.replace('@s.whatsapp.net', '@c.us') - } - ], 84], - expect200: true, - requiresPhoneConnection: false, - }) - return { - ...profile, - wid: whatsappID(wid) - } - } -} diff --git a/src/WAConnection/6.MessagesSend.ts b/src/WAConnection/6.MessagesSend.ts deleted file mode 100644 index 9c53f6a..0000000 --- a/src/WAConnection/6.MessagesSend.ts +++ /dev/null @@ -1,479 +0,0 @@ -import {WAConnection as Base} from './5.User' -import {createReadStream, promises as fs} from 'fs' -import { - MessageOptions, - MessageType, - Mimetype, - MimetypeMap, - MediaPathMap, - WALocationMessage, - WAContactMessage, - WAContactsArrayMessage, - WAGroupInviteMessage, - WAListMessage, - WAButtonsMessage, - WATextMessage, - WAMessageContent, WAMetric, WAFlag, WAMessage, BaileysError, WA_MESSAGE_STATUS_TYPE, WAMessageProto, MediaConnInfo, MessageTypeProto, URL_REGEX, WAUrlInfo, WA_DEFAULT_EPHEMERAL, WAMediaUpload -} from './Constants' -import { isGroupID, generateMessageID, extensionForMediaMessage, whatsappID, unixTimestampSeconds, getAudioDuration, newMessagesDB, encryptedStream, decryptMediaMessageBuffer, generateThumbnail } from './Utils' -import { Mutex } from './Mutex' -import { Readable } from 'stream' - -export class WAConnection extends Base { - /** - * Send a message to the given ID (can be group, single, or broadcast) - * @param id the id to send to - * @param message the message can be a buffer, plain string, location message, extended text message - * @param type type of message - * @param options Extra options - */ - async sendMessage( - id: string, - message: string | WATextMessage | WALocationMessage | WAContactMessage | WAContactsArrayMessage | WAGroupInviteMessage | WAMediaUpload | WAListMessage | WAButtonsMessage, - type: MessageType, - options: MessageOptions = {}, - ) { - const waMessage = await this.prepareMessage (id, message, type, options) - await this.relayWAMessage (waMessage, { waitForAck: options.waitForAck !== false }) - return waMessage - } - /** - * Send a list message - * @param id the id to send to - * @param button the optional button text, title and description button - * @param rows the rows of sections list message - */ - async sendListMessage( - id: string, - button: { buttonText?: string; description?: string; title?: string }, - rows: any = [], - ) { - let messageList = WAMessageProto.Message.fromObject({ - listMessage: WAMessageProto.ListMessage.fromObject({ - buttonText: button.buttonText, - description: button.description, - listType: 1, - sections: [ - { - title: button.title, - rows: [ ...rows ] - } - ] - }) - }) - let waMessageList = await this.prepareMessageFromContent(id, messageList, {}) - await this.relayWAMessage (waMessageList, { waitForAck: true }) - return waMessageList - } - /** Prepares a message for sending via sendWAMessage () */ - async prepareMessage( - id: string, - message: string | WATextMessage | WALocationMessage | WAContactMessage | WAContactsArrayMessage | WAGroupInviteMessage | WAMediaUpload | WAListMessage | WAButtonsMessage, - type: MessageType, - options: MessageOptions = {}, - ) { - const content = await this.prepareMessageContent( - message, - type, - options - ) - const preparedMessage = this.prepareMessageFromContent(id, content, options) - return preparedMessage - } - /** - * Toggles disappearing messages for the given chat - * - * @param jid the chat to toggle - * @param ephemeralExpiration 0 to disable, enter any positive number to enable disappearing messages for the specified duration; - * For the default see WA_DEFAULT_EPHEMERAL - */ - async toggleDisappearingMessages(jid: string, ephemeralExpiration?: number, opts: { waitForAck: boolean } = { waitForAck: true }) { - if(isGroupID(jid)) { - const tag = this.generateMessageTag(true) - await this.setQuery([ - [ - 'group', - { id: tag, jid, type: 'prop', author: this.user.jid }, - [ [ 'ephemeral', { value: ephemeralExpiration.toString() }, null ] ] - ] - ], [WAMetric.group, WAFlag.other], tag) - } else { - const message = this.prepareMessageFromContent( - jid, - this.prepareDisappearingMessageSettingContent(ephemeralExpiration), - {} - ) - await this.relayWAMessage(message, opts) - } - } - /** Prepares the message content */ - async prepareMessageContent (message: string | WATextMessage | WALocationMessage | WAContactMessage | WAContactsArrayMessage | WAGroupInviteMessage | WAMediaUpload | WAListMessage | WAButtonsMessage, type: MessageType, options: MessageOptions) { - let m: WAMessageContent = {} - switch (type) { - case MessageType.text: - case MessageType.extendedText: - if (typeof message === 'string') message = {text: message} as WATextMessage - - if ('text' in message) { - if (options.detectLinks !== false && message.text.match(URL_REGEX)) { - try { - message = await this.generateLinkPreview (message.text) - } catch (error) { // ignore if fails - this.logger.trace(`failed to generate link preview for message '${message.text}': ${error}`) - } - } - m.extendedTextMessage = WAMessageProto.ExtendedTextMessage.fromObject(message as any) - } else { - throw new BaileysError ('message needs to be a string or object with property \'text\'', message) - } - break - case MessageType.location: - case MessageType.liveLocation: - m.locationMessage = WAMessageProto.LocationMessage.fromObject(message as any) - break - case MessageType.contact: - m.contactMessage = WAMessageProto.ContactMessage.fromObject(message as any) - break - case MessageType.contactsArray: - m.contactsArrayMessage = WAMessageProto.ContactsArrayMessage.fromObject(message as any) - break - case MessageType.groupInviteMessage: - m.groupInviteMessage = WAMessageProto.GroupInviteMessage.fromObject(message as any) - break - case MessageType.listMessage: - m.listMessage = WAMessageProto.ListMessage.fromObject(message as any) - break - case MessageType.buttonsMessage: - m.buttonsMessage = WAMessageProto.ButtonsMessage.fromObject(message as any) - break - case MessageType.image: - case MessageType.sticker: - case MessageType.document: - case MessageType.video: - case MessageType.audio: - m = await this.prepareMessageMedia(message as Buffer, type, options) - break - } - return WAMessageProto.Message.fromObject (m) - } - prepareDisappearingMessageSettingContent(ephemeralExpiration?: number) { - ephemeralExpiration = ephemeralExpiration || 0 - const content: WAMessageContent = { - ephemeralMessage: { - message: { - protocolMessage: { - type: WAMessageProto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING, - ephemeralExpiration - } - } - } - } - return WAMessageProto.Message.fromObject(content) - } - /** Prepare a media message for sending */ - async prepareMessageMedia(media: WAMediaUpload, mediaType: MessageType, options: MessageOptions = {}) { - if (mediaType === MessageType.document && !options.mimetype) { - throw new Error('mimetype required to send a document') - } - if (mediaType === MessageType.sticker && options.caption) { - throw new Error('cannot send a caption with a sticker') - } - if (!(mediaType === MessageType.image || mediaType === MessageType.video) && options.viewOnce) { - throw new Error(`cannot send a ${mediaType} as a viewOnceMessage`) - } - if (!options.mimetype) { - options.mimetype = MimetypeMap[mediaType] - } - let isGIF = false - if (options.mimetype === Mimetype.gif) { - isGIF = true - options.mimetype = MimetypeMap[MessageType.video] - } - const requiresDurationComputation = mediaType === MessageType.audio && !options.duration - const requiresThumbnailComputation = (mediaType === MessageType.image || mediaType === MessageType.video) && !('thumbnail' in options) - const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation - const { - mediaKey, - encBodyPath, - bodyPath, - fileEncSha256, - fileSha256, - fileLength, - didSaveToTmpPath - } = await encryptedStream(media, mediaType, requiresOriginalForSomeProcessing) - // url safe Base64 encode the SHA256 hash of the body - const fileEncSha256B64 = encodeURIComponent( - fileEncSha256.toString('base64') - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/\=+$/, '') - ) - if(requiresThumbnailComputation) { - await generateThumbnail(bodyPath, mediaType, options) - } - if (requiresDurationComputation) { - try { - options.duration = await getAudioDuration(bodyPath) - } catch (error) { - this.logger.debug ({ error }, 'failed to obtain audio duration: ' + error.message) - } - } - // send a query JSON to obtain the url & auth token to upload our media - let json = await this.refreshMediaConn(options.forceNewMediaOptions) - - let mediaUrl: string - for (let host of json.hosts) { - const auth = encodeURIComponent(json.auth) // the auth token - const url = `https://${host.hostname}${MediaPathMap[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}` - - try { - const {body: responseText} = await this.fetchRequest( - url, - 'POST', - createReadStream(encBodyPath), - options.uploadAgent, - { 'Content-Type': 'application/octet-stream' } - ) - const result = JSON.parse(responseText) - mediaUrl = result?.url - - if (mediaUrl) break - else { - json = await this.refreshMediaConn(true) - throw new Error (`upload failed, reason: ${JSON.stringify(result)}`) - } - } catch (error) { - const isLast = host.hostname === json.hosts[json.hosts.length-1].hostname - this.logger.error (`Error in uploading to ${host.hostname} (${error}) ${isLast ? '' : ', retrying...'}`) - } - } - if (!mediaUrl) throw new Error('Media upload failed on all hosts') - // remove tmp files - await Promise.all( - [ - fs.unlink(encBodyPath), - didSaveToTmpPath && bodyPath && fs.unlink(bodyPath) - ] - .filter(Boolean) - ) - - const message = { - [mediaType]: MessageTypeProto[mediaType].fromObject( - { - url: mediaUrl, - mediaKey: mediaKey, - mimetype: options.mimetype, - fileEncSha256: fileEncSha256, - fileSha256: fileSha256, - fileLength: fileLength, - seconds: options.duration, - fileName: options.filename || 'file', - gifPlayback: isGIF || undefined, - caption: options.caption, - ptt: options.ptt, - viewOnce: options.viewOnce, - isAnimated: options.isAnimated - } - ) - } - return WAMessageProto.Message.fromObject(message)// as WAMessageContent - } - /** prepares a WAMessage for sending from the given content & options */ - prepareMessageFromContent(id: string, message: WAMessageContent, options: MessageOptions) { - if (!options.timestamp) options.timestamp = new Date() // set timestamp to now - if (typeof options.sendEphemeral === 'undefined') options.sendEphemeral = 'chat' - if (options.viewOnce) message = { viewOnceMessage: { message } } - // prevent an annoying bug (WA doesn't accept sending messages with '@c.us') - id = whatsappID (id) - - const key = Object.keys(message)[0] - const timestamp = unixTimestampSeconds(options.timestamp) - const quoted = options.quoted - - if (options.contextInfo) message[key].contextInfo = options.contextInfo - - if (quoted) { - const participant = quoted.key.fromMe ? this.user.jid : (quoted.participant || quoted.key.participant || quoted.key.remoteJid) - - message[key].contextInfo = message[key].contextInfo || { } - message[key].contextInfo.participant = participant - message[key].contextInfo.stanzaId = quoted.key.id - message[key].contextInfo.quotedMessage = quoted.message - - // if a participant is quoted, then it must be a group - // hence, remoteJid of group must also be entered - if (quoted.key.participant) { - message[key].contextInfo.remoteJid = quoted.key.remoteJid - } - } - if (options?.thumbnail) { - message[key].jpegThumbnail = Buffer.from(options.thumbnail, 'base64') - } - - const chat = this.chats.get(id) - if ( - // if we want to send a disappearing message - ((options?.sendEphemeral === 'chat' && chat?.ephemeral) || - options?.sendEphemeral === true) && - // and it's not a protocol message -- delete, toggle disappear message - key !== 'protocolMessage' && - // already not converted to disappearing message - key !== 'ephemeralMessage' - ) { - message[key].contextInfo = { - ...(message[key].contextInfo || {}), - expiration: chat?.ephemeral || WA_DEFAULT_EPHEMERAL, - ephemeralSettingTimestamp: chat?.eph_setting_ts - } - message = { - ephemeralMessage: { - message - } - } - } - message = WAMessageProto.Message.fromObject (message) - - const messageJSON = { - key: { - remoteJid: id, - fromMe: true, - id: options?.messageId || generateMessageID(), - }, - message: message, - messageTimestamp: timestamp, - messageStubParameters: [], - participant: id.includes('@g.us') ? this.user.jid : null, - status: WA_MESSAGE_STATUS_TYPE.PENDING - } - return WAMessageProto.WebMessageInfo.fromObject (messageJSON) - } - /** Relay (send) a WAMessage; more advanced functionality to send a built WA Message, you may want to stick with sendMessage() */ - async relayWAMessage(message: WAMessage, { waitForAck } = { waitForAck: true }) { - const json = ['action', {epoch: this.msgCount.toString(), type: 'relay'}, [['message', null, message]]] - const flag = message.key.remoteJid === this.user?.jid ? WAFlag.acknowledge : WAFlag.ignore // acknowledge when sending message to oneself - const mID = message.key.id - message.status = WA_MESSAGE_STATUS_TYPE.PENDING - const promise = this.query({ - json, - binaryTags: [WAMetric.message, flag], - tag: mID, - expect200: true, - requiresPhoneConnection: true - }) - .then(() => message.status = WA_MESSAGE_STATUS_TYPE.SERVER_ACK) - - if (waitForAck) { - await promise - } else { - const emitUpdate = (status: WA_MESSAGE_STATUS_TYPE) => { - message.status = status - this.emit('chat-update', { jid: message.key.remoteJid, messages: newMessagesDB([ message ]) }) - } - promise - .then(() => emitUpdate(WA_MESSAGE_STATUS_TYPE.SERVER_ACK)) - .catch(() => emitUpdate(WA_MESSAGE_STATUS_TYPE.ERROR)) - } - await this.chatAddMessageAppropriate (message) - } - /** - * Fetches the latest url & media key for the given message. - * You may need to call this when the message is old & the content is deleted off of the WA servers - * @param message - */ - @Mutex (message => message?.key?.id) - async updateMediaMessage (message: WAMessage) { - const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage - if (!content) throw new BaileysError (`given message ${message.key.id} is not a media message`, message) - - const query = ['query',{type: 'media', index: message.key.id, owner: message.key.fromMe ? 'true' : 'false', jid: message.key.remoteJid, epoch: this.msgCount.toString()},null] - const response = await this.query ({ - json: query, - binaryTags: [WAMetric.queryMedia, WAFlag.ignore], - expect200: true, - requiresPhoneConnection: true - }) - Object.keys (response[1]).forEach (key => content[key] = response[1][key]) // update message - } - async downloadMediaMessage (message: WAMessage): Promise - async downloadMediaMessage (message: WAMessage, type: 'buffer'): Promise - async downloadMediaMessage (message: WAMessage, type: 'stream'): Promise - /** - * Securely downloads the media from the message. - * Renews the download url automatically, if necessary. - */ - @Mutex (message => message?.key?.id) - async downloadMediaMessage (message: WAMessage, type: 'buffer' | 'stream' = 'buffer') { - let mContent = message.message?.ephemeralMessage?.message || message.message - if (!mContent) throw new BaileysError('No message present', { status: 400 }) - - const downloadMediaMessage = async () => { - const stream = await decryptMediaMessageBuffer(mContent) - if(type === 'buffer') { - let buffer = Buffer.from([]) - for await(const chunk of stream) { - buffer = Buffer.concat([buffer, chunk]) - } - return buffer - } - return stream - } - - try { - const buff = await downloadMediaMessage() - return buff - } catch (error) { - if (error instanceof BaileysError && error.status === 404) { // media needs to be updated - this.logger.info (`updating media of message: ${message.key.id}`) - await this.updateMediaMessage (message) - mContent = message.message?.ephemeralMessage?.message || message.message - const buff = await downloadMediaMessage() - return buff - } - throw error - } - } - /** - * Securely downloads the media from the message and saves to a file. - * Renews the download url automatically, if necessary. - * @param message the media message you want to decode - * @param filename the name of the file where the media will be saved - * @param attachExtension should the parsed extension be applied automatically to the file - */ - async downloadAndSaveMediaMessage (message: WAMessage, filename: string, attachExtension: boolean=true) { - const extension = extensionForMediaMessage (message.message) - const trueFileName = attachExtension ? (filename + '.' + extension) : filename - const buffer = await this.downloadMediaMessage(message) - - await fs.writeFile(trueFileName, buffer) - return trueFileName - } - /** Query a string to check if it has a url, if it does, return required extended text message */ - async generateLinkPreview (text: string) { - const query = ['query', {type: 'url', url: text, epoch: this.msgCount.toString()}, null] - const response = await this.query ({json: query, binaryTags: [26, WAFlag.ignore], expect200: true, requiresPhoneConnection: false}) - - if (response[1]) response[1].jpegThumbnail = response[2] - const data = response[1] as WAUrlInfo - - const content = {text} as WATextMessage - content.canonicalUrl = data['canonical-url'] - content.matchedText = data['matched-text'] - content.jpegThumbnail = data.jpegThumbnail - content.description = data.description - content.title = data.title - content.previewType = 0 - return content - } - @Mutex () - protected async refreshMediaConn (forceGet = false) { - if (!this.mediaConn || forceGet || (new Date().getTime()-this.mediaConn.fetchDate.getTime()) > this.mediaConn.ttl*1000) { - this.mediaConn = await this.getNewMediaConn() - this.mediaConn.fetchDate = new Date() - } - return this.mediaConn - } - protected async getNewMediaConn () { - const {media_conn} = await this.query({json: ['query', 'mediaConn'], requiresPhoneConnection: false}) - return media_conn as MediaConnInfo - } -} diff --git a/src/WAConnection/7.MessagesExtra.ts b/src/WAConnection/7.MessagesExtra.ts deleted file mode 100644 index 2e255e7..0000000 --- a/src/WAConnection/7.MessagesExtra.ts +++ /dev/null @@ -1,481 +0,0 @@ -import {WAConnection as Base} from './6.MessagesSend' -import { MessageType, WAMessageKey, MessageInfo, WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto, ChatModification, BaileysError, WAChatIndex, WAChat } from './Constants' -import { whatsappID, delay, toNumber, unixTimestampSeconds, GET_MESSAGE_ID, isGroupID, newMessagesDB } from './Utils' -import { Mutex } from './Mutex' - -export class WAConnection extends Base { - - @Mutex () - async loadAllUnreadMessages () { - const tasks = this.chats.all() - .filter(chat => chat.count > 0) - .map (chat => this.loadMessages(chat.jid, chat.count)) - const list = await Promise.all (tasks) - const combined: WAMessage[] = [] - list.forEach (({messages}) => combined.push(...messages)) - return combined - } - /** Get the message info, who has read it, who its been delivered to */ - @Mutex ((jid, messageID) => jid+messageID) - async messageInfo (jid: string, messageID: string) { - const query = ['query', {type: 'message_info', index: messageID, jid: jid, epoch: this.msgCount.toString()}, null] - const [,,response] = await this.query ({ - json: query, - binaryTags: [WAMetric.queryRead, WAFlag.ignore], - expect200: true, - requiresPhoneConnection: true - }) - - const info: MessageInfo = {reads: [], deliveries: []} - if (response) { - const reads = response.filter (node => node[0] === 'read') - if (reads[0]) { - info.reads = reads[0][2].map (item => item[1]) - } - const deliveries = response.filter (node => node[0] === 'delivery') - if (deliveries[0]) { - info.deliveries = deliveries[0][2].map (item => item[1]) - } - } - return info - } - /** - * Marks a chat as read/unread; updates the chat object too - * @param jid the ID of the person/group whose message you want to mark read - * @param unread unreads the chat, if true - */ - @Mutex (jid => jid) - async chatRead (jid: string, type: 'unread' | 'read' = 'read') { - jid = whatsappID (jid) - const chat = this.assertChatGet (jid) - - const count = type === 'unread' ? '-2' : Math.abs(chat.count).toString() - if (type === 'unread' || chat.count !== 0) { - const idx = await this.getChatIndex(jid) - await this.setQuery ([ - ['read', { jid, count, ...idx, participant: undefined }, null] - ], [ WAMetric.read, WAFlag.ignore ]) - } - chat.count = type === 'unread' ? -1 : 0 - this.emit ('chat-update', {jid, count: chat.count}) - } - /** - * Sends a read receipt for a given message; - * does not update the chat do @see chatRead - * @deprecated just use chatRead() - * @param jid the ID of the person/group whose message you want to mark read - * @param messageKey the key of the message - * @param count number of messages to read, set to < 0 to unread a message - */ - async sendReadReceipt(jid: string, messageKey: WAMessageKey, count: number) { - const attributes = { - jid, - count: count.toString(), - index: messageKey?.id, - participant: messageKey?.participant || undefined, - owner: messageKey?.fromMe?.toString() - } - const read = await this.setQuery ([['read', attributes, null]], [ WAMetric.read, WAFlag.ignore ]) - return read - } - async fetchMessagesFromWA (jid: string, count: number, indexMessage?: { id?: string; fromMe?: boolean }, mostRecentFirst: boolean = true) { - const json = [ - 'query', - { - epoch: this.msgCount.toString(), - type: 'message', - jid: jid, - kind: mostRecentFirst ? 'before' : 'after', - count: count.toString(), - index: indexMessage?.id, - owner: indexMessage?.fromMe === false ? 'false' : 'true', - }, - null, - ] - const response = await this.query({json, binaryTags: [WAMetric.queryMessages, WAFlag.ignore], expect200: false, requiresPhoneConnection: true}) - return (response[2] as WANode[])?.map(item => item[2] as WAMessage) || [] - } - /** - * Load the conversation with a group or person - * @param count the number of messages to load - * @param cursor the data for which message to offset the query by - * @param mostRecentFirst retrieve the most recent message first or retrieve from the converation start - */ - @Mutex (jid => jid) - async loadMessages ( - jid: string, - count: number, - cursor?: { id?: string; fromMe?: boolean }, - mostRecentFirst: boolean = true - ) { - jid = whatsappID(jid) - - const retrieve = (count: number, indexMessage: any) => this.fetchMessagesFromWA (jid, count, indexMessage, mostRecentFirst) - - const chat = this.chats.get (jid) - const hasCursor = cursor?.id && typeof cursor?.fromMe !== 'undefined' - const cursorValue = hasCursor && chat?.messages.get (GET_MESSAGE_ID(cursor)) - - let messages: WAMessage[] - if (chat?.messages && mostRecentFirst && (!hasCursor || cursorValue)) { - messages = chat.messages.paginatedByValue (cursorValue, count, null, 'before') - - const diff = count - messages.length - if (diff < 0) { - messages = messages.slice(-count) // get the last X messages - } else if (diff > 0) { - const fMessage = chat.messages.all()[0] - let fepoch = (fMessage && fMessage['epoch']) || 0 - const extra = await retrieve (diff, messages[0]?.key || cursor) - // add to DB - for (let i = extra.length-1;i >= 0; i--) { - const m = extra[i] - fepoch -= 1 - m['epoch'] = fepoch - - if(chat.messages.length < this.maxCachedMessages) { - chat.messages.insertIfAbsent(m) - } - } - messages.unshift (...extra) - } - } else messages = await retrieve (count, cursor) - - if (messages[0]) cursor = { id: messages[0].key.id, fromMe: messages[0].key.fromMe } - else cursor = null - - return {messages, cursor} - } - /** - * Load the entire friggin conversation with a group or person - * @param onMessage callback for every message retrieved - * @param chunkSize the number of messages to load in a single request - * @param mostRecentFirst retrieve the most recent message first or retrieve from the converation start - */ - loadAllMessages(jid: string, onMessage: (m: WAMessage) => Promise|void, chunkSize = 25, mostRecentFirst = true) { - let offsetID = null - const loadMessage = async () => { - const {messages} = await this.loadMessages(jid, chunkSize, offsetID, mostRecentFirst) - // callback with most recent message first (descending order of date) - let lastMessage - if (mostRecentFirst) { - for (let i = messages.length - 1; i >= 0; i--) { - await onMessage(messages[i]) - lastMessage = messages[i] - } - } else { - for (let i = 0; i < messages.length; i++) { - await onMessage(messages[i]) - lastMessage = messages[i] - } - } - // if there are still more messages - if (messages.length >= chunkSize) { - offsetID = lastMessage.key // get the last message - await delay(200) - return loadMessage() - } - } - return loadMessage() as Promise - } - /** - * Find a message in a given conversation - * @param chunkSize the number of messages to load in a single request - * @param onMessage callback for every message retrieved, if return true -- the loop will break - */ - async findMessage (jid: string, chunkSize: number, onMessage: (m: WAMessage) => boolean) { - const chat = this.chats.get (whatsappID(jid)) - let count = chat?.messages?.all().length || chunkSize - let offsetID - while (true) { - const {messages, cursor} = await this.loadMessages(jid, count, offsetID, true) - // callback with most recent message first (descending order of date) - for (let i = messages.length - 1; i >= 0; i--) { - if (onMessage(messages[i])) return - } - if (messages.length === 0) return - // if there are more messages - offsetID = cursor - await delay (200) - } - } - /** - * Loads all messages sent after a specific date - */ - async messagesReceivedAfter (date: Date, onlyUnrespondedMessages = false) { - const stamp = unixTimestampSeconds (date) - // find the index where the chat timestamp becomes greater - const idx = this.chats.all ().findIndex (c => c.t < stamp) - // all chats before that index -- i.e. all chats that were updated after that - const chats = this.chats.all ().slice (0, idx) - - const messages: WAMessage[] = [] - await Promise.all ( - chats.map (async chat => { - await this.findMessage (chat.jid, 5, m => { - if (toNumber(m.messageTimestamp) < stamp || (onlyUnrespondedMessages && m.key.fromMe)) return true - messages.push (m) - }) - }) - ) - return messages - } - /** Load a single message specified by the ID */ - async loadMessage (jid: string, id: string) { - let message: WAMessage - - jid = whatsappID (jid) - const chat = this.chats.get (jid) - if (chat) { - // see if message is present in cache - message = chat.messages.get (GET_MESSAGE_ID({ id, fromMe: true })) || chat.messages.get (GET_MESSAGE_ID({ id, fromMe: false })) - } - if (!message) { - // load the message before the given message - let messages = (await this.loadMessages (jid, 1, {id, fromMe: true})).messages - if (!messages[0]) messages = (await this.loadMessages (jid, 1, {id, fromMe: false})).messages - // the message after the loaded message is the message required - const actual = await this.loadMessages (jid, 1, messages[0] && messages[0].key, false) - message = actual.messages[0] - } - return message - } - /** - * Search WhatsApp messages with a given text string - * @param txt the search string - * @param inJid the ID of the chat to search in, set to null to search all chats - * @param count number of results to return - * @param page page number of results (starts from 1) - */ - async searchMessages(txt: string, inJid: string | null, count: number, page: number) { - const json = [ - 'query', - { - epoch: this.msgCount.toString(), - type: 'search', - search: Buffer.from(txt, 'utf-8'), - count: count.toString(), - page: page.toString(), - jid: inJid - }, - null, - ] - - const response: WANode = await this.query({json, binaryTags: [24, WAFlag.ignore], expect200: true}) // encrypt and send off - const messages = response[2] ? response[2].map (row => row[2]) : [] - return { - last: response[1]['last'] === 'true', - messages: messages as WAMessage[] - } - } - /** - * Delete a message in a chat for yourself - * @param messageKey key of the message you want to delete - */ - @Mutex (m => m.remoteJid) - async clearMessage (messageKey: WAMessageKey) { - const tag = Math.round(Math.random ()*1000000) - const attrs: WANode = [ - 'chat', - { jid: messageKey.remoteJid, modify_tag: tag.toString(), type: 'clear' }, - [ - ['item', {owner: `${messageKey.fromMe}`, index: messageKey.id}, null] - ] - ] - const result = await this.setQuery ([attrs]) - - const chat = this.chats.get (whatsappID(messageKey.remoteJid)) - if (chat) { - const value = chat.messages.get (GET_MESSAGE_ID(messageKey)) - value && chat.messages.delete (value) - } - return result - } - /** - * Star or unstar a message - * @param messageKey key of the message you want to star or unstar - */ - @Mutex (m => m.remoteJid) - async starMessage (messageKey: WAMessageKey, type: 'star' | 'unstar' = 'star') { - const attrs: WANode = [ - 'chat', - { - jid: messageKey.remoteJid, - type - }, - [ - ['item', {owner: `${messageKey.fromMe}`, index: messageKey.id}, null] - ] - ] - const result = await this.setQuery ([attrs]) - - const chat = this.chats.get (whatsappID(messageKey.remoteJid)) - if (result.status == 200 && chat) { - const message = chat.messages.get (GET_MESSAGE_ID(messageKey)) - if (message) { - message.starred = type === 'star' - - const chatUpdate: Partial = { jid: messageKey.remoteJid, messages: newMessagesDB([ message ]) } - this.emit ('chat-update', chatUpdate) - } - } - return result - } - /** - * Delete a message in a chat for everyone - * @param id the person or group where you're trying to delete the message - * @param messageKey key of the message you want to delete - */ - async deleteMessage (k: string | WAMessageKey, messageKey?: WAMessageKey) { - if (typeof k === 'object') { - messageKey = k - } - const json: WAMessageContent = { - protocolMessage: { - key: messageKey, - type: WAMessageProto.ProtocolMessage.ProtocolMessageType.REVOKE - } - } - const waMessage = this.prepareMessageFromContent (messageKey.remoteJid, json, {}) - await this.relayWAMessage (waMessage) - return waMessage - } - /** - * Generate forwarded message content like WA does - * @param message the message to forward - * @param forceForward will show the message as forwarded even if it is from you - */ - generateForwardMessageContent (message: WAMessage, forceForward: boolean=false) { - let content = message.message - if (!content) throw new BaileysError ('no content in message', { status: 400 }) - content = WAMessageProto.Message.fromObject(content) // hacky copy - - let key = Object.keys(content)[0] - - let score = content[key].contextInfo?.forwardingScore || 0 - score += message.key.fromMe && !forceForward ? 0 : 1 - if (key === MessageType.text) { - content[MessageType.extendedText] = { text: content[key] } - delete content[MessageType.text] - - key = MessageType.extendedText - } - if (score > 0) content[key].contextInfo = { forwardingScore: score, isForwarded: true } - else content[key].contextInfo = {} - return content - } - /** - * Forward a message like WA - * @param jid the chat ID to forward to - * @param message the message to forward - * @param forceForward will show the message as forwarded even if it is from you - */ - async forwardMessage(jid: string, message: WAMessage, forceForward: boolean=false) { - const content = this.generateForwardMessageContent(message, forceForward) - const waMessage = this.prepareMessageFromContent (jid, content, {}) - await this.relayWAMessage (waMessage) - return waMessage - } - /** - * Clear the chat messages - * @param jid the ID of the person/group you are modifiying - * @param includeStarred delete starred messages, default false - */ - async modifyChat (jid: string, type: ChatModification.clear, includeStarred?: boolean): Promise<{status: number;}>; - /** - * Modify a given chat (archive, pin etc.) - * @param jid the ID of the person/group you are modifiying - * @param durationMs only for muting, how long to mute the chat for - */ - async modifyChat (jid: string, type: ChatModification.pin | ChatModification.mute, durationMs: number): Promise<{status: number;}>; - /** - * Modify a given chat (archive, pin etc.) - * @param jid the ID of the person/group you are modifiying - */ - async modifyChat (jid: string, type: ChatModification | (keyof typeof ChatModification)): Promise<{status: number;}>; - @Mutex ((jid, type) => jid+type) - async modifyChat (jid: string, type: (keyof typeof ChatModification), arg?: number | boolean): Promise<{status: number;}> { - jid = whatsappID (jid) - const chat = this.assertChatGet (jid) - - let chatAttrs: Record = {jid: jid} - if (type === ChatModification.mute && !arg) { - throw new BaileysError( - 'duration must be set to the timestamp of the time of pinning/unpinning of the chat', - { status: 400 } - ) - } - - const durationMs:number = arg as number || 0 - const includeStarred:boolean = arg as boolean - let index: WAChatIndex; - switch (type) { - case ChatModification.pin: - case ChatModification.mute: - const strStamp = (unixTimestampSeconds() + Math.floor(durationMs/1000)).toString() - chatAttrs.type = type - chatAttrs[type] = strStamp - break - case ChatModification.unpin: - case ChatModification.unmute: - chatAttrs.type = type.replace ('un', '') // replace 'unpin' with 'pin' - chatAttrs.previous = chat[type.replace ('un', '')] - break - case ChatModification.clear: - chatAttrs.type = type - chatAttrs.star = includeStarred ? 'true' : 'false' - index = await this.getChatIndex(jid) - chatAttrs = { ...chatAttrs, ...index } - delete chatAttrs.participant - break - default: - chatAttrs.type = type - index = await this.getChatIndex(jid) - chatAttrs = { ...chatAttrs, ...index } - break - } - - const response = await this.setQuery ([['chat', chatAttrs, null]], [ WAMetric.chat, WAFlag.ignore ]) - - if (chat && response.status === 200) { - switch(type) { - case ChatModification.clear: - if (includeStarred) { - chat.messages.clear() - } else { - chat.messages = chat.messages.filter(m => m.starred) - } - break - case ChatModification.delete: - this.chats.deleteById(jid) - this.emit('chat-update', { jid, delete: 'true' }) - break - default: - this.chats.update(jid, chat => { - if (type.includes('un')) { - type = type.replace ('un', '') as ChatModification - delete chat[type.replace('un','')] - this.emit ('chat-update', { jid, [type]: false }) - } else { - chat[type] = chatAttrs[type] || 'true' - this.emit ('chat-update', { jid, [type]: chat[type] }) - } - }) - break - } - } - return response - } - protected async getChatIndex (jid: string): Promise { - const chatAttrs = {} as WAChatIndex - const { messages: [msg] } = await this.loadMessages(jid, 1) - if (msg) { - chatAttrs.index = msg.key.id - chatAttrs.owner = msg.key.fromMe.toString() as 'true' | 'false' - } - if (isGroupID(jid)) { - chatAttrs.participant = msg.key.fromMe ? this.user?.jid : whatsappID(msg.participant || msg.key.participant) - } - return chatAttrs - } -} diff --git a/src/WAConnection/8.Groups.ts b/src/WAConnection/8.Groups.ts deleted file mode 100644 index 379768d..0000000 --- a/src/WAConnection/8.Groups.ts +++ /dev/null @@ -1,202 +0,0 @@ -import {WAConnection as Base} from './7.MessagesExtra' -import { WAMetric, WAFlag, WANode, WAGroupMetadata, WAGroupCreateResponse, WAGroupModification, BaileysError } from '../WAConnection/Constants' -import { GroupSettingChange } from './Constants' -import { generateMessageID, whatsappID } from '../WAConnection/Utils' -import { Mutex } from './Mutex' - -export class WAConnection extends Base { - /** Generic function for group queries */ - async groupQuery(type: string, jid?: string, subject?: string, participants?: string[], additionalNodes?: WANode[]) { - const tag = this.generateMessageTag() - const json: WANode = [ - 'group', - { - author: this.user.jid, - id: tag, - type: type, - jid: jid, - subject: subject, - }, - participants ? participants.map(jid => ['participant', { jid }, null]) : additionalNodes, - ] - const result = await this.setQuery ([json], [WAMetric.group, 136], tag) - return result - } - /** - * Get the metadata of the group - * Baileys automatically caches & maintains this state - */ - @Mutex(jid => jid) - async groupMetadata (jid: string) { - const chat = this.chats.get(jid) - let metadata = chat?.metadata - if (!metadata) { - if (chat?.read_only) { - metadata = await this.groupMetadataMinimal(jid) - } else { - metadata = await this.fetchGroupMetadataFromWA(jid) - } - if (chat) chat.metadata = metadata - } - return metadata - } - /** Get the metadata of the group from WA */ - fetchGroupMetadataFromWA = async (jid: string) => { - const metadata = await this.query({json: ['query', 'GroupMetadata', jid], expect200: true}) - metadata.participants = metadata.participants.map(p => ( - { ...this.contactAddOrGet(p.id), ...p } - )) - return metadata as WAGroupMetadata - } - /** Get the metadata (works after you've left the group also) */ - groupMetadataMinimal = async (jid: string) => { - const query = ['query', {type: 'group', jid: jid, epoch: this.msgCount.toString()}, null] - const response = await this.query({json: query, binaryTags: [WAMetric.group, WAFlag.ignore], expect200: true}) - const json = response[2][0] - const creatorDesc = json[1] - const participants = json[2] ? json[2].filter (item => item[0] === 'participant') : [] - const description = json[2] ? json[2].find (item => item[0] === 'description') : null - return { - id: jid, - owner: creatorDesc?.creator, - creator: creatorDesc?.creator, - creation: parseInt(creatorDesc?.create), - subject: null, - desc: description && description[2].toString('utf-8'), - participants: participants.map (item => ( - { ...this.contactAddOrGet(item[1].jid), isAdmin: item[1].type === 'admin' } - )) - } as WAGroupMetadata - } - /** - * Create a group - * @param title like, the title of the group - * @param participants people to include in the group - */ - groupCreate = async (title: string, participants: string[]) => { - const response = await this.groupQuery('create', null, title, participants) as WAGroupCreateResponse - const gid = response.gid - let metadata: WAGroupMetadata - try { - metadata = await this.groupMetadata (gid) - } catch (error) { - this.logger.warn (`error in group creation: ${error}, switching gid & checking`) - // if metadata is not available - const comps = gid.replace ('@g.us', '').split ('-') - response.gid = `${comps[0]}-${+comps[1] + 1}@g.us` - - metadata = await this.groupMetadata (gid) - this.logger.warn (`group ID switched from ${gid} to ${response.gid}`) - } - await this.chatAdd(response.gid, title, { metadata }) - return response - } - /** - * Leave a group - * @param jid the ID of the group - */ - groupLeave = async (jid: string) => { - const response = await this.groupQuery('leave', jid) - - const chat = this.chats.get (jid) - if (chat) chat.read_only = 'true' - - return response - } - /** - * Update the subject of the group - * @param {string} jid the ID of the group - * @param {string} title the new title of the group - */ - groupUpdateSubject = async (jid: string, title: string) => { - const chat = this.chats.get (jid) - if (chat?.name === title) throw new BaileysError ('redundant change', { status: 400 }) - - const response = await this.groupQuery('subject', jid, title) - if (chat) chat.name = title - - return response - } - - /** - * Update the group description - * @param {string} jid the ID of the group - * @param {string} title the new title of the group - */ - groupUpdateDescription = async (jid: string, description: string) => { - const metadata = await this.groupMetadata (jid) - const node: WANode = [ - 'description', - {id: generateMessageID(), prev: metadata?.descId}, - Buffer.from (description, 'utf-8') - ] - const response = await this.groupQuery ('description', jid, null, null, [node]) - return response - } - /** - * Add somebody to the group - * @param jid the ID of the group - * @param participants the people to add - */ - groupAdd = (jid: string, participants: string[]) => - this.groupQuery('add', jid, null, participants) as Promise - /** - * Remove somebody from the group - * @param jid the ID of the group - * @param participants the people to remove - */ - groupRemove = (jid: string, participants: string[]) => - this.groupQuery('remove', jid, null, participants) as Promise - /** - * Make someone admin on the group - * @param jid the ID of the group - * @param participants the people to make admin - */ - groupMakeAdmin = (jid: string, participants: string[]) => - this.groupQuery('promote', jid, null, participants) as Promise - /** - * Make demote an admin on the group - * @param jid the ID of the group - * @param participants the people to make admin - */ - groupDemoteAdmin = (jid: string, participants: string[]) => - this.groupQuery('demote', jid, null, participants) as Promise - /** - * Make demote an admin on the group - * @param jid the ID of the group - * @param participants the people to make admin - */ - groupSettingChange = (jid: string, setting: GroupSettingChange, onlyAdmins: boolean) => { - const node: WANode = [ setting, {value: onlyAdmins ? 'true' : 'false'}, null ] - return this.groupQuery('prop', jid, null, null, [node]) as Promise<{status: number}> - } - /** - * Get the invite link of the given group - * @param jid the ID of the group - * @returns invite code - */ - async groupInviteCode(jid: string) { - const json = ['query', 'inviteCode', jid] - const response = await this.query({json, expect200: true, requiresPhoneConnection: false}) - return response.code as string - } - /** - * Join group via invite code - * @param code the invite code - * @returns Object containing gid - */ - async acceptInvite(code: string) { - const json = ['action', 'invite', code] - const response = await this.query({json, expect200: true}) - return response - } - /** - * Revokes the current invite link for a group chat - * @param jid the ID of the group - */ - async revokeInvite(jid: string) { - const json = ['action', 'inviteReset', jid] - const response = await this.query({json, expect200: true}) - return response - } -} diff --git a/src/WAConnection/Constants.ts b/src/WAConnection/Constants.ts deleted file mode 100644 index e1af32a..0000000 --- a/src/WAConnection/Constants.ts +++ /dev/null @@ -1,514 +0,0 @@ -import { WA } from '../Binary/Constants' -import { proto } from '../../WAMessage/WAMessage' -import { Agent } from 'https' -import KeyedDB from '@adiwajshing/keyed-db' -import { URL } from 'url' - -export const WS_URL = 'wss://web.whatsapp.com/ws' -export const DEFAULT_ORIGIN = 'https://web.whatsapp.com' - -export const KEEP_ALIVE_INTERVAL_MS = 20*1000 -export const WA_DEFAULT_EPHEMERAL = 7*24*60*60 - -// export the WAMessage Prototypes -export { proto as WAMessageProto } -export type WANode = WA.Node -export type WAMessage = proto.WebMessageInfo -export type WAMessageContent = proto.IMessage -export type WAContactMessage = proto.ContactMessage -export type WAContactsArrayMessage = proto.ContactsArrayMessage -export type WAGroupInviteMessage = proto.GroupInviteMessage -export type WAListMessage = proto.ListMessage -export type WAButtonsMessage = proto.ButtonsMessage -export type WAMessageKey = proto.IMessageKey -export type WATextMessage = proto.ExtendedTextMessage -export type WAContextInfo = proto.IContextInfo -export type WAGenericMediaMessage = proto.IVideoMessage | proto.IImageMessage | proto.IAudioMessage | proto.IDocumentMessage | proto.IStickerMessage -export import WA_MESSAGE_STUB_TYPE = proto.WebMessageInfo.WebMessageInfoStubType -export import WA_MESSAGE_STATUS_TYPE = proto.WebMessageInfo.WebMessageInfoStatus - -export type WAInitResponse = { - ref: string - ttl: number - status: 200 -} - -export interface WABusinessProfile { - description: string - email: string - business_hours: WABusinessHours - website: string[] - categories: WABusinessCategories[] - wid?: string -} - -export type WABusinessCategories = { - id: string - localized_display_name: string -} - -export type WABusinessHours = { - timezone: string - config?: WABusinessHoursConfig[] - business_config?: WABusinessHoursConfig[] -} - -export type WABusinessHoursConfig = { - day_of_week: string - mode: string - open_time?: number - close_time?: number -} - -export interface WALocationMessage { - degreesLatitude: number - degreesLongitude: number - address?: string -} -/** Reverse stub type dictionary */ -export const WA_MESSAGE_STUB_TYPES = function () { - const types = WA_MESSAGE_STUB_TYPE - const dict: Record = {} - Object.keys(types).forEach(element => dict[ types[element] ] = element) - return dict -}() - -export class BaileysError extends Error { - status?: number - context: any - - constructor (message: string, context: any, stack?: string) { - super (message) - this.name = 'BaileysError' - this.status = context.status - this.context = context - if(stack) { - this.stack = stack - } - } -} -export const TimedOutError = (stack?: string) => new BaileysError ('timed out', { status: 408 }, stack) -export const CancelledError = (stack?: string) => new BaileysError ('cancelled', { status: 500 }, stack) - -export interface WAQuery { - json: any[] | WANode - binaryTags?: WATag - timeoutMs?: number - tag?: string - expect200?: boolean - waitForOpen?: boolean - longTag?: boolean - requiresPhoneConnection?: boolean - startDebouncedTimeout?: boolean - maxRetries?: number -} - -export type WAMediaUpload = Buffer | { url: URL | string } - -export enum ReconnectMode { - /** does not reconnect */ - off = 0, - /** reconnects only when the connection is 'lost' or 'close' */ - onConnectionLost = 1, - /** reconnects on all disconnects, including take overs */ - onAllErrors = 2 -} -export type WALoadChatOptions = { - searchString?: string - custom?: (c: WAChat) => boolean -} -export type WAConnectOptions = { - /** fails the connection if no data is received for X seconds */ - maxIdleTimeMs?: number - /** maximum attempts to connect */ - maxRetries?: number - /** max time for the phone to respond to a connectivity test */ - phoneResponseTime?: number - connectCooldownMs?: number - /** agent used for WS connections */ - agent?: Agent - /** agent used for fetch requests -- uploading/downloading media */ - fetchAgent?: Agent - /** Always uses takeover for connections */ - alwaysUseTakeover?: boolean - /** - * Sometimes WA does not send the chats, - * this keeps pinging the phone to send the chats over - * */ - queryChatsTillReceived?: boolean - /** max time for the phone to respond to a query */ - maxQueryResponseTime?: number - /** Log QR to terminal or not */ - logQR?: boolean -} -/** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */ -export const URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)?/gi - -export type WAConnectionState = 'open' | 'connecting' | 'close' - -export const UNAUTHORIZED_CODES = [401, 419] -/** Types of Disconnect Reasons */ -export enum DisconnectReason { - /** The connection was closed intentionally */ - intentional = 'intentional', - /** The connection was terminated either by the client or server */ - close = 'close', - /** The connection was lost, called when the server stops responding to requests */ - lost = 'lost', - /** When WA Web is opened elsewhere & this session is disconnected */ - replaced = 'replaced', - /** The credentials for the session have been invalidated, i.e. logged out either from the phone or WA Web */ - invalidSession = 'invalid_session', - /** Received a 500 result in a query -- something has gone very wrong */ - badSession = 'bad_session', - /** No idea, can be a sign of log out too */ - unknown = 'unknown', - /** Well, the connection timed out */ - timedOut = 'timed out' -} -export interface MediaConnInfo { - auth: string - ttl: number - hosts: { - hostname: string - }[] - fetchDate: Date -} -export interface AuthenticationCredentials { - clientID: string - serverToken: string - clientToken: string - encKey: Buffer - macKey: Buffer -} -export interface AuthenticationCredentialsBase64 { - clientID: string - serverToken: string - clientToken: string - encKey: string - macKey: string -} -export interface AuthenticationCredentialsBrowser { - WABrowserId: string - WASecretBundle: {encKey: string, macKey: string} | string - WAToken1: string - WAToken2: string -} -export type AnyAuthenticationCredentials = AuthenticationCredentialsBrowser | AuthenticationCredentialsBase64 | AuthenticationCredentials - -export interface WAGroupCreateResponse { - status: number - gid?: string - participants?: [{ [key: string]: any }] -} -export type WAGroupParticipant = (WAContact & { isAdmin: boolean; isSuperAdmin: boolean }) -export interface WAGroupMetadata { - id: string - owner: string - subject: string - creation: number - desc?: string - descOwner?: string - descId?: string - /** is set when the group only allows admins to change group settings */ - restrict?: 'true' | 'false' - /** is set when the group only allows admins to write messages */ - announce?: 'true' | 'false' - // Baileys modified array - participants: WAGroupParticipant[] -} -export interface WAGroupModification { - status: number - participants?: { [key: string]: any } -} -export interface WAPresenceData { - lastKnownPresence?: Presence - lastSeen?: number - name?: string -} -export interface WAContact { - verify?: string - /** name of the contact, the contact has set on their own on WA */ - notify?: string - jid: string - /** I have no idea */ - vname?: string - /** name of the contact, you have saved on your WA */ - name?: string - index?: string - /** short name for the contact */ - short?: string - // Baileys Added - imgUrl?: string -} -export interface WAUser extends WAContact { - phone: any -} -export type WAContactUpdate = Partial & { jid: string, status?: string } -export interface WAChat { - jid: string - - t: number - /** number of unread messages, is < 0 if the chat is manually marked unread */ - count: number - archive?: 'true' | 'false' - clear?: 'true' | 'false' - read_only?: 'true' | 'false' - mute?: string - pin?: string - spam?: 'false' | 'true' - modify_tag?: string - name?: string - /** when ephemeral messages were toggled on */ - eph_setting_ts?: string - /** how long each message lasts for */ - ephemeral?: string - - // Baileys added properties - messages: KeyedDB - imgUrl?: string - presences?: { [k: string]: WAPresenceData } - metadata?: WAGroupMetadata -} -export type WAChatIndex = { index: string, owner: 'true' | 'false', participant?: string } -export type WAChatUpdate = Partial & { jid: string, hasNewMessage?: boolean } -export enum WAMetric { - debugLog = 1, - queryResume = 2, - liveLocation = 3, - queryMedia = 4, - queryChat = 5, - queryContact = 6, - queryMessages = 7, - presence = 8, - presenceSubscribe = 9, - group = 10, - read = 11, - chat = 12, - received = 13, - picture = 14, - status = 15, - message = 16, - queryActions = 17, - block = 18, - queryGroup = 19, - queryPreview = 20, - queryEmoji = 21, - queryRead = 22, - queryVCard = 29, - queryStatus = 30, - queryStatusUpdate = 31, - queryLiveLocation = 33, - queryLabel = 36, - queryQuickReply = 39 -} - -export const STORIES_JID = 'status@broadcast' - -export enum WAFlag { - available = 160, - other = 136, // don't know this one - ignore = 1 << 7, - acknowledge = 1 << 6, - unavailable = 1 << 4, - expires = 1 << 3, - composing = 1 << 2, - recording = 1 << 2, - paused = 1 << 2 -} -/** Tag used with binary queries */ -export type WATag = [WAMetric, WAFlag] -/** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */ -export enum Presence { - unavailable = 'unavailable', // "offline" - available = 'available', // "online" - composing = 'composing', // "typing..." - recording = 'recording', // "recording..." - paused = 'paused', // stop typing -} -/** Set of message types that are supported by the library */ -export enum MessageType { - text = 'conversation', - extendedText = 'extendedTextMessage', - contact = 'contactMessage', - contactsArray = 'contactsArrayMessage', - groupInviteMessage = 'groupInviteMessage', - listMessage = 'listMessage', - buttonsMessage = 'buttonsMessage', - location = 'locationMessage', - liveLocation = 'liveLocationMessage', - - image = 'imageMessage', - video = 'videoMessage', - sticker = 'stickerMessage', - document = 'documentMessage', - audio = 'audioMessage', - product = 'productMessage' -} - -export const MessageTypeProto = { - [MessageType.image]: proto.ImageMessage, - [MessageType.video]: proto.VideoMessage, - [MessageType.audio]: proto.AudioMessage, - [MessageType.sticker]: proto.StickerMessage, - [MessageType.document]: proto.DocumentMessage, -} -export enum ChatModification { - archive='archive', - unarchive='unarchive', - pin='pin', - unpin='unpin', - mute='mute', - unmute='unmute', - delete='delete', - clear='clear' -} -export const HKDFInfoKeys = { - [MessageType.image]: 'WhatsApp Image Keys', - [MessageType.audio]: 'WhatsApp Audio Keys', - [MessageType.video]: 'WhatsApp Video Keys', - [MessageType.document]: 'WhatsApp Document Keys', - [MessageType.sticker]: 'WhatsApp Image Keys' -} -export enum Mimetype { - jpeg = 'image/jpeg', - png = 'image/png', - mp4 = 'video/mp4', - gif = 'video/gif', - pdf = 'application/pdf', - ogg = 'audio/ogg; codecs=opus', - mp4Audio = 'audio/mp4', - /** for stickers */ - webp = 'image/webp', -} -export interface MessageOptions { - /** the message you want to quote */ - quoted?: WAMessage - /** some random context info (can show a forwarded message with this too) */ - contextInfo?: WAContextInfo - /** optional, if you want to manually set the timestamp of the message */ - timestamp?: Date - /** (for media messages) the caption to send with the media (cannot be sent with stickers though) */ - caption?: string - /** - * For location & media messages -- 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 - * */ - thumbnail?: string - /** (for media messages) specify the type of media (optional for all media types except documents) */ - mimetype?: Mimetype | string - /** (for media messages) file name for the media */ - filename?: string - /** For audio messages, if set to true, will send as a `voice note` */ - ptt?: boolean - /** For image or video messages, if set to true, will send as a `viewOnceMessage` */ - viewOnce?: boolean - /** Optional agent for media uploads */ - uploadAgent?: Agent - /** If set to true (default), automatically detects if you're sending a link & attaches the preview*/ - detectLinks?: boolean - /** Optionally specify the duration of the media (audio/video) in seconds */ - duration?: number - /** Fetches new media options for every media file */ - forceNewMediaOptions?: boolean - /** Wait for the message to be sent to the server (default true) */ - waitForAck?: boolean - /** Should it send as a disappearing messages. - * By default 'chat' -- which follows the setting of the chat */ - sendEphemeral?: 'chat' | boolean - /** Force message id */ - messageId?: string - /** For sticker messages, if set to true, will considered as animated sticker */ - isAnimated?: boolean -} -export interface WABroadcastListInfo { - status: number - name: string - recipients?: {id: string}[] -} -export interface WAUrlInfo { - 'canonical-url': string - 'matched-text': string - title: string - description: string - jpegThumbnail?: Buffer -} -export interface WAProfilePictureChange { - status: number - tag: string - eurl: string -} -export interface MessageInfo { - reads: {jid: string, t: string}[] - deliveries: {jid: string, t: string}[] -} -export interface WAMessageStatusUpdate { - from: string - to: string - /** Which participant caused the update (only for groups) */ - participant?: string - timestamp: Date - /** Message IDs read/delivered */ - ids: string[] - /** Status of the Message IDs */ - type: WA_MESSAGE_STATUS_TYPE -} - -export interface WAOpenResult { - /** Was this connection opened via a QR scan */ - newConnection?: true - user: WAUser - isNewUser?: true - auth: AuthenticationCredentials -} - -export enum GroupSettingChange { - messageSend = 'announcement', - settingsChange = 'locked', -} -export interface PresenceUpdate { - id: string - participant?: string - t?: string - type?: Presence - deny?: boolean -} -export interface BlocklistUpdate { - added?: string[] - removed?: string[] -} -// path to upload the media -export const MediaPathMap = { - imageMessage: '/mms/image', - videoMessage: '/mms/video', - documentMessage: '/mms/document', - audioMessage: '/mms/audio', - stickerMessage: '/mms/image', -} -// gives WhatsApp info to process the media -export const MimetypeMap = { - imageMessage: Mimetype.jpeg, - videoMessage: Mimetype.mp4, - documentMessage: Mimetype.pdf, - audioMessage: Mimetype.ogg, - stickerMessage: Mimetype.webp, -} -export type WAParticipantAction = 'add' | 'remove' | 'promote' | 'demote' -export type BaileysEvent = - 'open' | - 'connecting' | - 'close' | - 'ws-close' | - 'qr' | - 'connection-phone-change' | - 'contacts-received' | - 'chats-received' | - 'initial-data-received' | - 'chat-new' | - 'chat-update' | - 'group-participants-update' | - 'group-update' | - 'received-pong' | - 'blocklist-update' | - 'contact-update' diff --git a/src/WAConnection/Mutex.ts b/src/WAConnection/Mutex.ts deleted file mode 100644 index 68c44a8..0000000 --- a/src/WAConnection/Mutex.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * A simple mutex that can be used as a decorator. For examples, see Tests.Mutex.ts - * @param keyGetter if you want to lock functions based on certain arguments, specify the key for the function based on the arguments - */ -export function Mutex (keyGetter?: (...args: any[]) => string) { - let tasks: { [k: string]: Promise } = {} - return function (_, __, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value - descriptor.value = function (this: Object, ...args) { - const key = (keyGetter && keyGetter.call(this, ...args)) || 'undefined' - - tasks[key] = (async () => { - try { - tasks[key] && await tasks[key] - } catch { - - } - const result = await originalMethod.call(this, ...args) - return result - })() - return tasks[key] - } - } -} \ No newline at end of file diff --git a/src/WAConnection/Utils.ts b/src/WAConnection/Utils.ts deleted file mode 100644 index 6f101c0..0000000 --- a/src/WAConnection/Utils.ts +++ /dev/null @@ -1,470 +0,0 @@ -import * as Crypto from 'crypto' -import { Readable, Transform } from 'stream' -import HKDF from 'futoin-hkdf' -import Jimp from 'jimp' -import {createReadStream, createWriteStream, promises as fs, WriteStream} from 'fs' -import { exec } from 'child_process' -import {platform, release, tmpdir} from 'os' -import HttpsProxyAgent from 'https-proxy-agent' -import { URL } from 'url' -import { Agent } from 'https' -import Decoder from '../Binary/Decoder' -import { MessageType, HKDFInfoKeys, MessageOptions, WAChat, WAMessageContent, BaileysError, WAMessageProto, TimedOutError, CancelledError, WAGenericMediaMessage, WAMessage, WAMessageKey, DEFAULT_ORIGIN, WAMediaUpload } from './Constants' -import KeyedDB from '@adiwajshing/keyed-db' -import got, { Options, Response } from 'got' -import { join } from 'path' -import { IAudioMetadata } from 'music-metadata' -import { once } from 'events' - -const platformMap = { - 'aix': 'AIX', - 'darwin': 'Mac OS', - 'win32': 'Windows', - 'android': 'Android' -} -export const Browsers = { - ubuntu: browser => ['Ubuntu', browser, '18.04'] as [string, string, string], - macOS: browser => ['Mac OS', browser, '10.15.3'] as [string, string, string], - baileys: browser => ['Baileys', browser, '3.0'] as [string, string, string], - /** The appropriate browser based on your OS & release */ - appropriate: browser => [ platformMap [platform()] || 'Ubuntu', browser, release() ] as [string, string, string] -} -export const toNumber = (t: Long | number) => (t['low'] || t) as number -export const waChatKey = (pin: boolean) => ({ - key: (c: WAChat) => (pin ? (c.pin ? '1' : '0') : '') + (c.archive === 'true' ? '0' : '1') + c.t.toString(16).padStart(8, '0') + c.jid, - compare: (k1: string, k2: string) => k2.localeCompare (k1) -}) -export const waMessageKey = { - key: (m: WAMessage) => (5000 + (m['epoch'] || 0)).toString(16).padStart(6, '0') + toNumber(m.messageTimestamp).toString(16).padStart(8, '0'), - compare: (k1: string, k2: string) => k1.localeCompare (k2) -} -export const WA_MESSAGE_ID = (m: WAMessage) => GET_MESSAGE_ID (m.key) -export const GET_MESSAGE_ID = (key: WAMessageKey) => `${key.id}|${key.fromMe ? 1 : 0}` - -export const whatsappID = (jid: string) => jid?.replace ('@c.us', '@s.whatsapp.net') -export const isGroupID = (jid: string) => jid?.endsWith ('@g.us') - -export const newMessagesDB = (messages: WAMessage[] = []) => { - const db = new KeyedDB(waMessageKey, WA_MESSAGE_ID) - messages.forEach(m => !db.get(WA_MESSAGE_ID(m)) && db.insert(m)) - return db -} - -export function shallowChanges (old: T, current: T, {lookForDeletedKeys}: {lookForDeletedKeys: boolean}): Partial { - let changes: Partial = {} - for (let key in current) { - if (old[key] !== current[key]) { - changes[key] = current[key] || null - } - } - if (lookForDeletedKeys) { - for (let key in old) { - if (!changes[key] && old[key] !== current[key]) { - changes[key] = current[key] || null - } - } - } - return changes -} - -/** decrypt AES 256 CBC; where the IV is prefixed to the buffer */ -export function aesDecrypt(buffer: Buffer, key: Buffer) { - return aesDecryptWithIV(buffer.slice(16, buffer.length), key, buffer.slice(0, 16)) -} -/** decrypt AES 256 CBC */ -export function aesDecryptWithIV(buffer: Buffer, key: Buffer, IV: Buffer) { - 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 -export function aesEncrypt(buffer: Buffer, key: Buffer) { - const IV = 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 -export function aesEncrypWithIV(buffer: Buffer, key: Buffer, IV: Buffer) { - 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 -export function hmacSign(buffer: Buffer, key: Buffer) { - return Crypto.createHmac('sha256', key).update(buffer).digest() -} -export function sha256(buffer: Buffer) { - return Crypto.createHash('sha256').update(buffer).digest() -} -// HKDF key expansion -export function hkdf(buffer: Buffer, expandedLength: number, info = null) { - return HKDF(buffer, expandedLength, { salt: Buffer.alloc(32), info: info, hash: 'SHA-256' }) -} -// generate a buffer with random bytes of the specified length -export function randomBytes(length) { - return Crypto.randomBytes(length) -} -/** unix timestamp of a date in seconds */ -export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime()/1000) - -export type DebouncedTimeout = ReturnType -export const debouncedTimeout = (intervalMs: number = 1000, task: () => void = undefined) => { - let timeout: NodeJS.Timeout - return { - start: (newIntervalMs?: number, newTask?: () => void) => { - task = newTask || task - intervalMs = newIntervalMs || intervalMs - timeout && clearTimeout(timeout) - timeout = setTimeout(task, intervalMs) - }, - cancel: () => { - timeout && clearTimeout(timeout) - timeout = undefined - }, - setTask: (newTask: () => void) => task = newTask, - setInterval: (newInterval: number) => intervalMs = newInterval - } -} - -export const delay = (ms: number) => delayCancellable (ms).delay -export const delayCancellable = (ms: number) => { - const stack = new Error().stack - let timeout: NodeJS.Timeout - let reject: (error) => void - const delay: Promise = new Promise((resolve, _reject) => { - timeout = setTimeout(resolve, ms) - reject = _reject - }) - const cancel = () => { - clearTimeout (timeout) - reject (CancelledError(stack)) - } - return { delay, cancel } -} -export async function promiseTimeout(ms: number, promise: (resolve: (v?: T)=>void, reject: (error) => void) => void) { - if (!ms) return new Promise (promise) - const stack = new Error().stack - // Create a promise that rejects in milliseconds - let {delay, cancel} = delayCancellable (ms) - const p = new Promise ((resolve, reject) => { - delay - .then(() => reject(TimedOutError(stack))) - .catch (err => reject(err)) - - promise (resolve, reject) - }) - .finally (cancel) - return p as Promise -} -// whatsapp requires a message tag for every message, we just use the timestamp as one -export function generateMessageTag(epoch?: number) { - let tag = unixTimestampSeconds().toString() - if (epoch) tag += '.--' + epoch // attach epoch if provided - return tag -} -// generate a random 16 byte client ID -export function generateClientID() { - return randomBytes(16).toString('base64') -} -// generate a random 16 byte ID to attach to a message -export function generateMessageID() { - return '3EB0' + randomBytes(4).toString('hex').toUpperCase() -} -export function decryptWA (message: string | Buffer, macKey: Buffer, encKey: Buffer, decoder: Decoder, fromMe: boolean=false): [string, Object, [number, number]?] { - let commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message - if (commaIndex < 0) throw new BaileysError ('invalid message', { message }) // if there was no comma, then this message must be not be valid - - if (message[commaIndex+1] === ',') commaIndex += 1 - let data = message.slice(commaIndex+1, message.length) - - // 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: string = message.slice(0, commaIndex).toString() - let json - let tags - if (data.length > 0) { - if (typeof data === 'string') { - json = JSON.parse(data) // parse the JSON - } else { - if (!macKey || !encKey) { - throw new BaileysError ('recieved encrypted buffer when auth creds unavailable', { message }) - } - /* - If the data recieved was not a JSON, then it must be an encrypted message. - Such a message can only be decrypted if we're connected successfully to the servers & have encryption keys - */ - if (fromMe) { - tags = [data[0], data[1]] - data = data.slice(2, data.length) - } - - const checksum = data.slice(0, 32) // the first 32 bytes of the buffer are the HMAC sign of the message - data = data.slice(32, data.length) // the actual message - const computedChecksum = hmacSign(data, macKey) // compute the sign of the message we recieved using our macKey - - if (checksum.equals(computedChecksum)) { - // the checksum the server sent, must match the one we computed for the message to be valid - const decrypted = aesDecrypt(data, encKey) // decrypt using AES - json = decoder.read(decrypted) // decode the binary message into a JSON array - } else { - throw new BaileysError ('checksum failed', { - received: checksum.toString('hex'), - computed: computedChecksum.toString('hex'), - data: data.slice(0, 80).toString(), - tag: messageTag, - message: message.slice(0, 80).toString() - }) - } - } - } - return [messageTag, json, tags] -} -/** generates all the keys required to encrypt/decrypt & sign a media message */ -export function getMediaKeys(buffer, mediaType: MessageType) { - if (typeof buffer === 'string') { - buffer = Buffer.from (buffer.replace('data:;base64,', ''), 'base64') - } - // expand using HKDF to 112 bytes, also pass in the relevant app info - const expandedMediaKey = hkdf(buffer, 112, HKDFInfoKeys[mediaType]) - return { - iv: expandedMediaKey.slice(0, 16), - cipherKey: expandedMediaKey.slice(16, 48), - macKey: expandedMediaKey.slice(48, 80), - } -} -/** Extracts video thumb using FFMPEG */ -const extractVideoThumb = async ( - path: string, - destPath: string, - time: string, - size: { width: number; height: number }, -) => - new Promise((resolve, reject) => { - const cmd = `ffmpeg -ss ${time} -i ${path} -y -s ${size.width}x${size.height} -vframes 1 -f image2 ${destPath}` - exec(cmd, (err) => { - if (err) reject(err) - else resolve() - }) - }) as Promise - -export const compressImage = async (bufferOrFilePath: Buffer | string) => { - const jimp = await Jimp.read(bufferOrFilePath as any) - const result = await jimp.resize(48, 48).getBufferAsync(Jimp.MIME_JPEG) - return result -} -export const generateProfilePicture = async (buffer: Buffer) => { - const jimp = await Jimp.read (buffer) - const min = Math.min(jimp.getWidth (), jimp.getHeight ()) - const cropped = jimp.crop (0, 0, min, min) - return { - img: await cropped.resize(640, 640).getBufferAsync (Jimp.MIME_JPEG), - preview: await cropped.resize(96, 96).getBufferAsync (Jimp.MIME_JPEG) - } -} -export const ProxyAgent = (host: string | URL) => HttpsProxyAgent(host) as any as Agent -/** gets the SHA256 of the given media message */ -export const mediaMessageSHA256B64 = (message: WAMessageContent) => { - const media = Object.values(message)[0] as WAGenericMediaMessage - return media?.fileSha256 && Buffer.from(media.fileSha256).toString ('base64') -} -export async function getAudioDuration (buffer: Buffer | string) { - const musicMetadata = await import ('music-metadata') - let metadata: IAudioMetadata - if(Buffer.isBuffer(buffer)) { - metadata = await musicMetadata.parseBuffer(buffer, null, { duration: true }) - } else { - const rStream = createReadStream(buffer) - metadata = await musicMetadata.parseStream(rStream, null, { duration: true }) - rStream.close() - } - return metadata.format.duration; -} -export const toReadable = (buffer: Buffer) => { - const readable = new Readable({ read: () => {} }) - readable.push(buffer) - readable.push(null) - return readable -} -export const getStream = async (item: WAMediaUpload) => { - if(Buffer.isBuffer(item)) return { stream: toReadable(item), type: 'buffer' } - if(item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) { - return { stream: await getGotStream(item.url), type: 'remote' } - } - return { stream: createReadStream(item.url), type: 'file' } -} -/** generates a thumbnail for a given media, if required */ -export async function generateThumbnail(file: string, mediaType: MessageType, info: MessageOptions) { - if ('thumbnail' in info) { - // don't do anything if the thumbnail is already provided, or is null - if (mediaType === MessageType.audio) { - throw new Error('audio messages cannot have thumbnails') - } - } else if (mediaType === MessageType.image) { - const buff = await compressImage(file) - info.thumbnail = buff.toString('base64') - } else if (mediaType === MessageType.video) { - const imgFilename = join(tmpdir(), generateMessageID() + '.jpg') - try { - await extractVideoThumb(file, imgFilename, '00:00:00', { width: 48, height: 48 }) - const buff = await fs.readFile(imgFilename) - info.thumbnail = buff.toString('base64') - await fs.unlink(imgFilename) - } catch (err) { - console.log('could not generate video thumb: ' + err) - } - } -} -export const getGotStream = async(url: string | URL, options: Options & { isStream?: true } = {}) => { - const fetched = got.stream(url, { ...options, isStream: true }) - await new Promise((resolve, reject) => { - fetched.once('error', reject) - fetched.once('response', ({statusCode: status}: Response) => { - if (status >= 400) { - reject(new BaileysError ( - 'Invalid code (' + status + ') returned', - { status } - )) - } else { - resolve(undefined) - } - }) - }) - return fetched -} -export const encryptedStream = async(media: WAMediaUpload, mediaType: MessageType, saveOriginalFileIfRequired = true) => { - const { stream, type } = await getStream(media) - - const mediaKey = randomBytes(32) - const {cipherKey, iv, macKey} = getMediaKeys(mediaKey, mediaType) - // random name - const encBodyPath = join(tmpdir(), mediaType + generateMessageID() + '.enc') - const encWriteStream = createWriteStream(encBodyPath) - let bodyPath: string - let writeStream: WriteStream - if(type === 'file') { - bodyPath = (media as any).url - } else if(saveOriginalFileIfRequired) { - bodyPath = join(tmpdir(), mediaType + generateMessageID()) - writeStream = createWriteStream(bodyPath) - } - - let fileLength = 0 - const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv) - let hmac = Crypto.createHmac('sha256', macKey).update(iv) - let sha256Plain = Crypto.createHash('sha256') - let sha256Enc = Crypto.createHash('sha256') - - const onChunk = (buff: Buffer) => { - sha256Enc = sha256Enc.update(buff) - hmac = hmac.update(buff) - encWriteStream.write(buff) - } - for await(const data of stream) { - fileLength += data.length - sha256Plain = sha256Plain.update(data) - if (writeStream && !writeStream.write(data)) await once(writeStream, 'drain') - onChunk(aes.update(data)) - } - onChunk(aes.final()) - - const mac = hmac.digest().slice(0, 10) - sha256Enc = sha256Enc.update(mac) - - const fileSha256 = sha256Plain.digest() - const fileEncSha256 = sha256Enc.digest() - - encWriteStream.write(mac) - encWriteStream.end() - - writeStream && writeStream.end() - - return { - mediaKey, - encBodyPath, - bodyPath, - mac, - fileEncSha256, - fileSha256, - fileLength, - didSaveToTmpPath: type !== 'file' - } -} -/** - * Decode a media message (video, image, document, audio) & return decrypted buffer - * @param message the media message you want to decode - */ -export async function decryptMediaMessageBuffer(message: WAMessageContent): Promise { - /* - One can infer media type from the key in the message - it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc. - */ - const type = Object.keys(message)[0] as MessageType - if (!type) { - throw new BaileysError('unknown message type', message) - } - if (type === MessageType.text || type === MessageType.extendedText) { - throw new BaileysError('cannot decode text message', message) - } - if (type === MessageType.location || type === MessageType.liveLocation) { - const buffer = Buffer.from(message[type].jpegThumbnail) - const readable = new Readable({ read: () => {} }) - readable.push(buffer) - readable.push(null) - return readable - } - let messageContent: WAGenericMediaMessage - if (message.productMessage) { - const product = message.productMessage.product?.productImage - if (!product) throw new BaileysError ('product has no image', message) - messageContent = product - } else { - messageContent = message[type] - } - // download the message - const fetched = await getGotStream(messageContent.url, { - headers: { Origin: DEFAULT_ORIGIN } - }) - let remainingBytes = Buffer.from([]) - const { cipherKey, iv } = getMediaKeys(messageContent.mediaKey, type) - const aes = Crypto.createDecipheriv("aes-256-cbc", cipherKey, iv) - - const output = new Transform({ - transform(chunk, _, callback) { - let data = Buffer.concat([remainingBytes, chunk]) - const decryptLength = - Math.floor(data.length / 16) * 16 - remainingBytes = data.slice(decryptLength) - data = data.slice(0, decryptLength) - - try { - this.push(aes.update(data)) - callback() - } catch(error) { - callback(error) - } - }, - final(callback) { - try { - this.push(aes.final()) - callback() - } catch(error) { - callback(error) - } - }, - }) - return fetched.pipe(output, { end: true }) -} -export function extensionForMediaMessage(message: WAMessageContent) { - const getExtension = (mimetype: string) => mimetype.split(';')[0].split('/')[1] - const type = Object.keys(message)[0] as MessageType - let extension: string - if (type === MessageType.location || type === MessageType.liveLocation || type === MessageType.product) { - extension = '.jpeg' - } else { - const messageContent = message[type] as - | WAMessageProto.VideoMessage - | WAMessageProto.ImageMessage - | WAMessageProto.AudioMessage - | WAMessageProto.DocumentMessage - extension = getExtension (messageContent.mimetype) - } - return extension -} diff --git a/src/WAConnection/index.ts b/src/WAConnection/index.ts deleted file mode 100644 index f0cb3ad..0000000 --- a/src/WAConnection/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './8.Groups' -export * from './Utils' -export * from './Constants' -export * from './Mutex' \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 34a105f..4fcdeb2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,19 @@ -export * from '../WAMessage/WAMessage' -export * from './Binary/Constants' -export * from './Binary/Decoder' -export * from './Binary/Encoder' -export * from './WAConnection' +import makeWALegacySocket from './LegacySocket' +import makeWASocket from './Socket' + +export * from '../WAProto' +export * from './Utils' +export * from './Types' +export * from './Store' +export * from './Defaults' +export * from './WABinary' + +export type WALegacySocket = ReturnType + +export { makeWALegacySocket } + +export type WASocket = ReturnType + +export type AnyWASocket = WASocket | WALegacySocket + +export default makeWASocket \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 382ac74..3b6bfec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "es2018", "module": "commonjs", "experimentalDecorators": true, - "allowJs": true, + "allowJs": false, "checkJs": false, "outDir": "lib", "strict": false, @@ -12,8 +12,8 @@ "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, "declaration": true, - "lib": ["es2020", "esnext.array"] + "lib": ["es2020", "esnext.array", "DOM"] }, - "include": ["src/**/*.ts"], + "include": ["./src/**/*.ts"], "exclude": ["node_modules", "src/Tests/*", "src/Binary/GenerateStatics.ts"] } diff --git a/yarn.lock b/yarn.lock index 75e928c..2b197cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,22 +2,597 @@ # yarn lockfile v1 -"@adiwajshing/keyed-db@^0.2.2": +"@adiwajshing/eslint-config@git+https://github.com/adiwajshing/eslint-config": + version "1.0.0" + resolved "git+https://github.com/adiwajshing/eslint-config#db16c7427bd6dcf8fba20e0aaa526724e46c83aa" + dependencies: + "@typescript-eslint/eslint-plugin" "^4.33.0" + "@typescript-eslint/parser" "^4.33.0" + eslint-plugin-react "^7.26.1" + eslint-plugin-simple-import-sort "^7.0.0" + eslint-plugin-unused-imports "^1.1.5" + +"@adiwajshing/keyed-db@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@adiwajshing/keyed-db/-/keyed-db-0.2.4.tgz#2a09e88fce20b2672deb60a7750c5fe3ab0dfd99" integrity sha512-yprSnAtj80/VKuDqRcFFLDYltoNV8tChNwFfIgcf6PGD4sjzWIBgs08pRuTqGH5mk5wgL6PBRSsMCZqtZwzFEw== +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + +"@babel/compat-data@^7.14.5": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" + integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== + +"@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" + integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helpers" "^7.14.6" + "@babel/parser" "^7.14.6" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.14.5", "@babel/generator@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== + dependencies: + "@babel/types" "^7.14.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" + integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== + dependencies: + "@babel/compat-data" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" + +"@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== + dependencies: + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-member-expression-to-functions@^7.14.5": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" + integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-imports@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" + integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-transforms@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" + integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-optimise-call-expression@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" + integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + +"@babel/helper-replace-supers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" + integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-simple-access@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" + integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-validator-identifier@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/helpers@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" + integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== + dependencies: + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/highlight@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.7.2": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" + integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" + integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/runtime@^7.7.2": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" - integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a" + integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA== dependencies: regenerator-runtime "^0.13.4" -"@hapi/bourne@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.0.0.tgz#5bb2193eb685c0007540ca61d166d4e1edaf918d" - integrity sha512-WEezM1FWztfbzqIUbsDzFRVMxSoLy3HugVcux6KDDtTqzPsLE8NDRHfXvev66aH1i2oOKKar3/XDjbvh/OUBdg== +"@babel/template@^7.14.5", "@babel/template@^7.3.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.7.2": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" + integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.7" + "@babel/types" "^7.14.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz#118511f316e2e87ee4294761868e254d3da47960" + integrity sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@hapi/boom@^9.1.3": + version "9.1.3" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.3.tgz#22cad56e39b7a4819161a99b1db19eaaa9b6cc6e" + integrity sha512-RlrGyZ603hE/eRTZtTltocRm50HHmrmL3kGOP0SQ9MasazlW1mt/fkv4C5P/6rnpFXjwld/POFX1C8tMZE3ldg== + dependencies: + "@hapi/hoek" "9.x.x" + +"@hapi/hoek@9.x.x": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" + integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== + +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.6.tgz#3eb72ea80897495c3d73dd97aab7f26770e2260f" + integrity sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg== + dependencies: + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.0.6" + jest-util "^27.0.6" + slash "^3.0.0" + +"@jest/core@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.6.tgz#c5f642727a0b3bf0f37c4b46c675372d0978d4a1" + integrity sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow== + dependencies: + "@jest/console" "^27.0.6" + "@jest/reporters" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^27.0.6" + jest-config "^27.0.6" + jest-haste-map "^27.0.6" + jest-message-util "^27.0.6" + jest-regex-util "^27.0.6" + jest-resolve "^27.0.6" + jest-resolve-dependencies "^27.0.6" + jest-runner "^27.0.6" + jest-runtime "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" + jest-watcher "^27.0.6" + micromatch "^4.0.4" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.6.tgz#ee293fe996db01d7d663b8108fa0e1ff436219d2" + integrity sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg== + dependencies: + "@jest/fake-timers" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + jest-mock "^27.0.6" + +"@jest/fake-timers@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.6.tgz#cbad52f3fe6abe30e7acb8cd5fa3466b9588e3df" + integrity sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ== + dependencies: + "@jest/types" "^27.0.6" + "@sinonjs/fake-timers" "^7.0.2" + "@types/node" "*" + jest-message-util "^27.0.6" + jest-mock "^27.0.6" + jest-util "^27.0.6" + +"@jest/globals@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.6.tgz#48e3903f99a4650673d8657334d13c9caf0e8f82" + integrity sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw== + dependencies: + "@jest/environment" "^27.0.6" + "@jest/types" "^27.0.6" + expect "^27.0.6" + +"@jest/reporters@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.6.tgz#91e7f2d98c002ad5df94d5b5167c1eb0b9fd5b00" + integrity sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^27.0.6" + jest-resolve "^27.0.6" + jest-util "^27.0.6" + jest-worker "^27.0.6" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.0.0" + +"@jest/source-map@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.6.tgz#be9e9b93565d49b0548b86e232092491fb60551f" + integrity sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.6.tgz#3fa42015a14e4fdede6acd042ce98c7f36627051" + integrity sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w== + dependencies: + "@jest/console" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz#80a913ed7a1130545b1cd777ff2735dd3af5d34b" + integrity sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA== + dependencies: + "@jest/test-result" "^27.0.6" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.6" + jest-runtime "^27.0.6" + +"@jest/transform@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.6.tgz#189ad7107413208f7600f4719f81dd2f7278cc95" + integrity sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.0.6" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.6" + jest-regex-util "^27.0.6" + jest-util "^27.0.6" + micromatch "^4.0.4" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jest/types@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b" + integrity sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" "@jimp/bmp@^0.16.1": version "0.16.1" @@ -309,6 +884,27 @@ "@babel/runtime" "^7.7.2" regenerator-runtime "^0.13.3" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -362,151 +958,318 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@sindresorhus/is@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" - integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== - -"@szmarczak/http-timer@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" - integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: - defer-to-connect "^2.0.0" + type-detect "4.0.8" + +"@sinonjs/fake-timers@^7.0.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== + dependencies: + "@sinonjs/commons" "^1.7.0" "@tokenizer/token@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.1.1.tgz#f0d92c12f87079ddfd1b29f614758b9696bc29e3" integrity sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w== -"@types/cacheable-request@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" - integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/debug@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" - integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.1.15" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" + integrity sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" + integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== + dependencies: + "@babel/types" "^7.3.0" "@types/got@^9.6.11": - version "9.6.11" - resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.11.tgz#482b402cc5ee459481aeeadb08142ebb1a9afb26" - integrity sha512-dr3IiDNg5TDesGyuwTrN77E1Cd7DCdmCFtEfSGqr83jMMtcwhf/SGPbN2goY4JUWQfvxwY56+e5tjfi+oXeSdA== + version "9.6.12" + resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.12.tgz#fd42a6e1f5f64cd6bb422279b08c30bb5a15a56f" + integrity sha512-X4pj/HGHbXVLqTpKjA2ahI4rV/nNBc9mGO2I/0CgAra+F2dKgMXnENv2SRpemScBzBAI4vMelIVYViQxlSE6xA== dependencies: "@types/node" "*" "@types/tough-cookie" "*" form-data "^2.5.0" -"@types/http-cache-semantics@*": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" - integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== - -"@types/keyv@*": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" - integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== +"@types/graceful-fs@^4.1.2": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" -"@types/long@^4.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^26.0.24": + version "26.0.24" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + +"@types/json-schema@^7.0.7": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== -"@types/mocha@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" - integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w== +"@types/node@*", "@types/node@>=13.7.0": + version "16.3.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" + integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== -"@types/node@*", "@types/node@^14.6.2": - version "14.14.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" - integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== +"@types/node@^10.1.0": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^13.7.0": - version "13.13.48" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.48.tgz#46a3df718aed5217277f2395a682e055a487e341" - integrity sha512-z8wvSsgWQzkr4sVuMEEOvwMdOQjiRY2Y/ZW4fDfjfe3+TfQrZqFKOthBgk2RnVEmtOKrkwdZ7uTvsxTBLjKGDQ== +"@types/node@^14.6.2": + version "14.17.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.5.tgz#b59daf6a7ffa461b5648456ca59050ba8e40ed54" + integrity sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA== -"@types/pino-pretty@*": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@types/pino-pretty/-/pino-pretty-4.7.0.tgz#e4a18541f8464d1cc48216f5593cc6a0e62dc2c3" - integrity sha512-fIZ+VXf9gJoJR4tiiM7G+j/bZkPoZEfFGzA4d8tAWCTpTVyvVaBwnmdLs3wEXYpMjw8eXulrOzNCjmGHT3FgHw== +"@types/pino@^7.0.0": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@types/pino/-/pino-7.0.5.tgz#1c84a81b924a6a9e263dbb581dffdbad7a3c60c4" + integrity sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg== dependencies: - "@types/pino" "*" + pino "*" -"@types/pino-std-serializers@*": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/pino-std-serializers/-/pino-std-serializers-2.4.1.tgz#f8bd52a209c8b3c97d1533b1ba27f57c816382bf" - integrity sha512-17XcksO47M24IVTVKPeAByWUd3Oez7EbIjXpSbzMPhXVzgjGtrOa49gKBwxH9hb8dKv58OelsWQ+A1G1l9S3wQ== +"@types/prettier@^2.1.5": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" + integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== + +"@types/sharp@^0.29.4": + version "0.29.4" + resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.29.4.tgz#e47d8677befc9944d4b9d76ed837452814166de0" + integrity sha512-asrPef2LYenr76zBzyy+Fw40yGzq/q/CY77mUP5n43DnuWjQoOB+8vxnzFcqccz+LF+A7mbnknGCfpVWSG82Bw== dependencies: "@types/node" "*" -"@types/pino@*", "@types/pino@^6.3.2": - version "6.3.7" - resolved "https://registry.yarnpkg.com/@types/pino/-/pino-6.3.7.tgz#0ccef98a159230cb3fa2589c7e8b00a7550a69f6" - integrity sha512-v7FdDXVEL0Zx1zcCf0cJZMojChnF+O0ujDKV1UdocsLuUhENjdtNIaanCZK1zRELp35x//bI2/IHtYUK0vmRvw== - dependencies: - "@types/node" "*" - "@types/pino-pretty" "*" - "@types/pino-std-serializers" "*" - "@types/sonic-boom" "*" - -"@types/readable-stream@^2.3.9": - version "2.3.9" - resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.9.tgz#40a8349e6ace3afd2dd1b6d8e9b02945de4566a9" - integrity sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw== - dependencies: - "@types/node" "*" - safe-buffer "*" - -"@types/responselike@*", "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== - dependencies: - "@types/node" "*" - -"@types/sonic-boom@*": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@types/sonic-boom/-/sonic-boom-0.7.0.tgz#38337036293992a1df65dd3161abddf8fb9b7176" - integrity sha512-AfqR0fZMoUXUNwusgXKxcE9DPlHNDHQp6nKYUd4PSRpLobF5CCevSpyTEBcVZreqaWKCnGBr9KI1fHMTttoB7A== - dependencies: - "@types/node" "*" - -"@types/strip-bom@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" - integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= - -"@types/strip-json-comments@0.0.30": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" - integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/tough-cookie@*": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" - integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40" + integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg== -"@types/ws@^7.2.6": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.1.tgz#49eacb15a0534663d53a36fbf5b4d98f5ae9a73a" - integrity sha512-ISCK1iFnR+jYv7+jLNX0wDqesZ/5RAeY3wUx6QaphmocphU61h+b+PHjS18TF4WIPTu/MMzxIq2PHr32o2TS5Q== +"@types/ws@^8.0.0": + version "8.2.2" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21" + integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg== dependencies: "@types/node" "*" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +"@types/yargs-parser@*": + version "20.2.1" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" + integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + +"@types/yargs@^15.0.0": + version "15.0.14" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^16.0.0": + version "16.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== + dependencies: + "@typescript-eslint/experimental-utils" "4.33.0" + "@typescript-eslint/scope-manager" "4.33.0" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" + integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== + dependencies: + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" + debug "^4.3.1" + +"@typescript-eslint/scope-manager@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== + +"@typescript-eslint/typescript-estree@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^7.1.1, acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4: + version "8.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" + integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== + +acorn@^8.4.1: + version "8.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" + integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== agent-base@6: version "6.0.2" @@ -515,21 +1278,53 @@ agent-base@6: dependencies: debug "4" -ansi-colors@4.1.1: +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.9.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" + integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -544,12 +1339,17 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + any-base@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== -anymatch@~3.1.1: +anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -557,45 +1357,60 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -args@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/args/-/args-5.0.1.tgz#4bf298df90a4799a09521362c579278cc2fdd761" - integrity sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ== +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: - camelcase "5.0.0" - chalk "2.4.2" - leven "2.1.0" - mri "1.1.4" + sprintf-js "~1.0.2" -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= +array-includes@^3.1.3, array-includes@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + is-string "^1.0.7" -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -assert@^2.0.0: +array.prototype.flatmap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" + integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.19.0" + +astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" - integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== - dependencies: - es6-object-assign "^1.1.0" - is-nan "^1.2.1" - object-is "^1.0.1" - util "^0.12.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== asynckit@^0.4.0: version "0.4.0" @@ -612,12 +1427,73 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -available-typed-arrays@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" - integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== dependencies: - array-filter "^1.0.0" + follow-redirects "^1.14.4" + +babel-jest@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.6.tgz#e99c6e0577da2655118e3608b68761a5a69bd0d8" + integrity sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA== + dependencies: + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^27.0.6" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz#f7c6b3d764af21cb4a2a1ab6870117dbde15b456" + integrity sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz#909ef08e9f24a4679768be2f60a3df0856843f9d" + integrity sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw== + dependencies: + babel-plugin-jest-hoist "^27.0.6" + babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: version "1.0.2" @@ -629,10 +1505,14 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" bmp-js@^0.1.0: version "0.1.0" @@ -647,29 +1527,59 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@~3.0.2: +braces@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= -buffer-from@^1.0.0: +buffer-from@1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -buffer@^5.2.0: +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.2.0, buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -677,24 +1587,6 @@ buffer@^5.2.0: base64-js "^1.3.1" ieee754 "^1.1.13" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" - integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^2.0.0" - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -703,30 +1595,27 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -camelcase@^6.0.0: +camelcase@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -chalk@2.4.2: +caniuse-lite@^1.0.30001219: + version "1.0.30001245" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz#45b941bbd833cb0fa53861ff2bae746b3c6ca5d4" + integrity sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA== + +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -736,27 +1625,32 @@ chalk@2.4.2: supports-color "^5.3.0" chalk@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@3.5.1, chokidar@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== cliui@^7.0.2: version "7.0.4" @@ -767,12 +1661,25 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== color-convert@^1.9.0: version "1.9.3" @@ -793,17 +1700,38 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/color/-/color-4.1.0.tgz#9502e6a2dcacb26adf4c60910a27628d010b3de3" + integrity sha512-o2rkkxyLGgYoeUy1OodXpbPAQNmlNBrirQ8ODO8QutzDiDMNdezSOZLNnusQ6pUpCQJUsaJIo9DZJKqa2HgH7A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -combined-stream@^1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -815,57 +1743,91 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + content-type@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== + dependencies: + safe-buffer "~5.1.1" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: - array-find-index "^1.0.1" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" curve25519-js@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/curve25519-js/-/curve25519-js-0.0.4.tgz#e6ad967e8cd284590d657bbfc90d8b50e49ba060" integrity sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w== -dateformat@^4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c" - integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q== - -dateformat@~1.0.4-1.2.3: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" -debug@4, debug@4.3.1, debug@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" -decamelize@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +debug@^4.0.1: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decimal.js@^10.2.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decompress-response@^6.0.0: version "6.0.0" @@ -874,10 +1836,30 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== define-properties@^1.1.3: version "1.1.3" @@ -891,73 +1873,133 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +diff-sequences@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" + integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== -dotenv@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - -dynamic-dedupe@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" - integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: - xtend "^4.0.0" + webidl-conversions "^5.0.0" + +duplexify@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" + integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + +electron-to-chromium@^1.3.723: + version "1.3.779" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.779.tgz#de55492a756deec63424f89fbe62aec9776f0e6d" + integrity sha512-nreave0y/1Qhmo8XtO6C/LpawNyC6U26+q7d814/e+tIqUK073pM+4xW7WUXyqCRa5K4wdxHmNMBAi8ap9nEew== + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: - is-arrayish "^0.2.1" + ansi-colors "^4.1.1" -es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: - version "1.18.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" - integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== +es-abstract@^1.19.0, es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" has "^1.0.3" has-symbols "^1.0.2" - is-callable "^1.2.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" is-negative-zero "^2.0.1" - is-regex "^1.1.2" - is-string "^1.0.5" - object-inspect "^1.9.0" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" object-keys "^1.1.1" object.assign "^4.1.2" string.prototype.trimend "^1.0.4" string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.0" + unbox-primitive "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" @@ -968,50 +2010,303 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-object-assign@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" - integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-plugin-react@^7.26.1: + version "7.28.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf" + integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw== + dependencies: + array-includes "^3.1.4" + array.prototype.flatmap "^1.2.5" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.0.4" + object.entries "^1.1.5" + object.fromentries "^2.0.5" + object.hasown "^1.1.0" + object.values "^1.1.5" + prop-types "^15.7.2" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.6" + +eslint-plugin-simple-import-sort@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz#a1dad262f46d2184a90095a60c66fef74727f0f8" + integrity sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw== + +eslint-plugin-unused-imports@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-1.1.5.tgz#a2b992ef0faf6c6c75c3815cc47bde76739513c2" + integrity sha512-TeV8l8zkLQrq9LBeYFCQmYVIXMjfHgdRQLw7dEZp4ZB3PeR10Y5Uif11heCsHRmhdRIYMoewr1d9ouUHLbLHew== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint@^7.0.0: + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.3" + "@humanwhocodes/config-array" "^0.5.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.1.2" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.9" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exif-parser@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +expect@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.6.tgz#a4d74fbe27222c718fff68ef49d78e26a8fd4c05" + integrity sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw== + dependencies: + "@jest/types" "^27.0.6" + ansi-styles "^5.0.0" + jest-get-type "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-regex-util "^27.0.6" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + fast-redact@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.0.tgz#ac2f9e36c9f4976f5db9fb18c6ffbaf308cf316d" - integrity sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w== + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.1.tgz#d6015b971e933d03529b01333ba7f22c29961e92" + integrity sha512-kYpn4Y/valC9MdrISg47tZOpYBNoTXKgT9GYXFpHN/jYFs+lFkPoisY+LcBODdKVMY96ATzvzsWv+ES/4Kmufw== -fast-safe-stringify@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" - integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" -file-type@^16.3.0: - version "16.3.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.3.0.tgz#f03af91db30f92cc9a0b335c0644c46101522f6d" - integrity sha512-ZA0hV64611vJT42ltw0T9IDwHApQuxRdrmQZWTeDmeAUtZBBVSQW3nSQqhhW1cAgpXgqcJvm410BYHXJQ9AymA== +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-type@^16.5.0: + version "16.5.1" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.1.tgz#dd697dc5c3a2f4db63af746f38a6322e5e7bc6a5" + integrity sha512-Pi1G43smrCy82Q3be3sfKaeS5uHdfj905dP88YqhroG6TYbVY2ljTdDXeXqa6Cn5nOk6znOjWM2uZptA8vH/qQ== dependencies: readable-web-to-node-stream "^3.0.0" strtok3 "^6.0.3" token-types "^2.0.0" - typedarray-to-buffer "^3.1.5" file-type@^9.0.0: version "9.0.0" @@ -1025,36 +2320,31 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: - locate-path "^6.0.0" + locate-path "^5.0.0" path-exists "^4.0.0" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" + flatted "^3.1.0" + rimraf "^3.0.2" -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatted@^3.1.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" + integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== -flatstr@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" - integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= +follow-redirects@^1.14.4: + version "1.14.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd" + integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A== form-data@^2.5.0: version "2.5.1" @@ -1065,6 +2355,20 @@ form-data@^2.5.0: combined-stream "^1.0.6" mime-types "^2.1.12" +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -1080,7 +2384,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@~2.3.1: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -1090,17 +2394,36 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -futoin-hkdf@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.3.3.tgz#6ee1c9c105dfa0995ba4f80633cf1c0c32defcb2" - integrity sha512-oR75fYk3B3X9/B02Y6vusrBKucrpC6VjxhRL+C6B7FwUpuSRHbhBNG3AZbcE/xPyJmEQWsyqUFp3VeNNbA3S7A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + 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" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== @@ -1109,17 +2432,23 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: - pump "^3.0.0" + call-bind "^1.0.2" + get-intrinsic "^1.1.1" gifwrap@^0.9.2: version "0.9.2" @@ -1129,29 +2458,22 @@ gifwrap@^0.9.2: image-q "^1.1.1" omggif "^1.0.10" -glob-parent@~5.1.0: +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.1.3: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1168,33 +2490,35 @@ global@~4.4.0: min-document "^2.19.0" process "^0.11.10" -got@^11.8.1: - version "11.8.2" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" - integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: +globals@^13.6.0, globals@^13.9.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" + integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== + dependencies: + type-fest "^0.20.2" + +globby@^11.0.3: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -1227,6 +2551,18 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -1234,28 +2570,26 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" https-proxy-agent@^5.0.0: version "5.0.0" @@ -1265,22 +2599,58 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.8, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + image-q@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/image-q/-/image-q-1.1.1.tgz#fc84099664460b90ca862d9300b6bfbbbfbf8056" integrity sha1-/IQJlmRGC5DKhi2TALa/u7+/gFY= -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: - repeating "^2.0.0" + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= inflight@^1.0.4: version "1.0.6" @@ -1290,85 +2660,87 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -is-arguments@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" - integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== - dependencies: - call-bind "^1.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-bigint@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: - binary-extensions "^2.0.0" + has-bigints "^1.0.1" is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" + has-tostringtag "^1.0.0" -is-callable@^1.1.4, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== - dependencies: - has "^1.0.3" - -is-core-module@^2.8.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" + integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== dependencies: has "^1.0.3" is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -1380,92 +2752,559 @@ is-function@^1.0.1: resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== -is-generator-function@^1.0.7: - version "1.0.8" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" - integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-nan@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" - integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" - integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.1" + has-tostringtag "^1.0.0" -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: - has-symbols "^1.0.1" - -is-typed-array@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" - integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.2" - es-abstract "^1.18.0-next.2" - foreach "^2.0.5" - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.6.tgz#bed6183fcdea8a285482e3b50a9a7712d49a7a8b" + integrity sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA== + dependencies: + "@jest/types" "^27.0.6" + execa "^5.0.0" + throat "^6.0.1" + +jest-circus@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.6.tgz#dd4df17c4697db6a2c232aaad4e9cec666926668" + integrity sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q== + dependencies: + "@jest/environment" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.0.6" + is-generator-fn "^2.0.0" + jest-each "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-runtime "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + pretty-format "^27.0.6" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.6.tgz#d021e5f4d86d6a212450d4c7b86cb219f1e6864f" + integrity sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg== + dependencies: + "@jest/core" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + jest-config "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" + prompts "^2.0.1" + yargs "^16.0.3" + +jest-config@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.6.tgz#119fb10f149ba63d9c50621baa4f1f179500277f" + integrity sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^27.0.6" + "@jest/types" "^27.0.6" + babel-jest "^27.0.6" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + is-ci "^3.0.0" + jest-circus "^27.0.6" + jest-environment-jsdom "^27.0.6" + jest-environment-node "^27.0.6" + jest-get-type "^27.0.6" + jest-jasmine2 "^27.0.6" + jest-regex-util "^27.0.6" + jest-resolve "^27.0.6" + jest-runner "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" + micromatch "^4.0.4" + pretty-format "^27.0.6" + +jest-diff@^26.0.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-diff@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.6.tgz#4a7a19ee6f04ad70e0e3388f35829394a44c7b5e" + integrity sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.0.6" + jest-get-type "^27.0.6" + pretty-format "^27.0.6" + +jest-docblock@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3" + integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA== + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.6.tgz#cee117071b04060158dc8d9a66dc50ad40ef453b" + integrity sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA== + dependencies: + "@jest/types" "^27.0.6" + chalk "^4.0.0" + jest-get-type "^27.0.6" + jest-util "^27.0.6" + pretty-format "^27.0.6" + +jest-environment-jsdom@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz#f66426c4c9950807d0a9f209c590ce544f73291f" + integrity sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw== + dependencies: + "@jest/environment" "^27.0.6" + "@jest/fake-timers" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + jest-mock "^27.0.6" + jest-util "^27.0.6" + jsdom "^16.6.0" + +jest-environment-node@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.6.tgz#a6699b7ceb52e8d68138b9808b0c404e505f3e07" + integrity sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w== + dependencies: + "@jest/environment" "^27.0.6" + "@jest/fake-timers" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + jest-mock "^27.0.6" + jest-util "^27.0.6" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-get-type@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" + integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== + +jest-haste-map@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.6.tgz#4683a4e68f6ecaa74231679dca237279562c8dc7" + integrity sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w== + dependencies: + "@jest/types" "^27.0.6" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^27.0.6" + jest-serializer "^27.0.6" + jest-util "^27.0.6" + jest-worker "^27.0.6" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz#fd509a9ed3d92bd6edb68a779f4738b100655b37" + integrity sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^27.0.6" + "@jest/source-map" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.0.6" + is-generator-fn "^2.0.0" + jest-each "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-runtime "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + pretty-format "^27.0.6" + throat "^6.0.1" + +jest-leak-detector@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz#545854275f85450d4ef4b8fe305ca2a26450450f" + integrity sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ== + dependencies: + jest-get-type "^27.0.6" + pretty-format "^27.0.6" + +jest-matcher-utils@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz#2a8da1e86c620b39459f4352eaa255f0d43e39a9" + integrity sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA== + dependencies: + chalk "^4.0.0" + jest-diff "^27.0.6" + jest-get-type "^27.0.6" + pretty-format "^27.0.6" + +jest-message-util@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.6.tgz#158bcdf4785706492d164a39abca6a14da5ab8b5" + integrity sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.0.6" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.4" + pretty-format "^27.0.6" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.6.tgz#0efdd40851398307ba16778728f6d34d583e3467" + integrity sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw== + dependencies: + "@jest/types" "^27.0.6" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" + integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== + +jest-resolve-dependencies@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz#3e619e0ef391c3ecfcf6ef4056207a3d2be3269f" + integrity sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA== + dependencies: + "@jest/types" "^27.0.6" + jest-regex-util "^27.0.6" + jest-snapshot "^27.0.6" + +jest-resolve@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.6.tgz#e90f436dd4f8fbf53f58a91c42344864f8e55bff" + integrity sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA== + dependencies: + "@jest/types" "^27.0.6" + chalk "^4.0.0" + escalade "^3.1.1" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^27.0.6" + jest-validate "^27.0.6" + resolve "^1.20.0" + slash "^3.0.0" + +jest-runner@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.6.tgz#1325f45055539222bbc7256a6976e993ad2f9520" + integrity sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ== + dependencies: + "@jest/console" "^27.0.6" + "@jest/environment" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-docblock "^27.0.6" + jest-environment-jsdom "^27.0.6" + jest-environment-node "^27.0.6" + jest-haste-map "^27.0.6" + jest-leak-detector "^27.0.6" + jest-message-util "^27.0.6" + jest-resolve "^27.0.6" + jest-runtime "^27.0.6" + jest-util "^27.0.6" + jest-worker "^27.0.6" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runtime@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.6.tgz#45877cfcd386afdd4f317def551fc369794c27c9" + integrity sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q== + dependencies: + "@jest/console" "^27.0.6" + "@jest/environment" "^27.0.6" + "@jest/fake-timers" "^27.0.6" + "@jest/globals" "^27.0.6" + "@jest/source-map" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.6" + jest-message-util "^27.0.6" + jest-mock "^27.0.6" + jest-regex-util "^27.0.6" + jest-resolve "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^16.0.3" + +jest-serializer@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.6.tgz#93a6c74e0132b81a2d54623251c46c498bb5bec1" + integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.6.tgz#f4e6b208bd2e92e888344d78f0f650bcff05a4bf" + integrity sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/parser" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.0.6" + graceful-fs "^4.2.4" + jest-diff "^27.0.6" + jest-get-type "^27.0.6" + jest-haste-map "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-resolve "^27.0.6" + jest-util "^27.0.6" + natural-compare "^1.4.0" + pretty-format "^27.0.6" + semver "^7.3.2" + +jest-util@^27.0.0, jest-util@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.6.tgz#e8e04eec159de2f4d5f57f795df9cdc091e50297" + integrity sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ== + dependencies: + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^3.0.0" + picomatch "^2.2.3" + +jest-validate@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.6.tgz#930a527c7a951927df269f43b2dc23262457e2a6" + integrity sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA== + dependencies: + "@jest/types" "^27.0.6" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.0.6" + leven "^3.1.0" + pretty-format "^27.0.6" + +jest-watcher@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.6.tgz#89526f7f9edf1eac4e4be989bcb6dec6b8878d9c" + integrity sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ== + dependencies: + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.0.6" + string-length "^4.0.1" + +jest-worker@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" + integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.6.tgz#10517b2a628f0409087fbf473db44777d7a04505" + integrity sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA== + dependencies: + "@jest/core" "^27.0.6" + import-local "^3.0.2" + jest-cli "^27.0.6" + jimp@^0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.16.1.tgz#192f851a30e5ca11112a3d0aa53137659a78ca7a" @@ -1477,32 +3316,83 @@ jimp@^0.16.1: "@jimp/types" "^0.16.1" regenerator-runtime "^0.13.3" -jmespath@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" - integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= - -joycon@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" - integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ== - jpeg-js@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.2.tgz#8b345b1ae4abde64c2da2fe67ea216a114ac279d" integrity sha512-+az2gi/hvex7eLTMTlbRLOhH6P6WFdk2ITI8HJsaH2VqYO0I594zXSYEP+tf4FW+8Cy68ScDXoAsQdyQanv3sw== -js-yaml@4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== - dependencies: - argparse "^2.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsdom@^16.6.0: + version "16.6.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.5" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json5@2.x, json5@^2.1.2, json5@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" jsonfile@^6.0.1: version "6.1.0" @@ -1513,17 +3403,46 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -keyv@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" - integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" + integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== dependencies: - json-buffer "3.0.1" + array-includes "^3.1.3" + object.assign "^4.1.2" -leven@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" - integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +"libsignal@git+https://github.com/adiwajshing/libsignal-node": + version "2.0.1" + resolved "git+https://github.com/adiwajshing/libsignal-node#4ffc1f3d6147ffdec88f62e70f41e7de2e08cd42" + dependencies: + curve25519-js "^0.0.4" + protobufjs "6.8.8" load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.1" @@ -1539,53 +3458,39 @@ load-bmfont@^1.3.1, load-bmfont@^1.4.0: xhr "^2.0.1" xtend "^4.0.0" -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" + p-locate "^4.1.0" -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.21: +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + +lodash@4.x, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + js-tokens "^3.0.0 || ^4.0.0" lru-cache@^5.1.1: version "5.1.1" @@ -1594,68 +3499,86 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lunr@^2.3.9: version "2.3.9" resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -make-error@^1.1.1: +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" -marked@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.3.tgz#3551c4958c4da36897bda2a16812ef1399c8d6b0" - integrity sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA== +marked@~2.0.3: + version "2.0.7" + resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.7.tgz#bc5b857a09071b48ce82a1f7304913a993d4b7d1" + integrity sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ== media-typer@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -mime-db@1.47.0: - version "1.47.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" - integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== mime-types@^2.1.12: - version "2.1.30" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" - integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== dependencies: - mime-db "1.47.0" + mime-db "1.48.0" mime@^1.3.4: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^3.1.0: version "3.1.0" @@ -1669,18 +3592,28 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.4: +minimatch@^3.0.0, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3, minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@1.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -1688,116 +3621,113 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.5" -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -mocha@^8.1.3: - version "8.3.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc" - integrity sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.1.6" - growl "1.10.5" - he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" - ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -mri@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" - integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w== - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - music-metadata@^7.4.1: - version "7.8.3" - resolved "https://registry.yarnpkg.com/music-metadata/-/music-metadata-7.8.3.tgz#876c46f0c3f4cf27f40b2c0393463e6a71700f69" - integrity sha512-sF0oEmN88IJCuAR+1ZwyPqDvIrACddBJbWfRUQo3KZ6YN+V/EAex5AVWwlQxvY2XMcMJ+pPZEnALVnz9IXvU4A== + version "7.8.8" + resolved "https://registry.yarnpkg.com/music-metadata/-/music-metadata-7.8.8.tgz#3f1c5a38eb54642a96f0d66fb9e704088fb8b427" + integrity sha512-NUtXW6FQA1fvBp4Q5o78Df6LHWqyny8bHxArJ79i5lr2N28jEd9HYyH09CAHKLjwiv58QLRa8r3P5nX36Spcxw== dependencies: content-type "^1.0.4" debug "^4.3.1" - file-type "^16.3.0" + file-type "^16.5.0" media-typer "^1.1.0" strtok3 "^6.0.8" token-types "^2.1.1" -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== +node-abi@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.5.0.tgz#26e8b7b251c3260a5ac5ba5aef3b4345a0229248" + integrity sha512-LtHvNIBgOy5mO8mPEUtkCW/YCRWYEKshIvqhe1GHHyXEHEB5mgICyYnAcl4qan3uFeRROErKGzatFHPf6kDxWw== dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" + semver "^7.3.5" -normalize-path@^3.0.0, normalize-path@~3.0.0: +node-addon-api@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" + integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== + +node-cache@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== + dependencies: + clone "2.x" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-releases@^1.1.71: + version "1.1.73" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== + +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" -object-assign@^4.0.1: +npmlog@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + 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@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== - -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -1814,11 +3744,51 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.entries@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" + integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.fromentries@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" + integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.hasown@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" + integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + omggif@^1.0.10, omggif@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== +on-exit-leak-free@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" + integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1826,6 +3796,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + onigasm@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/onigasm/-/onigasm-2.2.5.tgz#cc4d2a79a0fa0b64caec1f4c7ea367585a676892" @@ -1833,30 +3810,66 @@ onigasm@^2.2.5: dependencies: lru-cache "^5.1.1" -p-cancelable@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.0.tgz#4d51c3b91f483d02a0d300765321fca393d758dd" - integrity sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ== - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: - yocto-queue "^0.1.0" + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - p-limit "^3.0.2" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pako@^1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-bmfont-ascii@^1.0.3: version "1.0.6" resolved "https://registry.yarnpkg.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz#11ac3c3ff58f7c2020ab22769079108d4dfa0285" @@ -1876,23 +3889,14 @@ parse-bmfont-xml@^1.1.4: xml2js "^0.4.5" parse-headers@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" - integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + version "2.0.4" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.4.tgz#9eaf2d02bed2d1eff494331ce3df36d7924760bf" + integrity sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw== -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== path-exists@^4.0.0: version "4.0.0" @@ -1904,90 +3908,71 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.7: +path-parse@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -peek-readable@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-3.1.3.tgz#932480d46cf6aa553c46c68566c4fb69a82cd2b1" - integrity sha512-mpAcysyRJxmICBcBa5IXH7SZPvWkcghm6Fk8RekoS3v+BpbSzlZzuWbMx+GXrlUwESi9qHar4nVEZNMKylIHvg== +peek-readable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.0.0.tgz#b024ef391c86136eba0ae9df3ff4f966a09e9a7e" + integrity sha512-kLbU4cz6h86poGVBKgAVMpFmD47nX04fPPQNKnv9fuj+IJZYkEBjsYAVu5nDbZWx0ZsWwWlMzeG90zQa5KLBaA== phin@^2.9.1: version "2.9.3" resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" - integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== - -pify@^2.0.0: +picomatch@^2.0.4, picomatch@^2.2.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= +pino-abstract-transport@v0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" + integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ== dependencies: - pinkie "^2.0.0" + duplexify "^4.1.2" + split2 "^4.0.0" -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pino-std-serializers@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" + integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== -pino-pretty@^4.3.0: - version "4.7.1" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-4.7.1.tgz#499cf185e110399deae731221c899915c811bd1a" - integrity sha512-ILE5YBpur88FlZ0cr1BNqVjgG9fOoK+md3peqmcs7AC6oq7SNiaJioIcrykMxfNsuygMYjUJtvAcARRE9aRc9w== - dependencies: - "@hapi/bourne" "^2.0.0" - args "^5.0.1" - chalk "^4.0.0" - dateformat "^4.5.1" - fast-safe-stringify "^2.0.7" - jmespath "^0.15.0" - joycon "^2.2.5" - pump "^3.0.0" - readable-stream "^3.6.0" - split2 "^3.1.1" - strip-json-comments "^3.1.1" - -pino-std-serializers@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" - integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== - -pino@^6.7.0: - version "6.11.2" - resolved "https://registry.yarnpkg.com/pino/-/pino-6.11.2.tgz#2f3d119c526651aab4ec3d280844785d52d0b690" - integrity sha512-bmzxwbrIPxQUlAuMkF4PWVErUGERU4z37HazlhflKFg08crsNE3fACGN6gPwg5xtKOK47Ux5cZm8YCuLV4wWJg== +pino@*, pino@^7.0.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/pino/-/pino-7.6.3.tgz#47841acd2b83d2b0c929966433a5b4abd7299f8e" + integrity sha512-TCUDiN0QmzruJ5UE4mIFkTcfCSROV7IVRp5SEUH77UnjDHbeoFntj9jQexvYZhCHp1yhjYCH6vZ8rV85L04KaA== dependencies: fast-redact "^3.0.0" - fast-safe-stringify "^2.0.7" - flatstr "^1.0.12" - pino-std-serializers "^3.1.0" - quick-format-unescaped "4.0.1" - sonic-boom "^1.0.2" + on-exit-leak-free "^0.2.0" + pino-abstract-transport v0.5.0 + pino-std-serializers "^4.0.0" + process-warning "^1.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.1.0" + safe-stable-stringify "^2.1.0" + sonic-boom "^2.2.1" + thread-stream "^0.13.0" + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" pixelmatch@^4.0.2: version "4.0.2" @@ -1996,25 +3981,127 @@ pixelmatch@^4.0.2: dependencies: pngjs "^3.0.0" +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + pngjs@^3.0.0, pngjs@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== +prebuild-install@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.0.tgz#3c5ce3902f1cb9d6de5ae94ca53575e4af0c1574" + integrity sha512-IvSenf33K7JcgddNz2D5w521EgO+4aMMjFt73Uk9FRzQ7P+QZPKrp7qPsDydsSwjGt3T5xRNnM1bj1zMTD5fTA== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +pretty-format@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" + integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ== + dependencies: + "@jest/types" "^27.0.6" + ansi-regex "^5.0.0" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process-warning@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress@^2.0.3: +progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +prompts@^2.0.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +protobufjs@6.8.8: + version "6.8.8" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" + integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + protobufjs@^6.10.1: - version "6.10.2" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b" - integrity sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -2027,9 +4114,14 @@ protobufjs@^6.10.1: "@protobufjs/pool" "^1.1.0" "@protobufjs/utf8" "^1.1.0" "@types/long" "^4.0.1" - "@types/node" "^13.7.0" + "@types/node" ">=13.7.0" long "^4.0.0" +psl@^1.1.33: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -2038,46 +4130,60 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + qrcode-terminal@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== -quick-format-unescaped@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz#437a5ea1a0b61deb7605f8ab6a8fd3858dbeb701" - integrity sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +quick-format-unescaped@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz#6d6b66b8207aa2b35eef12be1421bb24c428f652" + integrity sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg== -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: - safe-buffer "^5.1.0" + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" + 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" -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -readable-stream@^3.0.0, readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -2087,19 +4193,16 @@ readable-stream@^3.0.0, readable-stream@^3.6.0: util-deprecate "^1.0.1" readable-web-to-node-stream@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.1.tgz#3f619b1bc5dd73a4cfe5c5f9b4f6faba55dff845" - integrity sha512-4zDC6CvjUyusN7V0QLsXVB7pJCD9+vtrM9bYDRv6uBQ+SKfx36rp5AFNPRgh9auKRul/a1iFZJYXcCbwRL+SaA== + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== dependencies: - "@types/readable-stream" "^2.3.9" readable-stream "^3.6.0" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" +real-require@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" + integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== rechoir@^0.6.2: version "0.6.2" @@ -2108,37 +4211,52 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= +regexp.prototype.flags@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== dependencies: - is-finite "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.1.3" + +regexpp@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -resolve-alpn@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.1.tgz#4a006a7d533c81a5dd04681612090fde227cd6e1" - integrity sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -resolve@^1.0.0, resolve@^1.10.0: +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.1.6, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -2146,82 +4264,193 @@ resolve@^1.0.0, resolve@^1.10.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.1.6: - version "1.21.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" - integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== +resolve@^2.0.0-next.3: + version "2.0.0-next.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" + integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== dependencies: - is-core-module "^2.8.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" + is-core-module "^2.2.0" + path-parse "^1.0.6" -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== - dependencies: - lowercase-keys "^2.0.0" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.6.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -safe-buffer@*, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-stable-stringify@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" + integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -"semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -serialize-javascript@5.0.1: +saxes@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: - randombytes "^2.1.0" + xmlchars "^2.2.0" + +semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +sharp@^0.29.3: + version "0.29.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2" + integrity sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA== + dependencies: + color "^4.0.1" + detect-libc "^1.0.3" + node-addon-api "^4.2.0" + prebuild-install "^7.0.0" + semver "^7.3.5" + simple-get "^4.0.0" + tar-fs "^2.1.1" + tunnel-agent "^0.6.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shelljs@^0.8.4: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== dependencies: glob "^7.0.0" interpret "^1.0.0" rechoir "^0.6.2" shiki@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.9.3.tgz#7bf7bcf3ed50ca525ec89cc09254abce4264d5ca" - integrity sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA== + version "0.9.5" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.9.5.tgz#c8da81a05fbfd1810729c6873901a729a72ec541" + integrity sha512-XFn+rl3wIowDjzdr5DlHoHgQphXefgUTs2bNp/bZu4WF9gTrTLnKwio3f28VjiFG6Jpip7yQn/p4mMj6OrjrtQ== dependencies: + json5 "^2.2.0" onigasm "^2.2.5" - vscode-textmate "^5.2.0" + vscode-textmate "5.2.0" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" signal-exit@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + +signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== -sonic-boom@^1.0.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" - integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675" + integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +sonic-boom@^2.2.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.6.0.tgz#8786fc78be07c18a90381acd816d1d4afe3537a2" + integrity sha512-6xYZFRmDEtxGqfOKcDQ4cPLrNa0SPEDI+wlzDAHowXE6YV42NeXqg9mP2KkiM8JVu3lHfZ2iQKYlGOz+kTpphg== dependencies: atomic-sleep "^1.0.0" - flatstr "^1.0.12" -source-map-support@^0.5.12, source-map-support@^0.5.17: +source-map-support@^0.5.6: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -2229,51 +4458,68 @@ source-map-support@^0.5.12, source-map-support@^0.5.17: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +split2@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" + integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +stack-utils@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" + escape-string-regexp "^2.0.0" -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" + char-regex "^1.0.2" + strip-ansi "^6.0.0" -spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== - -split2@^3.1.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: - readable-stream "^3.0.0" + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" @@ -2284,6 +4530,20 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string.prototype.matchall@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" + integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.3.1" + side-channel "^1.0.4" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -2307,12 +4567,19 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: - ansi-regex "^3.0.0" + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" strip-ansi@^6.0.0: version "6.0.0" @@ -2321,50 +4588,39 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-bom@^2.0.0: +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - -strip-json-comments@3.1.1, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@^2.0.0: +strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strtok3@^6.0.3, strtok3@^6.0.8: - version "6.0.8" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.0.8.tgz#c839157f615c10ba0f4ae35067dad9959eeca346" - integrity sha512-QLgv+oiXwXgCgp2PdPPa+Jpp4D9imK9e/0BsyfeFMr6QL6wMVqoVn9+OXQ9I7MZbmUzN6lmitTJ09uwS2OmGcw== + version "6.2.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.2.0.tgz#7c24e74a9a24c7f2b976e4469654d459e6795d91" + integrity sha512-hBbPN4+f9fypbfTs0NImALgzYcb6k/blFr2mJVX6bUOmJCbXe/trDHdIC+Ir5XUXRMGFvq487ecwLitDoHVoew== dependencies: - "@tokenizer/token" "^0.1.1" - "@types/debug" "^4.1.5" - peek-readable "^3.1.3" - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" + peek-readable "^4.0.0" supports-color@^5.3.0: version "5.5.0" @@ -2373,17 +4629,98 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^6.0.9: + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tar-fs@^2.0.0, tar-fs@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +thread-stream@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.13.0.tgz#c68054bdea250c5d8d400caa3233a150d5461cca" + integrity sha512-kTMZeX4Dzlb1zZ00/01aerGaTw2i8NE4sWF0TvF1uXewRhCiUjCvatQkvxIvFqauWG2ADFS2Wpd3qBeYL9i3dg== + dependencies: + real-require "^0.1.0" + +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== timm@^1.6.1: version "1.7.1" @@ -2395,6 +4732,16 @@ tinycolor2@^1.4.1: resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2410,54 +4757,103 @@ token-types@^2.0.0, token-types@^2.1.1: "@tokenizer/token" "^0.1.1" ieee754 "^1.2.1" -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -ts-node-dev@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.1.6.tgz#ee2113718cb5a92c1c8f4229123ad6afbeba01f8" - integrity sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ== +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: - chokidar "^3.5.1" - dateformat "~1.0.4-1.2.3" - dynamic-dedupe "^0.3.0" - minimist "^1.2.5" - mkdirp "^1.0.4" - resolve "^1.0.0" - rimraf "^2.6.1" - source-map-support "^0.5.12" - tree-kill "^1.2.2" - ts-node "^9.0.0" - tsconfig "^7.0.0" + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" -ts-node@^9.0.0: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== dependencies: + punycode "^2.1.1" + +ts-jest@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.3.tgz#808492f022296cde19390bb6ad627c8126bf93f8" + integrity sha512-U5rdMjnYam9Ucw+h0QvtNDbc5+88nxt7tbIvqaZUhFrfG4+SkWhMXjejCLVGcpILTPuV+H3W/GZDZrnZFpPeXw== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + jest-util "^27.0.0" + json5 "2.x" + lodash "4.x" + make-error "1.x" + mkdirp "1.x" + semver "7.x" + yargs-parser "20.x" + +ts-node@^10.0.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5" + integrity sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw== + dependencies: + "@cspotcode/source-map-support" "0.6.1" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.17" yn "3.1.1" -tsconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" - integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: - "@types/strip-bom" "^3.0.0" - "@types/strip-json-comments" "0.0.30" - strip-bom "^3.0.0" - strip-json-comments "^2.0.0" + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -2466,39 +4862,39 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedoc-default-themes@^0.12.9: +typedoc-default-themes@^0.12.10: version "0.12.10" resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz#614c4222fe642657f37693ea62cad4dafeddf843" integrity sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA== typedoc@^0.20.0-beta.27: - version "0.20.35" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.20.35.tgz#c36996098cbeb2ef63d9d7991262a071b98336a3" - integrity sha512-7sNca19LXg2hgyGHq3b33tQ1YFApmd8aBDEzWQ2ry4VDkw/NdFWkysGiGRY1QckDCB0gVH8+MlXA4K71IB3azg== + version "0.20.37" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.20.37.tgz#9b9417149cb815e3d569fc300b5d2c6e4e6c5d93" + integrity sha512-9+qDhdc4X00qTNOtii6QX2z7ndAeWVOso7w3MPSoSJdXlVhpwPfm1yEp4ooKuWA9fiQILR8FKkyjmeqa13hBbw== dependencies: colors "^1.4.0" fs-extra "^9.1.0" handlebars "^4.7.7" lodash "^4.17.21" lunr "^2.3.9" - marked "^2.0.1" + marked "~2.0.3" minimatch "^3.0.0" progress "^2.0.3" shelljs "^0.8.4" shiki "^0.9.3" - typedoc-default-themes "^0.12.9" + typedoc-default-themes "^0.12.10" typescript@^4.0.0: - version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" - integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== uglify-js@^3.1.4: - version "3.13.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.4.tgz#592588bb9f47ae03b24916e2471218d914955574" - integrity sha512-kv7fCkIXyQIilD5/yQy8O+uagsYIOt5cZvs890W40/e/rvjMSzJw81o9Bg0tkURxzZBROtDQhW2LFjOGoK3RZw== + version "3.13.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" + integrity sha512-57H3ACYFXeo1IaZ1w02sfA71wI60MGco/IQFjOqK+WtKoprh7Go2/yvd2HPtoJILO2Or84ncLccI4xoHMTSbGg== -unbox-primitive@^1.0.0: +unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== @@ -2508,11 +4904,23 @@ unbox-primitive@^1.0.0: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + utif@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759" @@ -2520,35 +4928,81 @@ utif@^2.0.1: dependencies: pako "^1.0.5" -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@^0.12.0: - version "0.12.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" - integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - safe-buffer "^5.1.2" - which-typed-array "^1.1.2" +v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== +v8-to-istanbul@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz#4229f2a99e367f3f018fa1d5c2b8ec684667c69c" + integrity sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg== dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" -vscode-textmate@^5.2.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.4.0.tgz#4b25ffc1f14ac3a90faf9a388c67a01d24257cd7" - integrity sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w== +vscode-textmate@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" which-boxed-primitive@^1.0.2: version "1.0.2" @@ -2561,43 +5015,30 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" - integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.0" - es-abstract "^1.18.0-next.1" - foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" - -which@2.0.2: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +wide-align@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: - string-width "^1.0.2 || 2" + string-width "^1.0.2 || 2 || 3 || 4" + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2612,10 +5053,25 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@^7.3.1: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.5: + version "7.5.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" + integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== + +ws@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d" + integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw== xhr@^2.0.1: version "2.6.0" @@ -2627,6 +5083,11 @@ xhr@^2.0.1: parse-headers "^2.0.0" xtend "^4.0.0" +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + xml-parse-from-string@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" @@ -2645,6 +5106,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -2660,27 +5126,17 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@^20.2.2: - version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" - integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: +yargs@^16.0.3: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -2697,8 +5153,3 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==