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  }