Skip to content

Commit b35d7df

Browse files
committed
salesforce order confirmation tutorial
1 parent 9fa6e86 commit b35d7df

File tree

3 files changed

+378
-0
lines changed

3 files changed

+378
-0
lines changed

docs/english/_sidebar.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"label": "Tutorials",
9797
"items": [
9898
"tools/bolt-python/tutorial/ai-chatbot/ai-chatbot",
99+
"tools/bolt-python/tutorial/order-confirmation",
99100
"tools/bolt-python/tutorial/custom-steps",
100101
"tools/bolt-python/tutorial/custom-steps-for-jira/custom-steps-for-jira",
101102
"tools/bolt-python/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new",
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
---
2+
title: Create a Salesforce order confirmation app
3+
---
4+
5+
<Columns>
6+
<Column>
7+
Learn how to use the Bolt for Python template and create a simple order confirmation app that links to systems of record—like Salesforce—in this tutorial.
8+
</Column>
9+
<Column>
10+
![Image of delivery tracker app](/img/delivery-tracker-main.png)
11+
</Column>
12+
</Columns>
13+
14+
The scenario: you work for a large e-commerce company that employs many delivery workers. Those delivery workers don’t have access to a laptop when they’re on the go, but they do have access to Slack on their mobile devices. This tutorial teaches you how to create a simple Slack app that is geared towards these workers. Delivery drivers will enter order numbers on their mobile, along with some additional information about the order, and have that information sent to a channel in Slack and to a system of record. In this tutorial, Salesforce is our system of record.
15+
16+
You’ll also learn how to use the Bolt for Python starter app template and modify it to fit your needs. Note that this app is meant to be used for educational purposes and has not been tested rigorously enough to be used in production.
17+
18+
## Getting started
19+
20+
### Installing the Slack CLI
21+
22+
If you don't already have it, install the Slack CLI from your terminal. Navigate to [the installation guide](/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux/) and follow the steps.
23+
24+
### Cloning the starter app
25+
26+
Once installed, use the command `slack create` in your terminal and find the `bolt-python-starter-template`. Alternatively, you can clone the [Bolt for Python template](https://github.com/slack-samples/bolt-python-starter-template) using git.
27+
28+
Optionally, you can remove the portions from the template that are not used within this tutorial to make things a bit cleaner for yourself. To do this, open your project and delete the commands, events, and shortcuts folders from the `/listeners` folder. You can also do the same to the corresponding folders within the `/listeners/tests` folder as well. Finally, remove the imports of these files from the `/listeners/__init__.py` file.
29+
30+
## Creating your app
31+
32+
We’ll use the contents of the `manifest.json` file below, which can also be found [here](https://github.com/wongjas/delivery-confirmation-app/blob/main/manifest.json). This file describes the metadata associated with your app, like its name and permissions that it requests.
33+
34+
Copy the contents of the file and [create a new app](https://api.slack.com/apps/new). Next, choose **From a manifest** and follow the prompts, pasting the manifest file contents you copied.
35+
36+
```json
37+
{
38+
"_metadata": {
39+
"major_version": 1,
40+
"minor_version": 1
41+
},
42+
"display_information": {
43+
"name": "Name your app here!"
44+
},
45+
"features": {
46+
"bot_user": {
47+
"display_name": "Name your app here!",
48+
"always_online": false
49+
}
50+
},
51+
"oauth_config": {
52+
"scopes": {
53+
"bot": [
54+
"channels:history",
55+
"chat:write"
56+
]
57+
}
58+
},
59+
"settings": {
60+
"event_subscriptions": {
61+
"bot_events": [
62+
"message.channels"
63+
]
64+
},
65+
"interactivity": {
66+
"is_enabled": true
67+
},
68+
"org_deploy_enabled": false,
69+
"socket_mode_enabled": true,
70+
"token_rotation_enabled": false
71+
}
72+
}
73+
```
74+
75+
Customize your app with a name of your own instead of the default in the `display_name` and `name` fields.
76+
77+
### Tokens
78+
79+
Once your app has been created, scroll down to **App-Level Tokens** on the **Basic Information** page and create a token that requests the [`connections:write`](/reference/scopes/connections.write) scope. This token will allow you to use [Socket Mode](/apis/events-api/using-socket-mode), which is a secure way to develop on Slack through the use of WebSockets. Save the value of your app token and store it in a safe place (we’ll use it in the next step).
80+
81+
### Install app
82+
83+
Install your app by navigating to **Install App** in the left sidebar. When you press **Allow**, this means you’re agreeing to install your app with the permissions that it’s requesting. Copy the bot token that you receive as well and store this in a safe place as well for subsequent steps.
84+
85+
## Starting your app's server
86+
87+
Within a terminal of your choice, set the two tokens from the previous step as environment variables using the commands below. Make sure not to mix these two up, `SLACK_APP_TOKEN` will start with “xapp-“ and `SLACK_BOT_TOKEN` will start with “xoxb-“.
88+
89+
```bash
90+
export SLACK_APP_TOKEN=<YOUR-APP-TOKEN-HERE>
91+
export SLACK_BOT_TOKEN=<YOUR-BOT-TOKEN-HERE>
92+
```
93+
94+
Run the following commands to activate a virtual environment for your Python packages to be installed, install the dependencies, and start your app.
95+
96+
```bash
97+
# Setup your python virtual environment
98+
python3 -m venv .venv
99+
source .venv/bin/activate
100+
101+
# Install the dependencies
102+
pip install -r requirements.txt
103+
104+
# Start your local server
105+
python3 app.py
106+
```
107+
108+
Now that your app is running, you should be able to see it within Slack. In Slack, create a channel that you can test in and try inviting your bot to it using the `/invite @Your-app-name-here` command. Check that your app works by saying “hi” in the channel where your app is, and you should receive a message back from it. If you don’t, ensure you completed all the steps above.
109+
110+
## Coding the app
111+
112+
There will be four major steps needed to get from the template to the finish line:
113+
114+
1. Update the “hi” message to something more interesting and interactive
115+
2. Handle when the wrong delivery ID button is pressed
116+
3. Handle when the correct delivery IDs are sent and bring up a modal for more information
117+
4. Send the information to all of the places needed when the form is submitted (including third-party locations)
118+
119+
All of these steps require you to use [Block Kit Builder](https://app.slack.com/block-kit-builder), a tool that helps you create messages, modals and other surfaces within Slack. Open [Block Kit Builder](https://app.slack.com/block-kit-builder), take a look and play around! We’ll create some views next.
120+
121+
### Updating the "hi" message
122+
123+
The first thing we want to do is change the “hi, how are you?” message from our app into something more useful. Here’s something that you can use to start off with, but you can make it your own within Block Kit Builder. Once you have something you like, copy the blocks by clicking the **Copy Payload** button in the top right.
124+
125+
Take the function below and place your blocks within the blocks dictionary `[]`. Update the payload:
126+
* Remove the initial blocks key and convert any boolean true values to `True` to fit with Python conventions.
127+
* If you see variables within `{}` brackets, this is part of an f-string, which allows you to insert variables within strings in a clean manner. Place the `f` character before these strings like this:
128+
129+
```python
130+
{
131+
"type": "section",
132+
"text": {
133+
"type": "mrkdwn",
134+
"text": f"Confirm *{delivery_id}* is correct?", # place the "f" character here at the beginning of the string
135+
},
136+
},
137+
```
138+
139+
Place all of this in the `sample_message.py` file.
140+
141+
```python
142+
def delivery_message_callback(context: BoltContext, say: Say, logger: Logger):
143+
try:
144+
delivery_id = context["matches"][0]
145+
say(
146+
blocks=[] # insert your blocks here
147+
)
148+
except Exception as e:
149+
logger.error(e)
150+
```
151+
152+
Next, you’ll need to make some connections so that this function is called when a message is sent in the channel where your app is. Head to `messages/__init__.py` and add the line below to the register function. Don’t forget to add the import to the callback function as well!
153+
154+
```python
155+
156+
from .sample_message import delivery_message_callback ## import the function to this file
157+
158+
def register(app: App):
159+
app.message(re.compile("(hi|hello|hey)"))(sample_message_callback) # This can be deleted!
160+
# This regex will capture any number letters followed by dash
161+
# and then any number of digits, our "confirmation number" e.g. ASDF-1234
162+
app.message(re.compile(r"[A-Za-z]+-\d+"))(delivery_message_callback) ## add this line!
163+
164+
```
165+
166+
Now, restart your server to bring in the new code and test that your function works by sending an order confirmation ID, like `HWOA-1524`, in your testing channel. Your app should respond with the message you created within Block Kit Builder.
167+
168+
## Handling an incorrect delivery ID
169+
170+
Notice that if you try to click on either of the buttons within your message, nothing will happen. This is because we have yet to create a function to handle the button click. Let’s start with the `Not correct` button first.
171+
172+
1. Head to Block Kit Builder once again. We want to build a message that lets the user know that the wrong order ID has been submitted. Here's [something to get you started](https://app.slack.com/block-kit-builder/#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Delivery%20*%7Bdelivery_id%7D*%20was%20incorrect%20%E2%9D%8C%22%7D%7D%5D%7D).
173+
174+
2. Once you have something that you like, add it to the function below and place the function within the `actions/sample_action.py` file. Remember to make any strings with variables into f-strings!
175+
176+
```python
177+
def deny_delivery_callback(ack, body, client, logger: Logger):
178+
try:
179+
ack()
180+
delivery_id = body["message"]["text"].split("*")[1]
181+
182+
# Calls the chat.update function to replace the message,
183+
# preventing it from being pressed more than once.
184+
client.chat_update(
185+
channel=body["container"]["channel_id"],
186+
ts=body["container"]["message_ts"],
187+
blocks=[], # Add your blocks here!
188+
)
189+
190+
logger.info(f"Delivery denied by user {body['user']['id']}")
191+
except Exception as e:
192+
logger.error(e)
193+
```
194+
195+
This function will call the [`chat.update`](/methods/chat.update) Web API method, which will update the original message with buttons, to the one that we created previously. This will also prevent the message from being pressed more than once.
196+
197+
3. Make the connection to this function again within the `actions/__init__.py` folder with the following code:
198+
199+
```python
200+
201+
from slack_bolt import App
202+
from .sample_action import sample_action_callback # This can be deleted
203+
from .sample_action import deny_delivery_callback
204+
205+
def register(app: App):
206+
app.action("sample_action_id")(sample_action_callback) # This can be deleted
207+
app.action("deny_delivery")(deny_delivery_callback) # Add this line
208+
209+
```
210+
211+
Test out your code by sending in a confirmation number into your channel and clicking the `Not correct` button. If the message is updated, then you’re good to go onto the next step.
212+
213+
## Handling a correct delivery ID
214+
215+
The next step is to handle the `Confirm` button. In this case, we’re going to pull up a modal instead of just a message.
216+
217+
1. Using the following [modal](https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22approve_delivery_view%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Approve%20Delivery%22%7D,%22private_metadata%22:%22%7Bdelivery_id%7D%22,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Approving%20delivery%20*%7Bdelivery_id%7D*%22%7D%7D,%7B%22type%22:%22input%22,%22block_id%22:%22notes%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Additional%20delivery%20notes%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22notes_input%22,%22multiline%22:true,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Add%20notes...%22%7D%7D,%22optional%22:true%7D,%7B%22type%22:%22input%22,%22block_id%22:%22location%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Delivery%20Location%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22location_input%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Enter%20the%20location%20details...%22%7D%7D,%22optional%22:true%7D,%7B%22type%22:%22input%22,%22block_id%22:%22channel%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Notification%20Channel%22%7D,%22element%22:%7B%22type%22:%22channels_select%22,%22action_id%22:%22channel_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Select%20channel%20for%20notifications%22%7D%7D,%22optional%22:false%7D%5D,%22submit%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Approve%22%7D%7D) as a base, create a modal that captures the kind of information that you need.
218+
219+
2. Within the `actions/sample_action.pyfile`, add the following function, replacing the view with the one you created above. Again, any strings with variables will be updated to f-strings and also any booleans will need to be capitalized.
220+
221+
```python
222+
223+
def approve_delivery_callback(ack, body, client, logger: Logger):
224+
try:
225+
ack()
226+
227+
delivery_id = body["message"]["text"].split("*")[1]
228+
# Updates the original message so you can't press it twice
229+
client.chat_update(
230+
channel=body["container"]["channel_id"],
231+
ts=body["container"]["message_ts"],
232+
blocks=[
233+
{
234+
"type": "section",
235+
"text": {
236+
"type": "mrkdwn",
237+
"text": f"Processed delivery *{delivery_id}*...",
238+
},
239+
}
240+
],
241+
)
242+
243+
# Open a modal to gather information from the user
244+
client.views_open(
245+
trigger_id=body["trigger_id"],
246+
view={} # Add your view here
247+
)
248+
249+
logger.info(f"Approval modal opened by user {body['user']['id']}")
250+
except Exception as e:
251+
logger.error(e)
252+
253+
```
254+
255+
Similar to the `deny` button, we need to hook up all the connections. Your `actions/__init__.py` should look something like this:
256+
257+
```python
258+
259+
from slack_bolt import App
260+
from .sample_action import deny_delivery_callback
261+
from .sample_action import approve_delivery_callback
262+
263+
264+
def register(app: App):
265+
app.action("approve_delivery")(approve_delivery_callback)
266+
app.action("deny_delivery")(deny_delivery_callback)
267+
268+
```
269+
270+
Test your app by typing in a confirmation number in channel, click the confirm button and see if the modal comes up and you are able to capture information from the user.
271+
272+
## Submitting the form
273+
274+
Lastly, we’ll handle the submission of the form, which will trigger two things. We want to send the information into the specified channel, which will let the user know that the form was successful, as well as send the information into our system of record, Salesforce.
275+
276+
1. Here’s a [simple example](https://app.slack.com/block-kit-builder/?1#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22%E2%9C%85%20Delivery%20*%7Bdelivery_id%7D*%20approved:%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Delivery%20Notes:*%5Cn%7Bnotes%20or%20'None'%7D%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Delivery%20Location:*%5Cn%7Bloc%20or%20'None'%7D%22%7D%7D%5D%7D) of a message that you can use to present the information in channel. Modify it however you like and then place it within the code below in the `/views/sample_views.py` file.
277+
278+
```python
279+
280+
def handle_approve_delivery_view(ack, client, view, logger: Logger):
281+
try:
282+
ack()
283+
284+
delivery_id = view["private_metadata"]
285+
values = view["state"]["values"]
286+
notes = values["notes"]["notes_input"]["value"]
287+
loc = values["location"]["location_input"]["value"]
288+
channel = values["channel"]["channel_select"]["selected_channel"]
289+
290+
client.chat_postMessage(
291+
channel=channel,
292+
blocks=[], ## Add your message here
293+
)
294+
295+
except Exception as e:
296+
logger.error(f"Error in approve_delivery_view: {e}")
297+
298+
```
299+
300+
2. Making the connections in the `/views/__init__.py `file, we can test that this works by sending a message once again in our test channel.
301+
302+
```python
303+
304+
from slack_bolt import App
305+
from .sample_view import handle_approve_delivery_view
306+
307+
def register(app: App):
308+
app.view("sample_view_id")(sample_view_callback) # This can be deleted
309+
app.view("approve_delivery_view")(handle_approve_delivery_view) ## Add this line
310+
311+
```
312+
313+
3. Let’s also send the information to Salesforce. There are [several ways](https://github.com/simple-salesforce/simple-salesforce?tab=readme-ov-file#examples) for you to access Salesforce through its API, but in this workshop, we’ve utilized `username`, `password` and `token`. If you need help with getting your API token for Salesforce, take a look at [this article](https://help.salesforce.com/s/articleView?id=xcloud.user_security_token.htm&type=5). You’ll need to add these values as environment variables like we did earlier with our Slack tokens. You can use the following commands:
314+
315+
```bash
316+
317+
export SF_USERNAME=<YOUR-USERNAME>
318+
export SF_PASSWORD=<YOUR-PASSWORD>
319+
export SF_TOKEN=<YOUR-SFDC-TOKEN>
320+
321+
```
322+
323+
4. We’re going to use assume that order information is stored in the Order object and that the confirmation IDs map to the 8-digit Order numbers within Salesforce. Given that assumption, all we need to do is make a query to find the correct object, add the inputted information, and we’re done. Place this code before the last except within the `/views/sample_views.py` file.
324+
325+
```python
326+
327+
# Extract just the numeric portion from delivery_id
328+
delivery_number = "".join(filter(str.isdigit, delivery_id))
329+
330+
# Update Salesforce order object
331+
try:
332+
sf = Salesforce(
333+
username=os.environ.get("SF_USERNAME"),
334+
password=os.environ.get("SF_PASSWORD"),
335+
security_token=os.environ.get("SF_TOKEN"),
336+
)
337+
338+
# Assuming delivery_id maps to Salesforce Order number
339+
order = sf.query(f"SELECT Id FROM Order WHERE OrderNumber = '{delivery_number}'") # noqa: E501
340+
if order["records"]:
341+
order_id = order["records"][0]["Id"]
342+
sf.Order.update(
343+
order_id,
344+
{
345+
"Status": "Delivered",
346+
"Description": notes,
347+
"Shipping_Location__c": loc,
348+
},
349+
)
350+
logger.info(f"Updated order {delivery_id}")
351+
else:
352+
logger.warning(f"No order found for {delivery_id}")
353+
354+
except Exception as sf_error:
355+
logger.error(f"Update failed for order {delivery_id}: {sf_error}")
356+
# Continue execution even if Salesforce update fails
357+
358+
```
359+
360+
You’ll also need to add the two imports that are found within this code to the top of the file.
361+
362+
```python
363+
import os
364+
from simple_salesforce import Salesforce
365+
```
366+
367+
With these imports, add `simple_salesforce` to your `requirements.txt` file, then install that package with the following command once again.
368+
369+
```bash
370+
pip install -r requirements.txt
371+
```
372+
373+
## Testing your app
374+
375+
Test your app one last time, and you’re done!
376+
377+
Congratulations! You’ve built an app using [Bolt for Python](/bolt-python/) that allows you to send information into Slack, as well as into a third-party service. While there are more features you can add to make this a more robust app, we hope that this serves as a good introduction into connecting services like Salesforce using Slack as a conduit.

docs/img/delivery-tracker-main.png

64.3 KB
Loading

0 commit comments

Comments
 (0)