mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-05-08 04:55:55 +08:00
Merge pull request #461 from wdvxdr1123/patch/eventfilter
This commit is contained in:
commit
818cecc279
10
coolq/bot.go
10
coolq/bot.go
@ -18,7 +18,6 @@ import (
|
|||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
@ -392,22 +391,19 @@ func (bot *CQBot) Release() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) dispatchEventMessage(m MSG) {
|
func (bot *CQBot) dispatchEventMessage(m MSG) {
|
||||||
payload := gjson.Parse(m.ToJson())
|
if global.EventFilter != nil && global.EventFilter.Eval(global.MSG(m)) == false {
|
||||||
filter := global.EventFilter
|
|
||||||
if filter != nil && (*filter).Eval(payload) == false {
|
|
||||||
log.Debug("Event filtered!")
|
log.Debug("Event filtered!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, f := range bot.events {
|
for _, f := range bot.events {
|
||||||
fn := f
|
go func(fn func(MSG)) {
|
||||||
go func() {
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
fn(m)
|
fn(m)
|
||||||
end := time.Now()
|
end := time.Now()
|
||||||
if end.Sub(start) > time.Second*5 {
|
if end.Sub(start) > time.Second*5 {
|
||||||
log.Debugf("警告: 事件处理耗时超过 5 秒 (%v), 请检查应用是否有堵塞.", end.Sub(start))
|
log.Debugf("警告: 事件处理耗时超过 5 秒 (%v), 请检查应用是否有堵塞.", end.Sub(start))
|
||||||
}
|
}
|
||||||
}()
|
}(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
# 事件过滤器
|
# 事件过滤器
|
||||||
|
|
||||||
在go-cqhttp同级目录下新建`filter.json`文件即可开启事件过滤器,启动时会读取该文件中定义的过滤规则(使用 JSON 编写),若文件不存在,或过滤规则语法错误,则会暂停所有上报。
|
在go-cqhttp同级目录下新建`filter.json`文件即可开启事件过滤器,启动时会读取该文件中定义的过滤规则(使用 JSON 编写),若文件不存在,或过滤规则语法错误,则不会启用事件过滤器。
|
||||||
事件过滤器会处理所有事件(包括心跳事件在内的元事件),请谨慎使用!!
|
事件过滤器会处理所有事件(包括心跳事件在内的元事件),请谨慎使用!!
|
||||||
|
|
||||||
|
注意: 与客户端建立连接的握手事件**不会**经过事件过滤器
|
||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
这节首先给出一些示例,演示过滤器的基本用法,下一节将给出具体语法说明。
|
这节首先给出一些示例,演示过滤器的基本用法,下一节将给出具体语法说明。
|
||||||
|
|
||||||
|
### 过滤所有事件
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
".not": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 只上报以「!!」开头的消息
|
### 只上报以「!!」开头的消息
|
||||||
|
|
||||||
```json
|
```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 码
|
- `raw_message` 字段为未经**CQ码**处理的原始消息字符串,这意味着其中可能会出现形如 `[CQ:face,id=123]` 的 CQ 码
|
||||||
|
117
global/filter.go
117
global/filter.go
@ -1,6 +1,7 @@
|
|||||||
package global
|
package global
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -9,17 +10,37 @@ import (
|
|||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Filter interface {
|
type MSG map[string]interface{}
|
||||||
Eval(payload gjson.Result) bool
|
|
||||||
|
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
|
key string
|
||||||
filter Filter
|
filter Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotOperator struct {
|
type NotOperator struct {
|
||||||
operand_ Filter
|
operand Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
func notOperatorConstruct(argument gjson.Result) *NotOperator {
|
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")
|
panic("the argument of 'not' operator must be an object")
|
||||||
}
|
}
|
||||||
op := new(NotOperator)
|
op := new(NotOperator)
|
||||||
op.operand_ = Generate("and", argument)
|
op.operand = Generate("and", argument)
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (notOperator NotOperator) Eval(payload gjson.Result) bool {
|
func (op *NotOperator) Eval(payload MSG) bool {
|
||||||
return !(notOperator.operand_).Eval(payload)
|
return !op.operand.Eval(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
type AndOperator struct {
|
type AndOperator struct {
|
||||||
operands []OperationNode
|
operands []operationNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func andOperatorConstruct(argument gjson.Result) *AndOperator {
|
func andOperatorConstruct(argument gjson.Result) *AndOperator {
|
||||||
@ -51,26 +72,26 @@ func andOperatorConstruct(argument gjson.Result) *AndOperator {
|
|||||||
// "bar": "baz"
|
// "bar": "baz"
|
||||||
// }
|
// }
|
||||||
opKey := key.Str[1:]
|
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() {
|
} else if value.IsObject() {
|
||||||
// is an normal key with an object as the value
|
// is an normal key with an object as the value
|
||||||
// "foo": {
|
// "foo": {
|
||||||
// ".bar": "baz"
|
// ".bar": "baz"
|
||||||
// }
|
// }
|
||||||
opKey := key.String()
|
opKey := key.String()
|
||||||
op.operands = append(op.operands, OperationNode{opKey, Generate("and", value)})
|
op.operands = append(op.operands, operationNode{opKey, Generate("and", value)})
|
||||||
} else {
|
} else {
|
||||||
// is an normal key with a non-object as the value
|
// is an normal key with a non-object as the value
|
||||||
// "foo": "bar"
|
// "foo": "bar"
|
||||||
opKey := key.String()
|
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 true
|
||||||
})
|
})
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (andOperator *AndOperator) Eval(payload gjson.Result) bool {
|
func (andOperator *AndOperator) Eval(payload MSG) bool {
|
||||||
res := true
|
res := true
|
||||||
for _, operand := range andOperator.operands {
|
for _, operand := range andOperator.operands {
|
||||||
|
|
||||||
@ -106,11 +127,10 @@ func orOperatorConstruct(argument gjson.Result) *OrOperator {
|
|||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (orOperator OrOperator) Eval(payload gjson.Result) bool {
|
func (op *OrOperator) Eval(payload MSG) bool {
|
||||||
res := false
|
res := false
|
||||||
for _, operand := range orOperator.operands {
|
for _, operand := range op.operands {
|
||||||
res = res || operand.Eval(payload)
|
res = res || operand.Eval(payload)
|
||||||
|
|
||||||
if res == true {
|
if res == true {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -119,35 +139,36 @@ func (orOperator OrOperator) Eval(payload gjson.Result) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EqualOperator struct {
|
type EqualOperator struct {
|
||||||
value gjson.Result
|
operand string
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalOperatorConstruct(argument gjson.Result) *EqualOperator {
|
func equalOperatorConstruct(argument gjson.Result) *EqualOperator {
|
||||||
op := new(EqualOperator)
|
op := new(EqualOperator)
|
||||||
op.value = argument
|
op.operand = argument.String()
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (equalOperator EqualOperator) Eval(payload gjson.Result) bool {
|
func (op *EqualOperator) Eval(payload MSG) bool {
|
||||||
return payload.String() == equalOperator.value.String()
|
return payload.String() == op.operand
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotEqualOperator struct {
|
type NotEqualOperator struct {
|
||||||
value gjson.Result
|
operand string
|
||||||
}
|
}
|
||||||
|
|
||||||
func notEqualOperatorConstruct(argument gjson.Result) *NotEqualOperator {
|
func notEqualOperatorConstruct(argument gjson.Result) *NotEqualOperator {
|
||||||
op := new(NotEqualOperator)
|
op := new(NotEqualOperator)
|
||||||
op.value = argument
|
op.operand = argument.String()
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (notEqualOperator NotEqualOperator) Eval(payload gjson.Result) bool {
|
func (op *NotEqualOperator) Eval(payload MSG) bool {
|
||||||
return !(payload.String() == notEqualOperator.value.String())
|
return !(payload.String() == op.operand)
|
||||||
}
|
}
|
||||||
|
|
||||||
type InOperator struct {
|
type InOperator struct {
|
||||||
operand gjson.Result
|
operandString string
|
||||||
|
operandArray []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func inOperatorConstruct(argument gjson.Result) *InOperator {
|
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")
|
panic("the argument of 'in' operator must be an array or a string")
|
||||||
}
|
}
|
||||||
op := new(InOperator)
|
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
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inOperator InOperator) Eval(payload gjson.Result) bool {
|
func (op *InOperator) Eval(payload MSG) bool {
|
||||||
if inOperator.operand.IsArray() {
|
payloadStr := payload.String()
|
||||||
res := false
|
if op.operandArray != nil {
|
||||||
inOperator.operand.ForEach(func(key, value gjson.Result) bool {
|
for _, value := range op.operandArray {
|
||||||
res = res || value.String() == payload.String()
|
if value == payloadStr {
|
||||||
return true
|
return true
|
||||||
})
|
}
|
||||||
return res
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return strings.Contains(inOperator.operand.String(), payload.String())
|
return strings.Contains(op.operandString, payloadStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainsOperator struct {
|
type ContainsOperator struct {
|
||||||
@ -184,15 +214,12 @@ func containsOperatorConstruct(argument gjson.Result) *ContainsOperator {
|
|||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (containsOperator ContainsOperator) Eval(payload gjson.Result) bool {
|
func (op *ContainsOperator) Eval(payload MSG) bool {
|
||||||
if payload.IsObject() || payload.IsArray() {
|
return strings.Contains(payload.String(), op.operand)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return strings.Contains(payload.String(), containsOperator.operand)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegexOperator struct {
|
type RegexOperator struct {
|
||||||
regex string
|
regex *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func regexOperatorConstruct(argument gjson.Result) *RegexOperator {
|
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")
|
panic("the argument of 'regex' operator must be a string")
|
||||||
}
|
}
|
||||||
op := new(RegexOperator)
|
op := new(RegexOperator)
|
||||||
op.regex = argument.String()
|
op.regex = regexp.MustCompile(argument.String())
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (containsOperator RegexOperator) Eval(payload gjson.Result) bool {
|
func (op *RegexOperator) Eval(payload MSG) bool {
|
||||||
matched, _ := regexp.MatchString(containsOperator.regex, payload.String())
|
matched := op.regex.MatchString(payload.String())
|
||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +259,7 @@ func Generate(opName string, argument gjson.Result) Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var EventFilter = new(Filter)
|
var EventFilter Filter = nil
|
||||||
|
|
||||||
func BootFilter() {
|
func BootFilter() {
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -247,6 +274,6 @@ func BootFilter() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
*EventFilter = Generate("and", gjson.ParseBytes(f))
|
EventFilter = Generate("and", gjson.ParseBytes(f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user