Slack 機器人如何協助整合 OCR 票據與跨平台資訊,提升工作效率

Slack差旅報銷

在現代工作環境中,團隊經常需要在不同平台之間反覆切換:例如查詢 Jira 上的專案進度、查看資料庫中的訂單狀態,或是在 Azure 系統中監控錯誤報告。這些手動查詢的過程雖然看似不複雜,卻往往打斷專注,降低工作效率。當資訊分散在多個平台時,即時掌握整體情況變得更加困難。

Slack 作為一個強大的協作工具,能夠通過 Slack 機器人 應用程式將來自不同系統的數據與通知無縫整合,無需離開 Slack 視窗,團隊成員可以即時獲取關鍵資訊,大幅提升工作效率。

本文將帶你從零開始,學習如何建立一個 Slack 機器人 應用程式,並透過 韜睿軟體 AI OCR API 整合到 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 建立一個新的 APP

這邊有兩個建立方式可以選擇:manifest 可以讓YMAL 或 JSON 的設定檔,明確定義權限與功能,且如果需要建立新環境,在重復使用上很方便。

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 或沒有版本化設定需求的用戶。

1. 這邊我們選擇用 scratch,輸入 App 名稱和要建置的工作環境後,就會看到 App 的設定頁面


Event Subscription 的選項,完成以下動作:

  • 打開 “Enable Events”,並輸入你的 Webhook 路徑 ( Slack 會立刻發送請求去驗證這個 URL 是否有效 )。
  • 在 “Subscribe to bot events” 加入三個事件:
    • app_mention(被提及)
    • message.channels(公開頻道訊息)
    • message.im(私訊)

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

3. 然後在 “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。

補充:Slack 的 DM 形式大致可以分成以下幾種:

  • 人與人之間的私訊:
    你可以直接點選某個人的名字,開啟一對一對話
  • 多人私訊:
    你可以跟兩個以上的人開啟群組私訊(但不是頻道)
  • App DM(Bot DM):
    這是 Slack 自動幫你的 bot 建立的私訊頁面,通常在左側 sidebar 的「Apps」區塊裡

二、Django 開發

首先加上環境變數,將機器人的 OAuth Token 加進 Django 專案的 ‘settings.py’:

SLACK_BOT_TOKEN = "xoxb-345...20f"

然後新增一個 Django app,這裡我們將它命名為 ‘slack_bot’,在裡面建立 Slack 機器人的基本版型,以及配置 URL。

# 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 的邏輯,在事件處理上做出區分:

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 重送的訊息,是的話就直接忽略。

@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 這絕對是每個訊息的唯一值。

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 的方法設計如下:

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 秒就好,因為重送的訊息通常都很快。

最後整段檢驗圖片(檔案)訊息是否重複的驗證流程如下:

...

# 處理圖片訊息(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
# 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'
  1. 去 Slack api 的 “Bot Token Scope” 加上 users:read 的權限。(2017 年以後,如果要查詢 email,需再加上 users:read:email 的權限)
  2. 在 Django 建立 Slack 的 Model。收到訊息並完成 OAuth 驗證後,可以得到的欄位有這些:
  1. 串接 Slack 身分驗證的 API,就可以取得想要的用戶資訊了。
# 取得用戶資訊 (串接 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 做過調整。

所以我們會遇到這個錯誤:

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,點選右上角的 fork,按下 create fork 後,就會在你的 repository 建立一個相同的 django-slack-app。

3. 在專案外的資料夾將 fork 的套件 clone 到本地端。(安全起見)

cd 你的專案資料夾外面 # 先到一個安全的位置
git clone https://github.com/你的GitHub帳號/django-slack-app.git
cd django-slack-app
  1. 打開檔案,修改裡面所有用到 django.contrib.postgres.fields.JSONField 的地方 (包含遷移檔),改成 django.db.models
    GitHub 的版本似乎有更新過,因此只要修改遷移檔的 import,以及 29 行 JSONField 的引用路徑拿掉,就可以使用了。
  2. 到 setup.py 修改版本號,方便後續確認使用的是自己修改的版本。
setup(
    version="1.0.0+sqlitefix",                    # 版本號格式可能會影響 import,請自行注意
    install_requires=["slackclient", "celery"]
)
  1. commit 並 push 回 GitHub
  2. 回到原本的專案,將我們修改過的套件透過 git 網址安裝:uv add git+https://github.com/你的GitHub帳號/django-slack-app.git@master,在安裝的套件中有看到你版本號的 django-slack-app 就表示安裝成功了。
...
 + django-slack-app==1.0.0+sqlitefix (from git+https://github.com/你的GitHub帳號/django-slack-app.git@6b3530cbe0a121fa1c64574803f73c44484e3f25)
 ...


後續維護
官方更新時,你可以在 Fork 拉取更新後再 push 到 GitHub,然後用 uv sync 更新。

這樣我們在傳訊息時,就可以將用戶資訊記錄下來了。

五、總結

Slack 可以根據需求去設計流程自動化的應用程式 (機器人),若運用得當,員工甚至可以在 Slack 就完成所有的公司行政流程與業務,減少重複的工作量與時間,這正是流程自動化帶來的長遠效益。

透過韜睿軟體 Swift OCR Portal 您可以取得不同的辨識應用,加入您的Slack機器人中使用,而對 Slack 機器人有興趣,可以參考官方文件深入研究。

Loading

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *