]> 127.0.0.1 Git - bili_danmu/.git/commitdiff
Fix 保持亮牌仅限于当前房间 #197 (#200)
authorqydysky <qydysky@foxmail.com>
Sat, 10 May 2025 05:28:46 +0000 (13:28 +0800)
committerGitHub <noreply@github.com>
Sat, 10 May 2025 05:28:46 +0000 (13:28 +0800)
* Fix 保持亮牌仅限于当前房间 #197

* Fix 保持亮牌仅限于当前房间 #197

* Improve 优化代码

* Improve 优化代码

* Improve 优化代码

* Improve 优化代码

* Add msg VOICE_JOIN_SWITCH

* Improve 在无命令行房间参数时 可以无网络启动

21 files changed:
CV/Var.go
F/CookieCrypo.go
F/F.go
F/api.go
F/biliApiInterface.go
README.md
Reply/F.go
Reply/F/comp.go
Reply/F/danmuMerge/danmuMerge.go [new file with mode: 0644]
Reply/F/keepMedalLight/keepMedalLight.go
Reply/F/lessDanmu/lessDanmu.go [new file with mode: 0644]
Reply/Msg.go
Reply/Reply.go
Reply/stream.go
Send/Send.go
Send/Send_gift.go
Send/Send_pm.go
bili_danmu.go
demo/config/config_K_v.json
go.mod
go.sum

