1- import { createIntegration , createOAuthHandler , ExposableError , Logger } from '@gitbook/runtime' ;
2- import { Intercom } from 'intercom-client' ;
3- import { getIntercomClient , getIntercomOAuthConfig } from './client' ;
1+ import { Router } from 'itty-router' ;
2+
3+ import {
4+ createIntegration ,
5+ createOAuthHandler ,
6+ ExposableError ,
7+ Logger ,
8+ verifyIntegrationRequestSignature ,
9+ } from '@gitbook/runtime' ;
10+ import { getIntercomOAuthConfig } from './client' ;
411import { configComponent } from './config' ;
5- import { ingestConversations , parseConversationAsGitBook } from './conversations' ;
6- import { IntercomRuntimeContext } from './types' ;
12+ import { ingestLastClosedIntercomConversations } from './conversations' ;
13+ import type { IntercomIntegrationTask , IntercomRuntimeContext } from './types' ;
14+ import { handleIntercomWebhookRequest } from './intercom-webhooks' ;
15+ import { handleIntercomIntegrationTask } from './tasks' ;
716
817const logger = Logger ( 'intercom-conversations' ) ;
918
10- /**
11- * https://developers.intercom.com/docs/references/webhooks/webhook-models#webhook-notification-object
12- */
13- type IntercomWebhookPayload = {
14- type : 'notification_event' ;
15- // This is the workspace ID
16- app_id : string ;
17- topic : 'conversation.admin.closed' ;
18- data : {
19- item : Intercom . Conversation ;
20- } ;
21- } ;
22-
2319export default createIntegration < IntercomRuntimeContext > ( {
2420 fetch : async ( request , context ) => {
25- const url = new URL ( request . url ) ;
21+ const { environment } = context ;
22+
23+ const router = Router ( {
24+ base : new URL (
25+ environment . installation ?. urls . publicEndpoint ||
26+ environment . integration . urls . publicEndpoint ,
27+ ) . pathname ,
28+ } ) ;
2629
2730 /*
28- * Webhook to ingest conversations when they are closed .
31+ * OAuth flow .
2932 */
30- if ( url . pathname . endsWith ( '/webhook' ) ) {
31- const payload = await request . json < IntercomWebhookPayload > ( ) ;
32-
33- if ( payload . topic === 'conversation.admin.closed' ) {
34- const appId = payload . app_id ;
35-
36- // Find all installations matching this Intercom workspace (externalId = app_id)
37- const {
38- data : { items : installations } ,
39- } = await context . api . integrations . listIntegrationInstallations (
40- context . environment . integration . name ,
41- {
42- externalId : appId ,
43- } ,
44- ) ;
45-
46- if ( installations . length === 0 ) {
47- throw new Error ( `No installations found for Intercom workspace: ${ appId } ` ) ;
48- }
33+ router . get (
34+ '/oauth' ,
35+ createOAuthHandler ( getIntercomOAuthConfig ( context ) , {
36+ replace : false ,
37+ } ) ,
38+ ) ;
4939
50- const conversation = payload . data . item ;
51- logger . info (
52- `Webhook received with topic '${ payload . topic } ' for conversation id ${ conversation . id } . Processing for installations ${ installations . join ( ' ' ) } ` ,
53- ) ;
40+ /*
41+ * Webhook handler to ingest conversations when they are closed.
42+ */
43+ router . post ( '/webhook' , async ( request ) => {
44+ return handleIntercomWebhookRequest ( request , context ) ;
45+ } ) ;
5446
55- for ( const installation of installations ) {
56- try {
57- const installationContext : IntercomRuntimeContext = {
58- ...context ,
59- environment : {
60- ...context . environment ,
61- installation,
62- } ,
63- } ;
47+ /**
48+ * Integration tasks handler.
49+ */
50+ router . post ( '/tasks' , async ( request ) => {
51+ const verified = await verifyIntegrationRequestSignature ( request , environment ) ;
6452
65- const intercomClient = await getIntercomClient ( installationContext ) ;
53+ if ( ! verified ) {
54+ const message = `Invalid signature for integration task` ;
55+ logger . error ( message ) ;
56+ throw new ExposableError ( message ) ;
57+ }
6658
67- const gitbookConversation = await parseConversationAsGitBook (
68- intercomClient ,
69- conversation ,
70- ) ;
59+ const { task } = JSON . parse ( await request . text ( ) ) as { task : IntercomIntegrationTask } ;
60+ logger . debug ( 'Verified & received integration task' , task ) ;
7161
72- const installationApiClient = await context . api . createInstallationClient (
73- context . environment . integration . name ,
74- installation . id ,
75- ) ;
62+ context . waitUntil (
63+ ( async ( ) => {
64+ await handleIntercomIntegrationTask ( context , task ) ;
65+ } ) ( ) ,
66+ ) ;
7667
77- await installationApiClient . orgs . ingestConversation (
78- installation . target . organization ,
79- [ gitbookConversation ] ,
80- ) ;
81- } catch ( error ) {
82- logger . error ( 'Failed processing Intercom webhook for installation' , {
83- installationId : installation . id ,
84- error : error instanceof Error ? error . message : String ( error ) ,
85- } ) ;
86- }
87- }
88- } else {
89- throw new ExposableError ( `Unknown webhook received: ${ payload . topic } ` ) ;
68+ return new Response ( JSON . stringify ( { acknowledged : true } ) , {
69+ status : 200 ,
70+ headers : { 'content-type' : 'application/json' } ,
71+ } ) ;
72+ } ) ;
73+
74+ try {
75+ const response = await router . handle ( request , context ) ;
76+ if ( ! response ) {
77+ return new Response ( `No route matching ${ request . method } ${ request . url } ` , {
78+ status : 404 ,
79+ } ) ;
9080 }
91-
92- return new Response ( 'OK' , { status : 200 } ) ;
93- }
94-
95- /*
96- * OAuth flow.
97- */
98- if ( url . pathname . endsWith ( '/oauth' ) ) {
99- const oauthHandler = createOAuthHandler ( getIntercomOAuthConfig ( context ) , {
100- replace : false ,
81+ return response ;
82+ } catch ( error : any ) {
83+ logger . error ( `error handling request ${ error . message } ${ error . stack } ` ) ;
84+ return new Response ( 'Unexpected error' , {
85+ status : 500 ,
10186 } ) ;
102- return oauthHandler ( request , context ) ;
10387 }
104-
105- return new Response ( 'Not found' , { status : 404 } ) ;
10688 } ,
10789 components : [ configComponent ] ,
10890 events : {
@@ -112,7 +94,7 @@ export default createIntegration<IntercomRuntimeContext>({
11294 installation_setup : async ( _ , context ) => {
11395 const { installation } = context . environment ;
11496 if ( installation ?. configuration . oauth_credentials ) {
115- await ingestConversations ( context ) ;
97+ await ingestLastClosedIntercomConversations ( context ) ;
11698 }
11799 } ,
118100 } ,
0 commit comments