diff --git a/coolq/bot.go b/coolq/bot.go index 45d6861..4a10ffa 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -18,7 +18,6 @@ import ( jsoniter "github.com/json-iterator/go" log "github.com/sirupsen/logrus" - "github.com/tidwall/gjson" ) var json = jsoniter.ConfigCompatibleWithStandardLibrary @@ -392,22 +391,19 @@ func (bot *CQBot) Release() { } func (bot *CQBot) dispatchEventMessage(m MSG) { - payload := gjson.Parse(m.ToJson()) - filter := global.EventFilter - if filter != nil && (*filter).Eval(payload) == false { + if global.EventFilter != nil && global.EventFilter.Eval(global.MSG(m)) == false { log.Debug("Event filtered!") return } for _, f := range bot.events { - fn := f - go func() { + go func(fn func(MSG)) { start := time.Now() fn(m) end := time.Now() if end.Sub(start) > time.Second*5 { log.Debugf("警告: 事件处理耗时超过 5 秒 (%v), 请检查应用是否有堵塞.", end.Sub(start)) } - }() + }(f) } } diff --git a/docs/EventFilter.md b/docs/EventFilter.md index 2eb5f38..7f23406 100644 --- a/docs/EventFilter.md +++ b/docs/EventFilter.md @@ -1,12 +1,22 @@ # 事件过滤器 -在go-cqhttp同级目录下新建`filter.json`文件即可开启事件过滤器,启动时会读取该文件中定义的过滤规则(使用 JSON 编写),若文件不存在,或过滤规则语法错误,则会暂停所有上报。 +在go-cqhttp同级目录下新建`filter.json`文件即可开启事件过滤器,启动时会读取该文件中定义的过滤规则(使用 JSON 编写),若文件不存在,或过滤规则语法错误,则不会启用事件过滤器。 事件过滤器会处理所有事件(包括心跳事件在内的元事件),请谨慎使用!! +注意: 与客户端建立连接的握手事件**不会**经过事件过滤器 + ## 示例 这节首先给出一些示例,演示过滤器的基本用法,下一节将给出具体语法说明。 +### 过滤所有事件 + +```json +{ + ".not": {} +} +``` + ### 只上报以「!!」开头的消息 ```json @@ -128,5 +138,5 @@ 这里有几点需要注意: -- `message` 字段在运行过滤器时是消息段数组的形式(见 [消息格式]( https://github.com/howmanybots/onebot/blob/master/v11/specs/message/array.md )) +- `message` 字段在运行过滤器时和上报信息类型相同(见 [消息格式]( https://github.com/howmanybots/onebot/blob/master/v11/specs/message/array.md )) - `raw_message` 字段为未经**CQ码**处理的原始消息字符串,这意味着其中可能会出现形如 `[CQ:face,id=123]` 的 CQ 码 diff --git a/global/filter.go b/global/filter.go index f47f62e..44f25a4 100644 --- a/global/filter.go +++ b/global/filter.go @@ -1,6 +1,7 @@ package global import ( + "fmt" "io/ioutil" "regexp" "strings" @@ -9,17 +10,37 @@ import ( "github.com/tidwall/gjson" ) -type Filter interface { - Eval(payload gjson.Result) bool +type MSG map[string]interface{} + +func (m MSG) Get(s string) MSG { + if v,ok := m[s];ok { + if msg,ok := v.(MSG);ok { + return msg + } + return MSG{"__str__": v} // 用这个名字应该没问题吧 + } + return MSG{} } -type OperationNode struct { +func (m MSG) String() string { + if str,ok:=m["__str__"];ok { + return fmt.Sprint(str) + } + str, _ := json.MarshalToString(m) + return str +} + +type Filter interface { + Eval(payload MSG) bool +} + +type operationNode struct { key string filter Filter } type NotOperator struct { - operand_ Filter + operand Filter } func notOperatorConstruct(argument gjson.Result) *NotOperator { @@ -27,16 +48,16 @@ func notOperatorConstruct(argument gjson.Result) *NotOperator { panic("the argument of 'not' operator must be an object") } op := new(NotOperator) - op.operand_ = Generate("and", argument) + op.operand = Generate("and", argument) return op } -func (notOperator NotOperator) Eval(payload gjson.Result) bool { - return !(notOperator.operand_).Eval(payload) +func (op *NotOperator) Eval(payload MSG) bool { + return !op.operand.Eval(payload) } type AndOperator struct { - operands []OperationNode + operands []operationNode } func andOperatorConstruct(argument gjson.Result) *AndOperator { @@ -51,26 +72,26 @@ func andOperatorConstruct(argument gjson.Result) *AndOperator { // "bar": "baz" // } opKey := key.Str[1:] - op.operands = append(op.operands, OperationNode{"", Generate(opKey, value)}) + op.operands = append(op.operands, operationNode{"", Generate(opKey, value)}) } else if value.IsObject() { // is an normal key with an object as the value // "foo": { // ".bar": "baz" // } opKey := key.String() - op.operands = append(op.operands, OperationNode{opKey, Generate("and", value)}) + op.operands = append(op.operands, operationNode{opKey, Generate("and", value)}) } else { // is an normal key with a non-object as the value // "foo": "bar" opKey := key.String() - op.operands = append(op.operands, OperationNode{opKey, Generate("eq", value)}) + op.operands = append(op.operands, operationNode{opKey, Generate("eq", value)}) } return true }) return op } -func (andOperator *AndOperator) Eval(payload gjson.Result) bool { +func (andOperator *AndOperator) Eval(payload MSG) bool { res := true for _, operand := range andOperator.operands { @@ -106,11 +127,10 @@ func orOperatorConstruct(argument gjson.Result) *OrOperator { return op } -func (orOperator OrOperator) Eval(payload gjson.Result) bool { +func (op *OrOperator) Eval(payload MSG) bool { res := false - for _, operand := range orOperator.operands { + for _, operand := range op.operands { res = res || operand.Eval(payload) - if res == true { break } @@ -119,35 +139,36 @@ func (orOperator OrOperator) Eval(payload gjson.Result) bool { } type EqualOperator struct { - value gjson.Result + operand string } func equalOperatorConstruct(argument gjson.Result) *EqualOperator { op := new(EqualOperator) - op.value = argument + op.operand = argument.String() return op } -func (equalOperator EqualOperator) Eval(payload gjson.Result) bool { - return payload.String() == equalOperator.value.String() +func (op *EqualOperator) Eval(payload MSG) bool { + return payload.String() == op.operand } type NotEqualOperator struct { - value gjson.Result + operand string } func notEqualOperatorConstruct(argument gjson.Result) *NotEqualOperator { op := new(NotEqualOperator) - op.value = argument + op.operand = argument.String() return op } -func (notEqualOperator NotEqualOperator) Eval(payload gjson.Result) bool { - return !(payload.String() == notEqualOperator.value.String()) +func (op *NotEqualOperator) Eval(payload MSG) bool { + return !(payload.String() == op.operand) } type InOperator struct { - operand gjson.Result + operandString string + operandArray []string } func inOperatorConstruct(argument gjson.Result) *InOperator { @@ -155,20 +176,29 @@ func inOperatorConstruct(argument gjson.Result) *InOperator { panic("the argument of 'in' operator must be an array or a string") } op := new(InOperator) - op.operand = argument + if argument.IsArray() { + op.operandArray = []string{} + argument.ForEach(func(_, value gjson.Result) bool { + op.operandArray = append(op.operandArray, value.String()) + return true + }) + } else { + op.operandString = argument.String() + } return op } -func (inOperator InOperator) Eval(payload gjson.Result) bool { - if inOperator.operand.IsArray() { - res := false - inOperator.operand.ForEach(func(key, value gjson.Result) bool { - res = res || value.String() == payload.String() - return true - }) - return res +func (op *InOperator) Eval(payload MSG) bool { + payloadStr := payload.String() + if op.operandArray != nil { + for _, value := range op.operandArray { + if value == payloadStr { + return true + } + } + return false } - return strings.Contains(inOperator.operand.String(), payload.String()) + return strings.Contains(op.operandString, payloadStr) } type ContainsOperator struct { @@ -184,15 +214,12 @@ func containsOperatorConstruct(argument gjson.Result) *ContainsOperator { return op } -func (containsOperator ContainsOperator) Eval(payload gjson.Result) bool { - if payload.IsObject() || payload.IsArray() { - return false - } - return strings.Contains(payload.String(), containsOperator.operand) +func (op *ContainsOperator) Eval(payload MSG) bool { + return strings.Contains(payload.String(), op.operand) } type RegexOperator struct { - regex string + regex *regexp.Regexp } func regexOperatorConstruct(argument gjson.Result) *RegexOperator { @@ -200,12 +227,12 @@ func regexOperatorConstruct(argument gjson.Result) *RegexOperator { panic("the argument of 'regex' operator must be a string") } op := new(RegexOperator) - op.regex = argument.String() + op.regex = regexp.MustCompile(argument.String()) return op } -func (containsOperator RegexOperator) Eval(payload gjson.Result) bool { - matched, _ := regexp.MatchString(containsOperator.regex, payload.String()) +func (op *RegexOperator) Eval(payload MSG) bool { + matched := op.regex.MatchString(payload.String()) return matched } @@ -232,7 +259,7 @@ func Generate(opName string, argument gjson.Result) Filter { } } -var EventFilter = new(Filter) +var EventFilter Filter = nil func BootFilter() { defer func() { @@ -247,6 +274,6 @@ func BootFilter() { if err != nil { panic(err) } else { - *EventFilter = Generate("and", gjson.ParseBytes(f)) + EventFilter = Generate("and", gjson.ParseBytes(f)) } }