first commit

This commit is contained in:
aynakeya
2024-04-07 02:43:04 -07:00
commit 5bfed2f8bb
7 changed files with 312 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea
go.sum

View File

@@ -0,0 +1,42 @@
package main
import (
"fmt"
"liveroom"
"liveroom/provider/openblive"
"os"
"os/signal"
"time"
)
const apiServer = "http://0.0.0.0:9090"
func main() {
provider := openblive.NewOpenBLiveClientProvider(apiServer, 1661006726438)
room := provider(liveroom.LiveRoomConfig{
Room: "YOUR_CLIENT_KEY",
Provider: openblive.ProviderName,
})
room.OnMessage(func(msg *liveroom.Message) {
fmt.Println(msg.User.Username, msg.User.Uid, msg.User.Medal.Name, msg.Message)
})
room.OnStatusChange(
func(connected bool) {
if connected {
fmt.Println("Connected")
} else {
fmt.Println("Disconnected")
}
})
room.OnDisconnect(
func(liveroom liveroom.LiveRoom) {
fmt.Println("Disconnected AAAAAAAa")
})
fmt.Println("connect", room.Connect())
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt, os.Kill)
<-quit
fmt.Println("disconnect", room.Disconnect())
time.Sleep(3 * time.Second)
fmt.Println("Bye")
}

14
go.mod Normal file
View File

@@ -0,0 +1,14 @@
module liveroom
go 1.20
require (
github.com/aynakeya/open-bilibili-live v0.0.3
github.com/go-resty/resty/v2 v2.7.0
github.com/spf13/cast v1.5.1
)
require (
github.com/gorilla/websocket v1.5.0 // indirect
golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
)

44
liveroom.go Normal file
View File

@@ -0,0 +1,44 @@
package liveroom
type LiveRoomConfig struct {
Provider string `json:"provider"` // Provider is the name of the live room provider
Room string `json:"room"` // RoomID is the unique identifier of the live room
}
type LiveRoomProvider func(cfg LiveRoomConfig) LiveRoom
type UserMedal struct {
Name string `json:"name"`
Level int `json:"level"`
RoomID string `json:"room_id"`
}
const (
PrivilegeNone = iota
PrivilegeBasic
PrivilegeAdvanced
PrivilegeUltimate
)
type User struct {
Uid string
Username string
Admin bool
Privilege int
Medal UserMedal
}
type Message struct {
User User
Message string
}
type LiveRoom interface {
GetName() string // should return the name of the provider
Config() *LiveRoomConfig // should return mutable model (not a copy)
Connect() error
Disconnect() error
OnDisconnect(func(liveroom LiveRoom))
OnStatusChange(func(connected bool))
OnMessage(func(msg *Message))
}

View File

@@ -0,0 +1,68 @@
package openblive
import (
"encoding/json"
"errors"
"github.com/aynakeya/open-bilibili-live"
"github.com/go-resty/resty/v2"
"github.com/spf13/cast"
)
type remoteApiClient struct {
client *resty.Client
}
type openbliveResponse struct {
Result *openblive.AppStartResult `json:"result"`
Error *openblive.PublicError `json:"error"`
}
func newRemoteApiClient(remoteApi string) openblive.IApiClient {
return &remoteApiClient{
client: resty.New().SetBaseURL(remoteApi),
}
}
type sceneAppResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data openbliveResponse `json:"data"`
}
func parseApiResponse(resp *resty.Response, err error) (*openblive.AppStartResult, *openblive.PublicError) {
if err != nil {
return nil, openblive.ErrUnknown.WithDetail(err)
}
var sceneResp sceneAppResponse
err = json.Unmarshal(resp.Body(), &sceneResp)
if err != nil {
return nil, openblive.ErrUnknown.WithDetail(err)
}
if sceneResp.Code != 0 {
return nil, openblive.ErrUnknown.WithDetail(errors.New(sceneResp.Msg))
}
return sceneResp.Data.Result, sceneResp.Data.Error
}
func (r *remoteApiClient) AppStart(code string, appId int64) (*openblive.AppStartResult, *openblive.PublicError) {
return parseApiResponse(r.client.R().
SetQueryParam("code", code).
SetQueryParam("app_id", cast.ToString(appId)).
Get("/api/blivedm/openblive/app_start"))
}
func (r *remoteApiClient) AppEnd(appId int64, gameId string) *openblive.PublicError {
_, err := parseApiResponse(r.client.R().
SetQueryParam("app_id", cast.ToString(appId)).
SetQueryParam("game_id", gameId).
Get("/api/blivedm/openblive/app_end"))
return err
}
func (r *remoteApiClient) HearBeat(gameId string) *openblive.PublicError {
_, err := parseApiResponse(r.client.R().
SetQueryParam("game_id", gameId).
Get("/api/blivedm/openblive/heartbeat"))
return err
}

