@@ -298,6 +298,24 @@ export class TypebotService extends BaseChatbotService<TypebotModel, any> {
298298 return null ;
299299 } ;
300300
301+ let transferToHumanRequested = false ;
302+
303+ // Check if [transfer_human] marker detection is enabled for this instance
304+ let detectMarkerEnabled = true ;
305+ try {
306+ // eslint-disable-next-line @typescript-eslint/no-var-requires
307+ const { chatbotChatwootService : markerSvc } = require ( '@api/server.module' ) ;
308+ if ( markerSvc ) {
309+ const instDb = await this . prismaRepository . instance . findFirst ( { where : { name : instance . instanceName } } ) ;
310+ if ( instDb ) {
311+ const cfg = await markerSvc . getCoordinationConfig ( instDb . id ) ;
312+ detectMarkerEnabled = cfg . detectTransferMarker ;
313+ }
314+ }
315+ } catch {
316+ // Default: enabled
317+ }
318+
301319 for ( const message of messages ) {
302320 if ( message . type === 'text' ) {
303321 let formattedText = '' ;
@@ -313,6 +331,14 @@ export class TypebotService extends BaseChatbotService<TypebotModel, any> {
313331
314332 formattedText = formattedText . replace ( / \n $ / , '' ) ;
315333
334+ // Detect [transfer_human] marker from Typebot flow (configurable per instance)
335+ if ( detectMarkerEnabled && formattedText . includes ( '[transfer_human]' ) ) {
336+ transferToHumanRequested = true ;
337+ formattedText = formattedText . replace ( '[transfer_human]' , '' ) . trim ( ) ;
338+ this . logger . log ( `[Coordination] Detected [transfer_human] marker in Typebot message for ${ session . remoteJid } ` ) ;
339+ if ( ! formattedText ) continue ; // skip empty message after stripping marker
340+ }
341+
316342 if ( formattedText . includes ( '[list]' ) ) {
317343 await this . processListMessage ( instance , formattedText , session . remoteJid ) ;
318344 } else if ( formattedText . includes ( '[buttons]' ) ) {
@@ -408,55 +434,84 @@ export class TypebotService extends BaseChatbotService<TypebotModel, any> {
408434 } ,
409435 } ) ;
410436 } else {
411- let statusChange = 'closed' ;
412- if ( ! settings ?. keepOpen ) {
413- await prismaRepository . integrationSession . deleteMany ( {
414- where : {
415- id : session . id ,
416- } ,
417- } ) ;
418- statusChange = 'delete' ;
437+ // Check if transfer_human was requested via [transfer_human] marker or HTTP Request
438+ const currentSession = await prismaRepository . integrationSession . findUnique ( {
439+ where : { id : session . id } ,
440+ } ) ;
441+ const sessionPaused = currentSession ?. status === 'paused' ;
442+
443+ if ( transferToHumanRequested || sessionPaused ) {
444+ this . logger . log (
445+ `[Coordination] Transfer to human requested for ${ session . remoteJid } (marker: ${ transferToHumanRequested } , paused: ${ sessionPaused } )` ,
446+ ) ;
447+
448+ // Execute transfer_human logic internally
449+ if ( transferToHumanRequested && ! sessionPaused ) {
450+ try {
451+ // eslint-disable-next-line @typescript-eslint/no-var-requires
452+ const { chatbotChatwootService } = require ( '@api/server.module' ) ;
453+ if ( chatbotChatwootService ) {
454+ const instanceDb = await this . prismaRepository . instance . findFirst ( { where : { name : instance . instanceName } } ) ;
455+ if ( instanceDb ) {
456+ const result = await chatbotChatwootService . transferToHuman ( instanceDb . id , session . remoteJid ) ;
457+ this . logger . log ( `[Coordination] transferToHuman result: ${ JSON . stringify ( result ) } ` ) ;
458+ }
459+ }
460+ } catch ( err ) {
461+ this . logger . error ( `[Coordination] Error executing transferToHuman: ${ err ?. message } ` ) ;
462+ }
463+ }
419464 } else {
420- await prismaRepository . integrationSession . update ( {
421- where : {
422- id : session . id ,
423- } ,
424- data : {
425- status : 'closed' ,
426- } ,
427- } ) ;
428- }
465+ let statusChange = 'closed' ;
466+ if ( ! settings ?. keepOpen ) {
467+ await prismaRepository . integrationSession . deleteMany ( {
468+ where : {
469+ id : session . id ,
470+ } ,
471+ } ) ;
472+ statusChange = 'delete' ;
473+ } else {
474+ await prismaRepository . integrationSession . update ( {
475+ where : {
476+ id : session . id ,
477+ } ,
478+ data : {
479+ status : 'closed' ,
480+ } ,
481+ } ) ;
482+ }
429483
430- // Coordination: resolve Chatwoot conversation when bot flow completes
431- // Respects autoResolve config (global env var + per-instance override)
432- try {
433- let shouldAutoResolve = true ;
484+ // Coordination: resolve Chatwoot conversation when bot flow completes
485+ // Respects autoResolve config (global env var + per-instance override)
434486 try {
435- // eslint-disable-next-line @typescript-eslint/no-var-requires
436- const { chatbotChatwootService } = require ( '@api/server.module' ) ;
437- if ( chatbotChatwootService ) {
438- const instanceDb = await this . prismaRepository . instance . findFirst ( { where : { name : instance . instanceName } } ) ;
439- if ( instanceDb ) {
440- const config = await chatbotChatwootService . getCoordinationConfig ( instanceDb . id ) ;
441- shouldAutoResolve = config . autoResolve ;
487+ let shouldAutoResolve = true ;
488+ try {
489+ // eslint-disable-next-line @typescript-eslint/no-var-requires
490+ const { chatbotChatwootService } = require ( '@api/server.module' ) ;
491+ if ( chatbotChatwootService ) {
492+ const instanceDb = await this . prismaRepository . instance . findFirst ( { where : { name : instance . instanceName } } ) ;
493+ if ( instanceDb ) {
494+ const config = await chatbotChatwootService . getCoordinationConfig ( instanceDb . id ) ;
495+ shouldAutoResolve = config . autoResolve ;
496+ }
442497 }
498+ } catch {
499+ // If service not available, use default (true)
443500 }
444- } catch {
445- // If service not available, use default (true)
446- }
447- if ( shouldAutoResolve ) {
448- await this . resolveChatwootConversation ( session . remoteJid , instance ) ;
501+ if ( shouldAutoResolve ) {
502+ await this . resolveChatwootConversation ( session . remoteJid , instance ) ;
503+ }
504+ } catch ( error ) {
505+ this . logger . error ( `[Coordination] Error checking autoResolve config: ${ error ?. message } ` ) ;
449506 }
450- } catch ( error ) {
451- this . logger . error ( `[Coordination] Error checking autoResolve config: ${ error ?. message } ` ) ;
452- }
453507
454- const typebotData = {
455- remoteJid : session . remoteJid ,
456- status : statusChange ,
457- session,
458- } ;
459- instance . sendDataWebhook ( Events . TYPEBOT_CHANGE_STATUS , typebotData ) ;
508+ const typebotData = {
509+ remoteJid : session . remoteJid ,
510+ status : statusChange ,
511+ session,
512+ } ;
513+ instance . sendDataWebhook ( Events . TYPEBOT_CHANGE_STATUS , typebotData ) ;
514+ }
460515 }
461516 }
462517
@@ -485,7 +540,7 @@ export class TypebotService extends BaseChatbotService<TypebotModel, any> {
485540 key : { path : [ 'remoteJid' ] , equals : remoteJid } ,
486541 chatwootConversationId : { not : null } ,
487542 } ,
488- orderBy : { createdAt : 'desc' } ,
543+ orderBy : { messageTimestamp : 'desc' } ,
489544 } ) ;
490545
491546 if ( ! recentMessage ?. chatwootConversationId ) {
0 commit comments