#!/usr/bin/env node /** * 阶段三:把用户确认过的正文原样提交到公众号草稿箱。 * * 这里刻意不做任何 HTML 转换——content 就是预览页显示的那一份,由 news-engine 的 * 块渲染器产出。任何在这里的二次加工都会破坏「预览即草稿」。 * * stdin { userId, title, author?, digest, content, contentSourceUrl, thumb: { url?, localPath? } } * stdout { ok, draftMediaId } */ import fs from 'node:fs'; import process from 'node:process'; import { addWechatDraftArticle, uploadWechatPermanentThumb, } from '../wechat-news-morning-draft.mjs'; import { createLightContext, loadPushEnvironment, readStdinPayload, runScript, } from './news-item-wechat-context.mjs'; const PLACEHOLDER_SVG = '' + '' + 'TKMind' + ''; async function loadThumbBuffer(thumb) { const sharp = (await import('sharp')).default; let raw = null; const localPath = String(thumb?.localPath ?? '').trim(); const url = String(thumb?.url ?? '').trim(); if (localPath && fs.existsSync(localPath)) { raw = fs.readFileSync(localPath); } else if (/^https?:\/\//i.test(url)) { const response = await fetch(url, { signal: AbortSignal.timeout(20_000) }); if (response.ok) raw = Buffer.from(await response.arrayBuffer()); } if (!raw) return sharp(Buffer.from(PLACEHOLDER_SVG)).png().toBuffer(); return sharp(raw).resize(900, 900, { fit: 'cover' }).png().toBuffer(); } async function main() { const env = loadPushEnvironment(process.env); const payload = readStdinPayload(); const userId = String(payload.userId ?? '').trim(); const title = String(payload.title ?? '').trim(); const content = String(payload.content ?? '').trim(); if (!userId) throw new Error('缺少 userId'); if (!title) throw new Error('缺少草稿标题'); if (!content) throw new Error('缺少草稿正文'); const context = createLightContext(env); try { const { credentials, accessToken } = await context.resolveWechat(userId); const { wechatFetch } = context; const thumbMediaId = await uploadWechatPermanentThumb( accessToken, await loadThumbBuffer(payload.thumb), { wechatFetch }, ); const draft = await addWechatDraftArticle(accessToken, { title: title.slice(0, 64), author: String(payload.author ?? '').trim() || credentials.author || 'TKMind', digest: String(payload.digest ?? '').slice(0, 120), content, content_source_url: String(payload.contentSourceUrl ?? '').trim() || undefined, thumb_media_id: thumbMediaId, need_open_comment: 0, only_fans_can_comment: 0, }, { wechatFetch }); return { draftMediaId: draft.draftMediaId, thumbMediaId }; } finally { await context.close(); } } runScript(main);