index e152a971b8d00b5435e94fd81a1cd84948e475a0..d7f8235d06e1fad02b6791b937b83bc0124850d1 100644 (file)
--- a/CV/Var.go
+++ b/CV/Var.go
@@ -57,7 +57,7 @@ type Common struct {
        Live_qn           int            `json:"liveQn"`           //当前直播流质量
        Live_want_qn      int            `json:"-"`                //期望直播流质量
        Roomid            int            `json:"-"`                //房间ID
-       Cookie            syncmap.Map    `json:"-"`                //Cookie
+       Cookie            *syncmap.Map   `json:"-"`                //Cookie
        Title             string         `json:"title"`            //直播标题
        Uname             string         `json:"uname"`            //主播名
        UpUid             int            `json:"upUid"`            //主播uid
@@ -194,6 +194,24 @@ func (t *LiveQn) Disable(reUpTime time.Time) {
        t.ReUpTime = reUpTime
 }
 
+func (t *Common) GenReqCookie() string {
+       return reqf.Iter_2_Cookies_String(func(yield func(string, string) bool) {
+               t.Cookie.Range(func(k, v any) bool {
+                       yield(k.(string), v.(string))
+                       return true
+               })
+       })
+}
+
+func (t *Common) IsLogin() bool {
+       for _, n := range []string{`bili_jct`, `DedeUserID`, `LIVE_BUVID`} {
+               if _, ok := t.Cookie.Load(n); !ok {
+                       return false
+               }
+       }
+       return true
+}
+
 func (t *Common) IsOn(key string) bool {
        v, ok := t.K_v.LoadV(key).(bool)
        return ok && v
@@ -210,7 +228,7 @@ func (t *Common) Copy() *Common {
                Live_qn:           t.Live_qn,
                Live_want_qn:      t.Live_want_qn,
                Roomid:            t.Roomid,
-               Cookie:            t.Cookie.Copy(),
+               Cookie:            t.Cookie,
                Title:             t.Title,
                Uname:             t.Uname,
                UpUid:             t.UpUid,
@@ -306,6 +324,7 @@ func (t *Common) Init() *Common {
        t.PID = os.Getpid()
        t.Version = strings.TrimSpace(version)
        t.StartT = time.Now()
+       t.Cookie = &syncmap.Map{}
 
        t.AllStreamType = map[string]StreamType{
                `fmp4`: {
index 487e2a05195bd8d3abef9c037fe506b9237cb613..1e52c142c58e7ec2c3771ee7bf7d9cb58d6ef1c8 100644 (file)
@@ -23,6 +23,8 @@ var (
 )
 
 // 需要先判断path存在cookie文件
+//
+// 从文件获取cookie到缓存
 func CookieGet(path string) []byte {
        clog := clog.Base_add(`获取`)
 
@@ -102,6 +104,7 @@ func CookieGet(path string) []byte {
        }
 }
 
+// 保存到cookie到缓存及文件
 func CookieSet(path string, source []byte) {
        clog := clog.Base_add(`设置`)
 
diff --git a/F/F.go b/F/F.go
index 34d3ed05e74471ad1489bbb4710a3ec67d49c332..9d54bf874974c2b64b7ce047468d298627d2c3eb 100644 (file)
--- a/F/F.go
+++ b/F/F.go
@@ -126,20 +126,6 @@ func Heartbeat() ([]byte, int) {
        buffer.Write([]byte(obj))
 
        return buffer.Bytes(), heartBeatInterval
-
-}
-
-// cookie检查
-func CookieCheck(key []string) (missKey []string) {
-       for _, tk := range key {
-               if tk == `` {
-                       continue
-               }
-               if _, ok := c.C.Cookie.Load(tk); !ok {
-                       missKey = append(missKey, tk)
-               }
-       }
-       return
 }
 
 // just faster, use in right way
index fc9acd2b29a839764b75426c6042c682dd48275c..da3411ed873a510ea81bc5b9900ae6af4debb4c0 100644 (file)
--- a/F/api.go
+++ b/F/api.go
@@ -26,6 +26,7 @@ import (
        pio "github.com/qydysky/part/io"
        limit "github.com/qydysky/part/limit"
        reqf "github.com/qydysky/part/reqf"
+       psync "github.com/qydysky/part/sync"
 
        "github.com/mdp/qrterminal/v3"
        qr "github.com/skip2/go-qrcode"
@@ -41,6 +42,15 @@ var biliApi = cmp.Get(id, cmp.PreFuncCu[BiliApiInter]{
                ba.SetLocation(c.C.SerLocation)
                ba.SetProxy(c.C.Proxy)
                ba.SetReqPool(c.C.ReqPool)
+
+               savepath := "./cookie.txt"
+               if tmp, ok := c.C.K_v.LoadV("cookie路径").(string); ok && tmp != "" {
+                       savepath = tmp
+               }
+               ba.SetCookiesCallback(func(cookies []*http.Cookie) {
+                       CookieSet(savepath, []byte(reqf.Cookies_List_2_String(cookies))) //cookie 存入文件
+                       psync.StoreAll(c.C.Cookie, reqf.Cookies_List_2_Map(cookies))     //cookie 存入全局变量
+               })
                return ba
        },
 })
@@ -765,6 +775,8 @@ func (t *GetFunc) getDanmuInfo() (missKey []string) {
 
        //GetDanmuInfo
        if err, res := biliApi.GetDanmuInfo(t.Roomid); err != nil {
+               t.Token = ""
+               t.WSURL = t.WSURL[:0]
                apilog.L(`E: `, err)
                return
        } else {
@@ -843,105 +855,34 @@ func (t *GetFunc) Get_guardNum() (missKey []string) {
        return
 }
 
-// func (t *GetFunc) Info(UpUid int) (J.Info, error) {
-//     fkey := `Info`
-
-//     if v, ok := t.Cache.LoadV(fkey).(cacheItem); ok && v.exceeded.After(time.Now()) {
-//             return (v.data).(J.Info), nil
-//     }
-
-//     // 超额请求阻塞,超时将取消
-//     apilog := apilog.Base_add(`Info`)
-//     if api_limit.TO() {
-//             return J.Info{}, os.ErrDeadlineExceeded
-//     }
-
-//     query := fmt.Sprintf("mid=%d&token=&platform=web&web_location=1550101", UpUid)
-//     // wbi
-//     if e, queryE := biliApi.Wbi(query); e != nil {
-//             return J.Info{}, e
-//     } else {
-//             query = queryE
-//     }
-
-//     // html
-//     {
-//             Cookie := make(map[string]string)
-//             t.Cookie.Range(func(k, v interface{}) bool {
-//                     Cookie[k.(string)] = v.(string)
-//                     return true
-//             })
-//             req := t.ReqPool.Get()
-//             defer t.ReqPool.Put(req)
-
-//             if err := req.Reqf(reqf.Rval{
-//                     Url:     `https://api.bilibili.com/x/space/wbi/acc/info?` + query,
-//                     Proxy:   t.Proxy,
-//                     Timeout: 10 * 1000,
-//                     Retry:   2,
-//                     Header: map[string]string{
-//                             `Accept`: "application/json, text/plain, */*",
-//                             `Cookie`: reqf.Map_2_Cookies_String(Cookie),
-//                     },
-//             }); err != nil {
-//                     apilog.L(`E: `, err)
-//                     return J.Info{}, err
-//             }
-
-//             var info J.Info
-
-//             //Info
-//             if e := json.Unmarshal(req.Respon, &info); e != nil {
-//                     apilog.L(`E: `, e)
-//                     return J.Info{}, e
-//             }
-
-//             t.Cache.Store(fkey, cacheItem{
-//                     data:     info,
-//                     exceeded: time.Now().Add(time.Hour),
-//             })
-//             return info, nil
-//     }
-// }
-
 // 调用记录
 var boot_Get_cookie funcCtrl.FlashFunc //新的替代旧的
 
 // 扫码登录
 func (t *GetFunc) Get_cookie() (missKey []string) {
        apilog := apilog.Base_add(`获取Cookie`)
+
+       savepath := "./cookie.txt"
+       if tmp, ok := t.K_v.LoadV("cookie路径").(string); ok && tmp != "" {
+               savepath = tmp
+       }
+
        //获取其他Cookie
        defer func() {
                if err := biliApi.GetOtherCookies(); err != nil {
                        apilog.L(`E: `, err)
-               } else if cookies := biliApi.GetCookies(); len(cookies) != 0 {
-                       if err := save_cookie(cookies, t.Common); err != nil && !errors.Is(err, ErrNoCookiesSave) {
-                               apilog.L(`E: `, err)
-                       }
                }
        }()
 
-       savepath := "./cookie.txt"
-       if tmp, ok := t.K_v.LoadV("cookie路径").(string); ok && tmp != "" {
-               savepath = tmp
-       }
-
        if file.New(savepath, 0, true).IsExist() { //读取cookie文件
                if cookieString := string(CookieGet(savepath)); cookieString != `` {
-                       for k, v := range reqf.Cookies_String_2_Map(cookieString) { //cookie存入全局变量syncmap
-                               t.Cookie.Store(k, v)
-                       }
-
-                       if miss := CookieCheck([]string{
-                               `bili_jct`,
-                               `DedeUserID`,
-                       }); len(miss) == 0 {
-                               biliApi.SetCookies(reqf.Cookies_String_2_List(cookieString))
+                       biliApi.SetCookies(reqf.Cookies_String_2_List(cookieString)) //cookie 存入biliApi
+                       if biliApi.IsLogin() {
                                if e, res := biliApi.GetNav(); e != nil {
                                        apilog.L(`E: `, e)
                                } else if res.IsLogin {
                                        // uid
-                                       if uid, ok := t.Cookie.LoadV(`DedeUserID`).(string); ok { //cookie中无DedeUserID
+                                       if e, uid := biliApi.GetCookie(`DedeUserID`); e == nil { //cookie中无DedeUserID
                                                if uid, e := strconv.Atoi(uid); e == nil {
                                                        t.Uid = uid
                                                }
@@ -1030,12 +971,6 @@ func (t *GetFunc) Get_cookie() (missKey []string) {
        }
 
        { //循环查看是否通过
-               Cookie := make(map[string]string)
-               t.Cookie.Range(func(k, v interface{}) bool {
-                       Cookie[k.(string)] = v.(string)
-                       return true
-               })
-
                r := t.ReqPool.Get()
                defer t.ReqPool.Put(r)
                for pollC := 10; pollC > 0; pollC-- {
@@ -1052,11 +987,7 @@ func (t *GetFunc) Get_cookie() (missKey []string) {
                                return
                        } else if code == 0 {
                                if cookies := biliApi.GetCookies(); len(cookies) != 0 {
-                                       if err := save_cookie(cookies, t.Common); err != nil {
-                                               apilog.L(`E: `, err)
-                                               return
-                                       }
-                                       if uid, ok := t.Cookie.LoadV(`DedeUserID`).(string); ok { //cookie中无DedeUserID
+                                       if e, uid := biliApi.GetCookie(`DedeUserID`); e == nil { //cookie中无DedeUserID
                                                if uid, e := strconv.Atoi(uid); e == nil {
                                                        t.Uid = uid
                                                }
@@ -1071,93 +1002,21 @@ func (t *GetFunc) Get_cookie() (missKey []string) {
        return
 }
 
-// 获取其他Cookie
-// func (t *GetFunc) Get_other_cookie() {
-//     apilog := apilog.Base_add(`获取其他Cookie`)
-
-//     r := c.ReqPool.Get()
-//     defer c.ReqPool.Put(r)
-
-//     Cookie := make(map[string]string)
-//     c.Cookie.Range(func(k, v interface{}) bool {
-//             Cookie[k.(string)] = v.(string)
-//             return true
-//     })
-
-//     if e := r.Reqf(reqf.Rval{
-//             Url: `https://www.bilibili.com/`,
-//             Header: map[string]string{
-//                     `Cookie`: reqf.Map_2_Cookies_String(Cookie),
-//             },
-//             Proxy:   c.Proxy,
-//             Timeout: 10 * 1000,
-//             Retry:   2,
-//     }); e != nil {
-//             apilog.L(`E: `, e)
-//             return
-//     }
-
-//     if e := save_cookie(r.Response.Cookies()); e != nil && !errors.Is(e, ErrNoCookiesSave) {
-//             apilog.L(`E: `, e)
-//     }
-// }
-
 // 短信登录
 func Get_cookie_by_msg() {
        /*https://passport.bilibili.com/x/passport-login/web/sms/send*/
 }
 
-// 牌子字段
-// 获取牌子信息
-// func GetListInRoom(RoomID, TargetID int) (array []struct {
-//     Uid       int
-//     TodayFeed int
-//     TargetID  int
-//     IsLighted int
-//     MedalID   int
-//     RoomID    int
-// }) {
-//     apilog := apilog.Base_add(`获取牌子`)
-//     //验证cookie
-//     if missKey := CookieCheck([]string{
-//             `bili_jct`,
-//             `DedeUserID`,
-//             `LIVE_BUVID`,
-//     }); len(missKey) != 0 {
-//             apilog.L(`T: `, `Cookie无Key:`, missKey)
-//             return
-//     }
-
-//     //getHotRank
-//     if err, res := biliApi.GetFansMedal(RoomID, TargetID); err != nil {
-//             apilog.L(`E: `, err)
-//     } else {
-//             return res
-//     }
-
-//     return
-// }
-
 func GetBiliApi() BiliApiInter {
        return biliApi
 }
 
 // 获取当前佩戴的牌子
-func Get_weared_medal(uid, upUid int) (item J.GetWearedMedal_Data) {
-
+func Get_weared_medal(uid, upUid int) (item J.GetWearedMedal_Data, e error) {
        apilog := apilog.Base_add(`获取佩戴牌子`)
-       //验证cookie
-       if missKey := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missKey)
-               return
-       }
-
        if err, res := biliApi.GetWearedMedal(uid, upUid); err != nil {
                apilog.L(`E: `, err)
+               e = err
        } else {
                item.Roominfo.RoomID = res.RoomID
                item.TargetID = res.TargetID
@@ -1179,23 +1038,15 @@ func (t *GetFunc) CheckSwitch_FansMedal() (missKey []string) {
        }
 
        apilog := apilog.Base_add(`切换粉丝牌`)
-       //验证cookie
-       if missCookie := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missCookie) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missCookie)
+
+       //验证登陆
+       if !biliApi.IsLogin() {
+               apilog.L(`T: `, `未登陆`)
                return
        }
 
-       Cookie := make(map[string]string)
-       t.Cookie.Range(func(k, v interface{}) bool {
-               Cookie[k.(string)] = v.(string)
-               return true
-       })
        { //获取当前牌子,验证是否本直播间牌子
-               res := Get_weared_medal(t.Uid, t.UpUid)
+               res, _ := Get_weared_medal(t.Uid, t.UpUid)
 
                t.Wearing_FansMedal = res.Roominfo.RoomID //更新佩戴信息
                if res.TargetID == t.UpUid {
@@ -1243,13 +1094,10 @@ func (t *GetFunc) CheckSwitch_FansMedal() (missKey []string) {
 // 签到活动已下线
 func Dosign() {
        apilog := apilog.Base_add(`签到`).L(`T: `, `签到`)
-       //验证cookie
-       if missKey := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missKey)
+
+       //验证登陆
+       if !biliApi.IsLogin() {
+               apilog.L(`T: `, `未登陆`)
                return
        }
 
@@ -1301,17 +1149,8 @@ func (t *GetFunc) Get_LIVE_BUVID() (missKey []string) {
                        apilog.L(`E: `, err)
                        return
                }
-               cookies := biliApi.GetCookies()
-
-               //cookie
-               _ = save_cookie(cookies, t.Common)
-               var has bool
-               for k := range reqf.Cookies_List_2_Map(cookies) {
-                       if k == `LIVE_BUVID` {
-                               has = true
-                       }
-               }
-               if has {
+               psync.StoreAll(t.Cookie, reqf.Cookies_List_2_Map(biliApi.GetCookies()))
+               if e, _ := biliApi.GetCookie(`LIVE_BUVID`); e == nil {
                        apilog.L(`I: `, `获取到LIVE_BUVID,保存cookie`)
                        break
                } else {
@@ -1334,15 +1173,6 @@ func Gift_list() (list []struct {
        Expire_at int
 }) {
        apilog := apilog.Base_add(`礼物列表`)
-       //验证cookie
-       if missKey := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missKey)
-               return
-       }
        if c.C.Roomid == 0 {
                apilog.L(`E: `, `失败!无Roomid`)
                return
@@ -1372,13 +1202,9 @@ func (t *GetFunc) Silver_2_coin() (missKey []string) {
                return
        }
 
-       //验证cookie
-       if miss := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(miss) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, miss)
+       //验证登陆
+       if !biliApi.IsLogin() {
+               apilog.L(`T: `, `未登陆`)
                return
        }
 
@@ -1408,42 +1234,14 @@ func (t *GetFunc) Silver_2_coin() (missKey []string) {
        //交换
        if e, msg := biliApi.Silver2coin(); e != nil {
                apilog.L(`E: `, e)
-               return
        } else {
                apilog.L(`I: `, msg)
-               if cookies := biliApi.GetCookies(); len(cookies) != 0 {
-                       _ = save_cookie(cookies, t.Common)
-               }
        }
        return
 }
 
 var ErrNoCookiesSave = errors.New("ErrNoCookiesSave")
 
-func save_cookie(Cookies []*http.Cookie, cs *c.Common) error {
-       if len(Cookies) == 0 {
-               return ErrNoCookiesSave
-       }
-
-       for k, v := range reqf.Cookies_List_2_Map(Cookies) {
-               c.C.Cookie.Store(k, v)
-       }
-
-       Cookie := make(map[string]string)
-       c.C.Cookie.Range(func(k, v interface{}) bool {
-               Cookie[k.(string)] = v.(string)
-               return true
-       })
-
-       savepath := "./cookie.txt"
-       if tmp, ok := cs.K_v.LoadV("cookie路径").(string); ok && tmp != "" {
-               savepath = tmp
-       }
-       CookieSet(savepath, []byte(reqf.Map_2_Cookies_String(Cookie)))
-       biliApi.SetCookies(Cookies)
-       return nil
-}
-
 // 正在直播主播
 type UpItem struct {
        Uname      string `json:"uname"`
@@ -1461,13 +1259,9 @@ func GetHisStream() (Uplist []struct {
 }) {
        apilog := apilog.Base_add(`历史直播主播`).L(`T: `, `获取中`)
        defer apilog.L(`T: `, `完成`)
-       //验证cookie
-       if missKey := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missKey)
+       //验证登陆
+       if !biliApi.IsLogin() {
+               apilog.L(`T: `, `未登陆`)
                return
        }
        if api_limit.TO() {
@@ -1487,13 +1281,9 @@ func GetHisStream() (Uplist []struct {
 // 进入房间
 func RoomEntryAction(roomId int) {
        apilog := apilog.Base_add(`进入房间`)
-       //验证cookie
-       if missKey := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missKey)
+       //验证登陆
+       if !biliApi.IsLogin() {
+               apilog.L(`T: `, `未登陆`)
                return
        }
        if api_limit.TO() {
@@ -1543,13 +1333,9 @@ func Feed_list() (Uplist []struct {
 }) {
        apilog := apilog.Base_add(`正在直播主播`).L(`T: `, `获取中`)
        defer apilog.L(`T: `, `完成`)
-       //验证cookie
-       if missKey := CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 {
-               apilog.L(`T: `, `Cookie无Key:`, missKey)
+       //验证登陆
+       if !biliApi.IsLogin() {
+               apilog.L(`T: `, `未登陆`)
                return
        }
        if api_limit.TO() {
index eed35c5895fd4ed9f10d80e0d21a0c609dab23be..5931062c5b7b67fb7c326112a25f368a0faae939 100644 (file)
@@ -11,10 +11,12 @@ import (
 type BiliApiInter interface {
        SetReqPool(pool *pool.Buf[reqf.Req])
        SetProxy(proxy string)
-       SetLocation(secOfTimeZone int) // east positive
-       SetCookies(cookies []*http.Cookie)
-       GetCookies() (cookies []*http.Cookie)
-       GetCookie(name string) (error, string)
+       SetLocation(secOfTimeZone int)                   // east positive
+       SetCookies(cookies []*http.Cookie)               // 设置bili cookie,用于从cookie持久化中恢复
+       SetCookiesCallback(func(cookies []*http.Cookie)) // 当有新cookie时,将调用,用于cookie持久化
+       GetCookies() (cookies []*http.Cookie)            // 获取所有cookie,用于其他需要cookie的情况
+       GetCookie(name string) (error, string)           // 获取特定cookie,用于其他需要cookie的情况
+       IsLogin() bool                                   // 通过cookie判断是否登陆
 
        LikeReport(hitCount, uid, roomid, upUid int) (err error)
        LoginQrCode() (err error, imgUrl string, QrcodeKey string)
index bf797df84e0e11a2175785789fddb397e542cbeb..f729b46f39b21e961d0ef13eb7e6f43b590cd105 100644 (file)
--- a/README.md
+++ b/README.md
@@ -297,9 +297,14 @@ curl -s http://{主机名}:11000/ip/ | awk '/240:?/'
 ```
 
 #### 保持粉丝牌点亮
-配置文件中添加配置项`保持牌子亮着_指定时间`(>v0.14.11)。将会在指定时间启动保持。默认`00:00:00`
+(>0.17.1)保持粉丝牌功能修改为仅对当前直播间生效。以避免,在当前规则下,对于持有大量牌子将会频繁发送弹幕。
+取消配置项`保持牌子亮着_指定时间`。
+默认只在下播时,发送`进房弹幕_内容`的以保持亮牌,1天内将最多发送10条弹幕/30次点赞,间隔大于100s。
+添加配置项`保持牌子亮着-开播时也发送`默认为false,当为true时,开播时也会发送。
 
-保持期间,可能会频繁发送弹幕(间隔5秒/条),此时可能会影响其他使用。
+~~配置文件中添加配置项`保持牌子亮着_指定时间`(>v0.14.11)。将会在指定时间启动保持。默认`00:00:00`~~
+
+~~保持期间,可能会频繁发送弹幕(间隔5秒/条),此时可能会影响其他使用。~~
 
 #### 直播回放显示表情
 配置文件中添加配置项`弹幕表情`(>v0.14.9)。默认为true,当为true时,将会保存弹幕中的表情png到emots目录下,并在回放时显示表情。
index ccd6bd291dcba7a36ab6b2d23a75f524cc651326..c188b5d3cda92ef8ffccd0faafb753c42664b45d 100644 (file)
@@ -23,6 +23,7 @@ import (
 
        _ "github.com/go-sql-driver/mysql"
        _ "github.com/lib/pq"
+       p "github.com/qydysky/part"
        psql "github.com/qydysky/part/sql"
        _ "modernc.org/sqlite"
 
@@ -31,19 +32,15 @@ import (
        F "github.com/qydysky/bili_danmu/F"
        replyFunc "github.com/qydysky/bili_danmu/Reply/F"
        "github.com/qydysky/bili_danmu/Reply/F/danmuXml"
-       "github.com/qydysky/bili_danmu/Reply/F/keepMedalLight"
        videoInfo "github.com/qydysky/bili_danmu/Reply/F/videoInfo"
        send "github.com/qydysky/bili_danmu/Send"
 
-       p "github.com/qydysky/part"
        compress "github.com/qydysky/part/compress"
        pctx "github.com/qydysky/part/ctx"
        file "github.com/qydysky/part/file"
        fctrl "github.com/qydysky/part/funcCtrl"
        pio "github.com/qydysky/part/io"
-       limit "github.com/qydysky/part/limit"
        msgq "github.com/qydysky/part/msgq"
-       slice "github.com/qydysky/part/slice"
        psync "github.com/qydysky/part/sync"
        pweb "github.com/qydysky/part/web"
        websocket "github.com/qydysky/part/websocket"
@@ -61,28 +58,28 @@ func IsOn(s string) bool {
 
 // 字符重复度检查
 // a在buf中出现的字符占a的百分数
-func cross(a string, buf []string) float32 {
-       var s float32
-       var matched bool
-       for _, v1 := range a {
-               for _, v2 := range buf {
-                       for _, v3 := range v2 {
-                               if v3 == v1 {
-                                       matched = true
-                                       break
-                               }
-                       }
-                       if matched {
-                               break
-                       }
-               }
-               if matched {
-                       s += 1
-               }
-               matched = false
-       }
-       return s / float32(len([]rune(a)))
-}
+// func cross(a string, buf []string) float32 {
+//     var s float32
+//     var matched bool
+//     for _, v1 := range a {
+//             for _, v2 := range buf {
+//                     for _, v3 := range v2 {
+//                             if v3 == v1 {
+//                                     matched = true
+//                                     break
+//                             }
+//                     }
+//                     if matched {
+//                             break
+//                     }
+//             }
+//             if matched {
+//                     s += 1
+//             }
+//             matched = false
+//     }
+//     return s / float32(len([]rune(a)))
+// }
 
 // 在a中仅出现一次出现的字符占a的百分数
 // func selfcross(a string) float32 {
@@ -231,162 +228,6 @@ func StreamOCut(roomid int) (setTitle func(title ...string)) {
        return func(s ...string) {}
 }
 
-type Autoskip struct {
-       roomid int
-       buf    map[string]Autoskip_item
-       sync.Mutex
-       now    uint
-       ticker *time.Ticker
-}
-
-type Autoskip_item struct {
-       Exprie uint
-       Num    uint
-}
-
-var autoskip = Autoskip{
-       buf:    make(map[string]Autoskip_item),
-       ticker: time.NewTicker(time.Duration(2) * time.Second),
-}
-
-func init() {
-       go func() {
-               for {
-                       <-autoskip.ticker.C
-                       autoskip.Lock()
-                       if len(autoskip.buf) == 0 {
-                               autoskip.Unlock()
-                               continue
-                       }
-                       autoskip.now += 1
-                       if autoskip.roomid != c.C.Roomid {
-                               autoskip.buf = make(map[string]Autoskip_item)
-                               autoskip.roomid = c.C.Roomid
-                               flog.Base_add(`弹幕合并`).L(`T: `, `房间更新:`, autoskip.roomid)
-                               autoskip.Unlock()
-                               continue
-                       }
-                       for k, v := range autoskip.buf {
-                               if v.Exprie <= autoskip.now {
-                                       delete(autoskip.buf, k)
-                                       { //超时显示
-                                               if v.Num > 3 {
-                                                       c.C.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列
-                                                               uid: `0multi`,
-                                                               m: map[string]string{
-                                                                       `{num}`: strconv.Itoa(int(v.Num)),
-                                                                       `{msg}`: k,
-                                                               },
-                                                       })
-                                                       Msg_showdanmu(Danmu_item{
-                                                               msg:    strconv.Itoa(int(v.Num)) + " x " + k,
-                                                               uid:    `0multi`,
-                                                               roomid: autoskip.roomid,
-                                                       })
-                                               } else if v.Num > 1 {
-                                                       Msg_showdanmu(Danmu_item{
-                                                               msg:    strconv.Itoa(int(v.Num)) + " x " + k,
-                                                               uid:    `0default`,
-                                                               roomid: autoskip.roomid,
-                                                       })
-                                               }
-                                       }
-                               }
-                       }
-                       { //copy map
-                               tmp := make(map[string]Autoskip_item)
-                               for k, v := range autoskip.buf {
-                                       tmp[k] = v
-                               }
-                               autoskip.buf = tmp
-                       }
-                       autoskip.Unlock()
-               }
-       }()
-}
-
-func Autoskipf(s string) uint {
-       if !IsOn("弹幕合并") || s == "" {
-               return 0
-       }
-       autoskip.Lock()
-       defer autoskip.Unlock()
-       if autoskip.roomid != c.C.Roomid {
-               autoskip.buf = make(map[string]Autoskip_item)
-               autoskip.roomid = c.C.Roomid
-               flog.Base_add(`弹幕合并`).L(`T: `, `房间更新:`, autoskip.roomid)
-               return 0
-       }
-       { //验证是否已经存在
-               if v, ok := autoskip.buf[s]; ok && autoskip.now < v.Exprie {
-                       autoskip.buf[s] = Autoskip_item{
-                               Exprie: v.Exprie,
-                               Num:    v.Num + 1,
-                       }
-                       return v.Num
-               }
-       }
-       { //设置
-               autoskip.buf[s] = Autoskip_item{
-                       Exprie: autoskip.now + 8,
-                       Num:    1,
-               }
-       }
-       return 0
-}
-
-type Lessdanmu struct {
-       roomid    int
-       buf       []string
-       limit     *limit.Limit
-       max_num   int
-       threshold float32
-}
-
-var lessdanmu = Lessdanmu{
-       threshold: 0.7,
-}
-
-func init() {
-       if max_num, ok := c.C.K_v.LoadV(`每秒显示弹幕数`).(float64); ok && int(max_num) >= 1 {
-               flog.Base_add(`更少弹幕`).L(`T: `, `每秒弹幕数:`, int(max_num))
-               lessdanmu.max_num = int(max_num)
-               lessdanmu.limit = limit.New(int(max_num), "1s", "0s") //timeout right now
-       }
-}
-
-func Lessdanmuf(s string) (show bool) {
-       if !IsOn("相似弹幕忽略") {
-               return true
-       }
-       if lessdanmu.roomid != c.C.Roomid {
-               lessdanmu.buf = nil
-               lessdanmu.roomid = c.C.Roomid
-               lessdanmu.threshold = 0.7
-               flog.Base_add(`更少弹幕`).L(`T: `, `房间更新:`, lessdanmu.roomid)
-               return true
-       }
-       if len(lessdanmu.buf) < 20 {
-               lessdanmu.buf = append(lessdanmu.buf, s)
-               return true
-       }
-
-       o := cross(s, lessdanmu.buf)
-       if o == 1 {
-               return false
-       } //完全无用
-
-       Jiezouf(lessdanmu.buf)
-       slice.DelFront(&lessdanmu.buf, 1)
-
-       show = o < lessdanmu.threshold
-
-       if show && lessdanmu.max_num > 0 {
-               return !lessdanmu.limit.TO()
-       }
-       return
-}
-
 /*
        Moredanmu
        目标:弹幕机自动发送弹幕
@@ -596,68 +437,15 @@ func Entry_danmu(common *c.Common) {
                return
        }
        if v, _ := common.K_v.LoadV(`进房弹幕_仅发首日弹幕`).(bool); v {
-               res := F.Get_weared_medal(common.Uid, common.UpUid)
+               res, _ := F.Get_weared_medal(common.Uid, common.UpUid)
                if res.TodayIntimacy > 0 {
                        flog.L(`T: `, `今日已发弹幕`)
                        return
                }
        }
-       if array, ok := common.K_v.LoadV(`进房弹幕_内容`).([]interface{}); ok && len(array) != 0 {
+       if array, ok := common.K_v.LoadV(`进房弹幕_内容`).([]any); ok && len(array) != 0 {
                rand := p.Rand().MixRandom(0, int64(len(array)-1))
-               _ = send.Danmu_s(array[rand].(string), common.Roomid)
-       }
-}
-
-// 保持所有牌子点亮
-func KeepMedalLight(ctx context.Context, common *c.Common) {
-       if v, _ := common.K_v.LoadV(`保持牌子亮着`).(bool); !v {
-               return
-       }
-
-       v, _ := common.K_v.LoadV(`保持牌子亮着_指定时间`).(string)
-       if v == "" {
-               v = "00:00:00"
-       }
-
-       flog := flog.Base_add(`保持亮牌`)
-       if tt, e := time.Parse(time.TimeOnly, v); e != nil {
-               flog.L(`E: `, e)
-       } else {
-               flog.L(`I: `, "将在", v, "启动")
-               sec := tt.Hour()*3600 + tt.Minute()*60 + tt.Second()
-               go func() {
-                       ctx, done := pctx.WaitCtx(ctx)
-                       defer done()
-
-                       for {
-                               h, m, s := time.Now().Clock()
-                               now := h*3600 + m*60 + s
-                               if sec >= now {
-                                       select {
-                                       case <-time.After(time.Second * time.Duration(sec-now)):
-                                       case <-ctx.Done():
-                                               return
-                                       }
-                               } else {
-                                       select {
-                                       case <-time.After(time.Hour*24 + time.Second*time.Duration(sec-now)):
-                                       case <-ctx.Done():
-                                               return
-                                       }
-                               }
-
-                               if _, e := keepMedalLight.Main.Run(ctx, keepMedalLight.Func{
-                                       Uid:         common.Uid,
-                                       Logg:        flog,
-                                       BiliApi:     F.GetBiliApi(),
-                                       SendDanmu:   send.Danmu_s,
-                                       PreferDanmu: common.K_v.LoadV(`进房弹幕_内容`).([]any),
-                               }); e != nil {
-                                       flog.L(`E: `, e)
-                                       return
-                               }
-                       }
-               }()
+               replyFunc.KeepMedalLight.Do(array[rand].(string))
        }
 }
 
@@ -878,7 +666,7 @@ func init() {
 
                        // 获取当前房间的
                        var currentStreamO *M4SStream
-                       c.StreamO.Range(func(key, value interface{}) bool {
+                       c.StreamO.Range(func(key, value any) bool {
                                if key != nil && c.C.Roomid == key.(int) {
                                        currentStreamO = value.(*M4SStream)
                                        return false
index 2b923e50d25de409125e13bd624d73df6073b098..c8a210aa91d6b0a4800e380c51a41499dd7c3043 100644 (file)
@@ -10,7 +10,10 @@ import (
        _ "github.com/qydysky/bili_danmu/Reply/F/ass"
        _ "github.com/qydysky/bili_danmu/Reply/F/danmuCountPerMin"
        _ "github.com/qydysky/bili_danmu/Reply/F/danmuEmotes"
+       _ "github.com/qydysky/bili_danmu/Reply/F/danmuMerge"
        _ "github.com/qydysky/bili_danmu/Reply/F/danmuji"
+       _ "github.com/qydysky/bili_danmu/Reply/F/keepMedalLight"
+       _ "github.com/qydysky/bili_danmu/Reply/F/lessDanmu"
        _ "github.com/qydysky/bili_danmu/Reply/F/parseM3u8"
        _ "github.com/qydysky/bili_danmu/Reply/F/rev"
        _ "github.com/qydysky/bili_danmu/Reply/F/videoFastSeed"
@@ -18,6 +21,26 @@ import (
        log "github.com/qydysky/part/log"
 )
 
+var LessDanmu = comp.Get[interface {
+       Init(maxNumSec int)
+       InitRoom(roomid int)
+       Unset()
+       Do(s string) (show bool)
+}](`lessDanmu`)
+
+var DanmuMerge = comp.Get[interface {
+       InitSend(f func(roomid int, num uint, msg string)) bool
+       Init(ctx context.Context, roomid int)
+       Unset()
+       Do(s string) uint
+}](`danmuMerge`)
+
+var KeepMedalLight = comp.Get[interface {
+       Init(L *log.Log_interface, Roomid int, SendDanmu func(danmu string, RoomID int) error, PreferDanmu any)
+       Clear()
+       Do(prefer ...string)
+}](`keepMedalLight`)
+
 var Rev = comp.Get[interface {
        Init(l *log.Log_interface)
        ShowRev(roomid int, rev float64)
diff --git a/Reply/F/danmuMerge/danmuMerge.go b/Reply/F/danmuMerge/danmuMerge.go
new file mode 100644 (file)
index 0000000..1d071c2
--- /dev/null
@@ -0,0 +1,107 @@
+package danmumerge
+
+import (
+       "context"
+       "sync"
+       "time"
+
+       comp "github.com/qydysky/part/component2"
+)
+
+type TargetInterface interface {
+       InitSend(f func(roomid int, num uint, msg string)) bool
+       Init(ctx context.Context, roomid int)
+       Unset()
+       Do(s string) uint
+}
+
+func init() {
+       if e := comp.Register[TargetInterface]("danmuMerge", &danmuMerge{
+               buf:    make(map[string]*danmuMergeItem),
+               ticker: time.NewTicker(time.Duration(2) * time.Second),
+       }); e != nil {
+               panic(e)
+       }
+}
+
+type danmuMerge struct {
+       f      func(roomid int, num uint, msg string)
+       roomid int
+       buf    map[string]*danmuMergeItem
+       now    uint
+       ticker *time.Ticker
+       sync.RWMutex
+}
+
+type danmuMergeItem struct {
+       Exprie uint
+       Num    uint
+}
+
+// Unset implements TargetInterface.
+func (t *danmuMerge) Unset() {
+       t.Lock()
+       defer t.Unlock()
+       t.roomid = 0
+}
+
+// InitSend implements TargetInterface.
+func (t *danmuMerge) InitSend(f func(roomid int, num uint, msg string)) bool {
+       t.Lock()
+       defer t.Unlock()
+       t.f = f
+       return false
+}
+
+func (t *danmuMerge) Init(ctx context.Context, roomid int) {
+       t.Lock()
+       defer t.Unlock()
+       clear(t.buf)
+       t.roomid = roomid
+       go func() {
+               for {
+                       select {
+                       case <-t.ticker.C:
+                               t.now += 1
+                       case <-ctx.Done():
+                               return
+                       }
+
+                       t.Lock()
+                       if len(t.buf) != 0 {
+                               for k, v := range t.buf {
+                                       if v.Exprie <= t.now {
+                                               //超时显示
+                                               delete(t.buf, k)
+                                               t.f(roomid, v.Num, k)
+                                       }
+                               }
+                       }
+                       t.Unlock()
+               }
+       }()
+}
+
+// Do implements TargetInterface.
+func (t *danmuMerge) Do(s string) uint {
+       t.RLock()
+       defer t.RUnlock()
+
+       if t.roomid == 0 || t.f == nil {
+               return 0
+       }
+
+       { //验证是否已经存在
+               if v, ok := t.buf[s]; ok && t.now < v.Exprie {
+                       t.buf[s].Num += 1
+                       return v.Num
+               }
+       }
+       { //设置
+               t.buf[s] = &danmuMergeItem{
+                       Exprie: t.now + 8,
+                       Num:    1,
+               }
+       }
+       return 0
+}
index 05cb652d923993a6ebc8c0cda86f341fec3c7568..54db7ee13c071db3c9a37288de375a2628e3ceca 100644 (file)
 package keepMedalLight
 
 import (
-       "context"
-       "sync/atomic"
+       "container/ring"
+       "sync"
        "time"
 
-       "github.com/qydysky/bili_danmu/F"
        p "github.com/qydysky/part"
 
-       comp "github.com/qydysky/part/component"
+       comp2 "github.com/qydysky/part/component2"
 
        log "github.com/qydysky/part/log"
 )
 
-var (
-       Main = comp.NewComp(main)
-       rand = p.Rand()
-       skip atomic.Bool
-)
+// 点赞30次、发送弹幕10条,可点亮勋章3天
+// 点赞 = 1P ; 弹幕 = 3P
 
-type Func struct {
-       Uid         int
-       Logg        *log.Log_interface
-       BiliApi     F.BiliApiInter
-       SendDanmu   func(danmu string, RoomID int) error
-       PreferDanmu []any
+func init() {
+       comp2.RegisterOrPanic[interface {
+               Init(L *log.Log_interface, Roomid int, SendDanmu func(danmu string, RoomID int) error, PreferDanmu any)
+               Clear()
+               // 在所有可以发送点赞/弹幕的地方都加上,会评估是否需要点赞/弹幕,当prefer存在时,必然发送一条
+               Do(prefer ...string)
+       }](`keepMedalLight`, &keepMedalLight{})
 }
 
-func main(ctx context.Context, ptr Func) (ret any, err error) {
-       if !skip.CompareAndSwap(false, true) {
-               return
+type keepMedalLight struct {
+       roomid       int
+       log          *log.Log_interface
+       sendDanmu    func(danmu string, RoomID int) error
+       preferDanmu  []any
+       hisPointTime *ring.Ring
+       l            sync.RWMutex
+}
+
+func (t *keepMedalLight) Init(L *log.Log_interface, Roomid int, SendDanmu func(danmu string, RoomID int) error, PreferDanmu any) {
+       t.l.Lock()
+       defer t.l.Unlock()
+
+       t.roomid = Roomid
+       t.log = L
+       t.sendDanmu = SendDanmu
+       t.hisPointTime = ring.New(30)
+       if ds, ok := PreferDanmu.([]any); ok {
+               t.preferDanmu = append(t.preferDanmu, ds...)
        }
+}
 
-       ptr.Logg.L(`T: `, `开始`)
-       t := time.NewTicker(time.Second * 5)
+func (t *keepMedalLight) Clear() {
+       t.l.Lock()
+       defer t.l.Unlock()
 
-       defer func() {
-               t.Stop()
-               ptr.Logg.L(`I: `, `完成`)
-               skip.Store(false)
-       }()
+       t.roomid = 0
+}
 
-       if e, list := ptr.BiliApi.GetFansMedal(0, 0); e != nil {
-               ptr.Logg.L(`E: `, e)
-       } else {
-               for _, i := range list {
-
-                       select {
-                       case <-ctx.Done():
-                               return
-                       case <-t.C:
-                       }
-
-                       if len(ptr.PreferDanmu) > 0 {
-                               if s, ok := ptr.PreferDanmu[rand.MixRandom(0, int64(len(ptr.PreferDanmu)-1))].(string); ok {
-                                       if e := ptr.SendDanmu(s, i.RoomID); e != nil {
-                                               ptr.Logg.L(`E: `, e)
-                                       }
-                               }
-                       } else if e := ptr.SendDanmu(`点赞`, i.RoomID); e != nil {
-                               ptr.Logg.L(`E: `, e)
-                       }
+// 发送的时机
+func (t *keepMedalLight) Do(prefer ...string) {
+       t.l.Lock()
+       defer t.l.Unlock()
+
+       if t.roomid == 0 || t.sendDanmu == nil {
+               return
+       }
+
+       var waitToSend string
+
+       if len(prefer) > 0 {
+               waitToSend = prefer[0]
+       } else if d, ok := t.hisPointTime.Value.(time.Time); ok && time.Since(d) < time.Hour*24 {
+               // 环中最后一个时间在1天内
+               return
+       } else if d, ok := t.hisPointTime.Prev().Value.(time.Time); ok && time.Since(d) < time.Second*100 {
+               // 100s最多发一次
+               return
+       } else if len(t.preferDanmu) > 0 {
+               if s, ok := t.preferDanmu[p.Rand().MixRandom(0, int64(len(t.preferDanmu)-1))].(string); ok {
+                       waitToSend = s
                }
        }
 
-       if e, list := ptr.BiliApi.GetFansMedal(0, 0); e != nil {
-               ptr.Logg.L(`E: `, e)
+       if waitToSend == `` {
+               waitToSend = `点赞`
+       }
+
+       if waitToSend == `点赞` {
+               t.getPoint(1)
        } else {
-               for _, i := range list {
-
-                       select {
-                       case <-ctx.Done():
-                               return
-                       case <-t.C:
-                       }
-
-                       if i.IsLighted == 0 {
-                               if e, his := ptr.BiliApi.GetHisDanmu(i.RoomID); e != nil {
-                                       ptr.Logg.L(`E: `, e)
-                               } else if len(his) > 0 {
-                                       if e := ptr.SendDanmu(his[0], i.RoomID); e != nil {
-                                               ptr.Logg.L(`E: `, e)
-                                       }
-                               }
-                       }
-               }
+               t.getPoint(3)
        }
 
-       return
+       t.log.L(`T: `, `保持亮牌`)
+       if e := t.sendDanmu(waitToSend, t.roomid); e != nil {
+               t.log.L(`E: `, e)
+       }
+}
+
+func (t *keepMedalLight) getPoint(n int) {
+       for range n {
+               t.hisPointTime.Value = time.Now()
+               t.hisPointTime = t.hisPointTime.Next()
+       }
 }
diff --git a/Reply/F/lessDanmu/lessDanmu.go b/Reply/F/lessDanmu/lessDanmu.go
new file mode 100644 (file)
index 0000000..ed731d2
--- /dev/null
@@ -0,0 +1,109 @@
+package lessdanmu
+
+import (
+       "sync"
+
+       comp "github.com/qydysky/part/component2"
+       limit "github.com/qydysky/part/limit"
+       slice "github.com/qydysky/part/slice"
+)
+
+type TargetInterface interface {
+       Init(maxNumSec int)
+       InitRoom(roomid int)
+       Unset()
+       Do(s string) (show bool)
+}
+
+func init() {
+       if e := comp.Register[TargetInterface]("lessDanmu", &lessDanmu{
+               threshold: 0.7,
+       }); e != nil {
+               panic(e)
+       }
+}
+
+type lessDanmu struct {
+       roomid    int
+       buf       []string
+       limit     *limit.Limit
+       threshold float32
+       maxNumSec int
+       sync.RWMutex
+}
+
+// InitRoom implements TargetInterface.
+func (t *lessDanmu) InitRoom(roomid int) {
+       t.Lock()
+       defer t.Unlock()
+       t.roomid = roomid
+       clear(t.buf)
+       t.buf = t.buf[:0]
+}
+
+// Unset implements TargetInterface.
+func (t *lessDanmu) Unset() {
+       t.Lock()
+       defer t.Unlock()
+       t.roomid = 0
+}
+
+func (t *lessDanmu) Init(maxNumSec int) {
+       t.Lock()
+       defer t.Unlock()
+       t.limit = limit.New(maxNumSec, "1s", "0s")
+}
+
+// Do implements TargetInterface.
+func (t *lessDanmu) Do(s string) (show bool) {
+       t.Lock()
+       defer t.Unlock()
+
+       if t.roomid == 0 {
+               return
+       }
+
+       if len(t.buf) < 20 {
+               t.buf = append(t.buf, s)
+               return true
+       }
+       o := cross(s, t.buf)
+       if o == 1 {
+               return false
+       } //完全无用的弹幕
+
+       slice.DelFront(&t.buf, 1)
+
+       show = o < t.threshold
+
+       if show && t.maxNumSec > 0 {
+               return !t.limit.TO()
+       }
+
+       return
+}
+
+// 字符重复度检查
+// a在buf中出现的字符占a的百分数
+func cross(a string, buf []string) float32 {
+       var s float32
+       var matched bool
+       for _, v1 := range a {
+               for _, v2 := range buf {
+                       for _, v3 := range v2 {
+                               if v3 == v1 {
+                                       matched = true
+                                       break
+                               }
+                       }
+                       if matched {
+                               break
+                       }
+               }
+               if matched {
+                       s += 1
+               }
+               matched = false
+       }
+       return s / float32(len([]rune(a)))
+}
index 819f56270d985165fa3d7a82b02fe9176681bde2..7dc86104dcf0d25be8f4edda472f2647c8c162fa 100644 (file)
@@ -17,6 +17,7 @@ var msglog = c.C.Log.Base(`Msg`)
 
 // Msg类型数据处理方法map
 var Msg_map = map[string]func(replyF, string){
+       `VOICE_JOIN_SWITCH`:                    nil,
        `RECALL_DANMU_MSG`:                     nil,
        `master_qn_strategy_chg`:               nil,
        `COMMON_ANIMATION`:                     nil,
index 7dde09f6f00b26a8d625f63ac74f3903174c11dc..9dbfec698ff916b673efad1d3017b393e71b3f2e 100644 (file)
@@ -1347,12 +1347,12 @@ func (t replyF) danmu(s string) {
                if IsOn("反射弹幕机") {
                        go replyFunc.Danmuji.Danmujif(item.msg, Msg_senddanmu)
                }
-               if i := Autoskipf(item.msg); i > 0 {
+               if i := replyFunc.DanmuMerge.Do(item.msg); i > 0 {
                        danmulog.L(`I: `, item.auth, ":", item.msg)
                        return
                }
                //附加功能 更少弹幕
-               if !Lessdanmuf(item.msg) {
+               if !replyFunc.LessDanmu.Do(item.msg) {
                        danmulog.L(`I: `, item.auth, ":", item.msg)
                        return
                }
@@ -1375,12 +1375,8 @@ func (t replyF) danmu(s string) {
 // 传入字符串即可发送
 // 需要cookie
 func Msg_senddanmu(msg string) {
-       if missKey := F.CookieCheck([]string{
-               `bili_jct`,
-               `DedeUserID`,
-               `LIVE_BUVID`,
-       }); len(missKey) != 0 || c.C.Roomid == 0 {
-               msglog.L(`E: `, `c.Roomid == 0 || Cookie无Key:`, missKey)
+       if !c.C.IsLogin() || c.C.Roomid == 0 {
+               msglog.L(`E: `, `未登陆`)
                return
        }
        _ = send.Danmu_s(msg, c.C.Roomid)
@@ -1447,3 +1443,27 @@ func Itos(i []interface{}) string {
        }
        return r
 }
+
+// 弹幕合并
+var _ = replyFunc.DanmuMerge.InitSend(func(roomid int, num uint, msg string) {
+       if num > 3 {
+               c.C.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列
+                       uid: `0multi`,
+                       m: map[string]string{
+                               `{num}`: strconv.Itoa(int(num)),
+                               `{msg}`: msg,
+                       },
+               })
+               Msg_showdanmu(Danmu_item{
+                       msg:    strconv.Itoa(int(num)) + " x " + msg,
+                       uid:    `0multi`,
+                       roomid: roomid,
+               })
+       } else if num > 1 {
+               Msg_showdanmu(Danmu_item{
+                       msg:    strconv.Itoa(int(num)) + " x " + msg,
+                       uid:    `0default`,
+                       roomid: roomid,
+               })
+       }
+})
index 7b145959e4e118594edea12c971623f08b1a348b..70622d9e6b16bdebb76b37cf0a02e60c81a5192c 100644 (file)
@@ -346,13 +346,6 @@ func (t *M4SStream) fetchCheckStream() bool {
                t.stream_type = "flv"
        }
 
-       // 检查是否可以获取
-       CookieM := make(map[string]string)
-       t.common.Cookie.Range(func(k, v interface{}) bool {
-               CookieM[k.(string)] = v.(string)
-               return true
-       })
-
        var (
                noSer  []*regexp.Regexp
                noSerF = func(url string) (ban bool) {
@@ -404,7 +397,7 @@ func (t *M4SStream) fetchCheckStream() bool {
                                `Pragma`:          `no-cache`,
                                `Cache-Control`:   `no-cache`,
                                `Referer`:         "https://live.bilibili.com/",
-                               `Cookie`:          reqf.Map_2_Cookies_String(CookieM),
+                               `Cookie`:          t.common.GenReqCookie(),
                                `Connection`:      `close`,
                        },
                        Timeout:          5 * 1000,
@@ -794,7 +787,7 @@ func (t *M4SStream) saveStreamFlv() (e error) {
                                `Pragma`:          `no-cache`,
                                `Cache-Control`:   `no-cache`,
                                `Referer`:         "https://live.bilibili.com/",
-                               `Cookie`:          reqf.Map_2_Cookies_String(CookieM),
+                               `Cookie`:          t.common.GenReqCookie(),
                        },
                }); e != nil && reqf.IsTimeout(e) {
                        v.DisableAuto()
@@ -942,7 +935,7 @@ func (t *M4SStream) saveStreamFlv() (e error) {
                                        `Pragma`:          `no-cache`,
                                        `Cache-Control`:   `no-cache`,
                                        `Referer`:         "https://live.bilibili.com/",
-                                       `Cookie`:          reqf.Map_2_Cookies_String(CookieM),
+                                       `Cookie`:         t.common.GenReqCookie(),
                                },
                        })
                        if err := r.Wait(); err != nil && !errors.Is(err, io.EOF) {
index 85904c19942874a420017fba0782d5d75271ef28..1194962c08bb3bfa8d5d979066fef7b0c114abbc 100644 (file)
@@ -104,12 +104,6 @@ func Danmu_s2(data map[string]string) error {
        data[`csrf`] = csrf
        data[`csrf_token`] = csrf
 
-       Cookie := make(map[string]string)
-       c.C.Cookie.Range(func(k, v interface{}) bool {
-               Cookie[k.(string)] = v.(string)
-               return true
-       })
-
        postStr, contentType := reqf.ToForm(data)
        l.L(`I: `, "发送", data[`msg`], "至", data[`roomid`])
 
@@ -133,7 +127,7 @@ func Danmu_s2(data map[string]string) error {
                        `Pragma`:          `no-cache`,
                        `Cache-Control`:   `no-cache`,
                        `Referer`:         "https://live.bilibili.com/" + data[`roomid`],
-                       `Cookie`:          reqf.Map_2_Cookies_String(Cookie),
+                       `Cookie`:          c.C.GenReqCookie(),
                },
        })
        if err != nil {
index a540170002662df9cc87b8c681f30bb5dfd04c43..241d27a7358915256c5539ed40a4062cd8735ced 100644 (file)
@@ -58,12 +58,6 @@ func Send_gift(common *c.Common, gift_id, bag_id, gift_num int) {
                        `csrf=` + csrf + `&` +
                        `visit_id=`
 
-               Cookie := make(map[string]string)
-               common.Cookie.Range(func(k, v interface{}) bool {
-                       Cookie[k.(string)] = v.(string)
-                       return true
-               })
-
                req := common.ReqPool.Get()
                defer common.ReqPool.Put(req)
                if e := req.Reqf(reqf.Rval{
@@ -83,7 +77,7 @@ func Send_gift(common *c.Common, gift_id, bag_id, gift_num int) {
                                `Pragma`:          `no-cache`,
                                `Cache-Control`:   `no-cache`,
                                `Referer`:         "https://message.bilibili.com",
-                               `Cookie`:          reqf.Map_2_Cookies_String(Cookie),
+                               `Cookie`:          common.GenReqCookie(),
                        },
                }); e != nil {
                        log.L(`E: `, e)
index 2ba0a9e6608406e2b3a40e3792356ce8c9401987..a2648f678bd9d8c28a4a37a7d437ca595adda183 100644 (file)
@@ -50,12 +50,6 @@ func Send_pm(uid int, msg string) error {
 
        var send_str = `msg[sender_uid]=` + strconv.Itoa(c.C.Uid) + `&msg[receiver_id]=` + strconv.Itoa(uid) + `&msg[receiver_type]=1&msg[msg_type]=1&msg[msg_status]=0&msg[content]={"content":"` + msg + `"}&msg[timestamp]=` + strconv.Itoa(int(sys.Sys().GetSTime())) + `&msg[new_face_version]=0&msg[dev_id]=` + strings.ToUpper(uuid.New().String()) + `&from_firework=0&build=0&mobi_app=web&csrf_token=` + csrf + `&csrf=` + csrf
 
-       Cookie := make(map[string]string)
-       c.C.Cookie.Range(func(k, v interface{}) bool {
-               Cookie[k.(string)] = v.(string)
-               return true
-       })
-
        req := c.C.ReqPool.Get()
        defer c.C.ReqPool.Put(req)
        if e := req.Reqf(reqf.Rval{
@@ -75,7 +69,7 @@ func Send_pm(uid int, msg string) error {
                        `Pragma`:          `no-cache`,
                        `Cache-Control`:   `no-cache`,
                        `Referer`:         "https://message.bilibili.com",
-                       `Cookie`:          reqf.Map_2_Cookies_String(Cookie),
+                       `Cookie`:          c.C.GenReqCookie(),
                },
        }); e != nil {
                log.L(`E: `, e)
index 3e4a1cdbb63b147ad45fcb701ef3d7bd84f89758..a21449ce157d18f5ba1facd47037c8fba7daea03 100644 (file)
@@ -27,7 +27,6 @@ import (
        sys "github.com/qydysky/part/sys"
 
        msgq "github.com/qydysky/part/msgq"
-       reqf "github.com/qydysky/part/reqf"
        ws "github.com/qydysky/part/websocket"
 )
 
@@ -78,26 +77,18 @@ func Start(rootCtx context.Context) {
        }()
 
        {
-               //如果连接中断,则等待
-               F.KeepConnect()
-               //获取cookie
-               F.Get(c.C).Get(`Cookie`)
-               //获取LIVE_BUVID
-               //获取uid
-               F.Get(c.C).Get(`LIVE_BUVID`)
                //命令行操作 切换房间 发送弹幕
                go Cmd.Cmd()
-               // F.Get(c.C).Get(`Uid`)
-               //兑换硬币
-               F.Get(c.C).Silver_2_coin()
-               //每日签到
-               // F.Dosign()
                // 附加功能 savetojson
                reply.SaveToJson.Init()
-               // 附加功能 保持牌子点亮
-               reply.KeepMedalLight(mainCtx, c.C)
                //ass初始化
                replyFunc.Ass.Init(c.C.K_v.LoadV("Ass"))
+               //
+               if reply.IsOn(`相似弹幕忽略`) {
+                       if max_num, ok := c.C.K_v.LoadV(`每秒显示弹幕数`).(float64); ok && int(max_num) >= 1 {
+                               replyFunc.LessDanmu.Init(int(max_num))
+                       }
+               }
                //rev初始化
                if c.C.IsOn(`统计营收`) {
                        replyFunc.Rev.Init(danmulog)
@@ -131,21 +122,52 @@ func Start(rootCtx context.Context) {
                        }
                }
 
-               //使用带tag的消息队列在功能间传递消息
-               {
+               var (
+                       exitSign                     = false
+                       rs       fc.RangeSource[any] = func(yield func(any) bool) {
+                               for !exitSign {
+
+                                       if !yield(nil) {
+                                               break
+                                       }
+                               }
+                       }
+               )
+
+               for ctx := range rs.RangeCtx(mainCtx) {
+                       if c.C.Roomid == 0 {
+                               fmt.Println("回车查看指令")
+                               cancel1, ch := c.C.Danmu_Main_mq.Pull_tag_chan(`change_room`, 1, ctx)
+                               select {
+                               case roomid := <-ch:
+                                       c.C.Roomid = roomid.(int)
+                               case <-interrupt_chan:
+                                       exitSign = true
+                               }
+                               cancel1()
+                       } else {
+                               danmulog.L(`T: `, "房间号: ", strconv.Itoa(c.C.Roomid))
+                       }
+
+                       if exitSign {
+                               break
+                       }
+
+                       danmulog.L(`T: `, "准备")
+
+                       //如果连接中断,则等待
+                       F.KeepConnect()
+                       //获取cookie
+                       F.Get(c.C).Get(`Cookie`)
+                       //获取LIVE_BUVID
+                       F.Get(c.C).Get(`LIVE_BUVID`)
+                       //兑换硬币
+                       F.Get(c.C).Silver_2_coin()
+                       // 获取房间实际id
+                       c.C.Roomid = F.GetRoomRealId(c.C.Roomid)
+
+                       //使用带tag的消息队列在功能间传递消息
                        var cancelfunc = c.C.Danmu_Main_mq.Pull_tag(msgq.FuncMap{
-                               // `change_room`: func(_ any) bool { //房间改变
-                               //      c.C.Rev = 0.0     // 营收
-                               //      c.C.Renqi = 1     // 人气置1
-                               //      c.C.Watched = 0   // 观看人数
-                               //      c.C.OnlineNum = 0 // 在线人数
-                               //      c.C.GuardNum = 0  // 舰长数
-                               //      c.C.Note = ``     // 分区排行
-                               //      c.C.Uname = ``    // 主播id
-                               //      c.C.Title = ``
-                               //      c.C.Wearing_FansMedal = 0
-                               //      return false
-                               // },
                                `c.Rev_add`: func(data any) bool { //收入
                                        if rev, ok := data.(struct {
                                                Roomid int
@@ -186,8 +208,6 @@ func Start(rootCtx context.Context) {
                                },
                                `new day`: func(_ any) bool { //日期更换
                                        go func() {
-                                               //每日签到
-                                               // F.Dosign()
                                                //每日兑换硬币
                                                F.Get(c.C).Silver_2_coin()
                                                //附加功能 每日发送弹幕
@@ -198,41 +218,6 @@ func Start(rootCtx context.Context) {
                                        return false
                                },
                        })
-                       defer cancelfunc()
-               }
-
-               for exitSign := false; !exitSign; {
-                       if c.C.Roomid == 0 {
-                               fmt.Println("回车查看指令")
-                               ctx, cancel := context.WithCancel(mainCtx)
-                               cancel1, ch := c.C.Danmu_Main_mq.Pull_tag_chan(`change_room`, 1, ctx)
-                               select {
-                               case roomid := <-ch:
-                                       c.C.Roomid = roomid.(int)
-                               case <-interrupt_chan:
-                                       exitSign = true
-                               }
-                               cancel1()
-                               cancel()
-                       } else {
-                               danmulog.L(`T: `, "房间号: ", strconv.Itoa(c.C.Roomid))
-                       }
-
-                       if exitSign {
-                               break
-                       }
-
-                       danmulog.L(`T: `, "准备")
-
-                       //如果连接中断,则等待
-                       F.KeepConnect()
-                       //获取cookie,检查是否登陆失效
-                       F.Get(c.C).Get(`Cookie`)
-                       //获取LIVE_BUVID
-                       F.Get(c.C).Get(`LIVE_BUVID`)
-
-                       // 获取房间实际id
-                       c.C.Roomid = F.GetRoomRealId(c.C.Roomid)
 
                        common, _ := c.CommonsLoadOrInit.LoadOrInitPThen(c.C.Roomid)(func(actual *c.Common, loaded bool) (*c.Common, bool) {
                                if loaded {
@@ -244,17 +229,10 @@ func Start(rootCtx context.Context) {
                                return actual, loaded
                        })
 
-                       // if loaded {
-                       //      common.InIdle = false
-                       //      common.Rev = 0.0 // 营收
-                       // } else {
-                       //      common.Roomid = c.C.Roomid
-                       // }
-
-                       exitSign = entryRoom(rootCtx, mainCtx, danmulog.BaseAdd(common.Roomid), common)
+                       exitSign = entryRoom(rootCtx, ctx, danmulog.BaseAdd(common.Roomid), common)
 
+                       cancelfunc()
                        common.InIdle = true
-
                        time.Sleep(time.Second)
                }
 
@@ -266,33 +244,43 @@ func Start(rootCtx context.Context) {
 }
 
 func entryRoom(rootCtx, mainCtx context.Context, danmulog *part.Log_interface, common *c.Common) (exitSign bool) {
-       //附加功能 自动发送即将过期礼物
-       reply.AutoSend_silver_gift(common)
-       //获取热门榜
-       F.Get(common).Get(`Note`)
-
-       danmulog.L(`I: `, "连接到房间", common.Roomid)
-
-       Cookie := make(map[string]string)
-       common.Cookie.Range(func(k, v any) bool {
-               Cookie[k.(string)] = v.(string)
-               return true
-       })
-
-       // 检查与切换粉丝牌,只在cookie存在时启用
-       F.Get(common).Get(`CheckSwitch_FansMedal`)
+       var (
+               aliveT                                          = time.Now().Add(3 * time.Hour)
+               heartbeatmsg, heartinterval                     = F.Heartbeat()
+               exitloop                                        = false
+               i                                               = 0
+               rangeSource                 fc.RangeSource[any] = func(yield func(any) bool) {
+                       for !exitloop {
+                               //如果连接中断,则等待
+                               F.KeepConnect()
+                               //获取cookie,检查是否登陆失效
+                               F.Get(common).Get(`Cookie`)
+                               //获取LIVE_BUVID
+                               F.Get(common).Get(`LIVE_BUVID`)
+                               //附加功能 自动发送即将过期礼物
+                               reply.AutoSend_silver_gift(common)
+                               //获取热门榜
+                               F.Get(common).Get(`Note`)
+                               // 检查与切换粉丝牌,只在cookie存在时启用
+                               F.Get(common).Get(`CheckSwitch_FansMedal`)
+                               // 附加功能 保持牌子点亮
+                               if reply.IsOn(`保持牌子亮着`) && common.Wearing_FansMedal != 0 {
+                                       replyFunc.KeepMedalLight.Init(danmulog.Base("保持牌子点亮"), common.Roomid, send.Danmu_s, c.C.K_v.LoadV(`进房弹幕_内容`))
+                               } else {
+                                       replyFunc.KeepMedalLight.Clear()
+                               }
+                               if reply.IsOn(`相似弹幕忽略`) {
+                                       replyFunc.LessDanmu.InitRoom(common.Roomid)
+                               } else {
+                                       replyFunc.LessDanmu.Unset()
+                               }
+                               danmulog.L(`I: `, "连接到房间", common.Roomid)
+                               // 获取弹幕服务器
+                               F.Get(common).Get(`WSURL`)
 
-       // 对每个弹幕服务器尝试
-       F.Get(common).Get(`WSURL`)
-       aliveT := time.Now().Add(3 * time.Hour)
-       heartbeatmsg, heartinterval := F.Heartbeat()
+                               aliveT = time.Now().Add(3 * time.Hour)
 
-       var (
-               exitloop                        = false
-               i                               = 0
-               rangeSource fc.RangeSource[any] = func(yield func(any) bool) {
-                       for !exitloop && i < len(common.WSURL) && time.Now().Before(aliveT) {
-                               if !yield(nil) {
+                               if len(common.WSURL) == 0 || !yield(nil) {
                                        return
                                }
                        }
@@ -312,7 +300,7 @@ func entryRoom(rootCtx, mainCtx context.Context, danmulog *part.Log_interface, c
                        Func_abort_close:  func() { danmulog.L(`I: `, `服务器连接中断`) },
                        Func_normal_close: func() { danmulog.L(`I: `, `服务器连接关闭`) },
                        Header: map[string]string{
-                               `Cookie`:          reqf.Map_2_Cookies_String(Cookie),
+                               `Cookie`:          common.GenReqCookie(),
                                `Host`:            u.Hostname(),
                                `User-Agent`:      c.UA,
                                `Accept`:          `*/*`,
@@ -352,8 +340,8 @@ func entryRoom(rootCtx, mainCtx context.Context, danmulog *part.Log_interface, c
                        doneAuth := wsmsg.Pull_tag_only(`rec`, func(wm *ws.WsMsg) (disable bool) {
                                _ = wm.Msg(func(b []byte) error {
                                        if F.HelloChe(b) {
-                                       cancel()
-                               }
+                                               cancel()
+                                       }
                                        return nil
                                })
                                return true
@@ -414,24 +402,25 @@ func entryRoom(rootCtx, mainCtx context.Context, danmulog *part.Log_interface, c
                F.Get(common).Get(`GuardNum`)
                // 在线人数
                F.Get(common).Get(`getOnlineGoldRank`)
-               //验证cookie
-               if missKey := F.CookieCheck([]string{
-                       `bili_jct`,
-                       `DedeUserID`,
-                       `LIVE_BUVID`,
-               }); len(missKey) == 0 && reply.IsOn("自动弹幕机") {
-                       //附加功能 弹幕机 无cookie无法发送弹幕
+               //附加功能 弹幕机 无cookie无法发送弹幕
+               if common.IsLogin() && reply.IsOn("自动弹幕机") {
                        replyFunc.Danmuji.Danmuji_auto(ctx, c.C.K_v.LoadV(`自动弹幕机_内容`).([]any), c.C.K_v.LoadV(`自动弹幕机_发送间隔s`).(float64), reply.Msg_senddanmu)
                }
                { //附加功能 进房间发送弹幕 直播流保存 每日签到
                        F.RoomEntryAction(common.Roomid)
-                       // go F.Dosign()
                        reply.Entry_danmu(common)
+                       // go F.Dosign()
                        if _, e := recStartEnd.RecStartCheck.Run(ctx, common); e == nil {
                                reply.StreamOStart(common.Roomid)
                        } else {
                                danmulog.Base("功能", "指定房间录制区间").L(`I: `, common.Roomid, e)
                        }
+                       //弹幕合并
+                       if reply.IsOn("弹幕合并") {
+                               replyFunc.DanmuMerge.Init(ctx, common.Roomid)
+                       } else {
+                               replyFunc.DanmuMerge.Unset()
+                       }
                }
 
                //当前ws
@@ -477,6 +466,9 @@ func entryRoom(rootCtx, mainCtx context.Context, danmulog *part.Log_interface, c
                                                common.Danmu_Main_mq.Push_tag(`flash_room`, nil)
                                                return false
                                        }
+                                       if v, ok := common.K_v.LoadV("保持牌子亮着-开播时也发送").(bool); !common.Liveing || (ok && v) {
+                                               replyFunc.KeepMedalLight.Do()
+                                       }
                                        if v, ok := common.K_v.LoadV("下播后不记录人气观看人数").(bool); ok && v && !common.Liveing {
                                                return false
                                        }
index 6caaaf965c141845ad30621dad809287fdfebe52..e2d156d25ae8159504cf1027522c8ed1405e5bd0 100644 (file)
@@ -40,7 +40,7 @@
     "弹幕_识别表情代码": true,
     "发送还有几天过期的礼物": 3,
     "保持牌子亮着": true,
-    "保持牌子亮着_指定时间": "00:00:00",
+    "保持牌子亮着-开播时也发送": false,
     "弹幕输出到日志": true,
     "保存弹幕至db": {
         "dbname": "",
diff --git a/go.mod b/go.mod
index 8b0aa4d7780c3a0a8994d82f70e5dc516ea17d4e..d7310f98ebc7e2efad808ee781e606b20f6da41f 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.24
 require (
        github.com/gotk3/gotk3 v0.6.4
        github.com/mdp/qrterminal/v3 v3.2.0
-       github.com/qydysky/part v0.28.20250508122622
+       github.com/qydysky/part v0.28.20250509205808
        github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
        github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
        golang.org/x/text v0.24.0 // indirect
@@ -13,7 +13,7 @@ require (
 
 require (
        github.com/google/uuid v1.6.0
-       github.com/qydysky/biliApi v0.0.0-20250506170139-ecbeaf602c40
+       github.com/qydysky/biliApi v0.0.0-20250509210314-8407d638b123
        github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9
        golang.org/x/exp v0.0.0-20250215185904-eff6e970281f
 )
diff --git a/go.sum b/go.sum
index 919bef2afc0725a26fce238e352460af2e8b6201..7e0d856ed8a9d3102a4c0eab1cb04e17dbc4c56d 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -42,12 +42,12 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh
 github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/qydysky/biliApi v0.0.0-20250506170139-ecbeaf602c40 h1:QtROBClET9rmSkWgfsC+M9BAkPJQ9y1CcxAYuAkvDdM=
-github.com/qydysky/biliApi v0.0.0-20250506170139-ecbeaf602c40/go.mod h1:1FbgCj+aOwIvuRRuX/l5uTLb3JIwWyJSa0uEfwpYV/8=
+github.com/qydysky/biliApi v0.0.0-20250509210314-8407d638b123 h1:YXqqrwvzFwfs8LoierhEIKRdPQnTNwmqT91yZ6exvbA=
+github.com/qydysky/biliApi v0.0.0-20250509210314-8407d638b123/go.mod h1:1FbgCj+aOwIvuRRuX/l5uTLb3JIwWyJSa0uEfwpYV/8=
 github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9 h1:k451T+bpsLr+Dq9Ujo+Qtx0iomRA1XXS5ttlEojvfuQ=
 github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9/go.mod h1:cI8/gy/wjy2Eb+p2IUj2ZuDnC8R5Vrx3O0VMPvMvphA=
-github.com/qydysky/part v0.28.20250508122622 h1:D7+SQiUE8MANSTHTYNHH+MEgrhceX3hG9FFrpXvDSd8=
-github.com/qydysky/part v0.28.20250508122622/go.mod h1:wp71PQdKYcg9jn9yDDvqC4shS/kzejyvFqbfUxuHocY=
+github.com/qydysky/part v0.28.20250509205808 h1:vyv+TA9uK9IV4n0J5nuACqrARESX2HphoLgslecubyw=
+github.com/qydysky/part v0.28.20250509205808/go.mod h1:wp71PQdKYcg9jn9yDDvqC4shS/kzejyvFqbfUxuHocY=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=