From: qydysky Date: Sat, 10 May 2025 05:28:46 +0000 (+0800) Subject: Fix 保持亮牌仅限于当前房间 #197 (#200) X-Git-Tag: v0.18.0~5 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=47ea2c8c73fefce126f18c9c2a17ddc3eb68fa79;p=bili_danmu%2F.git Fix 保持亮牌仅限于当前房间 #197 (#200) * Fix 保持亮牌仅限于当前房间 #197 * Fix 保持亮牌仅限于当前房间 #197 * Improve 优化代码 * Improve 优化代码 * Improve 优化代码 * Improve 优化代码 * Add msg VOICE_JOIN_SWITCH * Improve 在无命令行房间参数时 可以无网络启动 --- diff --git a/CV/Var.go b/CV/Var.go index e152a97..d7f8235 100644 --- 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`: { diff --git a/F/CookieCrypo.go b/F/CookieCrypo.go index 487e2a0..1e52c14 100644 --- a/F/CookieCrypo.go +++ b/F/CookieCrypo.go @@ -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 34d3ed0..9d54bf8 100644 --- 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 diff --git a/F/api.go b/F/api.go index fc9acd2..da3411e 100644 --- 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() { diff --git a/F/biliApiInterface.go b/F/biliApiInterface.go index eed35c5..5931062 100644 --- a/F/biliApiInterface.go +++ b/F/biliApiInterface.go @@ -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) diff --git a/README.md b/README.md index bf797df..f729b46 100644 --- 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目录下,并在回放时显示表情。 diff --git a/Reply/F.go b/Reply/F.go index ccd6bd2..c188b5d 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -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 diff --git a/Reply/F/comp.go b/Reply/F/comp.go index 2b923e5..c8a210a 100644 --- a/Reply/F/comp.go +++ b/Reply/F/comp.go @@ -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 index 0000000..1d071c2 --- /dev/null +++ b/Reply/F/danmuMerge/danmuMerge.go @@ -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 +} diff --git a/Reply/F/keepMedalLight/keepMedalLight.go b/Reply/F/keepMedalLight/keepMedalLight.go index 05cb652..54db7ee 100644 --- a/Reply/F/keepMedalLight/keepMedalLight.go +++ b/Reply/F/keepMedalLight/keepMedalLight.go @@ -1,91 +1,102 @@ 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 index 0000000..ed731d2 --- /dev/null +++ b/Reply/F/lessDanmu/lessDanmu.go @@ -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))) +} diff --git a/Reply/Msg.go b/Reply/Msg.go index 819f562..7dc8610 100644 --- a/Reply/Msg.go +++ b/Reply/Msg.go @@ -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, diff --git a/Reply/Reply.go b/Reply/Reply.go index 7dde09f..9dbfec6 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -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, + }) + } +}) diff --git a/Reply/stream.go b/Reply/stream.go index 7b14595..70622d9 100644 --- a/Reply/stream.go +++ b/Reply/stream.go @@ -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) { diff --git a/Send/Send.go b/Send/Send.go index 85904c1..1194962 100644 --- a/Send/Send.go +++ b/Send/Send.go @@ -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 { diff --git a/Send/Send_gift.go b/Send/Send_gift.go index a540170..241d27a 100644 --- a/Send/Send_gift.go +++ b/Send/Send_gift.go @@ -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) diff --git a/Send/Send_pm.go b/Send/Send_pm.go index 2ba0a9e..a2648f6 100644 --- a/Send/Send_pm.go +++ b/Send/Send_pm.go @@ -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) diff --git a/bili_danmu.go b/bili_danmu.go index 3e4a1cd..a21449c 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -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 } diff --git a/demo/config/config_K_v.json b/demo/config/config_K_v.json index 6caaaf9..e2d156d 100644 --- a/demo/config/config_K_v.json +++ b/demo/config/config_K_v.json @@ -40,7 +40,7 @@ "弹幕_识别表情代码": true, "发送还有几天过期的礼物": 3, "保持牌子亮着": true, - "保持牌子亮着_指定时间": "00:00:00", + "保持牌子亮着-开播时也发送": false, "弹幕输出到日志": true, "保存弹幕至db": { "dbname": "", diff --git a/go.mod b/go.mod index 8b0aa4d..d7310f9 100644 --- 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 919bef2..7e0d856 100644 --- 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=