n8n-nodes-wechat-publish
Version:
N8N nodes for WeChat Official Account publishing - Create drafts and publish articles to WeChat Official Account platform
1,125 lines • 109 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WeChat = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const WeChatApiClient_1 = require("./WeChatApiClient");
class WeChat {
constructor() {
this.description = {
displayName: 'WeChat',
name: 'weChat',
icon: 'file:wechat.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"]}}',
description: 'Create drafts and publish articles to WeChat Official Account',
defaults: {
name: 'WeChat',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'weChatApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: 'Create Draft',
value: 'createDraft',
description: 'Create a new draft article',
action: 'Create a draft article',
},
{
name: 'Get Draft',
value: 'getDraft',
description: 'Get an existing draft article',
action: 'Get a draft article',
},
{
name: 'Delete Draft',
value: 'deleteDraft',
description: 'Delete an existing draft article',
action: 'Delete a draft article',
},
{
name: 'Publish Draft',
value: 'publishDraft',
description: 'Publish a draft article',
action: 'Publish a draft article',
},
{
name: 'Get Publish Status',
value: 'getPublishStatus',
description: 'Get the publishing status of an article',
action: 'Get publish status',
},
{
name: 'Upload Media',
value: 'uploadMedia',
description: 'Upload media file for articles',
action: 'Upload media file',
},
{
name: 'Get Material List',
value: 'getMaterialList',
description: 'Get list of permanent materials (images, videos, etc.)',
action: 'Get material list',
},
{
name: 'Batch Upload Memes',
value: 'batchUploadMemes',
description: 'Fetch and upload memes from Meme API to WeChat',
action: 'Batch upload memes',
},
{
name: 'Batch Publish Drafts',
value: 'batchPublishDrafts',
description: 'Batch publish multiple drafts with delay control',
action: 'Batch publish drafts with delay',
},
],
default: 'createDraft',
},
// Create Draft Parameters
{
displayName: 'Batch Processing Mode',
name: 'batchProcessingMode',
type: 'options',
options: [
{
name: 'Single Article Mode',
value: 'single',
description: 'Create draft for a single article',
},
{
name: 'Multi-Article Mode (3 articles per draft)',
value: 'multi',
description: 'Process multiple articles in batches of 3 per draft',
},
{
name: 'HTML Array Mode (Auto-parse HTML content)',
value: 'htmlArray',
description: 'Parse HTML content array and create drafts automatically',
},
],
default: 'single',
displayOptions: {
show: {
operation: ['createDraft'],
},
},
description: 'Choose how to process articles: single, JSON batches, or HTML array auto-parsing',
},
// HTML Array Mode Parameters
{
displayName: 'HTML Content Array',
name: 'htmlContentArray',
type: 'json',
default: `[
"<title>第一篇文章标题</title><p>这是第一篇文章的正文内容...</p><p>可以包含多个段落</p>",
"<title>第二篇文章标题</title><p>这是第二篇文章的正文内容...</p>",
"<title>第三篇文章标题</title><p>这是第三篇文章的正文内容...</p>"
]`,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: '💡 智能数据源:系统会自动从输入项中检测并提取HTML内容,如果输入项中没有数据则使用此参数。支持格式:1) HTML字符串数组 ["<title>标题</title><p>内容</p>"] 2) 对象数组 [{"output": ["<title>标题</title><p>内容</p>"]}]。可直接连接上一个节点的输出!',
},
{
displayName: 'Articles Per Batch',
name: 'articlesPerBatch',
type: 'number',
default: 3,
typeOptions: {
minValue: 1,
maxValue: 8,
},
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: '每组草稿包含的文章数量,微信公众号支持1-8篇文章为一组。默认为3篇',
},
{
displayName: 'Thumbnail Media ID JSON Array',
name: 'thumbnailMediaIdArray',
type: 'json',
default: `[
{"media_id": "your_media_id_1", "description": "封面图1"},
{"media_id": "your_media_id_2", "description": "封面图2"},
{"media_id": "your_media_id_3", "description": "封面图3"}
]`,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: '💡 封面图JSON数组:系统会随机从这个数组中选择media_id作为文章封面。支持格式:1) 直接连接"Get Material List"输出 2) 手动JSON数组。推荐连接"Get Material List"节点',
},
{
displayName: 'Default Article Type',
name: 'defaultArticleType',
type: 'options',
options: [
{
name: 'News Article',
value: 'news',
},
{
name: 'Image Article',
value: 'newspic',
},
],
default: 'news',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: 'HTML数组模式下,所有文章使用的默认文章类型',
},
{
displayName: 'Default Author',
name: 'defaultAuthor',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: 'HTML数组模式下,所有文章使用的默认作者名称',
},
{
displayName: 'Auto Generate Digest',
name: 'autoGenerateDigest',
type: 'boolean',
default: true,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: '是否自动从文章内容前54个字符生成摘要',
},
{
displayName: 'Default Enable Comments',
name: 'defaultNeedOpenComment',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: 'HTML数组模式下,所有文章是否默认启用评论',
},
{
displayName: 'Default Only Fans Can Comment',
name: 'defaultOnlyFansCanComment',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
defaultNeedOpenComment: [true],
},
},
description: 'HTML数组模式下,所有文章是否默认只允许粉丝评论',
},
// HTML Array Mode - Image Insertion Parameters
{
displayName: 'Enable Image Insertion',
name: 'htmlArrayEnableImageInsertion',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
},
},
description: '为HTML数组模式中的所有文章自动插入图片',
},
{
displayName: 'Image Insertion Mode',
name: 'htmlArrayImageInsertionMode',
type: 'options',
options: [
{
name: 'Auto (Smart Positioning)',
value: 'auto',
description: 'Automatically find optimal positions based on content structure',
},
{
name: 'By Paragraph',
value: 'paragraph',
description: 'Insert images between paragraphs at regular intervals',
},
{
name: 'Manual (No Auto-Insert)',
value: 'manual',
description: 'Use images already embedded in content',
},
],
default: 'auto',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
htmlArrayEnableImageInsertion: [true],
},
},
description: 'HTML数组模式下,如何将图片插入到文章内容中',
},
{
displayName: 'Image Media IDs or URLs',
name: 'htmlArrayInsertionImageIds',
type: 'json',
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
htmlArrayEnableImageInsertion: [true],
htmlArrayImageInsertionMode: ['auto', 'paragraph'],
},
},
description: '要插入文章的图片数据。支持格式:1) 来自"Get Material List"的数组(推荐):{{$json.materials_summary}}, 2) JSON字符串, 3) 逗号分隔的URL。💡 最佳实践:直接连接"Get Material List"节点使用{{$json.materials_summary}}',
},
{
displayName: 'Max Images Per Article',
name: 'htmlArrayMaxImagesPerArticle',
type: 'number',
default: 3,
typeOptions: {
minValue: 1,
maxValue: 10,
},
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
htmlArrayEnableImageInsertion: [true],
htmlArrayImageInsertionMode: ['auto', 'paragraph'],
},
},
description: 'HTML数组模式下,每篇文章最多插入的图片数量',
},
{
displayName: 'Image Selection Strategy',
name: 'htmlArrayImageInsertionStrategy',
type: 'options',
options: [
{
name: 'Sequential',
value: 'sequential',
description: 'Use images in the order provided',
},
{
name: 'Random',
value: 'random',
description: 'Randomly select images from the list',
},
{
name: 'Balanced',
value: 'balanced',
description: 'Distribute images evenly across the list',
},
],
default: 'sequential',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['htmlArray'],
htmlArrayEnableImageInsertion: [true],
htmlArrayImageInsertionMode: ['auto', 'paragraph'],
},
},
description: 'HTML数组模式下,选择使用哪些图片的策略',
},
{
displayName: 'Articles JSON',
name: 'articlesJson',
type: 'json',
default: `[
{
"article_type": "news",
"title": "文章标题1",
"content": "<p>文章内容1</p>",
"thumb_media_id": "你的媒体ID1",
"author": "作者名",
"digest": "摘要",
"content_source_url": "",
"need_open_comment": 0,
"only_fans_can_comment": 0
},
{
"article_type": "news",
"title": "文章标题2",
"content": "<p>文章内容2</p>",
"thumb_media_id": "你的媒体ID2",
"author": "作者名",
"digest": "摘要",
"content_source_url": "",
"need_open_comment": 0,
"only_fans_can_comment": 0
},
{
"article_type": "news",
"title": "文章标题3",
"content": "<p>文章内容3</p>",
"thumb_media_id": "你的媒体ID3",
"author": "作者名",
"digest": "摘要",
"content_source_url": "",
"need_open_comment": 0,
"only_fans_can_comment": 0
}
]`,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['multi'],
},
},
description: 'JSON格式的文章列表,系统会自动按每3篇文章分组创建草稿。支持的字段:article_type, title, content, thumb_media_id, author, digest, content_source_url, need_open_comment, only_fans_can_comment',
},
{
displayName: 'Article Type',
name: 'articleType',
type: 'options',
options: [
{
name: 'News Article',
value: 'news',
},
{
name: 'Image Article',
value: 'newspic',
},
],
default: 'news',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'The type of article to create',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'The title of the article (max 64 characters)',
},
{
displayName: 'Content',
name: 'content',
type: 'string',
typeOptions: {
rows: 10,
},
required: true,
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'The content of the article (HTML supported, max 20,000 characters)',
},
{
displayName: 'Thumbnail Media ID',
name: 'thumbMediaId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'The media ID of the thumbnail image (must be permanent media ID). 💡 Tip: Use "Get Material List" operation first to find existing media IDs, then copy the media_id value here.',
},
{
displayName: 'Author',
name: 'author',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'The author of the article',
},
{
displayName: 'Digest',
name: 'digest',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'Article summary (auto-generated from first 54 characters if empty)',
},
{
displayName: 'Content Source URL',
name: 'contentSourceUrl',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'Original article URL for "Read More" link',
},
{
displayName: 'Enable Comments',
name: 'needOpenComment',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'Whether to enable comments for this article',
},
{
displayName: 'Only Fans Can Comment',
name: 'onlyFansCanComment',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
needOpenComment: [true],
},
},
description: 'Whether only fans can comment',
},
// Image Insertion Parameters
{
displayName: 'Enable Image Insertion',
name: 'enableImageInsertion',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
},
},
description: 'Automatically insert images into article content',
},
{
displayName: 'Image Insertion Mode',
name: 'imageInsertionMode',
type: 'options',
options: [
{
name: 'Auto (Smart Positioning)',
value: 'auto',
description: 'Automatically find optimal positions based on content structure',
},
{
name: 'By Paragraph',
value: 'paragraph',
description: 'Insert images between paragraphs at regular intervals',
},
{
name: 'Manual (No Auto-Insert)',
value: 'manual',
description: 'Use images already embedded in content',
},
],
default: 'auto',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
enableImageInsertion: [true],
},
},
description: 'How to insert images into the content',
},
{
displayName: 'Image Media IDs or URLs',
name: 'insertionImageIds',
type: 'json',
default: '',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
enableImageInsertion: [true],
imageInsertionMode: ['auto', 'paragraph'],
},
},
description: 'Image data to insert into article. Supports: 1) Array from "Get Material List" (recommended): {{$json.materials_summary}}, 2) JSON string, 3) Comma-separated URLs. 💡 Best practice: Connect directly from "Get Material List" node using {{$json.materials_summary}}',
},
{
displayName: 'Max Images Per Article',
name: 'maxImagesPerArticle',
type: 'number',
default: 3,
typeOptions: {
minValue: 1,
maxValue: 10,
},
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
enableImageInsertion: [true],
imageInsertionMode: ['auto', 'paragraph'],
},
},
description: 'Maximum number of images to insert into the article',
},
{
displayName: 'Image Selection Strategy',
name: 'imageInsertionStrategy',
type: 'options',
options: [
{
name: 'Sequential',
value: 'sequential',
description: 'Use images in the order provided',
},
{
name: 'Random',
value: 'random',
description: 'Randomly select images from the list',
},
{
name: 'Balanced',
value: 'balanced',
description: 'Distribute images evenly across the list',
},
],
default: 'sequential',
displayOptions: {
show: {
operation: ['createDraft'],
batchProcessingMode: ['single'],
enableImageInsertion: [true],
imageInsertionMode: ['auto', 'paragraph'],
},
},
description: 'Strategy for selecting which images to use',
},
// Media ID Parameters
{
displayName: 'Publish Mode',
name: 'publishMode',
type: 'options',
options: [
{
name: 'Single Draft',
value: 'single',
description: 'Publish a single draft by media ID',
},
{
name: 'Multiple Drafts',
value: 'multiple',
description: 'Publish multiple drafts from JSON array',
},
],
default: 'single',
displayOptions: {
show: {
operation: ['publishDraft'],
},
},
description: 'Choose publishing mode: single draft or multiple drafts',
},
{
displayName: 'Media ID',
name: 'mediaId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['getDraft', 'deleteDraft'],
},
},
description: 'The media ID of the draft',
},
{
displayName: 'Media ID',
name: 'singleMediaId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['publishDraft'],
publishMode: ['single'],
},
},
description: 'The media ID of the draft to publish',
},
{
displayName: 'Media IDs JSON Array',
name: 'multipleMediaIds',
type: 'json',
default: `[
{"media_id": "draft_media_id_1"},
{"media_id": "draft_media_id_2"},
{"media_id": "draft_media_id_3"}
]`,
displayOptions: {
show: {
operation: ['publishDraft'],
publishMode: ['multiple'],
},
},
description: '💡 多个草稿的Media ID数组:系统会依次发布这些草稿。支持格式:1) 直接连接创建草稿节点输出 2) 手动JSON数组。推荐连接创建草稿节点',
},
{
displayName: 'Publish Delay (seconds)',
name: 'multiPublishDelay',
type: 'number',
default: 60,
typeOptions: {
minValue: 0,
maxValue: 3600,
},
displayOptions: {
show: {
operation: ['publishDraft'],
publishMode: ['multiple'],
},
},
description: '每次发布之间的延时间隔(秒)。设置为0表示无延时,推荐设置60-300秒避免频率限制',
},
// Publish ID Parameters
{
displayName: 'Publish ID',
name: 'publishId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['getPublishStatus'],
},
},
description: 'The publish ID to check status',
},
// Upload Media Parameters
{
displayName: 'Media Type',
name: 'mediaType',
type: 'options',
options: [
{
name: 'Image',
value: 'image',
},
{
name: 'Thumbnail',
value: 'thumb',
},
],
default: 'image',
displayOptions: {
show: {
operation: ['uploadMedia'],
},
},
description: 'The type of media to upload',
},
{
displayName: 'Media File',
name: 'mediaFile',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['uploadMedia'],
},
},
description: 'The binary data property name containing the file to upload',
},
// Get Material List Parameters
{
displayName: 'Material Type',
name: 'materialType',
type: 'options',
options: [
{
name: 'Image',
value: 'image',
},
{
name: 'Video',
value: 'video',
},
{
name: 'Voice',
value: 'voice',
},
{
name: 'News',
value: 'news',
},
],
default: 'image',
displayOptions: {
show: {
operation: ['getMaterialList'],
},
},
description: 'The type of materials to retrieve',
},
{
displayName: 'Offset',
name: 'offset',
type: 'number',
default: 0,
displayOptions: {
show: {
operation: ['getMaterialList'],
},
},
description: 'Offset for pagination (starting from 0)',
},
{
displayName: 'Count',
name: 'count',
type: 'number',
default: 20,
typeOptions: {
minValue: 1,
maxValue: 20,
},
displayOptions: {
show: {
operation: ['getMaterialList'],
},
},
description: 'Number of materials to retrieve (max 20)',
},
// Batch Publish Drafts Parameters
{
displayName: 'Draft IDs Data Source',
name: 'draftIdsSource',
type: 'options',
options: [
{
name: 'From Input Data (Recommended)',
value: 'inputData',
description: 'Extract draft IDs from previous node output',
},
{
name: 'Manual Input',
value: 'manual',
description: 'Manually specify draft IDs',
},
],
default: 'inputData',
displayOptions: {
show: {
operation: ['batchPublishDrafts'],
},
},
description: '💡 选择草稿ID数据源:推荐连接创建草稿节点的输出,系统会自动提取所有草稿的media_id',
},
{
displayName: 'Draft Media IDs',
name: 'draftMediaIds',
type: 'string',
default: '',
placeholder: 'media_id_1,media_id_2,media_id_3',
displayOptions: {
show: {
operation: ['batchPublishDrafts'],
draftIdsSource: ['manual'],
},
},
description: '手动输入要发布的草稿Media ID列表,用逗号分隔',
},
{
displayName: 'Publish Delay (seconds)',
name: 'publishDelay',
type: 'number',
default: 60,
typeOptions: {
minValue: 0,
maxValue: 3600,
},
displayOptions: {
show: {
operation: ['batchPublishDrafts'],
},
},
description: '每次发布之间的延时间隔(秒)。设置为0表示无延时,推荐设置60-300秒避免频率限制',
},
{
displayName: 'Stop on Error',
name: 'stopOnError',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['batchPublishDrafts'],
},
},
description: '遇到发布错误时是否停止后续发布。关闭此选项将继续发布剩余草稿',
},
{
displayName: 'Max Retries per Draft',
name: 'maxRetries',
type: 'number',
default: 2,
typeOptions: {
minValue: 0,
maxValue: 5,
},
displayOptions: {
show: {
operation: ['batchPublishDrafts'],
},
},
description: '每个草稿发布失败时的最大重试次数',
},
// Batch Upload Memes Parameters
{
displayName: 'Meme Count',
name: 'memeCount',
type: 'number',
default: 5,
typeOptions: {
minValue: 1,
maxValue: 50,
},
displayOptions: {
show: {
operation: ['batchUploadMemes'],
},
},
description: 'Number of memes to fetch and upload (max 50)',
},
{
displayName: 'Subreddit',
name: 'subreddit',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['batchUploadMemes'],
},
},
description: 'Specific subreddit to fetch memes from (leave empty for random from popular subreddits)',
},
{
displayName: 'Skip NSFW Content',
name: 'skipNsfw',
type: 'boolean',
default: true,
displayOptions: {
show: {
operation: ['batchUploadMemes'],
},
},
description: 'Skip NSFW (Not Safe For Work) content',
},
{
displayName: 'Skip Spoiler Content',
name: 'skipSpoiler',
type: 'boolean',
default: true,
displayOptions: {
show: {
operation: ['batchUploadMemes'],
},
},
description: 'Skip spoiler content',
},
{
displayName: 'Minimum Upvotes',
name: 'minUps',
type: 'number',
default: 0,
displayOptions: {
show: {
operation: ['batchUploadMemes'],
},
},
description: 'Minimum number of upvotes required for a meme to be included',
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
// Get credentials
const credentials = await this.getCredentials('weChatApi');
const apiClient = new WeChatApiClient_1.WeChatApiClient({
appId: credentials.appId,
appSecret: credentials.appSecret,
baseUrl: credentials.baseUrl || 'https://api.weixin.qq.com',
timeout: credentials.timeout || 30000,
});
for (let i = 0; i < items.length; i++) {
try {
const operation = this.getNodeParameter('operation', i);
let responseData;
switch (operation) {
case 'createDraft':
{
const batchProcessingMode = this.getNodeParameter('batchProcessingMode', i);
if (batchProcessingMode === 'htmlArray') {
// HTML Array Mode - Parse HTML content and create articles automatically
const articlesPerBatch = this.getNodeParameter('articlesPerBatch', i, 3);
const thumbnailMediaIdArray = this.getNodeParameter('thumbnailMediaIdArray', i);
const defaultArticleType = this.getNodeParameter('defaultArticleType', i, 'news');
const defaultAuthor = this.getNodeParameter('defaultAuthor', i, '');
const autoGenerateDigest = this.getNodeParameter('autoGenerateDigest', i, true);
const defaultNeedOpenComment = this.getNodeParameter('defaultNeedOpenComment', i, false);
const defaultOnlyFansCanComment = this.getNodeParameter('defaultOnlyFansCanComment', i, false);
// Parse thumbnail media ID array
let thumbnailMediaIds = [];
if (thumbnailMediaIdArray) {
try {
let parsedThumbnails;
if (typeof thumbnailMediaIdArray === 'string') {
parsedThumbnails = JSON.parse(thumbnailMediaIdArray);
}
else {
parsedThumbnails = thumbnailMediaIdArray;
}
if (Array.isArray(parsedThumbnails)) {
thumbnailMediaIds = parsedThumbnails
.filter(item => item && item.media_id)
.map(item => item.media_id);
}
else if (parsedThumbnails.materials_summary && Array.isArray(parsedThumbnails.materials_summary)) {
thumbnailMediaIds = parsedThumbnails.materials_summary
.filter((item) => item && item.media_id)
.map((item) => item.media_id);
}
else if (parsedThumbnails.item && Array.isArray(parsedThumbnails.item)) {
thumbnailMediaIds = parsedThumbnails.item
.filter((item) => item && item.media_id)
.map((item) => item.media_id);
}
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid thumbnail media ID array format: ${error instanceof Error ? error.message : String(error)}`, { itemIndex: i });
}
}
if (thumbnailMediaIds.length === 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No valid thumbnail media IDs found in the thumbnail array. Please provide a valid JSON array with media_id fields.', { itemIndex: i });
}
// HTML Array Mode - Image insertion parameters
const htmlArrayEnableImageInsertion = this.getNodeParameter('htmlArrayEnableImageInsertion', i, false);
const htmlArrayImageInsertionMode = this.getNodeParameter('htmlArrayImageInsertionMode', i, 'auto');
const htmlArrayInsertionImageIds = this.getNodeParameter('htmlArrayInsertionImageIds', i, '');
const htmlArrayMaxImagesPerArticle = this.getNodeParameter('htmlArrayMaxImagesPerArticle', i, 3);
const htmlArrayImageInsertionStrategy = this.getNodeParameter('htmlArrayImageInsertionStrategy', i, 'sequential');
let htmlContentArray = [];
let inputSource = 'parameter'; // 记录数据来源
// 尝试从输入项中获取数据
const inputData = items[i];
if (inputData && inputData.json) {
let dataToProcess = null;
// 情况1:直接是包含output的对象
if (inputData.json.output) {
dataToProcess = [inputData.json];
inputSource = 'input_item_single';
}
// 情况2:是包含output对象的数组
else if (Array.isArray(inputData.json) && inputData.json.length > 0 && inputData.json[0].output) {
dataToProcess = inputData.json;
inputSource = 'input_item_array';
}
// 情况3:是直接的HTML字符串数组
else if (Array.isArray(inputData.json) && inputData.json.length > 0 && typeof inputData.json[0] === 'string') {
htmlContentArray = inputData.json;
inputSource = 'input_item_html_array';
}
// 处理包含output字段的数据
if (dataToProcess) {
htmlContentArray = [];
for (const item of dataToProcess) {
if (item.output && Array.isArray(item.output)) {
htmlContentArray.push(...item.output);
}
else if (item.output && typeof item.output === 'string') {
htmlContentArray.push(item.output);
}
}
}
}
// 如果从输入项中没有获取到有效数据,则从参数中获取
if (!htmlContentArray || htmlContentArray.length === 0) {
const htmlContentArrayJson = this.getNodeParameter('htmlContentArray', i);
inputSource = 'parameter';
try {
const parsedData = JSON.parse(htmlContentArrayJson);
// 支持多种数据格式
if (Array.isArray(parsedData)) {
// 检查是否为对象数组(包含output字段)
if (parsedData.length > 0 && typeof parsedData[0] === 'object' && parsedData[0].output) {
// 从对象数组中提取output字段
htmlContentArray = [];
for (const item of parsedData) {
if (item.output && Array.isArray(item.output)) {
htmlContentArray.push(...item.output);
}
else if (item.output && typeof item.output === 'string') {
htmlContentArray.push(item.output);
}
}
}
else if (parsedData.length > 0 && typeof parsedData[0] === 'string') {
// 直接的HTML字符串数组
htmlContentArray = parsedData;
}
else {
throw