summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--danmaku/dmmsg/danmu.go55
-rw-r--r--danmaku/dmmsg/interactword.go39
-rw-r--r--danmaku/dmmsg/messages.go7
-rw-r--r--danmaku/dmmsg/msghandler.go4
-rw-r--r--danmaku/dmmsg/util.go17
-rw-r--r--recording/watcher.go55
6 files changed, 173 insertions, 4 deletions
diff --git a/danmaku/dmmsg/danmu.go b/danmaku/dmmsg/danmu.go
new file mode 100644
index 0000000..2cc5dfa
--- /dev/null
+++ b/danmaku/dmmsg/danmu.go
@@ -0,0 +1,55 @@
+package dmmsg
+
+/*
+Decoder of raw danmaku messages.
+*/
+
+import (
+ "fmt"
+)
+
+type RawDanMuMessage = BaseRawMessage[[]interface{}, interface{}]
+
+type DanMuMessage struct {
+ Content string
+ SourceUser struct {
+ Nickname string
+ UID int64
+ }
+}
+
+func (dm DanMuMessage) String() string {
+ return fmt.Sprintf("(user: %v, uid: %v) %v",
+ dm.SourceUser.Nickname, dm.SourceUser.UID, dm.Content)
+}
+
+const kInvalidDanmakuJson = "invalid danmaku JSON document"
+
+func ParseDanmakuMessage(body RawDanMuMessage) (dmm DanMuMessage, err error) {
+ if len(body.Info) != 16 {
+ err = fmt.Errorf("%s: \"info\" length != 16", kInvalidDanmakuJson)
+ return
+ }
+
+ dmm.Content, err = castValue[string](body.Info[1])
+ if err != nil {
+ return
+ }
+
+ userInfo, err := castValue[[]interface{}](body.Info[2])
+
+ var ok bool
+ uid, ok := userInfo[0].(float64)
+ if !ok {
+ err = fmt.Errorf("%s: uid is not a float64: %v", kInvalidDanmakuJson, userInfo[0])
+ return
+ }
+ dmm.SourceUser.UID = int64(uid)
+
+ dmm.SourceUser.Nickname, ok = userInfo[1].(string)
+ if !ok {
+ err = fmt.Errorf("%s: nickname is not a string: %v", kInvalidDanmakuJson, userInfo[1])
+ return
+ }
+ return
+}
diff --git a/danmaku/dmmsg/interactword.go b/danmaku/dmmsg/interactword.go
new file mode 100644
index 0000000..d5dc315
--- /dev/null
+++ b/danmaku/dmmsg/interactword.go
@@ -0,0 +1,39 @@
+package dmmsg
+
+type InteractWordMessage struct {
+ Contribution struct {
+ Grade int `json:"grade"`
+ } `json:"contribution"`
+ DanMuScore int `json:"dmscore"`
+ FansMedal struct {
+ AnchorRoomid int `json:"anchor_roomid"`
+ GuardLevel int `json:"guard_level"`
+ IconID int `json:"icon_id"`
+ IsLighted int `json:"is_lighted"`
+ Color int `json:"medal_color"`
+ ColorBorder int `json:"medal_color_border"`
+ ColorEnd int `json:"medal_color_end"`
+ ColorStart int `json:"medal_color_start"`
+ Level int `json:"medal_level"`
+ Name string `json:"medal_name"`
+ Score int `json:"score"`
+ Special string `json:"special"`
+ TargetID int `json:"target_id"`
+ } `json:"fans_medal"`
+ Identities []int `json:"identities"`
+ IsSpread int `json:"is_spread"`
+ MsgType int `json:"msg_type"`
+ PrivilegeType int `json:"privilege_type"`
+ RoomId int `json:"roomid"`
+ Score int64 `json:"score"`
+ SpreadDesc string `json:"spread_desc"`
+ SpreadInfo string `json:"spread_info"`
+ TailIcon int `json:"tail_icon"`
+ Timestamp int `json:"timestamp"`
+ TriggerTime int64 `json:"trigger_time"`
+ UID int `json:"uid"`
+ UserName string `json:"uname"`
+ UserNameColor string `json:"uname_color"`
+}
+
+type RawInteractWordMessage = BaseRawMessage[interface{}, InteractWordMessage]
diff --git a/danmaku/dmmsg/messages.go b/danmaku/dmmsg/messages.go
new file mode 100644
index 0000000..d1480b7
--- /dev/null
+++ b/danmaku/dmmsg/messages.go
@@ -0,0 +1,7 @@
+package dmmsg
+
+type BaseRawMessage[I any, D any] struct {
+ Cmd string `json:"cmd"`
+ Info I `json:"info"`
+ Data D `json:"data"`
+}
diff --git a/danmaku/dmmsg/msghandler.go b/danmaku/dmmsg/msghandler.go
new file mode 100644
index 0000000..32766aa
--- /dev/null
+++ b/danmaku/dmmsg/msghandler.go
@@ -0,0 +1,4 @@
+package dmmsg
+
+type DanmakuMessageHandler interface {
+}
diff --git a/danmaku/dmmsg/util.go b/danmaku/dmmsg/util.go
new file mode 100644
index 0000000..9d41ab9
--- /dev/null
+++ b/danmaku/dmmsg/util.go
@@ -0,0 +1,17 @@
+package dmmsg
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func castValue[T any](obj interface{}) (thing T, err error) {
+ casted, ok := (obj).(T)
+ if !ok {
+ err = fmt.Errorf("%s: required value is not of type \"%v\": %v",
+ kInvalidDanmakuJson, reflect.TypeOf(thing).String(), obj)
+ return
+ }
+ thing = casted
+ return
+}
diff --git a/recording/watcher.go b/recording/watcher.go
index 439ffcb..92d2135 100644
--- a/recording/watcher.go
+++ b/recording/watcher.go
@@ -3,6 +3,7 @@ package recording
import (
"bilibili-livestream-archiver/common"
"bilibili-livestream-archiver/danmaku"
+ "bilibili-livestream-archiver/danmaku/dmmsg"
"bilibili-livestream-archiver/danmaku/dmpkg"
"context"
"encoding/json"
@@ -26,7 +27,8 @@ const (
)
type liveInfo struct {
- Command liveCommand `json:"cmd"`
+ Command liveCommand `json:"cmd"`
+ Data map[string]interface{} `json:"data"`
}
type ErrorReason int
@@ -126,7 +128,7 @@ func watch(
switch msg.Operation {
case dmpkg.OpLayer7Data:
- logger.Printf("server message: op %v, body %v\n", msg.Operation, string(msg.Body))
+ //logger.Printf("server message: op %v, body %v\n", msg.Operation, string(msg.Body))
var info liveInfo
err := json.Unmarshal(msg.Body, &info)
if err != nil {
@@ -144,8 +146,53 @@ func watch(
chEvent <- WatcherLiveStop
}
default:
- logger.Printf("Ignoring server message %v %v %v\n",
- info.Command, msg.Operation, string(msg.Body))
+ switch info.Command {
+ case "ONLINE_RANK_COUNT":
+ fallthrough
+ case "STOP_LIVE_ROOM_LIST":
+ // useless message
+ fallthrough
+ case "HOT_RANK_CHANGED_V2":
+ // useless message
+ logger.Printf("Ignore message: %v\n", info.Command)
+ case "WATCHED_CHANGE":
+ // number of watched people changed
+ obj, exists := info.Data["num"]
+ if !exists {
+ continue
+ }
+ watchedPeopleNumber, ok := obj.(float64)
+ if !ok {
+ logger.Printf("Cannot parse watched people number: %v\n", obj)
+ continue
+ }
+ logger.Printf("Watched people (room: %v): %v", roomId, watchedPeopleNumber)
+ case "INTERACT_WORD":
+ var raw dmmsg.RawInteractWordMessage
+ err = json.Unmarshal(msg.Body, &raw)
+ if err != nil {
+ logger.Printf("Cannot parse RawInteractWordMessage JSON: %v\n", err)
+ continue
+ }
+ logger.Printf("Interact word message: user: %v medal: %v",
+ raw.Data.UserName, raw.Data.FansMedal.Name)
+ case "DANMU_MSG":
+ var raw dmmsg.RawDanMuMessage
+ err = json.Unmarshal(msg.Body, &raw)
+ if err != nil {
+ logger.Printf("Cannot parse Dan Mu message as JSON: %v\n", err)
+ continue
+ }
+ dmm, err := dmmsg.ParseDanmakuMessage(raw)
+ if err != nil {
+ logger.Printf("Cannot parse Dan Mu message JSON: %v\n", err)
+ continue
+ }
+ logger.Printf("Dan mu: %v\n", dmm.String())
+ default:
+ logger.Printf("Ignoring server message %v %v %v\n",
+ info.Command, msg.Operation, string(msg.Body))
+ }
}
default:
logger.Printf("Server message: %v\n", msg.String())