From f06a9021a05799ff9625b7ee0705b032a0149532 Mon Sep 17 00:00:00 2001 From: NTLx Date: Fri, 29 May 2026 13:20:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(baoyu-post-to-wechat):=20add=20content=5Fs?= =?UTF-8?q?ource=5Furl=20support=20for=20"=E9=98=85=E8=AF=BB=E5=8E=9F?= =?UTF-8?q?=E6=96=87"=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the ability to specify the original article URL ("阅读原文" / "Read Original" link) when publishing to WeChat Official Account draft via API. Changes: - ArticleOptions interface: add contentSourceUrl optional field - publishToDraft: conditionally write content_source_url for news articles - CLI: add --source-url parameter - Frontmatter: extract sourceUrl / contentSourceUrl / content_source_url - Priority chain: CLI --source-url → frontmatter → (none) - SKILL.md: document the new option and draft/add payload rule The field is only written for article_type=news per WeChat API spec. content_source_url is optional (max 1KB) per the official draft/add API. Co-Authored-By: Claude Opus 4.7 --- skills/baoyu-post-to-wechat/SKILL.md | 4 +++- skills/baoyu-post-to-wechat/scripts/wechat-api.ts | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/skills/baoyu-post-to-wechat/SKILL.md b/skills/baoyu-post-to-wechat/SKILL.md index d1ac7c6..b8c52f1 100644 --- a/skills/baoyu-post-to-wechat/SKILL.md +++ b/skills/baoyu-post-to-wechat/SKILL.md @@ -180,6 +180,7 @@ Ask method unless specified in EXTEND.md or CLI: | Title | Ask, or press Enter to auto-generate from content | | Summary | Frontmatter `description` → `summary` → ask or auto-generate | | Author | CLI `--author` → frontmatter `author` → EXTEND.md `default_author` | +| Source URL | CLI `--source-url` → frontmatter `sourceUrl`/`contentSourceUrl`/`content_source_url` | Auto-generation: title = first H1/H2 or first sentence; summary = first paragraph, truncated to 120 chars. @@ -194,7 +195,7 @@ Auto-generation: title = first H1/H2 or first sentence; summary = first paragrap **API method** (accepts `.md` or `.html`): ```bash -${BUN_X} {baseDir}/scripts/wechat-api.ts --theme [--color ] [--title ] [--summary <summary>] [--author <author>] [--cover <cover_path>] [--no-cite] +${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme <theme> [--color <color>] [--title <title>] [--summary <summary>] [--author <author>] [--cover <cover_path>] [--source-url <url>] [--no-cite] ``` Always pass `--theme` even if it's `default`. Only pass `--color` when explicitly set by the user or EXTEND.md. @@ -212,6 +213,7 @@ Any `--remote-*` flag implies `--remote`. CLI values override account-level then - `article_type`: `news` (default) or `newspic` - For `news`, include `thumb_media_id` (cover required) - Always include `need_open_comment` (default `1`) and `only_fans_can_comment` (default `0`) in the request body, even if the CLI doesn't expose them +- For `news`, optionally include `content_source_url` (original article URL, shown as "阅读原文" link, max 1KB). Provide via `--source-url` CLI flag or frontmatter `sourceUrl`/`contentSourceUrl`/`content_source_url` **Browser method** (accepts `--markdown` or `--html`): diff --git a/skills/baoyu-post-to-wechat/scripts/wechat-api.ts b/skills/baoyu-post-to-wechat/scripts/wechat-api.ts index 9097ff5..5575b74 100644 --- a/skills/baoyu-post-to-wechat/scripts/wechat-api.ts +++ b/skills/baoyu-post-to-wechat/scripts/wechat-api.ts @@ -64,6 +64,7 @@ interface ArticleOptions { content: string; thumbMediaId: string; articleType: ArticleType; + contentSourceUrl?: string; imageMediaIds?: string[]; needOpenComment?: number; onlyFansCanComment?: number; @@ -379,6 +380,7 @@ async function publishToDraft( }; if (options.author) article.author = options.author; if (options.digest) article.digest = options.digest; + if (options.contentSourceUrl) article.content_source_url = options.contentSourceUrl; } const res = await client(url, { @@ -479,6 +481,7 @@ Options: --title <title> Override title --author <name> Author name (max 16 chars) --summary <text> Article summary/digest (max 128 chars) + --source-url <url> Original article URL ("阅读原文" link, max 1KB) --theme <name> Theme name for markdown (default, grace, simple, modern). Default: default --color <name|hex> Primary color (blue, green, vermilion, etc. or hex) --cover <path> Cover image path (local or URL) @@ -500,6 +503,7 @@ Frontmatter Fields (markdown): title Article title author Author name digest/summary Article summary + sourceUrl/contentSourceUrl/content_source_url Original article URL coverImage/featureImage/cover/image Cover image path Comments: @@ -517,7 +521,7 @@ Config File Locations (in priority order): Example: npx -y bun wechat-api.ts article.md npx -y bun wechat-api.ts article.md --theme grace --cover cover.png - npx -y bun wechat-api.ts article.md --author "Author Name" --summary "Brief intro" + npx -y bun wechat-api.ts article.md --author "Author Name" --summary "Brief intro" --source-url "https://example.com/original" npx -y bun wechat-api.ts article.html --title "My Article" npx -y bun wechat-api.ts images/ --type newspic --title "Photo Album" npx -y bun wechat-api.ts article.md --dry-run @@ -533,6 +537,7 @@ interface CliArgs { title?: string; author?: string; summary?: string; + sourceUrl?: string; theme: string; color?: string; cover?: string; @@ -578,6 +583,8 @@ function parseArgs(argv: string[]): CliArgs { args.author = argv[++i]; } else if (arg === "--summary" && argv[i + 1]) { args.summary = argv[++i]; + } else if (arg === "--source-url" && argv[i + 1]) { + args.sourceUrl = argv[++i]; } else if (arg === "--theme" && argv[i + 1]) { args.theme = argv[++i]!; } else if (arg === "--color" && argv[i + 1]) { @@ -692,6 +699,7 @@ async function main(): Promise<void> { let title = args.title || ""; let author = args.author || ""; let digest = args.summary || ""; + let sourceUrl = args.sourceUrl || ""; let htmlPath: string; let htmlContent: string; let frontmatter: Record<string, string> = {}; @@ -708,6 +716,7 @@ async function main(): Promise<void> { if (!title && frontmatter.title) title = frontmatter.title; if (!author) author = frontmatter.author || ""; if (!digest) digest = frontmatter.digest || frontmatter.summary || frontmatter.description || ""; + if (!sourceUrl) sourceUrl = frontmatter.sourceUrl || frontmatter.contentSourceUrl || frontmatter.content_source_url || ""; } if (!title) { title = extractHtmlTitle(fs.readFileSync(htmlPath, "utf-8")); @@ -726,6 +735,7 @@ async function main(): Promise<void> { } if (!author) author = frontmatter.author || ""; if (!digest) digest = frontmatter.digest || frontmatter.summary || frontmatter.description || ""; + if (!sourceUrl) sourceUrl = frontmatter.sourceUrl || frontmatter.contentSourceUrl || frontmatter.content_source_url || ""; console.error(`[wechat-api] Theme: ${args.theme}${args.color ? `, color: ${args.color}` : ""}, citeStatus: ${args.citeStatus}`); const rendered = renderMarkdownWithPlaceholders(filePath, args.theme, args.color, args.citeStatus, args.title); @@ -754,6 +764,7 @@ async function main(): Promise<void> { console.error(`[wechat-api] Title: ${title}`); if (author) console.error(`[wechat-api] Author: ${author}`); if (digest) console.error(`[wechat-api] Digest: ${digest.slice(0, 50)}...`); + if (sourceUrl) console.error(`[wechat-api] Source URL: ${sourceUrl}`); console.error(`[wechat-api] Type: ${args.articleType}`); const extConfig = loadWechatExtendConfig(); @@ -768,6 +779,7 @@ async function main(): Promise<void> { title, author: author || undefined, digest: digest || undefined, + sourceUrl: sourceUrl || undefined, htmlPath, contentLength: htmlContent.length, placeholderImageCount: contentImages.length || undefined, @@ -839,6 +851,7 @@ async function main(): Promise<void> { content: htmlContent, thumbMediaId, articleType: args.articleType, + contentSourceUrl: sourceUrl || undefined, imageMediaIds: args.articleType === "newspic" ? imageMediaIds : undefined, needOpenComment: resolved.need_open_comment, onlyFansCanComment: resolved.only_fans_can_comment,