-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathintercom_publish.py
More file actions
231 lines (188 loc) · 8.49 KB
/
intercom_publish.py
File metadata and controls
231 lines (188 loc) · 8.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
"""
This script shows how to use Guru's SDK to publish cards,
boards, or entire collections to an external site, like a
third-party help site.
This script takes the contents of a board in Guru and makes
API calls to Intercom (https://www.intercom.com/) to create
or update articles in Intercom based on the changes seen in
Guru. There are a few parts to this:
1. Behind the scenes, the SDK enumerates all the sections
and cards on the board we specify.
2. The SDK also writes a metadata .json file to keep track
of which cards have been published before.
3. Using the metadata, the SDK knows whether a card has been
published before and needs to be updated in Intercom or is
a brand new card and we need to create an article in Intercom.
The SDK orchestrates everything on the Guru side. This script
just implements the calls to Intercom's API to create and update
each type of object. When the Guru SDK sees a card that's never
been published before, it'll call create_external_card and we
need to implement the Intercom API call to create the article.
"""
import os
import guru
import requests
from urllib.parse import unquote
GURU_USER = os.environ.get("GURU_USER")
GURU_API_TOKEN = os.environ.get("GURU_API_TOKEN")
INTERCOM_API_TOKEN = os.environ.get("INTERCOM_API_TOKEN")
class IntercomPublisher(guru.Publisher):
def __init__(self, g):
super().__init__(g)
# we use this to cache the responses to the API calls
# for looking up articles, sections, or collections.
self.cache = {}
def get_headers(self):
return {
"Authorization": "Bearer %s" % INTERCOM_API_TOKEN
}
def get_all(self, url):
"""
Make a GET call to Intercom's API and use its pagination
to load all pages of results.
"""
if self.cache.get(url):
return self.cache.get(url)
results = []
original_url = url
while url:
response = requests.get(url, headers=self.get_headers())
results += response.json().get("data")
url = response.json().get("pages", {}).get("next")
self.cache[original_url] = results
return results
def get_external_url(self, external_id, card):
"""
This builds the public-facing URL for an Intercom article. We use this
to convert links between Guru Cards to be links between Intercom articles.
"""
return "https://intercom.help/publishing-test-dev/en/articles/%s" % external_id
def find_external_board(self, guru_board):
"""
This checks if a board already exists in Intercom by checking for one
with the same name. Boards in Guru become 'Collections' in Intercom.
"""
intercom_collections = self.get_all("https://api.intercom.io/help_center/collections")
for intercom_collection in intercom_collections:
if intercom_collection["name"].lower() == guru_board.title.lower():
return intercom_collection["id"]
def create_external_board(self, board, board_group, collection):
"""
If a card is in a board and we can't find a 'collection' with the
same name in Intercom, we'll call this function to create the
collection in Intercom. It'd use this API call:
https://developers.intercom.com/intercom-api-reference/reference#create-a-collection
We don't have to implement this. If we don't, then we're simply
requiring that new collections be created manually in Intercom.
This might be fine, depending on how often you plan to make
new collections.
"""
pass
def update_external_board(self, external_id, board, board_group, collection):
"""
This is similar to create_external_board except it's called when
a Guru Board is updated (e.g. you changed it's name) and this would
make the Intercom API call to update a collection:
https://developers.intercom.com/intercom-api-reference/reference#update-a-collection
"""
pass
def find_external_section(self, guru_section):
"""
This checks if a section already exists in Intercom by checking for one with
the same name. This is a little confusing because both Guru and Intercom call
them "sections".
"""
intercom_sections = self.get_all("https://api.intercom.io/help_center/sections")
for intercom_section in intercom_sections:
if intercom_section["name"].lower() == guru_section.title.lower():
return intercom_section["id"]
def create_external_section(self, section, board, board_group, collection):
"""
If a card is in a section and we can't find a section with the
same name in Intercom, we'll call this function to create the
section in Intercom. It'd use this API call:
https://developers.intercom.com/intercom-api-reference/reference#create-a-section
We don't have to implement this. If we don't, then we're simply
requiring that new sections be created manually in Intercom.
This might be fine, depending on how often you plan to make
new sections.
"""
pass
def update_external_section(self, external_id, section, board, board_group, collection):
"""
This is similar to create_external_section except it's called when
a Guru Section is updated (e.g. you changed it's name) and this would
make the Intercom API call to update a section:
https://developers.intercom.com/intercom-api-reference/reference#update-a-section
"""
pass
def find_external_card(self, card):
"""
This checks if a card already exists externally by looking for an Intercom
article with the same title.
"""
articles = self.get_all("https://api.intercom.io/articles")
for article in articles:
if article.get("title").lower() == card.title.lower():
return article.get("id")
def convert_card_to_article(self, card, section, board):
"""
This builds the JSON payload for creating or updating an
Intercom article from the given Guru Card.
"""
data = {
"title": card.title,
"author_id": 5056532,
"body": card.content,
"state": "published",
}
# if the card is on a section, that's its parent in Intercom.
# if it's on a board, then that's its parent in Intercom.
if section:
data["parent_id"] = self.get_external_id(section.id)
data["parent_type"] = "section"
elif board:
data["parent_id"] = self.get_external_id(board.id)
data["parent_type"] = "collection"
return data
def create_external_card(self, card, changes, section, board, board_group, collection):
"""
This method is called automatically when the SDK sees a card
that it knows hasn't been published before. This means we need
to use Intercom's POST endpoint to create a new article.
"""
data = self.convert_card_to_article(card, section, board)
url = "https://api.intercom.io/articles"
# This method has to return the Intercom ID of the new article. We need
# to remember the Intercom ID that's associated with each Guru card so
# the next time we publish this card we can make the 'update' call to
# Intercom to update this particular article.
return requests.post(url, json=data, headers=self.get_headers()).json().get("id")
def update_external_card(self, external_id, card, changes, section, board, board_group, collection):
"""
This script stores metadata so it knows which cards have been
published before. If a card has already been published to
Intercom, it'll call this method so we can make the PUT call
to update the article in Intercom.
"""
data = self.convert_card_to_article(card, section, board)
url = "https://api.intercom.io/articles/%s" % external_id
# this method returns the response object so the SDK will know
# if the API call to update the article was successful.
return requests.put(url, json=data, headers=self.get_headers())
def delete_external_card(self, external_id):
# if we want to automatically delete Intercom articles when the Guru
# cards are archived, we could implement that here.
pass
if __name__ == "__main__":
g = guru.Guru(GURU_USER, GURU_API_TOKEN)
publisher = IntercomPublisher(g)
# 'Gi6dzBxi' is the slug that identifies the board we publish to Intercom.
# you can find this ID in the board's URL, like:
# https://app.getguru.com/boards/Gi6dzBxi/Intercom-Articles
publisher.publish_board("Gi6dzBxi")
# for now, we haven't implemented any of the 'delete' methods. if we do want to
# be able to delete Intercom articles when the guru cards are archived (or when
# they're removed from our 'intercom' board), we'll need to implement the delete
# method and also call process_deletions().
# publisher.process_deletions()