mirror of
https://github.com/fumiama/NanoBot.git
synced 2026-06-08 12:10:23 +08:00
finish
This commit is contained in:
450
rules.go
Normal file
450
rules.go
Normal file
@@ -0,0 +1,450 @@
|
||||
package nano
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PrefixRule check if the text message has the prefix and trim the prefix
|
||||
//
|
||||
// 检查消息前缀
|
||||
func PrefixRule(prefix string) Rule {
|
||||
return PrefixGroupRule(prefix)
|
||||
}
|
||||
|
||||
// PrefixGroupRule check if the text message has the prefix and trim the prefix
|
||||
//
|
||||
// 检查消息前缀
|
||||
func PrefixGroupRule(prefixes ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(msg.Content, prefix) {
|
||||
ctx.State["prefix"] = prefix
|
||||
arg := strings.TrimLeft(msg.Content[len(prefix):], " ")
|
||||
ctx.State["args"] = arg
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SuffixRule check if the text message has the suffix and trim the suffix
|
||||
//
|
||||
// 检查消息后缀
|
||||
func SuffixRule(suffix string) Rule {
|
||||
return SuffixGroupRule(suffix)
|
||||
}
|
||||
|
||||
// SuffixGroupRule check if the text message has the suffix and trim the suffix
|
||||
//
|
||||
// 检查消息后缀
|
||||
func SuffixGroupRule(suffixes ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, suffix := range suffixes {
|
||||
if strings.HasSuffix(msg.Content, suffix) {
|
||||
ctx.State["suffix"] = suffix
|
||||
arg := strings.TrimRight(msg.Content[:len(msg.Content)-len(suffix)], " ")
|
||||
ctx.State["args"] = arg
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CommandRule check if the message is a command and trim the command name
|
||||
//
|
||||
// this rule only supports Message
|
||||
func CommandRule(command string) Rule {
|
||||
return CommandGroupRule(command)
|
||||
}
|
||||
|
||||
// CommandGroupRule check if the message is a command and trim the command name
|
||||
//
|
||||
// this rule only supports Message
|
||||
func CommandGroupRule(commands ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
msg, ok := ctx.Value.(*Message)
|
||||
if !ok || msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
msg.Content = strings.TrimSpace(msg.Content)
|
||||
if msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
cmdMessage := ""
|
||||
args := ""
|
||||
switch {
|
||||
case strings.HasPrefix(msg.Content, "/"):
|
||||
cmdMessage, args, _ = strings.Cut(msg.Content, " ")
|
||||
cmdMessage, _, _ = strings.Cut(cmdMessage, "@")
|
||||
cmdMessage = cmdMessage[1:]
|
||||
default:
|
||||
return false
|
||||
}
|
||||
for _, command := range commands {
|
||||
if strings.HasPrefix(cmdMessage, command) {
|
||||
ctx.State["command"] = command
|
||||
ctx.State["args"] = args
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// RegexRule check if the message can be matched by the regex pattern
|
||||
func RegexRule(regexPattern string) Rule {
|
||||
regex := regexp.MustCompile(regexPattern)
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
if matched := regex.FindStringSubmatch(msg.Content); matched != nil {
|
||||
ctx.State["regex_matched"] = matched
|
||||
return true
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReplyRule check if the message is replying some message
|
||||
//
|
||||
// this rule only supports Message
|
||||
func ReplyRule(messageID string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
msg, ok := ctx.Value.(*Message)
|
||||
if !ok || msg.MessageReference == nil { // 确保无空
|
||||
return false
|
||||
}
|
||||
return messageID == msg.MessageReference.MessageID
|
||||
}
|
||||
}
|
||||
|
||||
func KeywordRule(src string) Rule {
|
||||
return KeywordGroupRule(src)
|
||||
}
|
||||
|
||||
// KeywordGroupRule check if the message has a keyword or keywords
|
||||
func KeywordGroupRule(src ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, str := range src {
|
||||
if strings.Contains(msg.Content, str) {
|
||||
ctx.State["keyword"] = str
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FullMatchRule check if src has the same copy of the message
|
||||
func FullMatchRule(src string) Rule {
|
||||
return FullMatchGroupRule(src)
|
||||
}
|
||||
|
||||
// FullMatchGroupRule check if src has the same copy of the message
|
||||
func FullMatchGroupRule(src ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Content == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, str := range src {
|
||||
if str == msg.Content {
|
||||
ctx.State["matched"] = msg.Content
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ShellRule 定义shell-like规则
|
||||
//
|
||||
// this rule only supports Message
|
||||
func ShellRule(cmd string, model interface{}) Rule {
|
||||
cmdRule := CommandRule(cmd)
|
||||
t := reflect.TypeOf(model)
|
||||
return func(ctx *Ctx) bool {
|
||||
if !cmdRule(ctx) {
|
||||
return false
|
||||
}
|
||||
// bind flag to struct
|
||||
args := ParseShell(ctx.State["args"].(string))
|
||||
val := reflect.New(t)
|
||||
fs := registerFlag(t, val)
|
||||
err := fs.Parse(args)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
ctx.State["args"] = fs.Args()
|
||||
ctx.State["flag"] = val.Interface()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// OnlyToMe only triggered in conditions of @bot or begin with the nicknames
|
||||
//
|
||||
// this rule only supports Message
|
||||
func OnlyToMe(ctx *Ctx) bool {
|
||||
return ctx.IsToMe
|
||||
}
|
||||
|
||||
// CheckUser only triggered by specific person
|
||||
func CheckUser(userID ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Author == nil { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, uid := range userID {
|
||||
if msg.Author.ID == uid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckChannel only triggered in specific channel
|
||||
func CheckChannel(channelID ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.ChannelID == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, cid := range channelID {
|
||||
if msg.ChannelID == cid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckGuild only triggered in specific guild
|
||||
func CheckGuild(guildID ...string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.GuildID == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, gid := range guildID {
|
||||
if msg.GuildID == gid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnlyPrivate requires that the ctx.Event is direct message
|
||||
func OnlyPrivate(ctx *Ctx) bool {
|
||||
if ctx.Type == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(ctx.Type, "Direct")
|
||||
}
|
||||
|
||||
// OnlyPublic requires that the ctx.Event is channel message
|
||||
func OnlyPublic(ctx *Ctx) bool {
|
||||
if ctx.Type == "" { // 确保无空
|
||||
return false
|
||||
}
|
||||
return !strings.HasPrefix(ctx.Type, "Direct")
|
||||
}
|
||||
|
||||
// OnlyChannel requires that the ctx.Event is channel message
|
||||
func OnlyChannel(ctx *Ctx) bool {
|
||||
return OnlyPublic(ctx)
|
||||
}
|
||||
|
||||
// SuperUserPermission only triggered by the bot's owner
|
||||
func SuperUserPermission(ctx *Ctx) bool {
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Author == nil { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, su := range ctx.Caller.SuperUsers {
|
||||
if su == msg.Author.ID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// CreaterPermission only triggered by the creater or higher permission
|
||||
func CreaterPermission(ctx *Ctx) bool {
|
||||
if SuperUserPermission(ctx) {
|
||||
return true
|
||||
}
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Author == nil || msg.Member == nil { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, role := range msg.Member.Roles {
|
||||
if role == RoleIDCreater {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AdminPermission only triggered by the admins or higher permission
|
||||
func AdminPermission(ctx *Ctx) bool {
|
||||
if SuperUserPermission(ctx) {
|
||||
return true
|
||||
}
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Author == nil || msg.Member == nil { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, role := range msg.Member.Roles {
|
||||
if role == RoleIDCreater || role == RoleIDAdmin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ChannelAdminPermission only triggered by the channel admins or higher permission
|
||||
func ChannelAdminPermission(ctx *Ctx) bool {
|
||||
if SuperUserPermission(ctx) {
|
||||
return true
|
||||
}
|
||||
switch msg := ctx.Value.(type) {
|
||||
case *Message:
|
||||
if msg.Author == nil || msg.Member == nil { // 确保无空
|
||||
return false
|
||||
}
|
||||
for _, role := range msg.Member.Roles {
|
||||
if role == RoleIDCreater || role == RoleIDAdmin || role == RoleIDChannelAdmin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// UserOrGrpAdmin 允许用户单独使用或群管使用
|
||||
func UserOrGrpAdmin(ctx *Ctx) bool {
|
||||
if OnlyPublic(ctx) {
|
||||
return AdminPermission(ctx)
|
||||
}
|
||||
return OnlyToMe(ctx)
|
||||
}
|
||||
|
||||
// UserOrChannelAdmin 允许用户单独使用或频道管理使用
|
||||
func UserOrChannelAdmin(ctx *Ctx) bool {
|
||||
if OnlyPublic(ctx) {
|
||||
return ChannelAdminPermission(ctx)
|
||||
}
|
||||
return OnlyToMe(ctx)
|
||||
}
|
||||
|
||||
// HasAttachments 消息包含 Attachments (典型: 图片) 返回 true
|
||||
func HasAttachments(ctx *Ctx) bool {
|
||||
msg, ok := ctx.Value.(*Message)
|
||||
if !ok || len(msg.Attachments) == 0 { // 确保无空
|
||||
return false
|
||||
}
|
||||
ctx.State["attachments"] = msg.Attachments
|
||||
return true
|
||||
}
|
||||
|
||||
// MustProvidePhoto 消息不存在图片阻塞120秒至有图片,超时返回 false
|
||||
func MustProvidePhoto(onmessage string, needphohint, failhint string) Rule {
|
||||
return func(ctx *Ctx) bool {
|
||||
msg, ok := ctx.Value.(*Message)
|
||||
if ok && len(msg.Attachments) > 0 { // 确保无空
|
||||
ctx.State["attachments"] = msg.Attachments
|
||||
return true
|
||||
}
|
||||
// 没有图片就索取
|
||||
if needphohint != "" {
|
||||
_, err := ctx.Caller.PostMessageToChannel(msg.ChannelID, &MessagePost{
|
||||
Content: needphohint,
|
||||
MessageReference: &MessageReference{MessageID: msg.ID},
|
||||
ReplyMessageID: msg.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
next := NewFutureEvent(onmessage, 999, false, ctx.CheckSession(), HasAttachments).Next()
|
||||
select {
|
||||
case <-time.After(time.Second * 120):
|
||||
if failhint != "" {
|
||||
_, _ = ctx.SendPlainMessage(true, failhint)
|
||||
}
|
||||
return false
|
||||
case newCtx := <-next:
|
||||
ctx.State["photos"] = newCtx.State["photos"]
|
||||
ctx.Event = newCtx.Event
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user