如何運(yùn)用Python建立你的第一個(gè)Slack聊天機(jī)器人?
聊天機(jī)器人(Bot) 是一種像 Slack 一樣的實(shí)用的互動(dòng)聊天服務(wù)方式。如果你之前從來(lái)沒(méi)有建立過(guò)聊天機(jī)器人,那么這篇文章提供了一個(gè)簡(jiǎn)單的入門指南,告訴你如何用 Python 結(jié)合 Slack API 建立你第一個(gè)聊天機(jī)器人。
我們通過(guò)搭建你的開(kāi)發(fā)環(huán)境, 獲得一個(gè) Slack API 的聊天機(jī)器人令牌,并用 Pyhon 開(kāi)發(fā)一個(gè)簡(jiǎn)單聊天機(jī)器人。
我們所需的工具
我們的聊天機(jī)器人我們將它稱作為“StarterBot”,它需要 Python 和 Slack API。要運(yùn)行我們的 Python 代碼,我們需要:
- Python 2 或者 Python 3
- pip 和 virtualenv 來(lái)處理 Python 應(yīng)用程序依賴關(guān)系
- 一個(gè)可以訪問(wèn) API 的免費(fèi) Slack 賬號(hào),或者你可以注冊(cè)一個(gè) Slack Developer Hangout team。
- 通過(guò) Slack 團(tuán)隊(duì)建立的官方 Python Slack 客戶端代碼庫(kù)
- Slack API 測(cè)試令牌
當(dāng)你在本教程中進(jìn)行構(gòu)建時(shí),Slack API 文檔 是很有用的。
本教程中所有的代碼都放在 slack-starterbot 公共庫(kù)里,并以 MIT 許可證開(kāi)源。
搭建我們的環(huán)境
我們現(xiàn)在已經(jīng)知道我們的項(xiàng)目需要什么樣的工具,因此讓我們來(lái)搭建我們所的開(kāi)發(fā)環(huán)境吧。首先到終端上(或者 Windows 上的命令提示符)并且切換到你想要存儲(chǔ)這個(gè)項(xiàng)目的目錄。在那個(gè)目錄里,創(chuàng)建一個(gè)新的 virtualenv 以便和其他的 Python 項(xiàng)目相隔離我們的應(yīng)用程序依賴關(guān)系。
- virtualenv starterbot
激活 virtualenv:
- source starterbot/bin/activate
你的提示符現(xiàn)在應(yīng)該看起來(lái)如截圖:
已經(jīng)激活的 starterbot 的 virtualenv的命令提示符這個(gè)官方的 slack 客戶端 API 幫助庫(kù)是由 Slack 建立的,它可以通過(guò) Slack 通道發(fā)送和接收消息。通過(guò)這個(gè)pip 命令安裝 slackclient 庫(kù):
- pip install slackclient
當(dāng) pip 命令完成時(shí),你應(yīng)該看到類似這樣的輸出,并返回提示符。
在已經(jīng)激活的 virtualenv 用 pip 安裝 slackclient 的輸出我們也需要為我們的 Slack 項(xiàng)目獲得一個(gè)訪問(wèn)令牌,以便我們的聊天機(jī)器人可以用它來(lái)連接到 Slack API。
Slack 實(shí)時(shí)消息傳遞(RTM)API
Slack 允許程序通過(guò)一個(gè) Web API 來(lái)訪問(wèn)他們的消息傳遞通道。去這個(gè) Slack Web API 頁(yè)面 注冊(cè)建立你自己的 Slack 項(xiàng)目。你也可以登錄一個(gè)你擁有管理權(quán)限的已有賬號(hào)。
使用 Web API頁(yè)面的右上角登錄按鈕登錄后你會(huì)到達(dá) 聊天機(jī)器人用戶頁(yè)面。
定制聊天機(jī)器人用戶頁(yè)面給你的聊天機(jī)器人起名為“starterbot”然后點(diǎn)擊 “Add bot integration” 按鈕。
添加一個(gè)bot integration 并起名為“starterbot”這個(gè)頁(yè)面將重新加載,你將看到一個(gè)新生成的訪問(wèn)令牌。你還可以將標(biāo)志改成你自己設(shè)計(jì)的。例如我給的這個(gè)“Full Stack Python”標(biāo)志。
為你的新 Slack 聊天機(jī)器人復(fù)制和粘貼訪問(wèn)令牌在頁(yè)面底部點(diǎn)擊“Save Integration”按鈕。你的聊天機(jī)器人現(xiàn)在已經(jīng)準(zhǔn)備好連接 Slack API。
Python 開(kāi)發(fā)人員的一個(gè)常見(jiàn)的做法是以環(huán)境變量輸出秘密令牌。輸出的 Slack 令牌名字為SLACK_BOT_TOKEN:
- export SLACK_BOT_TOKEN='你的 slack 令牌粘帖在這里'
好了,我們現(xiàn)在得到了將這個(gè) Slack API 用作聊天機(jī)器人的授權(quán)。
我們建立聊天機(jī)器人還需要更多信息:我們的聊天機(jī)器人的 ID。接下來(lái)我們將會(huì)寫(xiě)一個(gè)簡(jiǎn)短的腳本,從 Slack API 獲得該 ID。
獲得我們聊天機(jī)器人的 ID
這是最后寫(xiě)一些 Python 代碼的時(shí)候了! 我們編寫(xiě)一個(gè)簡(jiǎn)短的 Python 腳本獲得 StarterBot 的 ID 來(lái)熱身一下。這個(gè) ID 基于 Slack 項(xiàng)目而不同。
我們需要該 ID,當(dāng)解析從 Slack RTM 上發(fā)給 StarterBot 的消息時(shí),它用于對(duì)我們的應(yīng)用驗(yàn)明正身。我們的腳本也會(huì)測(cè)試我們 SLACK_BOT_TOKEN 環(huán)境變量是否設(shè)置正確。
建立一個(gè)命名為 printbotid.py 的新文件,并且填入下面的代碼:
- import os
- from slackclient import SlackClient
- BOT_NAME = 'starterbot'
- slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
- if __name__ == "__main__":
- api_call = slack_client.api_call("users.list")
- if api_call.get('ok'):
- # retrieve all users so we can find our bot
- users = api_call.get('members')
- for user in users:
- if 'name' in user and user.get('name') == BOT_NAME:
- print("Bot ID for '" + user['name'] + "' is " + user.get('id'))
- else:
- print("could not find bot user with the name " + BOT_NAME)
我們的代碼導(dǎo)入 SlackClient,并用我們?cè)O(shè)置的環(huán)境變量 SLACK_BOT_TOKEN 實(shí)例化它。 當(dāng)該腳本通過(guò) python 命令執(zhí)行時(shí),我們通過(guò)會(huì)訪問(wèn) Slack API 列出所有的 Slack 用戶并且獲得匹配一個(gè)名字為“satrterbot”的 ID。
這個(gè)獲得聊天機(jī)器人的 ID 的腳本我們僅需要運(yùn)行一次。
- python print_bot_id.py
當(dāng)它運(yùn)行為我們提供了聊天機(jī)器人的 ID 時(shí),腳本會(huì)打印出簡(jiǎn)單的一行輸出。
在你的 Slack 項(xiàng)目中用 Python 腳本打印 Slack 聊天機(jī)器人的 ID復(fù)制這個(gè)腳本打印出的唯一 ID。并將該 ID 作為一個(gè)環(huán)境變量 BOT_ID 輸出。
- (starterbot)$ export BOT_ID='bot id returned by script'
這個(gè)腳本僅僅需要運(yùn)行一次來(lái)獲得聊天機(jī)器人的 ID。 我們現(xiàn)在可以在我們的運(yùn)行 StarterBot 的 Python應(yīng)用程序中使用這個(gè) ID 。
編碼我們的 StarterBot
現(xiàn)在我們擁有了寫(xiě)我們的 StarterBot 代碼所需的一切。 創(chuàng)建一個(gè)新文件命名為 starterbot.py ,它包括以下代碼。
- import os
- import time
- from slackclient import SlackClient
對(duì) os 和 SlackClient 的導(dǎo)入我們看起來(lái)很熟悉,因?yàn)槲覀円呀?jīng)在 theprintbotid.py 中用過(guò)它們了。
通過(guò)我們導(dǎo)入的依賴包,我們可以使用它們獲得環(huán)境變量值,并實(shí)例化 Slack 客戶端。
- # starterbot 的 ID 作為一個(gè)環(huán)境變量
- BOT_ID = os.environ.get("BOT_ID")
- # 常量
- AT_BOT = "<@" + BOT_ID + ">:"
- EXAMPLE_COMMAND = "do"
- # 實(shí)例化 Slack 和 Twilio 客戶端
- slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
該代碼通過(guò)我們以輸出的環(huán)境變量 SLACK_BOT_TOKEN 實(shí)例化SlackClient` 客戶端。
- if __name__ == "__main__":
- READ_WEBSOCKET_DELAY = 1 # 1 從 firehose 讀取延遲 1 秒
- if slack_client.rtm_connect():
- print("StarterBot connected and running!")
- while True:
- command, channel = parse_slack_output(slack_client.rtm_read())
- if command and channel:
- handle_command(command, channel)
- time.sleep(READ_WEBSOCKET_DELAY)
- else:
- print("Connection failed. Invalid Slack token or bot ID?")
Slack 客戶端會(huì)連接到 Slack RTM API WebSocket,然后當(dāng)解析來(lái)自 firehose 的消息時(shí)會(huì)不斷循環(huán)。如果有任何發(fā)給 StarterBot 的消息,那么一個(gè)被稱作 handle_command 的函數(shù)會(huì)決定做什么。
接下來(lái)添加兩個(gè)函數(shù)來(lái)解析 Slack 的輸出并處理命令。
- def handle_command(command, channel):
- """
- Receives commands directed at the bot and determines if they
- are valid commands. If so, then acts on the commands. If not,
- returns back what it needs for clarification.
- """
- response = "Not sure what you mean. Use the *" + EXAMPLE_COMMAND + \
- "* command with numbers, delimited by spaces."
- if command.startswith(EXAMPLE_COMMAND):
- response = "Sure...write some more code then I can do that!"
- slack_client.api_call("chat.postMessage", channel=channel,
- text=response, as_user=True)
- def parse_slack_output(slack_rtm_output):
- """
- The Slack Real Time Messaging API is an events firehose.
- this parsing function returns None unless a message is
- directed at the Bot, based on its ID.
- """
- output_list = slack_rtm_output
- if output_list and len(output_list) > 0:
- for output in output_list:
- if output and 'text' in output and AT_BOT in output['text']:
- # 返回 @ 之后的文本,刪除空格
- return output['text'].split(AT_BOT)[1].strip().lower(), \
- output['channel']
- return None, None
parse_slack_output 函數(shù)從 Slack 接受信息,并且如果它們是發(fā)給我們的 StarterBot 時(shí)會(huì)作出判斷。消息以一個(gè)給我們的聊天機(jī)器人 ID 的直接命令開(kāi)始,然后交由我們的代碼處理。目前只是通過(guò) Slack 管道發(fā)布一個(gè)消息回去告訴用戶去多寫(xiě)一些 Python 代碼!
這是整個(gè)程序組合在一起的樣子 (你也可以 在 GitHub 中查看該文件):
- import os
- import time
- from slackclient import SlackClient
- # starterbot 的 ID 作為一個(gè)環(huán)境變量
- BOT_ID = os.environ.get("BOT_ID")
- # 常量
- AT_BOT = "<@" + BOT_ID + ">:"
- EXAMPLE_COMMAND = "do"
- # 實(shí)例化 Slack 和 Twilio 客戶端
- slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
- def handle_command(command, channel):
- """
- Receives commands directed at the bot and determines if they
- are valid commands. If so, then acts on the commands. If not,
- returns back what it needs for clarification.
- """
- response = "Not sure what you mean. Use the *" + EXAMPLE_COMMAND + \
- "* command with numbers, delimited by spaces."
- if command.startswith(EXAMPLE_COMMAND):
- response = "Sure...write some more code then I can do that!"
- slack_client.api_call("chat.postMessage", channel=channel,
- text=response, as_user=True)
- def parse_slack_output(slack_rtm_output):
- """
- The Slack Real Time Messaging API is an events firehose.
- this parsing function returns None unless a message is
- directed at the Bot, based on its ID.
- """
- output_list = slack_rtm_output
- if output_list and len(output_list) > 0:
- for output in output_list:
- if output and 'text' in output and AT_BOT in output['text']:
- # 返回 @ 之后的文本,刪除空格
- return output['text'].split(AT_BOT)[1].strip().lower(), \
- output['channel']
- return None, None
- if __name__ == "__main__":
- READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
- if slack_client.rtm_connect():
- print("StarterBot connected and running!")
- while True:
- command, channel = parse_slack_output(slack_client.rtm_read())
- if command and channel:
- handle_command(command, channel)
- time.sleep(READ_WEBSOCKET_DELAY)
- else:
- print("Connection failed. Invalid Slack token or bot ID?")
現(xiàn)在我們的代碼已經(jīng)有了,我們可以通過(guò) python starterbot.py 來(lái)運(yùn)行我們 StarterBot 的代碼了。
當(dāng) StarterBot 開(kāi)始運(yùn)行而且連接到 API 的輸出通道在 Slack 中創(chuàng)建新通道,并且把 StarterBot 邀請(qǐng)進(jìn)來(lái),或者把 StarterBot 邀請(qǐng)進(jìn)一個(gè)已經(jīng)存在的通道中。
在 Slack 界面創(chuàng)建一個(gè)新通道并且邀請(qǐng) StarterBot現(xiàn)在在你的通道中給 StarterBot 發(fā)命令。
在你的 Slack 通道里給你的 StarterBot 發(fā)命令如果你從聊天機(jī)器人得到的響應(yīng)中遇見(jiàn)問(wèn)題,你可能需要做一個(gè)修改。正如上面所寫(xiě)的這個(gè)教程,其中一行AT_BOT = "<@" + BOT_ID + ">:",在“@starter”(你給你自己的聊天機(jī)器人起的名字)后需要一個(gè)冒號(hào)。從 AT_BOT 字符串后面移除:。Slack 似乎需要在@ 一個(gè)人名后加一個(gè)冒號(hào),但這好像是有些不協(xié)調(diào)的。
結(jié)束
好吧,你現(xiàn)在已經(jīng)獲得一個(gè)簡(jiǎn)易的聊天機(jī)器人,你可以在代碼中很多地方加入你想要?jiǎng)?chuàng)建的任何特性。
我們能夠使用 Slack RTM API 和 Python 完成很多功能??纯赐ㄟ^(guò)這些文章你還可以學(xué)習(xí)到什么:
- 附加一個(gè)持久的關(guān)系數(shù)據(jù)庫(kù) 或者 NoSQL 后端 比如 PostgreSQL、MySQL 或者 SQLite ,來(lái)保存和檢索用戶數(shù)據(jù)
- 添加另外一個(gè)與聊天機(jī)器人互動(dòng)的通道,比如 短信 或者電話呼叫
- 集成其它的 web API,比如 GitHub、Twilio 或者 api.ai
有問(wèn)題? 通過(guò) Twitter 聯(lián)系我 @fullstackpython 或 @mattmakai。 我在 GitHub 上的用戶名是mattmakai。
這篇文章感興趣? Fork 這個(gè) GitHub 上的頁(yè)面吧。