mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
fix: Re-added the option to use proxy agent forn non-mobile api (#148)
This commit is contained in:
committed by
GitHub
parent
aa9872b039
commit
54f8215eef
@@ -53,7 +53,7 @@
|
|||||||
"pino": "^7.0.0",
|
"pino": "^7.0.0",
|
||||||
"protobufjs": "^6.11.3",
|
"protobufjs": "^6.11.3",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.0.0"
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@adiwajshing/eslint-config": "https://github.com/adiwajshing/eslint-config.git",
|
"@adiwajshing/eslint-config": "https://github.com/adiwajshing/eslint-config.git",
|
||||||
|
|||||||
19
src/Socket/Client/abstract-socket-client.ts
Normal file
19
src/Socket/Client/abstract-socket-client.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { EventEmitter } from 'events'
|
||||||
|
import { URL } from 'url'
|
||||||
|
import { SocketConfig } from '../../Types'
|
||||||
|
|
||||||
|
export abstract class AbstractSocketClient extends EventEmitter {
|
||||||
|
abstract get isOpen(): boolean
|
||||||
|
abstract get isClosed(): boolean
|
||||||
|
abstract get isClosing(): boolean
|
||||||
|
abstract get isConnecting(): boolean
|
||||||
|
|
||||||
|
constructor(public url: URL, public config: SocketConfig) {
|
||||||
|
super()
|
||||||
|
this.setMaxListeners(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract connect(): Promise<void>
|
||||||
|
abstract close(): Promise<void>
|
||||||
|
abstract send(str: Uint8Array | string, cb?: (err?: Error) => void): boolean;
|
||||||
|
}
|
||||||
3
src/Socket/Client/index.ts
Normal file
3
src/Socket/Client/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './abstract-socket-client'
|
||||||
|
export * from './mobile-socket-client'
|
||||||
|
export * from './web-socket-client'
|
||||||
66
src/Socket/Client/mobile-socket-client.ts
Normal file
66
src/Socket/Client/mobile-socket-client.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
src/Socket/Client/web-socket-client.ts
Normal file
57
src/Socket/Client/web-socket-client.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import WebSocket from 'ws'
|
||||||
|
import { DEFAULT_ORIGIN } from '../../Defaults'
|
||||||
|
import { AbstractSocketClient } from './abstract-socket-client'
|
||||||
|
|
||||||
|
export class WebSocketClient extends AbstractSocketClient {
|
||||||
|
|
||||||
|
protected socket: WebSocket | null = null
|
||||||
|
|
||||||
|
get isOpen(): boolean {
|
||||||
|
return this.socket?.readyState === WebSocket.OPEN
|
||||||
|
}
|
||||||
|
get isClosed(): boolean {
|
||||||
|
return this.socket === null || this.socket?.readyState === WebSocket.CLOSED
|
||||||
|
}
|
||||||
|
get isClosing(): boolean {
|
||||||
|
return this.socket === null || this.socket?.readyState === WebSocket.CLOSING
|
||||||
|
}
|
||||||
|
get isConnecting(): boolean {
|
||||||
|
return this.socket?.readyState === WebSocket.CONNECTING
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(): Promise<void> {
|
||||||
|
if(this.socket) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket = new WebSocket(this.url, {
|
||||||
|
origin: DEFAULT_ORIGIN,
|
||||||
|
headers: this.config.options?.headers as {},
|
||||||
|
handshakeTimeout: this.config.connectTimeoutMs,
|
||||||
|
timeout: this.config.connectTimeoutMs,
|
||||||
|
agent: this.config.agent,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.socket.setMaxListeners(0)
|
||||||
|
|
||||||
|
const events = ['close', 'error', 'upgrade', 'message', 'open', 'ping', 'pong', 'unexpected-response']
|
||||||
|
|
||||||
|
for(const event of events) {
|
||||||
|
this.socket?.on(event, (...args: any[]) => this.emit(event, ...args))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
if(!this.socket) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.close()
|
||||||
|
this.socket = null
|
||||||
|
}
|
||||||
|
send(str: string | Uint8Array, cb?: (err?: Error) => void): boolean {
|
||||||
|
this.socket?.send(str, cb)
|
||||||
|
|
||||||
|
return Boolean(this.socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { Socket } from 'net'
|
|
||||||
import { MOBILE_ENDPOINT, MOBILE_PORT } from '../Defaults'
|
|
||||||
import { SocketConfig } from '../Types'
|
|
||||||
|
|
||||||
export class MobileSocket extends Socket {
|
|
||||||
constructor(public config: SocketConfig) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.on('data', (d) => {
|
|
||||||
this.emit('message', d)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override connect() {
|
|
||||||
return super.connect({
|
|
||||||
host: MOBILE_ENDPOINT,
|
|
||||||
port: MOBILE_PORT,
|
|
||||||
}, () => {
|
|
||||||
this.emit('open')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
get isOpen(): boolean {
|
|
||||||
return this.readyState === 'open'
|
|
||||||
}
|
|
||||||
|
|
||||||
get isClosed(): boolean {
|
|
||||||
return this.readyState === 'closed'
|
|
||||||
}
|
|
||||||
|
|
||||||
get isClosing(): boolean {
|
|
||||||
return this.isClosed
|
|
||||||
}
|
|
||||||
|
|
||||||
get isConnecting(): boolean {
|
|
||||||
return this.readyState === 'opening'
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
this.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
send(data: unknown, cb?: ((err?: Error | undefined) => void) | undefined) {
|
|
||||||
return super.write(data as Uint8Array | string, undefined, cb as ((err?: Error | undefined) => void))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ import { KeyPair, SignedKeyPair, SocketConfig } from '../Types'
|
|||||||
import { aesEncryptGCM, Curve, md5 } from '../Utils/crypto'
|
import { aesEncryptGCM, Curve, md5 } from '../Utils/crypto'
|
||||||
import { jidEncode } from '../WABinary'
|
import { jidEncode } from '../WABinary'
|
||||||
import { makeBusinessSocket } from './business'
|
import { makeBusinessSocket } from './business'
|
||||||
import { MobileSocket } from './mobile-socket'
|
|
||||||
|
|
||||||
function urlencode(str: string) {
|
function urlencode(str: string) {
|
||||||
return str.replace(/-/g, '%2d').replace(/_/g, '%5f').replace(/~/g, '%7e')
|
return str.replace(/-/g, '%2d').replace(/_/g, '%5f').replace(/~/g, '%7e')
|
||||||
@@ -33,10 +32,6 @@ export const makeRegistrationSocket = (config: SocketConfig) => {
|
|||||||
sock.authState.creds.registered = true
|
sock.authState.creds.registered = true
|
||||||
sock.ev.emit('creds.update', sock.authState.creds)
|
sock.ev.emit('creds.update', sock.authState.creds)
|
||||||
|
|
||||||
if(sock.ws instanceof MobileSocket) {
|
|
||||||
sock.ws.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { Boom } from '@hapi/boom'
|
import { Boom } from '@hapi/boom'
|
||||||
|
import { URL } from 'url'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { proto } from '../../WAProto'
|
import { proto } from '../../WAProto'
|
||||||
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, MOBILE_NOISE_HEADER, 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 { DisconnectReason, SocketConfig } from '../Types'
|
||||||
import { addTransactionCapability, bindWaitForConnectionUpdate, configureSuccessfulPairing, Curve, generateLoginNode, generateMdTagPrefix, generateMobileNode, generateRegistrationNode, getCodeFromWSError, getErrorCodeFromStreamError, getNextPreKeysNode, makeNoiseHandler, printQRIfNecessaryListener, promiseTimeout } from '../Utils'
|
import { addTransactionCapability, bindWaitForConnectionUpdate, configureSuccessfulPairing, Curve, generateLoginNode, generateMdTagPrefix, generateMobileNode, generateRegistrationNode, getCodeFromWSError, getErrorCodeFromStreamError, getNextPreKeysNode, makeNoiseHandler, printQRIfNecessaryListener, promiseTimeout } from '../Utils'
|
||||||
import { makeEventBuffer } from '../Utils/event-buffer'
|
import { makeEventBuffer } from '../Utils/event-buffer'
|
||||||
import { assertNodeErrorFree, BinaryNode, binaryNodeToString, encodeBinaryNode, getBinaryNodeChild, getBinaryNodeChildren, S_WHATSAPP_NET } from '../WABinary'
|
import { assertNodeErrorFree, BinaryNode, binaryNodeToString, encodeBinaryNode, getBinaryNodeChild, getBinaryNodeChildren, S_WHATSAPP_NET } from '../WABinary'
|
||||||
import { MobileSocket } from './mobile-socket'
|
import { MobileSocketClient, WebSocketClient } from './Client'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to WA servers and performs:
|
* Connects to WA servers and performs:
|
||||||
@@ -18,6 +19,7 @@ import { MobileSocket } from './mobile-socket'
|
|||||||
|
|
||||||
export const makeSocket = (config: SocketConfig) => {
|
export const makeSocket = (config: SocketConfig) => {
|
||||||
const {
|
const {
|
||||||
|
waWebSocketUrl,
|
||||||
connectTimeoutMs,
|
connectTimeoutMs,
|
||||||
logger,
|
logger,
|
||||||
keepAliveIntervalMs,
|
keepAliveIntervalMs,
|
||||||
@@ -30,15 +32,18 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
makeSignalRepository,
|
makeSignalRepository,
|
||||||
} = config
|
} = config
|
||||||
|
|
||||||
config.mobile = config.mobile || config.auth.creds.registered
|
let url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
||||||
const ws = new MobileSocket(config)
|
|
||||||
ws.setMaxListeners?.(0)
|
|
||||||
|
|
||||||
// if not mobile or already registered -> auto connect
|
config.mobile = config.mobile || config.auth?.creds?.registered || url.protocol === 'tcp:'
|
||||||
if(!config.mobile || config.auth.creds.registered) {
|
|
||||||
ws.connect()
|
if(config.mobile && url.protocol !== 'tcp:') {
|
||||||
|
url = new URL(`tcp://${MOBILE_ENDPOINT}:${MOBILE_PORT}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ws = config.mobile ? new MobileSocketClient(url, config) : new WebSocketClient(url, config)
|
||||||
|
|
||||||
|
ws.connect()
|
||||||
|
|
||||||
const ev = makeEventBuffer(logger)
|
const ev = makeEventBuffer(logger)
|
||||||
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
||||||
const ephemeralKeyPair = Curve.generateKeyPair()
|
const ephemeralKeyPair = Curve.generateKeyPair()
|
||||||
@@ -64,7 +69,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
const uqTagId = generateMdTagPrefix()
|
const uqTagId = generateMdTagPrefix()
|
||||||
const generateMessageTag = () => `${uqTagId}${epoch++}`
|
const generateMessageTag = () => `${uqTagId}${epoch++}`
|
||||||
|
|
||||||
const sendPromise = promisify<void>(ws.send)
|
const sendPromise = promisify(ws.send)
|
||||||
/** send a raw buffer */
|
/** send a raw buffer */
|
||||||
const sendRawMessage = async(data: Uint8Array | Buffer) => {
|
const sendRawMessage = async(data: Uint8Array | Buffer) => {
|
||||||
if(!ws.isOpen) {
|
if(!ws.isOpen) {
|
||||||
@@ -135,12 +140,12 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for a message with a certain tag to be received
|
* Wait for a message with a certain tag to be received
|
||||||
* @param tag the message tag to await
|
* @param tag the message tag to await
|
||||||
* @param json query that was sent
|
* @param json query that was sent
|
||||||
* @param timeoutMs timeout after which the promise will reject
|
* @param timeoutMs timeout after which the promise will reject
|
||||||
*/
|
*/
|
||||||
const waitForMessage = async<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
const waitForMessage = async<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
||||||
let onRecv: (json) => void
|
let onRecv: (json) => void
|
||||||
let onErr: (err) => void
|
let onErr: (err) => void
|
||||||
try {
|
try {
|
||||||
@@ -237,7 +242,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
to: S_WHATSAPP_NET
|
to: S_WHATSAPP_NET
|
||||||
},
|
},
|
||||||
content: [
|
content: [
|
||||||
{ tag: 'count', attrs: { } }
|
{ tag: 'count', attrs: {} }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const countChild = getBinaryNodeChild(result, 'count')
|
const countChild = getBinaryNodeChild(result, 'count')
|
||||||
@@ -287,7 +292,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
|
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
|
||||||
/* Check if this is a response to a message we are expecting */
|
/* Check if this is a response to a message we are expecting */
|
||||||
const l0 = frame.tag
|
const l0 = frame.tag
|
||||||
const l1 = frame.attrs || { }
|
const l1 = frame.attrs || {}
|
||||||
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
||||||
|
|
||||||
Object.keys(l1).forEach(key => {
|
Object.keys(l1).forEach(key => {
|
||||||
@@ -374,9 +379,9 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
const diff = Date.now() - lastDateRecv.getTime()
|
const diff = Date.now() - lastDateRecv.getTime()
|
||||||
/*
|
/*
|
||||||
check if it's been a suspicious amount of time since the server responded with our last seen
|
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
|
it could be that the network is down
|
||||||
*/
|
*/
|
||||||
if(diff > keepAliveIntervalMs + 5000) {
|
if(diff > keepAliveIntervalMs + 5000) {
|
||||||
end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }))
|
end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }))
|
||||||
} else if(ws.isOpen) {
|
} else if(ws.isOpen) {
|
||||||
@@ -390,7 +395,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
type: 'get',
|
type: 'get',
|
||||||
xmlns: 'w:p',
|
xmlns: 'w:p',
|
||||||
},
|
},
|
||||||
content: [{ tag: 'ping', attrs: { } }]
|
content: [{ tag: 'ping', attrs: {} }]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
@@ -411,7 +416,7 @@ export const makeSocket = (config: SocketConfig) => {
|
|||||||
type: 'set',
|
type: 'set',
|
||||||
},
|
},
|
||||||
content: [
|
content: [
|
||||||
{ tag, attrs: { } }
|
{ tag, attrs: {} }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7501,10 +7501,10 @@ ws@^7.4.6:
|
|||||||
resolved "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz"
|
resolved "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz"
|
||||||
integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==
|
integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==
|
||||||
|
|
||||||
ws@^8.0.0:
|
ws@^8.13.0:
|
||||||
version "8.7.0"
|
version "8.13.0"
|
||||||
resolved "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
|
||||||
integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==
|
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
|
||||||
|
|
||||||
xdg-basedir@^5.0.1, xdg-basedir@^5.1.0:
|
xdg-basedir@^5.0.1, xdg-basedir@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user