TTWChatMessageServer 是一個聊天室訊息服務器,可對接 TikTok、Twitch、Kick、Odysee 與 Youtube 的直播聊天室訊息,支援以下功能:
- 即時接收聊天室訊息
- 支援禮物、關注、加入、分享等事件
- 可推送到 Bark 或 Socket
- 可透過簡單 HTTP 接口開關服務
- 可透過簡單 HTTP 接口快速修改BARK/SocketAPI配置
- 指令不再洩漏:
G#clip處理後即return,不再被當一般聊天訊息轉發、送去翻譯或寫入過濾器(原 log 中「G#clip」被當普通訊息送出 + 觸發翻譯 API) - 剪輯訊息品質:原本回傳
null FitPerfectSlothOSsloth-VOD2-62CGySUmMP8(title 為空顯示 null、只有剪輯 ID),改為組合成可點擊的https://clips.twitch.tv/<id>連結;未指定標題時顯示預設文案 - 權限限制:原本任何人輸入
G#clip都能建立剪輯,現在限縮為主播本人、訂閱者或追隨者 - 錯誤處理:新增
createClip失敗的 catch,推送錯誤原因到疊加層與 log - 順帶修正 G#Ad 訂閱者路徑:原本訂閱者發
G#Ad時,非同步訂閱檢查讓原始指令文字洩漏進一般聊天,現改為await檢查並return
改進前後差異:
| 情境 | 改進前 | 改進後 |
|---|---|---|
訊息文字內貼圖片網址(如 …/Neuro2.png?raw=true) |
該網址只當廣告文字,頭像仍是 Twitch 頭貼 | 自動偵測為頭像,並從文字中移除該網址 |
更新廣告時沒帶 icon= |
targetAd.iconURL = iconURL 把頭像覆寫成空白,回退成 Twitch 頭貼 |
保留原本頭像,不會被洗掉 |
| 定時器 / 重啟恢復發送 | 用 ad.iconURL || '',iconURL 為空時沒頭像,與即時發送不一致 |
已解析的頭像(含 Twitch fallback)存入廣告紀錄,三條路徑用同一張圖 |
更新廣告時沒帶 user= |
自訂顯示名稱被覆寫回 Twitch 名稱 | 保留原 overlayUser |
- 統計統一由 Server.js 管理:
message_stats.json現在只有 Server.js 會寫入,TikTok.js 不再直接寫檔,改為退出時把最終統計快照回傳給 Server.js 合併 - 寫檔只增不減(max-merge):每次寫入前先讀取現有檔案,與記憶體統計做「取較大值」合併後才寫入,避免 TikTok.js 未運行時舊的快取快照覆蓋掉完整統計
- 手動清空統計:
/keyword頁面新增「🗑️ 清空統計」按鈕(POST/keyword/clear),一次清空記憶體、message_stats.json,並同步通知 TikTok.js 清空(若在運行) - 子進程統計併入:Server.js 收到 TikTok.js 的
all統計快照時會mergeStats()併入自己的統計 map,TikTok.js 直連收到的訊息也能納入統計
- 新進階重連邏輯:前 10 次斷線固定 15 秒重連,之後每次 +5 秒,最多拉長到 5 分鐘
- 連線成功或程式關閉時自動重置重連計數與間隔
- 支援每人最多 5 則廣告,可透過
id=N參數指定修改第幾則 - 管理頁面新增倒數計時器(顯示下次發送剩餘時間),歸零自動刷新
- 管理頁面新增伺服器運行狀態指示燈(🟢 運行中 / 🔴 未運行)
- 管理頁面操作提示改為 Toast 浮動通知,不再使用瀏覽器 alert
- 倒數計時在伺服器未運行時自動隱藏,避免無限 reload
- 支援手動從管理頁面建立贊助廣告
- 伺服器重啟後自動恢復有效廣告的定時器(
resumeAdTimers)
- 新增
interval=參數,訂閱者可設定廣告自動重複間隔(最低 15 分鐘) - 新增三種審核模式:不需審核(預設)、過濾器自動審核、手動審核
- 新增管理頁面
/sponsor,可瀏覽/通過/拒絕/啟用/停用/刪除贊助廣告 - 廣告資料持久化儲存於
sponsor_ads.json - 新增
/api/sponsor-ads/*API 端點
- 移除 800ms 發送延遲,訊息凍結解凍後即時送出
- 新增
visibilitychange監聽,分頁回到前景時自動更新觀眾資料
- CHAT 事件不再跳過僅有表情無文字的訊息,改取
emotes陣列內容 - EMOTE 事件新增 Socket 與 Bark 推送
- 主控台、關鍵字、日誌檔案頁面頂部新增快速導航按鈕
keyword.html改用相對路徑EventSource,不再需要手動修改內網 IPkeyword.html頁面隱藏時自動關閉 SSE 連線,避免返回上一頁後連線殘留- Server.js 關鍵字 SSE 端點移除無意義的
pushLog刷版
- 支援 Youtube 頻道直播聊天室訊息接收
- 透過 YouTube Data API v3 輪詢方式取得即時訊息
- 頻道名稱自動解析(支援頻道名稱或 @handle)
- Youtube 的聊天訊息有頭像(API 有提供
profileImageUrl) - 自動遵循 API 的
pollingIntervalMillis決定輪詢頻率 - 需要使用 Google API Key(請在
.env設定YOUTUBE_API_KEY)
| 事件 | 類型 | 說明 |
|---|---|---|
| 💬 一般聊天 | textMessageEvent |
即時聊天訊息,可翻譯與過濾 |
| 💰 超級感謝 | superChatEvent |
Super Chat 付費醒目訊息(含金額) |
| 🖼️ 超級貼圖 | superStickerEvent |
Super Sticker 付費貼圖(含金額) |
| 🎉 新會員 | newSponsorEvent |
新的頻道會員加入 |
| 🎁 贈禮會員 | giftMembershipReceivedEvent |
收到贈送的會員資格 |
| ⭐ 會員里程碑 | memberMilestoneChatEvent |
會員達到里程碑 |
注意:免費追隨(訂閱按鈕)不會觸發聊天室事件,僅有付費會員相關事件會出現。
- 支援 Odysee 頻道直播聊天室訊息接收
- 透過 WebSocket 連接 sockety.odysee.tv 取得即時訊息
- 頻道名稱自動解析,不需手動輸入 claim ID
- 目前 Odysee 聊天訊息不支援頭像顯示(因 Odysee 的 WebSocket 資料未提供頭像網址)
新增 Socket 最多重試上限
在env文件裡設置 最大重試次數 SOCKET_RETRY_MAX_COUNT 預設 最多3次
新增了聊天室訊息翻譯功能
如果語言不是中文會自動使用env裡的配置的翻譯API 進行翻譯
暫時還提供設置 控制哪一種語言以外才翻譯
不過你也可以透過更正TranslateTest.js
function isChinese() 的判斷條件 來更正那個你的母語
未來版本 會去補充更正此環節 讓他可由手動配置 目前暫時未處理
npm install在專案目錄下建立 .env,可參考範例:
主要請以 Docs/envExample 裡的為準(或複製 .env.example 修改)
# Twitch 設定
CLIENT_ID=你的Twitch Client ID
CLIENT_SECRET=你的Twitch Client Secret
TWITCH_USER_NAME=你的Twitch頻道名稱
# TikTok 設定
TIKTOK_NAME=你的TikTok用戶名
# ─── 三種 TikTok 認證方式,擇一即可 ───
# [方法A] 完整 Cookie(建議,功能最完整)
# 從已登入 TikTok 的瀏覽器 DevTools 取得
TIKTOK_COOKIES=fblo_xxx=yyy; sessionid=xxx; ...
# [方法B] Session ID(舊版相容)
SESSION_ID=你的TikTok sessionid
TT_TARGET_IDC=你的TikTok Target IDC
# [方法C] EulerStream API Key(不用 Cookie,走原始簽名服務)
# SIGN_API_KEY=你的EulerStream API Key
# 推送設定
BARK_API=https://api.day.app/你的BarkKey
SOCKET_API=http://192.168.0.195:9322
# Kick 設定(OAuth 選填,閱讀公開聊天不需設定)
KICK_CLIENT_ID=你的Kick Client ID
KICK_CLIENT_SECRET=你的Kick Client Secret
KICK_USER_NAME=你的Kick頻道名稱
KICK_CHANNEL_ID=你的Kick頻道ID
# Odysee 設定
ODYSEE_CHANNEL_NAME=你的Odysee頻道名稱
# Youtube 設定
YOUTUBE_API_KEY=你的Google API Key
YOUTUBE_CHANNEL_ID=你的Youtube頻道名稱
# 禮物翻譯設定(可選)
TRANSLATE_API_URL=https://api.mymemory.translated.net/get
TRANSLATE_SOURCE_LANG=en
TRANSLATE_TARGET_LANG=zh-TW
GIFT_TRANSLATE_PREFILL_LIMIT=10👉 如何取得 TIKTOK_COOKIES(方法A):
| 方式 | 步驟 |
|---|---|
| DevTools Cookie 管理 | 在已登入的 TikTok 頁面按 F12 → Application → 左側 Cookies → tiktok.com → 全選所有 Cookie 項目 → 複製 → 貼到 TIKTOK_COOKIES= |
| DevTools Network | 在已登入的 TikTok 頁面按 F12 → Network → 重新整理 → 點任意請求 → 在 Request Headers 找到 Cookie: 整段複製 |
| Console 快速複製 | 在已登入的 TikTok 頁面按 F12 → Console → 輸入 copy(document.cookie) → 直接貼到 TIKTOK_COOKIES= |
⚠️ Cookie 有時效性(約數天~數週),過期後需重新取得。系統啟動時會在 log 顯示設定狀態。
方法B(SESSION_ID) 只需從 Cookie 中找到 sessionid 的值填入即可,相容舊版配置。
方法C(SIGN_API_KEY) 適用于不使用 Puppeteer direct-signer 的情境,需有 EulerStream 服務的 API Key。
禮物翻譯補充:
- 收到禮物時,系統會優先讀取
gift_map.json的對應翻譯。 - 如果禮物名稱尚未建立對應,會先加入
gift_map.json,再嘗試呼叫免費翻譯 API 補上翻譯。 - 啟動時
fetchAvailableGifts()也會同步輸出gift_list.json,並依照GIFT_TRANSLATE_PREFILL_LIMIT預先補一部分未翻譯的禮物名稱。 - 若你想手動修正翻譯結果,直接編輯
gift_map.json即可,之後事件會優先使用你手動設定的內容。
用於 Twitch OAuth,初始內容可為:
可參閱Docs/tokens.json
{
"accessToken": "",
"refreshToken": "",
"scope": [
"bits:read",
"channel:read:goals",
"channel:read:redemptions",
"channel:read:subscriptions",
"chat:read",
"clips:edit",
"moderator:read:followers",
"user:read:chat",
"user:read:subscriptions"
],
"expiresIn": 0,
"obtainmentTimestamp": 0
}啟動 Twitch 模式時,系統會自動檢查 tokens.json 內的 scope 是否包含 user:read:subscriptions(用於檢查聊天室的訂閱者身份)。
若缺少 scope,系統會:
- 印出 Twitch OAuth 授權連結(包含所有必要 scope)
- 引導你在瀏覽器中授權
- 請你貼上授權後瀏覽器導向的完整網址
- 自動交換 authorization code → access token → 更新
tokens.json - 自動更新
authProvider,不需重啟程式
此機制與 Youtube OAuth 的自動刷新類比,確保 token 權限完整。若無需訂閱者檢查功能(G#Ad 指令),可忽略 scope 不足的警告。
Twitch 訂閱者或主播本人可在聊天室輸入 G#Ad 指令投放自訂廣告,廣告會透過 Socket 推送至疊加層顯示。
| 身份 | 能否使用 |
|---|---|
主播本人(chatterId === tuser) |
✅ 自動通過 |
| Twitch 訂閱者 | ✅ 透過 apiClient.subscriptions.checkUserSubscription() 驗證 |
| 非訂閱者 | ❌ 拒絕,僅在 Server 日誌記錄 |
若缺少
user:read:subscriptionsscope,訂閱者檢查會失敗並提示重新授權,但不影響其他功能。
G#Ad <訊息> [tts] [icon=<網址>] [user=<名稱>] [interval=<分鐘>]| 參數 | 必填 | 範例 | 說明 |
|---|---|---|---|
G#Ad |
✅ | G#Ad |
指令前綴 |
<訊息> |
✅ | 歡迎來我的頻道 |
廣告文字,支援 emoji shortcode(如 :heart:)及圖片網址 |
tts |
❌ | tts |
啟用文字轉語音(文字會送至 TTS 引擎) |
icon=<網址> |
❌ | icon=https://example.com/logo.png |
自訂圖示 URL,預設使用 Twitch 頭貼 |
user=<名稱> |
❌ | user=我的商店 |
自訂顯示名稱,預設使用 Twitch 顯示名稱 |
interval=<分鐘> |
❌ | interval=30 |
自動重複間隔,最低 15 分鐘 |
id=<編號> |
❌ | id=2 |
指定要修改第幾則廣告(0-4),不指定則修改第 0 則 |
Note
頭像(icon)解析規則
- 有
icon=<網址>時以該網址為頭像。 - 沒有
icon=時,會自動偵測訊息文字中結尾為圖片副檔名(.png/.jpg/.jpeg/.gif/.webp/.avif/.bmp/.svg)的網址當作頭像,並從廣告文字中移除該網址。例如G#Ad 訂閱主播 哈基米 https://…/Neuro2.png?raw=true→ 頭像為該圖片、文字變為「訂閱主播 哈基米」。 - 兩者都沒有時使用贊助者的 Twitch 頭貼。
- 更新時未指定新的 icon/頭像網址,會保留原本的頭像(不會被覆寫成空白);已解析的頭像會存入廣告紀錄,即時發送、定時器與重啟恢復都會使用同一張圖。
每位贊助者最多可建立 5 則廣告,透過 id=N 參數指定要修改哪一則:
# 建立第一則廣告(自動)
G#Ad 歡迎訂閱! interval=30
# 修改第一則廣告(id=0,等同不指定)
G#Ad 新優惠訊息! id=0
# 新增第二則廣告
G#Ad 快來追蹤! id=1
# 修改第二則廣告
G#Ad 更新優惠! id=1- 不帶
id=時,預設修改第 0 則(第一則) - 若該使用者還沒有任何廣告,則自動新增
- 超過 5 則時無法新增,需先刪除舊廣告
# 最簡單:一次性的廣告
G#Ad 歡迎來我的頻道逛逛!
# 含 TTS
G#Ad 限時優惠中! tts
# 自訂圖示和名稱
G#Ad 今日特價商品 icon=https://i.imgur.com/abc.png user=商店小助手
# 每 30 分鐘自動重複
G#Ad 歡迎訂閱! interval=30
# 混合所有參數
G#Ad 快來參加抽獎! tts icon=https://i.imgur.com/xyz.png user=抽獎活動 interval=60| interval 值 | 結果 |
|---|---|
不設定 或 interval=0 |
單次發送,不重複 |
interval=30 |
每 30 分鐘自動重複發送一次 |
interval=5 |
強制調整為 15 分鐘,並發送 Bark + 疊加層通知告知贊助者 |
interval=15 |
每 15 分鐘重複(最低容許值) |
定時器在程式重啟後不會自動恢復,贊助者需重新發送一次 G#Ad 指令來啟動。
管理頁面 http://localhost:3332/sponsor 提供三種審核模式:
| 模式 | 說明 | 適用情境 |
|---|---|---|
| 不需審核 🟢(預設) | 送出即顯示,立即啟動定時器 | 信任的訂閱者群 |
| 過濾器自動審核 🟡 | 跑 processFilter(),通過過濾器才顯示 |
有廣告帳號騷擾時 |
| 手動審核 🔴 | 標記為「待審核」,需管理員手動通過或拒絕 | 需要完全掌控廣告內容 |
過濾器模式流程:
- 訂閱者發送
G#Ad指令 - 系統跑
processFilter()(比對使用者名稱、訊息內容) - 通過過濾器 → 自動通過,顯示 + 啟動定時器
- 被過濾器阻擋 → 標記為拒絕,發送 Bark 通知給管理員
手動模式流程:
- 訂閱者發送
G#Ad指令 - 標記為「待審核」,發送 Bark 通知(點擊可開啟管理頁面)
- 管理員前往
/sponsor審核 - 通過 → 立即發送 + 啟動定時器;拒絕 → 停止
| 功能 | 說明 |
|---|---|
| 📋 廣告列表 | 依贊助者分組,顯示狀態、間隔、最後發送時間 |
| ➕ 手動新增 | 直接從管理頁面建立贊助廣告(免透過聊天室指令) |
| ✅ 通過 / ❌ 拒絕 | 待審核的廣告可手動通過或拒絕 |
| 啟用或暫停定時重複(僅對有間隔的廣告顯示) | |
| 🗑️ 刪除 | 刪除廣告記錄 |
| ⚙️ 審核模式切換 | 即時切換三種審核模式,立即生效 |
| 事件 | 通知內容 | 附帶 icon |
|---|---|---|
| 廣告建立(不需審核) | 📢 贊助廣告 (使用者名) — [單次/每N分鐘] 訊息 |
優先取 :emoji: shortcode → 訊息內圖片網址 → Twitch 頭貼 |
| 廣告建立(手動審核) | 📋 贊助廣告待審核 (使用者名) — 訊息 + 管理頁面連結 |
同上 |
| 廣告被過濾器阻擋 | 🚫 贊助廣告被阻擋 (使用者名) — 原因 |
同上 |
| 定時器重複發送 | ⏰ 贊助廣告 (使用者名) — 訊息 + 管理頁面連結 |
同上 |
| 間隔低於 15 分調整 | ⚠️ 贊助廣告間隔調整 (使用者名) — 說明 |
同上 |
- 檔案位置:
sponsor_ads.json(專案根目錄) - 儲存內容:贊助者 ID、顯示名稱、廣告內容、審核狀態、間隔設定、時間戳
- 格式:巢狀 JSON(
{ settings, users }),啟動時自動載入 - 遷移相容:若偵測到舊版格式(無
settings/users欄位),自動轉換
# 贊助廣告管理頁面網址(Bark 通知點擊後開啟)
# 若管理頁面不在 localhost,需設為實際 IP
SPONSOR_MANAGE_URL=http://localhost:3332/sponsorTwitch 聊天室中主播本人、訂閱者或追隨者輸入 G#clip 即可為目前直播建立一段 60 秒的剪輯:
# 建立剪輯(預設標題)
G#clip
# 建立剪輯並指定標題
G#clip 這波操作太秀了| 項目 | 說明 |
|---|---|
| 權限 | 主播本人、訂閱者或追隨者(非追隨者會被拒絕) |
| 剪輯長度 | 60 秒(createAfterDelay) |
| 回饋 | 建立成功後推送 🎬 剪輯已建立:https://clips.twitch.tv/<id> 到疊加層與 Bark;失敗則顯示錯誤原因 |
| 指令消耗 | 處理後即 return,G#clip 不會被當成一般聊天訊息轉發或送去翻譯 |
權限檢查順序:主播本人 → 訂閱者(
checkUserSubscription)→ 追隨者(getChannelFollowers,需moderator:read:followersscope,授權範本已含)。任一通過即可建立剪輯。
剪輯內容是指令發送時間點往前的一段直播畫面(Twitch 剪輯機制),成功後由 Twitch 非同步處理,通常幾秒內即可在頻道的 Clips 頁面看到。
每次成功建立剪輯(G#clip 指令或自動剪輯)都會記錄到 clip_history.json(保留最近 200 筆)。開啟 http://localhost:3332/clips(主控台導覽列的「🎬 剪輯」)可查看:
- 歷史剪輯清單:標題、建立時間、來源(手動/自動)、剪輯連結
- 標題自動帶入:未指定標題的剪輯會透過 Twitch API 查詢實際標題(Twitch 自動生成的名稱);若 API 查不到則從剪輯 slug 擷取名稱,不會顯示「未命名」
- ▶ 預覽:在頁面上直接以嵌入播放器觀看剪輯(自動播放,Esc 或點遮罩關閉)
- 複製連結:複製
https://clips.twitch.tv/<id> - 複製 Embed:複製
https://clips.twitch.tv/embed?clip=<id> - 複製 iframe:複製可貼到網站的
<iframe>程式碼(parent已自動帶入目前頁面的主機名) - 刪除單筆:每筆紀錄右側有「刪除」按鈕,可個別移除
- 清空歷史:一鍵清空
clip_history.json - 每 5 秒自動更新清單
關於 Embed 的
parent參數:Twitch 只接受localhost、127.0.0.1或真實網域當 parent,私人 IP(如 192.168.x.x、10.x)無論加不加 port 都會被拒絕。/clips頁面會自動判斷:目前主機是私人 IP 時改用localhost並顯示提示。因此:
- 在本機請用
http://localhost:3332/clips開啟,預覽與複製的 iframe 皆可直接使用- 透過私人 IP(
192.168.0.102:3332)開啟時預覽無法播放(Twitch 限制),頁面會顯示改用 localhost 的提示- 若要嵌到其他網站,需手動把
parent改成該網站的網域,否則 Twitch 會顯示「clips.twitch.tv 拒絕連線」
標題查詢透過 Twitch 公開的 Clip 資料 API(不需額外 scope);查詢失敗時會退回首段 slug 名稱,不影響剪輯建立。
需同時滿足 isTwitch=1 與 .env 的 AUTO_CLIP_ENABLED=1 才啟用。系統每 30 秒評估一次,當觀眾數與聊天訊息速率相對頻道自己的近期平均出現高峰時,自動呼叫 G#clip 相同機制建立剪輯。
score = W_VIEWERS × (觀眾數 / max(基準觀眾, FLOOR_VIEWERS))
+ W_MSG × (訊息速率 / max(基準訊息速率, FLOOR_MSG_PER_MIN))
score ≥ SCORE_THRESHOLD 即觸發(上升緣 + cooldown 防連發)
- 基準(adaptive baseline):取最近
BASELINE_WINDOW_MIN分鐘內每個 poll 樣本(觀眾數、每分鐘訊息數)的中位數。基準是頻道自己的常態,因此低人氣頻道(5 人)跳到 12 人與高人氣頻道相對漲幅一樣會被偵測。 - 多平台總合:觀眾數 = TikTok + Twitch + Kick + Odysee + Youtube 的人數總合;訊息速率 = 各平台聊天訊息(通過過濾器)累計。同時開多平台直播時,任一平台的人氣或聊天熱絡都會被計入。
- 包含
/chat接口:外部 userscript 送進/chat的訊息也會計入 AutoClip 統計(與 WS 直連路徑做對稱去重,不會重複計算)。 - 即時速率:最近
RATE_WINDOW_MIN分鐘的平均每分鐘訊息數。 - Floor:觀眾少於
FLOOR_VIEWERS(預設 2)一律不觸發,防止空台誤發。 - 冷卻:觸發後
COOLDOWN_MIN分鐘內不重複觸發。
| 變數 | 預設 | 說明 |
|---|---|---|
AUTO_CLIP_ENABLED |
0 |
1 啟用(需 isTwitch=1) |
AUTO_CLIP_W_VIEWERS |
0.5 |
觀眾項權重 |
AUTO_CLIP_W_MSG |
0.5 |
訊息項權重 |
AUTO_CLIP_SCORE_THRESHOLD |
1.8 |
觸發門檻(常態約 1.0) |
AUTO_CLIP_BASELINE_WINDOW_MIN |
30 |
基準統計窗口(分鐘) |
AUTO_CLIP_RATE_WINDOW_MIN |
5 |
即時速率窗口(分鐘) |
AUTO_CLIP_WINDOW_MIN |
30 |
訊息時間戳保存窗口(分鐘) |
AUTO_CLIP_FLOOR_VIEWERS |
2 |
最低觀眾數(少於此不觸發) |
AUTO_CLIP_FLOOR_MSG_PER_MIN |
0.3 |
最低訊息速率 |
AUTO_CLIP_COOLDOWN_MIN |
15 |
觸發冷卻(分鐘) |
AUTO_CLIP_TITLE_PREFIX |
空 | 剪輯標題前綴,留空使用直播標題 |
每次評估都會在 console 印出完整狀態:觀眾 / 基準觀眾 / 訊息速率 / 基準速率 / 分數,方便調校門檻。
開啟 http://localhost:3332/autoclip(主控台導覽列「📈 剪輯分析」)可視覺化查看自動剪輯的判斷過程,方便調校門檻:
- 狀態卡:目前觀眾、訊息速率、觸發分數(對照門檻)、已觸發剪輯數、目前狀態原因
- 圖表(Chart.js,每 5 秒自動更新):
- 觀眾數 vs 基準觀眾(時間序列)
- 訊息速率 vs 基準訊息速率
- 觸發分數 vs 門檻線(紅點 = 實際觸發剪輯的時刻)
- 設定資訊:顯示目前權重、最低門檻、冷卻等參數
- 評估歷史執行時存在記憶體(TikTok.js 每 30 秒透過 IPC 推送到 Server.js),僅在 TikTok.js 結束離線時才寫入
autoclip_stats.json(保留最近 2000 筆),可一鍵清空
http://localhost:3332/helphttp://localhost:3332/open?user=你的TikTok名&twitchUser=你的Twitch名&kickUser=你的Kick名&isSocket=1&isTwitch=1&isTK=1&isKick=1&isBark=1參數說明:
| 參數 | 說明 |
|---|---|
| user | TikTok 用戶名稱(給 isTK 使用),若不設使用 .env 的值 |
| twitchUser | Twitch 用戶名稱(給 isTwitch 使用),若不設使用 .env 的值 |
| kickUser | Kick 頻道名稱(給 isKick 使用),若不設使用 .env 的值 |
| odyseeUser | Odysee 頻道名稱(給 isOdysee 使用),若不設使用 .env 的值 |
| youtubeUser | Youtube 頻道名稱或 ID(給 isYoutube 使用),若不設使用 .env 的值 |
| isTK=1 | 啟用 TikTok 直播聊天室 |
| isTwitch=1 | 啟用 Twitch 直播聊天室 |
| isKick=1 | 啟用 Kick 直播聊天室 |
| isOdysee=1 | 啟用 Odysee 直播聊天室 |
| isYoutube=1 | 啟用 Youtube 直播聊天室 |
| platforms=tiktok,twitch,kick,odysee,youtube | 自由組合平台(逗號分隔),例如 twitch,kick、tiktok,youtube |
| isBoth=1 | (已棄用,建議改用 platforms=tiktok,twitch) |
| isBark=1 | 啟用 Bark 推送通知 |
| isSocket=1 | 啟用 Socket 訊息推送 |
Warning
補丁服務器 重複訊息檢查功能 已經直接合併到TikTok.js/Server.js裡 以下參數不再使用
-
isWeb 啟用備用UserScript監聽服務器
-
isDelay 啟用延遲2秒後檢查重複訊息
-
isRepeat 啟用重複訊息檢查
系統使用 TCP Socket (預設 port 9322) 將各平台的訊息統一推送給用戶端。
Socket 不會主動因閒置斷線,連線生命週期完全由 Server 端程式(WebSocket.js / 用戶端)控制:
- 有資料時正常推送
- 無資料時保持連線,不做主動中斷
- Server 端若主動斷線,本系統會在 15 秒後自動重連
- 無需再設定
SOCKET_IDLE_TIMEOUT
當 Socket 因任何原因斷線時,發送中的訊息不會被丟棄,而是進入 pendingQueue(最多暫存 50 筆):
sendSocketMessage()— 聊天、加入、禮物等訊息sendAudienceUpdate()— 人數更新
重新連線成功後,系統會自動依序補發所有暫存訊息,再送出「已連線」通知。
好處:
- 減少無意義的斷線重連循環(之前閒置 2 分鐘斷線 → 15 秒重連 → 又閒置 2 分鐘斷線)
- 降低 CPU 和網路消耗
- 用戶端(如 iOS App)不需頻繁處理斷線重連狀態
- 斷線期間產生的訊息不會遺失,重連後自動補上
範例:
http://localhost:3332/open?user=coffeelatte0709&isTK=1&isBark=1
# 或指定 Twitch:
http://localhost:3332/open?twitchUser=coffeelatte0709&isTwitch=1&isBark=1
# 或指定 Kick:
http://localhost:3332/open?kickUser=你的頻道名&isKick=1&isBark=1http://localhost:3332/close會嘗試優雅關閉子進程,並發送最後一條訊息。
Odysee 聊天室透過 WebSocket 連接 sockety.odysee.tv,不需任何授權即可讀取:
http://localhost:3332/open?odyseeUser=你的頻道名&isOdysee=1&isSocket=1&isBark=1或透過 .env 設定 ODYSEE_CHANNEL_NAME:
http://localhost:3332/open?isOdysee=1&isSocket=1- Odysee 聊天訊息沒有頭像,
img欄位會是空字串 - 頻道名稱會自動解析,不需要手動輸入 claim ID
- 如果頻道未開播,程式會自動退出,不會持續輪詢
http://localhost:3332/open?user=你的TikTok名&twitchUser=你的Twitch名&kickUser=你的Kick名&odyseeUser=你的Odysee名&isTK=1&isTwitch=1&isKick=1&isOdysee=1或使用 platforms:
http://localhost:3332/open?odyseeUser=你的Odysee名&platforms=tiktok,twitch,kick,odysee需要一組 Google API Key 才能使用 Youtube Data API v3:
- 前往 Google Cloud Console
- 建立或選擇一個專案
- 啟用 YouTube Data API v3
- 建立 API Key,建議限制僅供 YouTube Data API 使用
- 在
.env加入:
YOUTUBE_API_KEY=你的API金鑰
YOUTUBE_CHANNEL_ID=你的Youtube頻道名稱或IDhttp://localhost:3332/open?youtubeUser=你的頻道名&isYoutube=1&isSocket=1&isBark=1或透過 .env 設定:
http://localhost:3332/open?isYoutube=1&isSocket=1| 事件 | 類型 | 說明 |
|---|---|---|
| 💬 一般聊天 | ChatMessage |
即時聊天訊息,可翻譯與過濾 |
| 💰 超級感謝 | SuperChat |
付費醒目訊息(含金額) |
| 🖼️ 超級貼圖 | SuperSticker |
付費貼圖(含金額) |
| 🎉 新會員 | NewSponsor |
新頻道會員加入 |
| 🎁 贈禮會員 | GiftMembership |
收到贈送的會員 |
| ⭐ 會員里程碑 | MemberMilestone |
會員達到里程碑 |
- 使用 YouTube Data API v3 輪詢方式,非 WebSocket
- 免費配額每日 10,000 單位,每次輪詢約花 5 單位
- 程式會自動遵循 API 回傳的
pollingIntervalMillis決定輪詢頻率 - 如果頻道未開播,程式會自動退出,不會持續輪詢
- 可透過
.env的YOUTUBE_API_KEY設定 API 金鑰
http://localhost:3332/open?user=你的TikTok名&youtubeUser=你的Youtube名&isTK=1&isYoutube=1&isSocket=1或使用 platforms:
http://localhost:3332/open?youtubeUser=你的Youtube名&platforms=tiktok,youtubeKick 公開聊天室可直接透過 WebSocket 讀取,不需任何授權:
http://localhost:3332/open?kickUser=你的頻道名&isKick=1&isSocket=1&isBark=1| 事件 | 說明 |
|---|---|
ChatMessage |
即時聊天訊息 |
Subscription |
新訂閱 |
GiftedSubscriptions |
贈送訂閱 |
UserBanned |
用戶被封禁 |
UserUnbanned |
用戶解封 |
StreamHost |
主機轉播 |
如需發送訊息或存取私有 API,可設定 Kick OAuth:
- 前往 Kick Dev Portal 註冊應用程式
- 設定 Redirect URI 為
http://localhost:3332/get-kick-token - 在
.env填入KICK_CLIENT_ID與KICK_CLIENT_SECRET - 瀏覽器開啟 Kick 授權 URL(由系統產生),授權完成後自動啟動聊天監聽
Token 會自動儲存至 kick_tokens.json,並在過期時自動刷新。
http://localhost:3332/open?kickUser=你的頻道名&isKick=1&isBark=1&isSocket=1http://localhost:3332/open?user=你的TikTok名&twitchUser=你的Twitch名&kickUser=你的Kick名&odyseeUser=你的Odysee名&youtubeUser=你的Youtube名&isTK=1&isTwitch=1&isKick=1&isOdysee=1&isYoutube=1- Kick 公開聊天不需 OAuth,直接填入頻道名稱即可
- OAuth 僅用於發送訊息等進階功能
kick-wss套件負責底層 WebSocket 連接,自動重連
原版 kick-wss 有兩個問題導致無法正常連接 Kick 聊天室:
getChannelInfo缺少必要 HTTP headers(User-Agent、Referer、Origin),被 Cloudflare 阻擋(403)LEGACY_EVENT_MAPPING將 Pusher 事件名App\Events\ChatMessageEvent錯誤轉換為短名,導致 switch 比對失敗
修正後的完整套件請參閱 Docs/kick-wss.zip,解壓後可取代 node_modules/kick-wss/。
修改內容:
| 檔案 | 修改 |
|---|---|
dist/WebSocketManager.js |
支援 channelId 選項,省略 API 呼叫 |
dist/WebSocketManager.js |
_channelIdExplicit 旗標正確判斷是否跳過 API |
dist/MessageParser.js |
移除 LEGACY_EVENT_MAPPING 錯誤的正規化 |
- 一次性狀態查詢
http://localhost:3332/status- 實時 SSE 狀態
http://localhost:3332/status/stream預設根目錄就是SSE查詢 展示
http://localhost:3332/- 訊息次數統計
http://localhost:3332/keyword- 本地日誌查看
http://localhost:3332/logViewer- 贊助廣告管理
http://localhost:3332/sponsorTip
用於統計重複訊息 以便屏蔽煩人廣告關鍵字用
或者做熱門關鍵字統計用
目前也加了 複製按鈕 方便快速複製添加
可以用此來判斷那些廣告帳號老是刷的關鍵字
以便後續加入封鎖關鍵字 或自動禁言規則裡
Note
統計資料由 Server.js 統一管理,寫入 message_stats.json 時會與現有檔案做 max-merge(只增不減),不會被舊快照覆蓋。
TikTok.js 在運行時其直連收到的訊息統計會同步併入;不在運行時 /chat 進來的訊息仍照常累計。
頁面右上角「🗑️ 清空統計」可一鍵清空記憶體與檔案(含 TikTok.js 統計),清空後從頭累計。
可快速修改 .env 的 BARK_API 與 SOCKET_API:
http://localhost:3332/config表單提交後會立即更新 process.env,下一次 /open 將生效
現在已添加配置頁存取密碼 對應env的CONFIG_KEY進行密碼設置
- 使用者訪問
http://localhost:3332/config - 如果尚未登入,系統會自動導向至
login.html
- 在
login.html輸入密碼並送出 - 後端驗證成功後,會產生一組隨機 Token
- Token 透過 Set-Cookie 寫入瀏覽器 (
authToken) - Token 有效期為 14 天
- 之後訪問
/config時,瀏覽器會自動帶上 Cookie - 後端檢查 Cookie 中的 Token 是否有效:
- 有效 → 顯示
config.html並填入環境變數 - 無效或過期 → 導向回
login.html
- 有效 → 顯示
- 使用者在
config.html點選「登出」按鈕 - 前端呼叫
/logout - 後端回應
Set-Cookie: authToken=; Max-Age=0,清除 Cookie - 使用者被導回
login.html
- 每次登入會生成一組新的 Token
- Token 有效期為 14 天
- 過期後需要重新登入
- 使用者也可以手動點選「登出」來清除 Cookie
所有運行日誌會在瀏覽器根目錄 SSE 頁面即時顯示,也會輸出到控制台
- 修改 .env 後,需要重新 /open 才能讓新設定生效
- 本服務建議保持內網或私人環境使用
- TikTok session 過期需重新抓取
MessageFilter.js 為統一的訊息過濾與統計模組,同時被 TikTok.js 與 Server.js 引用,提供三種過濾動作:
Note
統計統一由 Server.js 管理。Server.js 是 message_stats.json 的唯一寫入者,寫入時與現有檔案做 max-merge(只增不減);TikTok.js 不再寫檔,改為將統計快照回傳給 Server.js 用 mergeStats() 併入。/keyword 頁面的「清空統計」按鈕(POST /keyword/clear)會清空記憶體、檔案並通知 TikTok.js 清空。
{
name: '規則說明', // 用於日誌辨識
field: 'user' | 'message' | 'any', // 檢查對象
action: 'block' | 'replace' | 'delete', // 預設 'block'
// block 模式:回傳 true 表示阻擋
test: (value) => boolean,
// replace / delete 模式:
match: /pattern/g, // 要匹配的 pattern
replacement: '取代文字' // replace 專用,delete 強制為 ''
}| 模式 | 說明 | 使用時機 |
|---|---|---|
block |
完全阻擋該筆訊息 | 廣告帳號、無意義內容 |
replace |
將匹配文字取代為指定內容 | 遮罩髒話、敏感詞 |
delete |
刪除匹配文字,其餘保留 | 移除網址、特定關鍵字 |
| 函數 | 說明 |
|---|---|
addFilterRule(rule) |
新增一條規則 |
addFilterRules(rules) |
批量新增 |
processFilter({ user, message }) |
完整處理,回傳 { user, message, blocked, reason, field, modified } |
checkFilter(input) |
僅檢查是否阻擋(向後相容) |
isFiltered(input) |
checkFilter 的布林捷徑 |
getFilterRules() |
取得當前所有規則 |
clearFilterRules() |
清除所有規則 |
| 函數 | 說明 |
|---|---|
recordMessageStat(message) |
累加一則訊息的出現次數 |
getTopMessages(limit) |
取得出現次數最高的前 N 筆(預設 10) |
getAllMessageStatsSorted() |
取得全部統計,依次數由高到低排序 |
mergeStats(entries) |
以 max-merge 併入外部快照(只增不減,不覆蓋較高計數) |
saveStatsToFile(filePath) |
將統計寫入檔案(預設 ./message_stats.json) |
loadStatsFromFile(filePath) |
從檔案載入統計進記憶體(預設 ./message_stats.json) |
clearStats() |
清空所有統計(記憶體) |
Note
message_stats.json 的實際寫入統一由 Server.js 負責(SaveCacheKeywordDataAll 會先與現有檔案做 max-merge 再寫入);TikTok.js 在退出時僅將最終快照以 { type: "all", data } 回傳給 Server.js 併入。
模組啟動即載入以下預設規則:
User block(廣告帳號):
user:廣告帳號-加LINE/加瀨— 比對加LINE/加瀨/加line等關鍵字user:廣告帳號-特殊組合字— 含 LINE/瀨 + Unicode 組合裝飾字元user:廣告帳號-臺幣/蚪幣— 比對臺⃛幣⃛/蚪⃑.幣⃑模式user:廣告帳號-過長中文比例異常— 特殊字元數量 > 中文字數 2 倍
Message block(無意義訊息):
msg:僅標點符號— 純。,、....等符號msg:僅單一字元— 單一符號如。?!
可在任意檔案(或直接在 MessageFilter.js 底部)加入:
// replace 範例:遮罩髒話
addFilterRule({
name: 'msg:遮罩髒話',
field: 'message',
action: 'replace',
match: /他媽的|操你媽|幹你娘/g,
replacement: '***',
});
// delete 範例:移除網址
addFilterRule({
name: 'msg:刪除網址',
field: 'message',
action: 'delete',
match: /https?:\/\/\S+/g,
});
// block 範例:阻擋全數字訊息
addFilterRule({
name: 'msg:全數字',
field: 'message',
action: 'block',
test: (m) => /^\d{6,}$/.test(m),
});收到訊息(user, message)
↓
processFilter({ user, message })
↓
┌────┴────┐
│ blocked │ ← true → ❌ 阻擋,不發送
└────┬────┘
│ false
↓
┌─────┴─────┐
│ modified │ ← true → 使用 fr.user / fr.message 取代原值
└─────┬─────┘
│ false → 保持原值
↓
記錄統計 → 發送 Bark → 發送 Socket
補釘服務器 是專門用來接收 UserScript 所轉發的直播頁面訊息。
當你透過 Restream / Streamlabs 等工具推流到 TikTok 時
在對應的管理後台中會出現一個 TikTok Live Monitor 入口 之類的。
點擊後會開啟官方的直播監聽頁面, 最終頁面實際運行於:
在這個頁面中,你可以查看:
- 觀眾數
- 直播時長
- 禮物資訊
- 聊天室訊息(最重要)
- 直接監聽 DOM 內聊天室訊息的新增
- 即時抓取頁面上實際渲染出的聊天內容
- 將訊息轉送至補釘服務器(WebSocket.js)
- 再由補釘服務器分發給你的本地應用或推流系統
這種方式從根本上解決了:
第三方 TikTok Live API / Library 可能漏訊息的問題
- 你抓的是「官方頁面實際顯示的內容」
- 只要頁面能看到,腳本就一定能抓到
- 不依賴非官方 WebSocket 協議
- 不會因為封包解析錯誤而漏訊
這是「基於官方直播頁面實際渲染結果」的資料來源 準確度最高,幾乎不會遺漏。
TikTok Live 推流
↓
livecenter.tiktok.com(官方頁面)
↓
UserScript 監聽 DOM 變化
↓
WebSocket.js 補釘服務器
↓
你的本地應用 / PiP 聊天室 / 直播系統目前 UserScript 僅處理聊天室訊息(Chat Messages)
以下事件尚未納入處理範圍:
- 送禮事件(Gift)
- 使用者加入直播間(Join)
- 其他系統事件
現階段,UserScript 的角色是:
作為輔助訊息來源(Fallback / Patch Layer)
主要用來彌補第三方 TikTok Live API 在實際使用中 偶爾出現聊天室訊息遺漏 的問題。
運作方式為:
- 第三方 TikTok Live API → 作為主要資料來源
- UserScript(監聽官方頁面 DOM) → 作為補強與校正來源
後續可考慮:
- 將送禮、加入等事件一併納入監聽
- 逐步完整遷移至「頁面監聽方案」
- 最終降低甚至完全移除對第三方 TikTok Live API 的依賴
git update-index --assume-unchanged <file>git update-index --no-assume-unchanged <file>這個資料夾是之前做的一些小工具
Time.html 是很早以前我用在OBS瀏覽器來源 用來顯示當前時間的附加件
NetFix.py 則是平時用來FFMPEG重新編碼壓縮用 的小工具
live_engine 用於給Window的聊天疊加層
Gift.html TaiwndCSS 商品卡排版設計嘗試
Mask.py 一個讓你用來擋不想讓觀眾看到的東西 黑框框可視化視頻編輯器
依賴安裝
pip install PyQt6 PyOpenGL numpy pillow requests疊加層配置 請從live_engine/config.py 處理
寬高配置在這裡設置
運行請先進入 live_engine目錄下 在運行 main.py
運行會在本地部署一個Socket Server PORT跟ReplyKIT項目是一樣的 在PORT 9322
有一些訊息為了方便調試 確認參數 所以特別寫進 Main_Log.log TikTokRun.log


