Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

消息模板匹配器 #91

Merged
merged 54 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
27353f5
add new Matcher `PatternMatcher`
RikaCelery Oct 7, 2024
e175810
replace with const value
RikaCelery Oct 9, 2024
816234b
remove unused function
RikaCelery Oct 9, 2024
392941b
remove `OnPattern`
RikaCelery Oct 10, 2024
22c4ec8
remove unused const
RikaCelery Oct 10, 2024
ba285e3
add `PatternModel`
RikaCelery Oct 10, 2024
0635fc1
type safe PatternRule state
RikaCelery Oct 10, 2024
ece50eb
add `PatternReply`
RikaCelery Oct 10, 2024
66f9382
remove redundant `at` element after `reply` element
RikaCelery Oct 10, 2024
32b5226
fix wrong key && catch error
RikaCelery Oct 10, 2024
24877ed
Optional PatternSegment
RikaCelery Oct 10, 2024
e810fbf
use pointer
RikaCelery Oct 10, 2024
e4950df
fix nil
RikaCelery Oct 10, 2024
7101db5
move patterns to pattern.go
RikaCelery Oct 11, 2024
88963dc
set State in `patternMatch`
RikaCelery Oct 12, 2024
ab9dbeb
match tests
RikaCelery Oct 12, 2024
3752551
fix optional not work if len(msg) < len(pattern)
RikaCelery Oct 12, 2024
63b4e1f
match tests
RikaCelery Oct 12, 2024
cc54177
Merge remote-tracking branch 'origin/feat/pattern-matcher' into feat/…
RikaCelery Oct 12, 2024
9ecfc51
return empty value if not Valid
RikaCelery Oct 12, 2024
d501a63
fix test failed
RikaCelery Oct 12, 2024
07a5fcb
ignore empty message
RikaCelery Oct 12, 2024
039a7ed
change `At` parsed value to string
RikaCelery Oct 12, 2024
2e7bdfb
Merge remote-tracking branch 'upstream/main' into feat/pattern-matcher
RikaCelery Oct 12, 2024
9403051
Merge remote-tracking branch 'upstream/main' into feat/pattern-matcher
RikaCelery Oct 12, 2024
7526ee8
chore: make lint happy
RikaCelery Oct 12, 2024
6050559
optimize
RikaCelery Oct 12, 2024
0f30441
chore: make lint happy
RikaCelery Oct 12, 2024
b3366ef
move PatternRule to pattern.go
RikaCelery Oct 13, 2024
8dadba4
move KeyPattern to pattern.go
RikaCelery Oct 13, 2024
4524132
chained PatternRule builder
RikaCelery Oct 13, 2024
09e5c79
rename value getter
RikaCelery Oct 13, 2024
2c5cfea
optimize
RikaCelery Oct 13, 2024
2c51768
rename `containsOptional` to `mustMatchAllPatterns`
RikaCelery Oct 13, 2024
c1bd251
make `PatternParse` fields private
RikaCelery Oct 13, 2024
5fd8dd4
Merge remote-tracking branch 'origin/feat/pattern-matcher' into feat/…
RikaCelery Oct 13, 2024
8b77550
optimize
RikaCelery Oct 13, 2024
0f48fe7
optimize
RikaCelery Oct 13, 2024
4b95413
PatternSegment: make `Type` and `Parse` private
RikaCelery Oct 13, 2024
4e89e07
Merge remote-tracking branch 'origin/feat/pattern-matcher' into feat/…
RikaCelery Oct 13, 2024
78ce239
fixup! rename `containsOptional` to `mustMatchAllPatterns`
RikaCelery Oct 13, 2024
455afde
make lint happy
RikaCelery Oct 13, 2024
f606a75
At Pattern use `message.ID` parameters
RikaCelery Oct 13, 2024
6dfbd27
PatternSegment: make `optional` private
RikaCelery Oct 13, 2024
370fda4
`Raw` gatter
RikaCelery Oct 13, 2024
18dbda7
`PatternSegment` builder
RikaCelery Oct 13, 2024
2a96084
`Any` PatternSegment
RikaCelery Oct 13, 2024
6ac4209
tests for `Any`
RikaCelery Oct 13, 2024
7bbb37b
option for cleaning useless sibling `at` after `reply`
RikaCelery Oct 13, 2024
6b486c1
fix wrong clean logic
RikaCelery Oct 13, 2024
2077580
refactor: extract parser, custom parser support
RikaCelery Oct 14, 2024
dbe0b4d
refactor: make `cleanRedundantAt` an option of `Pattern`
RikaCelery Oct 14, 2024
df7234b
fix: fix stupid bugs
RikaCelery Oct 14, 2024
43ae5cd
use for range loop
RikaCelery Oct 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions extension/model.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package extension

import zero "github.com/wdvxdr1123/ZeroBot"

