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
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
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,
t.PID = os.Getpid()
t.Version = strings.TrimSpace(version)
t.StartT = time.Now()
+ t.Cookie = &syncmap.Map{}
t.AllStreamType = map[string]StreamType{
`fmp4`: {
)
// 需要先判断path存在cookie文件
+//
+// 从文件获取cookie到缓存
func CookieGet(path string) []byte {
clog := clog.Base_add(`获取`)
}
}
+// 保存到cookie到缓存及文件
func CookieSet(path string, source []byte) {
clog := clog.Base_add(`设置`)
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
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"
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
},
})
//GetDanmuInfo
if err, res := biliApi.GetDanmuInfo(t.Roomid); err != nil {
+ t.Token = ""
+ t.WSURL = t.WSURL[:0]
apilog.L(`E: `, err)
return
} else {
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
}
}
{ //循环查看是否通过
- 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-- {
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
}
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
}
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 {
// 签到活动已下线
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
}
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 {
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
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
}
//交换
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"`
}) {
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() {
// 进入房间
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() {
}) {
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() {
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)
```
#### 保持粉丝牌点亮
-配置文件中添加配置项`保持牌子亮着_指定时间`(>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目录下,并在回放时显示表情。
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
+ p "github.com/qydysky/part"
psql "github.com/qydysky/part/sql"
_ "modernc.org/sqlite"
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"
// 字符重复度检查
// 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 {
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
目标:弹幕机自动发送弹幕
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))
}
}
// 获取当前房间的
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
_ "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"
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)
--- /dev/null
+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
+}
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()
+ }
}
--- /dev/null
+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)))
+}
// 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,
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
}
// 传入字符串即可发送
// 需要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)
}
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,
+ })
+ }
+})
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) {
`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,
`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()
`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) {
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`])
`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 {
`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{
`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)
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{
`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)
sys "github.com/qydysky/part/sys"
msgq "github.com/qydysky/part/msgq"
- reqf "github.com/qydysky/part/reqf"
ws "github.com/qydysky/part/websocket"
)
}()
{
- //如果连接中断,则等待
- 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)
}
}
- //使用带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
},
`new day`: func(_ any) bool { //日期更换
go func() {
- //每日签到
- // F.Dosign()
//每日兑换硬币
F.Get(c.C).Silver_2_coin()
//附加功能 每日发送弹幕
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 {
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)
}
}
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
}
}
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`: `*/*`,
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
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
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
}
"弹幕_识别表情代码": true,
"发送还有几天过期的礼物": 3,
"保持牌子亮着": true,
- "保持牌子亮着_指定时间": "00:00:00",
+ "保持牌子亮着-开播时也发送": false,
"弹幕输出到日志": true,
"保存弹幕至db": {
"dbname": "",
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
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
)
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=