github.com/Mrs4s/go-cqhttp@v1.2.0/modules/filter/filter.go (about) 1 // Package filter implements an event filter for go-cqhttp 2 package filter 3 4 import ( 5 "regexp" 6 "strings" 7 8 "github.com/tidwall/gjson" 9 ) 10 11 // Filter 定义了一个消息上报过滤接口 12 type Filter interface { 13 Eval(payload gjson.Result) bool 14 } 15 16 type operationNode struct { 17 key string 18 filter Filter 19 } 20 21 // notOperator 定义了过滤器中Not操作符 22 type notOperator struct { 23 operand Filter 24 } 25 26 func newNotOp(argument gjson.Result) Filter { 27 if !argument.IsObject() { 28 panic("the argument of 'not' operator must be an object") 29 } 30 return ¬Operator{operand: Generate("and", argument)} 31 } 32 33 // Eval 对payload执行Not过滤 34 func (op *notOperator) Eval(payload gjson.Result) bool { 35 return !op.operand.Eval(payload) 36 } 37 38 // andOperator 定义了过滤器中And操作符 39 type andOperator struct { 40 operands []operationNode 41 } 42 43 func newAndOp(argument gjson.Result) Filter { 44 if !argument.IsObject() { 45 panic("the argument of 'and' operator must be an object") 46 } 47 op := new(andOperator) 48 argument.ForEach(func(key, value gjson.Result) bool { 49 switch { 50 case key.Str[0] == '.': 51 // is an operator 52 // ".foo": { 53 // "bar": "baz" 54 // } 55 opKey := key.Str[1:] 56 op.operands = append(op.operands, operationNode{"", Generate(opKey, value)}) 57 case value.IsObject(): 58 // is a normal key with an object as the value 59 // "foo": { 60 // ".bar": "baz" 61 // } 62 opKey := key.String() 63 op.operands = append(op.operands, operationNode{opKey, Generate("and", value)}) 64 default: 65 // is a normal key with a non-object as the value 66 // "foo": "bar" 67 opKey := key.String() 68 op.operands = append(op.operands, operationNode{opKey, Generate("eq", value)}) 69 } 70 return true 71 }) 72 return op 73 } 74 75 // Eval 对payload执行And过滤 76 func (op *andOperator) Eval(payload gjson.Result) bool { 77 res := true 78 for _, operand := range op.operands { 79 if len(operand.key) == 0 { 80 // is an operator 81 res = res && operand.filter.Eval(payload) 82 } else { 83 // is a normal key 84 val := payload.Get(operand.key) 85 res = res && operand.filter.Eval(val) 86 } 87 88 if !res { 89 break 90 } 91 } 92 return res 93 } 94 95 // orOperator 定义了过滤器中Or操作符 96 type orOperator struct { 97 operands []Filter 98 } 99 100 func newOrOp(argument gjson.Result) Filter { 101 if !argument.IsArray() { 102 panic("the argument of 'or' operator must be an array") 103 } 104 op := new(orOperator) 105 argument.ForEach(func(_, value gjson.Result) bool { 106 op.operands = append(op.operands, Generate("and", value)) 107 return true 108 }) 109 return op 110 } 111 112 // Eval 对payload执行Or过滤 113 func (op *orOperator) Eval(payload gjson.Result) bool { 114 res := false 115 for _, operand := range op.operands { 116 res = res || operand.Eval(payload) 117 if res { 118 break 119 } 120 } 121 return res 122 } 123 124 // eqOperator 定义了过滤器中Equal操作符 125 type eqOperator struct { 126 operand string 127 } 128 129 func newEqOp(argument gjson.Result) Filter { 130 return &eqOperator{operand: argument.String()} 131 } 132 133 // Eval 对payload执行Equal过滤 134 func (op *eqOperator) Eval(payload gjson.Result) bool { 135 return payload.String() == op.operand 136 } 137 138 // neqOperator 定义了过滤器中NotEqual操作符 139 type neqOperator struct { 140 operand string 141 } 142 143 func newNeqOp(argument gjson.Result) Filter { 144 return &neqOperator{operand: argument.String()} 145 } 146 147 // Eval 对payload执行NotEqual过滤 148 func (op *neqOperator) Eval(payload gjson.Result) bool { 149 return !(payload.String() == op.operand) 150 } 151 152 // inOperator 定义了过滤器中In操作符 153 type inOperator struct { 154 operandString string 155 operandArray []string 156 } 157 158 func newInOp(argument gjson.Result) Filter { 159 if argument.IsObject() { 160 panic("the argument of 'in' operator must be an array or a string") 161 } 162 op := new(inOperator) 163 if argument.IsArray() { 164 op.operandArray = []string{} 165 argument.ForEach(func(_, value gjson.Result) bool { 166 op.operandArray = append(op.operandArray, value.String()) 167 return true 168 }) 169 } else { 170 op.operandString = argument.String() 171 } 172 return op 173 } 174 175 // Eval 对payload执行In过滤 176 func (op *inOperator) Eval(payload gjson.Result) bool { 177 payloadStr := payload.String() 178 if op.operandArray != nil { 179 for _, value := range op.operandArray { 180 if value == payloadStr { 181 return true 182 } 183 } 184 return false 185 } 186 return strings.Contains(op.operandString, payloadStr) 187 } 188 189 // containsOperator 定义了过滤器中Contains操作符 190 type containsOperator struct { 191 operand string 192 } 193 194 func newContainOp(argument gjson.Result) Filter { 195 if argument.IsArray() || argument.IsObject() { 196 panic("the argument of 'contains' operator must be a string") 197 } 198 return &containsOperator{operand: argument.String()} 199 } 200 201 // Eval 对payload执行Contains过滤 202 func (op *containsOperator) Eval(payload gjson.Result) bool { 203 return strings.Contains(payload.String(), op.operand) 204 } 205 206 // regexOperator 定义了过滤器中Regex操作符 207 type regexOperator struct { 208 regex *regexp.Regexp 209 } 210 211 func newRegexOp(argument gjson.Result) Filter { 212 if argument.IsArray() || argument.IsObject() { 213 panic("the argument of 'regex' operator must be a string") 214 } 215 return ®exOperator{regex: regexp.MustCompile(argument.String())} 216 } 217 218 // Eval 对payload执行RegexO过滤 219 func (op *regexOperator) Eval(payload gjson.Result) bool { 220 return op.regex.MatchString(payload.String()) 221 } 222 223 // Generate 根据给定操作符名opName及操作符参数argument创建一个过滤器实例 224 func Generate(opName string, argument gjson.Result) Filter { 225 switch opName { 226 case "not": 227 return newNotOp(argument) 228 case "and": 229 return newAndOp(argument) 230 case "or": 231 return newOrOp(argument) 232 case "eq": 233 return newEqOp(argument) 234 case "neq": 235 return newNeqOp(argument) 236 case "in": 237 return newInOp(argument) 238 case "contains": 239 return newContainOp(argument) 240 case "regex": 241 return newRegexOp(argument) 242 default: 243 panic("the operator " + opName + " is not supported") 244 } 245 }