added proxy support

This commit is contained in:
Adhiraj
2020-09-06 15:48:01 +05:30
parent 7cc35d6e84
commit f12f6fd90d
10 changed files with 70 additions and 21 deletions

View File

@@ -1,4 +1,4 @@
import { Presence, ChatModification, delay } from '../WAConnection/WAConnection'
import { Presence, ChatModification, delay, DEFAULT_ORIGIN } from '../WAConnection/WAConnection'
import { promises as fs } from 'fs'
import * as assert from 'assert'
import fetch from 'node-fetch'
@@ -51,7 +51,7 @@ WAConnectionTest('Misc', (conn) => {
await delay (5000)
const ppUrl = await conn.getProfilePicture(conn.user.jid)
const fetched = await fetch(ppUrl, { headers: { Origin: 'https://web.whatsapp.com' } })
const fetched = await fetch(ppUrl)
const buff = await fetched.buffer ()
const newPP = await fs.readFile ('./Media/cat.jpeg')

View File

@@ -3,6 +3,7 @@ import WS from 'ws'
import * as Utils from './Utils'
import Encoder from '../Binary/Encoder'
import Decoder from '../Binary/Decoder'
import fetch from 'node-fetch'
import {
AuthenticationCredentials,
WAUser,
@@ -21,6 +22,7 @@ import {
ReconnectMode,
WAConnectOptions,
MediaConnInfo,
DEFAULT_ORIGIN,
} from './Constants'
import { EventEmitter } from 'events'
import KeyedDB from '@adiwajshing/keyed-db'
@@ -340,6 +342,17 @@ export class WAConnection extends EventEmitter {
}
})
}
/**
* Does a fetch request with the configuration of the connection
*/
protected fetchRequest = (endpoint: string, method: string = 'GET', body?: any) => (
fetch(endpoint, {
method,
body,
headers: { Origin: DEFAULT_ORIGIN },
agent: this.connectOptions.agent
})
)
generateMessageTag () {
return `${Utils.unixTimestampSeconds(this.referenceDate)}.--${this.msgCount}`
}

View File

