diff --git a/news-morning-templates.mjs b/news-morning-templates.mjs
index b30dbcc..64f4875 100644
--- a/news-morning-templates.mjs
+++ b/news-morning-templates.mjs
@@ -528,6 +528,7 @@ export const NEWS002_FRESH_CONTENT_SPEC = [
'8. 标题规则:中文 15~26 字;英文来源须转写为中文标题,可在括号内保留英文原题;每条必须可核验并写来源 URL。',
'9. 禁止复制、改写历史 daily-news-*.html 的正文;旧稿仅作 HTML 结构参考。',
'10. 禁止八卦绯闻、未经证实的传闻、标题党;每条都要能说清「为什么重要」,说不清就不要写。',
+ '11. 禁止色情低俗、成人站点、吃瓜黑料、无信息量网址标题;命中则丢弃该条,继续写其它新闻,禁止因此整页失败。',
].join('\n');
/* ------------------------------------------------------------------ *
diff --git a/package.json b/package.json
index f33eae2..3b675e9 100644
--- a/package.json
+++ b/package.json
@@ -107,7 +107,7 @@
"test": "node --test api-core-retry.test.mjs auth.test.mjs asr-proxy.test.mjs billing.test.mjs billing-token-state.test.mjs billing-recharge.test.mjs wechat-pay.test.mjs wechat-oauth.test.mjs wechat-voice-reco.test.mjs wechat-mp.test.mjs wechat-media.test.mjs wechat/image-generation-policy.test.mjs wechat/verify/generated-thumbnail.test.mjs schedule-intent.test.mjs schedule-reminder-worker.test.mjs scheduled-task-intent.test.mjs scheduled-task-service.test.mjs scheduled-task-executor.test.mjs scheduled-task-worker.test.mjs scheduled-task-worker-config.test.mjs wechat-news-morning-draft.test.mjs wechat-news-morning-draft-worker.test.mjs wechat-news-morning-draft-worker-config.test.mjs wechat/handlers/scheduled-task.test.mjs capabilities.test.mjs policies.test.mjs server/portal-api-auth-middleware.test.mjs server/portal-config-routes.test.mjs server/portal-plaza-discovery-routes.test.mjs server/portal-runtime-routes.test.mjs server/portal-gateway-services-bootstrap.test.mjs chat-skills.test.mjs chat-intent-router.test.mjs chat-finish-sync.test.mjs chat-agent-run-gate.test.mjs conversation-display.test.mjs user-publish.test.mjs user-memory-profile.test.mjs skills-registry.test.mjs skill-runtime-policy.test.mjs excel-analyst.test.mjs agent-run-gateway.test.mjs agent-run-routes.test.mjs session-broker.test.mjs sse-event-taxonomy.test.mjs goosed-proxy-boundary.test.mjs agent-run-stream.test.mjs mindspace-h5-html-finish-guard.test.mjs admin-routes.test.mjs services/orchestrator/admin-config.test.mjs services/orchestrator/contracts.test.mjs services/orchestrator/checkpoint.test.mjs services/orchestrator/runtime.test.mjs services/orchestrator/app.test.mjs services/orchestrator/server.test.mjs services/orchestrator/shadow-dispatcher.test.mjs services/orchestrator/shadow-observer.test.mjs services/orchestrator/observability.test.mjs services/orchestrator/executor-gateway.test.mjs services/orchestrator/executor-job-store.test.mjs image-make-admin-config.test.mjs asset-gateway.test.mjs image-make-client.test.mjs mindspace-image-generation.test.mjs mindspace-image-generation-routes.test.mjs mindspace-image-review.test.mjs mindspace-run-public-html-scope.test.mjs direct-chat-service.test.mjs tool-gateway.test.mjs mindspace.test.mjs health-channel-state.test.mjs health-intent-rules.test.mjs health-extraction.test.mjs health-observation-validate.test.mjs health-baseline-maturity.test.mjs health-publish-guard.test.mjs health-p0-experiment.test.mjs health-wechat-channel.test.mjs server/portal-health-routes.test.mjs mindspace-scan.test.mjs mindspace-assets.test.mjs mindspace-local-runtime-services.test.mjs mindspace-local-server-adapter.test.mjs mindspace-public-asset-token.test.mjs mindspace-remote-server-adapter.test.mjs mindspace-server-adapter.test.mjs mindspace-pages.test.mjs mindspace-page-sync-service.test.mjs public-site-bases.test.mjs mindspace-html-download-links.test.mjs mindspace-long-image.test.mjs mindspace-page-purge.test.mjs mindspace-public-delivery.test.mjs mindspace-public-page-context.test.mjs mindspace-published-page-csp.test.mjs mindspace-published-script-localize.test.mjs agent-run-deliverable-check.test.mjs mindspace-public-route.test.mjs mindspace-publications.test.mjs mindspace-public-links.test.mjs mindspace-chat-save.test.mjs mindspace-chat-save-service.test.mjs mindspace-chat-docx-package.test.mjs mindspace-public-finish-sync.test.mjs mindspace-wechat-html-delivery.test.mjs mindspace-wechat-mp-config.test.mjs mindspace-wechat-page-draft.test.mjs server/portal-mindspace-wechat-routes.test.mjs mindspace-chat-context.test.mjs mindspace-canonical-url.test.mjs mindspace-conversation-package.test.mjs mindspace-conversation-package-artifact-service.test.mjs mindspace-conversation-package-audit.test.mjs mindspace-conversation-package-backfill.test.mjs mindspace-conversation-package-public-html.test.mjs mindspace-conversation-package-verify.test.mjs mindspace-conversation-package-registry.test.mjs mindspace-conversation-package-routes.test.mjs mindspace-conversation-package-store.test.mjs mindspace-conversation-schema.test.mjs mindspace-runtime-config.test.mjs mindspace-config.test.mjs mindspace-analytics.test.mjs mindspace-service.test.mjs mindspace-storage-adapter.test.mjs mindspace-content-scan.test.mjs mindspace-html-localize.test.mjs mindspace-visual-editor.test.mjs mindspace-cleanup.test.mjs mindspace-thumbnails.test.mjs mindspace-workspace-thumbnails.test.mjs mindspace-workspace-sync.test.mjs mindspace-workspace-publication-delivery-service.test.mjs mindspace-workspace-tool-service.test.mjs mindspace-mcp-scoped-token.test.mjs mindspace-asset-preview.test.mjs mindspace-agent-jobs.test.mjs mindspace-agent-runner.test.mjs mindspace-sandbox-mcp.test.mjs mindspace-userdata-postgres.test.mjs postgres-user-data-space-service.test.mjs user-data-space-service.test.mjs page-data-routes.test.mjs page-access-policy.test.mjs page-access-visitor.test.mjs page-data-public-service.test.mjs page-data-integration.test.mjs page-data-log-store.test.mjs page-data-ops.test.mjs page-data-session-store.test.mjs page-data-browser-client.test.mjs page-data-policy-index.test.mjs message-stream.test.mjs mindspace-service/mindspace-rpc-server.test.mjs plaza-posts.test.mjs plaza-interactions.test.mjs plaza-algorithm.test.mjs plaza-seo.test.mjs plaza-ops.test.mjs user-auth.test.mjs llm-providers.test.mjs admin-guard.test.mjs user-feedback.test.mjs memory-v2.test.mjs memory-v2-admin-config.test.mjs memory-v2-lifecycle.test.mjs memory-v2-adapter-scaffold.test.mjs memory-v2-backend-contract.test.mjs memory-v2-health.test.mjs memory-v2-runtime.test.mjs memory-v2-plugin-backends.test.mjs memory-v2-pgvector.test.mjs memory-v2-pgvector-schema.test.mjs memory-v2-pgvector-backfill.test.mjs memory-v2-pgvector-smoke.test.mjs memory-v2-qdrant.test.mjs memory-v2-weaviate.test.mjs memory-v2-mem0.test.mjs memory-v2-letta.test.mjs memory-v2-external-adapters.test.mjs scripts/embed-memory-v2-local-hash.test.mjs scripts/check-memory-v2-app-canary.test.mjs scripts/check-memory-v2-config-gaps.test.mjs scripts/check-memory-v2-contracts.test.mjs scripts/check-memory-v2-health.test.mjs scripts/check-memory-v2-session-flow.test.mjs scripts/check-memory-v2-stack.test.mjs scripts/setup-memory-v2-pgvector-schema.test.mjs scripts/backfill-memory-v2-pgvector.test.mjs scripts/scaffold-memory-v2-backend.test.mjs scripts/smoke-memory-v2-pgvector.test.mjs scripts/smoke-memory-v2-qdrant.test.mjs scripts/smoke-memory-v2-external.test.mjs scripts/mock-memory-v2-services.test.mjs",
"test:episodic-memory": "node --test episodic-memory.test.mjs direct-chat-service.test.mjs chat-intent-router.test.mjs",
"test:deep-search": "node --test deep-search.test.mjs mindsearch.test.mjs",
- "test:news-engine": "node --test news-engine-client.test.mjs wechat-news-morning-search.test.mjs",
+ "test:news-engine": "node --test news-engine-client.test.mjs wechat-news-morning-search.test.mjs wechat-news-morning-content-filter.test.mjs wechat-news-morning-images.test.mjs wechat-news-morning-searxng-fallback.test.mjs",
"verify:news-engine-integration": "node scripts/verify-news-engine-integration.mjs",
"test:image-review": "node --test mindspace-image-review.test.mjs mindspace-image-generation.test.mjs",
"test:mindspace-service": "node --test mindspace-service/mindspace-rpc-server.test.mjs",
diff --git a/scripts/push-wechat-news-morning-draft-now.mjs b/scripts/push-wechat-news-morning-draft-now.mjs
index ed89487..7782342 100644
--- a/scripts/push-wechat-news-morning-draft-now.mjs
+++ b/scripts/push-wechat-news-morning-draft-now.mjs
@@ -34,6 +34,7 @@ async function ensureDraftConfig(service) {
async function main() {
const dryRun = process.argv.includes('--dry-run');
const allowYesterday = process.argv.includes('--allow-yesterday');
+ const skipQualityGate = process.argv.includes('--skip-gate');
if (!process.env.DATABASE_URL) {
console.error('DATABASE_URL missing');
process.exit(1);
@@ -59,6 +60,7 @@ async function main() {
console.log(JSON.stringify({
dryRun,
allowYesterday,
+ skipQualityGate,
sourceUserId: config.sourceUserId,
templateId: config.templateId ?? null,
templateVersion: config.templateVersion ?? null,
@@ -87,7 +89,8 @@ async function main() {
const result = await service.pushDraft({
requireToday: !allowYesterday,
- triggeredBy: 'manual-push-now',
+ triggeredBy: skipQualityGate ? 'manual-push-now-skip-gate' : 'manual-push-now',
+ skipQualityGate,
});
console.log('\n[success]');
console.log(JSON.stringify({
diff --git a/wechat-daily-news-inline.mjs b/wechat-daily-news-inline.mjs
index 7209f56..228052f 100644
--- a/wechat-daily-news-inline.mjs
+++ b/wechat-daily-news-inline.mjs
@@ -78,10 +78,13 @@ const SECTION_LABELS = {
local_life: 'CITIES',
official_feeds: 'WIRE',
finance: 'FINANCE',
+ business: 'FINANCE',
tech: 'TECH',
technews: 'TECH',
sports: 'SPORTS',
+ living: 'CITIES',
knowledge: 'INSIGHT',
+ deepdive: 'DEEP',
};
const FEATURE_CARD_THEMES = {
@@ -187,6 +190,12 @@ function isNews002Html(html) {
return /\bclass="[^"]*\blead-card\b/u.test(source) && !/\bclass="hero\b/u.test(source);
}
+export function resolveNewsMorningWechatLayoutStyle(html, layoutStyle = '') {
+ const requested = String(layoutStyle ?? '').trim().toLowerCase();
+ if (requested === 'news001' || requested === 'news002') return requested;
+ return isNews002Html(html) ? 'news002' : 'news001';
+}
+
/** feature=渐变头条卡,rank=多行榜单卡,list=带序号的列表条目,lead=news002 头条小图卡。 */
function classifyCard(cardHtml, { news002Mode = false } = {}) {
if (news002Mode && /\blead-card\b/u.test(cardHtml)) return 'lead';
@@ -262,24 +271,23 @@ function renderFeatureCard(cardHtml, { theme, title, body, imageUrl = '' }) {
}
function renderListCard({ title, body, index, imageUrl = '' }) {
- const thumb = imageUrl
- ? ``
+ const thumbBlock = imageUrl
+ ? [
+ ``,
+ `
`,
+ '',
+ ].join('')
: '';
const textBlock = [
`
${formatCardIndex(index)} ${title}
`, body ? `${body}
` : '', ].filter(Boolean).join(''); - if (thumb) { - return [ - `]*>([\s\S]*?)<\/p>/gi)]
+ .map((item) => stripInlineText(item[1]))
+ .filter(Boolean)
+ .join(' '),
+ maxBodyChars,
+ );
+ if (!heading && !body) return '';
+ return [
+ renderSectionTitle(title, 'deepdive'),
+ ` ${heading} ${body}]+src=["']([^"']+)["']/i)?.[1] ?? '';
const mapped = resolveMappedImageUrl(src, imageUrlMap);
@@ -734,10 +777,13 @@ const DEFAULT_SECTION_CARD_LIMITS = {
local_life: 3,
official_feeds: 3,
finance: 5,
+ business: 5,
tech: 5,
sports: 3,
technews: 2,
+ living: 3,
knowledge: 3,
+ deepdive: 1,
};
const DEFAULT_CARD_BODY_CHARS = 132;
@@ -817,8 +863,14 @@ function countSectionCards(sections) {
if (!section.id || section.id === 'weather' || section.id === 'history') continue;
if (/历史上的今天|近7天新闻早报/u.test(section.title)) continue;
const total = section.id === 'knowledge'
- ? extractClassDivBlocks(section.html, 'knowledge-item').length
- : (section.html.match(/
'); }); +test('news001 layout keeps gradient headlines and adds compact thumbs on list cards', () => { + const sourceImage = 'https://news.example/story.jpg'; + const wechatImage = 'https://mmbiz.qpic.cn/wechat-story.jpg'; + const html = NEWS002_HTML + .replace('
上科技操