11import type { IExecuteFunctions , INodeExecutionData , IDataObject } from 'n8n-workflow' ;
2+ import { NodeOperationError } from 'n8n-workflow' ;
23import { weComApiRequest } from '../../shared/transport' ;
34
45export async function executeAppChat (
@@ -7,6 +8,47 @@ export async function executeAppChat(
78 items : INodeExecutionData [ ] ,
89) : Promise < INodeExecutionData [ ] > {
910 const returnData : INodeExecutionData [ ] = [ ] ;
11+ const parseOptionalJsonParameter = (
12+ value : unknown ,
13+ parameterName : string ,
14+ itemIndex : number ,
15+ ) : IDataObject | IDataObject [ ] | undefined => {
16+ if ( value === undefined || value === null ) {
17+ return undefined ;
18+ }
19+ if ( typeof value === 'string' ) {
20+ const trimmed = value . trim ( ) ;
21+ if ( ! trimmed || trimmed === '{}' || trimmed === '[]' ) {
22+ return undefined ;
23+ }
24+ try {
25+ return JSON . parse ( trimmed ) as IDataObject | IDataObject [ ] ;
26+ } catch ( error ) {
27+ throw new NodeOperationError (
28+ this . getNode ( ) ,
29+ `${ parameterName } 必须是有效的 JSON: ${ ( error as Error ) . message } ` ,
30+ { itemIndex } ,
31+ ) ;
32+ }
33+ }
34+ return value as IDataObject | IDataObject [ ] ;
35+ } ;
36+ const resolveSafeValue = ( value : unknown ) : number | undefined => {
37+ if ( value === undefined || value === null ) {
38+ return undefined ;
39+ }
40+ if ( typeof value === 'boolean' ) {
41+ return value ? 1 : 0 ;
42+ }
43+ if ( typeof value === 'number' ) {
44+ return value ;
45+ }
46+ if ( typeof value === 'string' ) {
47+ const parsed = Number . parseInt ( value , 10 ) ;
48+ return Number . isNaN ( parsed ) ? undefined : parsed ;
49+ }
50+ return undefined ;
51+ } ;
1052
1153 for ( let i = 0 ; i < items . length ; i ++ ) {
1254 try {
@@ -164,18 +206,69 @@ export async function executeAppChat(
164206 } ,
165207 } ;
166208 } else if ( operation === 'sendNews' ) {
209+ const newsInputMode = this . getNodeParameter ( 'news_input_mode' , i , 'form' ) as string ;
210+ const newsJson = newsInputMode === 'json'
211+ ? parseOptionalJsonParameter (
212+ this . getNodeParameter ( 'news_json' , i , '[]' ) as string ,
213+ 'news_json' ,
214+ i ,
215+ )
216+ : undefined ;
167217 const articles = this . getNodeParameter ( 'articles' , i , { } ) as IDataObject ;
168- const articleList = ( articles . article as IDataObject [ ] ) || [ ] ;
169218 const safe = this . getNodeParameter ( 'safe' , i , false ) as boolean ;
170219
220+ let articleList : IDataObject [ ] = [ ] ;
221+ let safeValue : number | undefined = safe ? 1 : 0 ;
222+
223+ if ( newsInputMode === 'json' ) {
224+ if ( ! newsJson ) {
225+ throw new NodeOperationError (
226+ this . getNode ( ) ,
227+ '请选择 JSON 输入并提供 news_json' ,
228+ { itemIndex : i } ,
229+ ) ;
230+ }
231+ if ( Array . isArray ( newsJson ) ) {
232+ articleList = newsJson as IDataObject [ ] ;
233+ } else if ( ( newsJson as IDataObject ) . news ) {
234+ const newsPayload = ( newsJson as IDataObject ) . news as IDataObject ;
235+ if ( ! Array . isArray ( newsPayload . articles ) ) {
236+ throw new NodeOperationError (
237+ this . getNode ( ) ,
238+ 'news_json.news 必须包含 articles 数组' ,
239+ { itemIndex : i } ,
240+ ) ;
241+ }
242+ articleList = newsPayload . articles as IDataObject [ ] ;
243+ } else if ( Array . isArray ( ( newsJson as IDataObject ) . articles ) ) {
244+ articleList = ( newsJson as IDataObject ) . articles as IDataObject [ ] ;
245+ } else {
246+ throw new NodeOperationError (
247+ this . getNode ( ) ,
248+ 'news_json 必须是图文数组或包含 articles 的对象' ,
249+ { itemIndex : i } ,
250+ ) ;
251+ }
252+
253+ const safeFromJson = resolveSafeValue ( ( newsJson as IDataObject ) . safe ) ;
254+ if ( safeFromJson !== undefined ) {
255+ safeValue = safeFromJson ;
256+ }
257+ } else {
258+ articleList = ( articles . article as IDataObject [ ] ) || [ ] ;
259+ }
260+
171261 body = {
172262 ...body ,
173263 msgtype : 'news' ,
174264 news : {
175265 articles : articleList ,
176266 } ,
177- safe : safe ? 1 : 0 ,
178267 } ;
268+
269+ if ( safeValue !== undefined ) {
270+ body . safe = safeValue ;
271+ }
179272 } else if ( operation === 'sendVoice' ) {
180273 const mediaId = this . getNodeParameter ( 'media_ID' , i ) as string ;
181274
@@ -211,36 +304,130 @@ export async function executeAppChat(
211304 safe : safe ? 1 : 0 ,
212305 } ;
213306 } else if ( operation === 'sendTextCard' ) {
307+ const textcardInputMode = this . getNodeParameter (
308+ 'textcard_input_mode' ,
309+ i ,
310+ 'form' ,
311+ ) as string ;
312+ const textcardJson = textcardInputMode === 'json'
313+ ? parseOptionalJsonParameter (
314+ this . getNodeParameter ( 'textcard_json' , i , '{}' ) as string ,
315+ 'textcard_json' ,
316+ i ,
317+ )
318+ : undefined ;
214319 const title = this . getNodeParameter ( 'title' , i ) as string ;
215320 const description = this . getNodeParameter ( 'description' , i ) as string ;
216321 const url = this . getNodeParameter ( 'url' , i ) as string ;
217322 const btntxt = this . getNodeParameter ( 'btntxt' , i , '详情' ) as string ;
218323 const safe = this . getNodeParameter ( 'safe' , i , false ) as boolean ;
219324
220- body = {
221- ...body ,
222- msgtype : 'textcard' ,
223- textcard : {
325+ let textcard : IDataObject ;
326+ let safeValue : number | undefined = safe ? 1 : 0 ;
327+
328+ if ( textcardInputMode === 'json' ) {
329+ if ( ! textcardJson ) {
330+ throw new NodeOperationError (
331+ this . getNode ( ) ,
332+ '请选择 JSON 输入并提供 textcard_json' ,
333+ { itemIndex : i } ,
334+ ) ;
335+ }
336+ if ( Array . isArray ( textcardJson ) ) {
337+ throw new NodeOperationError (
338+ this . getNode ( ) ,
339+ 'textcard_json 必须是对象' ,
340+ { itemIndex : i } ,
341+ ) ;
342+ }
343+ const textcardPayload = ( textcardJson as IDataObject ) . textcard as IDataObject | undefined ;
344+ textcard = textcardPayload ?? ( textcardJson as IDataObject ) ;
345+ const safeFromJson = resolveSafeValue ( ( textcardJson as IDataObject ) . safe ) ;
346+ if ( safeFromJson !== undefined ) {
347+ safeValue = safeFromJson ;
348+ }
349+ } else {
350+ textcard = {
224351 title,
225352 description,
226353 url,
227354 btntxt,
228- } ,
229- safe : safe ? 1 : 0 ,
355+ } ;
356+ }
357+
358+ body = {
359+ ...body ,
360+ msgtype : 'textcard' ,
361+ textcard,
230362 } ;
363+
364+ if ( safeValue !== undefined ) {
365+ body . safe = safeValue ;
366+ }
231367 } else if ( operation === 'sendMpNews' ) {
368+ const mpnewsInputMode = this . getNodeParameter ( 'mpnews_input_mode' , i , 'form' ) as string ;
369+ const mpnewsJson = mpnewsInputMode === 'json'
370+ ? parseOptionalJsonParameter (
371+ this . getNodeParameter ( 'mpnews_json' , i , '[]' ) as string ,
372+ 'mpnews_json' ,
373+ i ,
374+ )
375+ : undefined ;
232376 const articles = this . getNodeParameter ( 'articles' , i , { } ) as IDataObject ;
233- const articleList = ( articles . article as IDataObject [ ] ) || [ ] ;
234377 const safe = this . getNodeParameter ( 'safe' , i , false ) as boolean ;
235378
379+ let articleList : IDataObject [ ] = [ ] ;
380+ let safeValue : number | undefined = safe ? 1 : 0 ;
381+
382+ if ( mpnewsInputMode === 'json' ) {
383+ if ( ! mpnewsJson ) {
384+ throw new NodeOperationError (
385+ this . getNode ( ) ,
386+ '请选择 JSON 输入并提供 mpnews_json' ,
387+ { itemIndex : i } ,
388+ ) ;
389+ }
390+ if ( Array . isArray ( mpnewsJson ) ) {
391+ articleList = mpnewsJson as IDataObject [ ] ;
392+ } else if ( ( mpnewsJson as IDataObject ) . mpnews ) {
393+ const mpnewsPayload = ( mpnewsJson as IDataObject ) . mpnews as IDataObject ;
394+ if ( ! Array . isArray ( mpnewsPayload . articles ) ) {
395+ throw new NodeOperationError (
396+ this . getNode ( ) ,
397+ 'mpnews_json.mpnews 必须包含 articles 数组' ,
398+ { itemIndex : i } ,
399+ ) ;
400+ }
401+ articleList = mpnewsPayload . articles as IDataObject [ ] ;
402+ } else if ( Array . isArray ( ( mpnewsJson as IDataObject ) . articles ) ) {
403+ articleList = ( mpnewsJson as IDataObject ) . articles as IDataObject [ ] ;
404+ } else {
405+ throw new NodeOperationError (
406+ this . getNode ( ) ,
407+ 'mpnews_json 必须是图文数组或包含 articles 的对象' ,
408+ { itemIndex : i } ,
409+ ) ;
410+ }
411+
412+ const safeFromJson = resolveSafeValue ( ( mpnewsJson as IDataObject ) . safe ) ;
413+ if ( safeFromJson !== undefined ) {
414+ safeValue = safeFromJson ;
415+ }
416+ } else {
417+ articleList = ( articles . article as IDataObject [ ] ) || [ ] ;
418+ }
419+
236420 body = {
237421 ...body ,
238422 msgtype : 'mpnews' ,
239423 mpnews : {
240424 articles : articleList ,
241425 } ,
242- safe : safe ? 1 : 0 ,
243426 } ;
427+
428+ if ( safeValue !== undefined ) {
429+ body . safe = safeValue ;
430+ }
244431 }
245432
246433 const response = await weComApiRequest . call ( this , 'POST' , '/cgi-bin/appchat/send' , body ) ;
@@ -266,4 +453,3 @@ export async function executeAppChat(
266453
267454 return returnData ;
268455}
269-
0 commit comments