有人在水 - 統計 Notion 待辦事項放到 Discord 頻道

孔子能從心所欲不逾矩。正是因為他已經掌握 GitHub Actions,能夠根據自己的需求自動化操作。
今日範例程式:https://github.com/Edit-Mr/2024-GitHub-Actions/tree/main/17
今天,我們將介紹如何使用 Bash 腳本或 Node.js 編寫 GitHub Actions,自動從 Notion 獲取待辦事項並更新到 Discord 頻道。希望能透過這個小專題幫助你了解如何利用進行自動化操作呼叫 API。如果你不會使用 Notion 的話,~~那你該去學學。~~你可以參考大致的流程與程式的邏輯。
首先讓我們看一下成果。你可以看到 Discord 頻道的標題會根據 Notion 中的待辦事項進行更新。
事前準備:建立 Notion 資料庫並獲取 API
我們在開始之前需要獲得幾個變數:
Notion 資料庫 ID
在我們開始時做之前請先打開 Notion 建立一個資料庫。這裡我以中電會的 Notion 為例。可以看到左到右分別是預設的 To-do、In progress、Complete。我們將會從這個資料庫中獲取待辦事項。
開啟資料庫連結並複製 ID。記得要點擊 view database 進入整個畫面都是資料庫的頁面喔。比如說如下的連結:
1https://www.notion.so/6e7c50281a8b406dbxxxxxxxx7892659?v=fe8e4b0c57e24axxxxxxxxxx13271567
這個連結中的 6e7c50281a8b406dbxxxxxxxx7892659
就是資料庫 ID。
取得 API Token
在 Notion 的設定中,可以取得 API Token。這個 Token 將會用來取得資料庫的資料。請記得不要將這個 Token 洩漏給他人。請至 Notion Developers 取得 API,輸入基本資料並選擇要應用的 Workspace。
請你複製這一串 Integration Token。生成完 API 之後,記得要邀請你剛才創建的機器人。點擊 Notion 右上角的三個點,Connect to,並選擇剛才創建的機器人。
建立 Discord Bot
在 Discord 開發者中心建立一個 Bot,並取得 Token。這個 Token 將會用來更新 Discord 頻道的標題。請至 Discord Developer Portal 建立一個新的應用程式。
Discord 頻道 ID
在 Discord 中,請先開啟開發者模式,接著右鍵要更新的頻道,並複製頻道 ID。這個 ID 將用於更新頻道的標題。這個頻道原則上就是用來顯示數字用的,所以可以鎖定權限讓其他人不能在裡面聊天。
開發者模式在 Discord 中的位置:設定 -> 進階 -> 開發者模式
今天我們會將一樣的專題分別使用 Bash 腳本或 Node.js 撰寫。首先讓我們先試著用 bash 寫看看。
Bash 版
Bash 自定義 Actions 允許我們使用 Shell 腳本來執行操作。這是一種簡單且強大的方式,可以實現許多自動化需求。相較於 JavaScript 自定義 Actions,Bash 版本通常較為簡單,適合用來執行簡單的 Shell 命令和操作。
步驟 1:設置專案結構
首先,創建一個新的 GitHub 存儲庫來容納我們的自定義 Action。在存儲庫中,創建以下目錄結構:
1my-custom-action/
2├── action.yml
3├── script.sh
4└── README.md
步驟 2:編寫 Action 配置文件
在 action.yml
文件中,我們需要定義 Action 的輸入、執行和輸出。以下是 action.yml
的內容:
1name: "Update Notion to Discord"
2description: "Fetch tasks from Notion and update Discord channel"
3inputs:
4 notion_database_id:
5 description: "Notion database ID"
6 required: true
7 notion_token:
8 description: "Notion API token"
9 required: true
10 discord_channel_id:
11 description: "Discord channel ID"
12 required: true
13 discord_token:
14 description: "Discord bot token"
15 required: true
16runs:
17 using: "composite"
18 steps:
19 - name: Run script
20 run: ./script.sh
21 env:
22 NOTION_DATABASE_ID: ${{ inputs.notion_database_id }}
23 NOTION_TOKEN: ${{ inputs.notion_token }}
24 DISCORD_CHANNEL_ID: ${{ inputs.discord_channel_id }}
25 DISCORD_TOKEN: ${{ inputs.discord_token }}
欸你發現了嗎?我們這裡使用了一個之前沒使用過的參數 composite
。這個參數可以讓我們在 Action 中執行多個步驟,這樣我們就可以在 Action 中執行多個 Shell 命令。同時我們還使用了之前沒使用過的 inputs
關鍵字,這個關鍵字可以讓我們在 Action 中訪問輸入的參數。
inputs
關鍵字用於定義 Action 的輸入參數,這些參數可以在 Action 運行時從外部設置。在這個例子中,我們定義了四個輸入參數:notion_database_id
、notion_token
、discord_channel_id
和 discord_token
。這些參數將用於從 Notion 獲取待辦事項並更新 Discord 頻道。當使用者第一次安裝 Action 時,他們需要提供這些參數的值。
步驟 3:編寫 Bash 腳本
在 script.sh
文件中,我們將編寫 Bash 腳本來實現具體的操作。以下是 script.sh
的內容:
1#!/bin/bash
2
3set -e
4
5update_tasks() {
6 local notion_database_id="$NOTION_DATABASE_ID"
7 local notion_token="$NOTION_TOKEN"
8 local discord_channel_id="$DISCORD_CHANNEL_ID"
9 local discord_token="$DISCORD_TOKEN"
10
11 # 從 Notion 獲取待辦事項
12 response=$(curl -s -X POST -H "Authorization: ${notion_token}" -H "Notion-Version: 2022-06-28" -H "Content-Type: application/json" "https://api.notion.com/v1/databases/${notion_database_id}/query")
13
14 if [ -n "$(echo "${response}" | jq '.results')" ]; then
15 not_started_count=0
16 in_progress_count=0
17 for row in $(echo "${response}" | jq -r '.results[] | @base64'); do
18 status_name=$(echo "${row}" | base64 -d | jq -r '.properties.Status.status.name')
19 echo "${status_name}"
20 if [ "${status_name}" = "Not started" ]; then
21 (( not_started_count++ ))
22 elif [ "${status_name}" = "In progress" ]; then
23 (( in_progress_count++ ))
24 fi
25 done
26
27 # 更新 Discord 頻道標題
28 update_discord_channel_title "還有 ${not_started_count} 件事沒人做"
29 update_discord_channel_title "${in_progress_count} 件事處理中"
30 else
31 echo "Error: Unable to retrieve data from Notion API."
32 exit 1
33 fi
34}
35
36update_discord_channel_title() {
37 local new_title="$1"
38 local channel_id="$DISCORD_CHANNEL_ID"
39 local url="https://discord.com/api/v10/channels/${channel_id}"
40 local token="Bot ${DISCORD_TOKEN}"
41
42 response=$(curl -s -X PATCH -H "Authorization: ${token}" -H "Content-Type: application/json" -d "{\"name\": \"${new_title}\"}" "${url}")
43 updated_title=$(echo "${response}" | jq -r '.name')
44 echo "Channel title updated successfully: ${updated_title}"
45}
46
47update_tasks
在腳本中,我們使用 curl
來從 Notion API 獲取待辦事項,並根據狀態更新 Discord 頻道的標題。
步驟 4:設置 GitHub Secrets
在 GitHub 存儲庫的設置中,添加所需的 Secrets:
NOTION_DATABASE_ID
NOTION_TOKEN
DISCORD_CHANNEL_ID
DISCORD_TOKEN
這些 Secrets 將用於在 Action 中安全地傳遞敏感信息。
步驟 5:運行和測試 Actions
當你將更改推送到 GitHub 存儲庫後,GitHub Actions 將會自動運行你的工作流程。你可以查看 Actions 頁面,確保自定義 Action 正確執行並更新 Discord 頻道標題。
JavaScript 版本
步驟 1:設置專案結構
首先,我們需要創建一個新的 GitHub 存儲庫來容納我們的自定義 Action。在存儲庫中,創建以下目錄結構:
1my-custom-action/
2├── nodejs.yml
3├── app.js
4└── package.json
老樣子,使用 npm init -y
來初始化一個 Node.js 專案。
步驟 2:編寫 Action 配置文件
在 action.yml
文件中,我們需要定義 Action 的輸入、執行和輸出。以下是 action.yml
的範例內容:
1name: "Update Notion to Discord"
2description: "Fetch tasks from Notion and update Discord channel"
3inputs:
4 notion_database_id:
5 description: "Notion database ID"
6 required: true
7 notion_token:
8 description: "Notion API token"
9 required: true
10 discord_channel_id:
11 description: "Discord channel ID"
12 required: true
13 discord_token:
14 description: "Discord bot token"
15 required: true
16runs:
17 using: "node20"
18 main: "app.js"
步驟 3:編寫 Action 腳本
在 app.js
文件中,我們將編寫 JavaScript 代碼來完成具體的操作。以下是 index.js
的內容:
1const core = require("@actions/core");
2const axios = require("axios");
3
4async function updateTasks() {
5 try {
6 // 讀取輸入參數
7 const notionDatabaseId = core.getInput("notion_database_id");
8 const notionToken = core.getInput("notion_token");
9 const discordChannelId = core.getInput("discord_channel_id");
10 const discordToken = core.getInput("discord_token");
11
12 // 從 Notion 獲取待辦事項
13 const notionResponse = await axios.post(
14 `https://api.notion.com/v1/databases/${notionDatabaseId}/query`,
15 {},
16 {
17 headers: {
18 Authorization: `Bearer ${notionToken}`,
19 "Notion-Version": "2022-06-28",
20 "Content-Type": "application/json"
21 }
22 }
23 );
24
25 let notStartedCount = 0;
26 let inProgressCount = 0;
27
28 // 解析 Notion API 的響應
29 notionResponse.data.results.forEach((result) => {
30 const status = result.properties.Status.status.name;
31 if (status === "Not started") {
32 notStartedCount++;
33 } else if (status === "In progress") {
34 inProgressCount++;
35 }
36 });
37
38 // 更新 Discord 頻道標題
39 await axios.patch(
40 `https://discord.com/api/v10/channels/${discordChannelId}`,
41 {
42 name: `還有 ${notStartedCount} 件事沒人做`
43 },
44 {
45 headers: {
46 Authorization: `Bot ${discordToken}`,
47 "Content-Type": "application/json"
48 }
49 }
50 );
51
52 await axios.patch(
53 `https://discord.com/api/v10/channels/${discordChannelId}`,
54 {
55 name: `${inProgressCount} 件事處理中`
56 },
57 {
58 headers: {
59 Authorization: `Bot ${discordToken}`,
60 "Content-Type": "application/json"
61 }
62 }
63 );
64
65 console.log("Discord channel title updated successfully");
66 } catch (error) {
67 core.setFailed(`Action failed with error: ${error.message}`);
68 }
69}
70
71updateTasks();
首先我們引入了 @actions/core
來訪問 Action 的輸入參數,並引入 axios
來發送 HTTP 請求。在 updateTasks
函數中,我們讀取輸入參數,從 Notion API 獲取待辦事項,並更新 Discord 頻道的標題。
步驟 4:安裝套件
在專案根目錄下執行以下命令來安裝依賴套件:
1 npm install @actions/core axios
配置 GitHub Actions 工作流程
現在,我們需要創建一個 GitHub Actions 工作流程來使用我們的自定義 Action。在 .github/workflows
目錄下創建一個新的 YAML 文件,例如 main.yml
,並添加以下內容:
1name: Update Notion to Discord
2
3on:
4 push:
5 branches:
6 - main
7
8jobs:
9 update:
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Checkout repository
14 uses: actions/checkout@v3
15
16 - name: Use custom action
17 uses: ./ # 使用自定義 Action 的路徑
18 with:
19 notion_database_id: ${{ secrets.NOTION_DATABASE_ID }}
20 notion_token: ${{ secrets.NOTION_TOKEN }}
21 discord_channel_id: ${{ secrets.DISCORD_CHANNEL_ID }}
22 discord_token: ${{ secrets.DISCORD_TOKEN }}
4. 設置 GitHub Secrets
在 GitHub 存儲庫的設置中,添加所需的 Secrets:
NOTION_DATABASE_ID
NOTION_TOKEN
DISCORD_CHANNEL_ID
DISCORD_TOKEN
這些 Secrets 將用於在 Action 中安全地傳遞敏感信息。
on 的部分定義了觸發條件。你可以根據具體需求選擇不同的觸發條件,例如 schedule(定時觸發),或其他事件。
1on:
2 schedule:
3 - cron: "0 0 * * *" # 每天 00:00 UTC 觸發
今天的 GitHub Actions 怎麼怪怪的?
今天和以前不一樣,我們的 Action 並不是直接執行的,而是透過一個腳本來執行的。這樣的好處是我們可以在腳本中執行多個命令,並且可以更靈活地控制流程。這樣的設計可以讓我們更好地利用 GitHub Actions 來實現自動化操作。
小結
今天我們探討了如何使用 Bash 腳本編寫 GitHub Actions 自定義 Action。我們編寫了一個簡單的 Bash 腳本,從 Notion 獲取待辦事項並更新 Discord 頻道的標題。通過這個實踐,我們了解了如何使用 Shell 腳本來實現自動化操作,並且學會了如何配置和使用自定義 Actions。希望這篇教程對你有所幫助。s