Merge branch 'master' into invalid-qr-patch

This commit is contained in:
Adhiraj Singh
2022-04-19 22:01:34 +05:30
10 changed files with 279 additions and 28 deletions

View File

@@ -43,7 +43,7 @@ const BASE_CONNECTION_CONFIG: CommonSocketConfig<any> = {
export const DEFAULT_CONNECTION_CONFIG: SocketConfig = {
...BASE_CONNECTION_CONFIG,
waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
linkPreviewImageThumbnailWidth: 192,
getMessage: async() => undefined
}

View File

@@ -14,9 +14,9 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
auth: initialAuthInfo
} = config
const ev = new EventEmitter() as LegacyBaileysEventEmitter
const authInfo = initialAuthInfo || newLegacyAuthCreds()
const state: ConnectionState = {
legacy: {
phoneConnected: false,
@@ -39,7 +39,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
// if no reconnects occur
// send close event
updateState({
connection: 'close',
connection: 'close',
qr: undefined,
lastDisconnect: {
error,
@@ -49,12 +49,12 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
})
/** Can you login to WA without scanning the QR */
const canLogin = () => !!authInfo?.encKey && !!authInfo?.macKey
const updateState = (update: Partial<ConnectionState>) => {
Object.assign(state, update)
ev.emit('connection.update', update)
}
/**
* Logs you out from WA
* If connected, invalidates the credentials with the server
@@ -82,7 +82,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
curveKeys = Curve.generateKeyPair()
const publicKey = Buffer.from(curveKeys.public).toString('base64')
let qrGens = 0
const qrLoop = ttl => {
const qr = [ref, publicKey, authInfo.clientID].join(',')
updateState({ qr })
@@ -96,7 +96,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
try {
// request new QR
const { ref: newRef, ttl: newTTL } = await socket.query({
json: ['admin', 'Conn', 'reref'],
json: ['admin', 'Conn', 'reref'],
expect200: true,
longTag: true,
requiresPhoneConnection: false
@@ -123,7 +123,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
const canDoLogin = canLogin()
const initQuery = (async() => {
const { ref, ttl } = await socket.query({
json: ['admin', 'init', version, browser, authInfo.clientID, true],
json: ['admin', 'init', version, browser, authInfo.clientID, true],
expect200: true,
longTag: true,
requiresPhoneConnection: false
@@ -168,7 +168,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
// wait for response with tag "s1"
let response = await Promise.race(
[
[
socket.waitForMessage('s1', false, undefined).promise,
...(loginTag ? [socket.waitForMessage(loginTag, false, connectTimeoutMs).promise] : [])
]
@@ -184,9 +184,9 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
if(response[1]?.challenge) {
const json = computeChallengeResponse(response[1].challenge, authInfo)
logger.info('resolving login challenge')
await socket.query({ json, expect200: true, timeoutMs: connectTimeoutMs })
response = await socket.waitForMessage('s2', true).promise
}
@@ -201,7 +201,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
// validate the new connection
const { user, auth } = validateNewConnection(response[1], authInfo, curveKeys)// validate the connection
const isNewLogin = user.id !== state.legacy!.user?.id
Object.assign(authInfo, auth)
updateEncKeys()
@@ -233,7 +233,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => {
}
process.nextTick(() => {
ev.emit('connection.update', {
ev.emit('connection.update', {
...state
})
})

View File

@@ -1,8 +1,8 @@
import { proto } from '../../WAProto'
import { GroupMetadata, ParticipantAction, SocketConfig } from '../Types'
import { generateMessageID } from '../Utils'
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, jidEncode, jidNormalizedUser } from '../WABinary'
import { makeSocket } from './socket'
import { proto } from '../../WAProto'
export const makeGroupsSocket = (config: SocketConfig) => {
const sock = makeSocket(config)
@@ -137,10 +137,10 @@ export const makeGroupsSocket = (config: SocketConfig) => {
},
groupAcceptInviteV4: async(jid: string, inviteMessage: proto.IGroupInviteMessage) => {
const results = await groupQuery(inviteMessage.groupJid, 'set', [{ tag: 'accept', attrs: {
code: inviteMessage.inviteCode,
expiration: inviteMessage.inviteExpiration.toString(),
admin: jid} }])
return results.attrs.from;
code: inviteMessage.inviteCode,
expiration: inviteMessage.inviteExpiration.toString(),
admin: jid } }])
return results.attrs.from
},
groupToggleEphemeral: async(jid: string, ephemeralExpiration: number) => {
const content: BinaryNode = ephemeralExpiration ?

View File

@@ -4,11 +4,12 @@ import { proto } from '../../WAProto'
import { WA_DEFAULT_EPHEMERAL } from '../Defaults'
import { AnyMessageContent, MediaConnInfo, MessageReceiptType, MessageRelayOptions, MiscMessageGenerationOptions, SocketConfig, WAMessageKey } from '../Types'
import { aggregateMessageKeysNotFromMe, encodeWAMessage, encryptSenderKeyMsgSignalProto, encryptSignalProto, extractDeviceJids, generateMessageID, generateWAMessage, getWAUploadToServer, jidToSignalProtocolAddress, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils'
import { getUrlInfo } from '../Utils/link-preview'
import { BinaryNode, BinaryNodeAttributes, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, isJidUser, jidDecode, jidEncode, jidNormalizedUser, JidWithDevice, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary'
import { makeGroupsSocket } from './groups'
export const makeMessagesSocket = (config: SocketConfig) => {
const { logger } = config
const { logger, linkPreviewImageThumbnailWidth } = config
const sock = makeGroupsSocket(config)
const {
ev,
@@ -484,7 +485,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
logger,
userJid,
// multi-device does not have this yet
//getUrlInfo: generateUrlInfo,
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth }),
upload: waUploadToServer,
mediaCache: config.mediaCache,
...options,

View File

@@ -19,6 +19,8 @@ export type SocketConfig = CommonSocketConfig<AuthenticationState> & {
userDevicesCache?: NodeCache
/** map to store the retry counts for failed messages */
msgRetryCounterMap?: { [msgId: string]: number }
/** width for link preview images */
linkPreviewImageThumbnailWidth: number
/**
* fetch a message from your store
* implement this so that messages failed to send (solves the "this message can take a while" issue) can be retried

51
src/Utils/link-preview.ts Normal file
View File

@@ -0,0 +1,51 @@
import { WAUrlInfo } from '../Types'
import { extractImageThumb, getHttpStream } from './messages-media'
const THUMBNAIL_WIDTH_PX = 192
/** Fetches an image and generates a thumbnail for it */
const getCompressedJpegThumbnail = async(url: string, thumbnailWidth: number) => {
const stream = await getHttpStream(url)
const result = await extractImageThumb(stream, thumbnailWidth)
return result
}
export type URLGenerationOptions = {
thumbnailWidth: number
}
/**
* Given a piece of text, checks for any URL present, generates link preview for the same and returns it
* Return undefined if the fetch failed or no URL was found
* @param text the text containing URL
* @returns the URL info required to generate link preview
*/
export const getUrlInfo = async(
text: string,
opts: URLGenerationOptions = { thumbnailWidth: THUMBNAIL_WIDTH_PX }
): Promise<WAUrlInfo | undefined> => {
try {
const { getLinkPreview } = await import('link-preview-js')
const info = await getLinkPreview(text)
if(info && 'title' in info) {
const [image] = info.images
const jpegThumbnail = image
? await getCompressedJpegThumbnail(image, opts.thumbnailWidth)
: undefined
return {
'canonical-url': info.url,
'matched-text': info.url,
title: info.title,
description: info.description,
jpegThumbnail
}
}
} catch(error) {
if(!error.message.includes('receive a valid')) {
throw error
}
}
}

View File

@@ -91,7 +91,7 @@ const extractVideoThumb = async(
})
}) as Promise<void>
export const extractImageThumb = async(bufferOrFilePath: Readable | Buffer | string) => {
export const extractImageThumb = async(bufferOrFilePath: Readable | Buffer | string, width = 32) => {
if(bufferOrFilePath instanceof Readable) {
bufferOrFilePath = await toBuffer(bufferOrFilePath)
}
@@ -99,7 +99,7 @@ export const extractImageThumb = async(bufferOrFilePath: Readable | Buffer | str
const lib = await getImageProcessingLibrary()
if('sharp' in lib) {
const result = await lib.sharp!.default(bufferOrFilePath)
.resize(32)
.resize(width)
.jpeg({ quality: 50 })
.toBuffer()
return result
@@ -109,7 +109,7 @@ export const extractImageThumb = async(bufferOrFilePath: Readable | Buffer | str
const jimp = await read(bufferOrFilePath as any)
const result = await jimp
.quality(50)
.resize(32, AUTO, RESIZE_BILINEAR)
.resize(width, AUTO, RESIZE_BILINEAR)
.getBufferAsync(MIME_JPEG)
return result
}