Files
memind/scripts/news-item-wechat-commit.mjs
T
john 028bb18dc0 feat(news): add single news-item page generation and early page-ready wait
Scheduled tasks can finish once the expected HTML is on disk and stable,
instead of waiting for Goose Finish. Adds news-item templates and scripts
for generating a single-event page and committing it as a WeChat draft.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 10:07:32 +08:00

80 lines
2.9 KiB
JavaScript

#!/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 = '<svg xmlns="http://www.w3.org/2000/svg" width="900" height="900">'
+ '<rect width="900" height="900" fill="#1a1a2e"/>'
+ '<text x="50%" y="50%" fill="#fff" font-size="42" text-anchor="middle" dominant-baseline="middle">TKMind</text>'
+ '</svg>';
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);