This repository has been archived by the owner on Jun 17, 2023. It is now read-only.
/
handler.go
770 lines (661 loc) · 18.3 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
package gonebot
import (
"bytes"
"fmt"
"regexp"
"strings"
"sync"
goarg "github.com/alexflint/go-arg"
)
type Middleware func(*Context) bool
type HandlerFunc func(*Context)
type Handler struct {
middlewares []Middleware
handleFunc HandlerFunc
parent *Handler
subHandlers map[EventName][]*Handler
mu sync.RWMutex
}
// 使用中间件
func (h *Handler) Use(middlewares ...Middleware) *Handler {
h.mu.Lock()
defer h.mu.Unlock()
h.middlewares = append(h.middlewares, middlewares...)
return h
}
// 指定事件处理函数
func (h *Handler) Handle(f HandlerFunc) {
h.handleFunc = f
}
// 添加子Handler
func (h *Handler) addSubHandler(subHandler *Handler, eventType ...EventName) {
h.mu.Lock()
defer h.mu.Unlock()
subHandler.parent = h
for _, event := range eventType {
h.subHandlers[event] = append(h.subHandlers[event], subHandler)
}
}
// 移除指定的子Handler
func (h *Handler) removeSubHandler(handler *Handler, eventType ...EventName) {
h.mu.Lock()
defer h.mu.Unlock()
if h.subHandlers == nil {
return
}
for _, event := range eventType {
for i, subHandler := range h.subHandlers[event] {
if subHandler == handler {
h.subHandlers[event] = append(h.subHandlers[event][:i], h.subHandlers[event][i+1:]...)
return
}
}
}
}
// 新建一个可以被删除的Handler,用于处理指定类型的事件。
//
// 调用remove方法可以删除当前Handler。
func (h *Handler) NewRemovableHandler(eventTypes ...EventName) (handler *Handler, remove func()) {
handler = &Handler{
parent: h,
subHandlers: make(map[EventName][]*Handler),
}
if len(eventTypes) == 0 {
eventTypes = append(eventTypes, EventName_AllEvent)
}
h.addSubHandler(handler, eventTypes...)
return handler, func() {
h.removeSubHandler(handler, eventTypes...)
}
}
// 新建一个Handler,用于处理指定类型的事件
func (h *Handler) NewHandler(eventTypes ...EventName) (handler *Handler) {
nh, _ := h.NewRemovableHandler(eventTypes...)
return nh
}
func (h *Handler) getMatchedHandler(eventName EventName) (handlers []*Handler) {
h.mu.RLock()
defer h.mu.RUnlock()
// 以下构造Handler链,以message.private.friend事件为例,
// 按message.private.friend、message.private、message、all的顺序将这些Handler放入链中
parts := strings.Split(string(eventName), ".")
for i := len(parts); i >= 0; i-- {
if i == 0 {
handlers = append(handlers, h.subHandlers[EventName_AllEvent]...)
break
}
shs := h.subHandlers[EventName(strings.Join(parts[:i], "."))]
handlers = append(handlers, shs...)
}
return
}
// 一次事件的处理过程,保存了当前Handler、当前中间件索引、是否中止等信息
type process struct {
handlerQueue []*Handler // 待的Handler队列
curHandler *Handler // 当前Handler
isLeaf bool // 当前Handler是否是叶子节点
middlewares []Middleware // 当前Handler的中间件
mwIdx int // 当前正在执行的中间件的索引
aborted bool // 是否已经被中断
next bool // 是否继续下一个Handler
done bool // 当前Handler是否已经执行完毕
shouldExpand bool // 是否需要展开子Handler
processedByHandler bool // 是否有Handler处理过当前事件
ctx *Context // 上下文
eventName EventName // 事件名称
}
// 更新process的当前Handler为队列的下一个,并重置标志
func (proc *process) nextHandler() bool {
if proc.aborted {
return false
}
if !proc.next {
return false
}
// 当前Handler为非叶子节点,并且中间件未返回false,进行展开
if !proc.isLeaf && proc.shouldExpand {
// 按照先序的顺序,子Handler应塞在队头
proc.handlerQueue = append(proc.curHandler.getMatchedHandler(proc.eventName), proc.handlerQueue...)
}
// 队列空,没有了
if len(proc.handlerQueue) == 0 {
return false
}
proc.curHandler = proc.handlerQueue[0]
proc.handlerQueue = proc.handlerQueue[1:]
proc.middlewares = proc.curHandler.middlewares
proc.isLeaf = len(proc.curHandler.subHandlers) == 0
proc.mwIdx = 0
proc.done = false
proc.shouldExpand = true
return true
}
// 中止过程
func (proc *process) abort() {
proc.aborted = true
}
// 运行过程
func (proc *process) run() {
startHandler := proc.curHandler // 记录开始的Handler
prevHandler := proc.curHandler // 上一个Handler,用于比较Handler是否发生变化
// 当前Handler尚未完成则继续,否则下一个
handlerLoop:
for !proc.done || proc.nextHandler() {
if prevHandler != proc.curHandler {
// 当前Handler发生了变化,需要更新CTX的Handler
proc.ctx.Handler = proc.curHandler
prevHandler = proc.curHandler
}
// 顺序执行中间件
for !proc.aborted && proc.mwIdx < len(proc.middlewares) {
mw := proc.middlewares[proc.mwIdx]
if !mw(proc.ctx) {
proc.shouldExpand = false // 中间件返回false,非叶子节点不展开子Handler
proc.done = true // 中间件返回false,标志当前Handler执行完毕
continue handlerLoop // 执行下一个Handler
}
proc.mwIdx++
}
if proc.aborted {
return
}
// 如果不是叶子节点,则向后执行它的子Handler
if !proc.isLeaf {
proc.done = true // 标志当前Handler执行完毕
continue
}
if proc.curHandler.handleFunc != nil {
proc.next = false // 叶子节点,默认不向后执行
// 提前设置done,让下次循环能正确获取下一个Handler。
// 否则会造成无限递归
proc.done = true
proc.curHandler.handleFunc(proc.ctx)
proc.processedByHandler = true
}
proc.done = true
}
// 复原CTX的Handler
proc.ctx.Handler = startHandler
}
// 从当前process创建一个新的process,用于执行当前process的后续
func (proc *process) forkAndNext() bool {
if proc.aborted {
return false
}
// 为了防止并发调用next,导致两个goroutine同时向后执行,
// 故规定,调用next之后,原先的process将停止继续处理,转由新的process处理
proc.aborted = true
newProc := *proc
newProc.aborted = false
if newProc.mwIdx < len(newProc.middlewares) {
// 调用时,中间件没执行完,则后移一个继续执行
newProc.mwIdx++
} else {
// 调用时,处于处理函数中,则将next置为true
newProc.next = true
}
newProc.ctx.abort = newProc.abort
newProc.ctx.next = newProc.forkAndNext
defer func() {
proc.ctx.abort = proc.abort
proc.ctx.next = proc.forkAndNext
}()
newProc.run()
proc.processedByHandler = newProc.processedByHandler
return newProc.processedByHandler
}
func (h *Handler) handleEvent(ctx *Context) {
proc := process{
handlerQueue: []*Handler{},
curHandler: h,
isLeaf: len(h.subHandlers) == 0,
middlewares: h.middlewares,
mwIdx: 0,
aborted: false,
next: true,
ctx: ctx,
eventName: ctx.Event.GetEventName(),
done: false,
shouldExpand: true,
}
ctx.abort = proc.abort
ctx.next = proc.forkAndNext
proc.run()
}
func OnEvent(eventName EventName) Middleware {
return func(ctx *Context) bool {
return ctx.Event.GetEventName() == eventName
}
}
// 与Bot相关
func OnlyToMe() Middleware {
return func(ctx *Context) bool {
return ctx.Event.IsToMe()
}
}
// 限制来自某些群聊,当参数为空时,表示全部群聊都可
func FromGroup(groupIds ...int64) Middleware {
return func(ctx *Context) bool {
gid, exist := getEventField(ctx.Event, "GroupId")
if !exist {
return false
}
if len(groupIds) == 0 {
return true
}
for _, id := range groupIds {
if id == gid {
return true
}
}
return false
}
}
// 限制来自某些人的私聊,当参数为空时,表示只要是私聊都可
func FromPrivate(userIds ...int64) Middleware {
return func(ctx *Context) bool {
uid, exist := getEventField(ctx.Event, "UserId")
if !exist {
return false
}
if len(userIds) == 0 {
return true
}
for _, id := range userIds {
if id == uid {
return true
}
}
return false
}
}
// 消息来源于某些人,必须传入至少一个参数
func FromUser(userIds ...int64) Middleware {
return func(ctx *Context) bool {
if len(userIds) == 0 {
return true
}
uid, exist := getEventField(ctx.Event, "UserId")
if !exist {
return false
}
for _, id := range userIds {
if id == uid {
return true
}
}
return false
}
}
func FromSession(sessionId string) Middleware {
return func(ctx *Context) bool {
return ctx.Event.GetSessionId() == sessionId
}
}
// 仅群组管理员
func FromAdmin() Middleware {
return func(ctx *Context) bool {
if ev, ok := ctx.Event.(*GroupMessageEvent); ok {
return ev.Sender.Role == "admin"
}
return false
}
}
// 群组管理员及更高权限,包括群主、超管
func FromAdminOrHigher() Middleware {
admin := FromAdmin()
owner := FromOwner()
su := FromSuperuser()
return func(ctx *Context) bool {
return admin(ctx) || owner(ctx) || su(ctx)
}
}
// 仅群组群主
func FromOwner() Middleware {
return func(ctx *Context) bool {
if ev, ok := ctx.Event.(*GroupMessageEvent); ok {
return ev.Sender.Role == "owner"
}
return false
}
}
// 群组群主及更高权限,包括超管
func FromOwnerOrHigher() Middleware {
owner := FromOwner()
su := FromSuperuser()
return func(ctx *Context) bool {
return owner(ctx) || su(ctx)
}
}
// 仅超管,群聊和私聊都可
func FromSuperuser() Middleware {
return func(ctx *Context) bool {
config := ctx.Engine.Config
sus := config.GetBaseConfig().Superuser
var senderId int64
if ev, ok := ctx.Event.(*GroupMessageEvent); ok {
senderId = ev.Sender.UserId
} else if ev, ok := ctx.Event.(*PrivateMessageEvent); ok {
senderId = ev.Sender.UserId
} else {
return false
}
for _, su := range sus {
if su == senderId {
return true
}
}
return false
}
}
type prefixMatchResult struct {
Matched string // 匹配到的前缀
Remain string // 去除前缀后的剩下文本
Raw string // 原始文本
}
// 事件为MessageEvent,且消息以某个前缀开头
func StartsWith(prefix ...string) Middleware {
return func(ctx *Context) bool {
e := ctx.Event
if !e.IsMessageEvent() {
return false
}
msgText := e.ExtractPlainText()
reg := regexp.MustCompile(fmt.Sprintf("^(%s)", strings.Join(prefix, "|")))
find := reg.FindString(msgText)
if find == "" {
return false
}
ctx.Set("prefix", &prefixMatchResult{
Matched: find,
Remain: strings.TrimPrefix(msgText, find),
Raw: msgText,
})
return true
}
}
// 获取StartsWith匹配结果
func (ctx *Context) GetPrefixMatchResult() *prefixMatchResult {
if v, ok := ctx.Get("prefix"); ok {
return v.(*prefixMatchResult)
}
return nil
}
type suffixMatchResult struct {
Matched string // 匹配到的后缀
Remain string // 去除后缀后的剩下文本
Raw string // 原始文本
}
// 事件为MessageEvent,且消息以某个后缀结尾
func EndsWith(suffix ...string) Middleware {
return func(ctx *Context) bool {
e := ctx.Event
if !e.IsMessageEvent() {
return false
}
msgText := e.ExtractPlainText()
reg := regexp.MustCompile(fmt.Sprintf("(%s)$", strings.Join(suffix, "|")))
find := reg.FindString(msgText)
if find == "" {
return false
}
ctx.Set("suffix", &suffixMatchResult{
Matched: find,
Remain: strings.TrimSuffix(msgText, find),
Raw: msgText,
})
return true
}
}
// 获取EndsWith的匹配结果
func (ctx *Context) GetSuffixMatchResult() *suffixMatchResult {
if v, ok := ctx.Get("suffix"); ok {
return v.(*suffixMatchResult)
}
return nil
}
type commandMatchResult struct {
CmdPrefix string // 命令前缀
Command string // 匹配到的命令
Args []string // 命令参数,以空格分割
Remain string // 去除命令后的剩下文本
Raw string // 原始文本
}
// 事件为MessageEvent,且消息开头为指令
func Command(cmd ...string) Middleware {
return func(ctx *Context) bool {
e := ctx.Event
if !e.IsMessageEvent() {
return false
}
cmdPrefixs := ctx.Engine.Config.GetBaseConfig().CmdPrefix
msgText := e.ExtractPlainText()
reg := regexp.MustCompile(fmt.Sprintf("^(%s)(%s)", strings.Join(cmdPrefixs, "|"), strings.Join(cmd, "|")))
find := reg.FindStringSubmatch(msgText)
if find == nil {
return false
}
remain := strings.TrimPrefix(msgText, find[0])
args := strings.Split(remain, " ")
argsFiltered := make([]string, 0)
for _, arg := range args {
if arg != "" {
argsFiltered = append(argsFiltered, arg)
}
}
ctx.Set("command", &commandMatchResult{
CmdPrefix: find[1],
Command: find[2],
Args: argsFiltered,
Remain: remain,
Raw: msgText,
})
return true
}
}
// 获取Command的匹配结果
func (ctx *Context) GetCommandMatchResult() *commandMatchResult {
if v, ok := ctx.Get("command"); ok {
return v.(*commandMatchResult)
}
return nil
}
// 完全匹配
func FullMatch(text ...string) Middleware {
return func(ctx *Context) bool {
e := ctx.Event
if !e.IsMessageEvent() {
return false
}
msgText := e.ExtractPlainText()
reg := regexp.MustCompile(fmt.Sprintf("^(%s)$", strings.Join(text, "|")))
find := reg.FindString(msgText)
if find == "" {
return false
}
ctx.Set("fullMatch", text)
return true
}
}
// 事件为MessageEvent,且消息中包含其中某个关键词
func Keyword(keywords ...string) Middleware {
return func(ctx *Context) bool {
e := ctx.Event
if !e.IsMessageEvent() {
return false
}
msgText := e.ExtractPlainText()
reg := regexp.MustCompile(fmt.Sprintf("(%s)", strings.Join(keywords, "|")))
find := reg.FindString(msgText)
if find == "" {
return false
}
ctx.Set("keyword", find)
return true
}
}
// 获取Keyword的匹配结果
func (ctx *Context) GetKeywordMatchResult() string {
if v, ok := ctx.Get("keyword"); ok {
return v.(string)
}
return ""
}
type regexpMatchResult struct {
matchGroup []string
groupNames []string
}
// 捕获组数量
func (r regexpMatchResult) Len() int {
return len(r.matchGroup)
}
// 获取第i个捕获组,从1开始。0为整个匹配结果
func (r regexpMatchResult) Get(idx int) string {
if idx >= len(r.matchGroup) {
return ""
}
return r.matchGroup[idx]
}
// 使用捕获组名称来获取捕获组,仅当正则表达式中使用了命名捕获组时有效
func (r regexpMatchResult) GetByName(name string) string {
for i, n := range r.groupNames {
if n == name {
return r.matchGroup[i]
}
}
return ""
}
// 事件为MessageEvent,且消息中存在子串满足正则表达式
func Regex(regex regexp.Regexp) Middleware {
return func(ctx *Context) bool {
e := ctx.Event
if !e.IsMessageEvent() {
return false
}
msgText := e.ExtractPlainText()
find := regex.FindStringSubmatch(msgText)
if find == nil {
return false
}
ctx.Set("regex", ®expMatchResult{
matchGroup: find,
groupNames: regex.SubexpNames(),
})
return true
}
}
func (ctx *Context) GetRegexMatchResult() *regexpMatchResult {
if v, ok := ctx.Get("regex"); ok {
return v.(*regexpMatchResult)
}
return nil
}
// ShellLikeCommand解析错误时要采取的动作
type ParseFailedAction int
const (
// 无法解析时,自动回复错误信息
ParseFailedAction_AutoReply ParseFailedAction = iota
// 无法解析时,跳过这个Handler
ParseFailedAction_Skip
// 无法解析时,允许进入这个Handler,交由用户处理
ParseFailedAction_LetMeHandle
)
type parseResult struct {
Parser *goarg.Parser
RawArgs []string // 从字符串切割出来的原始参数
Args interface{} // 解析后的参数结构体指针
Err error // 解析失败时的错误信息
}
// 用法
func (res *parseResult) GetUsage() string {
usage := bytes.NewBuffer(nil)
res.Parser.WriteUsage(usage)
return usage.String()
}
// 帮助,包含用法、参数说明、子命令说明
func (res *parseResult) GetHelp() string {
help := bytes.NewBuffer(nil)
res.Parser.WriteHelp(help)
return help.String()
}
// 定义了子命令的情况下,返回是否解析到了子命令。未定义子命令时,总是返回true
func (res *parseResult) HasSubcommand() bool {
return res.Parser.Subcommand() != nil
}
// 获取子命令结构体指针。
// 如果定义了子命令,但没有解析到子命令,返回nil。
// 如果没有定义子命令,返回最顶层的结构体指针。
func (res *parseResult) GetSubcommand() interface{} {
return res.Parser.Subcommand()
}
// 生成错误提示
func (res *parseResult) FormatErrorAndHelp(err error) string {
return fmt.Sprintf("%s\n%s", err.Error(), res.GetHelp())
}
// 命令行命令。
//
// cmd为命令名,会受命令前缀影响,例如:命令前缀为"!",命令为"test",则为"!test"。
//
// args为命令参数,传入结构体,**注意,不会向该结构体写入数据**。
// 用法见https://github.com/alexflint/go-arg。
//
// whenFailed为解析失败时的处理方式,推荐使用ParseFailedAction_AutoReply
func ShellLikeCommand(cmd string, args interface{}, whenFailed ParseFailedAction) Middleware {
onCmd := Command(cmd)
return func(ctx *Context) bool {
if !onCmd(ctx) {
return false
}
remain := ctx.GetCommandMatchResult().Remain
remain = strings.TrimSpace(remain)
argSlice := strings.Split(remain, " ")
argSliceFiltered := make([]string, 0, len(argSlice))
for _, arg := range argSlice {
if arg != "" {
argSliceFiltered = append(argSliceFiltered, arg)
}
}
// 防止并发修改,创建一个新的结构体来存储解析结果
pArgsCopy := createUnderlyingStruct(args) // 指针
parser, err := goarg.NewParser(goarg.Config{Program: cmd, IgnoreEnv: true}, pArgsCopy)
if err == nil {
err = parser.Parse(argSliceFiltered)
// 直接处理help参数并返回,不需要用户自己处理
if err == goarg.ErrHelp {
usage := bytes.NewBuffer(nil)
parser.WriteHelp(usage)
ctx.Reply(usage.String())
ctx.Abort()
return true
}
}
result := &parseResult{
Parser: parser,
RawArgs: argSliceFiltered,
Args: pArgsCopy,
Err: err,
}
ctx.Set("sh_cmd", result)
if err != nil {
switch whenFailed {
case ParseFailedAction_AutoReply:
ctx.Reply(fmt.Sprintf("参数解析失败:%v\n%s", err, result.GetUsage()))
ctx.Abort()
return true
case ParseFailedAction_Skip:
return false
case ParseFailedAction_LetMeHandle:
return true
}
}
return true
}
}
// 获取ShellLikeCommand的解析结果
func (ctx *Context) GetShellLikeCommandResult() *parseResult {
r, ok := ctx.Get("sh_cmd")
if !ok {
return nil
}
return r.(*parseResult)
}