feat(publish): add baijiahao and jianshu desktop adapters
Wires up Baijiahao (百家号) and Jianshu (简书) as first-class desktop publish targets, with risk-control prompts surfaced in the runtime controller and a normalized error message in publish records. Adds external-link buttons in the publish management table, an asset format conversion endpoint for cover image compatibility, and reorders publish-status display priority so failures take precedence over partial successes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import type { StoredPlatformState } from "../platforms";
|
||||
import {
|
||||
prepareBaijiahaoArticleHtml,
|
||||
prepareBaijiahaoMarkdown,
|
||||
} from "../../../../packages/publisher-platforms/src/baijiahao";
|
||||
|
||||
import {
|
||||
connectedPlatform,
|
||||
@@ -45,6 +49,14 @@ type BaijiahaoCutResponse = {
|
||||
};
|
||||
};
|
||||
|
||||
function baijiahaoManageUrl(articleId: string): string {
|
||||
return `https://baijiahao.baidu.com/builder/rc/edit?type=news&article_id=${encodeURIComponent(articleId)}`;
|
||||
}
|
||||
|
||||
function baijiahaoPublicUrl(articleId: string): string {
|
||||
return `https://baijiahao.baidu.com/s?id=${encodeURIComponent(articleId)}`;
|
||||
}
|
||||
|
||||
async function fetchAppInfo(): Promise<NonNullable<BaijiahaoAppInfoResponse["data"]>["user"] | null> {
|
||||
const response = await fetchJson<BaijiahaoAppInfoResponse>("https://baijiahao.baidu.com/builder/app/appinfo", {
|
||||
headers: {
|
||||
@@ -135,16 +147,25 @@ async function publishBaijiahao(context: AdapterContext): Promise<PlatformPublis
|
||||
throw new Error("baijiahao_token_missing");
|
||||
}
|
||||
|
||||
const content = normalizeArticleHtml(context.article);
|
||||
const content = prepareBaijiahaoArticleHtml(
|
||||
normalizeArticleHtml(context.article, {
|
||||
prepareMarkdown: prepareBaijiahaoMarkdown,
|
||||
}),
|
||||
);
|
||||
const processed = await uploadHtmlImages(
|
||||
content,
|
||||
async (sourceUrl) => uploadImage(sourceUrl, appId, false),
|
||||
["baijiahao.baidu.com", "bdstatic.com", "bcebos.com"],
|
||||
);
|
||||
|
||||
const coverUrl = context.article.cover_asset_url?.trim()
|
||||
? await uploadImage(context.article.cover_asset_url.trim(), appId, true)
|
||||
: null;
|
||||
const coverAssetUrl = context.article.cover_asset_url?.trim() || "";
|
||||
if (!coverAssetUrl) {
|
||||
throw new Error("publish_cover_required");
|
||||
}
|
||||
|
||||
const coverUrl = await uploadImage(coverAssetUrl, appId, true);
|
||||
if (!coverUrl) {
|
||||
throw new Error("baijiahao_cover_upload_failed");
|
||||
}
|
||||
|
||||
const endpoint =
|
||||
context.article.publish_type === "publish"
|
||||
@@ -162,22 +183,20 @@ async function publishBaijiahao(context: AdapterContext): Promise<PlatformPublis
|
||||
title: context.article.title,
|
||||
content: processed.html,
|
||||
abstract_from: "1",
|
||||
cover_layout: coverUrl ? "one" : "",
|
||||
cover_images: coverUrl
|
||||
? JSON.stringify([
|
||||
{
|
||||
src: coverUrl,
|
||||
cropData: {},
|
||||
machine_chooseimg: 0,
|
||||
isLegal: 0,
|
||||
cover_source_tag: "local",
|
||||
},
|
||||
])
|
||||
: "",
|
||||
cover_layout: "one",
|
||||
cover_images: JSON.stringify([
|
||||
{
|
||||
src: coverUrl,
|
||||
cropData: {},
|
||||
machine_chooseimg: 0,
|
||||
isLegal: 0,
|
||||
cover_source_tag: "local",
|
||||
},
|
||||
]),
|
||||
activity_list: JSON.stringify([
|
||||
{ id: "ttv", is_checked: 0 },
|
||||
{ id: "ai_tts", is_checked: 0 },
|
||||
{ id: "aigc_bjh_status", is_checked: 1 },
|
||||
{ id: "aigc_bjh_status", is_checked: 0 },
|
||||
{ id: "reward", is_checked: 0 },
|
||||
]),
|
||||
}),
|
||||
@@ -188,7 +207,8 @@ async function publishBaijiahao(context: AdapterContext): Promise<PlatformPublis
|
||||
throw new Error(response.errmsg || "baijiahao_publish_failed");
|
||||
}
|
||||
|
||||
const manageUrl = `https://baijiahao.baidu.com/builder/rc/edit?type=news&article_id=${articleId}`;
|
||||
const manageUrl = baijiahaoManageUrl(articleId);
|
||||
const articleUrl = baijiahaoPublicUrl(articleId);
|
||||
if (context.article.publish_type === "draft") {
|
||||
return manageUrlResult(true, "pending_review", articleId, manageUrl, "百家号草稿保存成功。");
|
||||
}
|
||||
@@ -198,8 +218,8 @@ async function publishBaijiahao(context: AdapterContext): Promise<PlatformPublis
|
||||
"success",
|
||||
articleId,
|
||||
manageUrl,
|
||||
"百家号发布成功,公开链接待补查。",
|
||||
null,
|
||||
"百家号发布成功。",
|
||||
articleUrl,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,9 +111,16 @@ export function markdownToHtml(markdown: string): string {
|
||||
}) as string;
|
||||
}
|
||||
|
||||
export function normalizeArticleHtml(article: PublisherPublishArticleRequest): string {
|
||||
export interface NormalizeArticleHtmlOptions {
|
||||
prepareMarkdown?: (markdown: string) => string;
|
||||
}
|
||||
|
||||
export function normalizeArticleHtml(
|
||||
article: PublisherPublishArticleRequest,
|
||||
options: NormalizeArticleHtmlOptions = {},
|
||||
): string {
|
||||
const markdown = article.markdown_content?.trim();
|
||||
const source = markdown ? markdownToHtml(markdown) : article.html_content?.trim() || "";
|
||||
const source = markdown ? markdownToHtml(options.prepareMarkdown?.(markdown) ?? markdown) : article.html_content?.trim() || "";
|
||||
let next = source.trim();
|
||||
next = next.replace(/<section\b/gi, "<div").replace(/<\/section>/gi, "</div>");
|
||||
next = next.replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi, "");
|
||||
|
||||
@@ -136,6 +136,8 @@ export function buildPublishedUrl(platformId: string, externalArticleId: string)
|
||||
return `https://zhuanlan.zhihu.com/p/${externalArticleId}`;
|
||||
case "jianshu":
|
||||
return `https://www.jianshu.com/p/${externalArticleId}`;
|
||||
case "baijiahao":
|
||||
return `https://baijiahao.baidu.com/s?id=${externalArticleId}`;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user