Appearance
聊天室
最後更新:2026-04-09
功能說明
聊天室是直播間內的即時互動系統,嵌入在 StreamerViewController(主播直播)和 STEventDetailViewController(賽事詳情)中。支援文字聊天、圖片、表情、禮物、紅包、競猜投票、主播推單、曬單分享、自動推訊息等功能。透過 WebSocket 連線實現即時通訊。
使用者流程
進入聊天室
- 進入直播間或賽事詳情頁 → 自動初始化
STChatManager STChatManager建立 WebSocket 連線,取得聊天室 Token 和設定- 發送
L01(subscribeHistory)訂閱歷史訊息 - 收到
L03(history)載入歷史訊息到STChatView - 後續透過
M01(instant)接收即時訊息
發送訊息
- 點擊輸入框 → 顯示
STChatInputView - 輸入文字 → 發送
- 發送前依序檢查:是否登入 → 是否被禁言 → VIP 等級 → 充值金額 → 投注金額 → 發送頻率 → 訪客次數
- 檢查通過 → WebSocket 發送
送禮物
- 點擊禮物按鈕 →
STGiftListView - 選擇後透過 HTTP API
SendGiftRequest發送 - 伺服器推送
G01(gift)到聊天室 →GiftContainerView播放動畫
搶紅包
- Socket 推送
G02(activity, type=redEnvelope)→ 顯示紅包小球 - 點擊 → 呼叫
ReceiveRedPacketRequestAPI 領取
競猜投票
- Socket 推送
G02(activity, type=answerLottery)→ 顯示ChatVoteViewController - 選擇投票 → 呼叫
SubmitVoteInfoRequestAPI - 等待結果(
G07winningResult 推送)
頁面跳轉
- 進入直播間 → 自動嵌入聊天室(無需使用者操作)
- 點擊曬單訊息中的賽事連結 → 賽事詳情頁
技術視角(開發看這裡)
相關檔案
| 類型 | 檔案路徑 |
|---|---|
| Manager(聊天核心) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/STChatManager.swift |
| ViewModel | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/VM/STChatViewModel.swift |
| API | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/STChatAPI.swift |
| Model(訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/M/STChatMessageModel.swift |
| View(聊天主視圖) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/V/STChatView.swift |
| View(Header) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/V/STChatHeaderView.swift |
| View(跑馬燈) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/V/STChatMarqueeView.swift |
| Cell(文字訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STTextChatMessageTableViewCell.swift |
| Cell(圖片訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STImageChatMessageTableViewCell.swift |
| Cell(曬單訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STChatOrderShareTableViewCell.swift |
| View(禮物列表) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/STGiftListView.swift |
| ViewController(禮物列表) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/STGiftListViewController.swift |
| ViewModel(禮物列表) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/VM/STGiftListViewModel.swift |
| View(禮物動畫容器) | /Users/user/Work/bbsport-new/BBSport/Tab/体育/Sport/EventDetail/Views/ChatView/Gift/Animation/GiftContainerView.swift |
| Socket 底層 | /Users/user/Work/bbsport-new/BBSport/Tools/UtilityToolComponentOC/Classes/Helper/Socket/SocketServices.m |
| Socket 管理(單例) | /Users/user/Work/bbsport-new/BBSport/Tools/UtilityToolComponentOC/Classes/Helper/Socket/BBSocketServices.m |
| Cell(自動訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STAutoMessageTableViewCell.swift |
| Cell(訊息基底) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STChatMessageBaseTableViewCell.swift |
| Cell(系統訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STChatSystemMessageTableViewCell.swift |
| Cell(禮物訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STGiftMessageTableViewCell.swift |
| Cell(競猜訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STLiveBetGameMessageTableViewCell.swift |
| Cell(回覆訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STReplyChatMessageTableViewCell.swift |
| Cell(用戶稱號圖片) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/Cell/STUserTitleImageCell.swift |
| View(更多訊息) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/V/STChatMoreMessageView.swift |
| View(引用回覆) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/V/STChatQuoteView.swift |
| View(跑馬燈 Label) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/STChatView/V/STMarqueeLabel.swift |
| View(聊天輸入框) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/ChatInputView/STChatInputView.swift |
| View(表情選擇) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/ChatInputView/STEmojiView.swift |
| ViewController(禮物輸入) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/STGiftInputViewController.swift |
| Layout(禮物分頁) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/STPageCollectionViewFlowLayout.swift |
| View(禮物排行) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/GiftRank/STGiftRankView.swift |
| View(禮物排行前三) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/GiftRank/V/STGiftRankTopThreeView.swift |
| View(排行視圖) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/GiftRank/V/STRankView.swift |
| ViewModel(禮物排行) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/GiftRank/VM/GiftRankViewModel.swift |
| Model(禮物排行 Cell) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/Gift/M/STGiftRankTableCellModel.swift |
| Cell(競猜遊戲) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/LiveBetGame/Cell/LiveBetGameCell.swift |
| Cell(骰寶競猜) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/LiveBetGame/Cell/LiveBetGameSicBoCell.swift |
| View(競猜遊戲) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/LiveBetGame/LiveBetGameView.swift |
| ViewModel(競猜遊戲) | /Users/user/Work/bbsport-new/BBSport/Tab/广场/FullScreenLive/LiveBetGame/LiveBetGameViewModel.swift |
| Model(直播投注遊戲) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/Model/LiveBetGame/LiveBetGameModel.swift |
| API(活動中獎結果) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+ActivityWinResultRequest.swift |
| API(活動中獎名單) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+ActivityWinUserListRequest.swift |
| API(取消訂閱直播) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+CancelLiveSubscribeRequest.swift |
| API(舉報類別列表) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+ChatReportListRequest.swift |
| API(娛樂直播) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+EntertainmentLiveRequest.swift |
| API(自動推訊息) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+FetchChatAutoMessageRequest.swift |
| API(支持率資訊) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetApprovalInfoRequest.swift |
| API(聊天 Token) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetChatTokenRequest.swift |
| API(直播活動) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetLiveActivityRequest.swift |
| API(直播投注遊戲) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetLiveBetGameRequest.swift |
| API(賽事直播狀態) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetMatchIDLiveStatusRequest.swift |
| API(推單支持率列表) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetPushOrderApprovalInfoRequest.swift |
| API(禮物排行榜) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetTopRankGiftListRequest.swift |
| API(投票資訊) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+GetVoteInfoRequest.swift |
| API(首頁聊天紀錄) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+HomeChatListRequest.swift |
| API(大額曬單配置) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LargeOrderShareConfigRequest.swift |
| API(加入活動) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LiveActivityJoinRequest.swift |
| API(表情列表) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LiveEmojiListRequest.swift |
| API(禮物列表) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LiveGiftListRequest.swift |
| API(主播資訊) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LiveHostInfoRequest.swift |
| API(直播訂閱資訊) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LiveSubcribeInfoRequest.swift |
| API(訂閱直播) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LiveSubscribeRequest.swift |
| API(抽獎資訊) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+LuckyDrawStartInfoRequest.swift |
| API(紅包雨狀態) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+RainRedPacketRequest.swift |
| API(領取紅包雨) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+ReceiveRainRedPacketRequest.swift |
| API(領取紅包) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+ReceiveRedPacketRequest.swift |
| API(紅包狀態) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+RedPacketRequest.swift |
| API(曬單上報) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+ReportOrderShareRequest.swift |
| API(送出舉報) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+SendChatReportRequest.swift |
| API(送禮物) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+SendGiftRequest.swift |
| API(提交支持率) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+SubmitApprovalInfoRequest.swift |
| API(提交投票) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+SubmitVoteInfoRequest.swift |
| API(轉盤遊戲資訊) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+TurnTableGameInfoRequest.swift |
| API(上報觀看人) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+UploadSTWatchManRequest.swift |
| API(進入動畫) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+UserEnterAnimationRequest.swift |
| API(影片 URL) | /Users/user/Work/bbsport-new/BBSport/API/STAPI/Chat/STAPI+VideoUrlRequest.swift |
API Endpoints
| 功能說明 | Request 名稱 | Endpoint | Method | 主要參數 |
|---|---|---|---|---|
| 取得聊天 Token | GetChatTokenRequest | api/forehead/user/chat/token/v2 | GET | liveHostType |
| 禮物列表 | LiveGiftListRequest | api/forehead/live/load/gift | POST | matchType |
| 表情列表 | LiveEmojiListRequest | api/forehead/live/emoji/list | POST | matchType |
| 自動推訊息 | FetchChatAutoMessageRequest | api/forehead/live/match/message/select | POST | matchId, platformId, hostId, messageType |
| 發送禮物 | SendGiftRequest | api/forehead/live/submit/gift/present | POST | liveRoomId, giftId, giftCount |
| 禮物排行榜 | GetTopRankGiftListRequest | api/forehead/live/room/gift/top5 | POST | liveRoomId |
| 紅包狀態 | RedPacketRequest | api/forehead/live/get/room/redEnvelope | POST | LiveRoomId |
| 紅包雨狀態 | RainRedPacketRequest | api/forehead/live/get/room/redEnvelope/template | POST | liveRoomId, sendRecordId |
| 領取紅包 | ReceiveRedPacketRequest | api/forehead/live/submit/receive/redEnvelope | POST | LiveRoomId, sendRecordId |
| 領取紅包雨 | ReceiveRainRedPacketRequest | api/forehead/live/submit/receive/redEnvelope/template | POST | liveRoomId, sendRecordId, catchTimes |
| 舉報類別列表 | ChatReportListRequest | api/forehead/live/chat/report/cate | POST | 無 |
| 送出舉報 | SendChatReportRequest | api/forehead/live/chat/report/add | POST | account, cate, game, targetUid 等 |
| 活動列表 | GetLiveActivityRequest | api/forehead/live/activity/select 或 api/forehead/live/match/activity/select | POST | liveRoomId |
| 加入活動 | LiveActivityJoinRequest | api/forehead/live/activity/join/record/add | POST | roomId, activityId, chatRoomId |
| 投票資訊 | GetVoteInfoRequest | api/forehead/live/lottery/vote/info | POST | roomId |
| 提交投票 | SubmitVoteInfoRequest | api/forehead/live/lottery/vote/submitInfo | POST | voteResult, recordId, roomId |
| 支持率資訊 | GetApprovalInfoRequest | api/forehead/live/lottery/approval/info | POST | roomId, matchId |
| 推單支持率列表 | GetPushOrderApprovalInfoRequest | api/forehead/live/lottery/approval/info/list | POST | roomId |
| 提交支持率 | SubmitApprovalInfoRequest | api/forehead/live/lottery/approval/submitInfo | POST | approvalResult, roomId, matchId, orderId |
| 抽獎資訊 | LuckyDrawStartInfoRequest | api/forehead/live/lottery/startInfo | POST | matchId, hostId |
| 抽獎結果 | ActivityWinResultRequest | api/forehead/live/lottery/win | POST | lotteryId, userId, lotteryType |
| 中獎名單 | ActivityWinUserListRequest | api/forehead/live/lottery/winList | POST | lotteryId, lotteryType, page, size |
| 主播轉盤資訊 | TurnTableGameInfoRequest | api/forehead/live/lottery/big/spin/info | POST | matchId, hostId |
| 大額曬單配置 | LargeOrderShareConfigRequest | api/forehead/live/showorder/config | POST | 無 |
| 曬單上報 | ReportOrderShareRequest | api/forehead/live/showorder/report | POST | ReportOrderShareRequestModel |
| 上報觀看人 | UploadSTWatchManRequest | api/forehead/live/submit/watch | POST | liveRoomId, matchId |
| 進入動畫 | UserEnterAnimationRequest | api/forehead/live/enter/animation | POST | chId |
| 直播訂閱資訊 | LiveSubcribeInfoRequest | api/forehead/live/user/subscribe/info | POST | 無 |
| 首頁聊天紀錄 | HomeChatListRequest | msg(host=chat) | GET | chId |
| 聊天室骰寶 | GetLiveBetGameRequest | api/forehead/live-bet-game/rounds/current | POST | liveRoomId |
資料模型
STChatMessageModel(聊天訊息)完整欄位
| 欄位 | 型別 | 說明 |
|---|---|---|
cmd | String | 指令代碼(對應 STChatMessageCommand) |
account | String | 使用者帳號 |
msg | String | 訊息內容(已處理過) |
originMsg | String | 原始訊息內容 |
nick | String | 暱稱(含冒號後綴 ": ") |
nickOrigin | String | 原始暱稱 |
role | Int | 角色(0=一般, 其他=特殊身份) |
time | Int | 時間戳(自動推訊息用來設定推送間隔,單位:分鐘) |
uid | String | 使用者 ID("0" = 系統訊息, "-1" = 自動訊息) |
content | String | 組合後的顯示內容 |
vip | Int | VIP 等級 |
msgType | Int | 訊息類型(對應 STChatMessageType) |
titleId | Int | 稱號 ID(從 ext 解析) |
photoTitle | String | 自訂表情名稱(從 ext 解析) |
chId | String | 聊天室頻道 ID |
giftPhotoId | String | 禮物圖片 ID |
fullScreenPhotoId | String | 全螢幕禮物動效 ID |
giftName | String | 禮物名稱 |
giftNumber | Int | 禮物數量 |
userIcon | String | 使用者頭像 |
giftId | Int | 禮物 ID |
giftDuration | Float | 禮物顯示時長 |
giftLevel | Int | 禮物級別(0=平庸, 1=普通, 2=全螢幕炫酷) |
orderInfo | STChatOrderInfo | 曬單資訊(僅曬單訊息有值) |
replyInfo | STChatReplyInfo | 回覆資訊(僅回覆訊息有值) |
STChatMessageType 訊息類型
| 值 | 名稱 | 說明 |
|---|---|---|
0x01 | text | 文字訊息 |
0x02 | image | 圖片/自訂表情 |
0x03 | richText | 富文本 |
0x04 | reply | 回覆訊息 |
0x05 | orderShare | 曬單訊息 |
0x07 | autoMessage | 平台自動推送訊息 |
999 | none | 無類型 |
STChatOrderInfo(曬單資訊)
| 欄位 | 型別 | 說明 |
|---|---|---|
betTitle | String | 投注標題 |
betSection | String | 投注選項 |
odd | String | 賠率 |
betAmount | String | 投注金額 |
wonAmount | String | 中獎金額 |
orderStatus | OrderShareStatus | 訂單狀態 |
betId | String | 投注 ID |
orderId | String | 訂單 ID |
isEarlySettlement | Bool | 是否提前結算 |
orderCanBeFollowed | Bool | 是否可跟單 |
msg | String | 訊息 |
orderImage | String | 訂單圖片 |
photoTitle | String | 圖片標題 |
nick | String | 暱稱 |
uid | String | 使用者 ID |
orderHint | String | 訂單提示 |
titleId | Int | 稱號 ID |
isFold | Bool | 是否折疊(預設 true) |
bets | [STChatBet] | 投注明細(串關用) |
STGiftModel(禮物)
| 欄位 | 型別 | 說明 |
|---|---|---|
id | Int | 禮物 ID |
name | String | 禮物名稱 |
photoId | String | 禮物圖片 ID |
fullScreenPhotoId | String | 全螢幕動效 ID |
amountDouble | Float | 禮物價格 |
giftNumber | Int | 禮物數量 |
giftDuration | Float | 顯示時長 |
giftLevel | Int | 級別(0=平庸, 1=普通, 2=全螢幕炫酷) |
type | Int | 類型(0=現金, 1=金幣) |
goldAmount | Int | 金幣額度 |
landscape | Bool | 是否橫屏 |
STChatManager 初始化參數
| 參數 | 說明 |
|---|---|
eventId | 賽事 ID |
hostId | 主播 ID(預設 0) |
liveRoomId | 直播房間 ID(預設 0) |
chatRoomType | .sport 或 .entertainment |
gameType | 遊戲類型(影響 chId prefix) |
liveHostType | 主播類型(傳給 Token API) |
WebSocket 連線管理
連線架構
STChatManager (Swift)
└── BBSocketServices (ObjC, 單例)
└── SocketServices (ObjC, 一個 URL 一個實例)
└── SRWebSocket (SocketRocket)BBSocketServices是單例,內部用NSMutableDictionary socketMap以 URL 為 key 管理多個SocketServices實例- 每個
SocketServices封裝一個SRWebSocket連線
連線建立流程
STChatManager.connectToSocket()被呼叫- 檢查
eventId != "0"且connectStatus != .connected - 呼叫
fetchChatData()取得公告、自動訊息、禮物、表情、紅包、抽獎等資料 - 呼叫
fetchChatToken()取得聊天 Token(api/forehead/user/chat/token/v2) - Token 取得成功後,呼叫
BBSocketServices.sharedInstance().openSocket(withUrlStr:)建立 WebSocket - WebSocket 連線成功 →
connectStatus = .connected→ 發送L01訂閱歷史訊息 → 呼叫enterAnimation()→ 啟動心跳
斷線重連策略
| 層級 | 機制 | 間隔 |
|---|---|---|
SocketServices(底層) | fl_reconnect → 非手動斷線時自動重連 | 5 秒後重試 |
STChatManager(上層) | reconnect() → 連線失敗時重新走完整連線流程 | 10 秒後重試 |
| Token 取得 | fetchChatToken 內建重試 | 最多 3 次重試 |
- 底層
SocketServices在didFailWithError和didCloseWithCode時,若非手動斷線(isCloseByUser = NO),5 秒後重連 - 上層
STChatManager在failConnected回調中先關閉 socket 再 10 秒後重新connectToSocket() - 重連成功後(
reConnected),發送L01(isFirst = "0")重新訂閱歷史訊息
心跳機制
- Ping 內容:
{"cmd": "ping"}(以 NSData 發送) - 間隔:每 30 秒發送一次
- 首次在連線成功後啟動,
heartbeatflag 確保不重複啟動 - 實作位置:
STChatManager.sendPing()→BBSocketServices.sendPingWithurlStr:→SocketServices.sendping:
Token 取得方式
- API:
GET api/forehead/user/chat/token/v2,帶liveHostType參數 - 回傳
STChatInfoModel,包含:token:聊天室 TokenchatBanInfo:禁言資訊(是否被禁言)chatRule:發言規則(VIP 限制、充值限制、投注限制)specialChIds:特殊頻道 ID(不受發言限制)
- Token 過期時伺服器推送
E04指令,Client 收到後重新呼叫fetchChatToken()
聊天室切換
changeChannel()方法:清空訊息 → 更新 eventId/hostId/liveRoomId → 若 socket 已連線則直接發送L01重新訂閱(免斷線重連)- 若 socket 未連線則走完整
connectToSocket()流程
WebSocket 指令與 Payload
完整指令列表
| Command | 方向 | 說明 |
|---|---|---|
L01 | 發送 | 訂閱歷史訊息 |
L02 | (廢棄) | 點讚數 |
L03 | 接收 | 收到歷史訊息 |
A01 | (廢棄) | 點讚數變化 |
B01 | (廢棄) | 接受系統廣播訊息 |
M01 | 發送/接收 | 即時訊息 |
R03 | 接收 | 使用者被禁言 |
R04 | 接收 | 使用者充值/VIP 不足 |
R05 | 接收 | 發送頻率太高 |
R06 | 接收 | 曬單次數達到上限 |
R09 | 接收 | 訪客發言次數達上限 |
G01 | 接收 | 禮物訊息 |
G02 | 接收 | 活動通知(紅包/紅包雨/投票) |
G04 | 接收 | 主播推單 |
G05 | 接收 | 主播撤單 |
G06 | 接收 | 抽獎訊息 |
G07 | 接收 | 活動中獎結果 |
P02 | 接收 | 使用者進入聊天室(歡迎動畫) |
P001 | 接收 | 主播聊天室自動推訊息 |
P003 | 接收 | 主播競猜(骰寶) |
E04 | 接收 | Token 過期 |
ping | 發送 | 心跳 |
L01 訂閱歷史訊息(發送)
json
{
"cmd": "L01",
"data": {
"chId": "fb12345|67",
"isFirst": "1",
"guestTeamId": "fb12345|67guest",
"homeTeamId": "fb12345|67home",
"nick": "使用者暱稱",
"uid": "123456",
"vip": 3,
"token": "chat-token-string",
"version": "1.0.0",
"platform": 1
}
}chId格式:{gameType.chatIdPrefix}{eventId}或{gameType.chatIdPrefix}{eventId}|{hostId}(有主播時)isFirst:首次連線"1",重連"0"platform:1=iOS, 2=Android, 3=Web
L03 歷史訊息(接收)
json
{
"cmd": "L03",
"data": {
"msg": [
{
"cmd": "L03",
"nick": "使用者暱稱",
"msg": "訊息內容",
"uid": "123456",
"vip": 3,
"role": 0,
"msgType": 1,
"chId": "fb12345|67",
"ext": { "titleId": "1", "photoTitle": "" }
}
]
}
}M01 即時訊息(發送 - 文字)
json
{
"cmd": "M01",
"data": {
"uid": "123456",
"account": "user_account",
"nick": "使用者暱稱",
"chId": "fb12345|67",
"token": "chat-token-string",
"vip": 3,
"ext": { "titleId": "1" },
"msg": "你好",
"msgType": 1
}
}M01 即時訊息(發送 - 自訂表情)
json
{
"cmd": "M01",
"data": {
"uid": "123456",
"account": "user_account",
"nick": "使用者暱稱",
"chId": "fb12345|67",
"token": "chat-token-string",
"vip": 3,
"ext": { "titleId": "1", "photoTitle": "表情名稱" },
"msg": "image-id-string",
"msgType": 2
}
}M01 即時訊息(發送 - 回覆)
json
{
"cmd": "M01",
"data": {
"uid": "123456",
"account": "user_account",
"nick": "使用者暱稱",
"chId": "fb12345|67",
"token": "chat-token-string",
"vip": 3,
"msg": "回覆內容",
"msgType": 4,
"ext": {
"titleId": "1",
"nick": "被回覆者暱稱",
"msg": "被回覆的訊息",
"msgType": 1,
"uid": "654321",
"betTitle": "",
"betSection": "",
"odd": "",
"bets": []
}
}
}M01 即時訊息(發送 - 曬單)
json
{
"cmd": "M01",
"data": {
"uid": "123456",
"account": "user_account",
"nick": "使用者暱稱",
"chId": "fb12345|67",
"token": "chat-token-string",
"vip": 3,
"msg": " 晒了一单",
"msgType": 5,
"ext": {
"titleId": "1",
"msgType": 5,
"betTitle": "投注標題",
"betSection": "投注選項",
"odd": "1.85",
"betAmount": "100",
"wonAmount": "185",
"orderStatus": 1,
"betId": "bet123",
"orderId": "order456",
"isEarlySettlement": false,
"orderCanBeFollowed": true,
"orderImage": "",
"photoTitle": "",
"orderHint": "",
"titleId": 1,
"bets": []
}
}
}R04 充值/VIP 不足(接收)
json
{
"cmd": "R04",
"data": {
"recharge_limit": 100,
"vip_limit": 3
}
}G02 活動通知(接收)
根據 notifyType 區分三種類型:
紅包(redEnvelope):
json
{
"cmd": "G02",
"data": {
"notifyType": "redEnvelope",
"sendRecordId": 12345
}
}紅包雨(redRain):
json
{
"cmd": "G02",
"data": {
"notifyType": "redRain",
"sendRecordId": 12345,
"times": 5,
"endBettingTime": 1680000000,
"abortStatus": 0
}
}投票/答題(answerLottery):
json
{
"cmd": "G02",
"data": {
"notifyType": "answerLottery",
"abortStatus": 0,
"closeTime": 1680000000,
"status": 1
}
}G04 主播推單(接收)
json
{
"cmd": "G04",
"data": {
"newData": [
{
"id": "order-id",
...
}
]
}
}G05 主播撤單(接收)
json
{
"cmd": "G05",
"data": {
"id": "order-id-to-delete"
}
}G06 抽獎訊息(接收)
json
{
"cmd": "G06",
"data": {
"lotteryType": 1,
"abortStatus": 0
}
}abortStatus:0=開始,非 0=中止lotteryType:.turnTable走主播轉盤路線,其他走普通抽獎
G07 中獎結果(接收)
json
{
"cmd": "G07",
"data": {
"lotteryId": 123,
"lotteryType": 1
}
}lotteryType:.turnTable走轉盤結果顯示;.answer重新拉投票資料;其他移除抽獎入口- 收到後會呼叫
fetchActivityWinResult()查詢使用者是否中獎
P001 主播自動推訊息(接收)
json
{
"cmd": "P001",
"data": {
"matchId": "12345",
"nickname": "系統",
"message": "自動推送的訊息內容",
"sendTime": "5"
}
}P02 使用者進入(接收)
json
{
"cmd": "P02",
"data": {
"chId": "fb12345|67",
...
}
}E04 Token 過期(接收)
json
{
"cmd": "E04",
"data": {}
}收到後自動重新呼叫 fetchChatToken() 取得新 Token。
發言限制
發言檢查分為兩層:本地預檢(進入聊天室時)和伺服器即時回報(發送訊息後)。
本地預檢(checkChatConditions)
進入聊天室後依照 Token API 回傳的規則檢查,順序為:
- 禁言:
chatBanInfo.isBan == true→ 提示「您已被禁言,有疑问可联系客服」 - 特殊頻道豁免:
specialChIds包含當前 chId → 跳過後續檢查 - 充值限制:
chatRule.rechargeLimitRule.rechargeLimitIsAccord == false→ 提示「充值大于 X 元才能发言」 - VIP 限制:
chatRule.vipLimitIsAccord == false→ 提示「VIP X 以上会员才能发言」
具體的 VIP 等級門檻(
vipLimitVal)和充值金額門檻(rechargeLimitVal)均由後台配置,透過 Token API 下發。
伺服器即時回報
| Command | 說明 | 提示訊息 |
|---|---|---|
R03 | 被禁言(舉報封鎖) | 「您已被禁言,有疑问可联系客服」 |
R04 | 充值/VIP 不足 | 「充值大于 X 元才能发言」或「VIP X 以上会员才能发言」 |
R05 | 發送頻率太快 | 「您的發送頻率太快了」(顯示為系統訊息) |
R06 | 曬單次數到限 | 「您的晒单次数达到限制」(Toast) |
R09 | 訪客發言次數到限 | 由伺服器推送 |
規則 Model 結構
STChatRuleModel
├── isAvailable: Bool // 是否符合所有規則
├── vipLimitVal: Int // VIP 等級門檻(後台配置)
├── vipCurrentVal: Int // 使用者目前 VIP 等級
├── vipLimitIsAccord: Bool // VIP 是否達標
├── rechargeLimitRule
│ ├── rechargeLimitVal: Int // 充值金額門檻(後台配置)
│ ├── rechargeCurrentVal: Int // 使用者目前充值金額
│ ├── rechargeLimitIsAccord: Bool // 充值是否達標
│ └── coin: String // 幣種
└── betLimitRule
├── limitVal: Int // 投注金額門檻(後台配置)
├── currentVal: Int // 使用者目前投注金額
├── limitIsAccord: Bool // 投注是否達標
├── cond: Int // 條件(0=未設置, 1=並且, 2=或是)
└── coin: String // 幣種實作重點
- WebSocket 連線管理:
STChatManager維護連線狀態(connecting/connected/noConnection),支援斷線重連 - 訊息上限:
kMaxMessageCount = 500,超過時移除最舊的訊息 - 發言限制層級:禁言 > 充值金額 > VIP 等級(本地預檢);伺服器額外檢查頻率限制、訪客次數、曬單次數
- 自動推訊息機制:體育聊天室依
time欄位(分鐘)設定定時推送間隔,使用STTimerTaskQueue管理;娛樂主播首次走 API,後續走 SocketP001 - 聊天室切換:
changeChannel()回調didConnectToNewChat,切換賽事/主播時若 socket 已連線則無需斷線重連 - Chat API Version:
kChatApiVersion = "1.0.0" - 支持率定時刷新:
approvalTimer每 5 秒呼叫fetchApproval()更新支持率資料 - 禮物發送走 HTTP 非 WebSocket:
SendGiftRequest是 POST API,成功 code 為 1 或 429;code 300000 代表餘額不足,data 回傳不足額(Double)
API 呼叫流程
進入聊天室 (WebSocket 連線)
STChatManager.connectToSocket()
├─ [API 1] GET api/forehead/user/chat/token/v2 {liveHostType}
│ → chatToken + chatBanInfo + chatRule (重試 3 次)
│
├─ 建立 WebSocket 連線
│ └─ BBSocketServices.openSocket(url: getChatSocketUrl2())
│
├─ 連線成功 → 訂閱歷史訊息
│ └─ [WS 送出] cmd:L01
│ {chId, isFirst:"1", guestTeamId, homeTeamId, nick, uid, vip, token}
│ → [WS 接收] cmd:L03 → 歷史訊息陣列
│
├─ [API 2] POST api/forehead/live/enter/animation {chId} → 進場動畫
│
└─ 啟動心跳: 每 30 秒 pingchId 格式
getChId():
├─ 有 hostId: gameType.chatIdPrefix + eventId + "|" + hostId
│ 例: "100100001|15234"
└─ 無 hostId: gameType.chatIdPrefix + eventId
例: "34100001"發送訊息
用戶輸入文字 → STChatManager.sendTextMessage()
└─ [WS 送出] cmd:M01
{chId, uid, nick, vip, token, msg:"文字", msgType:1, ext:{titleId}}
回覆訊息 → msgType:4, ext 含原訊息資訊
自定義表情 → msgType:2, msg=imageId, ext.photoTitle=imageName
注單分享 → msgType:5, ext 含下注詳情WebSocket 接收訊息類型
├─ L03: 歷史訊息
├─ M01: 即時文字訊息
├─ G01: 禮物通知
├─ G02: 活動事件 (投票/紅包/紅包雨)
├─ G04: 主播推單
├─ G05: 主播撤單
├─ G06: 抽獎開始
├─ G07: 中獎結果
├─ P001: 主播自動推送
├─ P002: 用戶進場
├─ P003: 骰寶遊戲
├─ R03: 用戶禁言
├─ R04: 充值不足
├─ R05: 發送頻率過高
├─ R09: 遊客發言上限
├─ E04: Token 過期 → 重新取得 token斷線重連
連線失敗 / 異常斷開
└─ 等待 5-10 秒 → 重新建立 WebSocket
→ reConnectedBlock → subscribeHistoryMessage(first: false)