-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathonebot.go
190 lines (162 loc) · 4.28 KB
/
onebot.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
package libonebot
import (
"context"
"fmt"
"regexp"
"sync"
"time"
"github.com/sirupsen/logrus"
)
const (
Version = "0.6.0" // LibOneBot 版本号
OneBotVersion = "12" // OneBot 标准版本
)
// OneBot 表示一个 OneBot 实例.
type OneBot struct {
Impl string
Self *Self // 机器人自身标识, 多机器人账号复用 OneBot 对象时为 nil
Config *Config
Logger *logrus.Logger
eventListenChans []chan marshaledEvent
eventListenChansLock *sync.RWMutex
actionHandler Handler
cancel context.CancelFunc
wg *sync.WaitGroup
}
var (
implPlatformRegex = regexp.MustCompile(`^[a-z][\-a-z0-9]*(\.[\-a-z0-9]+)*$`)
)
// NewOneBot 创建一个新的 OneBot 实例.
//
// 参数:
// impl: OneBot 实现名称, 不能为空
// self: OneBot 实例对应的机器人自身标识, 不能为 nil
// config: OneBot 配置, 不能为 nil
func NewOneBot(impl string, self *Self, config *Config) *OneBot {
if impl == "" {
panic("必须提供 OneBot 实现名称")
}
if !implPlatformRegex.MatchString(impl) {
panic("OneBot 实现名称不合法")
}
if self == nil {
panic("必须提供机器人自身标识")
}
if self.Platform == "" {
panic("必须提供机器人平台名称")
}
if !implPlatformRegex.MatchString(self.Platform) {
panic("机器人平台名称不合法")
}
if self.UserID == "" {
panic("必须提供 OneBot 实例对应的机器人用户 ID")
}
if config == nil {
panic("必须提供 OneBot 配置")
}
return newOneBotUnchecked(impl, self, config)
}
// NewOneBotMultiSelf 创建一个新的多机器人账号复用的 OneBot 实例.
//
// 参数:
// impl: OneBot 实现名称, 不能为空
// config: OneBot 配置, 不能为 nil
func NewOneBotMultiSelf(impl string, config *Config) *OneBot {
if impl == "" {
panic("必须提供 OneBot 实现名称")
}
if !implPlatformRegex.MatchString(impl) {
panic("OneBot 实现名称不合法")
}
if config == nil {
panic("必须提供 OneBot 配置")
}
return newOneBotUnchecked(impl, nil, config)
}
func newOneBotUnchecked(impl string, self *Self, config *Config) *OneBot {
return &OneBot{
Impl: impl,
Self: self,
Config: config,
Logger: logrus.New(),
eventListenChans: make([]chan marshaledEvent, 0),
eventListenChansLock: &sync.RWMutex{},
actionHandler: nil,
cancel: nil,
wg: &sync.WaitGroup{},
}
}
// Run 运行 OneBot 实例.
//
// 该方法会阻塞当前线程, 直到 Shutdown 被调用.
func (ob *OneBot) Run() {
ctx, cancel := context.WithCancel(context.Background())
ob.cancel = cancel
ob.startCommMethods(ctx)
ob.startHeartbeat(ctx)
ob.Logger.Infof("OneBot 已启动")
<-ctx.Done()
}
// Shutdown 停止 OneBot 实例.
func (ob *OneBot) Shutdown() {
ob.cancel() // this will stop everything (comm methods, heartbeat, etc)
ob.wg.Wait() // wait for everything to completely stop
ob.Logger.Infof("OneBot 已关闭")
}
// GetUserAgent 获取 OneBot 实例的 User-Agent.
func (ob *OneBot) GetUserAgent() string {
return fmt.Sprintf("OneBot/%v LibOneBot/%v", OneBotVersion, Version)
}
func (ob *OneBot) startCommMethods(ctx context.Context) {
if ob.Config.Comm.HTTP != nil {
for _, c := range ob.Config.Comm.HTTP {
ob.wg.Add(1)
go commRunHTTP(c, ob, ctx, ob.wg)
}
}
if ob.Config.Comm.HTTPWebhook != nil {
for _, c := range ob.Config.Comm.HTTPWebhook {
ob.wg.Add(1)
go commRunHTTPWebhook(c, ob, ctx, ob.wg)
}
}
if ob.Config.Comm.WS != nil {
for _, c := range ob.Config.Comm.WS {
ob.wg.Add(1)
go commRunWS(c, ob, ctx, ob.wg)
}
}
if ob.Config.Comm.WSReverse != nil {
for _, c := range ob.Config.Comm.WSReverse {
ob.wg.Add(1)
go commRunWSReverse(c, ob, ctx, ob.wg)
}
}
}
func (ob *OneBot) startHeartbeat(ctx context.Context) {
if !ob.Config.Heartbeat.Enabled {
return
}
if ob.Config.Heartbeat.Interval == 0 {
ob.Logger.Errorf("心跳间隔必须大于 0")
return
}
ob.wg.Add(1)
go func() {
defer ob.wg.Done()
ticker := time.NewTicker(time.Duration(ob.Config.Heartbeat.Interval) * time.Millisecond)
defer ticker.Stop()
ob.Logger.Infof("心跳开始")
for {
select {
case <-ticker.C:
ob.Logger.Debugf("扑通")
event := MakeHeartbeatMetaEvent(time.Now(), int64(ob.Config.Heartbeat.Interval))
ob.Push(&event)
case <-ctx.Done():
ob.Logger.Infof("心跳停止")
return
}
}
}()
}