import type { StoredPlatformState } from "../platforms"; import { prepareBaijiahaoArticleHtml, prepareBaijiahaoMarkdown, } from "../../../../packages/publisher-platforms/src/baijiahao"; import { connectedPlatform, disconnectedPlatform, fetchImageBlob, fetchJson, fetchText, manageUrlResult, normalizeArticleHtml, uploadHtmlImages, } from "./common"; import type { AdapterContext, PlatformAdapter, PlatformPublishResult } from "./types"; type BaijiahaoAppInfoResponse = { data?: { user?: { userid?: string | number; name?: string; uname?: string; username?: string; avatar?: string; app_id?: string; }; }; }; type BaijiahaoPublishResponse = { errno?: number; errmsg?: string; ret?: { id?: string | number; }; }; type BaijiahaoUploadResponse = { ret?: { https_url?: string; }; }; type BaijiahaoCutResponse = { data?: { new_url?: string; }; }; 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["user"] | null> { const response = await fetchJson("https://baijiahao.baidu.com/builder/app/appinfo", { headers: { accept: "application/json, text/plain, */*", }, }).catch(() => null); return response?.data?.user ?? null; } async function detectBaijiahao(platform: StoredPlatformState): Promise { try { const user = await fetchAppInfo(); const uid = user?.userid != null ? String(user.userid) : ""; const nickname = user?.name || user?.uname || user?.username || ""; if (!uid || !nickname) { return disconnectedPlatform(platform, "未检测到百家号登录态"); } return connectedPlatform(platform, uid, nickname, user?.avatar ?? null); } catch (error) { return disconnectedPlatform(platform, error instanceof Error ? error.message : "百家号登录检测失败"); } } async function getPublishToken(): Promise { const html = await fetchText("https://baijiahao.baidu.com/builder/rc/edit?type=news", { headers: { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", }, }); return /window\.__BJH__INIT__AUTH__\s*=\s*(["'])(.*?)\1/.exec(html)?.[2] ?? ""; } async function uploadImage(sourceUrl: string, appId: string, cropCover: boolean): Promise { const blob = await fetchImageBlob(sourceUrl); if (!blob) { return null; } const form = new FormData(); form.append("media", blob, "image.png"); form.append("org_file_name", "image.png"); form.append("type", "image"); form.append("app_id", appId); form.append("is_waterlog", "0"); form.append("save_material", "1"); form.append("no_compress", "0"); form.append("article_type", "news"); const uploaded = await fetchJson("https://baijiahao.baidu.com/materialui/picture/uploadProxy", { method: "POST", body: form, }).catch(() => null); const uploadedUrl = uploaded?.ret?.https_url ?? null; if (!uploadedUrl) { return null; } if (!cropCover) { return uploadedUrl; } const cut = await fetchJson("https://baijiahao.baidu.com/materialui/picture/auto_cutting", { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ org_url: uploadedUrl, type: "news", cutting_type: "cover_image", }), }).catch(() => null); return cut?.data?.new_url ?? uploadedUrl; } async function publishBaijiahao(context: AdapterContext): Promise { const user = await fetchAppInfo(); const appId = user?.app_id ?? ""; if (!user?.userid || !appId) { throw new Error("baijiahao_not_logged_in"); } const token = await getPublishToken(); if (!token) { throw new Error("baijiahao_token_missing"); } const content = prepareBaijiahaoArticleHtml( normalizeArticleHtml(context.article, { prepareMarkdown: prepareBaijiahaoMarkdown, }), ); const processed = await uploadHtmlImages( content, async (sourceUrl) => uploadImage(sourceUrl, appId, false), ); 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" ? "https://baijiahao.baidu.com/pcui/article/publish" : "https://baijiahao.baidu.com/pcui/article/save"; const response = await fetchJson(endpoint, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded", Token: token, }, body: new URLSearchParams({ type: "news", title: context.article.title, content: processed.html, abstract_from: "1", 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: 0 }, { id: "reward", is_checked: 0 }, ]), }), }); const articleId = response?.ret?.id != null ? String(response.ret.id) : ""; if (response.errno !== 0 || !articleId) { throw new Error(response.errmsg || "baijiahao_publish_failed"); } const manageUrl = baijiahaoManageUrl(articleId); const articleUrl = baijiahaoPublicUrl(articleId); if (context.article.publish_type === "draft") { return manageUrlResult(true, "pending_review", articleId, manageUrl, "百家号草稿保存成功。"); } return manageUrlResult( true, "success", articleId, manageUrl, "百家号发布成功。", articleUrl, ); } export const baijiahaoAdapter: PlatformAdapter = { platformId: "baijiahao", detect: detectBaijiahao, publish: publishBaijiahao, };