mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-06-30 03:43:25 +00:00
Compare commits
52 Commits
typeparam
...
v1.0.0-rc2
Author | SHA1 | Date | |
---|---|---|---|
23eea9188f | |||
12391f8df3 | |||
a3da5baae1 | |||
e4c73d59a5 | |||
d25209c366 | |||
70d1bfe510 | |||
cc3745130e | |||
bdf68ec694 | |||
15602e1daa | |||
43ea459365 | |||
c275806c62 | |||
d313effb79 | |||
5daea94157 | |||
9e136b21fa | |||
7dbda5cec7 | |||
296668441f | |||
810c781c25 | |||
111a5506b9 | |||
cfa35b6b0a | |||
c141501ae5 | |||
df3168ffd3 | |||
18a091145a | |||
eaf34288de | |||
b22ff34cb7 | |||
4a27a60456 | |||
0d291f79fa | |||
550c17c184 | |||
b4c3f2340e | |||
294bd05dad | |||
fb33d93b31 | |||
d522378315 | |||
ee9af5fa69 | |||
d161f35c69 | |||
40a765b117 | |||
d42d8dd395 | |||
f63c59f1a4 | |||
e4d10eb2ae | |||
112441d76e | |||
062eea62ab | |||
5b148d6c5e | |||
70da0ce6e4 | |||
d34531790c | |||
de4cfe0733 | |||
429ff80cf0 | |||
cbcfee9f69 | |||
937538a7cb | |||
afbf42b709 | |||
0a603dee92 | |||
34613306b3 | |||
779fa20704 | |||
d48dc4fb3c | |||
a75f412b82 |
4
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
4
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
@ -11,7 +11,7 @@ body:
|
||||
## 感谢您愿意填写错误回报!
|
||||
## 以下是一些注意事项,请务必阅读让我们能够更容易处理
|
||||
|
||||
### ❗ | 确定没有相同问题的ISSUE已被提出. (教程: https://github.com/Mrs4s/go-cqhttp/issues/633)
|
||||
### ❗ | 确定没有相同问题的ISSUE已被提出. (教程: https://forums.go-cqhttp.org/t/topic/141)
|
||||
### 🌎| 请准确填写环境信息
|
||||
### ❔ | 打开DEBUG模式复现,并提供出现问题前后至少 10 秒的完整日志内容。请自行删除日志内存在的个人信息及敏感内容。
|
||||
### ⚠ | 如果涉及内存泄漏/CPU占用异常请打开DEBUG模式并下载pprof性能分析.
|
||||
@ -24,7 +24,7 @@ body:
|
||||
attributes:
|
||||
label: 请确保您已阅读以上注意事项,并勾选下方的确认框。
|
||||
options:
|
||||
- label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://github.com/Mrs4s/go-cqhttp/issues/633)"
|
||||
- label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://forums.go-cqhttp.org/t/topic/141)"
|
||||
required: true
|
||||
- label: "我已经使用 [dev分支版本](https://github.com/Mrs4s/go-cqhttp/actions/workflows/ci.yml) 测试过,问题依旧存在。"
|
||||
required: true
|
||||
|
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -28,8 +28,7 @@ jobs:
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
stable: false
|
||||
go-version: 1.18.0-rc1
|
||||
go-version: 1.18
|
||||
- name: Cache downloaded module
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
|
8
.github/workflows/golint.yml
vendored
8
.github/workflows/golint.yml
vendored
@ -12,13 +12,19 @@ jobs:
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.18
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Static Check
|
||||
uses: dominikh/staticcheck-action@v1.2.0
|
||||
with:
|
||||
install-go: false
|
||||
version: "2022.1"
|
||||
|
||||
- name: Tests
|
||||
run: |
|
||||
go test $(go list ./...)
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.17'
|
||||
go-version: '1.18'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
|
@ -21,35 +21,28 @@ linters:
|
||||
disable-all: true
|
||||
fast: false
|
||||
enable:
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
- dogsled
|
||||
#- bodyclose
|
||||
#- deadcode
|
||||
#- depguard
|
||||
#- dogsled
|
||||
- gofmt
|
||||
- goimports
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- exhaustive
|
||||
- bidichk
|
||||
#- funlen
|
||||
#- goconst
|
||||
- gocritic
|
||||
#- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
#- gosec
|
||||
- gosimple
|
||||
#- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
#- misspell
|
||||
- nolintlint
|
||||
- rowserrcheck
|
||||
- staticcheck
|
||||
#- nolintlint
|
||||
#- rowserrcheck
|
||||
#- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
- typecheck
|
||||
#- stylecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
#- unparam
|
||||
#- unused
|
||||
- varcheck
|
||||
- whitespace
|
||||
- prealloc
|
||||
|
@ -3,6 +3,9 @@ env:
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
release:
|
||||
draft: true
|
||||
discussion_category_name: General
|
||||
builds:
|
||||
- id: nowin
|
||||
env:
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.17-alpine AS builder
|
||||
FROM golang:1.18-alpine AS builder
|
||||
|
||||
RUN go env -w GO111MODULE=auto \
|
||||
&& go env -w CGO_ENABLED=0 \
|
||||
|
@ -324,10 +324,9 @@ func Main() {
|
||||
log.Info("资源初始化完成, 开始处理信息.")
|
||||
log.Info("アトリは、高性能ですから!")
|
||||
|
||||
go selfupdate.CheckUpdate()
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
go selfdiagnosis.NetworkDiagnosis(cli)
|
||||
selfupdate.CheckUpdate()
|
||||
selfdiagnosis.NetworkDiagnosis(cli)
|
||||
}()
|
||||
|
||||
<-global.SetupMainSignalHandler()
|
||||
@ -366,6 +365,7 @@ func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, erro
|
||||
|
||||
func newClient() *client.QQClient {
|
||||
c := client.NewClientEmpty()
|
||||
c.UseFragmentMessage = base.ForceFragmented
|
||||
c.OnServerUpdated(func(bot *client.QQClient, e *client.ServerUpdatedEvent) bool {
|
||||
if !base.UseSSOAddress {
|
||||
log.Infof("收到服务器地址更新通知, 根据配置文件已忽略.")
|
||||
|
345
coolq/api.go
345
coolq/api.go
@ -38,7 +38,7 @@ type guildMemberPageToken struct {
|
||||
nextQueryParam string
|
||||
}
|
||||
|
||||
var defaultPageToken = &guildMemberPageToken{
|
||||
var defaultPageToken = guildMemberPageToken{
|
||||
guildID: 0,
|
||||
nextIndex: 0,
|
||||
nextRoleID: 2,
|
||||
@ -150,7 +150,7 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64, nextToken string) global.MSG
|
||||
if guild == nil {
|
||||
return Failed(100, "GUILD_NOT_FOUND")
|
||||
}
|
||||
token := defaultPageToken
|
||||
token := &defaultPageToken
|
||||
if nextToken != "" {
|
||||
i, exists := bot.nextTokenCache.Get(nextToken)
|
||||
if !exists {
|
||||
@ -407,7 +407,6 @@ func (bot *CQBot) CQGetGroupList(noCache bool) global.MSG {
|
||||
gs = append(gs, global.MSG{
|
||||
"group_id": g.Code,
|
||||
"group_name": g.Name,
|
||||
"group_memo": g.Memo,
|
||||
"group_create_time": g.GroupCreateTime,
|
||||
"group_level": g.GroupLevel,
|
||||
"max_member_count": g.MaxMemberCount,
|
||||
@ -449,7 +448,6 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG {
|
||||
return OK(global.MSG{
|
||||
"group_id": group.Code,
|
||||
"group_name": group.Name,
|
||||
"group_memo": group.Memo,
|
||||
"group_create_time": group.GroupCreateTime,
|
||||
"group_level": group.GroupLevel,
|
||||
"max_member_count": group.MaxMemberCount,
|
||||
@ -686,6 +684,24 @@ func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageTy
|
||||
return global.MSG{}
|
||||
}
|
||||
|
||||
// CQSendForwardMessage 发送合并转发消息
|
||||
//
|
||||
// @route(send_forward_msg)
|
||||
// @rename(m->messages)
|
||||
func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, messageType string) global.MSG {
|
||||
switch {
|
||||
case messageType == "group":
|
||||
return bot.CQSendGroupForwardMessage(groupID, m)
|
||||
case messageType == "private":
|
||||
fallthrough
|
||||
case userID != 0:
|
||||
return bot.CQSendPrivateForwardMessage(userID, m)
|
||||
case groupID != 0:
|
||||
return bot.CQSendGroupForwardMessage(groupID, m)
|
||||
}
|
||||
return global.MSG{}
|
||||
}
|
||||
|
||||
// CQSendGroupMessage 发送群消息
|
||||
//
|
||||
// https://git.io/Jtz1c
|
||||
@ -787,118 +803,126 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
|
||||
return OK(global.MSG{"message_id": mid})
|
||||
}
|
||||
|
||||
func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.ForwardElement {
|
||||
func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType message.SourceType) *message.ForwardElement {
|
||||
ts := time.Now().Add(-time.Minute * 5)
|
||||
fm := message.NewForwardMessage()
|
||||
source := message.Source{SourceType: message.SourceGroup, PrimaryID: groupID}
|
||||
|
||||
var w worker
|
||||
resolveElement := func(elems []message.IMessageElement) []message.IMessageElement {
|
||||
for i, elem := range elems {
|
||||
p := &elems[i]
|
||||
switch o := elem.(type) {
|
||||
case *LocalVideoElement:
|
||||
w.do(func() {
|
||||
gm, err := bot.uploadLocalVideo(source, o)
|
||||
if err != nil {
|
||||
log.Warnf(uploadFailedTemplate, "群", groupID, "视频", err)
|
||||
} else {
|
||||
*p = gm
|
||||
}
|
||||
})
|
||||
case *LocalImageElement:
|
||||
w.do(func() {
|
||||
gm, err := bot.uploadLocalImage(source, o)
|
||||
if err != nil {
|
||||
log.Warnf(uploadFailedTemplate, "群", groupID, "图片", err)
|
||||
} else {
|
||||
*p = gm
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return elems
|
||||
groupID := target
|
||||
source := message.Source{SourceType: sourceType, PrimaryID: target}
|
||||
if sourceType == message.SourcePrivate {
|
||||
groupID = 0
|
||||
}
|
||||
builder := bot.Client.NewForwardMessageBuilder(groupID)
|
||||
|
||||
convert := func(e gjson.Result) *message.ForwardNode {
|
||||
if e.Get("type").Str != "node" {
|
||||
return nil
|
||||
}
|
||||
ts.Add(time.Second)
|
||||
if e.Get("data.id").Exists() {
|
||||
i := e.Get("data.id").Int()
|
||||
m, _ := db.GetGroupMessageByGlobalID(int32(i))
|
||||
if m != nil {
|
||||
return &message.ForwardNode{
|
||||
SenderId: m.Attribute.SenderUin,
|
||||
SenderName: m.Attribute.SenderName,
|
||||
Time: func() int32 {
|
||||
msgTime := m.Attribute.Timestamp
|
||||
if msgTime == 0 {
|
||||
return int32(ts.Unix())
|
||||
var convertMessage func(m gjson.Result) *message.ForwardMessage
|
||||
convertMessage = func(m gjson.Result) *message.ForwardMessage {
|
||||
fm := message.NewForwardMessage()
|
||||
var w worker
|
||||
resolveElement := func(elems []message.IMessageElement) []message.IMessageElement {
|
||||
for i, elem := range elems {
|
||||
p := &elems[i]
|
||||
switch o := elem.(type) {
|
||||
case *LocalVideoElement:
|
||||
w.do(func() {
|
||||
gm, err := bot.uploadLocalVideo(source, o)
|
||||
if err != nil {
|
||||
log.Warnf(uploadFailedTemplate, "合并转发", target, "视频", err)
|
||||
} else {
|
||||
*p = gm
|
||||
}
|
||||
return int32(msgTime)
|
||||
}(),
|
||||
Message: resolveElement(bot.ConvertContentMessage(m.Content, message.SourceGroup)),
|
||||
})
|
||||
case *LocalImageElement:
|
||||
w.do(func() {
|
||||
gm, err := bot.uploadLocalImage(source, o)
|
||||
if err != nil {
|
||||
log.Warnf(uploadFailedTemplate, "合并转发", target, "图片", err)
|
||||
} else {
|
||||
*p = gm
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str)
|
||||
return nil
|
||||
return elems
|
||||
}
|
||||
uin := e.Get("data.[user_id,uin].0").Int()
|
||||
msgTime := e.Get("data.time").Int()
|
||||
if msgTime == 0 {
|
||||
msgTime = ts.Unix()
|
||||
}
|
||||
name := e.Get("data.name").Str
|
||||
c := e.Get("data.content")
|
||||
if c.IsArray() {
|
||||
nested := false
|
||||
c.ForEach(func(_, value gjson.Result) bool {
|
||||
if value.Get("type").Str == "node" {
|
||||
nested = true
|
||||
return false
|
||||
|
||||
convert := func(e gjson.Result) *message.ForwardNode {
|
||||
if e.Get("type").Str != "node" {
|
||||
return nil
|
||||
}
|
||||
if e.Get("data.id").Exists() {
|
||||
i := e.Get("data.id").Int()
|
||||
m, _ := db.GetGroupMessageByGlobalID(int32(i))
|
||||
if m != nil {
|
||||
msgTime := m.Attribute.Timestamp
|
||||
if msgTime == 0 {
|
||||
msgTime = ts.Unix()
|
||||
}
|
||||
return &message.ForwardNode{
|
||||
SenderId: m.Attribute.SenderUin,
|
||||
SenderName: m.Attribute.SenderName,
|
||||
Time: int32(msgTime),
|
||||
Message: resolveElement(bot.ConvertContentMessage(m.Content, message.SourceGroup)),
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if nested { // 处理嵌套
|
||||
fe := bot.uploadForwardElement(c, groupID)
|
||||
log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str)
|
||||
return nil
|
||||
}
|
||||
uin := e.Get("data.[user_id,uin].0").Int()
|
||||
msgTime := e.Get("data.time").Int()
|
||||
if msgTime == 0 {
|
||||
msgTime = ts.Unix()
|
||||
}
|
||||
name := e.Get("data.[name,nickname].0").Str
|
||||
c := e.Get("data.content")
|
||||
if c.IsArray() {
|
||||
nested := false
|
||||
c.ForEach(func(_, value gjson.Result) bool {
|
||||
if value.Get("type").Str == "node" {
|
||||
nested = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if nested { // 处理嵌套
|
||||
nestedNode := builder.NestedNode()
|
||||
builder.Link(nestedNode, convertMessage(c))
|
||||
return &message.ForwardNode{
|
||||
SenderId: uin,
|
||||
SenderName: name,
|
||||
Time: int32(msgTime),
|
||||
Message: []message.IMessageElement{nestedNode},
|
||||
}
|
||||
}
|
||||
}
|
||||
content := bot.ConvertObjectMessage(c, message.SourceGroup)
|
||||
if uin != 0 && name != "" && len(content) > 0 {
|
||||
return &message.ForwardNode{
|
||||
SenderId: uin,
|
||||
SenderName: name,
|
||||
Time: int32(msgTime),
|
||||
Message: []message.IMessageElement{fe},
|
||||
Message: resolveElement(content),
|
||||
}
|
||||
}
|
||||
log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content))
|
||||
return nil
|
||||
}
|
||||
content := bot.ConvertObjectMessage(c, message.SourceGroup)
|
||||
if uin != 0 && name != "" && len(content) > 0 {
|
||||
return &message.ForwardNode{
|
||||
SenderId: uin,
|
||||
SenderName: name,
|
||||
Time: int32(msgTime),
|
||||
Message: resolveElement(content),
|
||||
}
|
||||
}
|
||||
log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content))
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.IsArray() {
|
||||
for _, item := range m.Array() {
|
||||
node := convert(item)
|
||||
if m.IsArray() {
|
||||
for _, item := range m.Array() {
|
||||
node := convert(item)
|
||||
if node != nil {
|
||||
fm.AddNode(node)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node := convert(m)
|
||||
if node != nil {
|
||||
fm.AddNode(node)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node := convert(m)
|
||||
if node != nil {
|
||||
fm.AddNode(node)
|
||||
}
|
||||
|
||||
w.wait()
|
||||
return fm
|
||||
}
|
||||
w.wait()
|
||||
return bot.Client.UploadGroupForwardMessage(groupID, fm)
|
||||
return builder.Main(convertMessage(m))
|
||||
}
|
||||
|
||||
// CQSendGroupForwardMessage 扩展API-发送合并转发(群)
|
||||
@ -911,18 +935,39 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa
|
||||
return Failed(100)
|
||||
}
|
||||
|
||||
fe := bot.uploadForwardElement(m, groupID)
|
||||
if fe != nil {
|
||||
ret := bot.Client.SendGroupForwardMessage(groupID, fe)
|
||||
if ret == nil || ret.Id == -1 {
|
||||
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||
}
|
||||
return OK(global.MSG{
|
||||
"message_id": bot.InsertGroupMessage(ret),
|
||||
})
|
||||
fe := bot.uploadForwardElement(m, groupID, message.SourceGroup)
|
||||
if fe == nil {
|
||||
return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息")
|
||||
}
|
||||
return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息")
|
||||
ret := bot.Client.SendGroupForwardMessage(groupID, fe)
|
||||
if ret == nil || ret.Id == -1 {
|
||||
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||
}
|
||||
return OK(global.MSG{
|
||||
"message_id": bot.InsertGroupMessage(ret),
|
||||
})
|
||||
}
|
||||
|
||||
// CQSendPrivateForwardMessage 扩展API-发送合并转发(好友)
|
||||
//
|
||||
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4
|
||||
// @route(send_private_forward_msg)
|
||||
// @rename(m->messages)
|
||||
func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) global.MSG {
|
||||
if m.Type != gjson.JSON {
|
||||
return Failed(100)
|
||||
}
|
||||
fe := bot.uploadForwardElement(m, userID, message.SourcePrivate)
|
||||
if fe == nil {
|
||||
return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息")
|
||||
}
|
||||
mid := bot.SendPrivateMessage(userID, 0, &message.SendingMessage{Elements: []message.IMessageElement{fe}})
|
||||
if mid == -1 {
|
||||
log.Warnf("合并转发(好友)消息发送失败: 账号可能被风控.")
|
||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||
}
|
||||
return OK(global.MSG{"message_id": mid})
|
||||
}
|
||||
|
||||
// CQSendPrivateMessage 发送私聊消息
|
||||
@ -995,6 +1040,17 @@ func (bot *CQBot) CQSetGroupName(groupID int64, name string) global.MSG {
|
||||
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在")
|
||||
}
|
||||
|
||||
// CQGetGroupMemo 扩展API-获取群公告
|
||||
// @route(_get_group_notice)
|
||||
func (bot *CQBot) CQGetGroupMemo(groupID int64) global.MSG {
|
||||
r, err := bot.Client.GetGroupNotice(groupID)
|
||||
if err != nil {
|
||||
return Failed(100, "获取群公告失败", err.Error())
|
||||
}
|
||||
|
||||
return OK(r)
|
||||
}
|
||||
|
||||
// CQSetGroupMemo 扩展API-发送群公告
|
||||
//
|
||||
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E7%BE%A4%E5%85%AC%E5%91%8A
|
||||
@ -1115,9 +1171,9 @@ func (bot *CQBot) CQProcessFriendRequest(flag string, approve bool) global.MSG {
|
||||
return Failed(100, "FLAG_NOT_FOUND", "FLAG不存在")
|
||||
}
|
||||
if approve {
|
||||
req.(*client.NewFriendRequest).Accept()
|
||||
req.Accept()
|
||||
} else {
|
||||
req.(*client.NewFriendRequest).Reject()
|
||||
req.Reject()
|
||||
}
|
||||
return OK(nil)
|
||||
}
|
||||
@ -1224,27 +1280,6 @@ func (bot *CQBot) CQSetGroupAdmin(groupID, userID int64, enable bool) global.MSG
|
||||
return OK(nil)
|
||||
}
|
||||
|
||||
// CQGetVipInfo 扩展API-获取VIP信息
|
||||
//
|
||||
// https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96vip%E4%BF%A1%E6%81%AF
|
||||
// @route(_get_vip_info)
|
||||
func (bot *CQBot) CQGetVipInfo(userID int64) global.MSG {
|
||||
vip, err := bot.Client.GetVipInfo(userID)
|
||||
if err != nil {
|
||||
return Failed(100, "VIP_API_ERROR", err.Error())
|
||||
}
|
||||
msg := global.MSG{
|
||||
"user_id": vip.Uin,
|
||||
"nickname": vip.Name,
|
||||
"level": vip.Level,
|
||||
"level_speed": vip.LevelSpeed,
|
||||
"vip_level": vip.VipLevel,
|
||||
"vip_growth_speed": vip.VipGrowthSpeed,
|
||||
"vip_growth_total": vip.VipGrowthTotal,
|
||||
}
|
||||
return OK(msg)
|
||||
}
|
||||
|
||||
// CQGetGroupHonorInfo 获取群荣誉信息
|
||||
//
|
||||
// https://git.io/Jtz1H
|
||||
@ -1348,7 +1383,7 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global
|
||||
|
||||
if reply.Exists() {
|
||||
autoEscape := param.EnsureBool(operation.Get("auto_escape"), false)
|
||||
at := operation.Get("at_sender").Bool() && !isAnonymous && msgType == "group"
|
||||
at := !isAnonymous && operation.Get("at_sender").Bool() && msgType == "group"
|
||||
if at && reply.IsArray() {
|
||||
// 在 reply 数组头部插入CQ码
|
||||
replySegments := make([]global.MSG, 0)
|
||||
@ -1394,7 +1429,7 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global
|
||||
if operation.Get("delete").Bool() {
|
||||
bot.CQDeleteMessage(int32(context.Get("message_id").Int()))
|
||||
}
|
||||
if operation.Get("kick").Bool() && !isAnonymous {
|
||||
if !isAnonymous && operation.Get("kick").Bool() {
|
||||
bot.CQSetGroupKick(context.Get("group_id").Int(), context.Get("user_id").Int(), "", operation.Get("reject_add_request").Bool())
|
||||
}
|
||||
if operation.Get("ban").Bool() {
|
||||
@ -1476,18 +1511,18 @@ func (bot *CQBot) CQDownloadFile(url string, headers gjson.Result, threadCount i
|
||||
h := map[string]string{}
|
||||
if headers.IsArray() {
|
||||
for _, sub := range headers.Array() {
|
||||
str := strings.SplitN(sub.String(), "=", 2)
|
||||
if len(str) == 2 {
|
||||
h[str[0]] = str[1]
|
||||
first, second, ok := strings.Cut(sub.String(), "=")
|
||||
if ok {
|
||||
h[first] = second
|
||||
}
|
||||
}
|
||||
}
|
||||
if headers.Type == gjson.String {
|
||||
lines := strings.Split(headers.String(), "\r\n")
|
||||
for _, sub := range lines {
|
||||
str := strings.SplitN(sub, "=", 2)
|
||||
if len(str) == 2 {
|
||||
h[str[0]] = str[1]
|
||||
first, second, ok := strings.Cut(sub, "=")
|
||||
if ok {
|
||||
h[first] = second
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1526,7 +1561,7 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG {
|
||||
r := make([]global.MSG, len(nodes))
|
||||
for i, n := range nodes {
|
||||
bot.checkMedia(n.Message, 0)
|
||||
content := ToFormattedMessage(n.Message, message.Source{SourceType: message.SourceGroup}, false)
|
||||
content := ToFormattedMessage(n.Message, message.Source{SourceType: message.SourceGroup})
|
||||
if len(n.Message) == 1 {
|
||||
if forward, ok := n.Message[0].(*message.ForwardMessage); ok {
|
||||
content = transformNodes(forward.Nodes)
|
||||
@ -1537,8 +1572,9 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG {
|
||||
"user_id": n.SenderId,
|
||||
"nickname": n.SenderName,
|
||||
},
|
||||
"time": n.Time,
|
||||
"content": content,
|
||||
"time": n.Time,
|
||||
"content": content,
|
||||
"group_id": n.GroupId,
|
||||
}
|
||||
}
|
||||
return r
|
||||
@ -1574,9 +1610,9 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG {
|
||||
switch o := msg.(type) {
|
||||
case *db.StoredGroupMessage:
|
||||
m["group_id"] = o.GroupCode
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}, false)
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode})
|
||||
case *db.StoredPrivateMessage:
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate}, false)
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate})
|
||||
}
|
||||
return OK(m)
|
||||
}
|
||||
@ -1585,7 +1621,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG {
|
||||
// @route(get_guild_msg)
|
||||
func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG {
|
||||
source, seq := decodeGuildMessageID(messageID)
|
||||
if source == nil {
|
||||
if source.SourceType == 0 {
|
||||
log.Warnf("获取消息时出现错误: 无效消息ID")
|
||||
return Failed(100, "INVALID_MESSAGE_ID", "无效消息ID")
|
||||
}
|
||||
@ -1621,7 +1657,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG {
|
||||
"tiny_id": fU64(pull[0].Sender.TinyId),
|
||||
"nickname": pull[0].Sender.Nickname,
|
||||
}
|
||||
m["message"] = ToFormattedMessage(pull[0].Elements, *source, false)
|
||||
m["message"] = ToFormattedMessage(pull[0].Elements, source)
|
||||
m["reactions"] = convertReactions(pull[0].Reactions)
|
||||
bot.InsertGuildChannelMessage(pull[0])
|
||||
} else {
|
||||
@ -1636,7 +1672,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG {
|
||||
"tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID),
|
||||
"nickname": channelMsgByDB.Attribute.SenderName,
|
||||
}
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), *source)
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), source)
|
||||
}
|
||||
case message.SourceGuildDirect:
|
||||
// todo(mrs4s): 支持 direct 消息
|
||||
@ -1679,12 +1715,12 @@ func (bot *CQBot) CQGetGroupMessageHistory(groupID int64, seq int64) global.MSG
|
||||
log.Warnf("获取群历史消息失败: %v", err)
|
||||
return Failed(100, "MESSAGES_API_ERROR", err.Error())
|
||||
}
|
||||
ms := make([]global.MSG, 0, len(msg))
|
||||
ms := make([]*event, 0, len(msg))
|
||||
for _, m := range msg {
|
||||
bot.checkMedia(m.Elements, groupID)
|
||||
id := bot.InsertGroupMessage(m)
|
||||
t := bot.formatGroupMessage(m)
|
||||
t["message_id"] = id
|
||||
t.Others["message_id"] = id
|
||||
ms = append(ms, t)
|
||||
}
|
||||
return OK(global.MSG{
|
||||
@ -1778,12 +1814,10 @@ func (bot *CQBot) CQSetGroupAnonymousBan(groupID int64, flag string, duration in
|
||||
return Failed(100, "INVALID_FLAG", "无效的flag")
|
||||
}
|
||||
if g := bot.Client.FindGroup(groupID); g != nil {
|
||||
s := strings.SplitN(flag, "|", 2)
|
||||
if len(s) != 2 {
|
||||
id, nick, ok := strings.Cut(flag, "|")
|
||||
if !ok {
|
||||
return Failed(100, "INVALID_FLAG", "无效的flag")
|
||||
}
|
||||
id := s[0]
|
||||
nick := s[1]
|
||||
if err := g.MuteAnonymous(id, nick, duration); err != nil {
|
||||
log.Warnf("anonymous ban error: %v", err)
|
||||
return Failed(100, "CALL_API_ERROR", err.Error())
|
||||
@ -1940,6 +1974,15 @@ func (bot *CQBot) CQGetModelShow(model string) global.MSG {
|
||||
})
|
||||
}
|
||||
|
||||
// CQSendGroupSign 群打卡
|
||||
//
|
||||
// https://club.vip.qq.com/onlinestatus/set
|
||||
// @route(send_group_sign)
|
||||
func (bot *CQBot) CQSendGroupSign(groupID int64) global.MSG {
|
||||
bot.Client.SendGroupSign(groupID)
|
||||
return OK(nil)
|
||||
}
|
||||
|
||||
// CQSetModelShow 设置在线机型
|
||||
//
|
||||
// https://club.vip.qq.com/onlinestatus/set
|
||||
|
57
coolq/bot.go
57
coolq/bot.go
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -14,6 +15,7 @@ import (
|
||||
"github.com/Mrs4s/MiraiGo/client"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
"github.com/RomiChan/syncx"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/asm/base64"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -30,16 +32,15 @@ type CQBot struct {
|
||||
lock sync.RWMutex
|
||||
events []func(*Event)
|
||||
|
||||
friendReqCache sync.Map
|
||||
tempSessionCache sync.Map
|
||||
friendReqCache syncx.Map[string, *client.NewFriendRequest]
|
||||
tempSessionCache syncx.Map[int64, *client.TempSessionInfo]
|
||||
nextTokenCache *utils.Cache[*guildMemberPageToken]
|
||||
}
|
||||
|
||||
// Event 事件
|
||||
type Event struct {
|
||||
RawMsg global.MSG
|
||||
|
||||
once sync.Once
|
||||
Raw *event
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
@ -47,7 +48,7 @@ func (e *Event) marshal() {
|
||||
if e.buffer == nil {
|
||||
e.buffer = global.NewBuffer()
|
||||
}
|
||||
_ = json.NewEncoder(e.buffer).Encode(e.RawMsg)
|
||||
_ = json.NewEncoder(e.buffer).Encode(e.Raw)
|
||||
}
|
||||
|
||||
// JSONBytes return byes of json by lazy marshalling.
|
||||
@ -109,13 +110,9 @@ func NewQQBot(cli *client.QQClient) *CQBot {
|
||||
t := time.NewTicker(base.HeartbeatInterval)
|
||||
for {
|
||||
<-t.C
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": bot.Client.Uin,
|
||||
"post_type": "meta_event",
|
||||
"meta_event_type": "heartbeat",
|
||||
"status": bot.CQGetStatus()["data"],
|
||||
"interval": base.HeartbeatInterval.Milliseconds(),
|
||||
bot.dispatchEvent("meta_event/heartbeat", global.MSG{
|
||||
"status": bot.CQGetStatus()["data"],
|
||||
"interval": base.HeartbeatInterval.Milliseconds(),
|
||||
})
|
||||
}
|
||||
}()
|
||||
@ -288,7 +285,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int
|
||||
}
|
||||
m.Elements = newElem
|
||||
bot.checkMedia(newElem, groupID)
|
||||
ret := bot.Client.SendGroupMessage(groupID, m, base.ForceFragmented)
|
||||
ret := bot.Client.SendGroupMessage(groupID, m)
|
||||
if ret == nil || ret.Id == -1 {
|
||||
log.Warnf("群消息发送失败: 账号可能被风控.")
|
||||
return -1
|
||||
@ -360,17 +357,19 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
||||
default:
|
||||
if session == nil && groupID != 0 {
|
||||
msg := bot.Client.SendGroupTempMessage(groupID, target, m)
|
||||
//lint:ignore SA9003 there is a todo
|
||||
if msg != nil { // nolint
|
||||
// todo(Mrs4s)
|
||||
// id = bot.InsertTempMessage(target, msg)
|
||||
}
|
||||
break
|
||||
}
|
||||
msg, err := session.(*client.TempSessionInfo).SendMessage(m)
|
||||
msg, err := session.SendMessage(m)
|
||||
if err != nil {
|
||||
log.Errorf("发送临时会话消息失败: %v", err)
|
||||
break
|
||||
}
|
||||
//lint:ignore SA9003 there is a todo
|
||||
if msg != nil { // nolint
|
||||
// todo(Mrs4s)
|
||||
// id = bot.InsertTempMessage(target, msg)
|
||||
@ -566,11 +565,31 @@ func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) stri
|
||||
return msg.ID
|
||||
}
|
||||
|
||||
func (bot *CQBot) dispatchEventMessage(m global.MSG) {
|
||||
func (bot *CQBot) event(typ string, others global.MSG) *event {
|
||||
ev := new(event)
|
||||
post, detail, ok := strings.Cut(typ, "/")
|
||||
ev.PostType = post
|
||||
ev.DetailType = detail
|
||||
if ok {
|
||||
detail, sub, _ := strings.Cut(detail, "/")
|
||||
ev.DetailType = detail
|
||||
ev.SubType = sub
|
||||
}
|
||||
ev.Time = time.Now().Unix()
|
||||
ev.SelfID = bot.Client.Uin
|
||||
ev.Others = others
|
||||
return ev
|
||||
}
|
||||
|
||||
func (bot *CQBot) dispatchEvent(typ string, others global.MSG) {
|
||||
bot.dispatch(bot.event(typ, others))
|
||||
}
|
||||
|
||||
func (bot *CQBot) dispatch(ev *event) {
|
||||
bot.lock.RLock()
|
||||
defer bot.lock.RUnlock()
|
||||
|
||||
event := &Event{RawMsg: m}
|
||||
event := &Event{Raw: ev}
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(bot.events))
|
||||
for _, f := range bot.events {
|
||||
@ -578,7 +597,7 @@ func (bot *CQBot) dispatchEventMessage(m global.MSG) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
if pan := recover(); pan != nil {
|
||||
log.Warnf("处理事件 %v 时出现错误: %v \n%s", m, pan, debug.Stack())
|
||||
log.Warnf("处理事件 %v 时出现错误: %v \n%s", event.JSONString(), pan, debug.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
@ -627,13 +646,13 @@ func encodeGuildMessageID(primaryID, subID, seq uint64, source message.SourceTyp
|
||||
}))
|
||||
}
|
||||
|
||||
func decodeGuildMessageID(id string) (source *message.Source, seq uint64) {
|
||||
func decodeGuildMessageID(id string) (source message.Source, seq uint64) {
|
||||
b, _ := base64.StdEncoding.DecodeString(id)
|
||||
if len(b) < 25 {
|
||||
return
|
||||
}
|
||||
r := binary.NewReader(b)
|
||||
source = &message.Source{
|
||||
source = message.Source{
|
||||
SourceType: message.SourceType(r.ReadByte()),
|
||||
PrimaryID: r.ReadInt64(),
|
||||
SecondaryID: r.ReadInt64(),
|
||||
|
@ -2,6 +2,7 @@ package coolq
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/topic"
|
||||
|
||||
@ -41,7 +42,7 @@ func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG
|
||||
"role": role,
|
||||
"unfriendly": false,
|
||||
"title": m.SpecialTitle,
|
||||
"title_expire_time": m.SpecialTitleExpireTime,
|
||||
"title_expire_time": 0,
|
||||
"card_changeable": false,
|
||||
}
|
||||
}
|
||||
@ -59,26 +60,24 @@ func convertGuildMemberInfo(m []*client.GuildMemberInfo) (r []global.MSG) {
|
||||
return
|
||||
}
|
||||
|
||||
func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG {
|
||||
func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) *event {
|
||||
source := message.Source{
|
||||
SourceType: message.SourceGroup,
|
||||
PrimaryID: m.GroupCode,
|
||||
}
|
||||
cqm := ToStringMessage(m.Elements, source, true)
|
||||
postType := "message"
|
||||
cqm := toStringMessage(m.Elements, source)
|
||||
typ := "message/group/normal"
|
||||
if m.Sender.Uin == bot.Client.Uin {
|
||||
postType = "message_sent"
|
||||
typ = "message_sent/group/normal"
|
||||
}
|
||||
gm := global.MSG{
|
||||
"anonymous": nil,
|
||||
"font": 0,
|
||||
"group_id": m.GroupCode,
|
||||
"message": ToFormattedMessage(m.Elements, source, false),
|
||||
"message": ToFormattedMessage(m.Elements, source),
|
||||
"message_type": "group",
|
||||
"message_seq": m.Id,
|
||||
"post_type": postType,
|
||||
"raw_message": cqm,
|
||||
"self_id": bot.Client.Uin,
|
||||
"sender": global.MSG{
|
||||
"age": 0,
|
||||
"area": "",
|
||||
@ -86,9 +85,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG {
|
||||
"sex": "unknown",
|
||||
"user_id": m.Sender.Uin,
|
||||
},
|
||||
"sub_type": "normal",
|
||||
"time": m.Time,
|
||||
"user_id": m.Sender.Uin,
|
||||
"user_id": m.Sender.Uin,
|
||||
}
|
||||
if m.Sender.IsAnonymous() {
|
||||
gm["anonymous"] = global.MSG{
|
||||
@ -127,7 +124,9 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG {
|
||||
ms["card"] = mem.CardName
|
||||
ms["title"] = mem.SpecialTitle
|
||||
}
|
||||
return gm
|
||||
ev := bot.event(typ, gm)
|
||||
ev.Time = int64(m.Time)
|
||||
return ev
|
||||
}
|
||||
|
||||
func convertChannelInfo(c *client.ChannelInfo) global.MSG {
|
||||
@ -211,6 +210,15 @@ func convertReactions(reactions []*message.GuildMessageEmojiReaction) (r []globa
|
||||
return
|
||||
}
|
||||
|
||||
func toStringMessage(m []message.IMessageElement, source message.Source) string {
|
||||
elems := toElements(m, source)
|
||||
var sb strings.Builder
|
||||
for _, elem := range elems {
|
||||
elem.WriteCQCodeTo(&sb)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func fU64(v uint64) string {
|
||||
return strconv.FormatUint(v, 10)
|
||||
}
|
||||
|
392
coolq/cqcode.go
392
coolq/cqcode.go
@ -92,9 +92,14 @@ func replyID(r *message.ReplyElement, source message.Source) int32 {
|
||||
return db.ToGlobalID(id, seq)
|
||||
}
|
||||
|
||||
// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报
|
||||
func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []global.MSG) {
|
||||
r = make([]global.MSG, 0, len(e))
|
||||
// toElements 将消息元素数组转为MSG数组以用于消息上报
|
||||
//
|
||||
// nolint:govet
|
||||
func toElements(e []message.IMessageElement, source message.Source) (r []cqcode.Element) {
|
||||
type pair = cqcode.Pair // simplify code
|
||||
type pairs = []pair
|
||||
|
||||
r = make([]cqcode.Element, 0, len(e))
|
||||
m := &message.SendingMessage{Elements: e}
|
||||
reply := m.FirstOrNil(func(e message.IMessageElement) bool {
|
||||
_, ok := e.(*message.ReplyElement)
|
||||
@ -103,26 +108,24 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo
|
||||
if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 {
|
||||
replyElem := reply.(*message.ReplyElement)
|
||||
id := replyID(replyElem, source)
|
||||
if base.ExtraReplyData {
|
||||
r = append(r, global.MSG{
|
||||
"type": "reply",
|
||||
"data": map[string]string{
|
||||
"id": strconv.FormatInt(int64(id), 10),
|
||||
"seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10),
|
||||
"qq": strconv.FormatInt(replyElem.Sender, 10),
|
||||
"time": strconv.FormatInt(int64(replyElem.Time), 10),
|
||||
"text": ToStringMessage(replyElem.Elements, source),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
r = append(r, global.MSG{
|
||||
"type": "reply",
|
||||
"data": map[string]string{"id": strconv.FormatInt(int64(id), 10)},
|
||||
})
|
||||
elem := cqcode.Element{
|
||||
Type: "reply",
|
||||
Data: pairs{
|
||||
{K: "id", V: strconv.FormatInt(int64(id), 10)},
|
||||
},
|
||||
}
|
||||
if base.ExtraReplyData {
|
||||
elem.Data = append(elem.Data,
|
||||
pair{K: "seq", V: strconv.FormatInt(int64(replyElem.ReplySeq), 10)},
|
||||
pair{K: "qq", V: strconv.FormatInt(replyElem.Sender, 10)},
|
||||
pair{K: "time", V: strconv.FormatInt(int64(replyElem.Time), 10)},
|
||||
pair{K: "text", V: toStringMessage(replyElem.Elements, source)},
|
||||
)
|
||||
}
|
||||
r = append(r, elem)
|
||||
}
|
||||
for i, elem := range e {
|
||||
var m global.MSG
|
||||
var m cqcode.Element
|
||||
switch o := elem.(type) {
|
||||
case *message.ReplyElement:
|
||||
if base.RemoveReplyAt && i+1 < len(e) {
|
||||
@ -131,241 +134,155 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo
|
||||
e[i+1] = nil
|
||||
}
|
||||
}
|
||||
continue
|
||||
case *message.TextElement:
|
||||
m = global.MSG{
|
||||
"type": "text",
|
||||
"data": map[string]string{"text": o.Content},
|
||||
m = cqcode.Element{
|
||||
Type: "text",
|
||||
Data: pairs{
|
||||
{K: "text", V: o.Content},
|
||||
},
|
||||
}
|
||||
case *message.LightAppElement:
|
||||
m = global.MSG{
|
||||
"type": "json",
|
||||
"data": map[string]string{"data": o.Content},
|
||||
m = cqcode.Element{
|
||||
Type: "json",
|
||||
Data: pairs{
|
||||
{K: "data", V: o.Content},
|
||||
},
|
||||
}
|
||||
case *message.AtElement:
|
||||
if o.Target == 0 {
|
||||
m = global.MSG{
|
||||
"type": "at",
|
||||
"data": map[string]string{"qq": "all"},
|
||||
}
|
||||
} else {
|
||||
m = global.MSG{
|
||||
"type": "at",
|
||||
"data": map[string]string{"qq": strconv.FormatUint(uint64(o.Target), 10)},
|
||||
}
|
||||
target := "all"
|
||||
if o.Target != 0 {
|
||||
target = strconv.FormatUint(uint64(o.Target), 10)
|
||||
}
|
||||
m = cqcode.Element{
|
||||
Type: "at",
|
||||
Data: pairs{
|
||||
{K: "qq", V: target},
|
||||
},
|
||||
}
|
||||
case *message.RedBagElement:
|
||||
m = global.MSG{
|
||||
"type": "redbag",
|
||||
"data": map[string]string{"title": o.Title},
|
||||
m = cqcode.Element{
|
||||
Type: "redbag",
|
||||
Data: pairs{
|
||||
{K: "title", V: o.Title},
|
||||
},
|
||||
}
|
||||
case *message.ForwardElement:
|
||||
m = global.MSG{
|
||||
"type": "forward",
|
||||
"data": map[string]string{"id": o.ResId},
|
||||
m = cqcode.Element{
|
||||
Type: "forward",
|
||||
Data: pairs{
|
||||
{K: "id", V: o.ResId},
|
||||
},
|
||||
}
|
||||
case *message.FaceElement:
|
||||
m = global.MSG{
|
||||
"type": "face",
|
||||
"data": map[string]string{"id": strconv.FormatInt(int64(o.Index), 10)},
|
||||
m = cqcode.Element{
|
||||
Type: "face",
|
||||
Data: pairs{
|
||||
{K: "id", V: strconv.FormatInt(int64(o.Index), 10)},
|
||||
},
|
||||
}
|
||||
case *message.VoiceElement:
|
||||
m = global.MSG{
|
||||
"type": "record",
|
||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||
m = cqcode.Element{
|
||||
Type: "record",
|
||||
Data: pairs{
|
||||
{K: "file", V: o.Name},
|
||||
{K: "url", V: o.Url},
|
||||
},
|
||||
}
|
||||
case *message.ShortVideoElement:
|
||||
m = global.MSG{
|
||||
"type": "video",
|
||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||
m = cqcode.Element{
|
||||
Type: "video",
|
||||
Data: pairs{
|
||||
{K: "file", V: o.Name},
|
||||
{K: "url", V: o.Url},
|
||||
},
|
||||
}
|
||||
case *message.GroupImageElement:
|
||||
data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url, "subType": strconv.FormatInt(int64(o.ImageBizType), 10)}
|
||||
data := pairs{
|
||||
{K: "file", V: hex.EncodeToString(o.Md5) + ".image"},
|
||||
{K: "subType", V: strconv.FormatInt(int64(o.ImageBizType), 10)},
|
||||
{K: "url", V: o.Url},
|
||||
}
|
||||
switch {
|
||||
case o.Flash:
|
||||
data["type"] = "flash"
|
||||
data = append(data, pair{K: "type", V: "flash"})
|
||||
case o.EffectID != 0:
|
||||
data["type"] = "show"
|
||||
data["id"] = strconv.FormatInt(int64(o.EffectID), 10)
|
||||
data = append(data, pair{K: "type", V: "show"})
|
||||
data = append(data, pair{K: "id", V: strconv.FormatInt(int64(o.EffectID), 10)})
|
||||
}
|
||||
m = global.MSG{
|
||||
"type": "image",
|
||||
"data": data,
|
||||
m = cqcode.Element{
|
||||
Type: "image",
|
||||
Data: data,
|
||||
}
|
||||
case *message.GuildImageElement:
|
||||
data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url}
|
||||
m = global.MSG{
|
||||
"type": "image",
|
||||
"data": data,
|
||||
data := pairs{
|
||||
{K: "file", V: hex.EncodeToString(o.Md5) + ".image"},
|
||||
{K: "url", V: o.Url},
|
||||
}
|
||||
m = cqcode.Element{
|
||||
Type: "image",
|
||||
Data: data,
|
||||
}
|
||||
case *message.FriendImageElement:
|
||||
data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url}
|
||||
if o.Flash {
|
||||
data["type"] = "flash"
|
||||
data := pairs{
|
||||
{K: "file", V: hex.EncodeToString(o.Md5) + ".image"},
|
||||
{K: "url", V: o.Url},
|
||||
}
|
||||
m = global.MSG{
|
||||
"type": "image",
|
||||
"data": data,
|
||||
if o.Flash {
|
||||
data = append(data, pair{K: "type", V: "flash"})
|
||||
}
|
||||
m = cqcode.Element{
|
||||
Type: "image",
|
||||
Data: data,
|
||||
}
|
||||
case *message.DiceElement:
|
||||
m = global.MSG{
|
||||
"type": "dice",
|
||||
"data": map[string]string{"value": fmt.Sprint(o.Value)},
|
||||
m = cqcode.Element{
|
||||
Type: "dice",
|
||||
Data: pairs{
|
||||
{K: "value", V: strconv.FormatInt(int64(o.Value), 10)},
|
||||
},
|
||||
}
|
||||
case *message.FingerGuessingElement:
|
||||
m = cqcode.Element{
|
||||
Type: "rps",
|
||||
Data: pairs{
|
||||
{K: "value", V: strconv.FormatInt(int64(o.Value), 10)},
|
||||
},
|
||||
}
|
||||
case *message.MarketFaceElement:
|
||||
m = global.MSG{
|
||||
"type": "text",
|
||||
"data": map[string]string{"text": o.Name},
|
||||
m = cqcode.Element{
|
||||
Type: "text",
|
||||
Data: pairs{
|
||||
{K: "text", V: o.Name},
|
||||
},
|
||||
}
|
||||
case *message.ServiceElement:
|
||||
if isOk := strings.Contains(o.Content, "<?xml"); isOk {
|
||||
m = global.MSG{
|
||||
"type": "xml",
|
||||
"data": map[string]string{"data": o.Content, "resid": strconv.FormatInt(int64(o.Id), 10)},
|
||||
}
|
||||
} else {
|
||||
m = global.MSG{
|
||||
"type": "json",
|
||||
"data": map[string]string{"data": o.Content, "resid": strconv.FormatInt(int64(o.Id), 10)},
|
||||
}
|
||||
m = cqcode.Element{
|
||||
Type: "xml",
|
||||
Data: pairs{
|
||||
{K: "data", V: o.Content},
|
||||
{K: "resid", V: o.ResId},
|
||||
},
|
||||
}
|
||||
if !strings.Contains(o.Content, "<?xml") {
|
||||
m.Type = "json"
|
||||
}
|
||||
case *message.AnimatedSticker:
|
||||
m = global.MSG{
|
||||
"type": "face",
|
||||
"data": map[string]string{"id": strconv.FormatInt(int64(o.ID), 10), "type": "sticker"},
|
||||
m = cqcode.Element{
|
||||
Type: "face",
|
||||
Data: pairs{
|
||||
{K: "id", V: strconv.FormatInt(int64(o.ID), 10)},
|
||||
{K: "type", V: "sticker"},
|
||||
},
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if m != nil {
|
||||
r = append(r, m)
|
||||
}
|
||||
r = append(r, m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToStringMessage 将消息元素数组转为字符串以用于消息上报
|
||||
func ToStringMessage(e []message.IMessageElement, source message.Source, isRaw ...bool) (r string) {
|
||||
sb := global.NewBuffer()
|
||||
sb.Reset()
|
||||
write := func(format string, a ...interface{}) {
|
||||
_, _ = fmt.Fprintf(sb, format, a...)
|
||||
}
|
||||
ur := false
|
||||
if len(isRaw) != 0 {
|
||||
ur = isRaw[0]
|
||||
}
|
||||
// 方便
|
||||
m := &message.SendingMessage{Elements: e}
|
||||
reply := m.FirstOrNil(func(e message.IMessageElement) bool {
|
||||
_, ok := e.(*message.ReplyElement)
|
||||
return ok
|
||||
})
|
||||
if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 {
|
||||
replyElem := reply.(*message.ReplyElement)
|
||||
id := replyID(replyElem, source)
|
||||
if base.ExtraReplyData {
|
||||
write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
|
||||
id, replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
|
||||
cqcode.EscapeValue(ToStringMessage(replyElem.Elements, source)))
|
||||
} else {
|
||||
write("[CQ:reply,id=%d]", id)
|
||||
}
|
||||
}
|
||||
for i, elem := range e {
|
||||
switch o := elem.(type) {
|
||||
case *message.ReplyElement:
|
||||
if base.RemoveReplyAt && len(e) > i+1 {
|
||||
elem, ok := e[i+1].(*message.AtElement)
|
||||
if ok && elem.Target == o.Sender {
|
||||
e[i+1] = nil
|
||||
}
|
||||
}
|
||||
case *message.TextElement:
|
||||
sb.WriteString(cqcode.EscapeText(o.Content))
|
||||
case *message.AtElement:
|
||||
if o.Target == 0 {
|
||||
write("[CQ:at,qq=all]")
|
||||
continue
|
||||
}
|
||||
write("[CQ:at,qq=%d]", uint64(o.Target))
|
||||
case *message.RedBagElement:
|
||||
write("[CQ:redbag,title=%s]", o.Title)
|
||||
case *message.ForwardElement:
|
||||
write("[CQ:forward,id=%s]", o.ResId)
|
||||
case *message.FaceElement:
|
||||
write(`[CQ:face,id=%d]`, o.Index)
|
||||
case *message.VoiceElement:
|
||||
if ur {
|
||||
write(`[CQ:record,file=%s]`, o.Name)
|
||||
} else {
|
||||
write(`[CQ:record,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url))
|
||||
}
|
||||
case *message.ShortVideoElement:
|
||||
if ur {
|
||||
write(`[CQ:video,file=%s]`, o.Name)
|
||||
} else {
|
||||
write(`[CQ:video,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url))
|
||||
}
|
||||
case *message.GroupImageElement:
|
||||
var arg string
|
||||
if o.Flash {
|
||||
arg = ",type=flash"
|
||||
} else if o.EffectID != 0 {
|
||||
arg = ",type=show,id=" + strconv.FormatInt(int64(o.EffectID), 10)
|
||||
}
|
||||
arg += ",subType=" + strconv.FormatInt(int64(o.ImageBizType), 10)
|
||||
if ur {
|
||||
write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg)
|
||||
} else {
|
||||
write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg)
|
||||
}
|
||||
case *message.FriendImageElement:
|
||||
var arg string
|
||||
if o.Flash {
|
||||
arg = ",type=flash"
|
||||
}
|
||||
if ur {
|
||||
write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg)
|
||||
} else {
|
||||
write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg)
|
||||
}
|
||||
case *LocalImageElement:
|
||||
var arg string
|
||||
if o.Flash {
|
||||
arg = ",type=flash"
|
||||
}
|
||||
data, err := os.ReadFile(o.File)
|
||||
if err == nil {
|
||||
m := md5.Sum(data)
|
||||
if ur {
|
||||
write("[CQ:image,file=%s%s]", hex.EncodeToString(m[:])+".image", arg)
|
||||
} else {
|
||||
write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(m[:])+".image", cqcode.EscapeValue(o.URL), arg)
|
||||
}
|
||||
}
|
||||
case *message.GuildImageElement:
|
||||
write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url))
|
||||
case *message.DiceElement:
|
||||
write("[CQ:dice,value=%v]", o.Value)
|
||||
case *message.MarketFaceElement:
|
||||
sb.WriteString(o.Name)
|
||||
case *message.ServiceElement:
|
||||
if isOk := strings.Contains(o.Content, "<?xml"); isOk {
|
||||
write(`[CQ:xml,data=%s,resid=%d]`, cqcode.EscapeValue(o.Content), o.Id)
|
||||
} else {
|
||||
write(`[CQ:json,data=%s,resid=%d]`, cqcode.EscapeValue(o.Content), o.Id)
|
||||
}
|
||||
case *message.LightAppElement:
|
||||
write(`[CQ:json,data=%s]`, cqcode.EscapeValue(o.Content))
|
||||
case *message.AnimatedSticker:
|
||||
write(`[CQ:face,id=%d,type=sticker]`, o.ID)
|
||||
}
|
||||
}
|
||||
r = sb.String() // 内部已拷贝
|
||||
global.PutBuffer(sb)
|
||||
return
|
||||
}
|
||||
|
||||
// ToMessageContent 将消息转换成 Content. 忽略 Reply
|
||||
// 不同于 onebot 的 Array Message, 此函数转换出来的 Content 的 data 段为实际类型
|
||||
// 方便数据库查询
|
||||
@ -455,6 +372,8 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) {
|
||||
}
|
||||
case *message.DiceElement:
|
||||
m = global.MSG{"type": "dice", "data": global.MSG{"value": o.Value}}
|
||||
case *message.FingerGuessingElement:
|
||||
m = global.MSG{"type": "rps", "data": global.MSG{"value": o.Value}}
|
||||
case *message.MarketFaceElement:
|
||||
m = global.MSG{"type": "text", "data": global.MSG{"text": o.Name}}
|
||||
case *message.ServiceElement:
|
||||
@ -477,9 +396,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) {
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if m != nil {
|
||||
r = append(r, m)
|
||||
}
|
||||
r = append(r, m)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -917,9 +834,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
||||
case "record":
|
||||
f := d["file"]
|
||||
data, err := global.FindFile(f, d["cache"], global.VoicePath)
|
||||
if err == global.ErrSyntax {
|
||||
data, err = global.FindFile(f, d["cache"], global.VoicePathOld)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1053,6 +967,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
||||
return nil, errors.New("invalid dice value " + value)
|
||||
}
|
||||
return message.NewDice(int32(i)), nil
|
||||
case "rps":
|
||||
value := d["value"]
|
||||
i, _ := strconv.ParseInt(value, 10, 64)
|
||||
if i < 0 || i > 2 {
|
||||
return nil, errors.New("invalid finger-guessing value " + value)
|
||||
}
|
||||
return message.NewFingerGuessing(int32(i)), nil
|
||||
case "xml":
|
||||
resID := d["resid"]
|
||||
template := cqcode.EscapeValue(d["data"])
|
||||
@ -1106,33 +1027,28 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
||||
if cover, ok := d["cover"]; ok {
|
||||
data, _ = global.FindFile(cover, d["cache"], global.ImagePath)
|
||||
} else {
|
||||
_ = global.ExtractCover(v.File, v.File+".jpg")
|
||||
err = global.ExtractCover(v.File, v.File+".jpg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, _ = os.ReadFile(v.File + ".jpg")
|
||||
}
|
||||
v.thumb = bytes.NewReader(data)
|
||||
video, _ := os.Open(v.File)
|
||||
defer video.Close()
|
||||
_, err = video.Seek(4, io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _ = video.Seek(4, io.SeekStart)
|
||||
header := make([]byte, 4)
|
||||
_, err = video.Read(header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _ = video.Read(header)
|
||||
if !bytes.Equal(header, []byte{0x66, 0x74, 0x79, 0x70}) { // check file header ftyp
|
||||
_, _ = video.Seek(0, io.SeekStart)
|
||||
hash, _ := utils.ComputeMd5AndLength(video)
|
||||
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".mp4")
|
||||
if global.PathExists(cacheFile) && (d["cache"] == "" || d["cache"] == "1") {
|
||||
goto ok
|
||||
if !(d["cache"] == "" || d["cache"] == "1") || !global.PathExists(cacheFile) {
|
||||
err = global.EncodeMP4(v.File, cacheFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = global.EncodeMP4(v.File, cacheFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ok:
|
||||
v.File = cacheFile
|
||||
}
|
||||
return v, nil
|
||||
@ -1198,7 +1114,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy
|
||||
}
|
||||
return &LocalImageElement{File: fu.Path, URL: f}, nil
|
||||
}
|
||||
if strings.HasPrefix(f, "base64") && !video {
|
||||
if !video && strings.HasPrefix(f, "base64") {
|
||||
b, err := param.Base64DecodeString(strings.TrimPrefix(f, "base64://"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1241,10 +1157,6 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy
|
||||
}
|
||||
}
|
||||
exist := global.PathExists(rawPath)
|
||||
if !exist && global.PathExists(path.Join(global.ImagePathOld, f)) {
|
||||
exist = true
|
||||
rawPath = path.Join(global.ImagePathOld, f)
|
||||
}
|
||||
if !exist {
|
||||
if d["url"] != "" {
|
||||
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, sourceType)
|
||||
|
67
coolq/cqcode/element.go
Normal file
67
coolq/cqcode/element.go
Normal file
@ -0,0 +1,67 @@
|
||||
package cqcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
)
|
||||
|
||||
// Element single message
|
||||
type Element struct {
|
||||
Type string
|
||||
Data []Pair
|
||||
}
|
||||
|
||||
// Pair key value pair
|
||||
type Pair struct {
|
||||
K string
|
||||
V string
|
||||
}
|
||||
|
||||
// CQCode convert element to cqcode
|
||||
func (e *Element) CQCode() string {
|
||||
buf := strings.Builder{}
|
||||
e.WriteCQCodeTo(&buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// WriteCQCodeTo write element's cqcode into sb
|
||||
func (e *Element) WriteCQCodeTo(sb *strings.Builder) {
|
||||
if e.Type == "text" {
|
||||
sb.WriteString(EscapeText(e.Data[0].V)) // must be {"text": value}
|
||||
return
|
||||
}
|
||||
sb.WriteString("[CQ:")
|
||||
sb.WriteString(e.Type)
|
||||
for _, data := range e.Data {
|
||||
sb.WriteByte(',')
|
||||
sb.WriteString(data.K)
|
||||
sb.WriteByte('=')
|
||||
sb.WriteString(EscapeValue(data.V))
|
||||
}
|
||||
sb.WriteByte(']')
|
||||
}
|
||||
|
||||
// MarshalJSON see encoding/json.Marshaler
|
||||
func (e *Element) MarshalJSON() ([]byte, error) {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
buf := (*bytes.Buffer)(w)
|
||||
// fmt.Fprintf(buf, `{"type":"%s","data":{`, e.Type)
|
||||
buf.WriteString(`{"type":"`)
|
||||
buf.WriteString(e.Type)
|
||||
buf.WriteString(`","data":{`)
|
||||
for i, data := range e.Data {
|
||||
if i != 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
// fmt.Fprintf(buf, `"%s":%q`, data.K, data.V)
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(data.K)
|
||||
buf.WriteString(`":`)
|
||||
buf.WriteString(strconv.Quote(data.V))
|
||||
}
|
||||
buf.WriteString(`}}`)
|
||||
}), nil
|
||||
}
|
@ -3,15 +3,11 @@ package cqcode
|
||||
|
||||
import "strings"
|
||||
|
||||
/*EscapeText 将字符串raw中部分字符转义
|
||||
|
||||
& -> &
|
||||
|
||||
[ -> [
|
||||
|
||||
] -> ]
|
||||
|
||||
*/
|
||||
// EscapeText 将字符串raw中部分字符转义
|
||||
//
|
||||
// - & -> &
|
||||
// - [ -> [
|
||||
// - ] -> ]
|
||||
func EscapeText(s string) string {
|
||||
count := strings.Count(s, "&")
|
||||
count += strings.Count(s, "[")
|
||||
@ -47,31 +43,22 @@ func EscapeText(s string) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
/*EscapeValue 将字符串value中部分字符转义
|
||||
|
||||
, -> ,
|
||||
|
||||
& -> &
|
||||
|
||||
[ -> [
|
||||
|
||||
] -> ]
|
||||
|
||||
*/
|
||||
// EscapeValue 将字符串value中部分字符转义
|
||||
//
|
||||
// - , -> ,
|
||||
// - & -> &
|
||||
// - [ -> [
|
||||
// - ] -> ]
|
||||
func EscapeValue(value string) string {
|
||||
ret := EscapeText(value)
|
||||
return strings.ReplaceAll(ret, ",", ",")
|
||||
}
|
||||
|
||||
/*UnescapeText 将字符串content中部分字符反转义
|
||||
|
||||
& -> &
|
||||
|
||||
[ -> [
|
||||
|
||||
] -> ]
|
||||
|
||||
*/
|
||||
// UnescapeText 将字符串content中部分字符反转义
|
||||
//
|
||||
// - & -> &
|
||||
// - [ -> [
|
||||
// - ] -> ]
|
||||
func UnescapeText(content string) string {
|
||||
ret := content
|
||||
ret = strings.ReplaceAll(ret, "[", "[")
|
||||
@ -80,17 +67,12 @@ func UnescapeText(content string) string {
|
||||
return ret
|
||||
}
|
||||
|
||||
/*UnescapeValue 将字符串content中部分字符反转义
|
||||
|
||||
, -> ,
|
||||
|
||||
& -> &
|
||||
|
||||
[ -> [
|
||||
|
||||
] -> ]
|
||||
|
||||
*/
|
||||
// UnescapeValue 将字符串content中部分字符反转义
|
||||
//
|
||||
// - , -> ,
|
||||
// - & -> &
|
||||
// - [ -> [
|
||||
// - ] -> ]
|
||||
func UnescapeValue(content string) string {
|
||||
ret := strings.ReplaceAll(content, ",", ",")
|
||||
return UnescapeText(ret)
|
||||
|
439
coolq/event.go
439
coolq/event.go
@ -2,12 +2,12 @@ package coolq
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client"
|
||||
@ -21,41 +21,73 @@ import (
|
||||
)
|
||||
|
||||
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式
|
||||
func ToFormattedMessage(e []message.IMessageElement, source message.Source, isRaw ...bool) (r interface{}) {
|
||||
func ToFormattedMessage(e []message.IMessageElement, source message.Source) (r interface{}) {
|
||||
if base.PostFormat == "string" {
|
||||
r = ToStringMessage(e, source, isRaw...)
|
||||
r = toStringMessage(e, source)
|
||||
} else if base.PostFormat == "array" {
|
||||
r = ToArrayMessage(e, source)
|
||||
r = toElements(e, source)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
|
||||
type event struct {
|
||||
PostType string
|
||||
DetailType string
|
||||
SubType string
|
||||
Time int64
|
||||
SelfID int64
|
||||
Others global.MSG
|
||||
}
|
||||
|
||||
func (ev *event) MarshalJSON() ([]byte, error) {
|
||||
buf := global.NewBuffer()
|
||||
defer global.PutBuffer(buf)
|
||||
|
||||
detail := ""
|
||||
switch ev.PostType {
|
||||
case "message", "message_sent":
|
||||
detail = "message_type"
|
||||
case "notice":
|
||||
detail = "notice_type"
|
||||
case "request":
|
||||
detail = "request_type"
|
||||
case "meta_event":
|
||||
detail = "meta_event_type"
|
||||
default:
|
||||
panic("unknown post type: " + ev.PostType)
|
||||
}
|
||||
fmt.Fprintf(buf, `{"post_type":"%s","%s":"%s","time":%d,"self_id":%d`, ev.PostType, detail, ev.DetailType, ev.Time, ev.SelfID)
|
||||
if ev.SubType != "" {
|
||||
fmt.Fprintf(buf, `,"sub_type":"%s"`, ev.SubType)
|
||||
}
|
||||
for k, v := range ev.Others {
|
||||
v, _ := json.Marshal(v)
|
||||
fmt.Fprintf(buf, `,"%s":%s`, k, v)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return append([]byte(nil), buf.Bytes()...), nil
|
||||
}
|
||||
|
||||
func (bot *CQBot) privateMessageEvent(_ *client.QQClient, m *message.PrivateMessage) {
|
||||
bot.checkMedia(m.Elements, m.Sender.Uin)
|
||||
source := message.Source{
|
||||
SourceType: message.SourcePrivate,
|
||||
PrimaryID: m.Sender.Uin,
|
||||
}
|
||||
cqm := ToStringMessage(m.Elements, source, true)
|
||||
cqm := toStringMessage(m.Elements, source)
|
||||
id := bot.InsertPrivateMessage(m)
|
||||
log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
|
||||
typ := "message/private/friend"
|
||||
if m.Sender.Uin == bot.Client.Uin {
|
||||
typ = "message_sent/private/friend"
|
||||
}
|
||||
fm := global.MSG{
|
||||
"post_type": func() string {
|
||||
if m.Sender.Uin == bot.Client.Uin {
|
||||
return "message_sent"
|
||||
}
|
||||
return "message"
|
||||
}(),
|
||||
"message_type": "private",
|
||||
"sub_type": "friend",
|
||||
"message_id": id,
|
||||
"user_id": m.Sender.Uin,
|
||||
"target_id": m.Target,
|
||||
"message": ToFormattedMessage(m.Elements, source, false),
|
||||
"raw_message": cqm,
|
||||
"font": 0,
|
||||
"self_id": c.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
"message_id": id,
|
||||
"user_id": m.Sender.Uin,
|
||||
"target_id": m.Target,
|
||||
"message": ToFormattedMessage(m.Elements, source),
|
||||
"raw_message": cqm,
|
||||
"font": 0,
|
||||
"sender": global.MSG{
|
||||
"user_id": m.Sender.Uin,
|
||||
"nickname": m.Sender.Nickname,
|
||||
@ -63,7 +95,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
|
||||
"age": 0,
|
||||
},
|
||||
}
|
||||
bot.dispatchEventMessage(fm)
|
||||
bot.dispatchEvent(typ, fm)
|
||||
}
|
||||
|
||||
func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) {
|
||||
@ -71,11 +103,9 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
|
||||
for _, elem := range m.Elements {
|
||||
if file, ok := elem.(*message.GroupFileElement); ok {
|
||||
log.Infof("群 %v(%v) 内 %v(%v) 上传了文件: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, file.Name)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "group_upload",
|
||||
"group_id": m.GroupCode,
|
||||
"user_id": m.Sender.Uin,
|
||||
bot.dispatchEvent("notice/group_upload", global.MSG{
|
||||
"group_id": m.GroupCode,
|
||||
"user_id": m.Sender.Uin,
|
||||
"file": global.MSG{
|
||||
"id": file.Path,
|
||||
"name": file.Name,
|
||||
@ -83,8 +113,6 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
|
||||
"busid": file.Busid,
|
||||
"url": c.GetGroupFileUrl(m.GroupCode, file.Path, file.Busid),
|
||||
},
|
||||
"self_id": c.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
})
|
||||
return
|
||||
}
|
||||
@ -93,15 +121,15 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
|
||||
SourceType: message.SourceGroup,
|
||||
PrimaryID: m.GroupCode,
|
||||
}
|
||||
cqm := ToStringMessage(m.Elements, source, true)
|
||||
cqm := toStringMessage(m.Elements, source)
|
||||
id := bot.InsertGroupMessage(m)
|
||||
log.Infof("收到群 %v(%v) 内 %v(%v) 的消息: %v (%v)", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
|
||||
gm := bot.formatGroupMessage(m)
|
||||
if gm == nil {
|
||||
return
|
||||
}
|
||||
gm["message_id"] = id
|
||||
bot.dispatchEventMessage(gm)
|
||||
gm.Others["message_id"] = id
|
||||
bot.dispatch(gm)
|
||||
}
|
||||
|
||||
func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
|
||||
@ -111,7 +139,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
||||
SourceType: message.SourcePrivate,
|
||||
PrimaryID: e.Session.Sender,
|
||||
}
|
||||
cqm := ToStringMessage(m.Elements, source, true)
|
||||
cqm := toStringMessage(m.Elements, source)
|
||||
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
|
||||
id := m.Id
|
||||
// todo(Mrs4s)
|
||||
@ -120,17 +148,12 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
||||
// }
|
||||
log.Infof("收到来自群 %v(%v) 内 %v(%v) 的临时会话消息: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm)
|
||||
tm := global.MSG{
|
||||
"post_type": "message",
|
||||
"message_type": "private",
|
||||
"sub_type": "group",
|
||||
"temp_source": e.Session.Source,
|
||||
"message_id": id,
|
||||
"user_id": m.Sender.Uin,
|
||||
"message": ToFormattedMessage(m.Elements, source, false),
|
||||
"raw_message": cqm,
|
||||
"font": 0,
|
||||
"self_id": c.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
"temp_source": e.Session.Source,
|
||||
"message_id": id,
|
||||
"user_id": m.Sender.Uin,
|
||||
"message": ToFormattedMessage(m.Elements, source),
|
||||
"raw_message": cqm,
|
||||
"font": 0,
|
||||
"sender": global.MSG{
|
||||
"user_id": m.Sender.Uin,
|
||||
"group_id": m.GroupCode,
|
||||
@ -139,7 +162,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
||||
"age": 0,
|
||||
},
|
||||
}
|
||||
bot.dispatchEventMessage(tm)
|
||||
bot.dispatchEvent("message/private/group", tm)
|
||||
}
|
||||
|
||||
func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildChannelMessage) {
|
||||
@ -154,26 +177,23 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC
|
||||
PrimaryID: int64(m.GuildId),
|
||||
SecondaryID: int64(m.ChannelId),
|
||||
}
|
||||
log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, source, true))
|
||||
log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, toStringMessage(m.Elements, source))
|
||||
id := bot.InsertGuildChannelMessage(m)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "message",
|
||||
"message_type": "guild",
|
||||
"sub_type": "channel",
|
||||
ev := bot.event("message/guild/channel", global.MSG{
|
||||
"guild_id": fU64(m.GuildId),
|
||||
"channel_id": fU64(m.ChannelId),
|
||||
"message_id": id,
|
||||
"user_id": fU64(m.Sender.TinyId),
|
||||
"message": ToFormattedMessage(m.Elements, source, false), // todo: 增加对频道消息 Reply 的支持
|
||||
"self_id": bot.Client.Uin,
|
||||
"message": ToFormattedMessage(m.Elements, source), // todo: 增加对频道消息 Reply 的支持
|
||||
"self_tiny_id": fU64(bot.Client.GuildService.TinyId),
|
||||
"time": m.Time,
|
||||
"sender": global.MSG{
|
||||
"user_id": m.Sender.TinyId,
|
||||
"tiny_id": fU64(m.Sender.TinyId),
|
||||
"nickname": m.Sender.Nickname,
|
||||
},
|
||||
})
|
||||
ev.Time = m.Time
|
||||
bot.dispatch(ev)
|
||||
}
|
||||
|
||||
func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *client.GuildMessageReactionsUpdatedEvent) {
|
||||
@ -199,16 +219,12 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien
|
||||
str += "无任何表情"
|
||||
}
|
||||
log.Infof(str)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "message_reactions_updated",
|
||||
bot.dispatchEvent("notice/message_reactions_updated", global.MSG{
|
||||
"guild_id": fU64(e.GuildId),
|
||||
"channel_id": fU64(e.ChannelId),
|
||||
"message_id": msgID,
|
||||
"operator_id": fU64(e.OperatorId),
|
||||
"current_reactions": currentReactions,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": bot.Client.Uin,
|
||||
"self_tiny_id": fU64(bot.Client.GuildService.TinyId),
|
||||
"user_id": e.OperatorId,
|
||||
})
|
||||
@ -230,15 +246,11 @@ func (bot *CQBot) guildChannelMessageRecalledEvent(c *client.QQClient, e *client
|
||||
}
|
||||
msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, message.SourceGuildChannel)
|
||||
log.Infof("用户 %v(%v) 撤回了频道 %v(%v) 子频道 %v(%v) 的消息 %v", operator.Nickname, operator.TinyId, guild.GuildName, guild.GuildId, channel.ChannelName, channel.ChannelId, msgID)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "guild_channel_recall",
|
||||
bot.dispatchEvent("notice/guild_channel_recall", global.MSG{
|
||||
"guild_id": fU64(e.GuildId),
|
||||
"channel_id": fU64(e.ChannelId),
|
||||
"operator_id": fU64(e.OperatorId),
|
||||
"message_id": msgID,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": bot.Client.Uin,
|
||||
"self_tiny_id": fU64(bot.Client.GuildService.TinyId),
|
||||
"user_id": e.OperatorId,
|
||||
})
|
||||
@ -250,14 +262,10 @@ func (bot *CQBot) guildChannelUpdatedEvent(c *client.QQClient, e *client.GuildCh
|
||||
return
|
||||
}
|
||||
log.Infof("频道 %v(%v) 子频道 %v(%v) 信息已更新", guild.GuildName, guild.GuildId, e.NewChannelInfo.ChannelName, e.NewChannelInfo.ChannelId)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "channel_updated",
|
||||
bot.dispatchEvent("notice/channel_updated", global.MSG{
|
||||
"guild_id": fU64(e.GuildId),
|
||||
"channel_id": fU64(e.ChannelId),
|
||||
"operator_id": fU64(e.OperatorId),
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": bot.Client.Uin,
|
||||
"self_tiny_id": fU64(bot.Client.GuildService.TinyId),
|
||||
"user_id": e.OperatorId,
|
||||
"old_info": convertChannelInfo(e.OldChannelInfo),
|
||||
@ -275,16 +283,12 @@ func (bot *CQBot) guildChannelCreatedEvent(c *client.QQClient, e *client.GuildCh
|
||||
member = &client.GuildUserProfile{Nickname: "未知"}
|
||||
}
|
||||
log.Infof("频道 %v(%v) 内用户 %v(%v) 创建了子频道 %v(%v)", guild.GuildName, guild.GuildId, member.Nickname, member.TinyId, e.ChannelInfo.ChannelName, e.ChannelInfo.ChannelId)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "channel_created",
|
||||
bot.dispatchEvent("notice/channel_created", global.MSG{
|
||||
"guild_id": fU64(e.GuildId),
|
||||
"channel_id": fU64(e.ChannelInfo.ChannelId),
|
||||
"operator_id": fU64(e.OperatorId),
|
||||
"self_id": bot.Client.Uin,
|
||||
"self_tiny_id": fU64(bot.Client.GuildService.TinyId),
|
||||
"user_id": e.OperatorId,
|
||||
"time": time.Now().Unix(),
|
||||
"channel_info": convertChannelInfo(e.ChannelInfo),
|
||||
})
|
||||
}
|
||||
@ -299,16 +303,12 @@ func (bot *CQBot) guildChannelDestroyedEvent(c *client.QQClient, e *client.Guild
|
||||
member = &client.GuildUserProfile{Nickname: "未知"}
|
||||
}
|
||||
log.Infof("频道 %v(%v) 内用户 %v(%v) 删除了子频道 %v(%v)", guild.GuildName, guild.GuildId, member.Nickname, member.TinyId, e.ChannelInfo.ChannelName, e.ChannelInfo.ChannelId)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "channel_destroyed",
|
||||
bot.dispatchEvent("notice/channel_destroyed", global.MSG{
|
||||
"guild_id": fU64(e.GuildId),
|
||||
"channel_id": fU64(e.ChannelInfo.ChannelId),
|
||||
"operator_id": fU64(e.OperatorId),
|
||||
"self_id": bot.Client.Uin,
|
||||
"self_tiny_id": fU64(bot.Client.GuildService.TinyId),
|
||||
"user_id": e.OperatorId,
|
||||
"time": time.Now().Unix(),
|
||||
"channel_info": convertChannelInfo(e.ChannelInfo),
|
||||
})
|
||||
}
|
||||
@ -332,22 +332,15 @@ func (bot *CQBot) groupMutedEvent(c *client.QQClient, e *client.GroupMuteEvent)
|
||||
formatGroupName(g), formatMemberName(g.FindMember(e.TargetUin)), formatMemberName(g.FindMember(e.OperatorUin)))
|
||||
}
|
||||
}
|
||||
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
typ := "notice/group_ban/ban"
|
||||
if e.Time == 0 {
|
||||
typ = "notice/group_ban/lift_ban"
|
||||
}
|
||||
bot.dispatchEvent(typ, global.MSG{
|
||||
"duration": e.Time,
|
||||
"group_id": e.GroupCode,
|
||||
"notice_type": "group_ban",
|
||||
"operator_id": e.OperatorUin,
|
||||
"self_id": c.Uin,
|
||||
"user_id": e.TargetUin,
|
||||
"time": time.Now().Unix(),
|
||||
"sub_type": func() string {
|
||||
if e.Time == 0 {
|
||||
return "lift_ban"
|
||||
}
|
||||
return "ban"
|
||||
}(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -356,16 +349,15 @@ func (bot *CQBot) groupRecallEvent(c *client.QQClient, e *client.GroupMessageRec
|
||||
gid := db.ToGlobalID(e.GroupCode, e.MessageId)
|
||||
log.Infof("群 %v 内 %v 撤回了 %v 的消息: %v.",
|
||||
formatGroupName(g), formatMemberName(g.FindMember(e.OperatorUin)), formatMemberName(g.FindMember(e.AuthorUin)), gid)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
|
||||
ev := bot.event("notice/group_recall", global.MSG{
|
||||
"group_id": e.GroupCode,
|
||||
"notice_type": "group_recall",
|
||||
"self_id": c.Uin,
|
||||
"user_id": e.AuthorUin,
|
||||
"operator_id": e.OperatorUin,
|
||||
"time": e.Time,
|
||||
"message_id": gid,
|
||||
})
|
||||
ev.Time = int64(e.Time)
|
||||
bot.dispatch(ev)
|
||||
}
|
||||
|
||||
func (bot *CQBot) groupNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
||||
@ -375,42 +367,27 @@ func (bot *CQBot) groupNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
||||
sender := group.FindMember(notify.Sender)
|
||||
receiver := group.FindMember(notify.Receiver)
|
||||
log.Infof("群 %v 内 %v 戳了戳 %v", formatGroupName(group), formatMemberName(sender), formatMemberName(receiver))
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"group_id": group.Code,
|
||||
"notice_type": "notify",
|
||||
"sub_type": "poke",
|
||||
"self_id": c.Uin,
|
||||
"user_id": notify.Sender,
|
||||
"sender_id": notify.Sender,
|
||||
"target_id": notify.Receiver,
|
||||
"time": time.Now().Unix(),
|
||||
bot.dispatchEvent("notice/notify/poke", global.MSG{
|
||||
"group_id": group.Code,
|
||||
"user_id": notify.Sender,
|
||||
"sender_id": notify.Sender,
|
||||
"target_id": notify.Receiver,
|
||||
})
|
||||
case *client.GroupRedBagLuckyKingNotifyEvent:
|
||||
sender := group.FindMember(notify.Sender)
|
||||
luckyKing := group.FindMember(notify.LuckyKing)
|
||||
log.Infof("群 %v 内 %v 的红包被抢完, %v 是运气王", formatGroupName(group), formatMemberName(sender), formatMemberName(luckyKing))
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"group_id": group.Code,
|
||||
"notice_type": "notify",
|
||||
"sub_type": "lucky_king",
|
||||
"self_id": c.Uin,
|
||||
"user_id": notify.Sender,
|
||||
"sender_id": notify.Sender,
|
||||
"target_id": notify.LuckyKing,
|
||||
"time": time.Now().Unix(),
|
||||
bot.dispatchEvent("notice/notify/lucky_king", global.MSG{
|
||||
"group_id": group.Code,
|
||||
"user_id": notify.Sender,
|
||||
"sender_id": notify.Sender,
|
||||
"target_id": notify.LuckyKing,
|
||||
})
|
||||
case *client.MemberHonorChangedNotifyEvent:
|
||||
log.Info(notify.Content())
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"group_id": group.Code,
|
||||
"notice_type": "notify",
|
||||
"sub_type": "honor",
|
||||
"self_id": c.Uin,
|
||||
"user_id": notify.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
bot.dispatchEvent("notice/notify/honor", global.MSG{
|
||||
"group_id": group.Code,
|
||||
"user_id": notify.Uin,
|
||||
"honor_type": func() string {
|
||||
switch notify.Honor {
|
||||
case client.Talkative:
|
||||
@ -439,15 +416,10 @@ func (bot *CQBot) friendNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
||||
} else {
|
||||
log.Infof("好友 %v 戳了戳你.", friend.Nickname)
|
||||
}
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "notify",
|
||||
"sub_type": "poke",
|
||||
"self_id": c.Uin,
|
||||
"user_id": notify.Sender,
|
||||
"sender_id": notify.Sender,
|
||||
"target_id": notify.Receiver,
|
||||
"time": time.Now().Unix(),
|
||||
bot.dispatchEvent("notice/notify/poke", global.MSG{
|
||||
"user_id": notify.Sender,
|
||||
"sender_id": notify.Sender,
|
||||
"target_id": notify.Receiver,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -456,15 +428,10 @@ func (bot *CQBot) memberTitleUpdatedEvent(c *client.QQClient, e *client.MemberSp
|
||||
group := c.FindGroup(e.GroupCode)
|
||||
mem := group.FindMember(e.Uin)
|
||||
log.Infof("群 %v(%v) 内成员 %v(%v) 获得了新的头衔: %v", group.Name, group.Code, mem.DisplayName(), mem.Uin, e.NewTitle)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "notify",
|
||||
"sub_type": "title",
|
||||
"group_id": group.Code,
|
||||
"self_id": c.Uin,
|
||||
"user_id": e.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
"title": e.NewTitle,
|
||||
bot.dispatchEvent("notice/notify/title", global.MSG{
|
||||
"group_id": group.Code,
|
||||
"user_id": e.Uin,
|
||||
"title": e.NewTitle,
|
||||
})
|
||||
}
|
||||
|
||||
@ -476,14 +443,12 @@ func (bot *CQBot) friendRecallEvent(c *client.QQClient, e *client.FriendMessageR
|
||||
} else {
|
||||
log.Infof("好友 %v 撤回了消息: %v", e.FriendUin, gid)
|
||||
}
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "friend_recall",
|
||||
"self_id": c.Uin,
|
||||
"user_id": e.FriendUin,
|
||||
"time": e.Time,
|
||||
"message_id": gid,
|
||||
ev := bot.event("notice/friend_recall", global.MSG{
|
||||
"user_id": e.FriendUin,
|
||||
"message_id": gid,
|
||||
})
|
||||
ev.Time = e.Time
|
||||
bot.dispatch(ev)
|
||||
}
|
||||
|
||||
func (bot *CQBot) offlineFileEvent(c *client.QQClient, e *client.OfflineFileEvent) {
|
||||
@ -492,23 +457,19 @@ func (bot *CQBot) offlineFileEvent(c *client.QQClient, e *client.OfflineFileEven
|
||||
return
|
||||
}
|
||||
log.Infof("好友 %v(%v) 发送了离线文件 %v", f.Nickname, f.Uin, e.FileName)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "offline_file",
|
||||
"user_id": e.Sender,
|
||||
bot.dispatchEvent("notice/offline_file", global.MSG{
|
||||
"user_id": e.Sender,
|
||||
"file": global.MSG{
|
||||
"name": e.FileName,
|
||||
"size": e.FileSize,
|
||||
"url": e.DownloadUrl,
|
||||
},
|
||||
"self_id": c.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) joinGroupEvent(c *client.QQClient, group *client.GroupInfo) {
|
||||
log.Infof("Bot进入了群 %v.", formatGroupName(group))
|
||||
bot.dispatchEventMessage(bot.groupIncrease(group.Code, 0, c.Uin))
|
||||
bot.dispatch(bot.groupIncrease(group.Code, 0, c.Uin))
|
||||
}
|
||||
|
||||
func (bot *CQBot) leaveGroupEvent(c *client.QQClient, e *client.GroupLeaveEvent) {
|
||||
@ -517,44 +478,33 @@ func (bot *CQBot) leaveGroupEvent(c *client.QQClient, e *client.GroupLeaveEvent)
|
||||
} else {
|
||||
log.Infof("Bot退出了群 %v.", formatGroupName(e.Group))
|
||||
}
|
||||
bot.dispatchEventMessage(bot.groupDecrease(e.Group.Code, c.Uin, e.Operator))
|
||||
bot.dispatch(bot.groupDecrease(e.Group.Code, c.Uin, e.Operator))
|
||||
}
|
||||
|
||||
func (bot *CQBot) memberPermissionChangedEvent(c *client.QQClient, e *client.MemberPermissionChangedEvent) {
|
||||
st := func() string {
|
||||
if e.NewPermission == client.Administrator {
|
||||
return "set"
|
||||
}
|
||||
return "unset"
|
||||
}()
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "group_admin",
|
||||
"sub_type": st,
|
||||
"group_id": e.Group.Code,
|
||||
"user_id": e.Member.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": c.Uin,
|
||||
st := "unset"
|
||||
if e.NewPermission == client.Administrator {
|
||||
st = "set"
|
||||
}
|
||||
bot.dispatchEvent("notice/group_admin/"+st, global.MSG{
|
||||
"group_id": e.Group.Code,
|
||||
"user_id": e.Member.Uin,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) memberCardUpdatedEvent(c *client.QQClient, e *client.MemberCardUpdatedEvent) {
|
||||
log.Infof("群 %v 的 %v 更新了名片 %v -> %v", formatGroupName(e.Group), formatMemberName(e.Member), e.OldCard, e.Member.CardName)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "group_card",
|
||||
"group_id": e.Group.Code,
|
||||
"user_id": e.Member.Uin,
|
||||
"card_new": e.Member.CardName,
|
||||
"card_old": e.OldCard,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": c.Uin,
|
||||
bot.dispatchEvent("notice/group_card", global.MSG{
|
||||
"group_id": e.Group.Code,
|
||||
"user_id": e.Member.Uin,
|
||||
"card_new": e.Member.CardName,
|
||||
"card_old": e.OldCard,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) memberJoinEvent(_ *client.QQClient, e *client.MemberJoinGroupEvent) {
|
||||
log.Infof("新成员 %v 进入了群 %v.", formatMemberName(e.Member), formatGroupName(e.Group))
|
||||
bot.dispatchEventMessage(bot.groupIncrease(e.Group.Code, 0, e.Member.Uin))
|
||||
bot.dispatch(bot.groupIncrease(e.Group.Code, 0, e.Member.Uin))
|
||||
}
|
||||
|
||||
func (bot *CQBot) memberLeaveEvent(_ *client.QQClient, e *client.MemberLeaveGroupEvent) {
|
||||
@ -563,65 +513,47 @@ func (bot *CQBot) memberLeaveEvent(_ *client.QQClient, e *client.MemberLeaveGrou
|
||||
} else {
|
||||
log.Infof("成员 %v 离开了群 %v.", formatMemberName(e.Member), formatGroupName(e.Group))
|
||||
}
|
||||
bot.dispatchEventMessage(bot.groupDecrease(e.Group.Code, e.Member.Uin, e.Operator))
|
||||
bot.dispatch(bot.groupDecrease(e.Group.Code, e.Member.Uin, e.Operator))
|
||||
}
|
||||
|
||||
func (bot *CQBot) friendRequestEvent(c *client.QQClient, e *client.NewFriendRequest) {
|
||||
log.Infof("收到来自 %v(%v) 的好友请求: %v", e.RequesterNick, e.RequesterUin, e.Message)
|
||||
flag := strconv.FormatInt(e.RequestId, 10)
|
||||
bot.friendReqCache.Store(flag, e)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "request",
|
||||
"request_type": "friend",
|
||||
"user_id": e.RequesterUin,
|
||||
"comment": e.Message,
|
||||
"flag": flag,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": c.Uin,
|
||||
bot.dispatchEvent("request/friend", global.MSG{
|
||||
"user_id": e.RequesterUin,
|
||||
"comment": e.Message,
|
||||
"flag": flag,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) friendAddedEvent(c *client.QQClient, e *client.NewFriendEvent) {
|
||||
log.Infof("添加了新好友: %v(%v)", e.Friend.Nickname, e.Friend.Uin)
|
||||
bot.tempSessionCache.Delete(e.Friend.Uin)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "friend_add",
|
||||
"self_id": c.Uin,
|
||||
"user_id": e.Friend.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
bot.dispatchEvent("notice/friend_add", global.MSG{
|
||||
"user_id": e.Friend.Uin,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) groupInvitedEvent(c *client.QQClient, e *client.GroupInvitedRequest) {
|
||||
log.Infof("收到来自群 %v(%v) 内用户 %v(%v) 的加群邀请.", e.GroupName, e.GroupCode, e.InvitorNick, e.InvitorUin)
|
||||
flag := strconv.FormatInt(e.RequestId, 10)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "request",
|
||||
"request_type": "group",
|
||||
"sub_type": "invite",
|
||||
"group_id": e.GroupCode,
|
||||
"user_id": e.InvitorUin,
|
||||
"comment": "",
|
||||
"flag": flag,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": c.Uin,
|
||||
bot.dispatchEvent("request/group/invite", global.MSG{
|
||||
"group_id": e.GroupCode,
|
||||
"user_id": e.InvitorUin,
|
||||
"comment": "",
|
||||
"flag": flag,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) groupJoinReqEvent(c *client.QQClient, e *client.UserJoinGroupRequest) {
|
||||
log.Infof("群 %v(%v) 收到来自用户 %v(%v) 的加群请求.", e.GroupName, e.GroupCode, e.RequesterNick, e.RequesterUin)
|
||||
flag := strconv.FormatInt(e.RequestId, 10)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "request",
|
||||
"request_type": "group",
|
||||
"sub_type": "add",
|
||||
"group_id": e.GroupCode,
|
||||
"user_id": e.RequesterUin,
|
||||
"comment": e.Message,
|
||||
"flag": flag,
|
||||
"time": time.Now().Unix(),
|
||||
"self_id": c.Uin,
|
||||
bot.dispatchEvent("request/group/add", global.MSG{
|
||||
"group_id": e.GroupCode,
|
||||
"user_id": e.RequesterUin,
|
||||
"comment": e.Message,
|
||||
"flag": flag,
|
||||
})
|
||||
}
|
||||
|
||||
@ -631,17 +563,13 @@ func (bot *CQBot) otherClientStatusChangedEvent(c *client.QQClient, e *client.Ot
|
||||
} else {
|
||||
log.Infof("Bot 账号在客户端 %v (%v) 登出.", e.Client.DeviceName, e.Client.DeviceKind)
|
||||
}
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "client_status",
|
||||
"online": e.Online,
|
||||
bot.dispatchEvent("notice/client_status", global.MSG{
|
||||
"online": e.Online,
|
||||
"client": global.MSG{
|
||||
"app_id": e.Client.AppId,
|
||||
"device_name": e.Client.DeviceName,
|
||||
"device_kind": e.Client.DeviceKind,
|
||||
},
|
||||
"self_id": c.Uin,
|
||||
"time": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -668,61 +596,44 @@ func (bot *CQBot) groupEssenceMsg(c *client.QQClient, e *client.GroupDigestEvent
|
||||
if e.OperatorUin == bot.Client.Uin {
|
||||
return
|
||||
}
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
"post_type": "notice",
|
||||
subtype := "delete"
|
||||
if e.OperationType == 1 {
|
||||
subtype = "add"
|
||||
}
|
||||
bot.dispatchEvent("notice/essence/"+subtype, global.MSG{
|
||||
"group_id": e.GroupCode,
|
||||
"notice_type": "essence",
|
||||
"sub_type": func() string {
|
||||
if e.OperationType == 1 {
|
||||
return "add"
|
||||
}
|
||||
return "delete"
|
||||
}(),
|
||||
"self_id": c.Uin,
|
||||
"sender_id": e.SenderUin,
|
||||
"operator_id": e.OperatorUin,
|
||||
"time": time.Now().Unix(),
|
||||
"message_id": gid,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) groupIncrease(groupCode, operatorUin, userUin int64) global.MSG {
|
||||
return global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "group_increase",
|
||||
func (bot *CQBot) groupIncrease(groupCode, operatorUin, userUin int64) *event {
|
||||
return bot.event("notice/group_increase/approve", global.MSG{
|
||||
"group_id": groupCode,
|
||||
"operator_id": operatorUin,
|
||||
"self_id": bot.Client.Uin,
|
||||
"sub_type": "approve",
|
||||
"time": time.Now().Unix(),
|
||||
"user_id": userUin,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.GroupMemberInfo) global.MSG {
|
||||
return global.MSG{
|
||||
"post_type": "notice",
|
||||
"notice_type": "group_decrease",
|
||||
"group_id": groupCode,
|
||||
"operator_id": func() int64 {
|
||||
if operator != nil {
|
||||
return operator.Uin
|
||||
}
|
||||
return userUin
|
||||
}(),
|
||||
"self_id": bot.Client.Uin,
|
||||
"sub_type": func() string {
|
||||
if operator != nil {
|
||||
if userUin == bot.Client.Uin {
|
||||
return "kick_me"
|
||||
}
|
||||
return "kick"
|
||||
}
|
||||
return "leave"
|
||||
}(),
|
||||
"time": time.Now().Unix(),
|
||||
"user_id": userUin,
|
||||
func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.GroupMemberInfo) *event {
|
||||
op := userUin
|
||||
if operator != nil {
|
||||
op = operator.Uin
|
||||
}
|
||||
subtype := "leave"
|
||||
if operator != nil {
|
||||
if userUin == bot.Client.Uin {
|
||||
subtype = "kick_me"
|
||||
} else {
|
||||
subtype = "kick"
|
||||
}
|
||||
}
|
||||
return bot.event("notice/group_decrease/"+subtype, global.MSG{
|
||||
"group_id": groupCode,
|
||||
"operator_id": op,
|
||||
"user_id": userUin,
|
||||
})
|
||||
}
|
||||
|
||||
func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) {
|
||||
|
@ -1,129 +0,0 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
)
|
||||
|
||||
var output bytes.Buffer
|
||||
|
||||
func fprintf(format string, args ...interface{}) {
|
||||
_, _ = fmt.Fprintf(&output, format, args...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
f, _ := parser.ParseFile(token.NewFileSet(), "./../database.go", nil, 0)
|
||||
fprintf("// Code generated by mkrw.go; DO NOT EDIT.\n\n")
|
||||
fprintf("package leveldb\n\n")
|
||||
fprintf("import \"github.com/Mrs4s/go-cqhttp/db\"\n\n")
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
switch node := node.(type) {
|
||||
case *ast.FuncDecl:
|
||||
return false
|
||||
case *ast.TypeSpec:
|
||||
if !node.Name.IsExported() {
|
||||
return false
|
||||
}
|
||||
x, ok := node.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if x.Fields != nil && x.Fields.List != nil {
|
||||
mkWrite(node)
|
||||
mkRead(node)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
out, err := format.Source(output.Bytes())
|
||||
if err != nil {
|
||||
fmt.Println(string(output.Bytes()))
|
||||
panic(err)
|
||||
}
|
||||
os.WriteFile("database_gen.go", out, 0o644)
|
||||
}
|
||||
|
||||
func typeName(typ ast.Expr) string {
|
||||
switch typ := typ.(type) {
|
||||
case *ast.Ident:
|
||||
return typ.Name
|
||||
case *ast.ArrayType:
|
||||
if typ.Len != nil {
|
||||
panic("unexpected array type")
|
||||
}
|
||||
return "[]" + typeName(typ.Elt)
|
||||
case *ast.SelectorExpr:
|
||||
return typeName(typ.X) + "." + typ.Sel.Name
|
||||
}
|
||||
panic("unexpected type")
|
||||
}
|
||||
|
||||
func mkWrite(node *ast.TypeSpec) {
|
||||
typename := node.Name.String()
|
||||
structType := node.Type.(*ast.StructType)
|
||||
fprintf("func (w *writer) write%s(x *db.%s) {\n", typename, typename)
|
||||
fprintf("if x == nil {\n")
|
||||
fprintf("w.nil()\n")
|
||||
fprintf("return\n")
|
||||
fprintf("}\n")
|
||||
fprintf("w.coder(coderStruct)\n")
|
||||
for _, field := range structType.Fields.List {
|
||||
switch typ := field.Type.(type) {
|
||||
case *ast.Ident:
|
||||
for _, name := range field.Names {
|
||||
fprintf("w.%s(x.%s)\n", typ.Name, name.Name)
|
||||
}
|
||||
case *ast.ArrayType:
|
||||
if typeName(typ) != "[]global.MSG" {
|
||||
panic("unexpected array type")
|
||||
}
|
||||
for _, name := range field.Names {
|
||||
fprintf("w.arrayMsg(x.%s)\n", name.Name)
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
for _, name := range field.Names {
|
||||
fprintf("w.write%s(x.%s)\n", typeName(typ.X), name.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf("}\n\n")
|
||||
}
|
||||
|
||||
func mkRead(node *ast.TypeSpec) {
|
||||
typename := node.Name.String()
|
||||
structType := node.Type.(*ast.StructType)
|
||||
fprintf(`func (r *reader) read%s() *db.%s {
|
||||
coder := r.coder()
|
||||
if coder == coderNil {
|
||||
return nil
|
||||
}`+"\n", typename, typename)
|
||||
fprintf("x := &db.%s{}\n", typename)
|
||||
for _, field := range structType.Fields.List {
|
||||
switch typ := field.Type.(type) {
|
||||
case *ast.Ident:
|
||||
for _, name := range field.Names {
|
||||
fprintf("x.%s = r.%s()\n", name.Name, typ.Name)
|
||||
}
|
||||
case *ast.ArrayType:
|
||||
if typeName(typ) != "[]global.MSG" {
|
||||
panic("unexpected array type")
|
||||
}
|
||||
for _, name := range field.Names {
|
||||
fprintf("x.%s = r.arrayMsg()\n", name.Name)
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
for _, name := range field.Names {
|
||||
fprintf("x.%s = r.read%s()\n", name.Name, typeName(typ.X))
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf("return x\n")
|
||||
fprintf("}\n\n")
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
// Code generated by mkrw.go; DO NOT EDIT.
|
||||
|
||||
package leveldb
|
||||
|
||||
import "github.com/Mrs4s/go-cqhttp/db"
|
@ -2,7 +2,6 @@ package leveldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
)
|
||||
@ -24,12 +23,25 @@ func (w *intWriter) uvarint(x uint64) {
|
||||
}
|
||||
|
||||
// writer implements the index write.
|
||||
//
|
||||
// data format(use uvarint to encode integers):
|
||||
// | version | string data length | index data length | string data | index data |
|
||||
//
|
||||
// - version
|
||||
// - string data length
|
||||
// - index data length
|
||||
// - string data
|
||||
// - index data
|
||||
//
|
||||
// for string data part, each string is encoded as:
|
||||
// | string length | string |
|
||||
// for index data part, each value is encoded as:
|
||||
// | coder | value |
|
||||
//
|
||||
// - string length
|
||||
// - string
|
||||
//
|
||||
// for index data part, each object value is encoded as:
|
||||
//
|
||||
// - coder
|
||||
// - value
|
||||
//
|
||||
// * coder is the identifier of value's type.
|
||||
// * specially for string, it's value is the offset in string data part.
|
||||
type writer struct {
|
||||
@ -125,7 +137,7 @@ func (w *writer) bytes() []byte {
|
||||
out.uvarint(dataVersion)
|
||||
out.uvarint(uint64(w.strings.Len()))
|
||||
out.uvarint(uint64(w.data.Len()))
|
||||
_, _ = io.Copy(&out, &w.strings)
|
||||
_, _ = io.Copy(&out, &w.data)
|
||||
_, _ = w.strings.WriteTo(&out)
|
||||
_, _ = w.data.WriteTo(&out)
|
||||
return out.Bytes()
|
||||
}
|
||||
|
@ -2,26 +2,16 @@ package global
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
"github.com/Mrs4s/MiraiGo/binary" // 和 MiraiGo 共用同一 buffer 池
|
||||
)
|
||||
|
||||
// NewBuffer 从池中获取新 bytes.Buffer
|
||||
func NewBuffer() *bytes.Buffer {
|
||||
return bufferPool.Get().(*bytes.Buffer)
|
||||
return (*bytes.Buffer)(binary.SelectWriter())
|
||||
}
|
||||
|
||||
// PutBuffer 将 Buffer放入池中
|
||||
func PutBuffer(buf *bytes.Buffer) {
|
||||
// See https://golang.org/issue/23199
|
||||
const maxSize = 1 << 16
|
||||
if buf != nil && buf.Cap() < maxSize { // 对于大Buffer直接丢弃
|
||||
buf.Reset()
|
||||
bufferPool.Put(buf)
|
||||
}
|
||||
binary.PutWriter((*binary.Writer)(buf))
|
||||
}
|
||||
|
@ -43,6 +43,6 @@ func EncodeMP4(src string, dst string) error { // -y 覆盖文件
|
||||
|
||||
// ExtractCover 获取给定视频文件的Cover
|
||||
func ExtractCover(src string, target string) error {
|
||||
cmd := exec.Command("ffmpeg", "-i", src, "-y", "-r", "1", "-f", "image2", target)
|
||||
cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target)
|
||||
return errors.Wrap(cmd.Run(), "extract video cover failed")
|
||||
}
|
||||
|
31
global/fs.go
31
global/fs.go
@ -5,12 +5,11 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
@ -22,27 +21,18 @@ import (
|
||||
const (
|
||||
// ImagePath go-cqhttp使用的图片缓存目录
|
||||
ImagePath = "data/images"
|
||||
// ImagePathOld 兼容旧版go-cqhttp使用的图片缓存目录
|
||||
ImagePathOld = "data/image"
|
||||
// VoicePath go-cqhttp使用的语音缓存目录
|
||||
VoicePath = "data/voices"
|
||||
// VoicePathOld 兼容旧版go-cqhttp使用的语音缓存目录
|
||||
VoicePathOld = "data/record"
|
||||
// VideoPath go-cqhttp使用的视频缓存目录
|
||||
VideoPath = "data/videos"
|
||||
// CachePath go-cqhttp使用的缓存目录
|
||||
CachePath = "data/cache"
|
||||
// DumpsPath go-cqhttp使用错误转储目录
|
||||
DumpsPath = "dumps"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrSyntax Path语法错误时返回的错误
|
||||
ErrSyntax = errors.New("syntax error")
|
||||
// HeaderAmr AMR文件头
|
||||
HeaderAmr = []byte("#!AMR")
|
||||
HeaderAmr = "#!AMR"
|
||||
// HeaderSilk Silkv3文件头
|
||||
HeaderSilk = []byte("\x02#!SILK_V3")
|
||||
HeaderSilk = "\x02#!SILK_V3"
|
||||
)
|
||||
|
||||
// PathExists 判断给定path是否存在
|
||||
@ -78,13 +68,13 @@ func Check(err error, deleteSession bool) {
|
||||
|
||||
// IsAMRorSILK 判断给定文件是否为Amr或Silk格式
|
||||
func IsAMRorSILK(b []byte) bool {
|
||||
return bytes.HasPrefix(b, HeaderAmr) || bytes.HasPrefix(b, HeaderSilk)
|
||||
return bytes.HasPrefix(b, []byte(HeaderAmr)) || bytes.HasPrefix(b, []byte(HeaderSilk))
|
||||
}
|
||||
|
||||
// FindFile 从给定的File寻找文件,并返回文件byte数组。File是一个合法的URL。p为文件寻找位置。
|
||||
// 对于HTTP/HTTPS形式的URL,Cache为"1"或空时表示启用缓存
|
||||
func FindFile(file, cache, p string) (data []byte, err error) {
|
||||
data, err = nil, ErrSyntax
|
||||
data, err = nil, os.ErrNotExist
|
||||
switch {
|
||||
case strings.HasPrefix(file, "http"): // https also has prefix http
|
||||
hash := md5.Sum([]byte(file))
|
||||
@ -138,19 +128,18 @@ func DelFile(path string) bool {
|
||||
}
|
||||
|
||||
// ReadAddrFile 从给定path中读取合法的IP地址与端口,每个IP地址以换行符"\n"作为分隔
|
||||
func ReadAddrFile(path string) []*net.TCPAddr {
|
||||
func ReadAddrFile(path string) []netip.AddrPort {
|
||||
d, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
str := string(d)
|
||||
lines := strings.Split(str, "\n")
|
||||
var ret []*net.TCPAddr
|
||||
var ret []netip.AddrPort
|
||||
for _, l := range lines {
|
||||
ip := strings.Split(strings.TrimSpace(l), ":")
|
||||
if len(ip) == 2 {
|
||||
port, _ := strconv.Atoi(ip[1])
|
||||
ret = append(ret, &net.TCPAddr{IP: net.ParseIP(ip[0]), Port: port})
|
||||
addr, err := netip.ParseAddrPort(l)
|
||||
if err == nil {
|
||||
ret = append(ret, addr)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
|
@ -195,7 +195,8 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf.WriteString(colorReset)
|
||||
}
|
||||
|
||||
ret := append([]byte(nil), buf.Bytes()...) // copy buffer
|
||||
ret := make([]byte, len(buf.Bytes()))
|
||||
copy(ret, buf.Bytes()) // copy buffer
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ func DownloadFile(url, path string, limit int64, headers map[string]string) erro
|
||||
if limit > 0 && resp.ContentLength > limit {
|
||||
return ErrOverSize
|
||||
}
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
_, err = file.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -107,7 +107,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int,
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = io.Copy(file, s); err != nil {
|
||||
if _, err = file.ReadFrom(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return errUnsupportedMultiThreading
|
||||
|
10
go.mod
10
go.mod
@ -4,10 +4,10 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.5.1
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a
|
||||
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc
|
||||
github.com/fumiama/go-hide-param v0.1.4
|
||||
github.com/gabriel-vasile/mimetype v1.4.0
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||
github.com/mattn/go-colorable v0.1.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
@ -16,16 +16,16 @@ require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||
github.com/tidwall/gjson v1.14.0
|
||||
github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea
|
||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
||||
go.mongodb.org/mongo-driver v1.8.3
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 // indirect
|
||||
github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fumiama/imgsz v0.0.2 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
|
24
go.sum
24
go.sum
@ -1,9 +1,11 @@
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3 h1:U3UumMt052Ii1gGrkKM1MbX1uxCzxKhlfuNQ42LtIRQ=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8=
|
||||
github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4=
|
||||
github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a h1:DVrEVvLNKb53wTTduRdC+hZ80S/5G9qFmKYmCmIMVws=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0=
|
||||
github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8=
|
||||
github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA=
|
||||
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q=
|
||||
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -16,8 +18,6 @@ github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz
|
||||
github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY=
|
||||
github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak=
|
||||
github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
|
||||
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -96,8 +96,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc=
|
||||
github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko=
|
||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4=
|
||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
|
||||
@ -120,7 +120,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI=
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -139,16 +138,13 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -180,8 +176,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o=
|
||||
modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
|
@ -188,7 +188,7 @@ func fromStream(updateWith io.Reader) (err error, errRecover error) {
|
||||
}
|
||||
// We won't log this error, because it's always going to happen.
|
||||
defer func() { _ = fp.Close() }()
|
||||
if _, err = io.Copy(fp, bufio.NewReader(updateWith)); err != nil {
|
||||
if _, err = bufio.NewReader(updateWith).WriteTo(fp); err != nil {
|
||||
logrus.Errorf("Unable to copy data: %v\n", err)
|
||||
}
|
||||
|
||||
|
@ -21,12 +21,12 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
||||
case ".ocr_image", "ocr_image":
|
||||
p0 := p.Get("image").String()
|
||||
return c.bot.CQOcrImage(p0)
|
||||
case "_get_group_notice":
|
||||
p0 := p.Get("group_id").Int()
|
||||
return c.bot.CQGetGroupMemo(p0)
|
||||
case "_get_model_show":
|
||||
p0 := p.Get("model").String()
|
||||
return c.bot.CQGetModelShow(p0)
|
||||
case "_get_vip_info":
|
||||
p0 := p.Get("user_id").Int()
|
||||
return c.bot.CQGetVipInfo(p0)
|
||||
case "_send_group_notice":
|
||||
p0 := p.Get("group_id").Int()
|
||||
p1 := p.Get("content").String()
|
||||
@ -195,6 +195,12 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
||||
case "reload_event_filter":
|
||||
p0 := p.Get("file").String()
|
||||
return c.bot.CQReloadEventFilter(p0)
|
||||
case "send_forward_msg":
|
||||
p0 := p.Get("group_id").Int()
|
||||
p1 := p.Get("user_id").Int()
|
||||
p2 := p.Get("messages")
|
||||
p3 := p.Get("message_type").String()
|
||||
return c.bot.CQSendForwardMessage(p0, p1, p2, p3)
|
||||
case "send_group_forward_msg":
|
||||
p0 := p.Get("group_id").Int()
|
||||
p1 := p.Get("messages")
|
||||
@ -204,6 +210,9 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
||||
p1 := p.Get("message")
|
||||
p2 := p.Get("auto_escape").Bool()
|
||||
return c.bot.CQSendGroupMessage(p0, p1, p2)
|
||||
case "send_group_sign":
|
||||
p0 := p.Get("group_id").Int()
|
||||
return c.bot.CQSendGroupSign(p0)
|
||||
case "send_guild_channel_msg":
|
||||
p0 := p.Get("guild_id").Uint()
|
||||
p1 := p.Get("channel_id").Uint()
|
||||
@ -217,6 +226,10 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
||||
p3 := p.Get("message_type").String()
|
||||
p4 := p.Get("auto_escape").Bool()
|
||||
return c.bot.CQSendMessage(p0, p1, p2, p3, p4)
|
||||
case "send_private_forward_msg":
|
||||
p0 := p.Get("user_id").Int()
|
||||
p1 := p.Get("messages")
|
||||
return c.bot.CQSendPrivateForwardMessage(p0, p1)
|
||||
case "send_private_msg":
|
||||
p0 := p.Get("user_id").Int()
|
||||
p1 := p.Get("group_id").Int()
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// defaultConfig 默认配置文件
|
||||
//
|
||||
//go:embed default_config.yml
|
||||
var defaultConfig string
|
||||
|
||||
@ -137,8 +138,7 @@ func expand(s string, mapping func(string) string) string {
|
||||
r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_:/.]*)}`)
|
||||
return r.ReplaceAllStringFunc(s, func(s string) string {
|
||||
s = strings.Trim(s, "${}")
|
||||
// todo: use strings.Cut once go1.18 is released
|
||||
before, after, ok := cut(s, ":")
|
||||
before, after, ok := strings.Cut(s, ":")
|
||||
m := mapping(before)
|
||||
if ok && m == "" {
|
||||
return after
|
||||
@ -146,10 +146,3 @@ func expand(s string, mapping func(string) string) string {
|
||||
return m
|
||||
})
|
||||
}
|
||||
|
||||
func cut(s, sep string) (before, after string, found bool) {
|
||||
if i := strings.Index(s, sep); i >= 0 {
|
||||
return s[:i], s[i+len(sep):], true
|
||||
}
|
||||
return s, "", false
|
||||
}
|
||||
|
@ -3,68 +3,51 @@ package mime
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||
)
|
||||
|
||||
func init() {
|
||||
base.IsLawfulAudio = checkImage
|
||||
base.IsLawfulImage = checkImage
|
||||
base.IsLawfulAudio = checkAudio
|
||||
}
|
||||
|
||||
// keep sync with /docs/file.md#MINE
|
||||
var lawfulImage = [...]string{
|
||||
"image/bmp",
|
||||
"image/gif",
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/webp",
|
||||
}
|
||||
const limit = 4 * 1024
|
||||
|
||||
var lawfulAudio = [...]string{
|
||||
"audio/aac",
|
||||
"audio/aiff",
|
||||
"audio/amr",
|
||||
"audio/ape",
|
||||
"audio/flac",
|
||||
"audio/midi",
|
||||
"audio/mp4",
|
||||
"audio/mpeg",
|
||||
"audio/ogg",
|
||||
"audio/wav",
|
||||
"audio/x-m4a",
|
||||
}
|
||||
|
||||
func check(r io.ReadSeeker, list []string) (bool, string) {
|
||||
if base.SkipMimeScan {
|
||||
return true, ""
|
||||
}
|
||||
func scan(r io.ReadSeeker) string {
|
||||
_, _ = r.Seek(0, io.SeekStart)
|
||||
defer r.Seek(0, io.SeekStart)
|
||||
t, err := mimetype.DetectReader(r)
|
||||
if err != nil {
|
||||
logrus.Debugf("扫描 Mime 时出现问题: %v", err)
|
||||
return false, ""
|
||||
}
|
||||
for _, lt := range list {
|
||||
if t.Is(lt) {
|
||||
return true, t.String()
|
||||
}
|
||||
}
|
||||
return false, t.String()
|
||||
in := make([]byte, limit)
|
||||
_, _ = r.Read(in)
|
||||
return http.DetectContentType(in)
|
||||
}
|
||||
|
||||
// checkImage 判断给定流是否为合法图片
|
||||
// 返回 是否合法, 实际Mime
|
||||
// 判断后会自动将 Stream Seek 至 0
|
||||
func checkImage(r io.ReadSeeker) (bool, string) {
|
||||
return check(r, lawfulImage[:])
|
||||
func checkImage(r io.ReadSeeker) (ok bool, t string) {
|
||||
if base.SkipMimeScan {
|
||||
return true, ""
|
||||
}
|
||||
t = scan(r)
|
||||
switch t {
|
||||
case "image/bmp", "image/gif", "image/jpeg", "image/png", "image/webp":
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// checkImage 判断给定流是否为合法音频
|
||||
func checkAudio(r io.ReadSeeker) (bool, string) {
|
||||
return check(r, lawfulAudio[:])
|
||||
if base.SkipMimeScan {
|
||||
return true, ""
|
||||
}
|
||||
t := scan(r)
|
||||
// std mime type detection is not full supported for audio
|
||||
if strings.Contains(t, "text") || strings.Contains(t, "image") {
|
||||
return false, t
|
||||
}
|
||||
return true, t
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ package server
|
||||
// daemon 功能写在这,目前仅支持了-d 作为后台运行参数,stop,start,restart这些功能目前看起来并不需要,可以通过api控制,后续需要的话再补全。
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
@ -36,7 +36,7 @@ func Daemon() {
|
||||
|
||||
log.Info("[PID] ", proc.Process.Pid)
|
||||
// pid写入到pid文件中,方便后续stop的时候kill
|
||||
pidErr := savePid("go-cqhttp.pid", fmt.Sprintf("%d", proc.Process.Pid))
|
||||
pidErr := savePid("go-cqhttp.pid", strconv.FormatInt(int64(proc.Process.Pid), 10))
|
||||
if pidErr != nil {
|
||||
log.Errorf("save pid file error: %v", pidErr)
|
||||
}
|
||||
|
@ -2,12 +2,15 @@ package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -31,6 +34,7 @@ import (
|
||||
// HTTPServer HTTP通信相关配置
|
||||
type HTTPServer struct {
|
||||
Disabled bool `yaml:"disabled"`
|
||||
Address string `yaml:"address"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Timeout int32 `yaml:"timeout"`
|
||||
@ -63,6 +67,7 @@ type HTTPClient struct {
|
||||
filter string
|
||||
apiPort int
|
||||
timeout int32
|
||||
client *http.Client
|
||||
MaxRetries uint64
|
||||
RetriesInterval uint64
|
||||
}
|
||||
@ -75,8 +80,7 @@ type httpCtx struct {
|
||||
|
||||
const httpDefault = `
|
||||
- http: # HTTP 通信设置
|
||||
host: 127.0.0.1 # 服务端监听地址
|
||||
port: 5700 # 服务端监听端口
|
||||
address: 0.0.0.0:5700 # HTTP监听地址
|
||||
timeout: 5 # 反向 HTTP 超时时间, 单位秒,<5 时将被忽略
|
||||
long-polling: # 长轮询拓展
|
||||
enabled: false # 是否开启
|
||||
@ -208,9 +212,9 @@ func checkAuth(req *http.Request, token string) int {
|
||||
if auth == "" {
|
||||
auth = req.URL.Query().Get("access_token")
|
||||
} else {
|
||||
authN := strings.SplitN(auth, " ", 2)
|
||||
if len(authN) == 2 {
|
||||
auth = authN[1]
|
||||
_, after, ok := strings.Cut(auth, " ")
|
||||
if ok {
|
||||
auth = after
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,12 +246,21 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
var addr string
|
||||
network, addr := "tcp", conf.Address
|
||||
s := &httpServer{accessToken: conf.AccessToken}
|
||||
if conf.Host == "" || conf.Port == 0 {
|
||||
switch {
|
||||
case conf.Address != "":
|
||||
uri, err := url.Parse(conf.Address)
|
||||
if err == nil && uri.Scheme != "" {
|
||||
network = uri.Scheme
|
||||
addr = uri.Host + uri.Path
|
||||
}
|
||||
case conf.Host != "" || conf.Port != 0:
|
||||
addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||
log.Warnln("HTTP 服务器使用了过时的配置格式,请更新配置文件!")
|
||||
default:
|
||||
goto client
|
||||
}
|
||||
addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||
s.api = api.NewCaller(bot)
|
||||
if conf.RateLimit.Enabled {
|
||||
s.api.Use(rateLimit(conf.RateLimit.Frequency, conf.RateLimit.Bucket))
|
||||
@ -255,20 +268,16 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) {
|
||||
if conf.LongPolling.Enabled {
|
||||
s.api.Use(longPolling(bot, conf.LongPolling.MaxQueueSize))
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Infof("CQ HTTP 服务器已启动: %v", addr)
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: s,
|
||||
}
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Error(err)
|
||||
log.Infof("HTTP 服务启动失败, 请检查端口是否被占用.")
|
||||
listener, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
log.Infof("HTTP 服务启动失败, 请检查端口是否被占用: %v", err)
|
||||
log.Warnf("将在五秒后退出.")
|
||||
time.Sleep(time.Second * 5)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Infof("CQ HTTP 服务器已启动: %v", listener.Addr())
|
||||
log.Fatal(http.Serve(listener, s))
|
||||
}()
|
||||
client:
|
||||
for _, c := range conf.Post {
|
||||
@ -293,8 +302,30 @@ func (c HTTPClient) Run() {
|
||||
if c.timeout < 5 {
|
||||
c.timeout = 5
|
||||
}
|
||||
rawAddress := c.addr
|
||||
network, address := resolveURI(c.addr)
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * time.Duration(c.timeout),
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, _, addr string) (net.Conn, error) {
|
||||
if network == "unix" {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
host = addr
|
||||
}
|
||||
filepath, err := base64.RawURLEncoding.DecodeString(host)
|
||||
if err == nil {
|
||||
addr = string(filepath)
|
||||
}
|
||||
}
|
||||
return net.Dial(network, addr)
|
||||
},
|
||||
},
|
||||
}
|
||||
c.addr = address // clean path
|
||||
c.client = client
|
||||
log.Infof("HTTP POST上报器已启动: %v", rawAddress)
|
||||
c.bot.OnEventPush(c.onBotPushEvent)
|
||||
log.Infof("HTTP POST上报器已启动: %v", c.addr)
|
||||
}
|
||||
|
||||
func (c *HTTPClient) onBotPushEvent(e *coolq.Event) {
|
||||
@ -306,7 +337,6 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
client := http.Client{Timeout: time.Second * time.Duration(c.timeout)}
|
||||
header := make(http.Header)
|
||||
header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10))
|
||||
header.Set("User-Agent", "CQHttp/4.15.0")
|
||||
@ -320,36 +350,33 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) {
|
||||
header.Set("X-API-Port", strconv.FormatInt(int64(c.apiPort), 10))
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
var res *http.Response
|
||||
var err error
|
||||
for i := uint64(0); i <= c.MaxRetries; i++ {
|
||||
// see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0
|
||||
// we should create a new request for every single post trial
|
||||
req, err := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes()))
|
||||
req, err = http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes()))
|
||||
if err != nil {
|
||||
log.Warnf("上报 Event 数据到 %v 时创建请求失败: %v", c.addr, err)
|
||||
return
|
||||
}
|
||||
req.Header = header
|
||||
|
||||
res, err = client.Do(req)
|
||||
if res != nil {
|
||||
//goland:noinspection GoDeferInLoop
|
||||
defer res.Body.Close()
|
||||
}
|
||||
res, err = c.client.Do(req)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if i < c.MaxRetries {
|
||||
log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1)
|
||||
} else {
|
||||
log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上线", e.JSONBytes(), c.addr, err)
|
||||
log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上限", e.JSONBytes(), c.addr, err)
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval))
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
log.Debugf("上报Event数据 %s 到 %v", e.JSONBytes(), c.addr)
|
||||
|
||||
r, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -39,7 +39,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler {
|
||||
bot.OnEventPush(func(event *coolq.Event) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
queue.PushBack(event.RawMsg)
|
||||
queue.PushBack(event.Raw)
|
||||
for maxSize != 0 && queue.Len() > maxSize {
|
||||
queue.Remove(queue.Front())
|
||||
}
|
||||
@ -50,33 +50,37 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
once sync.Once
|
||||
ch = make(chan []interface{}, 1)
|
||||
ch = make(chan []interface{})
|
||||
timeout = time.Duration(p.Get("timeout").Int()) * time.Second
|
||||
)
|
||||
defer close(ch)
|
||||
go func() {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
if queue.Len() == 0 {
|
||||
for queue.Len() == 0 {
|
||||
cond.Wait()
|
||||
}
|
||||
once.Do(func() {
|
||||
limit := int(p.Get("limit").Int())
|
||||
if limit <= 0 || queue.Len() < limit {
|
||||
limit = queue.Len()
|
||||
limit := int(p.Get("limit").Int())
|
||||
if limit <= 0 || queue.Len() < limit {
|
||||
limit = queue.Len()
|
||||
}
|
||||
ret := make([]interface{}, limit)
|
||||
elem := queue.Front()
|
||||
for i := 0; i < limit; i++ {
|
||||
ret[i] = elem.Value
|
||||
elem = elem.Next()
|
||||
}
|
||||
select {
|
||||
case ch <- ret:
|
||||
for i := 0; i < limit; i++ { // remove sent msg
|
||||
queue.Remove(queue.Front())
|
||||
}
|
||||
ret := make([]interface{}, limit)
|
||||
for i := 0; i < limit; i++ {
|
||||
ret[i] = queue.Remove(queue.Front())
|
||||
}
|
||||
ch <- ret
|
||||
})
|
||||
default:
|
||||
// don't block if parent already return due to timeout
|
||||
}
|
||||
}()
|
||||
if timeout != 0 {
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
once.Do(func() {})
|
||||
return coolq.OK([]interface{}{})
|
||||
case ret := <-ch:
|
||||
return coolq.OK(ret)
|
||||
|
@ -54,7 +54,7 @@ func (l *lambdaResponseWriter) flush() error {
|
||||
buffer := global.NewBuffer()
|
||||
defer global.PutBuffer(buffer)
|
||||
body := utils.B2S(l.buf.Bytes())
|
||||
header := make(map[string]string)
|
||||
header := make(map[string]string, len(l.header))
|
||||
for k, v := range l.header {
|
||||
header[k] = v[0]
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -75,9 +78,7 @@ var upgrader = websocket.Upgrader{
|
||||
const wsDefault = ` # 正向WS设置
|
||||
- ws:
|
||||
# 正向WS服务器监听地址
|
||||
host: 127.0.0.1
|
||||
# 正向WS服务器监听端口
|
||||
port: 6700
|
||||
address: 0.0.0.0:8080
|
||||
middlewares:
|
||||
<<: *default # 引用默认中间件
|
||||
`
|
||||
@ -100,6 +101,7 @@ const wsReverseDefault = ` # 反向WS设置
|
||||
// WebsocketServer 正向WS相关配置
|
||||
type WebsocketServer struct {
|
||||
Disabled bool `yaml:"disabled"`
|
||||
Address string `yaml:"address"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
|
||||
@ -139,6 +141,17 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
network, address := "tcp", conf.Address
|
||||
if conf.Address == "" && (conf.Host != "" || conf.Port != 0) {
|
||||
log.Warn("正向 Websocket 使用了过时的配置格式,请更新配置文件")
|
||||
address = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||
} else {
|
||||
uri, err := url.Parse(conf.Address)
|
||||
if err == nil && uri.Scheme != "" {
|
||||
network = uri.Scheme
|
||||
address = uri.Host + uri.Path
|
||||
}
|
||||
}
|
||||
s := &webSocketServer{
|
||||
bot: b,
|
||||
conf: &conf,
|
||||
@ -146,7 +159,6 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) {
|
||||
filter: conf.Filter,
|
||||
}
|
||||
filter.Add(s.filter)
|
||||
addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||
s.handshake = fmt.Sprintf(`{"_post_method":2,"meta_event_type":"lifecycle","post_type":"meta_event","self_id":%d,"sub_type":"connect","time":%d}`,
|
||||
b.Client.Uin, time.Now().Unix())
|
||||
b.OnEventPush(s.onBotPushEvent)
|
||||
@ -154,8 +166,12 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) {
|
||||
mux.HandleFunc("/event", s.event)
|
||||
mux.HandleFunc("/api", s.api)
|
||||
mux.HandleFunc("/", s.any)
|
||||
log.Infof("CQ WebSocket 服务器已启动: %v", addr)
|
||||
log.Fatal(http.ListenAndServe(addr, &mux))
|
||||
listener, err := net.Listen(network, address)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof("CQ WebSocket 服务器已启动: %v", listener.Addr())
|
||||
log.Fatal(http.Serve(listener, &mux))
|
||||
}
|
||||
|
||||
// runWSClient 运行一个反向向WS client
|
||||
@ -196,8 +212,26 @@ func runWSClient(b *coolq.CQBot, node yaml.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *websocketClient) connect(typ, url string, conptr **wsConn) {
|
||||
log.Infof("开始尝试连接到反向WebSocket %s服务器: %v", typ, url)
|
||||
func resolveURI(addr string) (network, address string) {
|
||||
network, address = "tcp", addr
|
||||
uri, err := url.Parse(addr)
|
||||
if err == nil && uri.Scheme != "" {
|
||||
scheme, ext, _ := strings.Cut(uri.Scheme, "+")
|
||||
if ext != "" {
|
||||
network = ext
|
||||
uri.Scheme = scheme // remove `+unix`/`+tcp4`
|
||||
if ext == "unix" {
|
||||
uri.Host, uri.Path, _ = strings.Cut(uri.Path, ":")
|
||||
uri.Host = base64.StdEncoding.EncodeToString([]byte(uri.Host))
|
||||
}
|
||||
address = uri.String()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *websocketClient) connect(typ, addr string, conptr **wsConn) {
|
||||
log.Infof("开始尝试连接到反向WebSocket %s服务器: %v", typ, addr)
|
||||
header := http.Header{
|
||||
"X-Client-Role": []string{typ},
|
||||
"X-Self-ID": []string{strconv.FormatInt(c.bot.Client.Uin, 10)},
|
||||
@ -206,12 +240,30 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) {
|
||||
if c.token != "" {
|
||||
header["Authorization"] = []string{"Token " + c.token}
|
||||
}
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url, header) // nolint
|
||||
|
||||
network, address := resolveURI(addr)
|
||||
dialer := websocket.Dialer{
|
||||
NetDial: func(_, addr string) (net.Conn, error) {
|
||||
if network == "unix" {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
host = addr
|
||||
}
|
||||
filepath, err := base64.RawURLEncoding.DecodeString(host)
|
||||
if err == nil {
|
||||
addr = string(filepath)
|
||||
}
|
||||
}
|
||||
return net.Dial(network, addr) // support unix socket transport
|
||||
},
|
||||
}
|
||||
|
||||
conn, _, err := dialer.Dial(address, header) // nolint
|
||||
if err != nil {
|
||||
log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, url, err)
|
||||
log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, addr, err)
|
||||
if c.reconnectInterval != 0 {
|
||||
time.Sleep(c.reconnectInterval)
|
||||
c.connect(typ, url, conptr)
|
||||
c.connect(typ, addr, conptr)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -225,7 +277,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) {
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("已连接到反向WebSocket %s服务器 %v", typ, url)
|
||||
log.Infof("已连接到反向WebSocket %s服务器 %v", typ, addr)
|
||||
|
||||
var wrappedConn *wsConn
|
||||
if conptr != nil && *conptr != nil {
|
||||
@ -244,7 +296,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) {
|
||||
}
|
||||
|
||||
if typ != "Event" {
|
||||
go c.listenAPI(typ, url, wrappedConn)
|
||||
go c.listenAPI(typ, addr, wrappedConn)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user