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",
|
||||
"protobufjs": "^6.11.3",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.0.0"
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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 { jidEncode } from '../WABinary'
|
||||
import { makeBusinessSocket } from './business'
|
||||
import { MobileSocket } from './mobile-socket'
|
||||
|
||||
function urlencode(str: string) {
|
||||
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.ev.emit('creds.update', sock.authState.creds)
|
||||
|
||||
if(sock.ws instanceof MobileSocket) {
|
||||
sock.ws.connect()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Boom } from '@hapi/boom'
|
||||
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_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 { 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 { MobileSocket } from './mobile-socket'
|
||||
import { MobileSocketClient, WebSocketClient } from './Client'
|
||||
|
||||
/**
|
||||
* Connects to WA servers and performs:
|
||||
@@ -18,6 +19,7 @@ import { MobileSocket } from './mobile-socket'
|
||||
|
||||
export const makeSocket = (config: SocketConfig) => {
|
||||
const {
|
||||
waWebSocketUrl,
|
||||
connectTimeoutMs,
|
||||
logger,
|
||||
keepAliveIntervalMs,
|
||||
@@ -30,15 +32,18 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
makeSignalRepository,
|
||||
} = config
|
||||
|
||||
config.mobile = config.mobile || config.auth.creds.registered
|
||||
const ws = new MobileSocket(config)
|
||||
ws.setMaxListeners?.(0)
|
||||
let url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
||||
|
||||
// if not mobile or already registered -> auto connect
|
||||
if(!config.mobile || config.auth.creds.registered) {
|
||||
ws.connect()
|
||||
config.mobile = config.mobile || config.auth?.creds?.registered || url.protocol === 'tcp:'
|
||||
|
||||
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)
|
||||
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
||||
const ephemeralKeyPair = Curve.generateKeyPair()
|
||||
@@ -64,7 +69,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
const uqTagId = generateMdTagPrefix()
|
||||
const generateMessageTag = () => `${uqTagId}${epoch++}`
|
||||
|
||||
const sendPromise = promisify<void>(ws.send)
|
||||
const sendPromise = promisify(ws.send)
|
||||
/** send a raw buffer */
|
||||
const sendRawMessage = async(data: Uint8Array | Buffer) => {
|
||||
if(!ws.isOpen) {
|
||||
@@ -135,12 +140,12 @@ 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 timeoutMs timeout after which the promise will reject
|
||||
*/
|
||||
const waitForMessage = async<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
||||
* 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<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
||||
let onRecv: (json) => void
|
||||
let onErr: (err) => void
|
||||
try {
|
||||
@@ -237,7 +242,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
to: S_WHATSAPP_NET
|
||||
},
|
||||
content: [
|
||||
{ tag: 'count', attrs: { } }
|
||||
{ tag: 'count', attrs: {} }
|
||||
]
|
||||
})
|
||||
const countChild = getBinaryNodeChild(result, 'count')
|
||||
@@ -287,7 +292,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
|
||||
/* Check if this is a response to a message we are expecting */
|
||||
const l0 = frame.tag
|
||||
const l1 = frame.attrs || { }
|
||||
const l1 = frame.attrs || {}
|
||||
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
||||
|
||||
Object.keys(l1).forEach(key => {
|
||||
@@ -374,9 +379,9 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
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
|
||||
*/
|
||||
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.isOpen) {
|
||||
@@ -390,7 +395,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
type: 'get',
|
||||
xmlns: 'w:p',
|
||||
},
|
||||
content: [{ tag: 'ping', attrs: { } }]
|
||||
content: [{ tag: 'ping', attrs: {} }]
|
||||
}
|
||||
)
|
||||
.catch(err => {
|
||||
@@ -411,7 +416,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
type: 'set',
|
||||
},
|
||||
content: [
|
||||
{ tag, attrs: { } }
|
||||
{ tag, attrs: {} }
|
||||
]
|
||||
})
|
||||
)
|
||||
|
||||
@@ -7501,10 +7501,10 @@ ws@^7.4.6:
|
||||
resolved "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz"
|
||||
integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==
|
||||
|
||||
ws@^8.0.0:
|
||||
version "8.7.0"
|
||||
resolved "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz"
|
||||
integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==
|
||||
ws@^8.13.0:
|
||||
version "8.13.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
|
||||
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
|
||||
|
||||
xdg-basedir@^5.0.1, xdg-basedir@^5.1.0:
|
||||
version "5.1.0"
|
||||
|
||||
Reference in New Issue
Block a user