# API Gateway 更新說明
* UpdateStremer 的 API 重新命名為 EditMember,參數完全不用改
* getGiftRecord 新增必帶參數(username)
格式範例:
```javascript
// 必填欄位
website: "",
username: "",
uppername: "",
key: "",
startDate: "",
endDate: "",
// 選填欄位
streamerAccount: "", // filter 直播主帳號用,格式 streamerAccount or streamerAccount@agent or streamerAccount@agent.masterAgent
account: "", // filter 使用者帳號用,格式 account or account@agent or account@agent.masterAgent
starttime: "",
endtime: "",
giftID: "", // filter 指定禮物
page: "",
pagelimit: "",
```
## Client Gateway 更新說明
* client 呼叫 介面為 http://api.{domainName}/api/action/{actionName},參數如下
格式範例:
```json
{
query: '',
Authorization: ''
}
```
客端程式範例:
``` typescript
const service = axios.create({
timeout: 5000,
headers: { 'Content-Type': 'application/json' }
});
// Request interceptors
service.interceptors.request.use(
config => {
const store = Store();
const authorization = store.state.user.userState.Authorization;
if (authorization) {
config.data['Authorization'] = authorization;
}
return config;
},
error => {
Promise.reject(error);
}
);
```
* SendGift 的 API query 參數需多一個必填欄位 Authorization
格式範例:
```json
{
server:'giftSystem',
actionName: 'sendGift',
query: '{"Authorization": "xx", "giftID":1,"giftCount":1,"memberID":"member@agent.masteragent","gameID":"livestreamer@agent.masteragent","agentID":"agent.masteragent"}'
}
```
* actionName 的改名變動
* getStreamerInfo -> getRoomInfo
* setStreamerInfo -> setRoomInfo
* getPopularStreamer -> getRankingRoomList
* 呼叫 client-gateway 的 login 及 loginStreamer 要更新
* 原本是呼叫 accessGateway,現在各別獨立呼叫兩支 API,分別為loginAction 及 loginStreamer
## Websocket 連線訊息格式
### 系統訊息
系統訊息格式範例:
```json
{
"context": "api", // 目前系統訊息都統一會使用context為api
"data": {
// 系統訊息
"code": 40101,
"message": "The account is not logged in"
}
}
```
其中有以下系統訊息
| code | message | describe | solution |
| ----- | ---------------------------------------------------- | ---------------------------- | ---------------------------------------------------- |
| 40101 | "The account is not logged in" | 連線沒有進行驗證與登入 | 進行 websocket verify |
| 40301 | "The account had been ban" | 該會員被禁止進入聊天室 | 直播主解除 |
| 40302 | "The token is not valid" | token 失效或過期 | 重新登入 |
| 40303 | "This account has been temporarily banned from chat" | 該會員被短暫禁言 | 等待禁言時間過期 |
| 40304 | "This account has been banned from chat" | 該會員被永久禁言 | 直播主解除 |
| 40305 | "Authentication for this message failed" | 該聊天訊息驗證失敗 | 確認是否聊天格式中 memberID 與 verify 時帶的是否一樣 |
| 40306 | "Only streamer can send url" | 只有直播主允許訊息中夾帶網址 | 無 |
| 40308 | "System is maintaining" | 系統進行維護 | 等待維護結束 |
> 注意 40308 目前未實作完成
### 聊天訊息格式說明 <a id="message-format"></a>
聊天訊息可以分用戶聊天訊息與系統廣播訊息其中可以使用資料中 from 來進行分辨
如果是用戶的會是一個標準的 UUID 字串 系統則是為 0
其中訊息格式為
```json
"context": "user",
"from": "19b514d9-4ddb-40a9-a25c-5b0e22803ad4", // 用戶連線UUID而系統廣播則為0
"message": 請參考以下訊息格式, // 格式為json
"room": "chatroomSystem-chatroom-mabu.mabumaster-ben001@mabu.mabumaster", // 聊天室名稱
"sentAt": 1603210582540 // 發送訊息時間 單位是 micro second
```
其中格式中 message 可以分為以下幾種
> 注意 這些都需要轉為 json 掛載至訊息格式 message 中
---
### 聊天訊息類型格式 <a id="chat-message"></a>
資料格式:
| key | type | describe |
| --- | ---- | -------- |
| memberID | string | 會員 ID|
| type | string | 聊天訊息需要固定為 "chat"|
| role | string | 角色 直播主: "streamer" 會員: "member" |
| name | string | 會員暱稱 |
| text | string | 訊息 |
| textColor | string | 訊息顏色(HEX) |
example:s
```json
{
"memberID": "ben@mabu.mabumaster",
"type": "chat",
"name": "ben",
"text": "Hello",
"textColor": "#FFF"
}
```
### 禮物訊息格式
資料格式:
| key | type | describe |
| --- | ---- | -------- |
| type | string | 禮物訊息需要固定為 "gift" |
| presenter | string | 贈禮者 memberID |
| presenterNickname | string | 贈禮者暱稱 |
| recipient | string | 收禮直播主 memberID |
| gift | object | 贈送禮物的資訊 {id: 禮物 ID, name: 禮物名稱, animeUrl: 禮物動畫 url(部分路徑), count: 數量}|
### 禮物排行榜訊息格式
資料格式:
| key | type | describe |
| --- | ---- | -------- |
| type | string | 禮物排行榜訊息需要固定為 "giftRank" |
| rankState | string | "UP" or "DOWN" or "PLATEAU" or "NEW" |
| rankingList | array(object) | { memberId: 會員 ID; nickName: 會員暱稱; rank: 排名; state: 排名上升或下降; rise: 上升或下降名次; score: 分數;}
### 系統廣播訊息格式
資料格式:
| key | type | describe |
| --- | ---- | -------- |
| type | string | 系統廣播訊息需要固定為 "system" |
| message | string | 系統訊息 |
### 追隨直播主開台狀態訊息格式
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 追隨直播主開台狀態訊息需要固定為 "sub" |
| streamer | string | 直播主 memberID |
| status | string | 開台狀態 目前只有 "start" "done" |
### 頻道火熱度訊息格式
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 頻道火熱度訊息需要固定為 "popularity" |
| streamer | string | 直播主 memberID |
| value | number | 火熱度數值 |
### 會員追隨直播主訊息格是
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 頻道火熱度訊息需要固定為 "follow" |
| streamer | string | 直播主memberID |
| follower | string | 追隨者memberID|
### 會員進入聊天室訊息格式
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 會員進入聊天室訊息需要固定為 "joinRoom" |
| member | string | 進入聊天室會員的 memberID |
| relationship | Array(number) | 1: 禁言 2: 黑單 3: 管理員 4: 追隨 5: 暫時禁言 |
| nickname | string | 進入聊天室會員的 nickname |
| accountID | string | 進入聊天室會員的 accountID |
| avatar | string | 進入聊天室會員的頭像 |
| equipmentBar | string | 進入聊天室會員的勳章 |
| vip | string | 進入聊天室會員的vip等級 |
| role | string | 進入聊天室會員的角色 |
### 客端顯示會員進入聊天室訊息格式(跟 joinRoom 差異:避免客端頻繁進出刷訊息)
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 會員進入聊天室訊息需要固定為 "welcome" |
| member | string | 進入聊天室會員的 memberID |
| relationship | Array(number) | 1: 禁言 2: 黑單 3: 管理員 4: 追隨 5: 暫時禁言 |
| nickname | string | 進入聊天室會員的 nickname |
| accountID | string | 進入聊天室會員的 accountID |
| avatar | string | 進入聊天室會員的頭像 |
| equipmentBar | string | 進入聊天室會員的勳章 |
| vip | string | 進入聊天室會員的vip等級 |
| role | string | 進入聊天室會員的角色 |
### 會員離開聊天室訊息格式
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 會員離開聊天室訊息需要固定為 "leaveRoom" |
| member | string | 離開聊天室會員的 memberID |
| nickname | string | 離開聊天室會員的 nickname |
### 直播串流狀態訊息格式
資料格式
| key | type | describe |
| --- | ---- | -------- |
| type | string | 會員離開聊天室訊息需要固定為 "leaveRoom" |
| streamer | string | 直播主 memberID |
| status | string | "start" or "done" |
## 進入聊天室流程
```
會員登入取得token(Authorization) ---> 與chatroomSystem進行連線 ---> 進入聊天室 ---> 進行connection verify ---> 進入聊天室成功
```
## 使用 actionheroWebsocketClient 連線 chatroomSystem
1、取得 actionheroWebsocketClient
需要再 html 中掛載腳本 其中需要注意的是 src 需要依據連線環境的域名進行轉換 建議使用 nginx 進行區別環境轉導
```javascript
<script
type="text/javascript"
src="{{環境domain}}/ChatRoomsSystem/public/javascript/ActionheroWebsocketClient.min.js"
></script>
```
example code:
```typescript
function buildChatroomName(agentID: string, streamerMemberID: string): string {
return `chatroomSystem-chatroom-${agentID}-${streamerMemberID}`;
}
const client = new ActionheroWebsocketClient();
client.on("connected", function () {
// websocket連線成功 但並不代表有加入聊天室
console.log("connected!");
});
client.on("disconnected", function () {
console.log("disconnected :(");
});
client.on("reconnect", function () {
console.log("reconnect");
});
client.on("reconnecting", function () {
console.log("reconnecting");
});
type SystemMessage = {
context: "api";
message: { code: number; message: string };
};
client.on("api", (payload: SystemMessage) => {
// 系統訊息 詳細麻煩查詢上面 "系統訊息格式"
});
type SayMessagePayload = {
context: "user";
from: string | 0;
room: string;
message: string; // JSON
sentAt: number;
};
client.on("say", function (payload: SayMessagePayload) {
const encodeMessage = JSON.parse(payload.message);
switch (encodeMessage.type) {
case "chat":
// 聊天室顯示 聊天訊息
type ChatMessage = {
type: "chat";
memberID: string;
name: string;
text: string;
textColor: string;
}
const { name, text, textColor } = encodeMessage as ChatMessage;
break;
case "gift":
// 聊天室顯示 贈禮訊息
type GiftMessage = {
type: "gift";
presenter: string;
recipient: string;
gift: { id: number; name: string; animeUrl: string; count: number };
};
const { presenter, recipient, gift } = encodeMessage as GiftMessage;
break;
case "system":
// 聊天室顯示 系統訊息
type SystemMessage = {
type: "system";
message: string
}
const { message } = encodeMessage as SystemMessage;
break;
case "sub": {
// 追蹤的直播主開台關台通知
type SubMessage = {
type: "follow",
streamer: string,
status: "start" | "done";
}
const { streamer, status } = encodeMessage;
break;
}
case "giftRank": {
type RankState = "UP" | "DOWN" | "PLATEAU" | "NEW";
type RankingList = {
memberId: string;
nickName: string;
rank: number; // 排名
state: string; // 與上次排名上升或下降
rise: number; // 上升或下降的名次
score: number; // 排行分數
};
type GiftRankMessage = {
type: "giftRank";
rankStateL: RankState;
rankingList: RankingList;
}
const { rankState, rankingList } = encodeMessage as GiftRankMessage;
break;
}
case "popularity": {
// 火熱度
type PopularityMessage = {
type: "popularity",
streamer: string,
value: number
}
const { streamer, value } = encodeMessage as PopularityMessage;
break;
}
case "follow": {
// 有人追隨直播主
type FollowMessage = {
type: "follow",
streamer: string,
follower: string,
}
const { streamer, follower } = encodeMessage as FollowMessage;
break;
}
case "joinRoom": {
// 用戶進入聊天室
type JoinRoomMessage = {
type: "joinRoom",
member: string, // memberID
relationship: Array<number> // 1: 禁言 2: 黑單 3: 管理員 4: 追隨 5: 暫時禁言
}
const {member, relationship} = encodeMessage as JoinRoomMessage;
}
case "leaveRoom": {
// 用戶離開聊天室
type LeaveRoomMessage = {
type: "joinRoom",
member: string, // memberID
relationship: Array<number> // 1: 禁言 2: 黑單 3: 管理員 4: 追隨 5: 暫時禁言
}
const {member, relationship} = encodeMessage as LeaveRoomMessage;
}
case "streamStatus": {
// 串流狀態
type StreamStatus = {
type: "streamStatus",
streamer: string // 直播主memberID
status: "start" | "done"
}
const {streamer, status} = encodeMessage as StreamStatus
}
}
});
client.connect((error: string, details: string) => {
if (error) {
// server錯誤訊息
} else {
const [account, agentID] = streamerMemberID.split("@")
const room = buildChatroomName(agentID, streamerMemberID);
client.roomAdd(room, () => {
// 與server驗證 如果是遊客的話並不需要登入
client.action("verify", {
memberID: "memberID", // user's memberID
chatroom: room,
Authorization: "memberToken", // 使用者token
});
})
}
});
function saySomething(text: string): void {
client.say(
chatroom,
JSON.stringify({
type: "chat",
memberID: memberID // user's memberID
name: nickname // user's nickname,
text: "Hello World",
textColor: "#FFF" // color HEX code
})
)
}
// 離開連線
async function leaveWebsocket(): Promise<void>{
await Promise.all([
// 如果有使用event的話需要對應刪除 下列列舉幾個
client.removeAllListeners('connected'),
client.removeAllListeners('disconnected'),
client.removeAllListeners('say'),
// 最後一定要進行斷連線
client.disconnect()
])
}
```