From d7cf1d6be4c9bdff772213fe717ba79d3565e4bf Mon Sep 17 00:00:00 2001 From: qydysky Date: Sat, 19 Sep 2020 09:36:23 +0800 Subject: [PATCH] 14 --- .gitignore | 3 +- F.go | 154 +++++++++++++++++++++++++++++++++++++++----------- Msg.go | 99 ++++++++++++++++++-------------- bili_danmu.go | 68 +++++++++++++--------- 4 files changed, 220 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index beb92ad..4c635db 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ demo/cookie.txt player-loader-2.0.4.min.js pv-tracker.js demo/Autoban.txt -demo/his/danmu.log +demo/his/*.log danmu.log +msg_sample/*.json diff --git a/F.go b/F.go index c2a9aa7..c7a2a4c 100644 --- a/F.go +++ b/F.go @@ -1,8 +1,9 @@ package bili_danmu import ( + // "fmt" "strconv" - "bytes" + "strings" "sync" "time" @@ -11,7 +12,7 @@ import ( //功能开关 var AllF = map[string]bool{ - "Autoban":false,//自动封禁(仅提示,未完成) + "Autoban":true,//自动封禁(仅提示,未完成) "Danmuji":true,//反射型弹幕机,回应弹幕 "Danmuji_auto":false,//自动型弹幕机,定时输出 "Autoskip":true,//刷屏缩减,相同合并 @@ -26,10 +27,36 @@ func IsOn(s string) bool { } return false } +//公共 +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 selfcross(a string) (float32) { + buf := make(map[rune]bool) + for _,v := range a { + if _,ok := buf[v]; !ok { + buf[v] = true + } + } + return 1 - float32(len(buf)) / float32(len([]rune(a))) +} //功能区 type Autoban struct { - buf []byte + Banbuf []string + buf []string Inuse bool } @@ -37,22 +64,48 @@ var autoban = Autoban { Inuse:IsOn("Autoban"), } -func Autobanf(s string) float32 { - if !autoban.Inuse {return 0} +func Autobanf(s string) bool { + if !autoban.Inuse {return false} - if len(autoban.buf) == 0 { + if len(autoban.Banbuf) == 0 { f := p.File().FileWR(p.Filel{ File:"Autoban.txt", - Write:false, }) - autoban.buf = []byte(f) + + for _,v := range strings.Split(f, "\n") { + autoban.Banbuf = append(autoban.Banbuf, v) + } } - var scop int - for _, v := range []byte(s) { - if bytes.Contains(autoban.buf, []byte{v}) {scop += 1} + if len(autoban.buf) < 10 { + autoban.buf = append(autoban.buf, s) + return false } - return float32(scop) / float32(len(s)) + defer func(){ + autoban.buf = append(autoban.buf[1:], s) + }() + + var res []float32 + + pt := float32(len([]rune(s))) + if pt <= 3 {return false}//字数过少去除 + res = append(res, pt) + + pt = selfcross(s); + if pt > 0.6 {return false}//自身重复高去除 + res = append(res, pt) + + pt = cross(s, autoban.buf); + if pt < 0.7 {return false}//历史重复低去除 + res = append(res, pt) + + pt = cross(s, autoban.Banbuf); + if pt < 0.8 {return false}//ban字符重复低去除 + res = append(res, pt) + + l := p.Logf().New().Open("danmu.log").Base(1, "autoban") + l.W(res) + return true } type Danmuji struct { @@ -140,34 +193,18 @@ var lessdanmu = Lessdanmu{ Inuse:IsOn("Lessdanmu"), } -func Lessdanmuf(s string, bufsize int, drop float32) bool { - if !lessdanmu.Inuse {return false} +func Lessdanmuf(s string, bufsize int) float32 { + if !lessdanmu.Inuse {return 0} if len(lessdanmu.buf) < bufsize { lessdanmu.buf = append(lessdanmu.buf, s) - return false + return 0 } o := cross(s, lessdanmu.buf) - if o == 1 {return true}//完全无用 + if o == 1 {return 1}//完全无用 lessdanmu.buf = append(lessdanmu.buf[1:], s) - - return o > drop -} -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))) + return o } /* @@ -175,7 +212,56 @@ func cross(a string,buf []string) (float32) { 目标:弹幕机自动发送弹幕 原理:留存弹幕,称为buf。将当前若干弹幕在buf中的位置找出,根据位置聚集情况及该位置出现语句的频率,选择发送的弹幕 */ -type Moredanmu struct {} +// type Moredanmu struct { +// Inuse bool +// buf []string +// } + +// var moredanmu = Moredanmu{ +// Inuse:IsOn("Moredanmu"), +// } +// func moredanmuf(s string) { +// if !moredanmu.Inuse {return} +// // if len(moredanmu.buf) < bufsize { +// moredanmu.buf = append(moredanmu.buf, s) +// // } + +// // b := p.Buf("danmu.buf").Load() +// // if b.Get() != nil { +// // moredanmu.buf = *b.Get() +// // } +// } + +// func moredanmu_get(tb []string) { +// if !moredanmu.Inuse {return} + +// var tmp string +// for _,v := range tb { +// tmp += v +// } +// // for _,v := range tb { +// // tmp += len([]rune(v[:len(v)-1])) +// // } + +// var max float32 +// var loc int +// for i := 0; len(moredanmu.buf) >= i + len(tb); i++ { +// if m := cross(tmp, moredanmu.buf[i:i + len(tb)]);m > max { +// max = m +// loc = i +// } +// } +// if loc != 0 { +// p := moredanmu.buf[loc:loc + len(tb)] +// for i,v := range p{ +// if m := cross(v, p);m > max { +// max = m +// loc = i +// } +// } +// fmt.Println(len(moredanmu.buf),"=>",p[loc]) +// } +// } type Shortdanmu struct { Inuse bool diff --git a/Msg.go b/Msg.go index 5d08e7b..27b473e 100644 --- a/Msg.go +++ b/Msg.go @@ -2,8 +2,6 @@ package bili_danmu import ( "fmt" - "bytes" - "compress/zlib" p "github.com/qydysky/part" ) @@ -32,7 +30,8 @@ var Msg_map = map[string]func(replayF, string) { "ROOM_RANK":nil, "ROOM_SHIELD":nil, "USER_TOAST_MSG":nil, - "WIN_ACTIVITY":nil, + "WIN_ACTIVITY":replayF.win_activity,//活动 + "SPECIAL_GIFT":replayF.special_gift,//节奏风暴 "GUARD_BUY":replayF.guard_buy,//大航海购买 "WELCOME_GUARD":replayF.welcome_guard,//大航海进入 "DANMU_MSG":replayF.danmu,//弹幕 @@ -50,39 +49,18 @@ var Msg_map = map[string]func(replayF, string) { "ROOM_REAL_TIME_MESSAGE_UPDATE":nil,//replayF.roominfo,//粉丝数 } -func Msg(b []byte, compress bool) { - if compress { - readc, err := zlib.NewReader(bytes.NewReader(b[16:])) - if err != nil {msglog.E("解压错误");return} - defer readc.Close() - - buf := bytes.NewBuffer(nil) - if _, err := buf.ReadFrom(readc);err != nil {msglog.E("解压错误");return} - b = buf.Bytes() - } - - for len(b) != 0 { - - var packL int32 - if ist, packl := headChe(b[:16], len(b), WS_BODY_PROTOCOL_VERSION_NORMAL, WS_OP_MESSAGE, 0, 0); !ist { - msglog.E("头错误");return - } else { - packL = packl - } +func Msg(b []byte) { + s := string(b) + if cmd := p.Json().GetValFromS(s, "cmd");cmd == nil { + msglog.E("cmd", s) + return + } else { + var f replayF - s := string(b[16:packL]) - b = b[packL:] - if cmd := p.Json().GetValFromS(s, "cmd");cmd == nil { - msglog.E("cmd", s) - return + if F, ok := Msg_map[cmd.(string)]; ok { + if F != nil {F(f, s)} } else { - var f replayF - - if F, ok := Msg_map[cmd.(string)]; ok { - if F != nil {F(f, s)} - } else { - f.defaultMsg(s) - } + f.defaultMsg(s) } } @@ -95,6 +73,39 @@ func (replayF) defaultMsg(s string){ msglog.Base(1, "Unknow cmd").E(s) } +func (replayF) win_activity(s string){ + msglog.Fileonly(true) + defer msglog.Fileonly(false) + + title := p.Json().GetValFromS(s, "data.title"); + + fmt.Println("活动", title, "已开启") + msglog.Base(1, "房").I("活动", title, "已开启") +} + +func (replayF) special_gift(s string){ + msglog.Fileonly(true) + defer msglog.Fileonly(false) + + content := p.Json().GetValFromS(s, "data.39.content"); + action := p.Json().GetValFromS(s, "data.39.action"); + + var sh []interface{} + + if action != nil && action.(string) == "end" { + return + } + if content != nil { + sh = append(sh, "节奏风暴", content, "¥ 100") + } + + fmt.Println("\n====") + fmt.Println(sh...) + fmt.Println("====\n") + msglog.Base(1, "礼").I(sh...) + +} + func (replayF) guard_buy(s string){ msglog.Fileonly(true).Base(-1, "礼") defer msglog.Base(0).Fileonly(false) @@ -115,9 +126,9 @@ func (replayF) guard_buy(s string){ sh = append(sh, "¥", int(price.(float64)) / 1000) } - fmt.Println("====") + fmt.Println("\n====") fmt.Println(sh...) - fmt.Println("====") + fmt.Println("====\n") msglog.I(sh...) } @@ -198,9 +209,9 @@ func (replayF) send_gift(s string){ //小于3万金瓜子 if allprice < 30000 {msglog.T(sh...);return} - fmt.Println("====") + fmt.Println("\n====") fmt.Println(sh...) - fmt.Println("====") + fmt.Println("====\n") msglog.I(sh...) } @@ -270,9 +281,9 @@ func (replayF) super_chat_message(s string){ msglog.Fileonly(true) defer msglog.Fileonly(false) - fmt.Println("====") + fmt.Println("\n====") fmt.Println(sh...) - fmt.Println("====") + fmt.Println("====\n") msglog.Base(1, "礼").I(sh...) } @@ -345,7 +356,8 @@ func (replayF) danmu(s string) { //F附加方法 Danmujif(msg, Msg_cookie, Msg_roomid) - if Autobanf(msg) > 0.5 { + if Autobanf(msg) { + fmt.Println("风险", msg) msglog.Base(1, "风险").I(msg) return } @@ -355,14 +367,15 @@ func (replayF) danmu(s string) { } Msg_showdanmu(auth, msg) - msglog.I(auth, ":", msg) } } func Msg_showdanmu(auth interface{}, msg string) { - if Lessdanmuf(msg, 20, 0.5) {//与前20条弹幕重复的字数占比度>0.5的屏蔽 + if Lessdanmuf(msg, 20) > 0.7 {//与前20条弹幕重复的字数占比度>0.7的屏蔽 if auth != nil {msglog.I(auth, ":", msg)} return } + fmt.Println(Shortdanmuf(msg)) + if auth != nil {msglog.I(auth, ":", msg)} } \ No newline at end of file diff --git a/bili_danmu.go b/bili_danmu.go index dc5fc3e..bf273e2 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -6,6 +6,7 @@ import ( "strconv" "os" "os/signal" + "compress/zlib" p "github.com/qydysky/part" ) @@ -116,22 +117,43 @@ const ( //返回数据分派 func Reply(b []byte) { - danmulog.Base(-1, "返回分派") - defer danmulog.Base(0) + danmulog.Base(-1, "返回分派").Level(4) + defer danmulog.Base(0).Level(LogLevel) - if ist, _ := headChe(b[:16], len(b), WS_BODY_PROTOCOL_VERSION_DEFLATE, WS_OP_MESSAGE, 0, 4); ist { - Msg(b, true);return - } - if ist, _ := headChe(b[:16], len(b), WS_BODY_PROTOCOL_VERSION_NORMAL, WS_OP_MESSAGE, 0, 4); ist { - Msg(b, false);return + head := headChe(b[:16]) + if int(head.packL) > len(b) {danmulog.E("包缺损");return} + + if head.BodyV == WS_BODY_PROTOCOL_VERSION_DEFLATE { + readc, err := zlib.NewReader(bytes.NewReader(b[16:])) + if err != nil {danmulog.E("解压错误");return} + defer readc.Close() + + buf := bytes.NewBuffer(nil) + if _, err := buf.ReadFrom(readc);err != nil {danmulog.E("解压错误");return} + b = buf.Bytes() } - if ist, _ := headChe(b[:16], len(b), WS_HEADER_DEFAULT_VERSION, WS_OP_HEARTBEAT_REPLY, WS_HEADER_DEFAULT_SEQUENCE, 4); ist { - danmulog.T("heartbeat replay!"); - return + for len(b) != 0 { + head := headChe(b[:16]) + if int(head.packL) > len(b) {danmulog.E("包缺损");return} + + contain := b[16:head.packL] + switch head.OpeaT { + case WS_OP_MESSAGE:Msg(contain) + case WS_OP_HEARTBEAT_REPLY:danmulog.T("heartbeat replay!") + default :danmulog.T("unknow reply", contain) + } + + b = b[head.packL:] } +} - danmulog.T("unknow reply", b) +type header struct { + packL int32 + headL int16 + BodyV int16 + OpeaT int32 + Seque int32 } //头部生成与检查 @@ -147,29 +169,23 @@ func headGen(datalenght,Opeation,Sequence int) []byte { return buffer.Bytes() } -func headChe(head []byte, datalenght,Bodyv,Opeation,Sequence,show int) (bool,int32) { - danmulog.Base(-1, "头部检查") - defer danmulog.Base(0) +func headChe(head []byte) (header) { - if len(head) != WS_PACKAGE_HEADER_TOTAL_LENGTH {return false, 0} - - danmulog.Level(show) - defer danmulog.Level(LogLevel) + if len(head) != WS_PACKAGE_HEADER_TOTAL_LENGTH {danmulog.Base(1, "头部检查").E("输入头长度错误");return header{}} - packL := Btoi32(head[:4]) headL := Btoi16(head[4:6]) BodyV := Btoi16(head[6:8]) OpeaT := Btoi32(head[8:12]) Seque := Btoi32(head[12:16]) - if packL > int32(datalenght) {danmulog.E("包缺损", packL, datalenght);return false, packL} - if headL != WS_PACKAGE_HEADER_TOTAL_LENGTH {danmulog.E("头错误", headL);return false, packL} - if OpeaT != int32(Opeation) {danmulog.E("类型错误");return false, packL} - if Seque != int32(Sequence) {danmulog.E("Seq错误");return false, packL} - if BodyV != int16(Bodyv) {danmulog.E("压缩算法错误");return false, packL} - - return true, packL + return header{ + packL :packL, + headL :headL, + BodyV :BodyV, + OpeaT :OpeaT, + Seque :Seque, + } } //认证生成与检查 -- 2.39.2