mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Merge pull request #252 from Auties00/pairing_code
Pairing code login for Web API
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import parsePhoneNumber from 'libphonenumber-js'
|
||||
import NodeCache from 'node-cache'
|
||||
import readline from 'readline'
|
||||
import makeWASocket, { AnyMessageContent, delay, DisconnectReason, fetchLatestBaileysVersion, getAggregateVotesInPollMessage, makeCacheableSignalKeyStore, makeInMemoryStore, PHONENUMBER_MCC, proto, useMultiFileAuthState, WAMessageContent, WAMessageKey } from '../src'
|
||||
@@ -10,12 +9,17 @@ logger.level = 'trace'
|
||||
|
||||
const useStore = !process.argv.includes('--no-store')
|
||||
const doReplies = !process.argv.includes('--no-reply')
|
||||
const usePairingCode = process.argv.includes('--use-pairing-code')
|
||||
const useMobile = process.argv.includes('--mobile')
|
||||
|
||||
// external map to store retry counts of messages when decryption/encryption fails
|
||||
// keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts
|
||||
const msgRetryCounterCache = new NodeCache()
|
||||
|
||||
// Read line interface
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
||||
const question = (text: string) => new Promise<string>((resolve) => rl.question(text, resolve))
|
||||
|
||||
// the store maintains the data of the WA connection in memory
|
||||
// can be written out to a file & read from it
|
||||
const store = useStore ? makeInMemoryStore({ logger }) : undefined
|
||||
@@ -35,7 +39,7 @@ const startSock = async() => {
|
||||
const sock = makeWASocket({
|
||||
version,
|
||||
logger,
|
||||
printQRInTerminal: true,
|
||||
printQRInTerminal: !usePairingCode,
|
||||
mobile: useMobile,
|
||||
auth: {
|
||||
creds: state.creds,
|
||||
@@ -53,18 +57,27 @@ const startSock = async() => {
|
||||
|
||||
store?.bind(sock.ev)
|
||||
|
||||
// Pairing code for Web clients
|
||||
if(usePairingCode && !sock.authState.creds.registered) {
|
||||
if(useMobile) {
|
||||
throw new Error('Cannot use pairing code with mobile api')
|
||||
}
|
||||
|
||||
const phoneNumber = await question('Please enter your mobile phone number:\n')
|
||||
const code = await sock.requestPairingCode(phoneNumber)
|
||||
console.log(`Pairing code: ${code}`)
|
||||
}
|
||||
|
||||
// If mobile was chosen, ask for the code
|
||||
if(useMobile && !sock.authState.creds.registered) {
|
||||
const question = (text: string) => new Promise<string>((resolve) => rl.question(text, resolve))
|
||||
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
||||
const { registration } = sock.authState.creds || { registration: {} }
|
||||
|
||||
if(!registration.phoneNumber) {
|
||||
registration.phoneNumber = await question('Please enter your mobile phone number:\n')
|
||||
}
|
||||
|
||||
const phoneNumber = parsePhoneNumber(registration!.phoneNumber)
|
||||
const libPhonenumber = await import("libphonenumber-js")
|
||||
const phoneNumber = libPhonenumber.parsePhoneNumber(registration!.phoneNumber)
|
||||
if(!phoneNumber?.isValid()) {
|
||||
throw new Error('Invalid phone number: ' + registration!.phoneNumber)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { randomBytes } from 'crypto'
|
||||
import NodeCache from 'node-cache'
|
||||
import { proto } from '../../WAProto'
|
||||
import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaults'
|
||||
import { MessageReceiptType, MessageRelayOptions, MessageUserReceipt, SocketConfig, WACallEvent, WAMessageKey, WAMessageStatus, WAMessageStubType, WAPatchName } from '../Types'
|
||||
import { decodeMediaRetryNode, decryptMessageNode, delay, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils'
|
||||
import {
|
||||
aesDecryptCTR,
|
||||
aesEncryptGCM,
|
||||
Curve,
|
||||
decodeMediaRetryNode,
|
||||
decryptMessageNode,
|
||||
delay,
|
||||
derivePairingCodeKey,
|
||||
encodeBigEndian,
|
||||
encodeSignedDeviceIdentity,
|
||||
getCallStatusFromNode,
|
||||
getHistoryMsg,
|
||||
getNextPreKeys,
|
||||
getStatusFromReceiptType, hkdf,
|
||||
unixTimestampSeconds,
|
||||
xmppPreKey,
|
||||
xmppSignedPreKey
|
||||
} from '../Utils'
|
||||
import { cleanMessage } from '../Utils'
|
||||
import { makeMutex } from '../Utils/make-mutex'
|
||||
import { cleanMessage } from '../Utils/process-message'
|
||||
import { areJidsSameUser, BinaryNode, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, isJidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary'
|
||||
import {
|
||||
areJidsSameUser,
|
||||
BinaryNode,
|
||||
getAllBinaryNodeChildren,
|
||||
getBinaryNodeChild,
|
||||
getBinaryNodeChildBuffer,
|
||||
getBinaryNodeChildren,
|
||||
isJidGroup,
|
||||
isJidUser,
|
||||
jidDecode,
|
||||
jidNormalizedUser,
|
||||
S_WHATSAPP_NET
|
||||
} from '../WABinary'
|
||||
import { extractGroupMetadata } from './groups'
|
||||
import { makeMessagesSocket } from './messages-send'
|
||||
|
||||
@@ -235,7 +266,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
case 'remove':
|
||||
case 'add':
|
||||
case 'leave':
|
||||
const stubType = `GROUP_PARTICIPANT_${child.tag!.toUpperCase()}`
|
||||
const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
|
||||
msg.messageStubType = WAMessageStubType[stubType]
|
||||
|
||||
const participants = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
|
||||
@@ -306,7 +337,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
break
|
||||
case 'devices':
|
||||
const devices = getBinaryNodeChildren(child, 'device')
|
||||
if(areJidsSameUser(child.attrs.jid, authState.creds!.me!.id)) {
|
||||
if(areJidsSameUser(child.attrs.jid, authState.creds.me!.id)) {
|
||||
const deviceJids = devices.map(d => d.attrs.jid)
|
||||
logger.info({ deviceJids }, 'got my own devices')
|
||||
}
|
||||
@@ -334,7 +365,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
result.messageStubType = WAMessageStubType.GROUP_CHANGE_ICON
|
||||
|
||||
if(setPicture) {
|
||||
result.messageStubParameters = [ setPicture.attrs.id ]
|
||||
result.messageStubParameters = [setPicture.attrs.id]
|
||||
}
|
||||
|
||||
result.participant = node?.attrs.author
|
||||
@@ -364,6 +395,63 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
break
|
||||
case 'link_code_companion_reg':
|
||||
const linkCodeCompanionReg = getBinaryNodeChild(node, 'link_code_companion_reg')
|
||||
const ref = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_ref'))
|
||||
const primaryIdentityPublicKey = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'primary_identity_pub'))
|
||||
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'))
|
||||
const codePairingPublicKey = decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped)
|
||||
const companionSharedKey = Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey)
|
||||
const random = randomBytes(32)
|
||||
const linkCodeSalt = randomBytes(32)
|
||||
const linkCodePairingExpanded = hkdf(companionSharedKey, 32, {
|
||||
salt: linkCodeSalt,
|
||||
info: 'link_code_pairing_key_bundle_encryption_key'
|
||||
})
|
||||
const encryptPayload = Buffer.concat([Buffer.from(authState.creds.signedIdentityKey.public), primaryIdentityPublicKey, random])
|
||||
const encryptIv = randomBytes(12)
|
||||
const encrypted = aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0))
|
||||
const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted])
|
||||
const identitySharedKey = Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey)
|
||||
const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random])
|
||||
authState.creds.advSecretKey = hkdf(identityPayload, 32, { info: 'adv_secret' }).toString('base64')
|
||||
await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'set',
|
||||
id: sock.generateMessageTag(),
|
||||
xmlns: 'md'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'link_code_companion_reg',
|
||||
attrs: {
|
||||
jid: authState.creds.me!.id,
|
||||
stage: 'companion_finish',
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'link_code_pairing_wrapped_key_bundle',
|
||||
attrs: {},
|
||||
content: encryptedPayload
|
||||
},
|
||||
{
|
||||
tag: 'companion_identity_public',
|
||||
attrs: {},
|
||||
content: authState.creds.signedIdentityKey.public
|
||||
},
|
||||
{
|
||||
tag: 'link_code_pairing_ref',
|
||||
attrs: {},
|
||||
content: ref
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
authState.creds.registered = true
|
||||
ev.emit('creds.update', authState.creds)
|
||||
}
|
||||
|
||||
if(Object.keys(result).length) {
|
||||
@@ -371,6 +459,23 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
}
|
||||
}
|
||||
|
||||
function decipherLinkPublicKey(data: Uint8Array | Buffer) {
|
||||
const buffer = toRequiredBuffer(data)
|
||||
const salt = buffer.slice(0, 32)
|
||||
const secretKey = derivePairingCodeKey(authState.creds.pairingCode!, salt)
|
||||
const iv = buffer.slice(32, 48)
|
||||
const payload = buffer.slice(48, 80)
|
||||
return aesDecryptCTR(payload, secretKey, iv)
|
||||
}
|
||||
|
||||
function toRequiredBuffer(data: Uint8Array | Buffer | undefined) {
|
||||
if(data === undefined) {
|
||||
throw new Boom('Invalid buffer', { statusCode: 400 })
|
||||
}
|
||||
|
||||
return data instanceof Buffer ? data : Buffer.from(data)
|
||||
}
|
||||
|
||||
const willSendMessageAgain = (id: string, participant: string) => {
|
||||
const key = `${id}:${participant}`
|
||||
const retryCount = msgRetryCache.get<number>(key) || 0
|
||||
|
||||
@@ -1,13 +1,49 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { URL } from 'url'
|
||||
import { promisify } from 'util'
|
||||
import { proto } from '../../WAProto'
|
||||
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, MOBILE_ENDPOINT, MOBILE_NOISE_HEADER, MOBILE_PORT, NOISE_WA_HEADER } from '../Defaults'
|
||||
import {
|
||||
DEF_CALLBACK_PREFIX,
|
||||
DEF_TAG_PREFIX,
|
||||
INITIAL_PREKEY_COUNT,
|
||||
MIN_PREKEY_COUNT,
|
||||
MOBILE_ENDPOINT,
|
||||
MOBILE_NOISE_HEADER,
|
||||
MOBILE_PORT,
|
||||
NOISE_WA_HEADER
|
||||
} from '../Defaults'
|
||||
import { DisconnectReason, SocketConfig } from '../Types'
|
||||
import { addTransactionCapability, bindWaitForConnectionUpdate, configureSuccessfulPairing, Curve, generateLoginNode, generateMdTagPrefix, generateMobileNode, generateRegistrationNode, getCodeFromWSError, getErrorCodeFromStreamError, getNextPreKeysNode, makeNoiseHandler, printQRIfNecessaryListener, promiseTimeout } from '../Utils'
|
||||
import { makeEventBuffer } from '../Utils/event-buffer'
|
||||
import { assertNodeErrorFree, BinaryNode, binaryNodeToString, encodeBinaryNode, getBinaryNodeChild, getBinaryNodeChildren, S_WHATSAPP_NET } from '../WABinary'
|
||||
import {
|
||||
addTransactionCapability,
|
||||
aesEncryptCTR,
|
||||
bindWaitForConnectionUpdate,
|
||||
bytesToCrockford,
|
||||
configureSuccessfulPairing,
|
||||
Curve,
|
||||
derivePairingCodeKey,
|
||||
generateLoginNode,
|
||||
generateMdTagPrefix,
|
||||
generateMobileNode,
|
||||
generateRegistrationNode,
|
||||
getCodeFromWSError,
|
||||
getErrorCodeFromStreamError,
|
||||
getNextPreKeysNode,
|
||||
makeEventBuffer,
|
||||
makeNoiseHandler,
|
||||
printQRIfNecessaryListener,
|
||||
promiseTimeout
|
||||
} from '../Utils'
|
||||
import {
|
||||
assertNodeErrorFree,
|
||||
BinaryNode,
|
||||
binaryNodeToString,
|
||||
encodeBinaryNode,
|
||||
getBinaryNodeChild,
|
||||
getBinaryNodeChildren,
|
||||
jidEncode,
|
||||
S_WHATSAPP_NET
|
||||
} from '../WABinary'
|
||||
import { MobileSocketClient, WebSocketClient } from './Client'
|
||||
|
||||
/**
|
||||
@@ -34,7 +70,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
let url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
||||
|
||||
config.mobile = config.mobile || config.auth?.creds?.registered || url.protocol === 'tcp:'
|
||||
config.mobile = config.mobile || url.protocol === 'tcp:'
|
||||
|
||||
if(config.mobile && url.protocol !== 'tcp:') {
|
||||
url = new URL(`tcp://${MOBILE_ENDPOINT}:${MOBILE_PORT}`)
|
||||
@@ -141,15 +177,14 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
/**
|
||||
* 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 msgId the message tag to await
|
||||
* @param timeoutMs timeout after which the promise will reject
|
||||
*/
|
||||
const waitForMessage = async<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
||||
let onRecv: (json) => void
|
||||
let onErr: (err) => void
|
||||
try {
|
||||
const result = await promiseTimeout<T>(timeoutMs,
|
||||
return await promiseTimeout<T>(timeoutMs,
|
||||
(resolve, reject) => {
|
||||
onRecv = resolve
|
||||
onErr = err => {
|
||||
@@ -161,7 +196,6 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ws.off('error', onErr)
|
||||
},
|
||||
)
|
||||
return result
|
||||
} finally {
|
||||
ws.off(`TAG:${msgId}`, onRecv!)
|
||||
ws.off('close', onErr!) // if the socket closes, you'll never receive the message
|
||||
@@ -213,7 +247,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
node = generateRegistrationNode(creds, config)
|
||||
logger.info({ node }, 'not logged in, attempting registration...')
|
||||
} else {
|
||||
node = generateLoginNode(creds.me!.id, config)
|
||||
node = generateLoginNode(creds.me.id, config)
|
||||
logger.info({ node }, 'logging in...')
|
||||
}
|
||||
|
||||
@@ -448,6 +482,71 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }))
|
||||
}
|
||||
|
||||
const requestPairingCode = async(phoneNumber: string): Promise<string> => {
|
||||
authState.creds.pairingCode = bytesToCrockford(randomBytes(5))
|
||||
authState.creds.me = {
|
||||
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
||||
name: '~'
|
||||
}
|
||||
ev.emit('creds.update', authState.creds)
|
||||
await sendNode({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'set',
|
||||
id: generateMessageTag(),
|
||||
xmlns: 'md'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'link_code_companion_reg',
|
||||
attrs: {
|
||||
jid: authState.creds.me.id,
|
||||
stage: 'companion_hello',
|
||||
// eslint-disable-next-line camelcase
|
||||
should_show_push_notification: 'true'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
|
||||
attrs: {},
|
||||
content: await generatePairingKey()
|
||||
},
|
||||
{
|
||||
tag: 'companion_server_auth_key_pub',
|
||||
attrs: {},
|
||||
content: authState.creds.noiseKey.public
|
||||
},
|
||||
{
|
||||
tag: 'companion_platform_id',
|
||||
attrs: {},
|
||||
content: '49' // Chrome
|
||||
},
|
||||
{
|
||||
tag: 'companion_platform_display',
|
||||
attrs: {},
|
||||
content: config.browser[0]
|
||||
},
|
||||
{
|
||||
tag: 'link_code_pairing_nonce',
|
||||
attrs: {},
|
||||
content: '0'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
return authState.creds.pairingCode
|
||||
}
|
||||
|
||||
async function generatePairingKey() {
|
||||
const salt = randomBytes(32)
|
||||
const randomIv = randomBytes(16)
|
||||
const key = derivePairingCodeKey(authState.creds.pairingCode!, salt)
|
||||
const ciphered = aesEncryptCTR(authState.creds.pairingEphemeralKeyPair.public, key, randomIv)
|
||||
return Buffer.concat([salt, randomIv, ciphered])
|
||||
}
|
||||
|
||||
ws.on('message', onMessageRecieved)
|
||||
ws.on('open', async() => {
|
||||
try {
|
||||
@@ -619,6 +718,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
onUnexpectedError,
|
||||
uploadPreKeys,
|
||||
uploadPreKeysToServerIfRequired,
|
||||
requestPairingCode,
|
||||
/** Waits for the connection to WA to reach a state */
|
||||
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@ export type AccountSettings = {
|
||||
|
||||
export type AuthenticationCreds = SignalCreds & {
|
||||
readonly noiseKey: KeyPair
|
||||
readonly advSecretKey: string
|
||||
readonly pairingEphemeralKeyPair: KeyPair
|
||||
advSecretKey: string
|
||||
|
||||
me?: Contact
|
||||
account?: proto.IADVSignedDeviceIdentity
|
||||
@@ -66,6 +67,7 @@ export type AuthenticationCreds = SignalCreds & {
|
||||
registered: boolean
|
||||
backupToken: Buffer
|
||||
registration: RegistrationOptions
|
||||
pairingCode: string | undefined
|
||||
}
|
||||
|
||||
export type SignalDataTypeMap = {
|
||||
|
||||
@@ -196,6 +196,7 @@ export const initAuthCreds = (): AuthenticationCreds => {
|
||||
const identityKey = Curve.generateKeyPair()
|
||||
return {
|
||||
noiseKey: Curve.generateKeyPair(),
|
||||
pairingEphemeralKeyPair: Curve.generateKeyPair(),
|
||||
signedIdentityKey: identityKey,
|
||||
signedPreKey: signedKeyPair(identityKey, 1),
|
||||
registrationId: generateRegistrationId(),
|
||||
@@ -213,6 +214,7 @@ export const initAuthCreds = (): AuthenticationCreds => {
|
||||
identityId: randomBytes(20),
|
||||
registered: false,
|
||||
backupToken: randomBytes(20),
|
||||
registration: {} as never
|
||||
registration: {} as never,
|
||||
pairingCode: undefined,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from 'crypto'
|
||||
import { createCipheriv, createDecipheriv, createHash, createHmac, pbkdf2Sync, randomBytes } from 'crypto'
|
||||
import HKDF from 'futoin-hkdf'
|
||||
import * as libsignal from 'libsignal'
|
||||
import { KEY_BUNDLE_TYPE } from '../Defaults'
|
||||
@@ -74,6 +74,16 @@ export function aesDecryptGCM(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8
|
||||
return Buffer.concat([ decipher.update(enc), decipher.final() ])
|
||||
}
|
||||
|
||||
export function aesEncryptCTR(plaintext: Uint8Array, key: Uint8Array, iv: Uint8Array) {
|
||||
const cipher = createCipheriv('aes-256-ctr', key, iv)
|
||||
return Buffer.concat([cipher.update(plaintext), cipher.final()])
|
||||
}
|
||||
|
||||
export function aesDecryptCTR(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array) {
|
||||
const decipher = createDecipheriv('aes-256-ctr', key, iv)
|
||||
return Buffer.concat([decipher.update(ciphertext), decipher.final()])
|
||||
}
|
||||
|
||||
/** 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))
|
||||
@@ -114,4 +124,8 @@ export function md5(buffer: Buffer) {
|
||||
// HKDF key expansion
|
||||
export function hkdf(buffer: Uint8Array | Buffer, expandedLength: number, info: { salt?: Buffer, info?: string }) {
|
||||
return HKDF(!Buffer.isBuffer(buffer) ? Buffer.from(buffer) : buffer, expandedLength, info)
|
||||
}
|
||||
|
||||
export function derivePairingCodeKey(pairingCode: string, salt: Buffer) {
|
||||
return pbkdf2Sync(pairingCode, salt, 2 << 16, 32, 'sha256')
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import EventEmitter from 'events'
|
||||
import { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import { BaileysEvent, BaileysEventEmitter, BaileysEventMap, BufferedEventData, Chat, ChatUpdate, Contact, WAMessage, WAMessageStatus } from '../Types'
|
||||
import { trimUndefineds } from './generics'
|
||||
import { trimUndefined } from './generics'
|
||||
import { updateMessageWithReaction, updateMessageWithReceipt } from './messages'
|
||||
import { isRealMessage, shouldIncrementChatUnread } from './process-message'
|
||||
|
||||
@@ -209,7 +209,7 @@ function append<E extends BufferableEvent>(
|
||||
for(const contact of eventData.contacts as Contact[]) {
|
||||
const existingContact = data.historySets.contacts[contact.id]
|
||||
if(existingContact) {
|
||||
Object.assign(existingContact, trimUndefineds(contact))
|
||||
Object.assign(existingContact, trimUndefined(contact))
|
||||
} else {
|
||||
const historyContactId = `c:${contact.id}`
|
||||
const hasAnyName = contact.notify || contact.name || contact.verifiedName
|
||||
@@ -321,14 +321,14 @@ function append<E extends BufferableEvent>(
|
||||
}
|
||||
|
||||
if(upsert) {
|
||||
upsert = Object.assign(upsert, trimUndefineds(contact))
|
||||
upsert = Object.assign(upsert, trimUndefined(contact))
|
||||
} else {
|
||||
upsert = contact
|
||||
data.contactUpserts[contact.id] = upsert
|
||||
}
|
||||
|
||||
if(data.contactUpdates[contact.id]) {
|
||||
upsert = Object.assign(data.contactUpdates[contact.id], trimUndefineds(contact))
|
||||
upsert = Object.assign(data.contactUpdates[contact.id], trimUndefined(contact))
|
||||
delete data.contactUpdates[contact.id]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ export const isWABusinessPlatform = (platform: string) => {
|
||||
return platform === 'smbi' || platform === 'smba'
|
||||
}
|
||||
|
||||
export function trimUndefineds(obj: any) {
|
||||
export function trimUndefined(obj: any) {
|
||||
for(const key in obj) {
|
||||
if(typeof obj[key] === 'undefined') {
|
||||
delete obj[key]
|
||||
@@ -388,3 +388,27 @@ export function trimUndefineds(obj: any) {
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
const CROCKFORD_CHARACTERS = '123456789ABCDEFGHJKLMNPQRSTVWXYZ'
|
||||
|
||||
export function bytesToCrockford(buffer: Buffer): string {
|
||||
let value = 0
|
||||
let bitCount = 0
|
||||
const crockford: string[] = []
|
||||
|
||||
for(let i = 0; i < buffer.length; i++) {
|
||||
value = (value << 8) | (buffer[i] & 0xff)
|
||||
bitCount += 8
|
||||
|
||||
while(bitCount >= 5) {
|
||||
crockford.push(CROCKFORD_CHARACTERS.charAt((value >>> (bitCount - 5)) & 31))
|
||||
bitCount -= 5
|
||||
}
|
||||
}
|
||||
|
||||
if(bitCount > 0) {
|
||||
crockford.push(CROCKFORD_CHARACTERS.charAt((value << (5 - bitCount)) & 31))
|
||||
}
|
||||
|
||||
return crockford.join('')
|
||||
}
|
||||
@@ -208,8 +208,7 @@ export const encodeSignedDeviceIdentity = (
|
||||
account.accountSignatureKey = null
|
||||
}
|
||||
|
||||
const accountEnc = proto.ADVSignedDeviceIdentity
|
||||
return proto.ADVSignedDeviceIdentity
|
||||
.encode(account)
|
||||
.finish()
|
||||
return accountEnc
|
||||
}
|
||||
Reference in New Issue
Block a user