UNPKG

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
"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