Skip to content

Commit

Permalink
add oauth authentication and update contacts template
Browse files Browse the repository at this point in the history
  • Loading branch information
MarconLP committed Jan 2, 2025
1 parent 6463378 commit dda1ca3
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 47 deletions.
2 changes: 2 additions & 0 deletions frontend/src/lib/integrations/integrationsLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import IconGoogleAds from 'public/services/google-ads.png'
import IconGoogleCloud from 'public/services/google-cloud.png'
import IconGoogleCloudStorage from 'public/services/google-cloud-storage.png'
import IconHubspot from 'public/services/hubspot.png'
import IconIntercom from 'public/services/intercom.png'
import IconSalesforce from 'public/services/salesforce.png'
import IconSlack from 'public/services/slack.png'
import IconSnapchat from 'public/services/snapchat.png'
Expand All @@ -26,6 +27,7 @@ const ICONS: Record<IntegrationKind, any> = {
'google-cloud-storage': IconGoogleCloudStorage,
'google-ads': IconGoogleAds,
snapchat: IconSnapchat,
intercom: IconIntercom,
}

export const integrationsLogic = kea<integrationsLogicType>([
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3763,6 +3763,7 @@ export type IntegrationKind =
| 'google-cloud-storage'
| 'google-ads'
| 'snapchat'
| 'intercom'

export interface IntegrationType {
id: number
Expand Down
7 changes: 6 additions & 1 deletion posthog/cdp/templates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
from .hubspot.template_hubspot import template_event as hubspot_event, template as hubspot, TemplateHubspotMigrator
from .braze.template_braze import template as braze
from .customerio.template_customerio import template as customerio, TemplateCustomerioMigrator
from .intercom.template_intercom import template as intercom, TemplateIntercomMigrator
from .intercom.template_intercom import (
template as intercom,
template_send_event as intercom_send_event,
TemplateIntercomMigrator,
)
from .sendgrid.template_sendgrid import template as sendgrid, TemplateSendGridMigrator
from .clearbit.template_clearbit import template as clearbit
from .june.template_june import template as june
Expand Down Expand Up @@ -77,6 +81,7 @@
hubspot,
hubspot_event,
intercom,
intercom_send_event,
june,
klaviyo_event,
klaviyo_user,
Expand Down
288 changes: 243 additions & 45 deletions posthog/cdp/templates/intercom/template_intercom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,89 +2,287 @@
import dataclasses
from posthog.cdp.templates.hog_function_template import HogFunctionTemplate, HogFunctionTemplateMigrator


template: HogFunctionTemplate = HogFunctionTemplate(
status="beta",
type="destination",
id="template-Intercom",
id="template-intercom",
name="Intercom",
description="Send events and contact information to Intercom",
description="Update contacts in Intercom",
icon_url="/static/services/intercom.png",
category=["Customer Success"],
hog="""
if (empty(inputs.email)) {
print('`email` input is empty. Skipping.')
print('No email set. Skipping...')
return
}
let res := fetch(f'https://{inputs.host}/events', {
'method': 'POST',
'headers': {
'Authorization': f'Bearer {inputs.access_token}',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
'body': {
'event_name': event.event,
'created_at': toInt(toUnixTimestamp(toDateTime(event.timestamp))),
'email': inputs.email,
'id': event.distinct_id,
}
let regions := {
'US': 'api.intercom.io',
'EU': 'api.eu.intercom.io',
'AU': 'api.au.intercom.io',
}
let user := fetch(f'https://{regions[inputs.oauth['app.region']]}/contacts/search', {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Intercom-Version': '2.11',
'Accept': 'application/json',
'Authorization': f'Bearer {inputs.oauth.access_token}',
},
'body': {
'query': {
'field': 'email',
'operator': '=',
'value': inputs.email
}
}
})
if (res.status >= 200 and res.status < 300) {
print('Event sent successfully!')
return
let payload := {
'email': inputs.email
}
if (res.status == 404) {
throw Error('No existing contact found for email')
return
if (inputs.include_all_properties) {
for (let key, value in person.properties) {
if (not empty(value) and not key like '$%') {
payload[key] := value
}
}
}
throw Error(f'Error from intercom api (status {res.status}): {res.body}')
for (let key, value in inputs.properties) {
if (not empty(value)) {
payload[key] := value
}
}
let res
if (user.body.total_count == 1) {
res := fetch(f'https://{regions[inputs.oauth['app.region']]}/contacts/{user.body.data.1.id}', {
'method': 'PUT',
'headers': {
'Content-Type': 'application/json',
'Intercom-Version': '2.11',
'Accept': 'application/json',
'Authorization': f'Bearer {inputs.oauth.access_token}',
},
'body': payload
})
} else if (user.body.total_count == 0) {
res := fetch(f'https://{regions[inputs.oauth['app.region']]}/contacts', {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Intercom-Version': '2.11',
'Accept': 'application/json',
'Authorization': f'Bearer {inputs.oauth.access_token}',
},
'body': payload
})
} else {
throw Error('Found multiple contacts with the same email address. Skipping...')
}
if (res.status >= 400) {
throw Error(f'Error from intercom api (status {res.status}): {res.body}')
} else if (user.status >= 400) {
throw Error(f'Error from intercom api (status {user.status}): {user.body}')
}
""".strip(),
inputs_schema=[
{
"key": "access_token",
"key": "oauth",
"type": "integration",
"integration": "intercom",
"label": "Intercom account",
"requiredScopes": "placeholder", # intercom scopes are only configurable in the oauth app settings
"secret": False,
"required": True,
},
{
"key": "email",
"type": "string",
"label": "Intercom access token",
"description": "Create an Intercom app (https://developers.intercom.com/docs/build-an-integration/learn-more/authentication), then go to Configure > Authentication to find your token.",
"secret": True,
"label": "Email of the user",
"description": "Where to find the email of the user.",
"default": "{person.properties.email}",
"secret": False,
"required": True,
},
{
"key": "host",
"type": "choice",
"choices": [
{
"label": "US (api.intercom.io)",
"value": "api.intercom.io",
},
{
"label": "EU (api.eu.intercom.com)",
"value": "api.eu.intercom.com",
},
],
"label": "Data region",
"description": "Use the EU variant if your Intercom account is based in the EU region",
"default": "api.intercom.io",
"key": "include_all_properties",
"type": "boolean",
"label": "Include all properties as attributes",
"description": "If set, all person properties will be included. Individual attributes can be overridden below.",
"default": False,
"secret": False,
"required": True,
},
{
"key": "properties",
"type": "dictionary",
"label": "Property mapping",
"description": "Map of Intercom properties and their values.",
"default": {
"name": "{f'{person.properties.first_name} {person.properties.last_name}' == ' ' ? null : f'{person.properties.first_name} {person.properties.last_name}'}",
"phone": "{person.properties.phone}",
"last_seen_at": "{toUnixTimestamp(event.timestamp)}",
},
"secret": False,
"required": False,
},
],
filters={
"events": [
{"id": "$identify", "name": "$identify", "type": "events", "order": 0},
{"id": "$set", "name": "$set", "type": "events", "order": 1},
],
"actions": [],
"filter_test_accounts": True,
},
)

template_send_event: HogFunctionTemplate = HogFunctionTemplate(
status="beta",
type="destination",
id="template-intercom-event",
name="Intercom",
description="Send events to Intercom",
icon_url="/static/services/intercom.png",
category=["Customer Success"],
hog="""
if (empty(inputs.email)) {
print('No email set. Skipping...')
return
}
let regions := {
'US': 'api.intercom.io',
'EU': 'api.eu.intercom.io',
'AU': 'api.au.intercom.io',
}
let user := fetch(f'https://{regions[inputs.oauth['app.region']]}/contacts/search', {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Intercom-Version': '2.11',
'Accept': 'application/json',
'Authorization': f'Bearer {inputs.oauth.access_token}',
},
'body': {
'query': {
'field': 'email',
'operator': '=',
'value': inputs.email
}
}
})
let payload := {
'email': inputs.email
}
if (inputs.include_all_properties) {
for (let key, value in person.properties) {
if (not empty(value) and not key like '$%') {
payload[key] := value
}
}
}
for (let key, value in inputs.properties) {
if (not empty(value)) {
payload[key] := value
}
}
let res
if (user.body.total_count == 1) {
res := fetch(f'https://{regions[inputs.oauth['app.region']]}/contacts/{user.body.data.1.id}', {
'method': 'PUT',
'headers': {
'Content-Type': 'application/json',
'Intercom-Version': '2.11',
'Accept': 'application/json',
'Authorization': f'Bearer {inputs.oauth.access_token}',
},
'body': payload
})
}
if (res.status >= 400) {
throw Error(f'Error from intercom api (status {res.status}): {res.body}')
} else if (user.status >= 400) {
throw Error(f'Error from intercom api (status {user.status}): {user.body}')
}
""".strip(),
inputs_schema=[
{
"key": "oauth",
"type": "integration",
"integration": "intercom",
"label": "Intercom account",
"requiredScopes": "placeholder", # intercom scopes are only configurable in the oauth app settings
"secret": False,
"required": True,
},
{
"key": "email",
"type": "string",
"label": "Email of the user",
"description": "Where to find the email for the contact to be created. You can use the filters section to filter out unwanted emails or internal users.",
"description": "Where to find the email of the user.",
"default": "{person.properties.email}",
"secret": False,
"required": True,
},
{
"key": "eventName",
"type": "string",
"label": "Event name",
"description": "A standard event or custom event name.",
"default": "{event.event}",
"secret": False,
"required": True,
},
{
"key": "eventTime",
"type": "string",
"label": "Event time",
"description": "A Unix timestamp in seconds indicating when the actual event occurred.",
"default": "{toInt(toUnixTimestamp(event.timestamp))}",
"secret": False,
"required": True,
},
{
"key": "include_all_properties",
"type": "boolean",
"label": "Include all properties as attributes",
"description": "If set, all person properties will be included. Individual attributes can be overridden below.",
"default": False,
"secret": False,
"required": True,
},
{
"key": "properties",
"type": "dictionary",
"label": "Property mapping",
"description": "Map of Intercom properties and their values. You can use the filters section to filter out unwanted events.",
"default": {
"name": "{f'{person.properties.first_name} {person.properties.last_name}' == ' ' ? null : f'{person.properties.first_name} {person.properties.last_name}'}",
"phone": "{person.properties.phone}",
"last_seen_at": "{toUnixTimestamp(event.timestamp)}",
},
"secret": False,
"required": False,
},
],
filters={
"events": [{"id": "$identify", "name": "$identify", "type": "events", "order": 0}],
"events": [
{"id": "$pageview", "name": "$pageview", "type": "events", "order": 0},
],
"actions": [],
"filter_test_accounts": True,
},
Expand Down
Loading

0 comments on commit dda1ca3

Please sign in to comment.