// PrefixModel is model of zero.PrefixRule
type PrefixModel struct {
Prefix string `zero:"prefix"`
Expand Down Expand Up @@ -32,3 +34,8 @@ type FullMatchModel struct {
type RegexModel struct {
Matched []string `zero:"regex_matched"`
}

// PatternModel is model of zero.PatternRule
type PatternModel struct {
Matched []*zero.PatternParsed `zero:"pattern_matched"`
}
195 changes: 195 additions & 0 deletions pattern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package zero

import (
"github.com/wdvxdr1123/ZeroBot/message"
"regexp"
"strings"
)

type Pattern []PatternSegment

func NewPattern() *Pattern {
pattern := make(Pattern, 0)
fumiama marked this conversation as resolved.
Show resolved Hide resolved
return &pattern
}

type PatternSegment struct {
Type string
Optional bool
Parse func(msg *message.MessageSegment) *PatternParsed
}

// SetOptional set previous segment is optional, is v is empty, Optional will be true
// if Pattern is empty, panic
func (p *Pattern) SetOptional(v ...bool) *Pattern {
if len(*p) == 0 {
panic("pattern is empty")
}
if len(v) == 1 {
(*p)[len(*p)-1].Optional = v[0]
} else {
(*p)[len(*p)-1].Optional = true
}
return p
}

// PatternParsed PatternRule parse result
type PatternParsed struct {
Valid bool
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
Value any
Msg *message.MessageSegment
}

func (p PatternParsed) GetText() []string {
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
if !p.Valid {
return make([]string, 0)
fumiama marked this conversation as resolved.
Show resolved Hide resolved
}
return p.Value.([]string)
}
func (p PatternParsed) GetAt() string {
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
if !p.Valid {
return ""
}
return p.Value.(string)
}
func (p PatternParsed) GetImage() string {
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
if !p.Valid {
return ""
}
return p.Value.(string)
}
func (p PatternParsed) GetReply() string {
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
if !p.Valid {
return ""
}
return p.Value.(string)
}

// Text use regex to search a 'text' segment
func (p *Pattern) Text(regex string) *Pattern {
re := regexp.MustCompile(regex)
pattern := PatternSegment{
Type: "text",
Parse: func(msg *message.MessageSegment) *PatternParsed {
s := msg.Data["text"]
s = strings.Trim(s, " \n\r\t")
matchString := re.MatchString(s)
if matchString {
return &PatternParsed{
Valid: true,
Value: re.FindStringSubmatch(s),
Msg: msg,
}
} else {
return &PatternParsed{
Valid: false,
Value: nil,
Msg: nil,
}
}
},
}
*p = append(*p, pattern)
return p
}

// At use regex to match an 'at' segment, if id is not empty, only match specific target
func (p *Pattern) At(id ...string) *Pattern {
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
if len(id) > 1 {
panic("at pattern only support one id")
}
pattern := PatternSegment{
Type: "at",
Parse: func(msg *message.MessageSegment) *PatternParsed {
if len(id) == 0 || len(id) == 1 && id[0] == msg.Data["qq"] {
return &PatternParsed{
Valid: true,
Value: msg.Data["qq"],
Msg: msg,
}
} else {
return &PatternParsed{
Valid: false,
Value: nil,
Msg: nil,
}
}
},
}
*p = append(*p, pattern)
return p
}

// Image use regex to match an 'at' segment, if id is not empty, only match specific target
func (p *Pattern) Image() *Pattern {
pattern := PatternSegment{
Type: "image",
Parse: func(msg *message.MessageSegment) *PatternParsed {
return &PatternParsed{
Valid: true,
Value: msg.Data["file"],
Msg: msg,
}
},
}
*p = append(*p, pattern)
return p
}

// Reply type zero.PatternReplyMatched
func (p *Pattern) Reply() *Pattern {
pattern := PatternSegment{
Type: "reply",
Parse: func(msg *message.MessageSegment) *PatternParsed {
return &PatternParsed{
Valid: true,
Value: msg.Data["id"],
Msg: msg,
}
},
}
*p = append(*p, pattern)
return p
}
func containsOptional(pattern Pattern) bool {
RikaCelery marked this conversation as resolved.
Show resolved Hide resolved
for _, p := range pattern {
if p.Optional {
return true
}
}
return false
}
func patternMatch(ctx *Ctx, pattern Pattern, msgs []message.MessageSegment) bool {
if !containsOptional(pattern) && len(pattern) != len(msgs) {
return false
}
if _, ok := ctx.State[KEY_PATTERN]; !ok {
ctx.State[KEY_PATTERN] = make([]*PatternParsed, 0, 1)
}
i := 0
j := 0
for i < len(pattern) {
var parsed *PatternParsed
if j < len(msgs) && pattern[i].Type == (msgs[j].Type) {
parsed = pattern[i].Parse(&msgs[j])
} else {
parsed = &PatternParsed{
fumiama marked this conversation as resolved.
Show resolved Hide resolved
Valid: false,
Value: nil,
Msg: nil,
}
}
if j >= len(msgs) || pattern[i].Type != (msgs[j].Type) || !parsed.Valid {
if pattern[i].Optional {
ctx.State[KEY_PATTERN] = append(ctx.State[KEY_PATTERN].([]*PatternParsed), parsed)
i++
continue
}
return false
}
ctx.State[KEY_PATTERN] = append(ctx.State[KEY_PATTERN].([]*PatternParsed), parsed)
i++
j++
}
return true
}
Loading
Loading