fix: use newsletter-specific upload paths for channel media
Port of WhiskeySockets/Baileys#2434 by @alesdi. Newsletter media uploads were using regular /mms/* paths instead of newsletter-specific paths, causing broken/invisible media in channels (ACK error 479) and incorrect directPath values. Changes: - Add NEWSLETTER_MEDIA_PATH_MAP constant with /newsletter/* paths - Pass newsletter:true to upload function for newsletter media - Use newsletter paths + server_thumb_gen=1 query param in upload URL - Return thumbnailDirectPath/thumbnailSha256 from upload response - Omit url field for newsletter messages (directPath only) - Add mediatype attribute to plaintext node for newsletter stanzas - Extend WAMediaUploadFunction type with newsletter option + thumbnail fields
This commit is contained in:
@@ -157,6 +157,15 @@ export const MEDIA_PATH_MAP: { [T in MediaType]?: string } = {
|
|||||||
'thumbnail-sticker-pack': '/mms/thumbnail-sticker-pack'
|
'thumbnail-sticker-pack': '/mms/thumbnail-sticker-pack'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NEWSLETTER_MEDIA_PATH_MAP: { [T in MediaType]?: string } = {
|
||||||
|
image: '/newsletter/newsletter-image',
|
||||||
|
video: '/newsletter/newsletter-video',
|
||||||
|
document: '/newsletter/newsletter-document',
|
||||||
|
audio: '/newsletter/newsletter-audio',
|
||||||
|
sticker: '/newsletter/newsletter-image',
|
||||||
|
'thumbnail-link': '/newsletter/newsletter-image'
|
||||||
|
}
|
||||||
|
|
||||||
export const MEDIA_HKDF_KEY_MAPPING = {
|
export const MEDIA_HKDF_KEY_MAPPING = {
|
||||||
audio: 'Audio',
|
audio: 'Audio',
|
||||||
document: 'Document',
|
document: 'Document',
|
||||||
|
|||||||
@@ -1051,7 +1051,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
const bytes = encodeNewsletterMessage(patched as proto.IMessage)
|
const bytes = encodeNewsletterMessage(patched as proto.IMessage)
|
||||||
binaryNodeContent.push({
|
binaryNodeContent.push({
|
||||||
tag: 'plaintext',
|
tag: 'plaintext',
|
||||||
attrs: {},
|
attrs: mediaType ? { mediatype: mediaType } : {},
|
||||||
content: bytes
|
content: bytes
|
||||||
})
|
})
|
||||||
const stanza: BinaryNode = {
|
const stanza: BinaryNode = {
|
||||||
|
|||||||
+10
-2
@@ -990,8 +990,16 @@ export type MessageGenerationOptionsFromContent = MiscMessageGenerationOptions &
|
|||||||
|
|
||||||
export type WAMediaUploadFunction = (
|
export type WAMediaUploadFunction = (
|
||||||
encFilePath: string,
|
encFilePath: string,
|
||||||
opts: { fileEncSha256B64: string; mediaType: MediaType; timeoutMs?: number }
|
opts: { fileEncSha256B64: string; mediaType: MediaType; timeoutMs?: number; newsletter?: boolean }
|
||||||
) => Promise<{ mediaUrl: string; directPath: string; meta_hmac?: string; ts?: number; fbid?: number }>
|
) => Promise<{
|
||||||
|
mediaUrl: string
|
||||||
|
directPath: string
|
||||||
|
meta_hmac?: string
|
||||||
|
ts?: number
|
||||||
|
fbid?: number
|
||||||
|
thumbnailDirectPath?: string
|
||||||
|
thumbnailSha256?: string
|
||||||
|
}>
|
||||||
|
|
||||||
export type MediaGenerationOptions = {
|
export type MediaGenerationOptions = {
|
||||||
logger?: ILogger
|
logger?: ILogger
|
||||||
|
|||||||
@@ -10,7 +10,13 @@ import { join } from 'path'
|
|||||||
import { Readable, Transform } from 'stream'
|
import { Readable, Transform } from 'stream'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
import { proto } from '../../WAProto/index.js'
|
import { proto } from '../../WAProto/index.js'
|
||||||
import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP, type MediaType } from '../Defaults'
|
import {
|
||||||
|
DEFAULT_ORIGIN,
|
||||||
|
MEDIA_HKDF_KEY_MAPPING,
|
||||||
|
MEDIA_PATH_MAP,
|
||||||
|
type MediaType,
|
||||||
|
NEWSLETTER_MEDIA_PATH_MAP
|
||||||
|
} from '../Defaults'
|
||||||
import type {
|
import type {
|
||||||
BaileysEventMap,
|
BaileysEventMap,
|
||||||
DownloadableMessage,
|
DownloadableMessage,
|
||||||
@@ -681,6 +687,10 @@ type MediaUploadResult = {
|
|||||||
meta_hmac?: string
|
meta_hmac?: string
|
||||||
ts?: number
|
ts?: number
|
||||||
fbid?: number
|
fbid?: number
|
||||||
|
thumbnail_info?: {
|
||||||
|
thumbnail_sha256?: string
|
||||||
|
thumbnail_direct_path?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UploadParams = {
|
export type UploadParams = {
|
||||||
@@ -825,11 +835,21 @@ export const getWAUploadToServer = (
|
|||||||
{ customUploadHosts, fetchAgent, logger, options }: SocketConfig,
|
{ customUploadHosts, fetchAgent, logger, options }: SocketConfig,
|
||||||
refreshMediaConn: (force: boolean) => Promise<MediaConnInfo>
|
refreshMediaConn: (force: boolean) => Promise<MediaConnInfo>
|
||||||
): WAMediaUploadFunction => {
|
): WAMediaUploadFunction => {
|
||||||
return async (filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
|
return async (filePath, { mediaType, fileEncSha256B64, timeoutMs, newsletter }) => {
|
||||||
// send a query JSON to obtain the url & auth token to upload our media
|
// send a query JSON to obtain the url & auth token to upload our media
|
||||||
let uploadInfo = await refreshMediaConn(false)
|
let uploadInfo = await refreshMediaConn(false)
|
||||||
|
|
||||||
let urls: { mediaUrl: string; directPath: string; meta_hmac?: string; ts?: number; fbid?: number } | undefined
|
let urls:
|
||||||
|
| {
|
||||||
|
mediaUrl: string
|
||||||
|
directPath: string
|
||||||
|
meta_hmac?: string
|
||||||
|
ts?: number
|
||||||
|
fbid?: number
|
||||||
|
thumbnailDirectPath?: string
|
||||||
|
thumbnailSha256?: string
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
const hosts = [...customUploadHosts, ...uploadInfo.hosts]
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts]
|
||||||
|
|
||||||
fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64)
|
fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64)
|
||||||
@@ -851,7 +871,11 @@ export const getWAUploadToServer = (
|
|||||||
logger.debug(`uploading to "${hostname}"`)
|
logger.debug(`uploading to "${hostname}"`)
|
||||||
|
|
||||||
const auth = encodeURIComponent(uploadInfo.auth)
|
const auth = encodeURIComponent(uploadInfo.auth)
|
||||||
const url = `https://${hostname}${MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
|
const mediaPath = (newsletter ? NEWSLETTER_MEDIA_PATH_MAP[mediaType] : undefined) || MEDIA_PATH_MAP[mediaType]
|
||||||
|
let url = `https://${hostname}${mediaPath}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
|
||||||
|
if (newsletter) {
|
||||||
|
url += '&server_thumb_gen=1'
|
||||||
|
}
|
||||||
|
|
||||||
let result: MediaUploadResult | undefined
|
let result: MediaUploadResult | undefined
|
||||||
try {
|
try {
|
||||||
@@ -872,7 +896,9 @@ export const getWAUploadToServer = (
|
|||||||
directPath: result.direct_path!,
|
directPath: result.direct_path!,
|
||||||
meta_hmac: result.meta_hmac,
|
meta_hmac: result.meta_hmac,
|
||||||
fbid: result.fbid,
|
fbid: result.fbid,
|
||||||
ts: result.ts
|
ts: result.ts,
|
||||||
|
thumbnailDirectPath: result.thumbnail_info?.thumbnail_direct_path,
|
||||||
|
thumbnailSha256: result.thumbnail_info?.thumbnail_sha256
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -196,10 +196,11 @@ export const prepareWAMessageMedia = async (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const fileSha256B64 = fileSha256.toString('base64')
|
const fileSha256B64 = fileSha256.toString('base64')
|
||||||
const { mediaUrl, directPath } = await options.upload(filePath, {
|
const { directPath, thumbnailDirectPath, thumbnailSha256 } = await options.upload(filePath, {
|
||||||
fileEncSha256B64: fileSha256B64,
|
fileEncSha256B64: fileSha256B64,
|
||||||
mediaType: mediaType,
|
mediaType: mediaType,
|
||||||
timeoutMs: options.mediaUploadTimeoutMs
|
timeoutMs: options.mediaUploadTimeoutMs,
|
||||||
|
newsletter: true
|
||||||
})
|
})
|
||||||
|
|
||||||
await fs.unlink(filePath)
|
await fs.unlink(filePath)
|
||||||
@@ -207,10 +208,12 @@ export const prepareWAMessageMedia = async (
|
|||||||
const obj = WAProto.Message.fromObject({
|
const obj = WAProto.Message.fromObject({
|
||||||
// todo: add more support here
|
// todo: add more support here
|
||||||
[`${mediaType}Message`]: (MessageTypeProto as any)[mediaType].fromObject({
|
[`${mediaType}Message`]: (MessageTypeProto as any)[mediaType].fromObject({
|
||||||
url: mediaUrl,
|
// url intentionally omitted — newsletters use directPath only
|
||||||
directPath,
|
directPath,
|
||||||
fileSha256,
|
fileSha256,
|
||||||
fileLength,
|
fileLength,
|
||||||
|
thumbnailDirectPath,
|
||||||
|
thumbnailSha256: thumbnailSha256 ? Buffer.from(thumbnailSha256, 'base64') : undefined,
|
||||||
...uploadData,
|
...uploadData,
|
||||||
media: undefined
|
media: undefined
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user