From 2949f68532633f438bac7d80216ce7ab6d6b226c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 22 Aug 2020 13:24:32 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=8F=8D=E5=90=91WS=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/filter.go | 248 ++++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + server/websocket.go | 6 ++ 4 files changed, 257 insertions(+) create mode 100644 global/filter.go diff --git a/global/filter.go b/global/filter.go new file mode 100644 index 0000000..c439bd2 --- /dev/null +++ b/global/filter.go @@ -0,0 +1,248 @@ +package global + +import ( + "bytes" + "github.com/buger/jsonparser" + log "github.com/sirupsen/logrus" + "io/ioutil" + "regexp" + "sync" +) + +type Filter interface { + Eval(payload []byte) bool +} + +type OperationNode struct { + key string + filter Filter +} + +type NotOperator struct { + operand_ Filter +} + +func notOperatorConstruct(argument []byte) *NotOperator { + op := new(NotOperator) + op.operand_ = GetOperatorFactory().Generate("and", argument) + return op +} + +func (notOperator NotOperator) Eval(payload []byte) bool { + log.Debug("not "+string(payload)) + return !(notOperator.operand_).Eval(payload) +} + +type AndOperator struct { + operands []OperationNode +} + +func andOperatorConstruct(argument []byte) *AndOperator { + op := new(AndOperator) + _ = jsonparser.ObjectEach(argument, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { + if key[0] == '.' { + // is an operator + // ".foo": { + // "bar": "baz" + // } + opKey := string(key[1:]) + op.operands = append(op.operands, OperationNode{"", GetOperatorFactory().Generate(opKey, value)}) + } else if value[0] == '{' { + // is an normal key with an object as the value + // "foo": { + // ".bar": "baz" + // } + opKey := string(key) + op.operands = append(op.operands, OperationNode{opKey, GetOperatorFactory().Generate("and", value)}) + } else { + // is an normal key with a non-object as the value + // "foo": "bar" + opKey := string(key) + op.operands = append(op.operands, OperationNode{opKey, GetOperatorFactory().Generate("eq", value)}) + } + return nil + }) + return op +} + +func (andOperator *AndOperator) Eval(payload []byte) bool { + log.Debug("and "+string(payload)) + res := true + nodesLength := len(andOperator.operands) + for i := 0; i < nodesLength ; i++ { + + if len(andOperator.operands[i].key) == 0 { + // is an operator + res = res && andOperator.operands[i].filter.Eval(payload) + } else { + // is an normal key + val, _, _, _ := jsonparser.Get(payload, andOperator.operands[i].key) + res = res && andOperator.operands[i].filter.Eval(val) + } + + if res == false { + break + } + } + return res +} + +type OrOperator struct { + operands []Filter +} + +func orOperatorConstruct(argument []byte) *OrOperator { + op := new(OrOperator) + _, _ = jsonparser.ArrayEach(argument, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + op.operands = append(op.operands, GetOperatorFactory().Generate("and", value)) + }) + return op +} + +func (orOperator OrOperator) Eval(payload []byte) bool { + log.Debug("or "+string(payload)) + res:= false + nodesLength := len(orOperator.operands) + for i := 0; i < nodesLength ; i++ { + res = res || orOperator.operands[i].Eval(payload) + + if res == true { + break + } + } + return res +} + +type EqualOperator struct { + value []byte +} + +func equalOperatorConstruct(argument []byte) *EqualOperator { + op := new(EqualOperator) + op.value = argument + return op +} + +func (equalOperator EqualOperator) Eval(payload []byte) bool { + log.Debug("eq "+string(payload)) + return bytes.Equal(payload, equalOperator.value) +} + +type NotEqualOperator struct { + value []byte +} + +func notEqualOperatorConstruct(argument []byte) *NotEqualOperator { + op := new(NotEqualOperator) + op.value = argument + return op +} + +func (notEqualOperator NotEqualOperator) Eval(payload []byte) bool { + log.Debug("neq "+string(payload)) + return !bytes.Equal(payload, notEqualOperator.value) +} + + +type InOperator struct { + operands [][]byte +} + +func inOperatorConstruct(argument []byte) *InOperator { + op := new(InOperator) + _, _ = jsonparser.ArrayEach(argument, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + op.operands = append(op.operands, value) + }) + return op +} + +func (inOperator InOperator) Eval(payload []byte) bool { + log.Debug("in "+string(payload)) + res := false + for _, v := range inOperator.operands { + res = res || bytes.Equal(payload, v) + if res == true { + break + } + } + return res +} + +type ContainsOperator struct { + operand []byte +} + +func containsOperatorConstruct(argument []byte) *ContainsOperator { + op := new(ContainsOperator) + op.operand = argument + return op +} + +func (containsOperator ContainsOperator) Eval(payload []byte) bool { + log.Debug("contains "+string(payload)) + return bytes.Contains(payload, containsOperator.operand) +} + +type RegexOperator struct { + regex string +} + +func regexOperatorConstruct(argument []byte) *RegexOperator { + op := new(RegexOperator) + op.regex = string(argument) + return op +} + +func (containsOperator RegexOperator) Eval(payload []byte) bool { + log.Debug("regex "+string(payload)) + matched, _ := regexp.Match(containsOperator.regex, payload) + return matched +} +// 单例工厂 +type operatorFactory struct{ +} + +var instance *operatorFactory = &operatorFactory{} + +func GetOperatorFactory() *operatorFactory { + return instance +} + +func (o operatorFactory) Generate(opName string, argument []byte) Filter { + switch opName { + case "not": + return notOperatorConstruct(argument) + case "and": + return andOperatorConstruct(argument) + case "or": + return orOperatorConstruct(argument) + case "neq": + return notEqualOperatorConstruct(argument) + case "eq": + return equalOperatorConstruct(argument) + case "in": + return inOperatorConstruct(argument) + case "contains": + return containsOperatorConstruct(argument) + case "regex": + return regexOperatorConstruct(argument) + default: + log.Warnf("the operator '%s' is not supported", opName) + return nil + } +} + +var filter = new(Filter) +var once sync.Once // 过滤器单例模式 + +func GetFilter() *Filter { + once.Do(func() { + f, err := ioutil.ReadFile("filter.json") + if err != nil { + filter = nil + } else { + *filter = GetOperatorFactory().Generate("and", f) + } + }) + return filter +} \ No newline at end of file diff --git a/go.mod b/go.mod index e8c0fed..8dcdefe 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( github.com/Mrs4s/MiraiGo v0.0.0-20200821182324-7654a7a2a106 + github.com/buger/jsonparser v1.0.0 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/gin-gonic/gin v1.6.3 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index ebab85a..dd1f0b7 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Mrs4s/MiraiGo v0.0.0-20200821164244-fe8e98b9d8c5 h1:LVzj3ahW0LYJQMDcD github.com/Mrs4s/MiraiGo v0.0.0-20200821164244-fe8e98b9d8c5/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM= github.com/Mrs4s/MiraiGo v0.0.0-20200821182324-7654a7a2a106 h1:z2oaXbvOmu7/O666OkUeH5SGUjyQ3wUSNjtTOagY8Sk= github.com/Mrs4s/MiraiGo v0.0.0-20200821182324-7654a7a2a106/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM= +github.com/buger/jsonparser v1.0.0 h1:etJTGF5ESxjI0Ic2UaLQs2LQQpa8G9ykQScukbh4L8A= +github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/server/websocket.go b/server/websocket.go index 7faea24..d64093b 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -172,6 +172,12 @@ func (c *websocketClient) listenApi(conn *websocketConn, u bool) { } func (c *websocketClient) onBotPushEvent(m coolq.MSG) { + payload := []byte(m.ToJson()) + filter := global.GetFilter() + if filter != nil && (*filter).Eval(payload) == false { + log.Debug("Event filtered!") + return + } if c.eventConn != nil { log.Debugf("向WS服务器 %v 推送Event: %v", c.eventConn.RemoteAddr().String(), m.ToJson()) c.eventConn.Lock() From 5868b68437c11ea752c3227e893f256f5f7479cf Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 22 Aug 2020 16:14:26 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=B8=8E=E5=8E=9F=E7=89=88event=20filter?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/filter.go | 169 ++++++++++++++++++++++++-------------------- go.mod | 1 - go.sum | 2 - server/websocket.go | 3 +- 4 files changed, 96 insertions(+), 79 deletions(-) diff --git a/global/filter.go b/global/filter.go index c439bd2..e7fdad6 100644 --- a/global/filter.go +++ b/global/filter.go @@ -1,16 +1,16 @@ package global import ( - "bytes" - "github.com/buger/jsonparser" log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" "io/ioutil" "regexp" + "strings" "sync" ) type Filter interface { - Eval(payload []byte) bool + Eval(payload gjson.Result) bool } type OperationNode struct { @@ -22,14 +22,17 @@ type NotOperator struct { operand_ Filter } -func notOperatorConstruct(argument []byte) *NotOperator { +func notOperatorConstruct(argument gjson.Result) *NotOperator { + if !argument.IsObject() { + log.Error("the argument of 'not' operator must be an object") + } op := new(NotOperator) op.operand_ = GetOperatorFactory().Generate("and", argument) return op } -func (notOperator NotOperator) Eval(payload []byte) bool { - log.Debug("not "+string(payload)) +func (notOperator NotOperator) Eval(payload gjson.Result) bool { + log.Debug("not " + payload.Str) return !(notOperator.operand_).Eval(payload) } @@ -37,47 +40,49 @@ type AndOperator struct { operands []OperationNode } -func andOperatorConstruct(argument []byte) *AndOperator { +func andOperatorConstruct(argument gjson.Result) *AndOperator { + if !argument.IsObject() { + log.Error("the argument of 'and' operator must be an object") + } op := new(AndOperator) - _ = jsonparser.ObjectEach(argument, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { - if key[0] == '.' { + argument.ForEach(func(key, value gjson.Result) bool { + if key.Str[0] == '.' { // is an operator // ".foo": { // "bar": "baz" // } - opKey := string(key[1:]) + opKey := key.Str[1:] op.operands = append(op.operands, OperationNode{"", GetOperatorFactory().Generate(opKey, value)}) - } else if value[0] == '{' { + } else if value.IsObject() { // is an normal key with an object as the value // "foo": { // ".bar": "baz" // } - opKey := string(key) + opKey := key.Str op.operands = append(op.operands, OperationNode{opKey, GetOperatorFactory().Generate("and", value)}) } else { // is an normal key with a non-object as the value // "foo": "bar" - opKey := string(key) + opKey := key.Str op.operands = append(op.operands, OperationNode{opKey, GetOperatorFactory().Generate("eq", value)}) } - return nil + return true }) return op } -func (andOperator *AndOperator) Eval(payload []byte) bool { - log.Debug("and "+string(payload)) +func (andOperator *AndOperator) Eval(payload gjson.Result) bool { + log.Debug("and " + payload.Str) res := true - nodesLength := len(andOperator.operands) - for i := 0; i < nodesLength ; i++ { + for _, operand := range andOperator.operands { - if len(andOperator.operands[i].key) == 0 { + if len(operand.key) == 0 { // is an operator - res = res && andOperator.operands[i].filter.Eval(payload) + res = res && operand.filter.Eval(payload) } else { // is an normal key - val, _, _, _ := jsonparser.Get(payload, andOperator.operands[i].key) - res = res && andOperator.operands[i].filter.Eval(val) + val := payload.Get(operand.key) + res = res && operand.filter.Eval(val) } if res == false { @@ -91,20 +96,23 @@ type OrOperator struct { operands []Filter } -func orOperatorConstruct(argument []byte) *OrOperator { +func orOperatorConstruct(argument gjson.Result) *OrOperator { + if !argument.IsArray() { + log.Error("the argument of 'or' operator must be an array") + } op := new(OrOperator) - _, _ = jsonparser.ArrayEach(argument, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + argument.ForEach(func(_, value gjson.Result) bool { op.operands = append(op.operands, GetOperatorFactory().Generate("and", value)) + return true }) return op } -func (orOperator OrOperator) Eval(payload []byte) bool { - log.Debug("or "+string(payload)) +func (orOperator OrOperator) Eval(payload gjson.Result) bool { + log.Debug("or "+ payload.Str) res:= false - nodesLength := len(orOperator.operands) - for i := 0; i < nodesLength ; i++ { - res = res || orOperator.operands[i].Eval(payload) + for _, operand := range orOperator.operands { + res = res || operand.Eval(payload) if res == true { break @@ -114,88 +122,99 @@ func (orOperator OrOperator) Eval(payload []byte) bool { } type EqualOperator struct { - value []byte + value gjson.Result } -func equalOperatorConstruct(argument []byte) *EqualOperator { +func equalOperatorConstruct(argument gjson.Result) *EqualOperator { op := new(EqualOperator) op.value = argument return op } -func (equalOperator EqualOperator) Eval(payload []byte) bool { - log.Debug("eq "+string(payload)) - return bytes.Equal(payload, equalOperator.value) +func (equalOperator EqualOperator) Eval(payload gjson.Result) bool { + log.Debug("eq "+ payload.Str + "==" + equalOperator.value.Str) + return payload.Str == equalOperator.value.Str } type NotEqualOperator struct { - value []byte + value gjson.Result } -func notEqualOperatorConstruct(argument []byte) *NotEqualOperator { +func notEqualOperatorConstruct(argument gjson.Result) *NotEqualOperator { op := new(NotEqualOperator) op.value = argument return op } -func (notEqualOperator NotEqualOperator) Eval(payload []byte) bool { - log.Debug("neq "+string(payload)) - return !bytes.Equal(payload, notEqualOperator.value) +func (notEqualOperator NotEqualOperator) Eval(payload gjson.Result) bool { + log.Debug("neq " + payload.Str) + return !(payload.Str == notEqualOperator.value.Str) } type InOperator struct { - operands [][]byte + operand gjson.Result } -func inOperatorConstruct(argument []byte) *InOperator { - op := new(InOperator) - _, _ = jsonparser.ArrayEach(argument, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { - op.operands = append(op.operands, value) - }) - return op -} - -func (inOperator InOperator) Eval(payload []byte) bool { - log.Debug("in "+string(payload)) - res := false - for _, v := range inOperator.operands { - res = res || bytes.Equal(payload, v) - if res == true { - break - } +func inOperatorConstruct(argument gjson.Result) *InOperator { + if argument.IsObject() { + log.Error("the argument of 'in' operator must be an array or a string") } - return res -} - -type ContainsOperator struct { - operand []byte -} - -func containsOperatorConstruct(argument []byte) *ContainsOperator { - op := new(ContainsOperator) + op := new(InOperator) op.operand = argument return op } -func (containsOperator ContainsOperator) Eval(payload []byte) bool { - log.Debug("contains "+string(payload)) - return bytes.Contains(payload, containsOperator.operand) +func (inOperator InOperator) Eval(payload gjson.Result) bool { + log.Debug("in " + payload.Str) + if inOperator.operand.IsArray() { + res := false + inOperator.operand.ForEach(func(key, value gjson.Result) bool { + res = res || value.Str == payload.Str + return true + }) + return res + } + return strings.Contains(inOperator.operand.Str, payload.Str) +} + +type ContainsOperator struct { + operand string +} + +func containsOperatorConstruct(argument gjson.Result) *ContainsOperator { + if argument.IsArray() || argument.IsObject() { + log.Error("the argument of 'contains' operator must be a string") + } + op := new(ContainsOperator) + op.operand = argument.Str + return op +} + +func (containsOperator ContainsOperator) Eval(payload gjson.Result) bool { + log.Debug("contains "+ payload.Str) + if payload.IsObject() || payload.IsArray() { + return false + } + return strings.Contains(payload.String(), containsOperator.operand) } type RegexOperator struct { regex string } -func regexOperatorConstruct(argument []byte) *RegexOperator { +func regexOperatorConstruct(argument gjson.Result) *RegexOperator { + if argument.IsArray() || argument.IsObject() { + log.Error("the argument of 'regex' operator must be a string") + } op := new(RegexOperator) - op.regex = string(argument) + op.regex = argument.Str return op } -func (containsOperator RegexOperator) Eval(payload []byte) bool { - log.Debug("regex "+string(payload)) - matched, _ := regexp.Match(containsOperator.regex, payload) +func (containsOperator RegexOperator) Eval(payload gjson.Result) bool { + log.Debug("regex " + payload.Str) + matched, _ := regexp.MatchString(containsOperator.regex, payload.Str) return matched } // 单例工厂 @@ -208,7 +227,7 @@ func GetOperatorFactory() *operatorFactory { return instance } -func (o operatorFactory) Generate(opName string, argument []byte) Filter { +func (o operatorFactory) Generate(opName string, argument gjson.Result) Filter { switch opName { case "not": return notOperatorConstruct(argument) @@ -241,7 +260,7 @@ func GetFilter() *Filter { if err != nil { filter = nil } else { - *filter = GetOperatorFactory().Generate("and", f) + *filter = GetOperatorFactory().Generate("and", gjson.ParseBytes(f)) } }) return filter diff --git a/go.mod b/go.mod index 8dcdefe..e8c0fed 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.14 require ( github.com/Mrs4s/MiraiGo v0.0.0-20200821182324-7654a7a2a106 - github.com/buger/jsonparser v1.0.0 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/gin-gonic/gin v1.6.3 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index dd1f0b7..ebab85a 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/Mrs4s/MiraiGo v0.0.0-20200821164244-fe8e98b9d8c5 h1:LVzj3ahW0LYJQMDcD github.com/Mrs4s/MiraiGo v0.0.0-20200821164244-fe8e98b9d8c5/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM= github.com/Mrs4s/MiraiGo v0.0.0-20200821182324-7654a7a2a106 h1:z2oaXbvOmu7/O666OkUeH5SGUjyQ3wUSNjtTOagY8Sk= github.com/Mrs4s/MiraiGo v0.0.0-20200821182324-7654a7a2a106/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM= -github.com/buger/jsonparser v1.0.0 h1:etJTGF5ESxjI0Ic2UaLQs2LQQpa8G9ykQScukbh4L8A= -github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/server/websocket.go b/server/websocket.go index d64093b..7510776 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -172,7 +172,8 @@ func (c *websocketClient) listenApi(conn *websocketConn, u bool) { } func (c *websocketClient) onBotPushEvent(m coolq.MSG) { - payload := []byte(m.ToJson()) + log.Infof(m.ToJson()) + payload := gjson.Parse(m.ToJson()) filter := global.GetFilter() if filter != nil && (*filter).Eval(payload) == false { log.Debug("Event filtered!") From 2b92035fdc2837f7913f6ab545d5ff0e6ed6b814 Mon Sep 17 00:00:00 2001 From: wdvxdr1123 <34832863+wdvxdr1123@users.noreply.github.com> Date: Sat, 22 Aug 2020 16:24:17 +0800 Subject: [PATCH 3/3] remove unnecessary info --- server/websocket.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/websocket.go b/server/websocket.go index 7510776..ce96143 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -172,7 +172,6 @@ func (c *websocketClient) listenApi(conn *websocketConn, u bool) { } func (c *websocketClient) onBotPushEvent(m coolq.MSG) { - log.Infof(m.ToJson()) payload := gjson.Parse(m.ToJson()) filter := global.GetFilter() if filter != nil && (*filter).Eval(payload) == false {