Skip to content

fix(newsletter): parse metadata and fetch-messages responses (#2620 port)#503

Merged
rsalcara merged 1 commit into
developfrom
fix/newsletter-metadata-and-fetch-2620
Jun 6, 2026
Merged

fix(newsletter): parse metadata and fetch-messages responses (#2620 port)#503
rsalcara merged 1 commit into
developfrom
fix/newsletter-metadata-and-fetch-2620

Conversation

@rsalcara

@rsalcara rsalcara commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Port of upstream WhiskeySockets#2620 by @vinikjkkj. Three independent fixes batched together.

1. `newsletterMetadata` cast bruto perdia campos

Antes: `parseNewsletterMetadata` fazia `result as NewsletterMetadata` direto. O wire é snake_case nested (`thread_metadata.subscribers_count`, `thread_metadata.picture.direct_path`) e o tipo exposto é flat camelCase — o cast deixava `name`, `subscribers`, etc. como `undefined`.

Pior: para fetch de canais não-seguidos via invite, o servidor não retorna `thread_metadata.picture` — retorna `image` ou `preview` como siblings. Ler `.picture` direto trazia `undefined`.

Fix: unwrap do envelope `result` quando presente, exige `id` string, traduz para o shape flat com fallback `picture → image → preview` para o thumbnail. Resolve upstream issue WhiskeySockets#2204.

2. `newsletterFetchMessages` timed out

Stanza antiga:

```xml

<message_updates count since after/>

```

O servidor ignora — o request fica pendurado até o caller dar timeout. Resolve upstream issue WhiskeySockets#2555.

Stanza correta (capturada do WA Web):

```xml

<messages type='jid' jid='' count='N' [before/after]/>

```

O atributo de cursor mudou de `since` para `before`; a assinatura da função `(jid, count, since, after)` foi preservada (mapeamos `since` → `before` internamente). Resposta vem em `` com filhos ``; cada um é desempacotado por `parseFetchedNewsletterMessage` em um objeto estruturado:
`id`, `serverId`, `type`, `timestamp`, `isSender`, `views` / `forwards` / `responses`, `editTimestamp`, `originalTimestamp`, `mediaRcat`, `reactions[]`, `pollVotes[]` e o `proto.Message` decodificado do `<plaintext>`.

Mudança de retorno: era `Promise` (que ninguém usava funcional porque o request sempre dava timeout) → agora é `Promise<Array<…>>`. Nenhum caller funcional quebra.

3. `NewsletterCreateResponse` duplicada em `Types/Newsletter.ts`

Duas declarações `export interface NewsletterCreateResponse` consecutivas com corpos idênticos. TypeScript faz merge silencioso então não dava erro, mas o segundo bloco é puro lixo. Removido.

Por que isso não foi pego pela nossa investigação Frida

Nosso hook Frida capturou o cliente oficial do WhatsApp Android — viu como o servidor responde, mas não viu como o Baileys parseia. Esses três bugs vivem em código TypeScript do Baileys:

  • B1 é parser que faz cast bruto de uma resposta correta do servidor.
  • B2 é stanza que o cliente oficial nunca emite (ele usa subscribe + push em vez de fetch-by-poll).
  • B3 é definição duplicada — só linter pega.

@vinikjkkj provavelmente bateu de cara com o timeout / dados vazios rodando Baileys.

Test plan

  • `npm run build` passa.
  • `newsletterMetadata` para canal seguido — retorna `name`, `subscribers`, `picture.directPath` preenchidos.
  • `newsletterMetadata` para canal não-seguido via invite — retorna `picture` mesmo quando o servidor envia `image` ou `preview` como sibling.
  • `newsletterFetchMessages` para um canal com mensagens — retorna array tipado com `id`, `message` decodificado, `reactions`, `forwards`, em vez de timeout.
  • Type-search por `NewsletterCreateResponse` retorna 1 declaração só.

Summary by cubic

Fixes newsletter metadata parsing and message fetching so fields are returned correctly and requests no longer time out. Also removes a duplicate NewsletterCreateResponse type.

  • Bug Fixes

    • Metadata: unwrap the result envelope, map nested snake_case to the flat camelCase shape, and fall back picture → image → preview. Requires id to be a string.
    • Fetch messages: use the WA Web query (<messages type='jid' jid='<channel-jid>' count='N' [before/after]/> to s.whatsapp.net), map since to before, and parse each <message> into a structured object with counters, reactions, poll votes, and decoded proto.Message.
    • Cleanup: remove duplicated NewsletterCreateResponse interface.
  • Migration

    • newsletterFetchMessages now returns Promise<Array<…>> instead of Promise<BinaryNode>. Update callers to handle an array of parsed messages.

Written for commit f9c11d2. Summary will update on new commits.

Review in cubic

…Sockets#2620 port)

Port of upstream PR WhiskeySockets#2620 (@vinikjkkj). Three fixes
in one branch — `newsletterMetadata` parse, `newsletterFetchMessages`
query+parse, and a duplicated type definition cleanup.

## 1. newsletterMetadata

The earlier code cast the raw server response to `NewsletterMetadata`
whole, which loses fields in practice:

- The wire shape is snake_case nested (`thread_metadata.subscribers_count`,
  `thread_metadata.picture.direct_path`) while `NewsletterMetadata` is
  the flat camelCase shape we expose to callers. A direct cast leaves
  the channel name, subscribers count, etc. as `undefined`.
- For preview-only responses (e.g. fetching a non-followed channel via
  invite), `thread_metadata.picture` is absent and the server returns
  `image` / `preview` siblings. Reading `.picture` alone returned
  `undefined` for those cases.

`parseNewsletterMetadata` now unwraps the `result` envelope when
present, requires a string `id`, and translates the nested response into
the flat shape with the documented `picture → image → preview` fallback
chain. Fixes upstream issue WhiskeySockets#2204 (channel picture missing).

## 2. newsletterFetchMessages

The earlier stanza was:

  <iq to='<channel-jid>' xmlns='newsletter'>
    <message_updates count since after/>
  </iq>

That request reaches the server, the server doesn't recognize it, and
the request hangs until the caller times out. Upstream issue WhiskeySockets#2555.

The correct WA-Web-shaped query is:

  <iq to='s.whatsapp.net' xmlns='newsletter'>
    <messages type='jid' jid='<channel-jid>' count='N' [before/after]/>
  </iq>

The XML attribute name flipped from `since` to `before`. The function
signature (`(jid, count, since, after)`) is preserved so existing
callers keep working — we map `since` to the `before` attribute.

The response now comes back as a `<messages>` container with one or
more `<message>` children. Each `<message>` carries plaintext-encoded
`proto.Message` content plus side counters and metadata. The new
`parseFetchedNewsletterMessage` helper unpacks one `<message>` into a
structured object: `id`, `serverId`, `type`, `timestamp`, `isSender`,
`views`/`forwards`/`responses` counts, edit / original timestamps,
media `rcat` (category routing blob), `reactions` array, `pollVotes`
array, and the decoded `proto.Message`.

This changes the return type from `Promise<BinaryNode>` (effectively
opaque, never populated because the request always timed out) to
`Promise<Array<…>>`. Since the previous shape was a perpetually-empty
timeout, no working caller breaks.

## 3. NewsletterCreateResponse duplicated

`src/Types/Newsletter.ts` declared `NewsletterCreateResponse` twice
back-to-back with identical bodies. TypeScript silently merges duplicate
`interface` declarations so it didn't cause errors, but it broke type
search and made future edits risk drifting the two copies. Removed
the second declaration.

Files:
- src/Socket/newsletter.ts: `parseNewsletterMetadata` rewrite,
  `parseFetchedNewsletterMessage` helper, `newsletterFetchMessages`
  query+parse rewrite, new imports for `proto`, `BinaryNode`,
  `getBinaryNodeChildren`, `S_WHATSAPP_NET`.
- src/Types/Newsletter.ts: removed duplicated `NewsletterCreateResponse`.
Copilot AI review requested due to automatic review settings June 6, 2026 02:17
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2633c4f8-e5dc-42f7-947d-436d806f0a2f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/newsletter-metadata-and-fetch-2620

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Thanks for opening this pull request and contributing to the project!

The next step is for the maintainers to review your changes. If everything looks good, it will be approved and merged into the main branch.

In the meantime, anyone in the community is encouraged to test this pull request and provide feedback.

✅ How to confirm it works

If you’ve tested this PR, please comment below with:

Tested and working ✅

This helps us speed up the review and merge process.

📦 To test this PR locally:

# NPM
npm install @whiskeysockets/baileys@rsalcara/InfiniteAPI#fix/newsletter-metadata-and-fetch-2620

# Yarn (v2+)
yarn add @whiskeysockets/baileys@rsalcara/InfiniteAPI#fix/newsletter-metadata-and-fetch-2620

# PNPM
pnpm add @whiskeysockets/baileys@rsalcara/InfiniteAPI#fix/newsletter-metadata-and-fetch-2620

If you encounter any issues or have feedback, feel free to comment as well.

@rsalcara rsalcara merged commit eebb6d2 into develop Jun 6, 2026
7 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports upstream fixes for WhatsApp newsletter handling by correcting metadata parsing (including picture fallbacks) and implementing the WA Web <messages> IQ flow so newsletterFetchMessages returns parsed messages instead of timing out. Also cleans up a duplicated type definition.

Changes:

  • Fix parseNewsletterMetadata to unwrap the result envelope, require id, map nested fields, and fall back picture → image → preview.
  • Update newsletterFetchMessages to query s.whatsapp.net with <messages type='jid' ...> and parse each <message> into a structured object with decoded proto.Message.
  • Remove duplicate NewsletterCreateResponse interface in src/Types/Newsletter.ts.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Socket/newsletter.ts Adjusts newsletter metadata parsing and implements the new fetch-messages request/response parsing.
src/Types/Newsletter.ts Removes a duplicated NewsletterCreateResponse type declaration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Socket/newsletter.ts
Comment on lines +46 to +49
// 1. The actual response shape uses snake_case (`thread_metadata.
// subscribers_count`, `picture.direct_path`) while
// `NewsletterMetadata` is the flat camelCase shape we expose —
// casting lost the channel name, subscribers count, etc.
Comment thread src/Socket/newsletter.ts
Comment on lines +69 to 83
const thread = node.thread_metadata ?? {}
const viewer = node.viewer_metadata ?? {}
const pic = thread.picture ?? thread.image ?? thread.preview

return {
id: node.id,
name: thread.name?.text ?? '',
description: thread.description?.text,
invite: thread.invite,
creation_time: thread.creation_time ? parseInt(thread.creation_time, 10) : undefined,
subscribers: thread.subscribers_count ? parseInt(thread.subscribers_count, 10) : undefined,
picture: pic ? { id: pic.id, directPath: pic.direct_path } : undefined,
verification: thread.verification,
mute_state: viewer.mute
}
Comment thread src/Socket/newsletter.ts
Comment on lines +104 to +118
const reactionsNode = getBinaryNodeChild(node, 'reactions')
const reactions = reactionsNode
? getBinaryNodeChildren(reactionsNode, 'reaction').map(r => ({
code: r.attrs.code,
count: r.attrs.count ? parseInt(r.attrs.count, 10) : 0
}))
: []

const votesNode = getBinaryNodeChild(node, 'votes')
const pollVotes = votesNode
? getBinaryNodeChildren(votesNode, 'vote').map(v => ({
count: v.attrs.count ? parseInt(v.attrs.count, 10) : 0,
hash: v.content instanceof Uint8Array ? v.content : undefined
}))
: []
rsalcara added a commit that referenced this pull request Jun 6, 2026
…a picture fallback

PR #508 review caught an ambiguity in the picture-fallback chain inherited
from upstream PR WhiskeySockets#2620 (#503 port). The original chain only looked inside
`thread_metadata`:

    const pic = thread.picture ?? thread.image ?? thread.preview

but the in-code comment noted that for preview-only responses (non-followed
channel via invite) the server returns `image` / `preview` SIBLINGS of
`thread_metadata` rather than children of it. The chain was missing the
sibling case, so the picture came back undefined for that flow.

Extend the fallback to also try `node.image` and `node.preview`. Additive and
defensive — `undefined ?? undefined` short-circuits cleanly when the server
returns the followed-channel shape (which has worked since #503 merged).

Also refine the comment so the chain is unambiguous on the next read.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rsalcara added a commit that referenced this pull request Jun 6, 2026
…a picture fallback

PR #508 review caught an ambiguity in the picture-fallback chain inherited
from upstream PR WhiskeySockets#2620 (#503 port). The original chain only looked inside
`thread_metadata`:

    const pic = thread.picture ?? thread.image ?? thread.preview

but the in-code comment noted that for preview-only responses (non-followed
channel via invite) the server returns `image` / `preview` SIBLINGS of
`thread_metadata` rather than children of it. The chain was missing the
sibling case, so the picture came back undefined for that flow.

Extend the fallback to also try `node.image` and `node.preview`. Additive and
defensive — `undefined ?? undefined` short-circuits cleanly when the server
returns the followed-channel shape (which has worked since #503 merged).

Also refine the comment so the chain is unambiguous on the next read.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rsalcara added a commit that referenced this pull request Jun 6, 2026
release: develop → master 2026-06-06 (#503 + #506 + #507)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants