From 18cffdaa17b9ba3003b2c0f5bf6f1953426648e3 Mon Sep 17 00:00:00 2001 From: Adhiraj Date: Mon, 27 Jul 2020 22:48:40 +0530 Subject: [PATCH] User Agent string in fetch requests --- package.json | 1 + src/WAClient/Base.ts | 1 + src/WAClient/Messages.ts | 10 +++++++--- src/WAClient/Tests.ts | 2 +- src/WAClient/Utils.ts | 11 +++++++---- src/WAConnection/Base.ts | 3 ++- src/WAConnection/Utils.ts | 5 +++++ 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index cab0d39..fcbd8b4 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "node-fetch": "^2.6.0", "protobufjs": "^6.9.0", "qrcode-terminal": "^0.12.0", + "user-agents": "^1.0.559", "ws": "^7.3.0" }, "devDependencies": { diff --git a/src/WAClient/Base.ts b/src/WAClient/Base.ts index 52356c9..1f663d9 100644 --- a/src/WAClient/Base.ts +++ b/src/WAClient/Base.ts @@ -12,6 +12,7 @@ import { generateProfilePicture } from '../WAClient/Utils' export default class WhatsAppWebBase extends WAConnection { + /** Set the callback for message status updates (when a message is delivered, read etc.) */ setOnMessageStatusChange(callback: (update: MessageStatusUpdate) => void) { const func = (json) => { diff --git a/src/WAClient/Messages.ts b/src/WAClient/Messages.ts index 95e0a21..56fba07 100644 --- a/src/WAClient/Messages.ts +++ b/src/WAClient/Messages.ts @@ -271,7 +271,10 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups { const urlFetch = await fetch(hostname, { method: 'POST', body: body, - headers: { Origin: 'https://web.whatsapp.com' }, + headers: { + Origin: 'https://web.whatsapp.com', + 'User-Agent': this.userAgentString + }, }) const responseJSON = await urlFetch.json() if (!responseJSON.url) { @@ -341,14 +344,15 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups { * Renews the download url automatically, if necessary. */ async downloadMediaMessage (message: WAMessage) { + const fetchHeaders = { 'User-Agent': this.userAgentString } try { - const buff = await decodeMediaMessageBuffer (message.message) + const buff = await decodeMediaMessageBuffer (message.message, fetchHeaders) 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) + const buff = await decodeMediaMessageBuffer (message.message, fetchHeaders) return buff } throw error diff --git a/src/WAClient/Tests.ts b/src/WAClient/Tests.ts index 518241f..fe7c242 100644 --- a/src/WAClient/Tests.ts +++ b/src/WAClient/Tests.ts @@ -5,7 +5,7 @@ import * as assert from 'assert' import fetch from 'node-fetch' import { decodeMediaMessage, validateJIDForSending } from './Utils' -import { promiseTimeout, createTimeout } from '../WAConnection/Utils' +import { promiseTimeout, createTimeout, Browsers } from '../WAConnection/Utils' require ('dotenv').config () // dotenv to load test jid const testJid = process.env.TEST_JID || '1234@s.whatsapp.net' // set TEST_JID=xyz@s.whatsapp.net in a .env file in the root directory diff --git a/src/WAClient/Utils.ts b/src/WAClient/Utils.ts index 924bbb7..8bb4400 100644 --- a/src/WAClient/Utils.ts +++ b/src/WAClient/Utils.ts @@ -18,7 +18,6 @@ export function validateJIDForSending (jid: string) { ) } } - /** * Type of notification * @deprecated use WA_MESSAGE_STUB_TYPE instead @@ -102,7 +101,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) { +export async function decodeMediaMessageBuffer(message: WAMessageContent, fetchHeaders: {[k: string]: string} = {}) { /* One can infer media type from the key in the message it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc. @@ -127,7 +126,11 @@ export async function decodeMediaMessageBuffer(message: WAMessageContent) { } // download the message - const fetched = await fetch(messageContent.url, { headers: { Origin: 'https://web.whatsapp.com' } }) + const headers = { + Origin: 'https://web.whatsapp.com', + ...fetchHeaders + } + const fetched = await fetch(messageContent.url, { headers }) const buffer = await fetched.buffer() if (buffer.length <= 10) { @@ -184,7 +187,7 @@ export function extensionForMediaMessage(message: WAMessageContent) { * @deprecated use `client.downloadAndSaveMediaMessage` */ export async function decodeMediaMessage(message: WAMessageContent, filename: string, attachExtension: boolean=true) { - const buffer = await decodeMediaMessageBuffer (message) + const buffer = await decodeMediaMessageBuffer (message, {}) const extension = extensionForMediaMessage (message) const trueFileName = attachExtension ? (filename + '.' + extension) : filename fs.writeFileSync(trueFileName, buffer) diff --git a/src/WAConnection/Base.ts b/src/WAConnection/Base.ts index 11a0490..499d4b9 100644 --- a/src/WAConnection/Base.ts +++ b/src/WAConnection/Base.ts @@ -59,8 +59,9 @@ export default class WAConnectionBase { protected pendingRequests: (() => void)[] = [] protected reconnectLoop: () => Promise protected referenceDate = new Date () // used for generating tags - + protected userAgentString: string constructor () { + this.userAgentString = Utils.userAgentString (this.browserDescription[1]) this.registerCallback (['Cmd', 'type:disconnect'], json => this.unexpectedDisconnect(json[1].kind)) } async unexpectedDisconnect (error: string) { diff --git a/src/WAConnection/Utils.ts b/src/WAConnection/Utils.ts index d1ca5c0..54aa379 100644 --- a/src/WAConnection/Utils.ts +++ b/src/WAConnection/Utils.ts @@ -3,6 +3,7 @@ import HKDF from 'futoin-hkdf' import Decoder from '../Binary/Decoder' import {platform, release} from 'os' import { BaileysError } from './Constants' +import UserAgent from 'user-agents' const platformMap = { 'aix': 'AIX', @@ -17,6 +18,10 @@ export const Browsers = { /** The appropriate browser based on your OS & release */ appropriate: browser => [ platformMap [platform()] || 'Ubuntu', browser, release() ] as [string, string, string] } +export function userAgentString (browser) { + const agent = new UserAgent (new RegExp(browser)) + return agent.toString () +} /** 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))