一個 type guard、一則 PR、一次 merge。從使用者到貢獻者。

OpenClaw 也好一陣子了。每天跟 Pingu 在終端機裡協作,從寫文章、管專案、跑 cron、到整理健康數據,數位生活有一半是在 OpenClaw 裡完成的。

用久了,自然會踩到一些邊角的 bug。

遇到 Bug

這個 bug 出現在 Telegram 整合的部分。OpenClaw 有個 describeReplyTarget 函式,負責解析 Telegram 訊息的 reply context,就是回覆一則訊息時,被回覆那則訊息的內容。

問題在於,Telegram API 回傳的 replyLike.text 不一定是字串,有時候會是一個 ExternalReplyInfo 物件。但原本的程式碼直接對它呼叫 .trim()

const rawText = replyLike.text ?? replyLike.caption ?? "";
return rawText.trim(); // 如果 rawText 是物件 → TypeError

物件沒有 .trim() 方法,直接 TypeError 炸開。

修復

修法很簡單,一個 type guard:

const rawText = replyLike.text ?? replyLike.caption ?? "";
const safeText = typeof rawText === "string" ? rawText.trim() : "";

同一個函式裡,quoteText 其實已經用了一模一樣的 pattern。這次的修改就是把已有的防禦邏輯補到漏掉的地方。

順手補了一個 regression test,確保非字串值不會再炸:

// 傳入 text: { some: "object" } → 應該回傳 null,不是 throw

PR 提交

PR #50500,標題:

fix(telegram): add type guard for reply context text

提交之後,Greptile(自動 code review bot)給了 5/5 confidence

“This PR is safe to merge:the change is minimal, correct, and well-tested.”

Maintainer @obviyus 在 3/23 直接 merge 進 main,留了一句:

“Thanks @p3nchan.”

沒有來回修改,沒有 request changes,一發就中。

感想

這是第一次對一個有規模的開源專案提交 PR 並被 merge。

改動很小,就是一個 type guard、幾行程式碼。但從「使用者」變成「貢獻者」,身份轉換的感覺比程式碼本身重要太多了。

好的開源貢獻往往就是這樣。在日常使用中發現問題,然後順手修掉,比驚天動地的重構更實際。

每天在用的工具,裡面可能就有一個小小的 bug 等著被修。

認真玩,慢慢來。

延伸閱讀


小企鵝的經驗

OpenClaw 是每天會用到的核心工具,從寫文章、管專案到跑 cron 都靠它。用久了自然會踩到邊角的 bug,這次 Telegram reply context 的 type 問題就是日常用著用著被觸發的。第一次送 PR 給有規模的開源專案,本來預期會來回修很多次,結果一發就中,反而有點意外。順手修自己用得到的東西,這條路徑比想像中順。

常見問題

Q: OpenClaw 是什麼?

OpenClaw 是一個開源的 AI CLI 工具,可以在終端機中與 AI 互動,執行程式碼、管理檔案、自動化工作流程。

Q: 這個 PR 修了什麼問題?

修復了 Telegram 平台上 reply context text 的 runtime error。當 API 回傳非字串值時,原本的 .trim() 會直接炸掉,加了一個 type guard 就解決了。

Q: 第一次貢獻開源專案難嗎?

不難,但要找到對的切入點。從自己實際遇到的 bug 出發,修復自然順手,reviewer 也容易理解動機。


整理:Penna|小企鵝 Penchan