bfe8bac00c
Memind CI / Test, build, and release guards (push) Failing after 5m35s
Co-authored-by: Cursor <cursoragent@cursor.com>
512 lines
20 KiB
JavaScript
512 lines
20 KiB
JavaScript
import { extractPageTitle } from './wechat/verify/share-preview-repair.mjs';
|
|
|
|
const MAX_WECHAT_CONTENT_CHARS = 20000;
|
|
const MAX_WECHAT_CONTENT_TARGET_CHARS = 19600;
|
|
const DEFAULT_PORTAL_URL = 'https://m.tkmind.cn';
|
|
|
|
const CARD_VARIANTS = {
|
|
'highlight-box': {
|
|
wrap: 'background:linear-gradient(135deg,#b71c1c,#d32f2f);border-radius:12px;padding:14px;margin:0 0 10px;color:#fff;',
|
|
title: 'margin:0 0 6px;font-size:16px;font-weight:700;color:#fff;line-height:1.5;',
|
|
body: 'margin:0 0 6px;font-size:14px;line-height:1.7;color:#ffcdd2;',
|
|
source: 'margin:0;font-size:12px;color:#90caf4;',
|
|
tag: 'display:inline-block;font-size:11px;padding:2px 8px;border-radius:4px;margin:0 0 6px;background:rgba(255,255,255,.18);color:#fff;',
|
|
},
|
|
'highlight-box-orange': {
|
|
wrap: 'background:linear-gradient(135deg,#e65100,#bf360c);border-radius:12px;padding:14px;margin:0 0 10px;color:#fff;',
|
|
title: 'margin:0 0 6px;font-size:16px;font-weight:700;color:#fff;line-height:1.5;',
|
|
body: 'margin:0 0 6px;font-size:14px;line-height:1.7;color:#ffe0b2;',
|
|
source: 'margin:0;font-size:12px;color:#ffcdd2;',
|
|
tag: 'display:inline-block;font-size:11px;padding:2px 8px;border-radius:4px;margin:0 0 6px;background:rgba(255,255,255,.18);color:#fff;',
|
|
},
|
|
'highlight-box-gold': {
|
|
wrap: 'background:linear-gradient(135deg,#0d47a1,#1565c0);border-radius:12px;padding:14px;margin:0 0 10px;color:#fff;',
|
|
title: 'margin:0 0 6px;font-size:16px;font-weight:700;color:#fff;line-height:1.5;',
|
|
body: 'margin:0 0 6px;font-size:14px;line-height:1.7;color:#bbdefb;',
|
|
source: 'margin:0;font-size:12px;color:#bbdefb;',
|
|
tag: 'display:inline-block;font-size:11px;padding:2px 8px;border-radius:4px;margin:0 0 6px;background:rgba(255,255,255,.18);color:#fff;',
|
|
},
|
|
'highlight-box-teal': {
|
|
wrap: 'background:linear-gradient(135deg,#004d40,#00695c);border-radius:12px;padding:14px;margin:0 0 10px;color:#fff;',
|
|
title: 'margin:0 0 6px;font-size:16px;font-weight:700;color:#fff;line-height:1.5;',
|
|
body: 'margin:0 0 6px;font-size:14px;line-height:1.7;color:#b2dfdb;',
|
|
source: 'margin:0;font-size:12px;color:#b2dfdb;',
|
|
tag: 'display:inline-block;font-size:11px;padding:2px 8px;border-radius:4px;margin:0 0 6px;background:rgba(255,255,255,.18);color:#fff;',
|
|
},
|
|
default: {
|
|
wrap: 'background:#fff;border-radius:12px;padding:14px;margin:0 0 10px;',
|
|
title: 'margin:0 0 4px;font-size:16px;font-weight:600;color:#1a202c;line-height:1.5;',
|
|
body: 'margin:0 0 6px;font-size:14px;color:#4a5568;line-height:1.7;',
|
|
source: 'margin:0;font-size:12px;color:#a0aec0;',
|
|
tag: 'display:inline-block;font-size:11px;padding:2px 8px;border-radius:4px;margin:0 0 6px;background:#ffebee;color:#b71c1c;',
|
|
},
|
|
};
|
|
|
|
const TAG_CLASS_COLORS = {
|
|
'tag-red': 'background:#ffcdd2;color:#b71c1c;',
|
|
'tag-blue': 'background:#bbdefb;color:#0d47a1;',
|
|
'tag-green': 'background:#c8e6c9;color:#1b5e20;',
|
|
'tag-purple': 'background:#e1bee7;color:#6a1b9a;',
|
|
'tag-orange': 'background:#ffe0b2;color:#e65100;',
|
|
'tag-teal': 'background:#b2dfdb;color:#004d40;',
|
|
'tag-pink': 'background:#f8bbd0;color:#880e4f;',
|
|
'tag-gray': 'background:#e0e0e0;color:#424242;',
|
|
'tag-amber': 'background:#ffecb3;color:#ff6f00;',
|
|
};
|
|
|
|
function extractMetaDescription(html) {
|
|
const match = String(html ?? '').match(
|
|
/<meta[^>]+name=["']description["'][^>]+content=["']([^"']*)["']/i,
|
|
);
|
|
return match?.[1]?.trim() ?? '';
|
|
}
|
|
|
|
function stripScripts(html) {
|
|
return String(html ?? '').replace(/<script[\s\S]*?<\/script>/gi, '');
|
|
}
|
|
|
|
function sanitizeInlineHtml(html) {
|
|
return stripScripts(String(html ?? ''))
|
|
.replace(/<(?!\/?(a\b|br\b|strong\b|span\b|b\b|em\b|i\b)\b)[^>]+>/gi, '')
|
|
.replace(/\son\w+="[^"]*"/gi, '')
|
|
.trim();
|
|
}
|
|
|
|
function extractInnerHtml(block, pattern) {
|
|
const match = String(block ?? '').match(pattern);
|
|
return match?.[1] ? sanitizeInlineHtml(match[1]) : '';
|
|
}
|
|
|
|
function detectCardVariant(cardHtml) {
|
|
if (/highlight-box-orange/u.test(cardHtml)) return 'highlight-box-orange';
|
|
if (/highlight-box-gold/u.test(cardHtml)) return 'highlight-box-gold';
|
|
if (/highlight-box-teal/u.test(cardHtml)) return 'highlight-box-teal';
|
|
if (/highlight-box/u.test(cardHtml)) return 'highlight-box';
|
|
return 'default';
|
|
}
|
|
|
|
function renderTagHtml(cardHtml, styles) {
|
|
const tagMatch = String(cardHtml).match(/<span class="tag([^"]*)"[^>]*(?:style="([^"]*)")?[^>]*>([\s\S]*?)<\/span>/i);
|
|
if (!tagMatch) return '';
|
|
const className = tagMatch[1]?.trim() ?? '';
|
|
const inlineStyle = tagMatch[2]?.trim() ?? '';
|
|
const label = sanitizeInlineHtml(tagMatch[3]);
|
|
if (!label) return '';
|
|
const colorStyle = TAG_CLASS_COLORS[className.replace(/^\s+/, '')] ?? '';
|
|
const style = inlineStyle || colorStyle || styles.tag;
|
|
return `<span style="${style}">${label}</span>`;
|
|
}
|
|
|
|
function renderCard(cardHtml) {
|
|
const variant = detectCardVariant(cardHtml);
|
|
const styles = CARD_VARIANTS[variant] ?? CARD_VARIANTS.default;
|
|
const tag = renderTagHtml(cardHtml, styles);
|
|
const title = extractInnerHtml(cardHtml, /<h3[^>]*>([\s\S]*?)<\/h3>/i);
|
|
const body = extractInnerHtml(cardHtml, /<p[^>]*>([\s\S]*?)<\/p>/i);
|
|
const sourceRaw = extractInnerHtml(cardHtml, /<div class="source"[^>]*>([\s\S]*?)<\/div>/i);
|
|
const source = sourceRaw.replace(/^来源:?/u, '').trim();
|
|
if (!title) return '';
|
|
|
|
const parts = [
|
|
`<section style="${styles.wrap}">`,
|
|
tag ? `<p style="margin:0 0 6px;">${tag}</p>` : '',
|
|
`<p style="${styles.title}"><strong>${title}</strong></p>`,
|
|
body ? `<p style="${styles.body}">${body}</p>` : '',
|
|
source ? `<p style="${styles.source}">来源:${source}</p>` : '',
|
|
'</section>',
|
|
];
|
|
return parts.filter(Boolean).join('');
|
|
}
|
|
|
|
function renderSectionTitle(titleHtml) {
|
|
const text = sanitizeInlineHtml(titleHtml).replace(/<span class="icon"[^>]*>([\s\S]*?)<\/span>/gi, '$1');
|
|
if (!text) return '';
|
|
return `<p style="margin:0 0 12px;padding:12px 0 6px;border-bottom:3px solid #0d47a1;font-size:18px;font-weight:700;color:#b71c1c;line-height:1.4;">${text}</p>`;
|
|
}
|
|
|
|
function renderHero(html) {
|
|
const title = sanitizeInlineHtml(String(html).match(/<h1[^>]*>([\s\S]*?)<\/h1>/i)?.[1] ?? '') || '📰 每日新闻早报';
|
|
const dateBadge = sanitizeInlineHtml(String(html).match(/<div class="date-badge"[^>]*>([\s\S]*?)<\/div>/i)?.[1] ?? '');
|
|
const subtitle = sanitizeInlineHtml(String(html).match(/<div class="subtitle"[^>]*>([\s\S]*?)<\/div>/i)?.[1] ?? '');
|
|
return [
|
|
'<section style="text-align:center;padding:24px 14px;background:linear-gradient(135deg,#b71c1c,#d32f2f 50%,#0d47a1);color:#fff;margin:0 0 12px;border-radius:12px;">',
|
|
`<p style="margin:0 0 10px;font-size:24px;font-weight:700;line-height:1.35;">${title}</p>`,
|
|
dateBadge ? `<p style="margin:0 0 8px;font-size:14px;opacity:.92;">${dateBadge}</p>` : '',
|
|
subtitle ? `<p style="margin:0;font-size:14px;opacity:.88;line-height:1.7;">${subtitle}</p>` : '',
|
|
'</section>',
|
|
].filter(Boolean).join('');
|
|
}
|
|
|
|
function renderReadOriginalTop(publicUrl) {
|
|
if (!publicUrl) return '';
|
|
return [
|
|
'<section style="margin:0 0 16px;text-align:center;">',
|
|
`<a href="${publicUrl}" style="display:inline-block;padding:11px 22px;background:linear-gradient(135deg,#b71c1c,#d32f2f);color:#fff;font-size:14px;font-weight:600;text-decoration:none;border-radius:999px;letter-spacing:.02em;box-shadow:0 3px 10px rgba(183,28,28,.22);">📖 点击阅读原文</a>`,
|
|
'<p style="margin:8px 0 0;font-size:12px;color:#a0aec0;line-height:1.6;">查看完整排版、图片与更多栏目</p>',
|
|
'</section>',
|
|
].join('');
|
|
}
|
|
|
|
function renderCreatePortalCTA(portalUrl = DEFAULT_PORTAL_URL) {
|
|
return `<p style="margin:18px 0 0;font-size:14px;color:#718096;text-align:center;line-height:1.7;">✨ 一起创作 → <a href="${portalUrl}" style="color:#e53935;font-weight:600;text-decoration:underline;">点我</a></p>`;
|
|
}
|
|
|
|
function renderFollowMpSection(qrcodeImageUrl = '') {
|
|
if (qrcodeImageUrl) {
|
|
return [
|
|
'<section style="text-align:center;padding:22px 16px 18px;margin:18px 0 8px;background:linear-gradient(180deg,#fff8f8,#fff);border:1px solid #ffcdd2;border-radius:12px;">',
|
|
'<p style="margin:0 0 6px;font-size:16px;font-weight:700;color:#b71c1c;line-height:1.5;">📣 关注 TKMind 服务号</p>',
|
|
'<p style="margin:0 0 14px;font-size:13px;color:#666;line-height:1.7;">长按扫描关注,每日早报不错过</p>',
|
|
`<img src="${qrcodeImageUrl}" alt="TKMind 服务号二维码" style="width:168px;height:168px;display:block;margin:0 auto;border-radius:10px;border:1px solid #eee;" />`,
|
|
'<p style="margin:12px 0 0;font-size:12px;color:#a0aec0;">长按扫描关注</p>',
|
|
'</section>',
|
|
].join('');
|
|
}
|
|
return [
|
|
'<section style="text-align:center;padding:20px 16px;margin:18px 0 8px;background:#fafafa;border:1px dashed #e0e0e0;border-radius:12px;">',
|
|
'<p style="margin:0 0 6px;font-size:15px;font-weight:600;color:#333;">📣 关注 TKMind 服务号</p>',
|
|
'<p style="margin:0;font-size:13px;color:#888;line-height:1.7;">推送草稿时将嵌入公众号二维码</p>',
|
|
'</section>',
|
|
].join('');
|
|
}
|
|
|
|
function renderWeatherSection(sectionHtml) {
|
|
const title = extractInnerHtml(sectionHtml, /<div class="section-title"[^>]*>([\s\S]*?)<\/div>/i);
|
|
const summary = extractInnerHtml(sectionHtml, /<div class="weather-today"[^>]*>([\s\S]*?)<\/div>/i);
|
|
const cityCards = [...String(sectionHtml).matchAll(/<div class="weather-card[^"]*"[^>]*>[\s\S]*?<span class="w-city">([\s\S]*?)<\/span>[\s\S]*?<span class="w-cond">([\s\S]*?)<\/span>[\s\S]*?<span class="w-temp">([\s\S]*?)<\/span>/gi)]
|
|
.map((item) => `${sanitizeInlineHtml(item[1])}${sanitizeInlineHtml(item[2])} ${sanitizeInlineHtml(item[3])}`)
|
|
.slice(0, 12);
|
|
const footer = extractInnerHtml(sectionHtml, /<p style="font-size:12px;color:#a0aec0;">([\s\S]*?)<\/p>/i);
|
|
|
|
const parts = [renderSectionTitle(title)];
|
|
parts.push('<section style="background:linear-gradient(135deg,#e3f2fd,#bbdefb);border:1px solid #90caf9;border-radius:12px;padding:14px 16px;margin-bottom:12px;">');
|
|
if (summary) {
|
|
parts.push(`<p style="margin:0 0 8px;font-size:14px;color:#0d47a1;line-height:1.75;">${summary.replace(/<div[^>]*>/gi, ' ').replace(/<\/div>/gi, ' ')}</p>`);
|
|
}
|
|
if (cityCards.length > 0) {
|
|
parts.push(`<p style="margin:0;font-size:13px;color:#1565c0;line-height:1.8;">${cityCards.join(' · ')}</p>`);
|
|
}
|
|
parts.push('</section>');
|
|
if (footer) {
|
|
parts.push(`<p style="margin:0 0 16px;font-size:12px;color:#a0aec0;line-height:1.6;">${footer}</p>`);
|
|
}
|
|
return parts.join('');
|
|
}
|
|
|
|
function splitSections(html) {
|
|
const body = String(html ?? '').match(/<body>([\s\S]*?)<\/body>/i)?.[1] ?? String(html ?? '');
|
|
return body
|
|
.split(/<div class="section"[^>]*>/i)
|
|
.slice(1)
|
|
.map((chunk) => {
|
|
const id = chunk.match(/^[^>]*id="([^"]+)"/i)?.[1] ?? '';
|
|
const htmlChunk = `<div class="section"${chunk.split(/<div class="section"|<div class="footer"/i)[0]}`;
|
|
const title = stripInlineText(extractInnerHtml(htmlChunk, /<div class="section-title"[^>]*>([\s\S]*?)<\/div>/i));
|
|
return { id, title, html: htmlChunk };
|
|
});
|
|
}
|
|
|
|
function stripInlineText(value) {
|
|
return sanitizeInlineHtml(String(value ?? ''))
|
|
.replace(/<span class="icon"[^>]*>([\s\S]*?)<\/span>/gi, '$1')
|
|
.replace(/<[^>]+>/g, '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
const DEFAULT_SECTION_CARD_LIMITS = {
|
|
headlines: 6,
|
|
world: 3,
|
|
domestic: 3,
|
|
google: 2,
|
|
finance: 3,
|
|
tech: 3,
|
|
sports: 2,
|
|
technews: 2,
|
|
hot: 1,
|
|
};
|
|
|
|
const TRIM_PROFILES = [
|
|
{ skipHistory: true, sectionCardLimits: null, maxBodyChars: null },
|
|
{
|
|
skipHistory: true,
|
|
sectionCardLimits: DEFAULT_SECTION_CARD_LIMITS,
|
|
maxBodyChars: 120,
|
|
},
|
|
{
|
|
skipHistory: true,
|
|
sectionCardLimits: { ...DEFAULT_SECTION_CARD_LIMITS, headlines: 5, world: 2, domestic: 2 },
|
|
maxBodyChars: 90,
|
|
},
|
|
{
|
|
skipHistory: true,
|
|
sectionCardLimits: { headlines: 4, world: 2, domestic: 2, google: 2, finance: 2, tech: 2, sports: 1, technews: 1, hot: 1 },
|
|
maxBodyChars: 72,
|
|
},
|
|
{
|
|
skipHistory: true,
|
|
sectionCardLimits: { headlines: 3, world: 2, domestic: 2, google: 2, finance: 2, tech: 2, sports: 1, technews: 0, hot: 1 },
|
|
maxBodyChars: 64,
|
|
},
|
|
{
|
|
skipHistory: true,
|
|
sectionCardLimits: { headlines: 3, world: 2, domestic: 2, google: 1, finance: 2, tech: 2, sports: 1, technews: 0, hot: 0 },
|
|
maxBodyChars: 56,
|
|
},
|
|
];
|
|
|
|
function buildFallbackTrimProfiles() {
|
|
const profiles = [];
|
|
for (let headlines = 3; headlines >= 2; headlines -= 1) {
|
|
for (let maxBodyChars = 56; maxBodyChars >= 24; maxBodyChars -= 8) {
|
|
for (const skipWeather of [false, true]) {
|
|
profiles.push({
|
|
skipHistory: true,
|
|
skipWeather,
|
|
sectionCardLimits: {
|
|
headlines,
|
|
world: 2,
|
|
domestic: 2,
|
|
google: 1,
|
|
finance: 2,
|
|
tech: 2,
|
|
sports: 1,
|
|
technews: 0,
|
|
hot: 0,
|
|
},
|
|
maxBodyChars,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return profiles;
|
|
}
|
|
|
|
function resolveSectionCardLimit(section, sectionCardLimits) {
|
|
if (!sectionCardLimits) return null;
|
|
return sectionCardLimits[section.id] ?? 3;
|
|
}
|
|
|
|
function estimateQrcodeUrlForLayout(qrcodeImageUrl = '') {
|
|
const url = String(qrcodeImageUrl ?? '');
|
|
if (!url) return '';
|
|
if (url.length >= 180) return url;
|
|
return `${url}${'x'.repeat(180 - url.length)}`;
|
|
}
|
|
|
|
function estimateFixedLayoutLength({ publicUrl = '', qrcodeImageUrl = '', portalUrl = DEFAULT_PORTAL_URL } = {}) {
|
|
const qrcodeForEstimate = estimateQrcodeUrlForLayout(qrcodeImageUrl);
|
|
return (
|
|
(publicUrl ? renderReadOriginalTop(publicUrl).length : 0)
|
|
+ renderFollowMpSection(qrcodeForEstimate).length
|
|
+ renderCreatePortalCTA(portalUrl).length
|
|
);
|
|
}
|
|
|
|
function buildWechatInlineContent(html, {
|
|
publicUrl = '',
|
|
qrcodeImageUrl = '',
|
|
portalUrl = DEFAULT_PORTAL_URL,
|
|
trimProfile = TRIM_PROFILES[0],
|
|
includeFixedLayout = true,
|
|
} = {}) {
|
|
const sections = splitSections(html);
|
|
const bodyParts = [renderHero(html)];
|
|
|
|
for (const section of sections) {
|
|
if (trimProfile.skipHistory && shouldSkipSection(section, trimProfile)) {
|
|
continue;
|
|
}
|
|
if (resolveSectionCardLimit(section, trimProfile.sectionCardLimits) === 0) {
|
|
continue;
|
|
}
|
|
if (section.id === 'weather') {
|
|
if (!trimProfile.skipWeather) {
|
|
bodyParts.push(renderWeatherSection(section.html));
|
|
}
|
|
continue;
|
|
}
|
|
bodyParts.push(renderGenericSection(section.html, {
|
|
maxCards: resolveSectionCardLimit(section, trimProfile.sectionCardLimits),
|
|
maxBodyChars: trimProfile.maxBodyChars,
|
|
}));
|
|
}
|
|
|
|
bodyParts.push(renderFooter(html));
|
|
|
|
const ordered = [...bodyParts];
|
|
if (includeFixedLayout) {
|
|
if (publicUrl) ordered.splice(1, 0, renderReadOriginalTop(publicUrl));
|
|
ordered.push(renderFollowMpSection(qrcodeImageUrl));
|
|
ordered.push(renderCreatePortalCTA(portalUrl));
|
|
}
|
|
|
|
return {
|
|
content: ordered.join(''),
|
|
sectionCount: sections.length,
|
|
renderedSectionCount: sections.filter((section) => {
|
|
if (trimProfile.skipHistory && shouldSkipSection(section, trimProfile)) return false;
|
|
if (resolveSectionCardLimit(section, trimProfile.sectionCardLimits) === 0) return false;
|
|
return true;
|
|
}).length,
|
|
};
|
|
}
|
|
|
|
function truncatePlainText(value, maxChars) {
|
|
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
if (!maxChars || text.length <= maxChars) return text;
|
|
return `${text.slice(0, Math.max(0, maxChars - 1))}…`;
|
|
}
|
|
|
|
function shouldSkipSection(section, trimProfile) {
|
|
if (!section) return true;
|
|
if (['history', 'knowledge'].includes(section.id)) return true;
|
|
if (trimProfile?.skipSectionIds?.includes(section.id)) return true;
|
|
return /历史上的今天|近7天新闻早报/u.test(section.title);
|
|
}
|
|
|
|
function renderGenericSection(sectionHtml, { maxCards = null, maxBodyChars = null } = {}) {
|
|
const title = extractInnerHtml(sectionHtml, /<div class="section-title"[^>]*>([\s\S]*?)<\/div>/i);
|
|
if (/近7天新闻早报/u.test(stripInlineText(title))) return '';
|
|
const cards = sectionHtml.split(/<div class="card(?:\s|")/i).slice(1);
|
|
const parts = [renderSectionTitle(title)];
|
|
const limit = maxCards == null ? cards.length : Math.min(maxCards, cards.length);
|
|
for (let index = 0; index < limit; index += 1) {
|
|
let cardChunk = cards[index];
|
|
if (maxBodyChars) {
|
|
cardChunk = cardChunk.replace(/<p[^>]*>([\s\S]*?)<\/p>/i, (match, body) => {
|
|
const plain = body.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
|
|
return match.replace(body, truncatePlainText(plain, maxBodyChars));
|
|
});
|
|
}
|
|
const rendered = renderCard(`<div class="card ${cardChunk}`);
|
|
if (rendered) parts.push(rendered);
|
|
}
|
|
return parts.join('');
|
|
}
|
|
|
|
function renderFooter(html) {
|
|
const footer = String(html).match(/<div class="footer"[^>]*>([\s\S]*?)<\/div>/i)?.[1] ?? '';
|
|
if (!footer) return '';
|
|
const lines = [...footer.matchAll(/<p[^>]*>([\s\S]*?)<\/p>/gi)]
|
|
.map((item) => sanitizeInlineHtml(item[1]))
|
|
.filter(Boolean)
|
|
.slice(0, 3);
|
|
if (lines.length === 0) return '';
|
|
return [
|
|
'<section style="text-align:center;padding:20px 12px 8px;color:#a0aec0;">',
|
|
...lines.map((line) => `<p style="margin:0 0 8px;font-size:13px;line-height:1.7;color:#718096;">${line}</p>`),
|
|
'</section>',
|
|
].join('');
|
|
}
|
|
|
|
export function convertDailyNewsHtmlToWechatInlineArticle(html, {
|
|
publicUrl = '',
|
|
qrcodeImageUrl = '',
|
|
portalUrl = DEFAULT_PORTAL_URL,
|
|
} = {}) {
|
|
const title = (extractPageTitle(html) || '每日新闻早报').slice(0, 32);
|
|
const digest = (extractMetaDescription(html) || title).slice(0, 120);
|
|
let built = null;
|
|
let trimLevel = 0;
|
|
let usedFallback = false;
|
|
const allProfiles = [...TRIM_PROFILES, ...buildFallbackTrimProfiles()];
|
|
|
|
const fixedLayoutLength = estimateFixedLayoutLength({ publicUrl, qrcodeImageUrl, portalUrl });
|
|
const trimTargetChars = Math.max(12000, MAX_WECHAT_CONTENT_CHARS - fixedLayoutLength - 800);
|
|
|
|
for (const [index, trimProfile] of allProfiles.entries()) {
|
|
built = buildWechatInlineContent(html, {
|
|
publicUrl,
|
|
qrcodeImageUrl,
|
|
portalUrl,
|
|
trimProfile,
|
|
includeFixedLayout: false,
|
|
});
|
|
trimLevel = index;
|
|
usedFallback = index >= TRIM_PROFILES.length;
|
|
if (built.content.length <= trimTargetChars) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let selectedProfile = allProfiles[trimLevel] ?? allProfiles[allProfiles.length - 1];
|
|
if (built) {
|
|
built = buildWechatInlineContent(html, {
|
|
publicUrl,
|
|
qrcodeImageUrl,
|
|
portalUrl,
|
|
trimProfile: selectedProfile,
|
|
includeFixedLayout: true,
|
|
});
|
|
let tightenGuard = 0;
|
|
while (built.content.length > MAX_WECHAT_CONTENT_CHARS && tightenGuard < 24) {
|
|
tightenGuard += 1;
|
|
const limits = {
|
|
world: 2,
|
|
domestic: 2,
|
|
google: 1,
|
|
tech: 2,
|
|
sports: 1,
|
|
technews: 0,
|
|
hot: 0,
|
|
headlines: selectedProfile.sectionCardLimits?.headlines ?? 3,
|
|
finance: selectedProfile.sectionCardLimits?.finance ?? 2,
|
|
};
|
|
const maxBody = selectedProfile.maxBodyChars ?? 64;
|
|
if (maxBody > 16) {
|
|
selectedProfile.maxBodyChars = maxBody - 4;
|
|
} else if (limits.headlines > 2) {
|
|
limits.headlines -= 1;
|
|
} else if (limits.finance > 1) {
|
|
limits.finance -= 1;
|
|
} else {
|
|
break;
|
|
}
|
|
selectedProfile = {
|
|
...selectedProfile,
|
|
skipHistory: true,
|
|
skipWeather: true,
|
|
maxBodyChars: selectedProfile.maxBodyChars ?? maxBody,
|
|
sectionCardLimits: limits,
|
|
};
|
|
built = buildWechatInlineContent(html, {
|
|
publicUrl,
|
|
qrcodeImageUrl,
|
|
portalUrl,
|
|
trimProfile: selectedProfile,
|
|
includeFixedLayout: true,
|
|
});
|
|
usedFallback = true;
|
|
}
|
|
}
|
|
|
|
if (!built || built.content.length > MAX_WECHAT_CONTENT_CHARS) {
|
|
throw new Error(
|
|
`微信正文超过 ${MAX_WECHAT_CONTENT_CHARS} 字符(当前 ${built?.content.length ?? 0}),请缩短 HTML 页面后重试`,
|
|
);
|
|
}
|
|
|
|
return {
|
|
title,
|
|
digest,
|
|
content: built.content,
|
|
contentSourceUrl: publicUrl || undefined,
|
|
contentMode: 'inline_html',
|
|
contentLength: built.content.length,
|
|
sectionCount: built.sectionCount,
|
|
renderedSectionCount: built.renderedSectionCount,
|
|
trimLevel,
|
|
trimmed: trimLevel > 0,
|
|
usedFallbackTrim: usedFallback,
|
|
};
|
|
}
|
|
|
|
export const wechatDailyNewsInlineInternals = {
|
|
renderCard,
|
|
renderHero,
|
|
buildWechatInlineContent,
|
|
TRIM_PROFILES,
|
|
MAX_WECHAT_CONTENT_CHARS,
|
|
};
|