diff --git a/skills/baoyu-post-to-wechat/scripts/wechat-api.ts b/skills/baoyu-post-to-wechat/scripts/wechat-api.ts
index b4b92dd..e1127ad 100644
--- a/skills/baoyu-post-to-wechat/scripts/wechat-api.ts
+++ b/skills/baoyu-post-to-wechat/scripts/wechat-api.ts
@@ -23,6 +23,20 @@ interface PublishResponse {
errmsg?: string;
}
+interface ImageInfo {
+ placeholder: string;
+ localPath: string;
+ originalPath: string;
+}
+
+interface MarkdownRenderResult {
+ title: string;
+ author: string;
+ summary: string;
+ htmlPath: string;
+ contentImages: ImageInfo[];
+}
+
type ArticleType = "news" | "newspic";
interface ArticleOptions {
@@ -143,18 +157,20 @@ async function uploadImage(
async function uploadImagesInHtml(
html: string,
accessToken: string,
- baseDir: string
+ baseDir: string,
+ contentImages: ImageInfo[] = [],
): Promise<{ html: string; firstMediaId: string; allMediaIds: string[] }> {
const imgRegex = /
]*\ssrc=["']([^"']+)["'][^>]*>/gi;
const matches = [...html.matchAll(imgRegex)];
- if (matches.length === 0) {
+ if (matches.length === 0 && contentImages.length === 0) {
return { html, firstMediaId: "", allMediaIds: [] };
}
let firstMediaId = "";
let updatedHtml = html;
const allMediaIds: string[] = [];
+ const uploadedBySource = new Map();
for (const match of matches) {
const [fullTag, src] = match;
@@ -172,7 +188,11 @@ async function uploadImagesInHtml(
console.error(`[wechat-api] Uploading image: ${imagePath}`);
try {
- const resp = await uploadImage(imagePath, accessToken, baseDir);
+ let resp = uploadedBySource.get(imagePath);
+ if (!resp) {
+ resp = await uploadImage(imagePath, accessToken, baseDir);
+ uploadedBySource.set(imagePath, resp);
+ }
const newTag = fullTag
.replace(/\ssrc=["'][^"']+["']/, ` src="${resp.url}"`)
.replace(/\sdata-local-path=["'][^"']+["']/, "");
@@ -186,6 +206,30 @@ async function uploadImagesInHtml(
}
}
+ for (const image of contentImages) {
+ if (!updatedHtml.includes(image.placeholder)) continue;
+
+ const imagePath = image.localPath || image.originalPath;
+ console.error(`[wechat-api] Uploading placeholder image: ${imagePath}`);
+
+ try {
+ let resp = uploadedBySource.get(imagePath);
+ if (!resp) {
+ resp = await uploadImage(imagePath, accessToken, baseDir);
+ uploadedBySource.set(imagePath, resp);
+ }
+
+ const replacementTag = `
`;
+ updatedHtml = replaceAllPlaceholders(updatedHtml, image.placeholder, replacementTag);
+ allMediaIds.push(resp.media_id);
+ if (!firstMediaId) {
+ firstMediaId = resp.media_id;
+ }
+ } catch (err) {
+ console.error(`[wechat-api] Failed to upload placeholder ${image.placeholder}:`, err);
+ }
+ }
+
return { html: updatedHtml, firstMediaId, allMediaIds };
}
@@ -245,7 +289,7 @@ async function publishToDraft(
}
function parseFrontmatter(content: string): { frontmatter: Record; body: string } {
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
+ const match = content.match(/^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
if (!match) return { frontmatter: {}, body: content };
const frontmatter: Record = {};
@@ -266,38 +310,42 @@ function parseFrontmatter(content: string): { frontmatter: Record {
let htmlPath: string;
let htmlContent: string;
let frontmatter: Record = {};
+ let contentImages: ImageInfo[] = [];
if (args.isHtml) {
htmlPath = filePath;
@@ -491,8 +540,14 @@ async function main(): Promise {
if (!digest) digest = frontmatter.digest || frontmatter.summary || frontmatter.description || "";
console.error(`[wechat-api] Theme: ${args.theme}${args.color ? `, color: ${args.color}` : ""}, citeStatus: ${args.citeStatus}`);
- htmlPath = renderMarkdownToHtml(filePath, args.theme, args.color, args.citeStatus);
+ const rendered = renderMarkdownWithPlaceholders(filePath, args.theme, args.color, args.citeStatus, args.title);
+ htmlPath = rendered.htmlPath;
+ contentImages = rendered.contentImages;
+ if (!title) title = rendered.title;
+ if (!author) author = rendered.author;
+ if (!digest) digest = rendered.summary;
console.error(`[wechat-api] HTML generated: ${htmlPath}`);
+ console.error(`[wechat-api] Placeholder images: ${contentImages.length}`);
htmlContent = extractHtmlContent(htmlPath);
}
@@ -527,6 +582,7 @@ async function main(): Promise {
digest: digest || undefined,
htmlPath,
contentLength: htmlContent.length,
+ placeholderImageCount: contentImages.length || undefined,
account: resolved.alias || undefined,
}, null, 2));
return;
@@ -540,7 +596,8 @@ async function main(): Promise {
const { html: processedHtml, firstMediaId, allMediaIds } = await uploadImagesInHtml(
htmlContent,
accessToken,
- baseDir
+ baseDir,
+ contentImages,
);
htmlContent = processedHtml;