forked from slackapi/Slack-Python-Onboarding-Tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bot.py
249 lines (225 loc) · 11.3 KB
/
bot.py
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# -*- coding: utf-8 -*-
"""
Python Slack Bot class for use with the pythOnBoarding app
"""
import os
import message
from slackclient import SlackClient
# To remember which teams have authorized your app and what tokens are
# associated with each team, we can store this information in memory on
# as a global object. When your bot is out of development, it's best to
# save this in a more persistant memory store.
authed_teams = {}
class Bot(object):
""" Instanciates a Bot object to handle Slack onboarding interactions."""
def __init__(self):
super(Bot, self).__init__()
self.name = "pythonboardingbot"
self.emoji = ":robot_face:"
# When we instantiate a new bot object, we can access the app
# credentials we set earlier in our local development environment.
self.oauth = {"client_id": os.environ.get("CLIENT_ID"),
"client_secret": os.environ.get("CLIENT_SECRET"),
# Scopes provide and limit permissions to what our app
# can access. It's important to use the most restricted
# scope that your app will need.
"scope": "bot"}
self.verification = os.environ.get("VERIFICATION_TOKEN")
# NOTE: Python-slack requires a client connection to generate
# an oauth token. We can connect to the client without authenticating
# by passing an empty string as a token and then reinstantiating the
# client with a valid OAuth token once we have one.
self.client = SlackClient("")
# We'll use this dictionary to store the state of each message object.
# In a production envrionment you'll likely want to store this more
# persistantly in a database.
self.messages = {}
def auth(self, code):
"""
Authenticate with OAuth and assign correct scopes.
Save a dictionary of authed team information in memory on the bot
object.
Parameters
----------
code : str
temporary authorization code sent by Slack to be exchanged for an
OAuth token
"""
# After the user has authorized this app for use in their Slack team,
# Slack returns a temporary authorization code that we'll exchange for
# an OAuth token using the oauth.access endpoint
auth_response = self.client.api_call(
"oauth.access",
client_id=self.oauth["client_id"],
client_secret=self.oauth["client_secret"],
code=code
)
# To keep track of authorized teams and their associated OAuth tokens,
# we will save the team ID and bot tokens to the global
# authed_teams object
team_id = auth_response["team_id"]
authed_teams[team_id] = {"bot_token":
auth_response["bot"]["bot_access_token"]}
# Then we'll reconnect to the Slack Client with the correct team's
# bot token
self.client = SlackClient(authed_teams[team_id]["bot_token"])
def open_dm(self, user_id):
"""
Open a DM to send a welcome message when a 'team_join' event is
recieved from Slack.
Parameters
----------
user_id : str
id of the Slack user associated with the 'team_join' event
Returns
----------
dm_id : str
id of the DM channel opened by this method
"""
new_dm = self.client.api_call("im.open",
user=user_id)
dm_id = new_dm["channel"]["id"]
return dm_id
def onboarding_message(self, team_id, user_id):
"""
Create and send an onboarding welcome message to new users. Save the
time stamp of this message on the message object for updating in the
future.
Parameters
----------
team_id : str
id of the Slack team associated with the incoming event
user_id : str
id of the Slack user associated with the incoming event
"""
# We've imported a Message class from `message.py` that we can use
# to create message objects for each onboarding message we send to a
# user. We can use these objects to keep track of the progress each
# user on each team has made getting through our onboarding tutorial.
# First, we'll check to see if there's already messages our bot knows
# of for the team id we've got.
if self.messages.get(team_id):
# Then we'll update the message dictionary with a key for the
# user id we've recieved and a value of a new message object
self.messages[team_id].update({user_id: message.Message()})
else:
# If there aren't any message for that team, we'll add a dictionary
# of messages for that team id on our Bot's messages attribute
# and we'll add the first message object to the dictionary with
# the user's id as a key for easy access later.
self.messages[team_id] = {user_id: message.Message()}
message_obj = self.messages[team_id][user_id]
# Then we'll set that message object's channel attribute to the DM
# of the user we'll communicate with
message_obj.channel = self.open_dm(user_id)
# We'll use the message object's method to create the attachments that
# we'll want to add to our Slack message. This method will also save
# the attachments on the message object which we're accessing in the
# API call below through the message object's `attachments` attribute.
message_obj.create_attachments()
post_message = self.client.api_call("chat.postMessage",
channel=message_obj.channel,
username=self.name,
icon_emoji=self.emoji,
text=message_obj.text,
attachments=message_obj.attachments
)
timestamp = post_message["ts"]
# We'll save the timestamp of the message we've just posted on the
# message object which we'll use to update the message after a user
# has completed an onboarding task.
message_obj.timestamp = timestamp
def update_emoji(self, team_id, user_id):
"""
Update onboarding welcome message after recieving a "reaction_added"
event from Slack. Update timestamp for welcome message.
Parameters
----------
team_id : str
id of the Slack team associated with the incoming event
user_id : str
id of the Slack user associated with the incoming event
"""
# These updated attachments use markdown and emoji to mark the
# onboarding task as complete
completed_attachments = {"text": ":white_check_mark: "
"~*Add an emoji reaction to this "
"message*~ :thinking_face:",
"color": "#439FE0"}
# Grab the message object we want to update by team id and user id
message_obj = self.messages[team_id].get(user_id)
# Update the message's attachments by switching in incomplete
# attachment with the completed one above.
message_obj.emoji_attachment.update(completed_attachments)
# Update the message in Slack
post_message = self.client.api_call("chat.update",
channel=message_obj.channel,
ts=message_obj.timestamp,
text=message_obj.text,
attachments=message_obj.attachments
)
# Update the timestamp saved on the message object
message_obj.timestamp = post_message["ts"]
def update_pin(self, team_id, user_id):
"""
Update onboarding welcome message after recieving a "pin_added"
event from Slack. Update timestamp for welcome message.
Parameters
----------
team_id : str
id of the Slack team associated with the incoming event
user_id : str
id of the Slack user associated with the incoming event
"""
# These updated attachments use markdown and emoji to mark the
# onboarding task as complete
completed_attachments = {"text": ":white_check_mark: "
"~*Pin this message*~ "
":round_pushpin:",
"color": "#439FE0"}
# Grab the message object we want to update by team id and user id
message_obj = self.messages[team_id].get(user_id)
# Update the message's attachments by switching in incomplete
# attachment with the completed one above.
message_obj.pin_attachment.update(completed_attachments)
# Update the message in Slack
post_message = self.client.api_call("chat.update",
channel=message_obj.channel,
ts=message_obj.timestamp,
text=message_obj.text,
attachments=message_obj.attachments
)
# Update the timestamp saved on the message object
message_obj.timestamp = post_message["ts"]
def update_share(self, team_id, user_id):
"""
Update onboarding welcome message after recieving a "message" event
with an "is_share" attachment from Slack. Update timestamp for
welcome message.
Parameters
----------
team_id : str
id of the Slack team associated with the incoming event
user_id : str
id of the Slack user associated with the incoming event
"""
# These updated attachments use markdown and emoji to mark the
# onboarding task as complete
completed_attachments = {"text": ":white_check_mark: "
"~*Share this Message*~ "
":mailbox_with_mail:",
"color": "#439FE0"}
# Grab the message object we want to update by team id and user id
message_obj = self.messages[team_id].get(user_id)
# Update the message's attachments by switching in incomplete
# attachment with the completed one above.
message_obj.share_attachment.update(completed_attachments)
# Update the message in Slack
post_message = self.client.api_call("chat.update",
channel=message_obj.channel,
ts=message_obj.timestamp,
text=message_obj.text,
attachments=message_obj.attachments
)
# Update the timestamp saved on the message object
message_obj.timestamp = post_message["ts"]