mirror of
https://github.com/fumiama/ReiBot.git
synced 2026-06-12 06:00:35 +08:00
add plugin
This commit is contained in:
39
README.md
39
README.md
@@ -5,9 +5,42 @@ Lightweight Telegram bot framework
|
|||||||
|
|
||||||
This framework is a simple wrapper for [go-telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api), aiming to make the event processing easier.
|
This framework is a simple wrapper for [go-telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api), aiming to make the event processing easier.
|
||||||
|
|
||||||
## Example
|
## Quick Start
|
||||||
|
> Here is a plugin-based example
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
See under `example` folder or below.
|
import (
|
||||||
|
rei "github.com/fumiama/ReiBot"
|
||||||
|
tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rei.OnMessagePrefix("echo").SetBlock(true).SecondPriority().
|
||||||
|
Handle(func(ctx *rei.Ctx) {
|
||||||
|
args := ctx.State["args"].(string)
|
||||||
|
if args == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := ctx.Value.(*tgba.Message)
|
||||||
|
ctx.Caller.Send(tgba.NewMessage(msg.Chat.ID, args))
|
||||||
|
})
|
||||||
|
rei.Run(rei.Bot{
|
||||||
|
Token: "",
|
||||||
|
Buffer: 256,
|
||||||
|
UpdateConfig: tgba.UpdateConfig{
|
||||||
|
Offset: 0,
|
||||||
|
Limit: 0,
|
||||||
|
Timeout: 60,
|
||||||
|
},
|
||||||
|
Debug: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Event-Based
|
||||||
|
|
||||||
|
> If Handler in Bot is implemented, the plugin function will be disabled.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -32,7 +65,7 @@ func main() {
|
|||||||
Timeout: 60,
|
Timeout: 60,
|
||||||
},
|
},
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Handler: rei.Handler{
|
Handler: &rei.Handler{
|
||||||
OnMessage: func(updateid int, bot *rei.TelegramClient, msg *tgba.Message) {
|
OnMessage: func(updateid int, bot *rei.TelegramClient, msg *tgba.Message) {
|
||||||
if len(msg.Text) <= len("测试") {
|
if len(msg.Text) <= len("测试") {
|
||||||
return
|
return
|
||||||
|
|||||||
2
bot.go
2
bot.go
@@ -17,7 +17,7 @@ type Bot struct {
|
|||||||
// Debug 控制调试信息的输出与否
|
// Debug 控制调试信息的输出与否
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
// Handler 注册对各种事件的处理
|
// Handler 注册对各种事件的处理
|
||||||
Handler Handler
|
Handler *Handler
|
||||||
// handlers 方便调用的 handler
|
// handlers 方便调用的 handler
|
||||||
handlers map[string]GeneralHandleType
|
handlers map[string]GeneralHandleType
|
||||||
}
|
}
|
||||||
|
|||||||
24
client.go
24
client.go
@@ -18,18 +18,20 @@ type TelegramClient struct {
|
|||||||
// NewTelegramClient ...
|
// NewTelegramClient ...
|
||||||
func NewTelegramClient(c *Bot) (tc TelegramClient) {
|
func NewTelegramClient(c *Bot) (tc TelegramClient) {
|
||||||
tc.b = *c
|
tc.b = *c
|
||||||
h := reflect.ValueOf(&tc.b.Handler).Elem()
|
if tc.b.Handler != nil {
|
||||||
t := h.Type()
|
h := reflect.ValueOf(tc.b.Handler).Elem()
|
||||||
tc.b.handlers = make(map[string]GeneralHandleType, 16)
|
t := h.Type()
|
||||||
for i := 0; i < h.NumField(); i++ {
|
tc.b.handlers = make(map[string]GeneralHandleType, 16)
|
||||||
f := h.Field(i)
|
for i := 0; i < h.NumField(); i++ {
|
||||||
if f.IsZero() {
|
f := h.Field(i)
|
||||||
continue
|
if f.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tp := t.Field(i).Name[2:]
|
||||||
|
log.Println("[INFO] register handler", tp)
|
||||||
|
handler := f.Interface()
|
||||||
|
tc.b.handlers[tp] = *(*GeneralHandleType)(unsafe.Add(unsafe.Pointer(&handler), unsafe.Sizeof(uintptr(0))))
|
||||||
}
|
}
|
||||||
tp := t.Field(i).Name[2:]
|
|
||||||
log.Println("[INFO] register handler", tp)
|
|
||||||
handler := f.Interface()
|
|
||||||
tc.b.handlers[tp] = *(*GeneralHandleType)(unsafe.Add(unsafe.Pointer(&handler), unsafe.Sizeof(uintptr(0))))
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
8
context.go
Normal file
8
context.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package rei
|
||||||
|
|
||||||
|
type Ctx struct {
|
||||||
|
Event
|
||||||
|
State
|
||||||
|
Caller *TelegramClient
|
||||||
|
ma *Matcher
|
||||||
|
}
|
||||||
176
engine.go
Normal file
176
engine.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package rei
|
||||||
|
|
||||||
|
// New 生成空引擎
|
||||||
|
func NewEngine() *Engine {
|
||||||
|
return &Engine{
|
||||||
|
preHandler: []Rule{},
|
||||||
|
midHandler: []Rule{},
|
||||||
|
postHandler: []Process{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultEngine = NewEngine()
|
||||||
|
|
||||||
|
// Engine is the pre_handler, mid_handler, post_handler manager
|
||||||
|
type Engine struct {
|
||||||
|
preHandler []Rule
|
||||||
|
midHandler []Rule
|
||||||
|
postHandler []Process
|
||||||
|
matchers []*Matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 移除该 Engine 注册的所有 Matchers
|
||||||
|
func (e *Engine) Delete() {
|
||||||
|
for _, m := range e.matchers {
|
||||||
|
m.Delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsePreHandler 向该 Engine 添加新 PreHandler(Rule),
|
||||||
|
// 会在 Rule 判断前触发,如果 preHandler
|
||||||
|
// 没有通过,则 Rule, Matcher 不会触发
|
||||||
|
//
|
||||||
|
// 可用于分群组管理插件等
|
||||||
|
func (e *Engine) UsePreHandler(rules ...Rule) {
|
||||||
|
e.preHandler = append(e.preHandler, rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseMidHandler 向该 Engine 添加新 MidHandler(Rule),
|
||||||
|
// 会在 Rule 判断后, Matcher 触发前触发,如果 midHandler
|
||||||
|
// 没有通过,则 Matcher 不会触发
|
||||||
|
//
|
||||||
|
// 可用于速率限制等
|
||||||
|
func (e *Engine) UseMidHandler(rules ...Rule) {
|
||||||
|
e.midHandler = append(e.midHandler, rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsePostHandler 向该 Engine 添加新 PostHandler(Rule),
|
||||||
|
// 会在 Matcher 触发后触发,如果 PostHandler 返回 false,
|
||||||
|
// 则后续的 post handler 不会触发
|
||||||
|
//
|
||||||
|
// 可用于速率限制等
|
||||||
|
func (e *Engine) UsePostHandler(handler ...Process) {
|
||||||
|
e.postHandler = append(e.postHandler, handler...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On 添加新的指定消息类型的匹配器(默认Engine)
|
||||||
|
func On(typ string, rules ...Rule) *Matcher { return defaultEngine.On(typ, rules...) }
|
||||||
|
|
||||||
|
// On 添加新的指定消息类型的匹配器
|
||||||
|
func (e *Engine) On(typ string, rules ...Rule) *Matcher {
|
||||||
|
matcher := &Matcher{
|
||||||
|
Type: typ,
|
||||||
|
Rules: rules,
|
||||||
|
Engine: e,
|
||||||
|
}
|
||||||
|
e.matchers = append(e.matchers, matcher)
|
||||||
|
return StoreMatcher(matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnMessage 消息触发器
|
||||||
|
func (e *Engine) OnMessage(rules ...Rule) *Matcher { return e.On("Message", rules...) }
|
||||||
|
|
||||||
|
// OnMessage 消息触发器
|
||||||
|
func OnMessage(rules ...Rule) *Matcher { return On("Message", rules...) }
|
||||||
|
|
||||||
|
// OnEditedMessage 修改消息触发器
|
||||||
|
func (e *Engine) OnEditedMessage(rules ...Rule) *Matcher { return e.On("EditedMessage", rules...) }
|
||||||
|
|
||||||
|
// OnEditedMessage 修改消息触发器
|
||||||
|
func OnEditedMessage(rules ...Rule) *Matcher { return On("EditedMessage", rules...) }
|
||||||
|
|
||||||
|
// OnChannelPost ...
|
||||||
|
func (e *Engine) OnChannelPost(rules ...Rule) *Matcher { return e.On("ChannelPost", rules...) }
|
||||||
|
|
||||||
|
// OnChannelPost ...
|
||||||
|
func OnChannelPost(rules ...Rule) *Matcher { return On("ChannelPost", rules...) }
|
||||||
|
|
||||||
|
// OnEditedChannelPost ...
|
||||||
|
func (e *Engine) OnEditedChannelPost(rules ...Rule) *Matcher {
|
||||||
|
return e.On("EditedChannelPost", rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEditedChannelPost ...
|
||||||
|
func OnEditedChannelPost(rules ...Rule) *Matcher {
|
||||||
|
return On("EditedChannelPost", rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnInlineQuery ...
|
||||||
|
func (e *Engine) OnInlineQuery(rules ...Rule) *Matcher { return e.On("InlineQuery", rules...) }
|
||||||
|
|
||||||
|
// OnInlineQuery ...
|
||||||
|
func OnInlineQuery(rules ...Rule) *Matcher { return On("InlineQuery", rules...) }
|
||||||
|
|
||||||
|
// OnChosenInlineResult ...
|
||||||
|
func (e *Engine) OnChosenInlineResult(rules ...Rule) *Matcher {
|
||||||
|
return e.On("ChosenInlineResult", rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnChosenInlineResult ...
|
||||||
|
func OnChosenInlineResult(rules ...Rule) *Matcher { return On("ChosenInlineResult", rules...) }
|
||||||
|
|
||||||
|
// OnCallbackQuery ...
|
||||||
|
func (e *Engine) OnCallbackQuery(rules ...Rule) *Matcher { return e.On("CallbackQuery", rules...) }
|
||||||
|
|
||||||
|
// OnCallbackQuery ...
|
||||||
|
func OnCallbackQuery(rules ...Rule) *Matcher { return On("CallbackQuery", rules...) }
|
||||||
|
|
||||||
|
// OnShippingQuery ...
|
||||||
|
func (e *Engine) OnShippingQuery(rules ...Rule) *Matcher { return e.On("ShippingQuery", rules...) }
|
||||||
|
|
||||||
|
// OnShippingQuery ...
|
||||||
|
func OnShippingQuery(rules ...Rule) *Matcher { return On("ShippingQuery", rules...) }
|
||||||
|
|
||||||
|
// OnPreCheckoutQuery ...
|
||||||
|
func (e *Engine) OnPreCheckoutQuery(rules ...Rule) *Matcher {
|
||||||
|
return e.On("PreCheckoutQuery", rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPreCheckoutQuery ...
|
||||||
|
func OnPreCheckoutQuery(rules ...Rule) *Matcher { return On("PreCheckoutQuery", rules...) }
|
||||||
|
|
||||||
|
// OnPoll ...
|
||||||
|
func (e *Engine) OnPoll(rules ...Rule) *Matcher { return e.On("Poll", rules...) }
|
||||||
|
|
||||||
|
// OnPoll ...
|
||||||
|
func OnPoll(rules ...Rule) *Matcher { return On("Poll", rules...) }
|
||||||
|
|
||||||
|
// OnPollAnswer ...
|
||||||
|
func (e *Engine) OnPollAnswer(rules ...Rule) *Matcher { return e.On("PollAnswer", rules...) }
|
||||||
|
|
||||||
|
// OnPollAnswer ...
|
||||||
|
func OnPollAnswer(rules ...Rule) *Matcher { return On("PollAnswer", rules...) }
|
||||||
|
|
||||||
|
// OnMyChatMember ...
|
||||||
|
func (e *Engine) OnMyChatMember(rules ...Rule) *Matcher { return e.On("MyChatMember", rules...) }
|
||||||
|
|
||||||
|
// OnMyChatMember ...
|
||||||
|
func OnMyChatMember(rules ...Rule) *Matcher { return On("MyChatMember", rules...) }
|
||||||
|
|
||||||
|
// OnChatMember ...
|
||||||
|
func (e *Engine) OnChatMember(rules ...Rule) *Matcher { return e.On("ChatMember", rules...) }
|
||||||
|
|
||||||
|
// OnChatMember ...
|
||||||
|
func OnChatMember(rules ...Rule) *Matcher { return On("ChatMember", rules...) }
|
||||||
|
|
||||||
|
// OnChatJoinRequest ...
|
||||||
|
func (e *Engine) OnChatJoinRequest(rules ...Rule) *Matcher { return e.On("ChatJoinRequest", rules...) }
|
||||||
|
|
||||||
|
// OnChatJoinRequest ...
|
||||||
|
func OnChatJoinRequest(rules ...Rule) *Matcher { return On("ChatJoinRequest", rules...) }
|
||||||
|
|
||||||
|
// OnPrefix 前缀触发器
|
||||||
|
func OnMessagePrefix(prefix string, rules ...Rule) *Matcher {
|
||||||
|
return defaultEngine.OnMessagePrefix(prefix, rules...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPrefix 前缀触发器
|
||||||
|
func (e *Engine) OnMessagePrefix(prefix string, rules ...Rule) *Matcher {
|
||||||
|
matcher := &Matcher{
|
||||||
|
Type: "Message",
|
||||||
|
Rules: append([]Rule{PrefixRule(prefix)}, rules...),
|
||||||
|
Engine: e,
|
||||||
|
}
|
||||||
|
e.matchers = append(e.matchers, matcher)
|
||||||
|
return StoreMatcher(matcher)
|
||||||
|
}
|
||||||
80
event.go
80
event.go
@@ -14,7 +14,7 @@ type Event struct {
|
|||||||
// UpdateID is the update's unique identifier.
|
// UpdateID is the update's unique identifier.
|
||||||
UpdateID int
|
UpdateID int
|
||||||
// Value is the non-null field value in Update
|
// Value is the non-null field value in Update
|
||||||
Value reflect.Value
|
Value any
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *TelegramClient) processEvent(update tgba.Update) {
|
func (tc *TelegramClient) processEvent(update tgba.Update) {
|
||||||
@@ -26,6 +26,29 @@ func (tc *TelegramClient) processEvent(update tgba.Update) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tp := t.Field(i).Name
|
tp := t.Field(i).Name
|
||||||
|
if tc.b.Handler == nil {
|
||||||
|
matcherLock.RLock()
|
||||||
|
n := len(matcherMap[tp])
|
||||||
|
if n == 0 {
|
||||||
|
matcherLock.RUnlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("[INFO] pass", tp, "event to plugins")
|
||||||
|
matchers := make([]*Matcher, n)
|
||||||
|
copy(matchers, matcherMap[tp])
|
||||||
|
matcherLock.RUnlock()
|
||||||
|
ctx := &Ctx{
|
||||||
|
Event: Event{
|
||||||
|
Type: tp,
|
||||||
|
UpdateID: update.UpdateID,
|
||||||
|
Value: f.Interface(),
|
||||||
|
},
|
||||||
|
State: State{},
|
||||||
|
Caller: tc,
|
||||||
|
}
|
||||||
|
match(ctx, matchers)
|
||||||
|
continue
|
||||||
|
}
|
||||||
h, ok := tc.b.handlers[tp]
|
h, ok := tc.b.handlers[tp]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
@@ -34,3 +57,58 @@ func (tc *TelegramClient) processEvent(update tgba.Update) {
|
|||||||
go h(update.UpdateID, tc, f.UnsafePointer())
|
go h(update.UpdateID, tc, f.UnsafePointer())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func match(ctx *Ctx, matchers []*Matcher) {
|
||||||
|
loop:
|
||||||
|
for _, matcher := range matchers {
|
||||||
|
for k := range ctx.State { // Clear State
|
||||||
|
delete(ctx.State, k)
|
||||||
|
}
|
||||||
|
matcherLock.RLock()
|
||||||
|
m := matcher.copy()
|
||||||
|
matcherLock.RUnlock()
|
||||||
|
ctx.ma = m
|
||||||
|
|
||||||
|
// pre handler
|
||||||
|
if m.Engine != nil {
|
||||||
|
for _, handler := range m.Engine.preHandler {
|
||||||
|
if !handler(ctx) { // 有 pre handler 未满足
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range m.Rules {
|
||||||
|
if rule != nil && !rule(ctx) { // 有 Rule 的条件未满足
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mid handler
|
||||||
|
if m.Engine != nil {
|
||||||
|
for _, handler := range m.Engine.midHandler {
|
||||||
|
if !handler(ctx) { // 有 mid handler 未满足
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Process != nil {
|
||||||
|
m.Process(ctx) // 处理事件
|
||||||
|
}
|
||||||
|
if matcher.Temp { // 临时 Matcher 删除
|
||||||
|
matcher.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Engine != nil {
|
||||||
|
// post handler
|
||||||
|
for _, handler := range m.Engine.postHandler {
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Block { // 阻断后续
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
48
example/handler/main.go
Normal file
48
example/handler/main.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
rei "github.com/fumiama/ReiBot"
|
||||||
|
tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rei.Run(rei.Bot{
|
||||||
|
Token: "",
|
||||||
|
Buffer: 256,
|
||||||
|
UpdateConfig: tgba.UpdateConfig{
|
||||||
|
Offset: 0,
|
||||||
|
Limit: 0,
|
||||||
|
Timeout: 60,
|
||||||
|
},
|
||||||
|
Debug: true,
|
||||||
|
Handler: &rei.Handler{
|
||||||
|
OnMessage: func(updateid int, bot *rei.TelegramClient, msg *tgba.Message) {
|
||||||
|
if len(msg.Text) <= len("测试") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(msg.Text, "测试") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err := bot.Send(tgba.NewMessage(msg.Chat.ID, msg.Text[len("测试"):]))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[ERRO]", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OnEditedMessage: func(updateid int, bot *rei.TelegramClient, msg *tgba.Message) {
|
||||||
|
if len(msg.Text) <= len("测试") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(msg.Text, "测试") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err := bot.Send(tgba.NewMessage(msg.Chat.ID, "已编辑:"+msg.Text[len("测试"):]))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[ERRO]", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
rei "github.com/fumiama/ReiBot"
|
rei "github.com/fumiama/ReiBot"
|
||||||
tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
rei.OnMessagePrefix("echo").SetBlock(true).SecondPriority().
|
||||||
|
Handle(func(ctx *rei.Ctx) {
|
||||||
|
args := ctx.State["args"].(string)
|
||||||
|
if args == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := ctx.Value.(*tgba.Message)
|
||||||
|
ctx.Caller.Send(tgba.NewMessage(msg.Chat.ID, args))
|
||||||
|
})
|
||||||
rei.Run(rei.Bot{
|
rei.Run(rei.Bot{
|
||||||
Token: "",
|
Token: "",
|
||||||
Buffer: 256,
|
Buffer: 256,
|
||||||
@@ -18,31 +24,5 @@ func main() {
|
|||||||
Timeout: 60,
|
Timeout: 60,
|
||||||
},
|
},
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Handler: rei.Handler{
|
|
||||||
OnMessage: func(updateid int, bot *rei.TelegramClient, msg *tgba.Message) {
|
|
||||||
if len(msg.Text) <= len("测试") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(msg.Text, "测试") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := bot.Send(tgba.NewMessage(msg.Chat.ID, msg.Text[len("测试"):]))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[ERRO]", err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
OnEditedMessage: func(updateid int, bot *rei.TelegramClient, msg *tgba.Message) {
|
|
||||||
if len(msg.Text) <= len("测试") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(msg.Text, "测试") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err := bot.Send(tgba.NewMessage(msg.Chat.ID, "已编辑:"+msg.Text[len("测试"):]))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[ERRO]", err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
130
matcher.go
Normal file
130
matcher.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package rei
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Rule filter the event
|
||||||
|
Rule func(ctx *Ctx) bool
|
||||||
|
// Process 事件处理函数
|
||||||
|
Process func(ctx *Ctx)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Matcher 是 ZeroBot 匹配和处理事件的最小单元
|
||||||
|
type Matcher struct {
|
||||||
|
// Temp 是否为临时Matcher,临时 Matcher 匹配一次后就会删除当前 Matcher
|
||||||
|
Temp bool
|
||||||
|
// Block 是否阻断后续 Matcher,为 true 时当前Matcher匹配成功后,后续Matcher不参与匹配
|
||||||
|
Block bool
|
||||||
|
// Priority 优先级,越小优先级越高
|
||||||
|
Priority int
|
||||||
|
// Event 当前匹配到的事件
|
||||||
|
Event *Event
|
||||||
|
// Type 匹配的事件类型
|
||||||
|
Type string
|
||||||
|
// Rules 匹配规则
|
||||||
|
Rules []Rule
|
||||||
|
// Process 处理事件的函数
|
||||||
|
Process Process
|
||||||
|
// Engine 注册 Matcher 的 Engine,Engine可为一系列 Matcher 添加通用 Rule 和 其他钩子
|
||||||
|
Engine *Engine
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// 所有主匹配器列表
|
||||||
|
matcherMap = make(map[string][]*Matcher, 0)
|
||||||
|
// Matcher 修改读写锁
|
||||||
|
matcherLock = sync.RWMutex{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// State store the context of a matcher.
|
||||||
|
type State map[string]interface{}
|
||||||
|
|
||||||
|
func sortMatcher(typ string) {
|
||||||
|
sort.Slice(matcherMap[typ], func(i, j int) bool { // 按优先级排序
|
||||||
|
return matcherMap[typ][i].Priority < matcherMap[typ][j].Priority
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBlock 设置是否阻断后面的 Matcher 触发
|
||||||
|
func (m *Matcher) SetBlock(block bool) *Matcher {
|
||||||
|
m.Block = block
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPriority 设置当前 Matcher 优先级
|
||||||
|
func (m *Matcher) SetPriority(priority int) *Matcher {
|
||||||
|
matcherLock.Lock()
|
||||||
|
defer matcherLock.Unlock()
|
||||||
|
m.Priority = priority
|
||||||
|
sortMatcher(m.Type)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstPriority 设置当前 Matcher 优先级 - 0
|
||||||
|
func (m *Matcher) FirstPriority() *Matcher {
|
||||||
|
return m.SetPriority(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecondPriority 设置当前 Matcher 优先级 - 1
|
||||||
|
func (m *Matcher) SecondPriority() *Matcher {
|
||||||
|
return m.SetPriority(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThirdPriority 设置当前 Matcher 优先级 - 2
|
||||||
|
func (m *Matcher) ThirdPriority() *Matcher {
|
||||||
|
return m.SetPriority(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindEngine bind the matcher to a engine
|
||||||
|
func (m *Matcher) BindEngine(e *Engine) *Matcher {
|
||||||
|
m.Engine = e
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreMatcher store a matcher to matcher list.
|
||||||
|
func StoreMatcher(m *Matcher) *Matcher {
|
||||||
|
matcherLock.Lock()
|
||||||
|
defer matcherLock.Unlock()
|
||||||
|
matcherMap[m.Type] = append(matcherMap[m.Type], m)
|
||||||
|
sortMatcher(m.Type)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreTempMatcher store a matcher only triggered once.
|
||||||
|
func StoreTempMatcher(m *Matcher) *Matcher {
|
||||||
|
m.Temp = true
|
||||||
|
StoreMatcher(m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete remove the matcher from list
|
||||||
|
func (m *Matcher) Delete() {
|
||||||
|
matcherLock.Lock()
|
||||||
|
defer matcherLock.Unlock()
|
||||||
|
for i, matcher := range matcherMap[m.Type] {
|
||||||
|
if m == matcher {
|
||||||
|
matcherMap[m.Type] = append(matcherMap[m.Type][:i], matcherMap[m.Type][i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) copy() *Matcher {
|
||||||
|
return &Matcher{
|
||||||
|
Type: m.Type,
|
||||||
|
Rules: m.Rules,
|
||||||
|
Block: m.Block,
|
||||||
|
Priority: m.Priority,
|
||||||
|
Process: m.Process,
|
||||||
|
Temp: m.Temp,
|
||||||
|
Engine: m.Engine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 直接处理事件
|
||||||
|
func (m *Matcher) Handle(handler Process) *Matcher {
|
||||||
|
m.Process = handler
|
||||||
|
return m
|
||||||
|
}
|
||||||
28
rules.go
Normal file
28
rules.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package rei
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrefixRule check if the message has the prefix and trim the prefix
|
||||||
|
//
|
||||||
|
// 检查消息前缀
|
||||||
|
func PrefixRule(prefixes ...string) Rule {
|
||||||
|
return func(ctx *Ctx) bool {
|
||||||
|
msg, ok := ctx.Value.(*tgba.Message)
|
||||||
|
if !ok || msg.Text == "" { // 确保无空
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if strings.HasPrefix(msg.Text, prefix) {
|
||||||
|
ctx.State["prefix"] = prefix
|
||||||
|
arg := strings.TrimLeft(msg.Text[len(prefix):], " ")
|
||||||
|
ctx.State["args"] = arg
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user