Skip to content

delete_message tool, optional caption on send_file, and a media-download 403 fix - #166

Open
vyakunin wants to merge 3 commits into
verygoodplugins:mainfrom
vyakunin:fix/local-patches
Open

delete_message tool, optional caption on send_file, and a media-download 403 fix#166
vyakunin wants to merge 3 commits into
verygoodplugins:mainfrom
vyakunin:fix/local-patches

Conversation

@vyakunin

Copy link
Copy Markdown

Three independent commits; happy to split into separate PRs.

1. delete_message

Adds a delete/revoke tool. for_everyone: true revokes for both sides ("Delete for everyone", only valid for messages this client sent); false removes it locally so list_messages stops returning it while the other party still sees it. Takes chat_jid + the whatsmeow message_id returned by send_message.

2. send_file caption

send_file could only send a bare attachment. Adds an optional caption rendered with the file, matching what the app does. Omitting it preserves current behaviour.

3. Media-download 403

Downloads were failing with 403 on some media. The signed query string was being stripped from directPath before the fetch, so the request arrived unsigned. Keeping the query intact fixes it.

Each commit stands alone and can be cherry-picked.

Vladimir Yakunin and others added 3 commits July 19, 2026 00:26
New POST /api/delete on the Go bridge wraps whatsmeow's BuildRevoke + SendMessage
to "Delete for everyone" (default) or just drop the row from the local sqlite
store. Python MCP exposes it as `delete_message(chat_jid, message_id, for_everyone)`.

Useful for retracting a wrong link/file we just sent (the scenario that
motivated this: a broken APK link was sent to a relative on the family_vpn
project, and we needed to revoke before they tapped it).

Constraints: only own messages can be revoked (whatsmeow); WhatsApp's server
enforces the ~2-day "delete for everyone" window — older messages get an error
back from SendMessage, which we surface as success=false.
Bridge (Go) already supports media+caption in a single WA message — every
image/video/document branch in sendWhatsAppMessage sets Caption from the
`message` field. The Python wrapper just wasn't plumbing it through, so
callers had to send the file and the explanation as two separate bubbles.

Add `caption: str = ""` to send_file in both whatsapp.py and the MCP
tool in main.py; if non-empty, include it as `message` in the /api/send
payload. Backwards-compatible with existing call sites that pass only
recipient + media_path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
extractDirectPathFromURL stripped the URL query string, dropping the
oh/oe (+ccb,_nc_sid) CDN-auth signature. whatsmeow's Download rebuilds
the request as <freshHost><directPath>&hash=... with NO auth header, so
authorization rides entirely on those query params inside directPath.
Stripping them 403'd EVERY image and document download.

The regression surfaced after the whatsmeow bump (20260107 -> 20260609,
also carried here): that version switched Download to build from
GetDirectPath() instead of the embedded URL, so the lossy derivation
started biting. Returning path+query is exactly the protobuf DirectPath
whatsmeow expects; the discarded host is replaced by a media-conn host.

Verified end-to-end against the live bridge: a document (the family_vpn
log, 54712 B, decrypts to the real text) and a fresh uncached image
(576x1280 JPEG) both download where they previously 403'd. Adds a
table-driven regression test asserting the signed query survives.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vyakunin
vyakunin requested a review from jack-arturo as a code owner July 18, 2026 22:29
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.

1 participant