github.com/fumiama/NanoBot@v0.0.0-20231122134259-c22d8183efca/rules.go (about) 1 package nano 2 3 import ( 4 "reflect" 5 "regexp" 6 "strings" 7 "time" 8 ) 9 10 // PrefixRule check if the text message has the prefix and trim the prefix 11 // 12 // 检查消息前缀 13 func PrefixRule(prefix string) Rule { 14 return PrefixGroupRule(prefix) 15 } 16 17 // PrefixGroupRule check if the text message has the prefix and trim the prefix 18 // 19 // 检查消息前缀 20 func PrefixGroupRule(prefixes ...string) Rule { 21 return func(ctx *Ctx) bool { 22 switch msg := ctx.Value.(type) { 23 case *Message: 24 if msg.Content == "" { // 确保无空 25 return false 26 } 27 for _, prefix := range prefixes { 28 if strings.HasPrefix(msg.Content, MessageEscape(prefix)) { 29 ctx.State["prefix"] = prefix 30 arg := strings.TrimLeft(msg.Content[len(prefix):], " ") 31 ctx.State["args"] = arg 32 return true 33 } 34 } 35 return false 36 default: 37 return false 38 } 39 } 40 } 41 42 // SuffixRule check if the text message has the suffix and trim the suffix 43 // 44 // 检查消息后缀 45 func SuffixRule(suffix string) Rule { 46 return SuffixGroupRule(suffix) 47 } 48 49 // SuffixGroupRule check if the text message has the suffix and trim the suffix 50 // 51 // 检查消息后缀 52 func SuffixGroupRule(suffixes ...string) Rule { 53 return func(ctx *Ctx) bool { 54 switch msg := ctx.Value.(type) { 55 case *Message: 56 if msg.Content == "" { // 确保无空 57 return false 58 } 59 for _, suffix := range suffixes { 60 if strings.HasSuffix(msg.Content, MessageEscape(suffix)) { 61 ctx.State["suffix"] = suffix 62 arg := strings.TrimRight(msg.Content[:len(msg.Content)-len(suffix)], " ") 63 ctx.State["args"] = arg 64 return true 65 } 66 } 67 return false 68 default: 69 return false 70 } 71 } 72 } 73 74 // CommandRule check if the message is a command and trim the command name 75 // 76 // this rule only supports Message 77 func CommandRule(command string) Rule { 78 return CommandGroupRule(command) 79 } 80 81 // CommandGroupRule check if the message is a command and trim the command name 82 // 83 // this rule only supports Message 84 func CommandGroupRule(commands ...string) Rule { 85 return func(ctx *Ctx) bool { 86 msg, ok := ctx.Value.(*Message) 87 if !ok || msg.Content == "" { // 确保无空 88 return false 89 } 90 msg.Content = strings.TrimSpace(msg.Content) 91 if msg.Content == "" { // 确保无空 92 return false 93 } 94 cmdMessage := "" 95 args := "" 96 switch { 97 case strings.HasPrefix(msg.Content, "/"): 98 cmdMessage, args, _ = strings.Cut(msg.Content, " ") 99 cmdMessage, _, _ = strings.Cut(cmdMessage, "@") 100 cmdMessage = cmdMessage[1:] 101 default: 102 return false 103 } 104 for _, command := range commands { 105 if strings.HasPrefix(cmdMessage, MessageEscape(command)) { 106 ctx.State["command"] = command 107 ctx.State["args"] = args 108 return true 109 } 110 } 111 return false 112 } 113 } 114 115 // RegexRule check if the message can be matched by the regex pattern 116 func RegexRule(regexPattern string) Rule { 117 regex := regexp.MustCompile(regexPattern) 118 return func(ctx *Ctx) bool { 119 switch msg := ctx.Value.(type) { 120 case *Message: 121 if msg.Content == "" { // 确保无空 122 return false 123 } 124 if matched := regex.FindStringSubmatch(msg.Content); matched != nil { 125 ctx.State["regex_matched"] = matched 126 return true 127 } 128 return false 129 default: 130 return false 131 } 132 } 133 } 134 135 // ReplyRule check if the message is replying some message 136 // 137 // this rule only supports Message 138 func ReplyRule(messageID string) Rule { 139 return func(ctx *Ctx) bool { 140 msg, ok := ctx.Value.(*Message) 141 if !ok || msg.MessageReference == nil { // 确保无空 142 return false 143 } 144 return messageID == msg.MessageReference.MessageID 145 } 146 } 147 148 func KeywordRule(src string) Rule { 149 return KeywordGroupRule(src) 150 } 151 152 // KeywordGroupRule check if the message has a keyword or keywords 153 func KeywordGroupRule(src ...string) Rule { 154 return func(ctx *Ctx) bool { 155 switch msg := ctx.Value.(type) { 156 case *Message: 157 if msg.Content == "" { // 确保无空 158 return false 159 } 160 for _, str := range src { 161 if strings.Contains(msg.Content, MessageEscape(str)) { 162 ctx.State["keyword"] = str 163 return true 164 } 165 } 166 return false 167 default: 168 return false 169 } 170 } 171 } 172 173 // FullMatchRule check if src has the same copy of the message 174 func FullMatchRule(src string) Rule { 175 return FullMatchGroupRule(src) 176 } 177 178 // FullMatchGroupRule check if src has the same copy of the message 179 func FullMatchGroupRule(src ...string) Rule { 180 return func(ctx *Ctx) bool { 181 switch msg := ctx.Value.(type) { 182 case *Message: 183 if msg.Content == "" { // 确保无空 184 return false 185 } 186 for _, str := range src { 187 if MessageEscape(str) == msg.Content { 188 ctx.State["matched"] = msg.Content 189 return true 190 } 191 } 192 return false 193 default: 194 return false 195 } 196 } 197 } 198 199 // ShellRule 定义shell-like规则 200 // 201 // this rule only supports Message 202 func ShellRule(cmd string, model interface{}) Rule { 203 cmdRule := CommandRule(cmd) 204 t := reflect.TypeOf(model) 205 return func(ctx *Ctx) bool { 206 if !cmdRule(ctx) { 207 return false 208 } 209 // bind flag to struct 210 args := ParseShell(ctx.State["args"].(string)) 211 val := reflect.New(t) 212 fs := registerFlag(t, val) 213 err := fs.Parse(args) 214 if err != nil { 215 return false 216 } 217 ctx.State["args"] = fs.Args() 218 ctx.State["flag"] = val.Interface() 219 return true 220 } 221 } 222 223 // OnlyToMe only triggered in conditions of @bot or begin with the nicknames 224 // 225 // this rule only supports Message 226 func OnlyToMe(ctx *Ctx) bool { 227 return ctx.IsToMe 228 } 229 230 // CheckUser only triggered by specific person 231 func CheckUser(userID ...string) Rule { 232 return func(ctx *Ctx) bool { 233 switch msg := ctx.Value.(type) { 234 case *Message: 235 if msg.Author == nil { // 确保无空 236 return false 237 } 238 for _, uid := range userID { 239 if msg.Author.ID == uid { 240 return true 241 } 242 } 243 return false 244 default: 245 return false 246 } 247 } 248 } 249 250 // CheckChannel only triggered in specific channel 251 func CheckChannel(channelID ...string) Rule { 252 return func(ctx *Ctx) bool { 253 switch msg := ctx.Value.(type) { 254 case *Message: 255 if msg.ChannelID == "" { // 确保无空 256 return false 257 } 258 for _, cid := range channelID { 259 if msg.ChannelID == cid { 260 return true 261 } 262 } 263 return false 264 default: 265 return false 266 } 267 } 268 } 269 270 // CheckGuild only triggered in specific guild 271 func CheckGuild(guildID ...string) Rule { 272 return func(ctx *Ctx) bool { 273 switch msg := ctx.Value.(type) { 274 case *Message: 275 if msg.GuildID == "" { // 确保无空 276 return false 277 } 278 for _, gid := range guildID { 279 if msg.GuildID == gid { 280 return true 281 } 282 } 283 return false 284 default: 285 return false 286 } 287 } 288 } 289 290 // OnlyQQ 必须是 QQ 消息 291 func OnlyQQ(ctx *Ctx) bool { 292 return ctx.IsQQ 293 } 294 295 // OnlyGuild 必须是频道消息 296 func OnlyGuild(ctx *Ctx) bool { 297 return !ctx.IsQQ 298 } 299 300 // OnlyDirect 必须是频道私聊 301 func OnlyDirect(ctx *Ctx) bool { 302 if ctx.Type != "" { 303 return strings.HasPrefix(ctx.Type, "Direct") 304 } 305 return false 306 } 307 308 // OnlyChannel 必须是频道 Channel 309 func OnlyChannel(ctx *Ctx) bool { 310 return !OnlyDirect(ctx) && !OnlyQQ(ctx) 311 } 312 313 // OnlyPublic 消息类型包含 At 或 Public (包括QQ群) 314 func OnlyPublic(ctx *Ctx) bool { 315 if ctx.Type != "" { 316 return strings.HasPrefix(ctx.Type, "At") || strings.HasPrefix(ctx.Type, "Public") || strings.HasPrefix(ctx.Type, "GroupAt") 317 } 318 return false 319 } 320 321 // OnlyPrivate is !OnlyPublic (包括QQ私聊) 322 func OnlyPrivate(ctx *Ctx) bool { 323 return !OnlyPublic(ctx) 324 } 325 326 // OnlyQQGroup 只在 QQ 群 327 func OnlyQQGroup(ctx *Ctx) bool { 328 return ctx.Type == "GroupAtMessageCreate" 329 } 330 331 // OnlyQQPrivate 只在 QQ 私聊 332 func OnlyQQPrivate(ctx *Ctx) bool { 333 return ctx.Type == "C2cMessageCreate" 334 } 335 336 // SuperUserPermission only triggered by the bot's owner 337 func SuperUserPermission(ctx *Ctx) bool { 338 switch msg := ctx.Value.(type) { 339 case *Message: 340 if msg.Author == nil { // 确保无空 341 return false 342 } 343 for _, su := range ctx.caller.SuperUsers { 344 if su == msg.Author.ID || (ctx.IsQQ && su == SuperUserAllQQUsers) { 345 return true 346 } 347 } 348 return false 349 default: 350 return false 351 } 352 } 353 354 // CreaterPermission only triggered by the creater or higher permission 355 func CreaterPermission(ctx *Ctx) bool { 356 if SuperUserPermission(ctx) { 357 return true 358 } 359 switch msg := ctx.Value.(type) { 360 case *Message: 361 if msg.Author == nil || msg.Member == nil { // 确保无空 362 return false 363 } 364 for _, role := range msg.Member.Roles { 365 if role == RoleIDCreater { 366 return true 367 } 368 } 369 return false 370 default: 371 return false 372 } 373 } 374 375 // AdminPermission only triggered by the admins or higher permission 376 func AdminPermission(ctx *Ctx) bool { 377 if SuperUserPermission(ctx) { 378 return true 379 } 380 switch msg := ctx.Value.(type) { 381 case *Message: 382 if msg.Author == nil || msg.Member == nil { // 确保无空 383 return false 384 } 385 for _, role := range msg.Member.Roles { 386 if role == RoleIDCreater || role == RoleIDAdmin { 387 return true 388 } 389 } 390 return false 391 default: 392 return false 393 } 394 } 395 396 // ChannelAdminPermission only triggered by the channel admins or higher permission 397 func ChannelAdminPermission(ctx *Ctx) bool { 398 if SuperUserPermission(ctx) { 399 return true 400 } 401 switch msg := ctx.Value.(type) { 402 case *Message: 403 if msg.Author == nil || msg.Member == nil { // 确保无空 404 return false 405 } 406 for _, role := range msg.Member.Roles { 407 if role == RoleIDCreater || role == RoleIDAdmin || role == RoleIDChannelAdmin { 408 return true 409 } 410 } 411 return false 412 default: 413 return false 414 } 415 } 416 417 // UserOrGrpAdmin 允许用户单独使用或群管使用 418 func UserOrGrpAdmin(ctx *Ctx) bool { 419 if OnlyPublic(ctx) { 420 return AdminPermission(ctx) 421 } 422 return OnlyToMe(ctx) 423 } 424 425 // UserOrChannelAdmin 允许用户单独使用或频道管理使用 426 func UserOrChannelAdmin(ctx *Ctx) bool { 427 if OnlyPublic(ctx) { 428 return ChannelAdminPermission(ctx) 429 } 430 return OnlyToMe(ctx) 431 } 432 433 // HasAttachments 消息包含 Attachments (典型: 图片) 返回 true 434 func HasAttachments(ctx *Ctx) bool { 435 msg, ok := ctx.Value.(*Message) 436 if !ok || len(msg.Attachments) == 0 { // 确保无空 437 return false 438 } 439 ctx.State["attachments"] = msg.Attachments 440 return true 441 } 442 443 // MustProvidePhoto 消息不存在图片阻塞120秒至有图片,超时返回 false 444 func MustProvidePhoto(onmessage string, needphohint, failhint string) Rule { 445 return func(ctx *Ctx) bool { 446 msg, ok := ctx.Value.(*Message) 447 if ok && len(msg.Attachments) > 0 { // 确保无空 448 ctx.State["attachments"] = msg.Attachments 449 return true 450 } 451 // 没有图片就索取 452 if needphohint != "" { 453 _, err := ctx.PostMessageToChannel(msg.ChannelID, &MessagePost{ 454 Content: needphohint, 455 MessageReference: &MessageReference{MessageID: msg.ID}, 456 ReplyMessageID: msg.ID, 457 }) 458 if err != nil { 459 return false 460 } 461 } 462 next := NewFutureEvent(onmessage, 999, false, ctx.CheckSession(), HasAttachments).Next() 463 select { 464 case <-time.After(time.Second * 120): 465 if failhint != "" { 466 _, _ = ctx.SendPlainMessage(true, failhint) 467 } 468 return false 469 case newCtx := <-next: 470 ctx.State["attachments"] = newCtx.State["attachments"] 471 ctx.Event = newCtx.Event 472 return true 473 } 474 } 475 }