From 2007c0c060108fdc9dcb8db59dc94e403d48ca92 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Tue, 17 Feb 2026 10:36:47 -0800 Subject: [PATCH] feat(channels): emit line and zalo binary attachment fallback notices --- docs/plans/state.json | 14 ++++++++++++++ src/channels/line/adapter.test.ts | 4 +++- src/channels/line/adapter.ts | 7 +++++++ src/channels/zalo/adapter.test.ts | 4 +++- src/channels/zalo/adapter.ts | 7 +++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/docs/plans/state.json b/docs/plans/state.json index 94c63c9..47d6132 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -3646,6 +3646,20 @@ "docs/plans/state.json" ], "test_status": "pnpm test:run src/skills/planner.test.ts src/cli/skills.test.ts + pnpm typecheck passing" + }, + "line-zalo-binary-fallback-notices": { + "status": "completed", + "date": "2026-02-17", + "updated": "2026-02-17", + "summary": "Improved LINE/Zalo binary attachment handling by emitting recipient-visible fallback messages whenever raw binary upload is unavailable, preventing silent loss while preserving warning logs.", + "files_modified": [ + "src/channels/line/adapter.ts", + "src/channels/line/adapter.test.ts", + "src/channels/zalo/adapter.ts", + "src/channels/zalo/adapter.test.ts", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/channels/line/adapter.test.ts src/channels/zalo/adapter.test.ts + pnpm typecheck passing" } }, "overall_progress": { diff --git a/src/channels/line/adapter.test.ts b/src/channels/line/adapter.test.ts index 6dd908d..fb65de8 100644 --- a/src/channels/line/adapter.test.ts +++ b/src/channels/line/adapter.test.ts @@ -95,9 +95,11 @@ describe('LineAdapter', () => { ], }); - expect(mockFetch).toHaveBeenCalledTimes(2); + expect(mockFetch).toHaveBeenCalledTimes(3); const secondBody = JSON.parse(String(mockFetch.mock.calls[1]?.[1]?.body ?? '{}')); + const thirdBody = JSON.parse(String(mockFetch.mock.calls[2]?.[1]?.body ?? '{}')); expect(secondBody.messages?.[0]?.text).toBe('file.txt: https://example.com/file.txt'); + expect(thirdBody.messages?.[0]?.text).toBe('[LINE] Binary attachment not uploaded yet: attachment (image/png).'); expect(warnSpy).toHaveBeenCalledWith('LINE: skipping attachment data (image/png) — upload not implemented'); warnSpy.mockRestore(); }); diff --git a/src/channels/line/adapter.ts b/src/channels/line/adapter.ts index 844e8ac..dad795c 100644 --- a/src/channels/line/adapter.ts +++ b/src/channels/line/adapter.ts @@ -90,6 +90,7 @@ export class LineAdapter implements ChannelAdapter { } if (attachment.data) { console.warn(`LINE: skipping attachment data (${attachment.mimeType}) — upload not implemented`); + await this.sendPush(peerId, formatBinaryAttachmentNotice('LINE', attachment.filename, attachment.mimeType)); } } } @@ -240,3 +241,9 @@ export class LineAdapter implements ChannelAdapter { function escapeRegex(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } + +function formatBinaryAttachmentNotice(channel: string, filename?: string, mimeType?: string): string { + const name = filename || 'attachment'; + const type = mimeType || 'application/octet-stream'; + return `[${channel}] Binary attachment not uploaded yet: ${name} (${type}).`; +} diff --git a/src/channels/zalo/adapter.test.ts b/src/channels/zalo/adapter.test.ts index af62bec..346d434 100644 --- a/src/channels/zalo/adapter.test.ts +++ b/src/channels/zalo/adapter.test.ts @@ -81,9 +81,11 @@ describe('ZaloAdapter', () => { ], }); - expect(mockFetch).toHaveBeenCalledTimes(2); + expect(mockFetch).toHaveBeenCalledTimes(3); const secondBody = JSON.parse(String(mockFetch.mock.calls[1]?.[1]?.body ?? '{}')); + const thirdBody = JSON.parse(String(mockFetch.mock.calls[2]?.[1]?.body ?? '{}')); expect(secondBody.message?.text).toBe('file.txt: https://example.com/file.txt'); + expect(thirdBody.message?.text).toBe('[Zalo] Binary attachment not uploaded yet: attachment (application/pdf).'); expect(warnSpy).toHaveBeenCalledWith('Zalo: skipping attachment data (application/pdf) — upload not implemented'); warnSpy.mockRestore(); }); diff --git a/src/channels/zalo/adapter.ts b/src/channels/zalo/adapter.ts index bceb384..de0471f 100644 --- a/src/channels/zalo/adapter.ts +++ b/src/channels/zalo/adapter.ts @@ -80,6 +80,7 @@ export class ZaloAdapter implements ChannelAdapter { } if (attachment.data) { console.warn(`Zalo: skipping attachment data (${attachment.mimeType}) — upload not implemented`); + await this.sendText(peerId, formatBinaryAttachmentNotice('Zalo', attachment.filename, attachment.mimeType)); } } } @@ -189,3 +190,9 @@ export class ZaloAdapter implements ChannelAdapter { function escapeRegex(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } + +function formatBinaryAttachmentNotice(channel: string, filename?: string, mimeType?: string): string { + const name = filename || 'attachment'; + const type = mimeType || 'application/octet-stream'; + return `[${channel}] Binary attachment not uploaded yet: ${name} (${type}).`; +}