@@ -1,5 +1,5 @@
import * as Utils from './Utils'
import { WAMessage, WAChat, MessageLogLevel, WANode, KEEP_ALIVE_INTERVAL_MS, BaileysError, WAConnectOptions, DisconnectReason, UNAUTHORIZED_CODES, WAContact, TimedOutError, CancelledError, WAOpenResult } from './Constants'
import { WAMessage, WAChat, MessageLogLevel, WANode, KEEP_ALIVE_INTERVAL_MS, BaileysError, WAConnectOptions, DisconnectReason, UNAUTHORIZED_CODES, WAContact, TimedOutError, CancelledError, WAOpenResult, DEFAULT_ORIGIN, WS_URL } from './Constants'
import {WAConnection as Base} from './1.Validation'
import Decoder from '../Binary/Decoder'
import WS from 'ws'
@@ -73,7 +73,7 @@ export class WAConnection extends Base {
const reconnectID = shouldUseReconnect ? this.user.jid.replace ('@s.whatsapp.net', '@c.us') : null
this.conn = new WS('wss://web.whatsapp.com/ws', null, { origin: 'https://web.whatsapp.com', timeout: timeoutMs })
this.conn = new WS(WS_URL, null, { origin: DEFAULT_ORIGIN, timeout: timeoutMs, agent: options.agent })
this.conn.on('message', data => this.onMessageRecieved(data as any))
this.conn.on ('open', () => {

View File

@@ -1,5 +1,4 @@
import {WAConnection as Base} from './5.User'
import fetch from 'node-fetch'
import {promises as fs} from 'fs'
import {
MessageOptions,
@@ -114,18 +113,14 @@ export class WAConnection extends Base {
for (let host of json.hosts) {
const hostname = `https://${host.hostname}${MediaPathMap[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
try {
const urlFetch = await fetch(hostname, {
method: 'POST',
body: body,
headers: { Origin: 'https://web.whatsapp.com' },
})
const urlFetch = await this.fetchRequest(hostname, 'POST', body)
mediaUrl = (await urlFetch.json())?.url
if (mediaUrl) break
else throw new Error (`upload failed`)
} catch (error) {
const isLast = host.hostname === json.hosts[json.hosts.length-1].hostname
this.log (`Error in uploading to ${host}${isLast ? '' : ', retrying...'}`, MessageLogLevel.info)
this.log (`Error in uploading to ${host.hostname}${isLast ? '' : ', retrying...'}`, MessageLogLevel.info)
}
}
if (!mediaUrl) throw new Error('Media upload failed on all hosts')
@@ -212,15 +207,14 @@ export class WAConnection extends Base {
* Renews the download url automatically, if necessary.
*/
async downloadMediaMessage (message: WAMessage) {
const fetchHeaders = { }
try {
const buff = await decodeMediaMessageBuffer (message.message, fetchHeaders)
const buff = await decodeMediaMessageBuffer (message.message, this.fetchRequest)
return buff
} catch (error) {
if (error instanceof BaileysError && error.status === 404) { // media needs to be updated
this.log (`updating media of message: ${message.key.id}`, MessageLogLevel.info)
await this.updateMediaMessage (message)
const buff = await decodeMediaMessageBuffer (message.message, fetchHeaders)
const buff = await decodeMediaMessageBuffer (message.message, this.fetchRequest)
return buff
}
throw error

View File

@@ -1,8 +1,13 @@
import { WA } from '../Binary/Constants'
import { proto } from '../../WAMessage/WAMessage'
import { Agent } from 'https'
export const WS_URL = 'wss://web.whatsapp.com/ws'
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com'
export const KEEP_ALIVE_INTERVAL_MS = 20*1000
// export the WAMessage Prototypes
export { proto as WAMessageProto }
export type WANode = WA.Node
@@ -15,6 +20,7 @@ export type WAContextInfo = proto.IContextInfo
export import WA_MESSAGE_STUB_TYPE = proto.WebMessageInfo.WEB_MESSAGE_INFO_STUBTYPE
export import WA_MESSAGE_STATUS_TYPE = proto.WebMessageInfo.WEB_MESSAGE_INFO_STATUS
export interface WALocationMessage {
degreesLatitude: number
degreesLongitude: number
@@ -67,6 +73,8 @@ export type WAConnectOptions = {
waitForChats?: boolean
connectCooldownMs?: number
/** agent which can be used for proxying connections */
agent?: Agent
}
export type WAConnectionState = 'open' | 'connecting' | 'close'

View File

@@ -2,12 +2,14 @@ import * as Crypto from 'crypto'
import HKDF from 'futoin-hkdf'
import Jimp from 'jimp'
import {promises as fs} from 'fs'
import fetch from 'node-fetch'
import { exec } from 'child_process'
import {platform, release} from 'os'
import HttpsProxyAgent from 'https-proxy-agent'
import { URL } from 'url'
import { Agent } from 'https'
import Decoder from '../Binary/Decoder'
import { MessageType, HKDFInfoKeys, MessageOptions, WAChat, WAMessageContent, BaileysError, WAMessageProto, TimedOutError, CancelledError } from './Constants'
import { MessageType, HKDFInfoKeys, MessageOptions, WAChat, WAMessageContent, BaileysError, WAMessageProto, TimedOutError, CancelledError, DEFAULT_ORIGIN } from './Constants'
const platformMap = {
'aix': 'AIX',
@@ -220,6 +222,8 @@ export const generateProfilePicture = async (buffer: Buffer) => {
preview: await cropped.resize(96, 96).getBufferAsync (Jimp.MIME_JPEG)
}
}
export const ProxyAgent = (host: string | URL) => HttpsProxyAgent(host) as any as Agent
/** generates a thumbnail for a given media, if required */
export async function generateThumbnail(buffer: Buffer, mediaType: MessageType, info: MessageOptions) {
if (info.thumbnail === null || info.thumbnail) {
@@ -249,7 +253,7 @@ export async function generateThumbnail(buffer: Buffer, mediaType: MessageType,
* Decode a media message (video, image, document, audio) & return decrypted buffer
* @param message the media message you want to decode
*/
export async function decodeMediaMessageBuffer(message: WAMessageContent, fetchHeaders: {[k: string]: string} = {}) {
export async function decodeMediaMessageBuffer(message: WAMessageContent, fetchRequest: (host: string, method: string) => any) {
/*
One can infer media type from the key in the message
it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc.
@@ -274,8 +278,7 @@ export async function decodeMediaMessageBuffer(message: WAMessageContent, fetchH
}
// download the message
const headers = { Origin: 'https://web.whatsapp.com' }
const fetched = await fetch(messageContent.url, { headers })
const fetched = await fetchRequest(messageContent.url, 'GET')
const buffer = await fetched.buffer()
if (buffer.length <= 10) {