Skip to content

fix: dedup duplicate publish dispatches and wait for Toutiao image upload#209

Open
xiaopanddxiong wants to merge 272 commits into
leaperone:mainfrom
xiaopanddxiong:fix/dedup-publish-and-image-upload-wait
Open

fix: dedup duplicate publish dispatches and wait for Toutiao image upload#209
xiaopanddxiong wants to merge 272 commits into
leaperone:mainfrom
xiaopanddxiong:fix/dedup-publish-and-image-upload-wait

Conversation

@xiaopanddxiong
Copy link
Copy Markdown

What & Why

While integrating MultiPost via the REST API for an automated publishing pipeline, I hit two reliability issues. Both are fixed here.

1. Same content gets published 2-3 times per task

Symptom: One REST API task triggers Chrome to instantly open 2-3 publish tabs, and the same content gets posted 2-3 times.

Root cause: When the extension receives NEW_TASK from ping, it opens multipost.app/on-task?taskId=.... That page then dispatches chrome.runtime.sendMessage({ action: "MULTIPOST_EXTENSION_PUBLISH", data }) — but it dispatches it multiple times (likely React StrictMode useEffect double-invocation, or other internal logic). The background handler had no dedup, so each dispatch ran the full publish flow.

I confirmed this by inspecting Service Worker network — only one ping returned NEW_TASK, but chrome.windows.create({ url: "tabs/publish.html" }) was being called 3 times.

Fix (src/background/index.ts):

  • Add a 60-second sliding-window dedup keyed by content fingerprint (platforms + image count + first 256 chars of content/title) on the MULTIPOST_EXTENSION_PUBLISH handler.
  • Duplicates within the window are dropped with a log line.

Also added a defensive in-memory dedup on NEW_TASK task URLs in src/background/services/api.ts (LRU-capped at 500 entries) to guard against ping returning the same task multiple times.

2. Toutiao dynamic post: image is missing when uploading large images

Symptom: For images larger than ~3-5 MB, the post goes live with no image attached.

Root cause: src/sync/dynamic/toutiao.ts had a hard-coded setTimeout(5000) between dispatching the file input and clicking the upload-confirm button. For large images, 5 seconds isn't enough — the confirm button is still disabled, the click is a no-op, and the post is published without the image.

Fix:

  • Replace the fixed 5s wait with a polling loop (max 60s) that waits for button[data-e2e="imageUploadConfirm-btn"] to become non-disabled before clicking.
  • After clicking confirm, wait (max 30s) for the modal to disappear and the inserted thumbnail to appear in the editor before proceeding.

How tested

  • Triggered a publish task via the REST API with a 5.4 MB PNG.
  • Before fix: 3 publish tabs spawned, post sent 3 times, image sometimes missing.
  • After fix: exactly 1 publish tab, 1 post on Toutiao with the image attached. Service Worker console shows [MultiPost] skip duplicate PUBLISH request for the suppressed dispatches.

Files changed

  • src/background/index.ts — content-fingerprint dedup for MULTIPOST_EXTENSION_PUBLISH
  • src/background/services/api.ts — task URL dedup for NEW_TASK
  • src/sync/dynamic/toutiao.ts — poll for upload completion instead of fixed 5s wait

No new dependencies, no behavioral change for the happy path.

cunoe and others added 30 commits February 7, 2025 21:56
* feat: add CSDN article sync support with image upload and publishing

* feat: add Article tab with content import and sync functionality

* chore: update project dependencies and eslint configuration

* feat: enhance article sync with cover image, digest, and platform selection

* feat: improve content processing and image handling during article sync
harryisfish and others added 24 commits November 28, 2025 16:00
- Update @mozilla/readability from 0.5.0 to 0.6.0
- Add pnpm overrides to force update vulnerable dependencies
- Fix 17 out of 19 security vulnerabilities (89% reduction)
- Remaining 2 vulnerabilities have no official patches yet

Security improvements:
- High severity: 6 → 0 (100% fixed)
- Moderate severity: 8 → 1 (87.5% fixed)
- Low severity: 4 → 1 (75% fixed)

Remaining vulnerabilities (development-only, no production impact):
- @parcel/reporter-dev-server (moderate)
- tsup (low)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
- Add dynamic/substack.ts for Notes publishing (supports images/videos)
- Add article/substack.ts for long-form article publishing (supports cover image)
- Register Substack in DynamicInfoMap and ArticleInfoMap
- Add i18n translations for Substack platform name

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: migrate from ESLint/Prettier/Stylelint to Biome

- Remove ESLint, Prettier, Stylelint and related dependencies
- Add @biomejs/biome for linting and formatting
- Update package.json scripts: lint, lint:fix, format
- Update lint-staged config to use biome
- Update VS Code extensions recommendations
- Format all source files with biome

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: use react-jsx transform to avoid explicit React imports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: change video inject functions return type to Promise<void>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: resolve all lint warnings

