>實務上的業務情境,經常會需要在不同平台之間反覆切換;像是在 Jira 查找專案進度,去資料庫查找訂單狀態、去 Azure 查看線上系統的錯誤報告、專案的緊急變動可能又要隨時留意 Slack……
>
>這些手動的查詢和資訊同步過程,雖然每個環節看似不難,卻不斷地打斷你的專注力,降低了團隊的整體效率。尤其是當資訊分散在不同地方,要即時掌握全貌,更是難上加難。
>
>透過 Slack 機器人,我們可以將來自不同系統的數據與通知,無縫整合到團隊的日常溝通中。不再需要手動查詢,也無需離開 Slack 視窗,就能即時獲取所有關鍵資訊。
>
>本文將示範如何從零開始,在 Slack 的工作空間建立一個機器人應用程式,並根據公司需求設計業務邏輯,整合公司的服務與資訊,提升團隊工作效率。
# 一、前置作業
Slack Bot 的核心是透過 Slack API 接收事件(如訊息、指令)並回應。你會需要:
- Django 作為 Web Server(處理 Slack 的 webhook)
- Slack App(在 Slack Developer Portal 註冊)
- Slack API Token(用於呼叫 Slack API)
- ngrok 或雲端部署(讓 Slack 能夠存取你的 webhook)
1. 在 [Slack API](https://api.slack.com/apps) 建立一個新的 APP

這邊有兩個建立方式可以選擇:
- `manifest`:YMAL 或 JSON 的設定檔,明確定義權限與功能,且如果需要建立新環境,在重復使用上很方便。
```ymal
display_information:
name: My Slack Bot
features:
bot_user:
display_name: MyBot
always_online: true
oauth_config:
scopes:
bot:
- chat:write
- app_mentions:read
settings:
event_subscriptions:
request_url: https://yourdomain.com/slack/events
bot_events:
- app_mention
```
熟悉 YAML/JSON 的開發者,或是有想要自動化部署或 CI/CD,以及有多個 App 或 workspace 要管理的需求,推薦使用 manifest。
- `scratch`:Slack 提供的 UI 步驟,適合初學者,可以邊設定邊測試 webhook、OAuth 等功能。
適合第一次開發 Slack App,想快速測試功能,或是還不熟悉 YAML/JSON 或沒有版本化設定需求的用戶。
2. 這邊我們選擇用 scratch,輸入 App 名稱和要建置的工作環境後,就會看到 App 的設定頁面

在 ==Event Subscription== 的選項,完成以下動作:
- 打開 "Enable Events",並輸入你的 Webhook 路徑 ( Slack 會立刻發送請求去驗證這個 URL 是否有效 )。
- 在 "Subscribe to bot events" 加入三個事件:
- `app_mention`(被提及)
- `message.channels`(公開頻道訊息)
- `message.im`(私訊)

3. 在 ==OAuth & Permission== 選項,點選 "Install to <工作空間>" 的按鈕取得 OAuth Token。

然後在 "Bot Token Scopes" 加入一個 Token:
* `chat:write`:允許你的 bot 發送訊息到頻道、私訊、thread 等

4. Slack 會自動幫你在「應用程式 (Apps)」建立一個私訊對話 (Direct Message),通稱 App DM。

傳送訊息被關閉是正常的。這個頁面不是頻道,也不是真正的 DM 對話,除非你有在 Slack Api 設定 Event subscriptions(例如 message.im),以及 Interactivity 或 slash commands,否則你無法在這裡輸入訊息給 bot。
:::info
Slack 的 DM 有幾種形式:
- **人與人之間的私訊:**
你可以直接點選某個人的名字,開啟一對一對話
- **多人私訊:**
你可以跟兩個以上的人開啟群組私訊(但不是頻道)
- **App DM(Bot DM):**
這是 Slack 自動幫你的 bot 建立的私訊頁面,通常在左側 sidebar 的「Apps」區塊裡
:::
# 二、Django 開發
首先加上環境變數,將機器人的 OAuth Token 加進系統:
```python
SLACK_BOT_TOKEN = "xoxb-...TXd20f"
```
然後新增一個 app,這裡我們將它命名為 'slack_bot'
```python
# bot_view.py
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import requests
import os
SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN")
@csrf_exempt
def slack_webhook(request):
payload = json.loads(request.body)
# Slack URL verification
if payload.get("type") == "url_verification":
return JsonResponse({"challenge": payload.get("challenge")})
# 處理訊息事件
if payload.get("type") == "event_callback":
event = payload.get("event")
if event.get("type") == "app_mention":
user = event.get("user")
channel = event.get("channel")
text = event.get("text")
reply = f"Hi <@{user}>! 你說的是:{text}"
requests.post("https://slack.com/api/chat.postMessage", headers={
"Authorization": f"Bearer {SLACK_BOT_TOKEN}",
"Content-Type": "application/json"
}, json={
"channel": channel,
"text": reply
})
return JsonResponse({"status": "ok"})
# urls.py
from django.urls import path
from app.slack_bot.views.bot_view import slack_webhook
urlpatterns = [
path("webhook/", slack_webhook),
]
# <django-project> urls.py
urlpatterns = [
...
path("api/slack/", include("app.slack_bot.urls")),
...
]
```
## 測試連線:在頻道中提及 Bot
在 Slack 我們可以先試著透過觸發標註事件 (app_mention) 去檢查 Webhook 是否有順利連線且順利運作我們設計的邏輯。
打開想讓 Bot 回應的頻道 (這邊我們新建一個頻道,叫 "bot_test"),在訊息框輸入 `/invite @<機器人名稱>`

機器人進入群組後就可以使用了。只要你標註機器人,訊息就會觸發 Webhook。

## 讓 Slack Bot 處理圖片
要接收圖片,我們要在 "Subscribe to bot events" 加入一個事件:
- `files:read`:讀取使用者上傳的檔案(圖片)
並在 Event Subscription 確認有加上這三個事件:
- `message.channels`(公開頻道)
- `message.groups`(私密頻道)
- `message.im`(私訊)
Slack 只會幫可以處理這三種事件的帳號處理圖片,否則 Slack 不會發送圖片訊息給你的 webhook。
加完後記得重新「Install to Workspace」更新 token 權限。
然後我們修改一下 Webhook 的邏輯,在事件處理上做出區分:
```python
def download_slack_image(image_url):
headers = {
"Authorization": f"Bearer {SLACK_BOT_TOKEN}"
}
response = requests.get(image_url, headers=headers)
if response.status_code == 200:
filename = "downloaded_image.png"
with open(filename, "wb") as f:
f.write(response.content)
return filename
return None
...
# 處理圖片訊息(message + files)
elif event.get("type") == "message" and "files" in event:
print("收到 message 和 files 了!")
for file in event["files"]:
if file.get("mimetype", "").startswith("image/"):
image_url = file.get("url_private")
filename = download_slack_image(image_url)
if filename:
reply = f"Hi <@{user}>! 我已經收到你的圖片 `{filename}`,準備處理囉!"
send_slack_message(channel, reply)
```
測試時不用標註機器人,直接上傳圖片即可。

# 三、業務情境實作 - 串接 API
如果公司有內部的系統服務 API(如資料查詢、身分驗證等),也可以將它們串接到 Slack 機器人上,其實就是把剛才處理圖片的訊息內容改成 API 的回傳值就可以了。
這邊以串接公司票據辨識的 API 作為範例,得到的回傳結果如下:



(車票與發票皆為網路搜尋的範本,敏感資訊已有額外處理。)
## 補充:訊息事件的去重處理
要注意的是,通常在 Slack 串接 API 可能會需要去重 (去除重複呼叫) 的設計。因為如果伺服器回應太慢,Slack 就會重複發送請求。
在 View 裡面我們要做兩個處理:一個是在 Webhook 開頭直接先確認標頭,看是不是 Slack 重送的訊息,是的話就直接忽略。
```python
@csrf_exempt
def slack_webhook(request):
# Slack retry 重送直接忽略
if request.headers.get("X-Slack-Retry-Num"):
return JsonResponse({"status": "ignored"})
...
```
另一個是要在處理圖片事件 (message + file_share) 時,檢查是否有重複發送的事件。
可以用 set 去接住事件 id,但 **Slack 不是每種情境都有 client_msg_id** (像手機傳圖、桌機拖圖、或直接用第三方應用傳圖就會回傳 None)。
所以去重的 key 可以再加上 event_ts (事件時間戳),在 Slack 這絕對是每個訊息的唯一值。
```python
msg_id = event.get("client_msg_id") or event.get("event_ts")
if msg_id in processed_messages:
print(f"跳過重複訊息 {msg_id}")
return
processed_messages.add(msg_id)
```
或是也可以用 Redis 暫存已經處理過的圖片 URL,如果在處理前的檢查發現這是重複請求的話,就會擋下來。存入、取得和刪除 URL 的方法設計如下:
```python
class RedisUtils:
...
@staticmethod
def store_image_url(email: str, image_url: str, ttl=600):
"""將 image_url 存入 Redis,預設有效 10 分鐘"""
redis_conn = get_redis_connection("default")
redis_conn.set(f"image_url:{email}", image_url, ex=ttl)
print(f"將 image_url 存入 Redis:{image_url}")
@staticmethod
def get_stored_image_url(email: str):
redis_conn = get_redis_connection("default")
return redis_conn.get(f"image_url:{email}")
```
然後在處理圖片訊息前,我們也檢查一下這張圖是否已經重複檢驗過。Redis 的時間抓 30 秒就好,因為重送的訊息通常都很快。
最後整段檢驗圖片(檔案)訊息是否重複的驗證流程如下:
```python
...
# 處理圖片訊息(message + files)
elif event.get("type") == "message" and "files" in event:
# 取用 client_msg_id 或 event_ts 當唯一 key
msg_id = event.get("client_msg_id") or event.get("event_ts")
if event.get("type") == "message" and "files" in event:
# 在 Redis 檢查這張圖是否已經處理過
if RedisUtils.get_stored_image_url(msg_id):
print(f"跳過重複訊息 {msg_id}")
return JsonResponse({"status": "duplicate"})
RedisUtils.store_image_url(msg_id, ttl=30)
...
```
# 四、儲存 Slack 用戶資訊
如果有需要紀錄用戶資料去做統計、或是身分驗證等用途,Slack 有提供相關 API 可以存取留言過的用戶資訊:
1. 在 Django 安裝 slack app 的套件:`uv add django-slack-app`
```python
# settings.py
INSTALLED_APPS = ['slack_app', ...]
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'slack_app.auth_backends.SlackAuthenticationBackend',
]
SLACK_CLIENT_ID = '你的 Slack App Client ID'
SLACK_CLIENT_SECRET = '你的 Slack App Secret'
SLACK_SIGNING_SECRET = '你的 Signing Secret'
```
2. 去 Slack api 的 "Bot Token Scope" 加上 `users:read` 的權限。(2017 年以後,如果要查詢 email,需再加上 `users:read:email` 的權限)
3. 在 Django 建立 Slack 的 Model。收到訊息並完成 OAuth 驗證後,可以得到的欄位有這些:


4. 串接 Slack 身分驗證的 API,就可以取得想要的用戶資訊了。
```python
# 取得用戶資訊 (串接 Slack 提供的用戶驗證 API)
def get_slack_user_info(user_id):
url = 'https://slack.com/api/users.info'
headers = {
'Authorization': f'Bearer {SLACK_BOT_TOKEN}'
}
params = {
'user': user_id
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
return data.get('user', {})
...
# 處理事件
if payload.get("type") == "event_callback":
event = payload.get("event")
team_id = payload.get('team_id')
channel = event.get("channel")
user = event.get("user")
# 呼叫 Slack API 取得使用者資訊
user_info = get_slack_user_info(user).get('profile', {})
# 儲存或更新 Model
CustomSlackUser.objects.update_or_create(
slack_user_id=user,
defaults={
'team_id': team_id,
'real_name': user_info.get('real_name'),
'display_name': user_info.get('display_name'),
'email': user_info.get('email'),
'updated_time': timezone.now()
}
)
...
```
本來應該這樣就完成了。
但是 django-slack-app 本身有個**很大的問題**:他在 PyPI 上的最新版本是 1.0.40(2020 年 3 月 27 日)——**也就是說,它已經 5 年多沒有更新,還停留在 Django 2.x / Postgres JSONField 的時代,沒有針對 SQLite 或新版 Django 做過調整。**
所以我們會遇到這個錯誤:
```powershell
ModuleNotFoundError: No module named 'psycopg2'
```
這是因為在 slack-app 的 Model 有用到舊版的 JSONField:在 Django 3.1 以前,JSON 的模型欄位是引用 `django.contrib.postgres.fields.JSONField` 提供的方法,當時這還是 PostgreSQL 專用的欄位,所以要安裝相關套件,也就是 'psycopg2'。
但在 Django 3.1 以後,JSONField 已經變成跨資料庫的核心欄位,可以直接從 `django.db.models` 匯入,SQLite、PostgreSQL、MySQL 都能用。
一個方法是,我們可以直接修改裡面的 import 就好 (記得要連遷移檔都一起改),但這樣當我們更新或重新安裝 django-slack-app 時,就有可能被改回來。
所以另一種做法是:**fork 一份 django-slack-app 到自己的 repo,把 model 和 migration 都改好,之後直接用你自己的版本安裝。**
1. 先解除安裝舊套件:`uv remove django-slack-app`
2. 到 [django-slack-app 的 GitHub](https://github.com/webscopeio/django-slack-app),點選右上角的 fork,按下 create fork 後,就會在你的 repository 建立一個相同的 django-slack-app。

3. 在專案外的資料夾將 fork 的套件 clone 到本地端。(安全起見)
```powershell
cd 你的專案資料夾外面 # 先到一個安全的位置
git clone https://github.com/你的GitHub帳號/django-slack-app.git
cd django-slack-app
```
4. 打開檔案,修改裡面所有用到 `django.contrib.postgres.fields.JSONField` 的地方 (包含遷移檔),改成 `django.db.models`。
:::success
GitHub 的版本似乎有更新過,因此只要修改遷移檔的 import,以及 29 行 JSONField 的引用路徑拿掉,就可以使用了。
:::
5. 到 setup.py 修改版本號,方便後續確認使用的是自己修改的版本。
```python
setup(
version="1.0.0+sqlitefix", # 版本號格式可能會影響 import,請自行注意
install_requires=["slackclient", "celery"]
)
```
6. commit 並 push 回 GitHub
7. 回到原本的專案,將我們修改過的套件透過 git 網址安裝:`uv add git+https://github.com/StevenShih-0402/django-slack-app.git@master`,在安裝的套件中有看到你版本號的 django-slack-app 就表示安裝成功了。
```powershell
...
+ django-slack-app==1.0.0+sqlitefix (from git+https://github.com/StevenShih-0402/django-slack-app.git@6b3530cbe0a121fa1c64574803f73c44484e3f25)
...
```
:::warning
**後續維護**
官方更新時,你可以在 Fork 拉取更新後再 push 到 GitHub,然後用 `uv sync` 更新。
:::
這樣我們在傳訊息時,就可以將用戶資訊記錄下來了。

# 五、公開發布你的機器人
當我們的機器人 (應用程式) 開發完成後,當然是希望別人可以下載來用了。在 Slack 要公開發布你的機器人,有兩種方式:
1. **上架到 App Directory (Slack Market)**
>提交應用程式給 Slack 審核團隊,通過後會正式列在 Slack 官方的 Marketplace 上。
像是 Google 系列工具、Notion 筆記工具、Zapier 流程自動化工具等應用程式,都有公開發布在 Slack Market 讓各公司的用戶下載。如果你希望廣泛推廣、獲得更多曝光、並建立品牌信任度,這是最合適的管道。
2. **公開發佈 (Public Distribution)**
>即使不給 Slack 官方審核,也可以在應用程式儀表板中啟用 OAuth 流程,並獲得一個可分享的「新增至 Slack」按鈕或 URL。
像是如果你的應用程式只給特定公司使用,~~或是你的應用程式可能過不了 Slack 官方審核~~,就很適合這個方法。
如果有興趣或真的想開發大型商用的應用程式,可以再去研究如何上架到 Slack Market。但無論如何,公開發布都是必須的,因此這邊先示範如何發布你的機器人:
1. 請先確認程式部署的伺服器網域 (測試時可用 zrok 或 ngrok 等臨時網域,如:`https://4x47wqlbrawp.share.zrok.io`,但這需要每天更新。),以及 OAuth Permissions 有順利建立。
2. 在 Slack API 左側選擇 "Manage Distribute" 選項,確認它要求的所有規範都沒問題,就可以按下 Activate Public Distribution:


3. 之後就可以透過上述三種方法 (按鈕 UI、分享網址、嵌入連結)選擇你要安裝的工作空間。

# 六、總結
Slack 可以根據需求去設計流程自動化的應用程式 (機器人),若運用得當,員工甚至可以在 Slack 就完成所有的公司行政流程與業務,減少重複的工作量與時間,這正是流程自動化帶來的長遠效益。
若對 Slack 機器人有興趣,可以參考[官方文件](https://docs.slack.dev/apis/)深入研究。
**此文章同時公開發表於「韜睿軟體有限公司」官網,對於影像辨識、自然語言處理,以及自動化流程等技術應用有興趣的話,這裡分享了許多相關主題的文章,歡迎前來瀏覽~**
{%preview https://www.ignsw.com/slack-%e6%a9%9f%e5%99%a8%e4%ba%ba%e5%a6%82%e4%bd%95%e5%8d%94%e5%8a%a9%e6%95%b4%e5%90%88-ocr-%e7%a5%a8%e6%93%9a%e8%88%87%e8%b7%a8%e5%b9%b3%e5%8f%b0%e8%b3%87%e8%a8%8a%e6%8f%90%e5%8d%87%e5%b7%a5%e6%95%88/ %}