1
0
mirror of https://github.com/fumiama/ReiBot.git synced 2026-06-05 00:50:25 +08:00
Files
ReiBot/event.go
2022-11-24 18:36:37 +08:00

277 lines
7.0 KiB
Go

package rei
import (
"reflect"
"strings"
base14 "github.com/fumiama/go-base16384"
tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5"
log "github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
// Event ...
type Event struct {
// Type is the non-null field name in Update
Type string
// UpdateID is the update's unique identifier.
UpdateID int
// Value is the non-null field value in Update
Value any
// value is the reflect value of Value
value reflect.Value
}
func (tc *TelegramClient) processEvent(update tgba.Update) {
v := reflect.ValueOf(&update).Elem()
t := reflect.ValueOf(&update).Elem().Type()
for i := 1; i < v.NumField(); i++ {
f := v.Field(i)
if f.IsZero() {
continue
}
tp := t.Field(i).Name
if tc.b.Handler == nil {
matcherLock.RLock()
n := len(matcherMap[tp])
if n == 0 {
matcherLock.RUnlock()
continue
}
log.Debugln("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(),
value: f,
},
State: State{},
Caller: tc,
}
switch tp {
case "Message":
ctx.Message = (*tgba.Message)(f.UnsafePointer())
if ctx.Message.From == nil {
ctx.Message.From = &tgba.User{}
}
log.Println("receive Message Text from", ctx.Message.From.ID, ":", ctx.Message.Text)
case "CallbackQuery":
c := (*tgba.CallbackQuery)(f.UnsafePointer())
ctx.Message = c.Message
if c.From == nil {
c.From = &tgba.User{}
}
log.Println("receive CallbackQuery Data from", c.From.ID, ":", c.Data)
}
go match(ctx, matchers)
continue
}
h, ok := tc.b.handlers[tp]
if !ok {
continue
}
log.Debugln("process", tp, "event")
go h(update.UpdateID, tc, f.UnsafePointer())
}
}
func match(ctx *Ctx, matchers []*Matcher) {
if ctx.Message != nil && ctx.Event.Type == "Message" {
// Caption也当作消息处理
if ctx.Message.Text == "" && ctx.Message.Caption != "" {
ctx.Message.Text = ctx.Message.Caption
ctx.Message.Entities = ctx.Message.CaptionEntities
log.Println("cpoy Message Caption to Text:", ctx.Message.Text)
}
}
if ctx.Message != nil && ctx.Event.Type == "Message" && ctx.Message.Text != "" { // 确保无空
ctx.IsToMe = func(ctx *Ctx) bool {
if ctx.Message.Chat.IsPrivate() {
log.Debugln("[event] private event")
return true
}
name := ctx.Caller.Self.String()
if strings.HasPrefix(ctx.Message.Text, name) {
log.Debugln("[event] message before process:", ctx.Message.Text)
if len(ctx.Message.Entities) > 0 {
n := len(name)
for i := n; ctx.Message.Text[i] == ' '; i++ {
n++
}
c := 0
i := 0
for _, e := range ctx.Message.Entities {
c += e.Length
if c >= n {
break
}
i++
}
if i > 0 {
switch {
case c < n:
ctx.Message.Entities = nil
case c == n:
if i+1 < len(ctx.Message.Entities) {
ctx.Message.Entities = ctx.Message.Entities[i+1:]
ctx.Message.Entities[0].Offset = 0
}
default:
ctx.Message.Entities = ctx.Message.Entities[i:]
ctx.Message.Entities[0].Length -= c - n
ctx.Message.Entities[0].Offset = 0
}
if len(ctx.Message.Entities) > 1 {
o := ctx.Message.Entities[0].Length
for _, e := range ctx.Message.Entities[1:] {
e.Offset = o
o += e.Length
}
}
}
}
ctx.Message.Text = strings.TrimLeft(ctx.Message.Text[len(name):], " ")
log.Debugln("[event] message after process:", ctx.Message.Text)
return true
}
u16txt, err := base14.UTF82UTF16BE(helper.StringToBytes(ctx.Message.Text))
if err != nil {
return false
}
for i, e := range ctx.Message.Entities {
if e.IsMention() && e.Length > 0 {
a := 2 * (e.Offset + 1)
b := 2 * (e.Offset + e.Length)
if a < b && a < len(u16txt) && b <= len(u16txt) {
n, err := base14.UTF16BE2UTF8(u16txt[a:b])
if err != nil {
continue
}
if helper.BytesToString(n) == name {
log.Debugln("[event] message before process:", ctx.Message.Text)
n, err = base14.UTF16BE2UTF8(append(u16txt[:2*e.Offset], u16txt[b:]...))
if err != nil {
continue
}
ctx.Message.Text = helper.BytesToString(n)
o := e.Offset
ctx.Message.Entities = append(ctx.Message.Entities[:i], ctx.Message.Entities[i+1:]...)
for _, e1 := range ctx.Message.Entities[i:] {
e1.Offset = o
o += e1.Length
}
if ctx.Message.Text[0] == ' ' {
n := 0
for _, c := range ctx.Message.Text {
if c == ' ' {
n++
} else {
break
}
}
ctx.Message.Text = ctx.Message.Text[n:]
u16txt = u16txt[2*n:]
for _, e1 := range ctx.Message.Entities {
if e1.Offset >= n {
e1.Offset -= n
}
}
}
if ctx.Message.Text[len(ctx.Message.Text)-1] == ' ' {
n := 0
for i := len(ctx.Message.Text) - 1; i >= 0; i-- {
if ctx.Message.Text[i] == ' ' {
n++
} else {
break
}
}
ctx.Message.Text = ctx.Message.Text[:len(ctx.Message.Text)-n]
if len(ctx.Message.Entities) > 0 {
elast := ctx.Message.Entities[len(ctx.Message.Entities)-n]
if elast.Offset+elast.Length == len(u16txt)/2 {
if elast.Length > n {
elast.Length -= n
} else {
ctx.Message.Entities = ctx.Message.Entities[:len(ctx.Message.Entities)-1]
}
}
}
}
log.Debugln("[event] message after process:", ctx.Message.Text)
return true
}
}
}
}
return strings.Contains(ctx.Message.Text, name)
}(ctx)
}
log.Debugln("[event] is to me:", ctx.IsToMe)
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 未满足
if m.Break { // 阻断后续
break loop
}
continue loop
}
}
}
for _, rule := range m.Rules {
if rule != nil && !rule(ctx) { // 有 Rule 的条件未满足
if m.Break { // 阻断后续
break loop
}
continue loop
}
}
// mid handler
if m.Engine != nil {
for _, handler := range m.Engine.midHandler {
if !handler(ctx) { // 有 mid handler 未满足
if m.Break { // 阻断后续
break loop
}
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
}
}
}