- Remove unreachable code in eastmoney.ts and dayu.ts
- Remove unused uploadImage function and imageFiles parameter in eastmoney.ts
- Remove unused QiEVideoUploader class definition in qie.ts
- Fix useless else in dayu.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
- Add post type button click after initial new post button
- Add input event dispatch for file uploads
- Use arrayBuffer instead of blob for file processing
- Add comprehensive debug logging for troubleshooting
- Fix autoPublish logic to properly trigger share button

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Use ClipboardEvent for pasting content instead of execCommand
- Update file input selector to match Facebook's exact accept attribute
- Use last file input element for uploads (matching reference implementation)
- Use arrayBuffer instead of blob for file processing
- Add input event dispatch after change event for uploads
- Search for editor by aria-placeholder to find correct textbox
- Fix autoPublish flag (was isAutoPublish)
- Add comprehensive debug logging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update trigger button selector to use top-bar container
- Find editor by data-placeholder attribute for better accuracy
- Use innerText instead of innerHTML for content input
- Combine images and videos into single mediaFiles array
- Add 8 file limit check with debug logging
- Fix autoPublish flag (was isAutoPublish)
- Add comprehensive debug logging
- Simplify code structure by removing separate uploadFiles function

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update selectors for Reddit's new UI components (shadowRoot access)
- Use faceplate-textarea-input for title input
- Use r-post-media-input for file uploads
- Click Image & Video tab when media files present
- Find content editor as third contenteditable div
- Add title length limit (300 chars)
- Use r-post-form-submit-button for submit
- Fix autoPublish flag (was isAutoPublish)
- Simplify code structure and add debug logging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change from TEXT to IMAGE type to match the updated publishing flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add Maimai (脉脉) dynamic publishing support
- Add Juejin (掘金) dynamic publishing support
- Update Juejin article publishing to use autoPublish flag
- Add i18n translations for Maimai platform

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Cast Element to HTMLElement for DOM properties like offsetParent,
click(), focus(), value.

Files fixed:
- alipay.ts, netease.ts, pinduoduo.ts, sohu.ts, vivovideo.ts,
  yiche.ts, yidian.ts: fix offsetParent and click() type errors
- chejiahao.ts: fix multiple type issues including NodeListOf casts
- dayu.ts: fix maxlength -> maxLength and NodeListOf cast

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix autoPublish -> isAutoPublish in 7 platform sync files
- Fix ArticleData.content -> htmlContent in article/juejin.ts
- Fix webhook.ts content property type access
- Remove deprecated index_old.tsx
- Update options page to always redirect to multipost.app

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add developer documentation link
- Update platform list with YouTube
- Remove deprecated scraper features section
Removed Twitter and Bento.me contact links from README.
- Simplify drag-and-drop simulation in dynamic Kuaishou publisher
- Add autoPublish check before clicking publish button in video Kuaishou
- Use production BASE_URL consistently in popup instead of localhost in dev

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…#208)

* fix: resolve dependabot security alerts via pnpm overrides

Override vulnerable transitive dependencies to patched versions:
- rollup: >=4.59.0 (arbitrary file write via path traversal)
- svelte: >=5.53.5 (multiple SSR XSS vulnerabilities)
- minimatch: >=10.2.3 (ReDoS)
- ajv: >=8.18.0 (ReDoS with $data option)
- devalue: >=5.6.3 (DoS via memory/CPU exhaustion)
- @isaacs/brace-expansion: >=5.0.1 (uncontrolled resource consumption)
- lodash: >=4.17.23 (prototype pollution)

Note: @parcel/reporter-dev-server and tsup have no patched versions
available yet (dev-only dependencies, lower risk).

* fix: correct overrides after review

- Revert svelte override to ^4.2.19 (v5 is breaking change, v5-only
  CVEs don't apply to this project)
- Remove lodash override (>=4.17.23 caused lodash to be dropped from
  tree instead of upgraded; lodash is optional dep, low risk)
- Remove @isaacs/brace-expansion override (package not in dependency
  tree, override had no effect)

---------

Co-authored-by: Harry <harry@Harrys-Mac-mini.local>
1) src/background/index.ts: 对 MULTIPOST_EXTENSION_PUBLISH 增加指纹去重
   (60s 窗口, key=平台+图片数+内容前256字符), 解决 multipost.app
   on-task 网页对扩展重复 dispatch 同一发布请求导致同条内容被发布
   多次的问题.

2) src/background/services/api.ts: 对 NEW_TASK 的 task URL 增加内存
   级去重 (LRU 上限 500), 兜底 ping 多次返回同一 taskId 的情况.

3) src/sync/dynamic/toutiao.ts: 将固定 5s 等待替换为轮询确认按钮
   可点击 (最长 60s) + 等待弹窗关闭与缩略图出现 (最长 30s),
   解决大图(>5MB) 上传未完成就触发发布按钮导致图片丢失的问题.
@leaperone-bot
Copy link
Copy Markdown

leaperone-bot commented May 3, 2026

🤖 Leo

Code Review

⏳ 等待审查...

最后更新: 2026-05-07 15:56 · beb348c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants