---
group: 開發者文件
order: 35
---
# 自製上市公司重訊追蹤器

> 這篇教學示範如何用一段 prompt 讓 AI agent（如 Claude Code）幫你建立一個「上市公司重訊追蹤器」，並在有新重訊時自動推播到你的 Slack。

---

## 這個追蹤器能做什麼

- **瀏覽上市/上櫃公司**：搜尋公司名稱或股票代號，查看公司基本資訊
- **查看最新重訊**：即時顯示任意公司的最新重大訊息（來源：公開資訊觀測站）
- **追蹤自選公司**：把想關注的公司加入追蹤清單（存在瀏覽器，不需後端）
- **自動推播通知**：網頁開著時，背景每 5 分鐘自動檢查追蹤公司有無新重訊，有新的就推播到你的 Slack

---

## 前置條件

在開始之前，請先完成以下設定：

1. **歐噴資料庫帳號**：到 [data.openfun.tw](https://data.openfun.tw) 免費註冊
2. **綁定 Slack 頻道**：登入後到「個人頁面 → 推播設定」，完成 Slack 連結（否則通知無法送達）
3. **GitHub 帳號**（選填）：若想把追蹤器部署到 GitHub Pages，需要有帳號

---

## 使用方法

### 步驟一：開啟 Claude Code（或任何 AI coding agent）

### 步驟二：貼上以下 prompt

複製下方整段 prompt 貼給 AI agent，它會自動幫你完成所有程式碼：

---

```
請幫我建立一個「上市公司重訊追蹤器」純前端 web 服務（單一 index.html，無後端）。

## 資料來源

以下資料集說明和查詢方式請自行讀取 skill.md：
- 上市公司清單：https://data.openfun.tw/datasets/tw.com.twse~ref~listed/skill.md
- 上櫃公司清單：https://data.openfun.tw/datasets/tw.com.tpex~ref~otc/skill.md
- 重訊查詢（Elasticsearch）：https://data.openfun.tw/datasets/tw.openfun~api~elastic-mopsov/skill.md

整體架構和 API 呼叫方式請參考：
https://data.openfun.tw/knowledge/build-openfun-web-service

## 登入與 Token 設計

使用 Device Authorization Grant 流程，申請具備以下 scope 的永久 token：
- requested_scopes: ["permanent", "notify"]
- requested_dataset_scope: ["tw.openfun~api~elastic-mopsov", "tw.com.twse~ref~listed", "tw.com.tpex~ref~otc"]

Token 存入 localStorage（key: openfun_token），需實作過期偵測（永久 token expires_in 為 null）。

注意：requested_dataset_scope 最多 3 個，此 tracker 剛好用完（elastic-mopsov、listed、otc）；不需要 requested_category_scope。永久 token 鎖定後，只有這 3 個 dataset 可查，其他 dataset（含 public）均會被拒絕。

## 功能需求

### 1. 登入流程
- 頁面載入時檢查 localStorage 是否有有效 token
- 未登入時顯示登入按鈕，點擊後開啟 Device Auth 授權頁（verification_uri_complete）
- 在同分頁輪詢直到授權完成，無需開新視窗

### 2. 公司瀏覽與搜尋
- 搜尋框：可輸入公司名稱或股票代號（支援上市 + 上櫃）；判斷輸入是否為 4 位數字，若是則用 `?公司代號=` 精確比對，否則用 `?q=` 全文搜尋
- 搜尋結果顯示：公司代號、公司名稱、產業別、市場別（上市/上櫃）
- 點擊公司 → 開啟公司詳情頁（或展開面板），顯示：
  - 公司基本資訊（從 listed/otc 資料集取得）
  - 最新 10 筆重訊（從 elastic-mopsov 取得，含主旨、時間、原始連結）
- 追蹤/取消追蹤按鈕（在搜尋結果和詳情頁均有）

### 3. 追蹤清單
- 首頁顯示目前追蹤的公司清單（從 localStorage 讀取）
- 每家公司顯示最新一筆重訊的主旨與時間
- 可從清單中移除追蹤

### 4. 背景輪詢與推播
- 登入後立即開始輪詢，間隔 5 分鐘
- 每次輪詢：對每家追蹤公司查詢 elastic-mopsov，取最新一筆的 spoke_at
- 若比上次記錄的 spoke_at 新 → 呼叫 POST /api/v1/notify 推播：
  - message: "{公司名稱} 有新重訊：{公告主旨}"
  - url: 該公告的 hyperlink
  - digest: false（即時推播，若使用者為免費帳號改為 true）
- 輪詢狀態顯示在頁面角落（上次檢查時間）

### 5. 匯出/匯入追蹤清單
- 提供「匯出設定」按鈕，下載 JSON 檔（公司代號清單）
- 提供「匯入設定」按鈕，上傳 JSON 檔還原追蹤清單

## 技術要求

- 單一 index.html 檔，CSS 內嵌，無外部 CDN 依賴
- 支援 RWD（手機和桌面均可使用）
- 重訊查詢使用 Elasticsearch Query DSL（POST + JSON body），Base URL: https://elastic-mopsov.api.openfun.dev
- notify 失敗時（如 no_channel_bound、membership_required）要顯示友善的錯誤提示，不要 crash
- 頁籤在背景時降低輪詢頻率（15 分鐘）節省資源

## 完成後

程式碼完成後，詢問使用者：「這個追蹤器目前可以在本機瀏覽器直接開啟使用。如果你想部署到網路上讓其他人也能用，或是方便你在手機上存取，有幾種選項，需要我介紹嗎？」

若使用者想部署，請參考：https://data.openfun.tw/knowledge/deploy-static-web-service
```

---

### 步驟三：跟著 agent 完成授權設定

Agent 會：
1. 產生 `index.html` 程式碼
2. 引導你在瀏覽器開啟並測試
3. 說明如何部署到 GitHub Pages

首次使用追蹤器時，你需要完成歐噴帳號授權（選擇「永久授權」），之後不需要重複登入。

---

## 常見問題

**Q：通知沒有送到 Slack？**
A：確認已在歐噴個人頁面完成 Slack 綁定。若看到 `no_channel_bound` 錯誤，到 [data.openfun.tw/user](https://data.openfun.tw/user) → 推播設定 → 連結 Slack。

**Q：網頁關掉後還能收到通知嗎？**
A：不行。這個追蹤器使用瀏覽器端 JavaScript 輪詢，網頁關掉後就停止。若需要「關著也能收到通知」，需要搭配後端 cron job，超出純前端架構。

**Q：追蹤的公司清單存在哪？**
A：存在瀏覽器的 localStorage，只有你自己的這台電腦這個瀏覽器看得到。可以用「匯出設定」備份，換電腦時用「匯入設定」還原。

**Q：免費帳號可以用即時通知嗎？**
A：免費帳號只能使用 digest 模式（彙整推播），即時推播需要付費帳號。Prompt 中的 `digest: false` 改成 `digest: true` 即可讓免費帳號也能使用（通知會延遲到下次 digest 批次送出）。

---

## 更多推播範例

熟悉上面的重訊追蹤器之後，同樣的做法（前置條件、使用步驟都一樣）也能套用到其他資料。以下兩個範例只要把步驟二的 prompt 換掉即可。

## 延伸範例一：政府新聞稿關鍵字追蹤器

### 這個追蹤器能做什麼

- **訂關鍵字**：輸入想關注的詞（如「長照」「颱風」「電價」），可選擇只看某個機關、或只比對標題
- **看最新新聞稿**：每個追蹤條件顯示最近 5 則符合的新聞稿（來源：中央五院、直轄市／縣市政府與部會所屬機關的新聞稿與公告）
- **自動推播通知**：網頁開著時，每 5 分鐘檢查一次，有新的新聞稿就推播到你的 Slack

### 登入方式與重訊追蹤器不同

政府新聞稿資料集（`tw.openfun~api~elastic-govnews`）**不能申請永久 token**（device auth 帶 `permanent` 會回 `400 只能限定公開資料集`），所以這個追蹤器提供兩種登入：

1. **歐噴帳號授權**：24 小時有效，過期後按一次登入即可
2. **貼上 API 金鑰**：到 [data.openfun.tw/user](https://data.openfun.tw/user) 申請長效金鑰貼進追蹤器，之後不用重新登入

### Prompt

```
請幫我建立一個「政府新聞稿關鍵字追蹤器」純前端 web 服務（單一 index.html，無後端）。

## 資料來源

資料集說明和查詢方式請自行讀取 skill.md：
- 政府新聞稿搜尋（Elasticsearch）：https://data.openfun.tw/datasets/tw.openfun~api~elastic-govnews/skill.md
- 收錄機關清單：https://data.openfun.tw/datasets/tw.openfun~ref~govnews-list/skill.md

整體架構和 API 呼叫方式請參考：
https://data.openfun.tw/knowledge/build-openfun-web-service

## 登入與 Token 設計

elastic-govnews 不是公開資料集，不能申請永久 token（device auth 帶 permanent + requested_dataset_scope 會回 400「只能限定公開資料集」）。請提供兩種登入方式：
1. Device Authorization Grant，只申請 requested_scopes: ["notify"]（不要帶 permanent），token 24 小時有效
2. 讓使用者貼上自己在 data.openfun.tw/user 申請的 API 金鑰（長效）

Token 存入 localStorage（key: openfun_token），到期時間存 openfun_token_expires_at（API 金鑰存 "never"）。ES 回 401 時清除 token 並提示重新登入。

## 功能需求

### 1. 追蹤條件
- 使用者輸入「關鍵字」（必填）、「機關」（選填）、「只比對標題」（勾選框）後加入追蹤
- 機關欄位提供下拉建議：用 ES terms aggregation 取 org_name.keyword 的所有值（size 2000）
- 查詢方式：bool.must 放 multi_match（type: "phrase"），fields 預設 ["title^3", "content_text"]，勾「只比對標題」時改為 ["title"]；有填機關時 bool.filter 加 term org_name.keyword
- 依 time 由新到舊排序，每次取 10 筆，_source 取 title、org_name、category、time、source_url
- 同一則新聞常以不同分類重複刊登（同機關同標題、不同 _id），請依「org_name + title」去重

### 2. 追蹤清單
- 顯示所有追蹤條件，每個條件列出最近 5 則（時間、機關、標題，標題連到 source_url）
- 可移除追蹤條件

### 3. 背景輪詢與推播
- 登入後立即檢查一次，之後每 5 分鐘一次；頁籤在背景時改為 15 分鐘
- 判斷「新的一則」請用 _id 比對（每個條件記住最近 100 個已看過的 _id），不要用 time 比大小：time 常只有日期（00:00:00），也有預排在未來時間的，用時間判斷會漏報或重複
- 某個條件第一次查詢時只建立基準、不推播
- 有新的就呼叫 POST https://data.openfun.tw/api/v1/notify：
  - message: "【{關鍵字}｜{機關}】{發布機關}：{標題}"
  - url: source_url
  - digest: 先送 false（即時）；若回 membership_required 改送 true
- 頁面角落顯示上次檢查時間

### 4. 匯出/匯入
- 「匯出設定」下載 JSON（追蹤條件清單），「匯入設定」上傳 JSON 還原

## 技術要求

- 單一 index.html，CSS 內嵌，無外部 CDN 依賴，支援 RWD
- ES 端點：POST https://elastic-govnews.api.openfun.dev/gov-news_announcements/_search（Header: Authorization: Bearer {token}）
- notify 失敗時（如 no_channel_bound、membership_required）要顯示友善的錯誤提示，不要 crash；錯誤碼在回應的 error 欄位
- 所有 localStorage 讀寫包 try/catch

## 完成後

程式碼完成後，詢問使用者：「這個追蹤器目前可以在本機瀏覽器直接開啟使用。如果你想部署到網路上讓其他人也能用，或是方便你在手機上存取，有幾種選項，需要我介紹嗎？」

若使用者想部署，請參考：https://data.openfun.tw/knowledge/deploy-static-web-service
```

---

## 延伸範例二：立法院委員會會議議程追蹤器

### 這個追蹤器能做什麼

- **勾選委員會**：選你關心的常設委員會（內政、經濟、財政、教育及文化、交通、社福及衛環、外交及國防、司法及法制）
- **訂議程關鍵字**：如「原子能法」「長照」，不分委員會，任何會議（含公聽會、黨團協商）的議程出現關鍵字就通知
- **看即將召開的會議**：列出符合條件、日期在今天之後的會議，含時間、地點、會議事由
- **自動推播通知**：新排會議、或已排會議的時間／地點／議程有異動時推播到你的 Slack

### 登入說明

立法院 API（`tw.openfun~api~legislation`）查資料**不需要登入**；登入只是為了推播，可以申請永久 token（鎖定 `tw.openfun~api~legislation`），之後不用重新登入。沒登入也能使用，只是不會推播。

### Prompt

```
請幫我建立一個「立法院委員會會議議程追蹤器」純前端 web 服務（單一 index.html，無後端）。

## 資料來源

資料集說明和查詢方式請自行讀取 skill.md：
- 立法院 API（LYAPI）：https://data.openfun.tw/datasets/tw.openfun~api~legislation/skill.md

整體架構、Device Auth 與 notify 呼叫方式請參考：
https://data.openfun.tw/knowledge/build-openfun-web-service

## 登入與 Token 設計

- 查立法院資料不需要 token。重要：從瀏覽器呼叫 https://ly.govapi.tw/v2 時「不要」帶 Authorization header，帶了會觸發 CORS preflight 而失敗（Failed to fetch）；不帶 header 可正常取得資料
- token 只用在 notify 推播。使用 Device Authorization Grant 申請永久 token：
  - requested_scopes: ["permanent", "notify"]
  - requested_dataset_scope: ["tw.openfun~api~legislation"]
- Token 存入 localStorage（key: openfun_token），永久 token 的 expires_in 為 null
- 未登入時仍可瀏覽會議，只是不推播，頁首顯示「未登入：只顯示，不推播」

## 功能需求

### 1. 追蹤設定
- 委員會清單：GET /committees?limit=100，只列 委員會類別 = 1（常設委員會），以勾選框呈現，存 委員會代號
- 關鍵字：可新增／移除，比對「會議標題 + 會議資料[0].會議事由」（字串包含）
- 不要用 /meets 的 q 參數篩關鍵字：q 是模糊全文搜尋（實測 q=原子能法 回 7457 筆），請在前端自行比對

### 2. 取得會議
- GET /meets?limit=100（預設依 屆、會期、日期 由新到舊，已涵蓋即將召開與近期會議）
- 每筆會議取：會議代碼、會議種類、會議標題、日期（陣列）、委員會代號（陣列）、會議資料[0] 的 會議事由、會議時間區間、會議地點、ppg_url（公聽會可能沒有 ppg_url）
- 符合條件 = 委員會代號 有交集 或 關鍵字命中
- 「即將召開」區塊列出符合條件、最後一個日期 ≥ 今天（台灣時間）的會議，依日期排序，顯示日期、時間、地點、標題（有 ppg_url 就連過去）、符合原因、會議事由

### 3. 背景輪詢與推播
- 每 15 分鐘檢查一次，頁籤在背景時改為 60 分鐘
- 以 會議代碼 為 key，記錄每場會議的指紋（日期 + 會議時間區間 + 會議地點 + 會議事由）
  - 沒看過的 會議代碼 → 推播「新排會議」
  - 指紋改變 → 推播「議程異動」
- 第一次執行、以及使用者每次修改追蹤設定後的第一次檢查，只建立基準、不推播（避免把既有會議全部當成新會議）
- notify：POST https://data.openfun.tw/api/v1/notify
  - message: "【立法院｜{符合原因}】新排會議：{會議標題}（{日期} {時間}，{地點}）" 或 "【立法院｜{符合原因}】議程異動：{會議標題}（{日期} {時間}）"
  - url: ppg_url（沒有就省略）
  - digest: 先送 false；若回 membership_required 改送 true

### 4. 匯出/匯入
- 「匯出設定」下載 JSON（委員會代號與關鍵字），「匯入設定」上傳 JSON 還原

## 技術要求

- 單一 index.html，CSS 內嵌，無外部 CDN 依賴，支援 RWD
- notify 失敗時（如 no_channel_bound、membership_required）要顯示友善的錯誤提示，不要 crash；錯誤碼在回應的 error 欄位
- 所有 localStorage 讀寫包 try/catch

## 完成後

程式碼完成後，詢問使用者：「這個追蹤器目前可以在本機瀏覽器直接開啟使用。如果你想部署到網路上讓其他人也能用，或是方便你在手機上存取，有幾種選項，需要我介紹嗎？」

若使用者想部署，請參考：https://data.openfun.tw/knowledge/deploy-static-web-service
```

---

## 延伸範例常見問題

**Q：政府新聞稿追蹤器每天都要重新登入？**
A：用歐噴帳號授權的話是（token 24 小時有效），因為這個資料集不支援永久授權。若不想每天登入，到 [data.openfun.tw/user](https://data.openfun.tw/user) 申請 API 金鑰，貼進追蹤器即可。

**Q：政府新聞稿追蹤器通知太多、有些不相關？**
A：關鍵字預設比對標題和內文，內文順帶提到也會命中。勾「只比對標題」或指定機關可以減少雜訊。

**Q：立法院追蹤器為什麼沒有顯示即將召開的委員會會議？**
A：追蹤器只能顯示立法院已公布的排會。委員會通常在開會前幾天才公布議程；休會期間或新會期剛開始時，可能暫時沒有委員會會議。

---

## 有想法但不知道有什麼資料可以做？

如果你心裡有想追蹤的事情（例如「某個議題的政府公告」「某家公司有沒有被裁罰」「我家附近的標案」），但不確定歐噴資料庫有沒有對應的資料，可以直接跟 agent 討論，請它幫你盤點。把下面這段 prompt 貼給 agent，再把 `{…}` 換成你的想法：

```
我想做一個「有新資料就推播到 Slack」的純前端追蹤器，想追蹤的是：{用一兩句話描述你想追蹤什麼，例如：我關心的幾家公司有沒有新的裁罰、某個縣市有沒有新的標案}

請先不要寫程式，幫我盤點歐噴資料庫（data.openfun.tw）裡有沒有適合的資料。

## 盤點方式

1. 先引導我用 Device Authorization Grant 登入取得 token（流程見 https://data.openfun.tw/knowledge/build-openfun-web-service ）
2. 用 GET https://data.openfun.tw/api/v1/search?q={關鍵字} 搜尋候選資料集；每個需求最多試 2 個關鍵字變體，找不到就直接告訴我「目前沒有對應資料集」，不要一直換關鍵字
3. 對每個候選資料集，讀它的 skill.md（https://data.openfun.tw/datasets/{slug}/skill.md），並實際打 API 確認以下幾點：
   - 最新一筆資料的日期是什麼時候（資料有沒有在持續更新、大概多久更新一次）
   - 能不能篩到我關心的對象（例如依公司、機關、關鍵字、地區篩選）
   - 每筆資料有沒有穩定的 ID 可以判斷「這筆是新的」；如果只能靠日期，日期是不是精確到時間
   - 從瀏覽器直接呼叫會不會被 CORS 擋（帶與不帶 Authorization header 都試試看）
   - 能不能申請永久 token：用 device auth 帶 requested_scopes ["permanent","notify"] 和 requested_dataset_scope [slug] 試申請，回 400 就代表只能用 24 小時 token 或 API 金鑰
4. 整理成表格給我：資料集 slug、內容、最新一筆日期、可篩選的條件、判斷新資料的方式、CORS、能否永久 token，每項寫實測結果
5. 由我決定要用哪個資料集，確認後再照 https://data.openfun.tw/knowledge/mopsov-tracker-tutorial 裡的範例 prompt 格式幫我實作
```
