---
# System prepended metadata

title: Slack Chatbot - 串接外部 API 與跨平台資訊整合分享
tags: [openData]

---

>實務上的業務情境，經常會需要在不同平台之間反覆切換；像是在 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
![image](https://hackmd.io/_uploads/B1zMOvltgx.png)
這邊有兩個建立方式可以選擇：
- `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 的設定頁面
![8x3nyobr](https://hackmd.io/_uploads/r1VtyvF5xg.png)
在 ==Event Subscription== 的選項，完成以下動作：
- 打開 "Enable Events"，並輸入你的 Webhook 路徑 ( Slack 會立刻發送請求去驗證這個 URL 是否有效 )。
- 在 "Subscribe to bot events" 加入三個事件：
    - `app_mention`（被提及）
    - `message.channels`（公開頻道訊息）
    - `message.im`（私訊）
![mnygykzn](https://hackmd.io/_uploads/HJks1DK5le.png)

3. 在 ==OAuth & Permission== 選項，點選 "Install to <工作空間>" 的按鈕取得 OAuth Token。
![mslgp84e](https://hackmd.io/_uploads/SJkegDY5gl.png)

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

![image](https://hackmd.io/_uploads/ryTxuueKeg.png)

4. Slack 會自動幫你在「應用程式 (Apps)」建立一個私訊對話 (Direct Message)，通稱 App DM。
![ccx3po3f](https://hackmd.io/_uploads/BkzoxPY5xx.png)
傳送訊息被關閉是正常的。這個頁面不是頻道，也不是真正的 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 @<機器人名稱>`
![4ziql1c3](https://hackmd.io/_uploads/B1G4ZPY5xe.png)
機器人進入群組後就可以使用了。只要你標註機器人，訊息就會觸發 Webhook。
![q8ak2tls](https://hackmd.io/_uploads/Hyn9-vF5xg.png)

## 讓 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)
```

測試時不用標註機器人，直接上傳圖片即可。
![zofwbswo](https://hackmd.io/_uploads/H1ifzvK5ee.png)

# 三、業務情境實作 - 串接 API

如果公司有內部的系統服務 API(如資料查詢、身分驗證等)，也可以將它們串接到 Slack 機器人上，其實就是把剛才處理圖片的訊息內容改成 API 的回傳值就可以了。
這邊以串接公司票據辨識的 API 作為範例，得到的回傳結果如下：
![gyg6awmr](https://hackmd.io/_uploads/Byy0GPF5lg.png)
![螢幕擷取畫面 2025-09-04 153402](https://hackmd.io/_uploads/ryixI-d5el.png)
![螢幕擷取畫面 2025-09-04 153613](https://hackmd.io/_uploads/HJseLWd5gg.png)

(車票與發票皆為網路搜尋的範本，敏感資訊已有額外處理。)

## 補充：訊息事件的去重處理
要注意的是，通常在 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 驗證後，可以得到的欄位有這些：
![image](https://hackmd.io/_uploads/ry0V8emtge.png)
![image](https://hackmd.io/_uploads/HkjB8gQtge.png)
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。
![2jsfr4sx](https://hackmd.io/_uploads/ByHq7DKqel.png)
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` 更新。
:::

這樣我們在傳訊息時，就可以將用戶資訊記錄下來了。
![geex6hak](https://hackmd.io/_uploads/S1tZEwF5el.png)

# 五、公開發布你的機器人
當我們的機器人 (應用程式) 開發完成後，當然是希望別人可以下載來用了。在 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：
![image](https://hackmd.io/_uploads/ry0cOPfJWx.png)
![image](https://hackmd.io/_uploads/S1u2uwGyZg.png)
3. 之後就可以透過上述三種方法 (按鈕 UI、分享網址、嵌入連結)選擇你要安裝的工作空間。
![image](https://hackmd.io/_uploads/Hk5A5wGJbl.png)

# 六、總結
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/ %}