-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmessage.go
executable file
·522 lines (457 loc) · 12.7 KB
/
message.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
package rbq
import (
"github.com/buger/jsonparser"
"log"
"regexp"
"strings"
)
// CQRecv 接收的CQ码消息
// 从收到的消息中解析出CQ码和纯文本消息
type CQRecv struct {
cq []CQCodeInterface
cqm map[string][]CQCodeInterface
pureText string // 不包含CQ码的纯文本消息
}
func newCQRecv() *CQRecv {
return &CQRecv{
cq: make([]CQCodeInterface, 0, 10),
cqm: make(map[string][]CQCodeInterface),
}
}
func coverCQIToSepType[T any](cqi []CQCodeInterface) []T {
ret := make([]T, len(cqi))
for k, v := range cqi {
ret[k] = v.(T)
}
return ret
}
func (cqr *CQRecv) reset() {
cqr.cq = cqr.cq[:0]
cqr.cqm = make(map[string][]CQCodeInterface)
cqr.pureText = ""
}
// decodeMessage 解析消息,将消息中的CQ码和纯文本分离并将CQ码解析为结构体
func (cqr *CQRecv) decodeMessage(s string) {
cqr.reset()
p := regexp.MustCompile(`\[CQ:.*?]`)
sp := p.Split(s, -1)
cqr.pureText = strings.Join(sp, "")
cq := p.FindAllString(s, -1)
for _, v := range cq {
cqCode := cqDecodeFromString(v)
if cqCode != nil {
cqr.pushCQCode(cqCode)
}
}
}
func (cqr *CQRecv) pushCQCode(cq CQCodeInterface) {
t := cq.CQType()
cqr.cq = append(cqr.cq, cq)
cqr.cqm[t] = append(cqr.cqm[t], cq)
}
func (cqr *CQRecv) GetAllCQCode() []CQCodeInterface {
return cqr.cq
}
func (cqr *CQRecv) GetCQCodeByType(t string) []CQCodeInterface {
var ret []CQCodeInterface
if cqs, _ok := cqr.cqm[t]; _ok {
ret = cqs
}
return ret
}
func (cqr *CQRecv) GetText() string {
return cqr.pureText
}
// GetCQFace 获取表情
func (cqr *CQRecv) GetCQFace() []*CQFace {
return coverCQIToSepType[*CQFace](cqr.GetCQCodeByType("face"))
}
// GetCQRecord 获取语音消息
// 因为qq单条消息只可能发送一条语音,所以接受时也只可能接收到一条,所以只返回单个
func (cqr *CQRecv) GetCQRecord() *CQRecord {
r := coverCQIToSepType[*CQRecord](cqr.GetCQCodeByType("record"))
if len(r) > 0 {
return r[0]
}
return nil
}
func (cqr *CQRecv) GetCQAt() []*CQAt {
return coverCQIToSepType[*CQAt](cqr.GetCQCodeByType("at"))
}
// GetCQVideo 获取视频消息
// 因为qq单条消息只可能发送一条视频,所以接受时也只可能接收到一条,所以只返回单个
func (cqr *CQRecv) GetCQVideo() *CQVideo {
r := coverCQIToSepType[*CQVideo](cqr.GetCQCodeByType("video"))
if len(r) > 0 {
return r[0]
}
return nil
}
// GetCQShare 获取分享链接
func (cqr *CQRecv) GetCQShare() *CQShare {
r := coverCQIToSepType[*CQShare](cqr.GetCQCodeByType("share"))
if len(r) > 0 {
return r[0]
}
return nil
}
// GetCQLocation TODO 获取位置分享,go-cqhttp 暂未实现
//func (cpr CQRecv) GetCQLocation() *CQLocation {
// r := coverCQIToSepType[*CQLocation](cpr.GetCQCodeByType("location"))
// if len(r) > 0 {
// return r[0]
// }
// return nil
//}
// GetCQImage 获取图片
func (cqr *CQRecv) GetCQImage() []*CQImage {
return coverCQIToSepType[*CQImage](cqr.GetCQCodeByType("image"))
}
// GetCQReply 获取回复消息
func (cqr *CQRecv) GetCQReply() *CQReply {
r := coverCQIToSepType[*CQReply](cqr.GetCQCodeByType("reply"))
if len(r) > 0 {
return r[0]
}
return nil
}
// GetCQRedBag 获取红包
func (cqr *CQRecv) GetCQRedBag() *CQRedBag {
r := coverCQIToSepType[*CQRedBag](cqr.GetCQCodeByType("redbag"))
if len(r) > 0 {
return r[0]
}
return nil
}
// GetCQForward 获取转发消息
func (cqr *CQRecv) GetCQForward() *CQForward {
r := coverCQIToSepType[*CQForward](cqr.GetCQCodeByType("forward"))
if len(r) > 0 {
return r[0]
}
return nil
}
type CQSend struct {
cq []*CQCode
cqm map[string][]*CQCode
err []error
}
func newCQSend() *CQSend {
return &CQSend{
cq: make([]*CQCode, 0, 10),
cqm: make(map[string][]*CQCode),
}
}
func (cqs *CQSend) cqReset() {
cqs.cq = make([]*CQCode, 0, 10)
cqs.cqm = make(map[string][]*CQCode)
cqs.err = make([]error, 0, 10)
}
// AddCQCode 添加CQ码
func (cqs *CQSend) AddCQCode(cq *CQCode) *CQSend {
if cq == nil {
return cqs
}
if cq.CQType() == "reply" && len(cqs.cqm["reply"]) > 0 {
return cqs
}
cqs.cq = append(cqs.cq, cq)
cqs.cqm[cq.CQType()] = append(cqs.cqm[cq.CQType()], cq)
return cqs
}
// AddText 添加纯文本
func (cqs *CQSend) AddText(text ...string) *CQSend {
for _, t := range text {
cqs.AddCQCode(NewCQText(t))
}
return cqs
}
// AddCQFace 添加表情
func (cqs *CQSend) AddCQFace(faceId ...int) *CQSend {
for _, id := range faceId {
cqs.AddCQCode(NewCQFace(id))
}
return cqs
}
// AddCQRecord 发送语音消息
// file 语音文件路径,支持 http://,https://,base64://,file://协议
func (cqs *CQSend) AddCQRecord(file string) *CQSend {
c, e := NewCQRecord(file, false, true, true, 0)
if e != nil {
cqs.err = append(cqs.err, e)
}
return cqs.AddCQCode(c)
}
// AddCQVideo 发送短视频消息
// file 视频文件路径,支持 http://,https://,base64://,file://协议
// cover 封面文件路径,支持 http://,https://,base64://,file://协议,图片必须是jpg格式
func (cqs *CQSend) AddCQVideo(file, cover string) *CQSend {
c, e := NewCQVideo(file, cover, 1)
if e != nil {
cqs.err = append(cqs.err, e)
}
return cqs.AddCQCode(c)
}
// AddCQAt at某人,传入0表示at全体成员
func (cqs *CQSend) AddCQAt(qq ...int64) *CQSend {
for _, id := range qq {
cqs.AddCQCode(NewCQAt(id, ""))
}
return cqs
}
// AddCQShare 发送分享链接
func (cqs *CQSend) AddCQShare(url, title, content, image string) *CQSend {
c, e := NewCQShare(url, title, content, image)
if e != nil {
cqs.err = append(cqs.err, e)
}
return cqs.AddCQCode(c)
}
// AddCQLocation TODO 发送位置,go-cqhttp 暂未实现
//func (cqs *CQSend) AddCQLocation(lat, lon float64, title, content string) *CQSend {
//r, e := NewCQLocation(lat, lon, title, content)
//if e != nil {
// cqs.err = append(cqs.err, e)
//}
//return cqs.AddCQCode(r)
//}
// AddCQMusic 发送音乐分享
func (cqs *CQSend) AddCQMusic(type_ string, id int64) *CQSend {
c, e := NewCQMusic(type_, id, "", "", "", "", "")
if e != nil {
cqs.err = append(cqs.err, e)
}
return cqs.AddCQCode(c)
}
// AddCQMusicCustom 发送自定义音乐分享
func (cqs *CQSend) AddCQMusicCustom(url, audio, title, content, image string) *CQSend {
c, e := NewCQMusic(CQMusicTypeCustom, 0, url, audio, title, content, image)
if e != nil {
cqs.err = append(cqs.err, e)
}
return cqs.AddCQCode(c)
}
// AddImage 发送图片
func (cqs *CQSend) AddImage(file string) *CQSend {
c, e := NewCQImage(file, "", 0, false, 0, 2)
if e != nil {
cqs.err = append(cqs.err, e)
}
return cqs.AddCQCode(c)
}
// AddCQReply 回复消息
func (cqs *CQSend) AddCQReply(id int64) *CQSend {
return cqs.AddCQCode(NewCQReply(id))
}
// AddCQPoke 戳一戳
func (cqs *CQSend) AddCQPoke(qq int64) *CQSend {
c := NewCQPoke(qq)
return cqs.AddCQCode(c)
}
// AddCQForwardMsg 转发消息
func (cqs *CQSend) AddCQForwardMsg(id ...int64) *CQSend {
for _, i := range id {
f, e := NewCQForwardNode(i, "", 0, nil, nil)
if e != nil {
cqs.err = append(cqs.err, e)
continue
}
cqs.AddCQCode(f)
}
return cqs
}
// AddCQCustomForwardMsg 转发消息-自定义内容
func (cqs *CQSend) AddCQCustomForwardMsg(name string, qq int64, content, seq any) *CQSend {
f, e := NewCQForwardNode(0, name, qq, content, seq)
if e != nil {
cqs.err = append(cqs.err, e)
return cqs
}
return cqs.AddCQCode(f)
}
// AddCQCardImage 发送装逼大图
func (cqs *CQSend) AddCQCardImage(file string) *CQSend {
c, err := NewCQCardImage(file, 0, 0, 0, 0, "", "")
if err != nil {
cqs.err = append(cqs.err, err)
return cqs
}
return cqs.AddCQCode(c)
}
// AddCQTTS 发送文字转语音消息
func (cqs *CQSend) AddCQTTS(text string) *CQSend {
c := NewCQTTS(text)
return cqs.AddCQCode(c)
}
type MessageHandle struct {
*CQRecv
*CQSend
recv *RecvNormalMsg
rep *ReplyMessage
nrf bool // not reset flag,是否在发送消息后对消息执行复位操作
}
// IsGroup 判断是否是群聊消息
func (c *MessageHandle) IsGroup() bool {
return c.recv.GroupId > 0
}
// GetSender 获取发送者的QQ
func (c *MessageHandle) GetSender() int64 {
return c.recv.Sender.UserId
}
// GetGroupNo 获取群号
func (c *MessageHandle) GetGroupNo() int64 {
if c.IsGroup() {
return c.recv.GroupId
}
return int64(0)
}
// GetMessage 获取接收到的消息
func (c *MessageHandle) GetMessage() string {
return c.recv.Message
}
func (c *MessageHandle) GetMessageId() int64 {
return c.recv.MessageId
}
// AddReply 重写添加CQReply方法,id 如果小于等于 0,将会自动获取当前消息的id
//func (c *MessageHandle) AddReply(id int64) {
// if id <= 0 {
// id = c.recv.MessageId
// }
// //c.rep.AddReply(id)
//}
// NotReset 发送消息后不执行复位操作
func (c *MessageHandle) NotReset() {
c.nrf = true
}
func (c *MessageHandle) reset() {
if !c.nrf {
c.cqReset()
}
}
// Reply 发送消息,默认向消息来源发送,如群,私聊
// 返回的字段分别为 消息id,转发id(仅转发时返回),错误
func (c *MessageHandle) Reply(ss ...string) (int64, string, error) {
for _, s := range ss {
c.AddCQCode(NewCQText(s))
}
msgId, fId, err := c.rep.send(c.recv.UserId, c.recv.GroupId, c.CQSend)
c.reset()
return msgId, fId, err
}
// SendToPrivate 向指定私聊发送消息
func (c *MessageHandle) SendToPrivate(userId int64) (int64, string, error) {
msgId, fId, err := c.rep.send(userId, 0, c.CQSend)
c.reset()
return msgId, fId, err
}
// SendToGroup 向指定群聊发送消息
func (c *MessageHandle) SendToGroup(groupId int64) (int64, string, error) {
msgId, fId, err := c.rep.send(0, groupId, c.CQSend)
c.reset()
return msgId, fId, err
}
// RecvNormalMsg 接受的消息结构体类型
type RecvNormalMsg struct {
Anonymous string `json:"anonymous"` // 匿名,群属性
GroupId int64 `json:"group_id"` // 群ID
Font int64 `json:"font"`
Message string `json:"message"`
MessageId int64 `json:"message_id"`
MessageType string `json:"message_type"`
PostType string `json:"PostType"`
RowMessage string `json:"row_message"`
SelfId int64 `json:"self_id"`
TargetId int64 `json:"target_id"` // 发送目标的user_id 私聊属性
SubType string `json:"sub_type"`
Time int64 `json:"time"`
UserId int64 `json:"user_id"`
Sender struct {
Age int64 `json:"age"`
Area string `json:"area"` // 地区,群属性
Card string `json:"card"` // 卡片?,群属性
Level string `json:"level"` // 等级,群属性
Role string `json:"admin"` // 角色,群属性
Nickname string `json:"nickname"`
Title string `json:"title"` // 角色title,群属性(名字前面的称谓)
Sex string `json:"sex"`
UserId int64 `json:"user_id"`
}
}
func parseMessageBytes(recv []byte) *RecvNormalMsg {
postType, err := jsonparser.GetString(recv, "post_type")
if err != nil {
// 获取不到信息类型,直接return掉
return nil
}
if postType == "message" {
var recvMsg *RecvNormalMsg
err2 := json.Unmarshal(recv, &recvMsg)
if err2 != nil {
return nil
}
log.Println("接收到消息:", recvMsg.Message)
return recvMsg
}
return nil
}
type ReplyMessage struct {
}
func newReplyMessage() *ReplyMessage {
return &ReplyMessage{}
}
func (r *ReplyMessage) send(userID, groupID int64, cqs *CQSend) (int64, string, error) {
for _, err := range cqs.err {
log.Println(err)
}
msg, cards, forward := r.tidyCQCode(cqs.cqm)
// 每次只会 send 一条消息,如果有多条消息可以send,会被忽略
var msgId int64
var forwardId string
var err error
if msg != "" {
log.Println("发送消息:", msg)
msgId, err = cqapi.SendMsg(userID, groupID, msg, false)
}
for _, card := range cards {
if msgId != 0 || err != nil {
log.Println("发送卡片已忽略,已有其他消息发送")
continue
}
log.Println("发送消息:", card)
msgId, err = cqapi.SendMsg(userID, groupID, card, false)
}
// 合并转发只能对群聊发送, go-cqhttp 未提供相关接口
if len(forward) > 0 {
if msgId == 0 && err == nil {
log.Println("发送合并转发消息")
msgId, forwardId, err = cqapi.SendForwardMsg(userID, groupID, forward)
} else {
log.Println("发送合并内容已忽略,已有其他消息发送")
}
}
return msgId, forwardId, err
}
func (r *ReplyMessage) tidyCQCode(cqm map[string][]*CQCode) (string, []string, []*CQCode) {
var msg string // 可以合并一次性发送的cq码
var card []string // 一次发送只能有一个的cq码
var forward []*CQCode // 合并转发,该消息类型比较特殊
sb := &strings.Builder{}
for t, cqs := range cqm {
switch t {
case "text", "face", "at", "image", "reply":
for _, cq := range cqs {
sb.WriteString(cq.String())
}
case "node":
for _, cq := range cqs {
forward = append(forward, cq)
}
default:
for _, cq := range cqs {
card = append(card, cq.String())
}
}
}
msg = sb.String()
return msg, card, forward
}