mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
mobile: deprecation.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Boom } from '@hapi/boom'
|
import { Boom } from '@hapi/boom'
|
||||||
import NodeCache from 'node-cache'
|
import NodeCache from 'node-cache'
|
||||||
import readline from 'readline'
|
import readline from 'readline'
|
||||||
import makeWASocket, { AnyMessageContent, BinaryInfo, delay, DisconnectReason, downloadAndProcessHistorySyncNotification, encodeWAM, fetchLatestBaileysVersion, getAggregateVotesInPollMessage, getHistoryMsg, isJidNewsletter, makeCacheableSignalKeyStore, makeInMemoryStore, PHONENUMBER_MCC, proto, useMultiFileAuthState, WAMessageContent, WAMessageKey } from '../src'
|
import makeWASocket, { AnyMessageContent, BinaryInfo, delay, DisconnectReason, downloadAndProcessHistorySyncNotification, encodeWAM, fetchLatestBaileysVersion, getAggregateVotesInPollMessage, getHistoryMsg, isJidNewsletter, makeCacheableSignalKeyStore, makeInMemoryStore, proto, useMultiFileAuthState, WAMessageContent, WAMessageKey } from '../src'
|
||||||
//import MAIN_LOGGER from '../src/Utils/logger'
|
//import MAIN_LOGGER from '../src/Utils/logger'
|
||||||
import open from 'open'
|
import open from 'open'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
@@ -13,7 +13,6 @@ logger.level = 'trace'
|
|||||||
const useStore = !process.argv.includes('--no-store')
|
const useStore = !process.argv.includes('--no-store')
|
||||||
const doReplies = process.argv.includes('--do-reply')
|
const doReplies = process.argv.includes('--do-reply')
|
||||||
const usePairingCode = process.argv.includes('--use-pairing-code')
|
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
|
// 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
|
// keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts
|
||||||
@@ -45,7 +44,6 @@ const startSock = async() => {
|
|||||||
version,
|
version,
|
||||||
logger,
|
logger,
|
||||||
printQRInTerminal: !usePairingCode,
|
printQRInTerminal: !usePairingCode,
|
||||||
mobile: useMobile,
|
|
||||||
auth: {
|
auth: {
|
||||||
creds: state.creds,
|
creds: state.creds,
|
||||||
/** caching makes the store faster to send/recv messages */
|
/** caching makes the store faster to send/recv messages */
|
||||||
@@ -63,93 +61,13 @@ const startSock = async() => {
|
|||||||
store?.bind(sock.ev)
|
store?.bind(sock.ev)
|
||||||
|
|
||||||
// Pairing code for Web clients
|
// Pairing code for Web clients
|
||||||
if(usePairingCode && !sock.authState.creds.registered) {
|
if (usePairingCode && !sock.authState.creds.registered) {
|
||||||
if(useMobile) {
|
// todo move to QR event
|
||||||
throw new Error('Cannot use pairing code with mobile api')
|
const phoneNumber = await question('Please enter your phone number:\n')
|
||||||
}
|
|
||||||
|
|
||||||
const phoneNumber = await question('Please enter your mobile phone number:\n')
|
|
||||||
const code = await sock.requestPairingCode(phoneNumber)
|
const code = await sock.requestPairingCode(phoneNumber)
|
||||||
console.log(`Pairing code: ${code}`)
|
console.log(`Pairing code: ${code}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If mobile was chosen, ask for the code
|
|
||||||
if(useMobile && !sock.authState.creds.registered) {
|
|
||||||
const { registration } = sock.authState.creds || { registration: {} }
|
|
||||||
|
|
||||||
if(!registration.phoneNumber) {
|
|
||||||
registration.phoneNumber = await question('Please enter your mobile phone number:\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
const libPhonenumber = await import("libphonenumber-js")
|
|
||||||
const phoneNumber = libPhonenumber.parsePhoneNumber(registration!.phoneNumber)
|
|
||||||
if(!phoneNumber?.isValid()) {
|
|
||||||
throw new Error('Invalid phone number: ' + registration!.phoneNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
registration.phoneNumber = phoneNumber.format('E.164')
|
|
||||||
registration.phoneNumberCountryCode = phoneNumber.countryCallingCode
|
|
||||||
registration.phoneNumberNationalNumber = phoneNumber.nationalNumber
|
|
||||||
const mcc = PHONENUMBER_MCC[phoneNumber.countryCallingCode]
|
|
||||||
if(!mcc) {
|
|
||||||
throw new Error('Could not find MCC for phone number: ' + registration!.phoneNumber + '\nPlease specify the MCC manually.')
|
|
||||||
}
|
|
||||||
|
|
||||||
registration.phoneNumberMobileCountryCode = mcc
|
|
||||||
|
|
||||||
async function enterCode() {
|
|
||||||
try {
|
|
||||||
const code = await question('Please enter the one time code:\n')
|
|
||||||
const response = await sock.register(code.replace(/["']/g, '').trim().toLowerCase())
|
|
||||||
console.log('Successfully registered your phone number.')
|
|
||||||
console.log(response)
|
|
||||||
rl.close()
|
|
||||||
} catch(error) {
|
|
||||||
console.error('Failed to register your phone number. Please try again.\n', error)
|
|
||||||
await askForOTP()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function enterCaptcha() {
|
|
||||||
const response = await sock.requestRegistrationCode({ ...registration, method: 'captcha' })
|
|
||||||
const path = __dirname + '/captcha.png'
|
|
||||||
fs.writeFileSync(path, Buffer.from(response.image_blob!, 'base64'))
|
|
||||||
|
|
||||||
open(path)
|
|
||||||
const code = await question('Please enter the captcha code:\n')
|
|
||||||
fs.unlinkSync(path)
|
|
||||||
registration.captcha = code.replace(/["']/g, '').trim().toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function askForOTP() {
|
|
||||||
if (!registration.method) {
|
|
||||||
await delay(2000)
|
|
||||||
let code = await question('How would you like to receive the one time code for registration? "sms" or "voice"\n')
|
|
||||||
code = code.replace(/["']/g, '').trim().toLowerCase()
|
|
||||||
if(code !== 'sms' && code !== 'voice') {
|
|
||||||
return await askForOTP()
|
|
||||||
}
|
|
||||||
|
|
||||||
registration.method = code
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await sock.requestRegistrationCode(registration)
|
|
||||||
await enterCode()
|
|
||||||
} catch(error) {
|
|
||||||
console.error('Failed to request registration code. Please try again.\n', error)
|
|
||||||
|
|
||||||
if(error?.reason === 'code_checkpoint') {
|
|
||||||
await enterCaptcha()
|
|
||||||
}
|
|
||||||
|
|
||||||
await askForOTP()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
askForOTP()
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => {
|
const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => {
|
||||||
await sock.presenceSubscribe(jid)
|
await sock.presenceSubscribe(jid)
|
||||||
await delay(500)
|
await delay(500)
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -89,16 +89,6 @@ 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!
|
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!
|
||||||
|
|
||||||
**Note:** install `qrcode-terminal` using `yarn add qrcode-terminal` to auto-print the QR to the terminal.
|
|
||||||
|
|
||||||
**Note:** the code to support the legacy version of WA Web (pre multi-device) has been removed in v5. Only the standard multi-device connection is now supported. This is done as WA seems to have completely dropped support for the legacy version.
|
|
||||||
|
|
||||||
## Connecting native mobile api
|
|
||||||
|
|
||||||
Baileys also supports the native mobile API, which allows users to authenticate as a standalone WhatsApp client using their phone number.
|
|
||||||
|
|
||||||
Run the [example](Example/example.ts) file with ``--mobile`` cli flag to use the native mobile API.
|
|
||||||
|
|
||||||
## Configuring the Connection
|
## Configuring the Connection
|
||||||
|
|
||||||
You can configure the connection by passing a `SocketConfig` object.
|
You can configure the connection by passing a `SocketConfig` object.
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
"changelog:preview": "conventional-changelog -p angular -u",
|
"changelog:preview": "conventional-changelog -p angular -u",
|
||||||
"changelog:update": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
"changelog:update": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
||||||
"example": "node --inspect -r ts-node/register Example/example.ts",
|
"example": "node --inspect -r ts-node/register Example/example.ts",
|
||||||
"example:mobile": "node --inspect -r ts-node/register Example/example.ts --mobile",
|
|
||||||
"gen:protobuf": "sh WAProto/GenerateStatics.sh",
|
"gen:protobuf": "sh WAProto/GenerateStatics.sh",
|
||||||
"lint": "eslint src --ext .js,.ts,.jsx,.tsx",
|
"lint": "eslint src --ext .js,.ts,.jsx,.tsx",
|
||||||
"lint:fix": "eslint src --fix --ext .js,.ts,.jsx,.tsx",
|
"lint:fix": "eslint src --fix --ext .js,.ts,.jsx,.tsx",
|
||||||
|
|||||||
@@ -1,35 +1,19 @@
|
|||||||
import { createHash } from 'crypto'
|
|
||||||
import { proto } from '../../WAProto'
|
import { proto } from '../../WAProto'
|
||||||
import { makeLibSignalRepository } from '../Signal/libsignal'
|
import { makeLibSignalRepository } from '../Signal/libsignal'
|
||||||
import type { AuthenticationState, MediaType, SocketConfig, WAVersion } from '../Types'
|
import type { AuthenticationState, MediaType, SocketConfig, WAVersion } from '../Types'
|
||||||
import { Browsers } from '../Utils'
|
import { Browsers } from '../Utils'
|
||||||
import logger from '../Utils/logger'
|
import logger from '../Utils/logger'
|
||||||
import { version } from './baileys-version.json'
|
import { version } from './baileys-version.json'
|
||||||
import phoneNumberMCC from './phonenumber-mcc.json'
|
|
||||||
|
|
||||||
export const UNAUTHORIZED_CODES = [401, 403, 419]
|
export const UNAUTHORIZED_CODES = [401, 403, 419]
|
||||||
|
|
||||||
export const PHONENUMBER_MCC = phoneNumberMCC
|
|
||||||
|
|
||||||
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com'
|
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com'
|
||||||
export const MOBILE_ENDPOINT = 'g.whatsapp.net'
|
|
||||||
export const MOBILE_PORT = 443
|
|
||||||
export const DEF_CALLBACK_PREFIX = 'CB:'
|
export const DEF_CALLBACK_PREFIX = 'CB:'
|
||||||
export const DEF_TAG_PREFIX = 'TAG:'
|
export const DEF_TAG_PREFIX = 'TAG:'
|
||||||
export const PHONE_CONNECTION_CB = 'CB:Pong'
|
export const PHONE_CONNECTION_CB = 'CB:Pong'
|
||||||
|
|
||||||
export const WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60
|
export const WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60
|
||||||
|
|
||||||
const WA_VERSION = '2.24.6.77'
|
|
||||||
|
|
||||||
const WA_VERSION_HASH = createHash('md5').update(WA_VERSION).digest('hex')
|
|
||||||
export const MOBILE_TOKEN = Buffer.from('0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM' + WA_VERSION_HASH)
|
|
||||||
export const MOBILE_REGISTRATION_ENDPOINT = 'https://v.whatsapp.net/v2'
|
|
||||||
export const MOBILE_USERAGENT = `WhatsApp/${WA_VERSION} iOS/15.3.1 Device/Apple-iPhone_7`
|
|
||||||
export const REGISTRATION_PUBLIC_KEY = Buffer.from([
|
|
||||||
5, 142, 140, 15, 116, 195, 235, 197, 215, 166, 134, 92, 108, 60, 132, 56, 86, 176, 97, 33, 204, 232, 234, 119, 77,
|
|
||||||
34, 251, 111, 18, 37, 18, 48, 45,
|
|
||||||
])
|
|
||||||
export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0'
|
export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0'
|
||||||
export const DICT_VERSION = 2
|
export const DICT_VERSION = 2
|
||||||
export const KEY_BUNDLE_TYPE = Buffer.from([5])
|
export const KEY_BUNDLE_TYPE = Buffer.from([5])
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './abstract-socket-client'
|
export * from './types'
|
||||||
export * from './mobile-socket-client'
|
export * from './websocket'
|
||||||
export * from './web-socket-client'
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { connect, Socket } from 'net'
|
|
||||||
import { AbstractSocketClient } from './abstract-socket-client'
|
|
||||||
|
|
||||||
export class MobileSocketClient extends AbstractSocketClient {
|
|
||||||
protected socket: Socket | null = null
|
|
||||||
|
|
||||||
get isOpen(): boolean {
|
|
||||||
return this.socket?.readyState === 'open'
|
|
||||||
}
|
|
||||||
get isClosed(): boolean {
|
|
||||||
return this.socket === null || this.socket?.readyState === 'closed'
|
|
||||||
}
|
|
||||||
get isClosing(): boolean {
|
|
||||||
return this.socket === null || this.socket?.readyState === 'closed'
|
|
||||||
}
|
|
||||||
get isConnecting(): boolean {
|
|
||||||
return this.socket?.readyState === 'opening'
|
|
||||||
}
|
|
||||||
|
|
||||||
async connect(): Promise<void> {
|
|
||||||
if(this.socket) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.config.agent) {
|
|
||||||
|
|
||||||
throw new Error('There are not support for proxy agent for mobile connection')
|
|
||||||
} else {
|
|
||||||
this.socket = connect({
|
|
||||||
host: this.url.hostname,
|
|
||||||
port: Number(this.url.port) || 443
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.setMaxListeners(0)
|
|
||||||
|
|
||||||
const events = ['close', 'connect', 'data', 'drain', 'end', 'error', 'lookup', 'ready', 'timeout']
|
|
||||||
|
|
||||||
for(const event of events) {
|
|
||||||
this.socket?.on(event, (...args: any[]) => this.emit(event, ...args))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.on('data', (...args: any[]) => this.emit('message', ...args))
|
|
||||||
this.socket.on('ready', (...args: any[]) => this.emit('open', ...args))
|
|
||||||
}
|
|
||||||
|
|
||||||
async close(): Promise<void> {
|
|
||||||
if(!this.socket) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise<void>(resolve => {
|
|
||||||
this.socket!.end(resolve)
|
|
||||||
this.socket = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
send(str: string | Uint8Array, cb?: (err?: Error) => void): boolean {
|
|
||||||
if(this.socket === null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.socket.write(str, undefined, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import WebSocket from 'ws'
|
import WebSocket from 'ws'
|
||||||
import { DEFAULT_ORIGIN } from '../../Defaults'
|
import { DEFAULT_ORIGIN } from '../../Defaults'
|
||||||
import { AbstractSocketClient } from './abstract-socket-client'
|
import { AbstractSocketClient } from './types'
|
||||||
|
|
||||||
export class WebSocketClient extends AbstractSocketClient {
|
export class WebSocketClient extends AbstractSocketClient {
|
||||||
|
|
||||||
@@ -3,12 +3,12 @@ import NodeCache from 'node-cache'
|
|||||||
import { proto } from '../../WAProto'
|
import { proto } from '../../WAProto'
|
||||||
import { DEFAULT_CACHE_TTLS, PROCESSABLE_HISTORY_TYPES } from '../Defaults'
|
import { DEFAULT_CACHE_TTLS, PROCESSABLE_HISTORY_TYPES } from '../Defaults'
|
||||||
import { ALL_WA_PATCH_NAMES, ChatModification, ChatMutation, LTHashState, MessageUpsertType, PresenceData, SocketConfig, WABusinessHoursConfig, WABusinessProfile, WAMediaUpload, WAMessage, WAPatchCreate, WAPatchName, WAPresence, WAPrivacyCallValue, WAPrivacyGroupAddValue, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '../Types'
|
import { ALL_WA_PATCH_NAMES, ChatModification, ChatMutation, LTHashState, MessageUpsertType, PresenceData, SocketConfig, WABusinessHoursConfig, WABusinessProfile, WAMediaUpload, WAMessage, WAPatchCreate, WAPatchName, WAPresence, WAPrivacyCallValue, WAPrivacyGroupAddValue, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '../Types'
|
||||||
|
import { LabelActionBody } from '../Types/Label'
|
||||||
import { chatModificationToAppPatch, ChatMutationMap, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, extractSyncdPatches, generateProfilePicture, getHistoryMsg, newLTHashState, processSyncAction } from '../Utils'
|
import { chatModificationToAppPatch, ChatMutationMap, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, extractSyncdPatches, generateProfilePicture, getHistoryMsg, newLTHashState, processSyncAction } from '../Utils'
|
||||||
import { makeMutex } from '../Utils/make-mutex'
|
import { makeMutex } from '../Utils/make-mutex'
|
||||||
import processMessage from '../Utils/process-message'
|
import processMessage from '../Utils/process-message'
|
||||||
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary'
|
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary'
|
||||||
import { makeSocket } from './socket'
|
import { makeSocket } from './socket'
|
||||||
import { Label, LabelActionBody } from '../Types/Label'
|
|
||||||
|
|
||||||
const MAX_SYNC_ATTEMPTS = 2
|
const MAX_SYNC_ATTEMPTS = 2
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
/** update the profile picture for yourself or a group */
|
/** update the profile picture for yourself or a group */
|
||||||
const updateProfilePicture = async(jid: string, content: WAMediaUpload) => {
|
const updateProfilePicture = async(jid: string, content: WAMediaUpload) => {
|
||||||
let targetJid;
|
let targetJid
|
||||||
if(!jid) {
|
if(!jid) {
|
||||||
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update')
|
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update')
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
/** remove the profile picture for yourself or a group */
|
/** remove the profile picture for yourself or a group */
|
||||||
const removeProfilePicture = async(jid: string) => {
|
const removeProfilePicture = async(jid: string) => {
|
||||||
let targetJid;
|
let targetJid
|
||||||
if(!jid) {
|
if(!jid) {
|
||||||
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update')
|
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update')
|
||||||
}
|
}
|
||||||
@@ -777,6 +777,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
authState.creds.lastPropHash = propsNode?.attrs?.hash
|
authState.creds.lastPropHash = propsNode?.attrs?.hash
|
||||||
ev.emit('creds.update', authState.creds)
|
ev.emit('creds.update', authState.creds)
|
||||||
}
|
}
|
||||||
|
|
||||||
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,7 +1003,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
// if we don't have the app state key
|
// if we don't have the app state key
|
||||||
// we keep buffering events until we finally have
|
// we keep buffering events until we finally have
|
||||||
// the key and can sync the messages
|
// the key and can sync the messages
|
||||||
if(!authState.creds?.myAppStateKeyId && !config.mobile) {
|
// todo scrutinize
|
||||||
|
if(!authState.creds?.myAppStateKeyId) {
|
||||||
ev.buffer()
|
ev.buffer()
|
||||||
needToFlushWithAppStateSync = true
|
needToFlushWithAppStateSync = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults'
|
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults'
|
||||||
import { UserFacingSocketConfig } from '../Types'
|
import { UserFacingSocketConfig } from '../Types'
|
||||||
import { makeRegistrationSocket as _makeSocket } from './registration'
|
import { makeBusinessSocket } from './business'
|
||||||
|
|
||||||
// export the last socket layer
|
// export the last socket layer
|
||||||
const makeWASocket = (config: UserFacingSocketConfig) => (
|
const makeWASocket = (config: UserFacingSocketConfig) => (
|
||||||
_makeSocket({
|
makeBusinessSocket({
|
||||||
...DEFAULT_CONNECTION_CONFIG,
|
...DEFAULT_CONNECTION_CONFIG,
|
||||||
...config
|
...config
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -469,17 +469,15 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
|
||||||
} else {
|
} else {
|
||||||
const { user: meUser, device: meDevice } = jidDecode(meId)!
|
const { user: meUser } = jidDecode(meId)!
|
||||||
|
|
||||||
if(!participant) {
|
if(!participant) {
|
||||||
devices.push({ user })
|
devices.push({ user })
|
||||||
// do not send message to self if the device is 0 (mobile)
|
if(user !== meUser) {
|
||||||
|
devices.push({ user: meUser })
|
||||||
if(!(additionalAttributes?.['category'] === 'peer' && user === meUser)) {
|
}
|
||||||
if(meDevice !== undefined && meDevice !== 0) {
|
|
||||||
devices.push({ user: meUser })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(additionalAttributes?.['category'] !== 'peer') {
|
||||||
const additionalDevices = await getUSyncDevices([ meId, jid ], !!useUserDevicesCache, true)
|
const additionalDevices = await getUSyncDevices([ meId, jid ], !!useUserDevicesCache, true)
|
||||||
devices.push(...additionalDevices)
|
devices.push(...additionalDevices)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
/* eslint-disable camelcase */
|
|
||||||
import axios, { AxiosRequestConfig } from 'axios'
|
|
||||||
import { MOBILE_REGISTRATION_ENDPOINT, MOBILE_TOKEN, MOBILE_USERAGENT, REGISTRATION_PUBLIC_KEY } from '../Defaults'
|
|
||||||
import { KeyPair, SignedKeyPair, SocketConfig } from '../Types'
|
|
||||||
import { aesEncryptGCM, Curve, md5 } from '../Utils/crypto'
|
|
||||||
import { jidEncode } from '../WABinary'
|
|
||||||
import { makeBusinessSocket } from './business'
|
|
||||||
|
|
||||||
function urlencode(str: string) {
|
|
||||||
return str.replace(/-/g, '%2d').replace(/_/g, '%5f').replace(/~/g, '%7e')
|
|
||||||
}
|
|
||||||
|
|
||||||
const validRegistrationOptions = (config: RegistrationOptions) => config?.phoneNumberCountryCode &&
|
|
||||||
config.phoneNumberNationalNumber &&
|
|
||||||
config.phoneNumberMobileCountryCode
|
|
||||||
|
|
||||||
export const makeRegistrationSocket = (config: SocketConfig) => {
|
|
||||||
const sock = makeBusinessSocket(config)
|
|
||||||
|
|
||||||
const register = async(code: string) => {
|
|
||||||
if(!validRegistrationOptions(config.auth.creds.registration)) {
|
|
||||||
throw new Error('please specify the registration options')
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await mobileRegister({ ...sock.authState.creds, ...sock.authState.creds.registration as RegistrationOptions, code }, config.options)
|
|
||||||
|
|
||||||
sock.authState.creds.me = {
|
|
||||||
id: jidEncode(result.login!, 's.whatsapp.net'),
|
|
||||||
name: '~'
|
|
||||||
}
|
|
||||||
|
|
||||||
sock.authState.creds.registered = true
|
|
||||||
sock.ev.emit('creds.update', sock.authState.creds)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestRegistrationCode = async(registrationOptions?: RegistrationOptions) => {
|
|
||||||
registrationOptions = registrationOptions || config.auth.creds.registration
|
|
||||||
if(!validRegistrationOptions(registrationOptions)) {
|
|
||||||
throw new Error('Invalid registration options')
|
|
||||||
}
|
|
||||||
|
|
||||||
sock.authState.creds.registration = registrationOptions
|
|
||||||
|
|
||||||
sock.ev.emit('creds.update', sock.authState.creds)
|
|
||||||
|
|
||||||
return mobileRegisterCode({ ...config.auth.creds, ...registrationOptions }, config.options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...sock,
|
|
||||||
register,
|
|
||||||
requestRegistrationCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backup_token: Base64.getEncoder().encodeToString(Arrays.copyOfRange(Base64.getDecoder().decode(UUID.randomUUID().toString().replace('-','')),0,15))
|
|
||||||
|
|
||||||
export interface RegistrationData {
|
|
||||||
registrationId: number
|
|
||||||
signedPreKey: SignedKeyPair
|
|
||||||
noiseKey: KeyPair
|
|
||||||
signedIdentityKey: KeyPair
|
|
||||||
identityId: Buffer
|
|
||||||
phoneId: string
|
|
||||||
deviceId: string
|
|
||||||
backupToken: Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RegistrationOptions {
|
|
||||||
/** your phone number */
|
|
||||||
phoneNumber?: string
|
|
||||||
/** the country code of your phone number */
|
|
||||||
phoneNumberCountryCode: string
|
|
||||||
/** your phone number without country code */
|
|
||||||
phoneNumberNationalNumber: string
|
|
||||||
/** the country code of your mobile network
|
|
||||||
* @see {@link https://de.wikipedia.org/wiki/Mobile_Country_Code}
|
|
||||||
*/
|
|
||||||
phoneNumberMobileCountryCode: string
|
|
||||||
/** the network code of your mobile network
|
|
||||||
* @see {@link https://de.wikipedia.org/wiki/Mobile_Network_Code}
|
|
||||||
*/
|
|
||||||
phoneNumberMobileNetworkCode: string
|
|
||||||
/**
|
|
||||||
* How to send the one time code
|
|
||||||
*/
|
|
||||||
method?: 'sms' | 'voice' | 'captcha'
|
|
||||||
/**
|
|
||||||
* The captcha code if it was requested
|
|
||||||
*/
|
|
||||||
captcha?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RegistrationParams = RegistrationData & RegistrationOptions
|
|
||||||
|
|
||||||
function convertBufferToUrlHex(buffer: Buffer) {
|
|
||||||
var id = ''
|
|
||||||
|
|
||||||
buffer.forEach((x) => {
|
|
||||||
// encode random identity_id buffer as percentage url encoding
|
|
||||||
id += `%${x.toString(16).padStart(2, '0').toLowerCase()}`
|
|
||||||
})
|
|
||||||
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registrationParams(params: RegistrationParams) {
|
|
||||||
const e_regid = Buffer.alloc(4)
|
|
||||||
e_regid.writeInt32BE(params.registrationId)
|
|
||||||
|
|
||||||
const e_skey_id = Buffer.alloc(3)
|
|
||||||
e_skey_id.writeInt16BE(params.signedPreKey.keyId)
|
|
||||||
|
|
||||||
params.phoneNumberCountryCode = params.phoneNumberCountryCode.replace('+', '').trim()
|
|
||||||
params.phoneNumberNationalNumber = params.phoneNumberNationalNumber.replace(/[/-\s)(]/g, '').trim()
|
|
||||||
|
|
||||||
return {
|
|
||||||
cc: params.phoneNumberCountryCode,
|
|
||||||
in: params.phoneNumberNationalNumber,
|
|
||||||
Rc: '0',
|
|
||||||
lg: 'en',
|
|
||||||
lc: 'GB',
|
|
||||||
mistyped: '6',
|
|
||||||
authkey: Buffer.from(params.noiseKey.public).toString('base64url'),
|
|
||||||
e_regid: e_regid.toString('base64url'),
|
|
||||||
e_keytype: 'BQ',
|
|
||||||
e_ident: Buffer.from(params.signedIdentityKey.public).toString('base64url'),
|
|
||||||
// e_skey_id: e_skey_id.toString('base64url'),
|
|
||||||
e_skey_id: 'AAAA',
|
|
||||||
e_skey_val: Buffer.from(params.signedPreKey.keyPair.public).toString('base64url'),
|
|
||||||
e_skey_sig: Buffer.from(params.signedPreKey.signature).toString('base64url'),
|
|
||||||
fdid: params.phoneId,
|
|
||||||
network_ratio_type: '1',
|
|
||||||
expid: params.deviceId,
|
|
||||||
simnum: '1',
|
|
||||||
hasinrc: '1',
|
|
||||||
pid: Math.floor(Math.random() * 1000).toString(),
|
|
||||||
id: convertBufferToUrlHex(params.identityId),
|
|
||||||
backup_token: convertBufferToUrlHex(params.backupToken),
|
|
||||||
token: md5(Buffer.concat([MOBILE_TOKEN, Buffer.from(params.phoneNumberNationalNumber)])).toString('hex'),
|
|
||||||
fraud_checkpoint_code: params.captcha,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests a registration code for the given phone number.
|
|
||||||
*/
|
|
||||||
export function mobileRegisterCode(params: RegistrationParams, fetchOptions?: AxiosRequestConfig) {
|
|
||||||
return mobileRegisterFetch('/code', {
|
|
||||||
params: {
|
|
||||||
...registrationParams(params),
|
|
||||||
mcc: `${params.phoneNumberMobileCountryCode}`.padStart(3, '0'),
|
|
||||||
mnc: `${params.phoneNumberMobileNetworkCode || '001'}`.padStart(3, '0'),
|
|
||||||
sim_mcc: '000',
|
|
||||||
sim_mnc: '000',
|
|
||||||
method: params?.method || 'sms',
|
|
||||||
reason: '',
|
|
||||||
hasav: '1'
|
|
||||||
},
|
|
||||||
...fetchOptions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mobileRegisterExists(params: RegistrationParams, fetchOptions?: AxiosRequestConfig) {
|
|
||||||
return mobileRegisterFetch('/exist', {
|
|
||||||
params: registrationParams(params),
|
|
||||||
...fetchOptions
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the phone number on whatsapp with the received OTP code.
|
|
||||||
*/
|
|
||||||
export async function mobileRegister(params: RegistrationParams & { code: string }, fetchOptions?: AxiosRequestConfig) {
|
|
||||||
//const result = await mobileRegisterFetch(`/reg_onboard_abprop?cc=${params.phoneNumberCountryCode}&in=${params.phoneNumberNationalNumber}&rc=0`)
|
|
||||||
|
|
||||||
return mobileRegisterFetch('/register', {
|
|
||||||
params: { ...registrationParams(params), code: params.code.replace('-', '') },
|
|
||||||
...fetchOptions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts the given string as AEAD aes-256-gcm with the public whatsapp key and a random keypair.
|
|
||||||
*/
|
|
||||||
export function mobileRegisterEncrypt(data: string) {
|
|
||||||
const keypair = Curve.generateKeyPair()
|
|
||||||
const key = Curve.sharedKey(keypair.private, REGISTRATION_PUBLIC_KEY)
|
|
||||||
|
|
||||||
const buffer = aesEncryptGCM(Buffer.from(data), new Uint8Array(key), Buffer.alloc(12), Buffer.alloc(0))
|
|
||||||
|
|
||||||
return Buffer.concat([Buffer.from(keypair.public), buffer]).toString('base64url')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function mobileRegisterFetch(path: string, opts: AxiosRequestConfig = {}) {
|
|
||||||
let url = `${MOBILE_REGISTRATION_ENDPOINT}${path}`
|
|
||||||
|
|
||||||
if(opts.params) {
|
|
||||||
const parameter = [] as string[]
|
|
||||||
|
|
||||||
for(const param in opts.params) {
|
|
||||||
if(opts.params[param] !== null && opts.params[param] !== undefined) {
|
|
||||||
parameter.push(param + '=' + urlencode(opts.params[param]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
url += `?${parameter.join('&')}`
|
|
||||||
delete opts.params
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!opts.headers) {
|
|
||||||
opts.headers = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts.headers['User-Agent'] = MOBILE_USERAGENT
|
|
||||||
|
|
||||||
const response = await axios(url, opts)
|
|
||||||
|
|
||||||
var json = response.data
|
|
||||||
|
|
||||||
if(response.status > 300 || json.reason) {
|
|
||||||
throw json
|
|
||||||
}
|
|
||||||
|
|
||||||
if(json.status && !['ok', 'sent'].includes(json.status)) {
|
|
||||||
throw json
|
|
||||||
}
|
|
||||||
|
|
||||||
return json as ExistsResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface ExistsResponse {
|
|
||||||
status: 'fail' | 'sent'
|
|
||||||
voice_length?: number
|
|
||||||
voice_wait?: number
|
|
||||||
sms_length?: number
|
|
||||||
sms_wait?: number
|
|
||||||
reason?: 'incorrect' | 'missing_param' | 'code_checkpoint'
|
|
||||||
login?: string
|
|
||||||
flash_type?: number
|
|
||||||
ab_hash?: string
|
|
||||||
ab_key?: string
|
|
||||||
exp_cfg?: string
|
|
||||||
lid?: string
|
|
||||||
image_blob?: string
|
|
||||||
audio_blob?: string
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,6 @@ import {
|
|||||||
DEF_TAG_PREFIX,
|
DEF_TAG_PREFIX,
|
||||||
INITIAL_PREKEY_COUNT,
|
INITIAL_PREKEY_COUNT,
|
||||||
MIN_PREKEY_COUNT,
|
MIN_PREKEY_COUNT,
|
||||||
MOBILE_ENDPOINT,
|
|
||||||
MOBILE_NOISE_HEADER,
|
|
||||||
MOBILE_PORT,
|
|
||||||
NOISE_WA_HEADER
|
NOISE_WA_HEADER
|
||||||
} from '../Defaults'
|
} from '../Defaults'
|
||||||
import { DisconnectReason, SocketConfig } from '../Types'
|
import { DisconnectReason, SocketConfig } from '../Types'
|
||||||
@@ -24,7 +21,6 @@ import {
|
|||||||
derivePairingCodeKey,
|
derivePairingCodeKey,
|
||||||
generateLoginNode,
|
generateLoginNode,
|
||||||
generateMdTagPrefix,
|
generateMdTagPrefix,
|
||||||
generateMobileNode,
|
|
||||||
generateRegistrationNode,
|
generateRegistrationNode,
|
||||||
getCodeFromWSError,
|
getCodeFromWSError,
|
||||||
getErrorCodeFromStreamError,
|
getErrorCodeFromStreamError,
|
||||||
@@ -45,7 +41,7 @@ import {
|
|||||||
jidEncode,
|
jidEncode,
|
||||||
S_WHATSAPP_NET
|
S_WHATSAPP_NET
|
||||||
} from '../WABinary'
|
} from '../WABinary'
|
||||||
import { MobileSocketClient, WebSocketClient } from './Client'
|
import { WebSocketClient } from './Client'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to WA servers and performs:
|
* Connects to WA servers and performs:
|
||||||
@@ -69,19 +65,18 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
makeSignalRepository,
|
makeSignalRepository,
|
||||||
} = config
|
} = config
|
||||||
|
|
||||||
let url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
||||||
|
|
||||||
config.mobile = config.mobile || url.protocol === 'tcp:'
|
|
||||||
|
|
||||||
if(config.mobile && url.protocol !== 'tcp:') {
|
if(config.mobile || url.protocol === 'tcp:') {
|
||||||
url = new URL(`tcp://${MOBILE_ENDPOINT}:${MOBILE_PORT}`)
|
throw new Boom('Mobile API is not supported anymore', { statusCode: DisconnectReason.loggedOut })
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!config.mobile && url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
if(url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
||||||
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const ws = config.socket ? config.socket : config.mobile ? new MobileSocketClient(url, config) : new WebSocketClient(url, config)
|
const ws = config.socket ? config.socket : new WebSocketClient(url, config)
|
||||||
|
|
||||||
ws.connect()
|
ws.connect()
|
||||||
|
|
||||||
@@ -91,8 +86,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
/** WA noise protocol wrapper */
|
/** WA noise protocol wrapper */
|
||||||
const noise = makeNoiseHandler({
|
const noise = makeNoiseHandler({
|
||||||
keyPair: ephemeralKeyPair,
|
keyPair: ephemeralKeyPair,
|
||||||
NOISE_HEADER: config.mobile ? MOBILE_NOISE_HEADER : NOISE_WA_HEADER,
|
NOISE_HEADER: NOISE_WA_HEADER,
|
||||||
mobile: config.mobile,
|
|
||||||
logger,
|
logger,
|
||||||
routingInfo: authState?.creds?.routingInfo
|
routingInfo: authState?.creds?.routingInfo
|
||||||
})
|
})
|
||||||
@@ -247,9 +241,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
const keyEnc = noise.processHandshake(handshake, creds.noiseKey)
|
const keyEnc = noise.processHandshake(handshake, creds.noiseKey)
|
||||||
|
|
||||||
let node: proto.IClientPayload
|
let node: proto.IClientPayload
|
||||||
if(config.mobile) {
|
if(!creds.me) {
|
||||||
node = generateMobileNode(config)
|
|
||||||
} else if(!creds.me) {
|
|
||||||
node = generateRegistrationNode(creds, config)
|
node = generateRegistrationNode(creds, config)
|
||||||
logger.info({ node }, 'not logged in, attempting registration...')
|
logger.info({ node }, 'not logged in, attempting registration...')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { proto } from '../../WAProto'
|
import type { proto } from '../../WAProto'
|
||||||
import { RegistrationOptions } from '../Socket/registration'
|
|
||||||
import type { Contact } from './Contact'
|
import type { Contact } from './Contact'
|
||||||
import type { MinimalMessage } from './Message'
|
import type { MinimalMessage } from './Message'
|
||||||
|
|
||||||
@@ -60,13 +59,7 @@ export type AuthenticationCreds = SignalCreds & {
|
|||||||
/** number of times history & app state has been synced */
|
/** number of times history & app state has been synced */
|
||||||
accountSyncCounter: number
|
accountSyncCounter: number
|
||||||
accountSettings: AccountSettings
|
accountSettings: AccountSettings
|
||||||
// mobile creds
|
registered: boolean
|
||||||
deviceId: string
|
|
||||||
phoneId: string
|
|
||||||
identityId: Buffer
|
|
||||||
registered: boolean
|
|
||||||
backupToken: Buffer
|
|
||||||
registration: RegistrationOptions
|
|
||||||
pairingCode: string | undefined
|
pairingCode: string | undefined
|
||||||
lastPropHash: string | undefined
|
lastPropHash: string | undefined
|
||||||
routingInfo: Buffer | undefined
|
routingInfo: Buffer | undefined
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ export type SocketConfig = {
|
|||||||
defaultQueryTimeoutMs: number | undefined
|
defaultQueryTimeoutMs: number | undefined
|
||||||
/** ping-pong interval for WS connection */
|
/** ping-pong interval for WS connection */
|
||||||
keepAliveIntervalMs: number
|
keepAliveIntervalMs: number
|
||||||
/** should baileys use the mobile api instead of the multi device api */
|
/** should baileys use the mobile api instead of the multi device api
|
||||||
|
* @deprecated This feature has been removed
|
||||||
|
*/
|
||||||
mobile?: boolean
|
mobile?: boolean
|
||||||
/** proxy agent */
|
/** proxy agent */
|
||||||
agent?: Agent
|
agent?: Agent
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import NodeCache from 'node-cache'
|
import NodeCache from 'node-cache'
|
||||||
import type { Logger } from 'pino'
|
import type { Logger } from 'pino'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { DEFAULT_CACHE_TTLS } from '../Defaults'
|
import { DEFAULT_CACHE_TTLS } from '../Defaults'
|
||||||
import type { AuthenticationCreds, CacheStore, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction, TransactionCapabilityOptions } from '../Types'
|
import type { AuthenticationCreds, CacheStore, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction, TransactionCapabilityOptions } from '../Types'
|
||||||
import { Curve, signedKeyPair } from './crypto'
|
import { Curve, signedKeyPair } from './crypto'
|
||||||
@@ -208,13 +207,6 @@ export const initAuthCreds = (): AuthenticationCreds => {
|
|||||||
accountSettings: {
|
accountSettings: {
|
||||||
unarchiveChats: false
|
unarchiveChats: false
|
||||||
},
|
},
|
||||||
// mobile creds
|
|
||||||
deviceId: Buffer.from(uuidv4().replace(/-/g, ''), 'hex').toString('base64url'),
|
|
||||||
phoneId: uuidv4(),
|
|
||||||
identityId: randomBytes(20),
|
|
||||||
registered: false,
|
|
||||||
backupToken: randomBytes(20),
|
|
||||||
registration: {} as never,
|
|
||||||
pairingCode: undefined,
|
pairingCode: undefined,
|
||||||
lastPropHash: undefined,
|
lastPropHash: undefined,
|
||||||
routingInfo: undefined,
|
routingInfo: undefined,
|
||||||
|
|||||||
@@ -16,13 +16,11 @@ const generateIV = (counter: number) => {
|
|||||||
export const makeNoiseHandler = ({
|
export const makeNoiseHandler = ({
|
||||||
keyPair: { private: privateKey, public: publicKey },
|
keyPair: { private: privateKey, public: publicKey },
|
||||||
NOISE_HEADER,
|
NOISE_HEADER,
|
||||||
mobile,
|
|
||||||
logger,
|
logger,
|
||||||
routingInfo
|
routingInfo
|
||||||
}: {
|
}: {
|
||||||
keyPair: KeyPair
|
keyPair: KeyPair
|
||||||
NOISE_HEADER: Uint8Array
|
NOISE_HEADER: Uint8Array
|
||||||
mobile: boolean
|
|
||||||
logger: Logger
|
logger: Logger
|
||||||
routingInfo?: Buffer | undefined
|
routingInfo?: Buffer | undefined
|
||||||
}) => {
|
}) => {
|
||||||
@@ -113,16 +111,12 @@ export const makeNoiseHandler = ({
|
|||||||
|
|
||||||
const certDecoded = decrypt(serverHello!.payload!)
|
const certDecoded = decrypt(serverHello!.payload!)
|
||||||
|
|
||||||
if(mobile) {
|
const { intermediate: certIntermediate } = proto.CertChain.decode(certDecoded)
|
||||||
proto.CertChain.NoiseCertificate.decode(certDecoded)
|
|
||||||
} else {
|
|
||||||
const { intermediate: certIntermediate } = proto.CertChain.decode(certDecoded)
|
|
||||||
|
|
||||||
const { issuerSerial } = proto.CertChain.NoiseCertificate.Details.decode(certIntermediate!.details!)
|
const { issuerSerial } = proto.CertChain.NoiseCertificate.Details.decode(certIntermediate!.details!)
|
||||||
|
|
||||||
if(issuerSerial !== WA_CERT_DETAILS.SERIAL) {
|
if(issuerSerial !== WA_CERT_DETAILS.SERIAL) {
|
||||||
throw new Boom('certification match failed', { statusCode: 400 })
|
throw new Boom('certification match failed', { statusCode: 400 })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyEnc = encrypt(noiseKey.public)
|
const keyEnc = encrypt(noiseKey.public)
|
||||||
@@ -183,11 +177,11 @@ export const makeNoiseHandler = ({
|
|||||||
inBytes = inBytes.slice(size + 3)
|
inBytes = inBytes.slice(size + 3)
|
||||||
|
|
||||||
if(isFinished) {
|
if(isFinished) {
|
||||||
const result = decrypt(frame as Uint8Array)
|
const result = decrypt(frame)
|
||||||
frame = await decodeBinaryNode(result)
|
frame = await decodeBinaryNode(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace({ msg: (frame as any)?.attrs?.id }, 'recv frame')
|
logger.trace({ msg: (frame as BinaryNode)?.attrs?.id }, 'recv frame')
|
||||||
|
|
||||||
onFrame(frame)
|
onFrame(frame)
|
||||||
size = getBytesSize()
|
size = getBytesSize()
|
||||||
|
|||||||
@@ -9,30 +9,20 @@ import { encodeBigEndian } from './generics'
|
|||||||
import { createSignalIdentity } from './signal'
|
import { createSignalIdentity } from './signal'
|
||||||
|
|
||||||
const getUserAgent = (config: SocketConfig): proto.ClientPayload.IUserAgent => {
|
const getUserAgent = (config: SocketConfig): proto.ClientPayload.IUserAgent => {
|
||||||
const osVersion = config.mobile ? '15.3.1' : '0.1'
|
|
||||||
const version = config.mobile ? [2, 24, 6] : config.version
|
|
||||||
const device = config.mobile ? 'iPhone_7' : 'Desktop'
|
|
||||||
const manufacturer = config.mobile ? 'Apple' : ''
|
|
||||||
const platform = config.mobile ? proto.ClientPayload.UserAgent.Platform.IOS : proto.ClientPayload.UserAgent.Platform.WEB
|
|
||||||
const phoneId = config.mobile ? { phoneId: config.auth.creds.phoneId } : {}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appVersion: {
|
appVersion: {
|
||||||
primary: version[0],
|
primary: config.version[0],
|
||||||
secondary: version[1],
|
secondary: config.version[1],
|
||||||
tertiary: version[2],
|
tertiary: config.version[2],
|
||||||
},
|
},
|
||||||
platform,
|
platform: proto.ClientPayload.UserAgent.Platform.WEB,
|
||||||
releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
|
releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
|
||||||
mcc: config.auth.creds.registration?.phoneNumberMobileCountryCode || '000',
|
osVersion: '0.1',
|
||||||
mnc: config.auth.creds.registration?.phoneNumberMobileNetworkCode || '000',
|
device: 'Desktop',
|
||||||
osVersion: osVersion,
|
osBuildNumber: '0.1',
|
||||||
manufacturer,
|
|
||||||
device,
|
|
||||||
osBuildNumber: osVersion,
|
|
||||||
localeLanguageIso6391: 'en',
|
localeLanguageIso6391: 'en',
|
||||||
localeCountryIso31661Alpha2: 'US',
|
localeCountryIso31661Alpha2: 'US'
|
||||||
...phoneId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,34 +48,11 @@ const getClientPayload = (config: SocketConfig) => {
|
|||||||
userAgent: getUserAgent(config),
|
userAgent: getUserAgent(config),
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!config.mobile) {
|
payload.webInfo = getWebInfo(config)
|
||||||
payload.webInfo = getWebInfo(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateMobileNode = (config: SocketConfig): proto.IClientPayload => {
|
|
||||||
if(!config.auth.creds) {
|
|
||||||
throw new Boom('No registration data found', { data: config })
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload: proto.IClientPayload = {
|
|
||||||
...getClientPayload(config),
|
|
||||||
sessionId: Math.floor(Math.random() * 999999999 + 1),
|
|
||||||
shortConnect: true,
|
|
||||||
connectAttemptCount: 0,
|
|
||||||
device: 0,
|
|
||||||
dnsSource: {
|
|
||||||
appCached: false,
|
|
||||||
dnsMethod: proto.ClientPayload.DNSSource.DNSResolutionMethod.SYSTEM,
|
|
||||||
},
|
|
||||||
passive: false, // XMPP heartbeat setting (false: server actively pings) (true: client actively pings)
|
|
||||||
pushName: 'test',
|
|
||||||
username: Number(`${config.auth.creds.registration.phoneNumberCountryCode}${config.auth.creds.registration.phoneNumberNationalNumber}`),
|
|
||||||
}
|
|
||||||
return proto.ClientPayload.fromObject(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateLoginNode = (userJid: string, config: SocketConfig): proto.IClientPayload => {
|
export const generateLoginNode = (userJid: string, config: SocketConfig): proto.IClientPayload => {
|
||||||
const { user, device } = jidDecode(userJid)!
|
const { user, device } = jidDecode(userJid)!
|
||||||
|
|||||||
Reference in New Issue
Block a user