View File

@@ -0,0 +1,118 @@
package openblive
import (
"context"
openblive "github.com/aynakeya/open-bilibili-live"
"liveroom"
"strconv"
)
const ProviderName = "openblive"
type OpenBLiveClient struct {
cfg liveroom.LiveRoomConfig
openbliveClient *openblive.BLiveClient
conn openblive.BLiveLongConnection
onMessage func(msg *liveroom.Message)
onDisconnect func(liveroom liveroom.LiveRoom)
onStatusChange func(connected bool)
}
func NewOpenBLiveClientProvider(apiServer string, appId int64) liveroom.LiveRoomProvider {
return func(cfg liveroom.LiveRoomConfig) liveroom.LiveRoom {
if cfg.Provider != ProviderName {
return nil
}
return &OpenBLiveClient{
cfg: cfg,
openbliveClient: openblive.NewBliveClient(appId, cfg.Room, newRemoteApiClient(apiServer)),
}
}
}
func guardLevelToPrivilege(level int) int {
switch level {
case 1:
return liveroom.PrivilegeUltimate
case 2:
return liveroom.PrivilegeAdvanced
case 3:
return liveroom.PrivilegeBasic
default:
return liveroom.PrivilegeNone
}
}
func (o *OpenBLiveClient) danmuHandler(data openblive.DanmakuData) {
if o.onMessage == nil {
return
}
o.onMessage(&liveroom.Message{
User: liveroom.User{
Uid: data.OpenID,
Username: data.UName,
Admin: false, // not supported by open bilibili live
Privilege: guardLevelToPrivilege(data.GuardLevel),
Medal: liveroom.UserMedal{
Name: data.FansMedalName,
Level: data.FansMedalLevel,
RoomID: strconv.Itoa(data.RoomID),
},
},
Message: data.Msg,
})
}
func (o *OpenBLiveClient) disconnectHandler(conn openblive.BLiveLongConnection) {
if o.onStatusChange != nil {
o.onStatusChange(false)
}
if o.onDisconnect != nil {
o.onDisconnect(o)
}
}
func (o *OpenBLiveClient) GetName() string {
return ProviderName
}
func (o *OpenBLiveClient) Config() *liveroom.LiveRoomConfig {
return &o.cfg
}
func (o *OpenBLiveClient) Connect() error {
err := o.openbliveClient.Start()
if err != nil {
return err
}
o.conn = o.openbliveClient.GetLongConn()
o.conn.OnDanmu(o.danmuHandler)
o.conn.OnDisconnect(o.disconnectHandler)
e := o.conn.EstablishConnection(context.Background())
if e == nil {
if o.onStatusChange != nil {
o.onStatusChange(true)
}
}
return e
}
func (o *OpenBLiveClient) Disconnect() error {
_ = o.conn.CloseConnection()
if o.onStatusChange != nil {
o.onStatusChange(false)
}
return o.openbliveClient.End()
}
func (o *OpenBLiveClient) OnStatusChange(f func(connected bool)) {
o.onStatusChange = f
}
func (o *OpenBLiveClient) OnDisconnect(f func(liveroom liveroom.LiveRoom)) {
o.onDisconnect = f
}
func (o *OpenBLiveClient) OnMessage(f func(msg *liveroom.Message)) {
o.onMessage = f
}

24
registry.go Normal file
View File

@@ -0,0 +1,24 @@
package liveroom
var _providers map[string]LiveRoomProvider = make(map[string]LiveRoomProvider)
func RegisterProvider(name string, provider LiveRoomProvider) {
if _, ok := _providers[name]; ok {
panic("provider " + name + " already exists")
return
}
_providers[name] = provider
}
func GetProvider(name string) (LiveRoomProvider, bool) {
provider, ok := _providers[name]
return provider, ok
}
func ListAvailableProviders() []string {
var names []string
for name := range _providers {
names = append(names, name)
}
return names
}