mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-06-30 03:43:25 +00:00
Compare commits
110 Commits
v1.0.0-bet
...
v1.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
17d983060a | |||
a526ec6dbc | |||
d4613cf9c1 | |||
47afd927e3 | |||
e59e0994d5 | |||
9eab6a75fc | |||
fb20e07219 | |||
f42f70c337 | |||
77c3bece08 | |||
e4e17fb8c4 | |||
f6f31a87f9 | |||
9a8ac6fd00 | |||
8bc989cfa2 | |||
19554da68a | |||
f94c9fbda6 | |||
c5239d7c86 | |||
dca96982b5 | |||
ca16e4c2a7 | |||
9c323c6a4e | |||
3c6dfa8e48 | |||
459f9e0f44 | |||
4716d79a7e | |||
f0a472bc3c | |||
66017bffb6 | |||
9a9736f38d | |||
8ee342c871 | |||
605d8bb07d | |||
dfcd9a523c | |||
92e1aafaf3 | |||
700ecdad06 | |||
8043621363 | |||
df64ed2ca2 | |||
2fbaff8e72 | |||
111ac7f865 | |||
9534fc42fc | |||
80827b8614 | |||
3905052b68 | |||
36e0ec4e41 | |||
33d20d4698 | |||
1f45c596b2 | |||
0138a6c467 | |||
2492fa88f5 | |||
da212d334c | |||
aa64e1d379 | |||
1ecb1f62dc | |||
0487f849bc | |||
25171e5777 | |||
196d9bc1a3 | |||
ced7ca6138 | |||
65e5f67f1f | |||
d36d7aa2ed | |||
a5314f0b6d | |||
ee3e1bac76 | |||
79aabf5bd7 | |||
f86947a8df | |||
457898d37b | |||
223a888a34 | |||
773d2b77e6 | |||
35e74170b7 | |||
96861bd8e1 | |||
f6073f5a61 | |||
a27848979d | |||
e4abf426bc | |||
1aed38ac18 | |||
cccf454500 | |||
b9e7006547 | |||
d8a373cfa4 | |||
fb7ba0557c | |||
6011a46f1b | |||
de4f4c8676 | |||
cbd91d27bd | |||
987c57f7cf | |||
379a589fc8 | |||
6ba8774ff1 | |||
b1652c0f4b | |||
48b095f825 | |||
e1f1f715e2 | |||
e2257ee499 | |||
beb5c77767 | |||
823e6cccdd | |||
92b29dee2e | |||
2df33118be | |||
382d2ffda8 | |||
056f10ff64 | |||
0900cf5310 | |||
971a9575ff | |||
c32920ac40 | |||
7db6070b18 | |||
37e2fdaea7 | |||
2506d9144b | |||
67aa9781f2 | |||
44539a8f63 | |||
e3c06731e7 | |||
8506d7586f | |||
b20ab9dc52 | |||
a157bb3220 | |||
c1a7dda54a | |||
f350d971fd | |||
6dfa5e5959 | |||
a0cb34dc7a | |||
6fecede756 | |||
8a269aab69 | |||
7f6e1f61ea | |||
6f83db0ad0 | |||
f488b17dc7 | |||
4250199693 | |||
75aedd867a | |||
af46324dd9 | |||
08fd86493d | |||
88594184f8 |
9
.github/workflows/golint.yml
vendored
9
.github/workflows/golint.yml
vendored
@ -9,11 +9,20 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Go environment
|
||||||
|
uses: actions/setup-go@v2.1.3
|
||||||
|
with:
|
||||||
|
go-version: 1.16
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: |
|
||||||
|
go test $(go list ./...)
|
||||||
|
|
||||||
- name: Commit back
|
- name: Commit back
|
||||||
if: github.repository_owner == 'Mrs4s'
|
if: github.repository_owner == 'Mrs4s'
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
11
.github/workflows/suggester.yml
vendored
11
.github/workflows/suggester.yml
vendored
@ -9,12 +9,21 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Go environment
|
||||||
|
uses: actions/setup-go@v2.1.3
|
||||||
|
with:
|
||||||
|
go-version: 1.16
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: |
|
||||||
|
go test $(go list ./...)
|
||||||
|
|
||||||
- name: Suggester
|
- name: Suggester
|
||||||
uses: reviewdog/action-suggester@v1
|
uses: reviewdog/action-suggester@v1
|
||||||
with:
|
with:
|
||||||
tool_name: golangci-lint
|
tool_name: golangci-lint
|
||||||
|
@ -33,14 +33,14 @@ linters:
|
|||||||
#- goconst
|
#- goconst
|
||||||
- gocritic
|
- gocritic
|
||||||
#- gocyclo
|
#- gocyclo
|
||||||
- gofumpt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
#- gosec
|
#- gosec
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- misspell
|
#- misspell
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- staticcheck
|
- staticcheck
|
||||||
@ -55,7 +55,7 @@ linters:
|
|||||||
- prealloc
|
- prealloc
|
||||||
- predeclared
|
- predeclared
|
||||||
- asciicheck
|
- asciicheck
|
||||||
- golint
|
- revive
|
||||||
- forbidigo
|
- forbidigo
|
||||||
- makezero
|
- makezero
|
||||||
#- interfacer
|
#- interfacer
|
||||||
|
48
README.md
48
README.md
@ -109,13 +109,13 @@ go-cqhttp兼容[OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v1
|
|||||||
| [CQ:cardimage] | [一种 xml 的图片消息(装逼大图)] |
|
| [CQ:cardimage] | [一种 xml 的图片消息(装逼大图)] |
|
||||||
| [CQ:tts] | [文本转语音] |
|
| [CQ:tts] | [文本转语音] |
|
||||||
|
|
||||||
[图片]: docs/cqhttp.md#%E5%9B%BE%E7%89%87
|
[图片]: https://docs.go-cqhttp.org/cqcode/#%E5%9B%BE%E7%89%87
|
||||||
[红包]: docs/cqhttp.md#%E7%BA%A2%E5%8C%85
|
[红包]: https://docs.go-cqhttp.org/cqcode/#%E7%BA%A2%E5%8C%85
|
||||||
[戳一戳]: docs/cqhttp.md#%E6%88%B3%E4%B8%80%E6%88%B3
|
[戳一戳]: https://docs.go-cqhttp.org/cqcode/#%E6%88%B3%E4%B8%80%E6%88%B3
|
||||||
[礼物]: docs/cqhttp.md#%E7%A4%BC%E7%89%A9
|
[礼物]: https://docs.go-cqhttp.org/cqcode/#%E7%A4%BC%E7%89%A9
|
||||||
[合并转发消息节点]: docs/cqhttp.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF%E8%8A%82%E7%82%B9
|
[合并转发消息节点]: https://docs.go-cqhttp.org/cqcode/#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF%E8%8A%82%E7%82%B9
|
||||||
[一种 xml 的图片消息(装逼大图)]: docs/cqhttp.md#cardimage-%E4%B8%80%E7%A7%8Dxml%E7%9A%84%E5%9B%BE%E7%89%87%E6%B6%88%E6%81%AF%E8%A3%85%E9%80%BC%E5%A4%A7%E5%9B%BE
|
[一种 xml 的图片消息(装逼大图)]: https://docs.go-cqhttp.org/cqcode/#cardimage
|
||||||
[文本转语音]: docs/cqhttp.md#%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3
|
[文本转语音]: https://docs.go-cqhttp.org/cqcode/#%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -200,19 +200,19 @@ go-cqhttp兼容[OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v1
|
|||||||
| /get_group_file_url | [获取群文件资源链接] |
|
| /get_group_file_url | [获取群文件资源链接] |
|
||||||
| /get_status | [获取状态] |
|
| /get_status | [获取状态] |
|
||||||
|
|
||||||
[设置群头像]: docs/cqhttp.md#%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%A4%B4%E5%83%8F
|
[设置群头像]: https://docs.go-cqhttp.org/api/#%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%A4%B4%E5%83%8F
|
||||||
[获取图片信息]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87%E4%BF%A1%E6%81%AF
|
[获取图片信息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87%E4%BF%A1%E6%81%AF
|
||||||
[获取消息]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF
|
[获取消息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF
|
||||||
[获取合并转发内容]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E5%86%85%E5%AE%B9
|
[获取合并转发内容]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E5%86%85%E5%AE%B9
|
||||||
[发送合并转发(群)]: docs/cqhttp.md#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E7%BE%A4
|
[发送合并转发(群)]: 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
|
||||||
[获取中文分词]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D
|
[获取中文分词]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D-%E9%9A%90%E8%97%8F-api
|
||||||
[图片 ocr]: docs/cqhttp.md#%E5%9B%BE%E7%89%87ocr
|
[图片 ocr]: https://docs.go-cqhttp.org/api/#%E5%9B%BE%E7%89%87-ocr
|
||||||
[获取群系统消息]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%BE%A4%E7%B3%BB%E7%BB%9F%E6%B6%88%E6%81%AF
|
[获取群系统消息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E7%B3%BB%E7%BB%9F%E6%B6%88%E6%81%AF
|
||||||
[获取群文件系统信息]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF
|
[获取群文件系统信息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF
|
||||||
[获取群根目录文件列表]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%A0%B9%E7%9B%AE%E5%BD%95%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8
|
[获取群根目录文件列表]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%A0%B9%E7%9B%AE%E5%BD%95%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8
|
||||||
[获取群子目录文件列表]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%AD%90%E7%9B%AE%E5%BD%95%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8
|
[获取群子目录文件列表]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%AD%90%E7%9B%AE%E5%BD%95%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8
|
||||||
[获取群文件资源链接]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E8%B5%84%E6%BA%90%E9%93%BE%E6%8E%A5
|
[获取群文件资源链接]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E8%B5%84%E6%BA%90%E9%93%BE%E6%8E%A5
|
||||||
[获取状态]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%8A%B6%E6%80%81
|
[获取状态]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%8A%B6%E6%80%81
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -260,12 +260,14 @@ go-cqhttp兼容[OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v1
|
|||||||
| 事件类型 | 拓展 Event |
|
| 事件类型 | 拓展 Event |
|
||||||
| -------- | ---------------- |
|
| -------- | ---------------- |
|
||||||
| 通知事件 | [好友戳一戳] |
|
| 通知事件 | [好友戳一戳] |
|
||||||
|
| 通知事件 | [群内戳一戳] |
|
||||||
| 通知事件 | [群成员名片更新] |
|
| 通知事件 | [群成员名片更新] |
|
||||||
| 通知事件 | [接收到离线文件] |
|
| 通知事件 | [接收到离线文件] |
|
||||||
|
|
||||||
[好友戳一戳]: docs/cqhttp.md#%E5%A5%BD%E5%8F%8B%E6%88%B3%E4%B8%80%E6%88%B3
|
[好友戳一戳]: https://docs.go-cqhttp.org/event/#%E5%A5%BD%E5%8F%8B%E6%88%B3%E4%B8%80%E6%88%B3
|
||||||
[群成员名片更新]: docs/cqhttp.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%90%8D%E7%89%87%E6%9B%B4%E6%96%B0
|
[群内戳一戳]: https://docs.go-cqhttp.org/event/#%E7%BE%A4%E5%86%85%E6%88%B3%E4%B8%80%E6%88%B3
|
||||||
[接收到离线文件]: docs/cqhttp.md#%E6%8E%A5%E6%94%B6%E5%88%B0%E7%A6%BB%E7%BA%BF%E6%96%87%E4%BB%B6
|
[群成员名片更新]: https://docs.go-cqhttp.org/event/#%E7%BE%A4%E6%88%90%E5%91%98%E5%90%8D%E7%89%87%E6%9B%B4%E6%96%B0
|
||||||
|
[接收到离线文件]: https://docs.go-cqhttp.org/event/#%E6%8E%A5%E6%94%B6%E5%88%B0%E7%A6%BB%E7%BA%BF%E6%96%87%E4%BB%B6
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
148
coolq/api.go
148
coolq/api.go
@ -46,6 +46,18 @@ func (bot *CQBot) CQGetLoginInfo() MSG {
|
|||||||
return OK(MSG{"user_id": bot.Client.Uin, "nickname": bot.Client.Nickname})
|
return OK(MSG{"user_id": bot.Client.Uin, "nickname": bot.Client.Nickname})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQGetQiDianAccountInfo 获取企点账号信息
|
||||||
|
func (bot *CQBot) CQGetQiDianAccountInfo() MSG {
|
||||||
|
if bot.Client.QiDian == nil {
|
||||||
|
return Failed(100, "QIDIAN_PROTOCOL_REQUEST", "请使用企点协议")
|
||||||
|
}
|
||||||
|
return OK(MSG{
|
||||||
|
"master_id": bot.Client.QiDian.MasterUin,
|
||||||
|
"ext_name": bot.Client.QiDian.ExtName,
|
||||||
|
"create_time": bot.Client.QiDian.CreateTime,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// CQGetFriendList 获取好友列表
|
// CQGetFriendList 获取好友列表
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1L
|
// https://git.io/Jtz1L
|
||||||
@ -61,6 +73,20 @@ func (bot *CQBot) CQGetFriendList() MSG {
|
|||||||
return OK(fs)
|
return OK(fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQDeleteFriend 删除好友
|
||||||
|
//
|
||||||
|
//
|
||||||
|
func (bot *CQBot) CQDeleteFriend(uin int64) MSG {
|
||||||
|
if bot.Client.FindFriend(uin) == nil {
|
||||||
|
return Failed(100, "FRIEND_NOT_FOUND", "好友不存在")
|
||||||
|
}
|
||||||
|
if err := bot.Client.DeleteFriend(uin); err != nil {
|
||||||
|
log.Errorf("删除好友时出现错误: %v", err)
|
||||||
|
return Failed(100, "DELETE_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// CQGetGroupList 获取群列表
|
// CQGetGroupList 获取群列表
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1t
|
// https://git.io/Jtz1t
|
||||||
@ -260,6 +286,54 @@ func (bot *CQBot) CQUploadGroupFile(groupID int64, file, name, folder string) MS
|
|||||||
return OK(nil)
|
return OK(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQGroupFileCreateFolder 拓展API-创建群文件文件夹
|
||||||
|
//
|
||||||
|
//
|
||||||
|
func (bot *CQBot) CQGroupFileCreateFolder(groupID int64, parentID, name string) MSG {
|
||||||
|
fs, err := bot.Client.GetGroupFileSystem(groupID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err)
|
||||||
|
return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
if err = fs.CreateFolder(parentID, name); err != nil {
|
||||||
|
log.Errorf("创建群 %v 文件夹失败: %v", groupID, err)
|
||||||
|
return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQGroupFileDeleteFolder 拓展API-删除群文件文件夹
|
||||||
|
//
|
||||||
|
//
|
||||||
|
func (bot *CQBot) CQGroupFileDeleteFolder(groupID int64, id string) MSG {
|
||||||
|
fs, err := bot.Client.GetGroupFileSystem(groupID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err)
|
||||||
|
return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
if err = fs.DeleteFolder(id); err != nil {
|
||||||
|
log.Errorf("删除群 %v 文件夹 %v 时出现文件: %v", groupID, id, err)
|
||||||
|
return Failed(200, "FILE_SYSTEM_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQGroupFileDeleteFile 拓展API-删除群文件
|
||||||
|
//
|
||||||
|
//
|
||||||
|
func (bot *CQBot) CQGroupFileDeleteFile(groupID int64, parentID, id string, busID int32) MSG {
|
||||||
|
fs, err := bot.Client.GetGroupFileSystem(groupID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err)
|
||||||
|
return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
if res := fs.DeleteFile(parentID, id, busID); res != "" {
|
||||||
|
log.Errorf("删除群 %v 文件 %v 时出现文件: %v", groupID, id, res)
|
||||||
|
return Failed(200, "FILE_SYSTEM_API_ERROR", res)
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// CQGetWordSlices 隐藏API-获取中文分词
|
// CQGetWordSlices 隐藏API-获取中文分词
|
||||||
//
|
//
|
||||||
// https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D-%E9%9A%90%E8%97%8F-api
|
// https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D-%E9%9A%90%E8%97%8F-api
|
||||||
@ -285,7 +359,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, i interface{}, autoEscape bo
|
|||||||
}
|
}
|
||||||
fixAt := func(elem []message.IMessageElement) {
|
fixAt := func(elem []message.IMessageElement) {
|
||||||
for _, e := range elem {
|
for _, e := range elem {
|
||||||
if at, ok := e.(*message.AtElement); ok && at.Target != 0 {
|
if at, ok := e.(*message.AtElement); ok && at.Target != 0 && at.Display == "" {
|
||||||
at.Display = "@" + func() string {
|
at.Display = "@" + func() string {
|
||||||
mem := group.FindMember(at.Target)
|
mem := group.FindMember(at.Target)
|
||||||
if mem != nil {
|
if mem != nil {
|
||||||
@ -302,9 +376,9 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, i interface{}, autoEscape bo
|
|||||||
fixAt(elem)
|
fixAt(elem)
|
||||||
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
||||||
if mid == -1 {
|
if mid == -1 {
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(ToStringMessage(elem, int64(mid))), mid)
|
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(ToStringMessage(elem, groupID)), mid)
|
||||||
return OK(MSG{"message_id": mid})
|
return OK(MSG{"message_id": mid})
|
||||||
}
|
}
|
||||||
str = func() string {
|
str = func() string {
|
||||||
@ -329,7 +403,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, i interface{}, autoEscape bo
|
|||||||
fixAt(elem)
|
fixAt(elem)
|
||||||
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
||||||
if mid == -1 {
|
if mid == -1 {
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(str), mid)
|
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(str), mid)
|
||||||
return OK(MSG{"message_id": mid})
|
return OK(MSG{"message_id": mid})
|
||||||
@ -456,7 +530,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
ret := bot.Client.SendGroupForwardMessage(groupID, &message.ForwardMessage{Nodes: sendNodes})
|
ret := bot.Client.SendGroupForwardMessage(groupID, &message.ForwardMessage{Nodes: sendNodes})
|
||||||
if ret == nil || ret.Id == -1 {
|
if ret == nil || ret.Id == -1 {
|
||||||
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
return OK(MSG{
|
return OK(MSG{
|
||||||
"message_id": bot.InsertGroupMessage(ret),
|
"message_id": bot.InsertGroupMessage(ret),
|
||||||
@ -475,7 +549,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, i interface{
|
|||||||
elem := bot.ConvertObjectMessage(m, false)
|
elem := bot.ConvertObjectMessage(m, false)
|
||||||
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
||||||
if mid == -1 {
|
if mid == -1 {
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
log.Infof("发送好友 %v(%v) 的消息: %v (%v)", userID, userID, limitedString(m.String()), mid)
|
log.Infof("发送好友 %v(%v) 的消息: %v (%v)", userID, userID, limitedString(m.String()), mid)
|
||||||
return OK(MSG{"message_id": mid})
|
return OK(MSG{"message_id": mid})
|
||||||
@ -500,7 +574,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, i interface{
|
|||||||
}
|
}
|
||||||
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
||||||
if mid == -1 {
|
if mid == -1 {
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
log.Infof("发送好友 %v(%v) 的消息: %v (%v)", userID, userID, limitedString(str), mid)
|
log.Infof("发送好友 %v(%v) 的消息: %v (%v)", userID, userID, limitedString(str), mid)
|
||||||
return OK(MSG{"message_id": mid})
|
return OK(MSG{"message_id": mid})
|
||||||
@ -546,12 +620,26 @@ func (bot *CQBot) CQSetGroupName(groupID int64, name string) MSG {
|
|||||||
// CQSetGroupMemo 扩展API-发送群公告
|
// CQSetGroupMemo 扩展API-发送群公告
|
||||||
//
|
//
|
||||||
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E7%BE%A4%E5%85%AC%E5%91%8A
|
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E7%BE%A4%E5%85%AC%E5%91%8A
|
||||||
func (bot *CQBot) CQSetGroupMemo(groupID int64, msg string) MSG {
|
func (bot *CQBot) CQSetGroupMemo(groupID int64, msg string, img string) MSG {
|
||||||
if g := bot.Client.FindGroup(groupID); g != nil {
|
if g := bot.Client.FindGroup(groupID); g != nil {
|
||||||
if g.SelfPermission() == client.Member {
|
if g.SelfPermission() == client.Member {
|
||||||
return Failed(100, "PERMISSION_DENIED", "权限不足")
|
return Failed(100, "PERMISSION_DENIED", "权限不足")
|
||||||
}
|
}
|
||||||
_ = bot.Client.AddGroupNoticeSimple(groupID, msg)
|
if img != "" {
|
||||||
|
data, err := global.FindFile(img, "", global.ImagePath)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "IMAGE_NOT_FOUND", "图片未找到")
|
||||||
|
}
|
||||||
|
err = bot.Client.AddGroupNoticeWithPic(groupID, msg, data)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "SEND_NOTICE_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := bot.Client.AddGroupNoticeSimple(groupID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "SEND_NOTICE_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
return OK(nil)
|
return OK(nil)
|
||||||
}
|
}
|
||||||
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在")
|
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在")
|
||||||
@ -952,9 +1040,12 @@ func (bot *CQBot) CQGetImage(file string) MSG {
|
|||||||
}
|
}
|
||||||
local := path.Join(global.CachePath, file+"."+path.Ext(msg["filename"].(string)))
|
local := path.Join(global.CachePath, file+"."+path.Ext(msg["filename"].(string)))
|
||||||
if !global.PathExists(local) {
|
if !global.PathExists(local) {
|
||||||
if data, err := global.GetBytes(msg["url"].(string)); err == nil {
|
f, _ := os.OpenFile(local, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o0644)
|
||||||
_ = ioutil.WriteFile(local, data, 0644)
|
if body, err := global.HTTPGetReadCloser(msg["url"].(string)); err == nil {
|
||||||
|
_, _ = f.ReadFrom(body)
|
||||||
|
_ = body.Close()
|
||||||
}
|
}
|
||||||
|
f.Close()
|
||||||
}
|
}
|
||||||
msg["file"] = local
|
msg["file"] = local
|
||||||
return OK(msg)
|
return OK(msg)
|
||||||
@ -1042,7 +1133,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) MSG {
|
|||||||
if isGroup {
|
if isGroup {
|
||||||
return gid.(int64)
|
return gid.(int64)
|
||||||
}
|
}
|
||||||
return sender.Uin
|
return 0
|
||||||
}(), false),
|
}(), false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1308,6 +1399,8 @@ func (bot *CQBot) CQGetVersionInfo() MSG {
|
|||||||
return 2
|
return 2
|
||||||
case client.MacOS:
|
case client.MacOS:
|
||||||
return 3
|
return 3
|
||||||
|
case client.QiDian:
|
||||||
|
return 4
|
||||||
default:
|
default:
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@ -1315,6 +1408,37 @@ func (bot *CQBot) CQGetVersionInfo() MSG {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQGetModelShow 获取在线机型
|
||||||
|
//
|
||||||
|
// https://club.vip.qq.com/onlinestatus/set
|
||||||
|
func (bot *CQBot) CQGetModelShow(modelName string) MSG {
|
||||||
|
variants, err := bot.Client.GetModelShow(modelName)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "GET_MODEL_SHOW_API_ERROR", "无法获取在线机型")
|
||||||
|
}
|
||||||
|
a := make([]MSG, 0, len(variants))
|
||||||
|
for _, v := range variants {
|
||||||
|
a = append(a, MSG{
|
||||||
|
"model_show": v.ModelShow,
|
||||||
|
"need_pay": v.NeedPay,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return OK(MSG{
|
||||||
|
"variants": a,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQSetModelShow 设置在线机型
|
||||||
|
//
|
||||||
|
// https://club.vip.qq.com/onlinestatus/set
|
||||||
|
func (bot *CQBot) CQSetModelShow(modelName string, modelShow string) MSG {
|
||||||
|
err := bot.Client.SetModelShow(modelName, modelShow)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "SET_MODEL_SHOW_API_ERROR", "无法设置在线机型")
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// OK 生成成功返回值
|
// OK 生成成功返回值
|
||||||
func OK(data interface{}) MSG {
|
func OK(data interface{}) MSG {
|
||||||
return MSG{"data": data, "retcode": 0, "status": "ok"}
|
return MSG{"data": data, "retcode": 0, "status": "ok"}
|
||||||
|
32
coolq/bot.go
32
coolq/bot.go
@ -23,6 +23,7 @@ import (
|
|||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
@ -58,7 +59,9 @@ func NewQQBot(cli *client.QQClient, conf *config.Config) *CQBot {
|
|||||||
}
|
}
|
||||||
if enableLevelDB {
|
if enableLevelDB {
|
||||||
p := path.Join("data", "leveldb")
|
p := path.Join("data", "leveldb")
|
||||||
db, err := leveldb.OpenFile(p, nil)
|
db, err := leveldb.OpenFile(p, &opt.Options{
|
||||||
|
WriteBuffer: 128 * opt.KiB,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("打开数据库失败, 如果频繁遇到此问题请清理 data/leveldb 文件夹或关闭数据库功能。")
|
log.Fatalf("打开数据库失败, 如果频繁遇到此问题请清理 data/leveldb 文件夹或关闭数据库功能。")
|
||||||
}
|
}
|
||||||
@ -129,10 +132,7 @@ func (bot *CQBot) GetMessage(mid int32) MSG {
|
|||||||
m := MSG{}
|
m := MSG{}
|
||||||
data, err := bot.db.Get(binary.ToBytes(mid), nil)
|
data, err := bot.db.Get(binary.ToBytes(mid), nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
buff := global.NewBuffer()
|
err = gob.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||||
defer global.PutBuffer(buff)
|
|
||||||
buff.Write(binary.GZipUncompress(data))
|
|
||||||
err = gob.NewDecoder(buff).Decode(&m)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if i, ok := elem.(*message.VoiceElement); ok {
|
if i, ok := elem.(*message.VoiceElement); ok {
|
||||||
fv, err := bot.Client.UploadPrivatePtt(target, i.Data)
|
fv, err := bot.Client.UploadPrivatePtt(target, bytes.NewReader(i.Data)) // todo: io.ReadSeeker
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("警告: 私聊 %v 消息语音上传失败: %v", target, err)
|
log.Warnf("警告: 私聊 %v 消息语音上传失败: %v", target, err)
|
||||||
continue
|
continue
|
||||||
@ -316,7 +316,10 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
log.Errorf("错误: 群员(%v) 不在 群(%v), 无法发起临时会话", target, groupID)
|
log.Errorf("错误: 群员(%v) 不在 群(%v), 无法发起临时会话", target, groupID)
|
||||||
default:
|
default:
|
||||||
if session == nil && groupID != 0 {
|
if session == nil && groupID != 0 {
|
||||||
bot.Client.SendGroupTempMessage(groupID, target, m)
|
msg := bot.Client.SendGroupTempMessage(groupID, target, m)
|
||||||
|
if msg != nil {
|
||||||
|
id = bot.InsertTempMessage(target, msg)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
msg, err := session.(*client.TempSessionInfo).SendMessage(m)
|
msg, err := session.(*client.TempSessionInfo).SendMessage(m)
|
||||||
@ -340,9 +343,6 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
}
|
}
|
||||||
log.Errorf("错误: 请先添加 %v(%v) 为好友", nickname, target)
|
log.Errorf("错误: 请先添加 %v(%v) 为好友", nickname, target)
|
||||||
}
|
}
|
||||||
if id == -1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +365,7 @@ func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 {
|
|||||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
if err := bot.db.Put(binary.ToBytes(id), binary.GZipCompress(buf.Bytes()), nil); err != nil {
|
if err := bot.db.Put(binary.ToBytes(id), buf.Bytes(), nil); err != nil {
|
||||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 {
|
|||||||
"target": m.Target,
|
"target": m.Target,
|
||||||
"sender": m.Sender,
|
"sender": m.Sender,
|
||||||
"time": m.Time,
|
"time": m.Time,
|
||||||
"message": ToStringMessage(m.Elements, m.Sender.Uin, true),
|
"message": ToStringMessage(m.Elements, 0, true),
|
||||||
}
|
}
|
||||||
id := toGlobalID(m.Sender.Uin, m.Id)
|
id := toGlobalID(m.Sender.Uin, m.Id)
|
||||||
if bot.db != nil {
|
if bot.db != nil {
|
||||||
@ -391,7 +391,7 @@ func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 {
|
|||||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
if err := bot.db.Put(binary.ToBytes(id), binary.GZipCompress(buf.Bytes()), nil); err != nil {
|
if err := bot.db.Put(binary.ToBytes(id), buf.Bytes(), nil); err != nil {
|
||||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@ -408,8 +408,8 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
|||||||
"group-name": m.GroupName,
|
"group-name": m.GroupName,
|
||||||
"target": target,
|
"target": target,
|
||||||
"sender": m.Sender,
|
"sender": m.Sender,
|
||||||
"time": time.Now().Unix(),
|
"time": int32(time.Now().Unix()),
|
||||||
"message": ToStringMessage(m.Elements, m.Sender.Uin, true),
|
"message": ToStringMessage(m.Elements, 0, true),
|
||||||
}
|
}
|
||||||
id := toGlobalID(m.Sender.Uin, m.Id)
|
id := toGlobalID(m.Sender.Uin, m.Id)
|
||||||
if bot.db != nil {
|
if bot.db != nil {
|
||||||
@ -419,7 +419,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
|||||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
if err := bot.db.Put(binary.ToBytes(id), binary.GZipCompress(buf.Bytes()), nil); err != nil {
|
if err := bot.db.Put(binary.ToBytes(id), buf.Bytes(), nil); err != nil {
|
||||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
439
coolq/cqcode.go
439
coolq/cqcode.go
@ -14,12 +14,10 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
@ -28,7 +26,7 @@ import (
|
|||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
"github.com/Mrs4s/go-cqhttp/global/config"
|
"github.com/Mrs4s/go-cqhttp/global/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -37,7 +35,11 @@ var typeReg = regexp.MustCompile(`\[CQ:(\w+)`)
|
|||||||
var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`)
|
var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var conf *config.Config
|
// RemoveReplyAt 是否删除reply后的at
|
||||||
|
var RemoveReplyAt bool
|
||||||
|
|
||||||
|
// ExtraReplyData 是否上报额外reply信息
|
||||||
|
var ExtraReplyData bool
|
||||||
|
|
||||||
// IgnoreInvalidCQCode 是否忽略无效CQ码
|
// IgnoreInvalidCQCode 是否忽略无效CQ码
|
||||||
var IgnoreInvalidCQCode = false
|
var IgnoreInvalidCQCode = false
|
||||||
@ -45,20 +47,6 @@ var IgnoreInvalidCQCode = false
|
|||||||
// SplitURL 是否分割URL
|
// SplitURL 是否分割URL
|
||||||
var SplitURL = false
|
var SplitURL = false
|
||||||
|
|
||||||
// magicCQ 代表 uint32([]byte("[CQ:"))
|
|
||||||
var magicCQ = uint32(0)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
conf = config.Get()
|
|
||||||
CQHeader := "[CQ:"
|
|
||||||
magicCQ = *(*uint32)(unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&CQHeader)).Data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// add 指针运算
|
|
||||||
func add(ptr unsafe.Pointer, offset uintptr) unsafe.Pointer {
|
|
||||||
return unsafe.Pointer(uintptr(ptr) + offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxImageSize = 1024 * 1024 * 30 // 30MB
|
maxImageSize = 1024 * 1024 * 30 // 30MB
|
||||||
maxVideoSize = 1024 * 1024 * 100 // 100MB
|
maxVideoSize = 1024 * 1024 * 100 // 100MB
|
||||||
@ -126,12 +114,8 @@ func (e *PokeElement) Type() message.ElementType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报
|
// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报
|
||||||
func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []MSG) {
|
func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []MSG) {
|
||||||
r = make([]MSG, 0, len(e))
|
r = make([]MSG, 0, len(e))
|
||||||
ur := false
|
|
||||||
if len(isRaw) != 0 {
|
|
||||||
ur = isRaw[0]
|
|
||||||
}
|
|
||||||
m := &message.SendingMessage{Elements: e}
|
m := &message.SendingMessage{Elements: e}
|
||||||
reply := m.FirstOrNil(func(e message.IMessageElement) bool {
|
reply := m.FirstOrNil(func(e message.IMessageElement) bool {
|
||||||
_, ok := e.(*message.ReplyElement)
|
_, ok := e.(*message.ReplyElement)
|
||||||
@ -139,21 +123,25 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
|||||||
})
|
})
|
||||||
if reply != nil {
|
if reply != nil {
|
||||||
replyElem := reply.(*message.ReplyElement)
|
replyElem := reply.(*message.ReplyElement)
|
||||||
if conf.Message.ExtraReplyData {
|
rid := groupID
|
||||||
|
if rid == 0 {
|
||||||
|
rid = replyElem.Sender
|
||||||
|
}
|
||||||
|
if ExtraReplyData {
|
||||||
r = append(r, MSG{
|
r = append(r, MSG{
|
||||||
"type": "reply",
|
"type": "reply",
|
||||||
"data": map[string]string{
|
"data": map[string]string{
|
||||||
"id": fmt.Sprint(toGlobalID(id, reply.(*message.ReplyElement).ReplySeq)),
|
"id": fmt.Sprint(toGlobalID(rid, replyElem.ReplySeq)),
|
||||||
"seq": string(replyElem.ReplySeq),
|
"seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10),
|
||||||
"qq": strconv.FormatInt(replyElem.Sender, 10),
|
"qq": strconv.FormatInt(replyElem.Sender, 10),
|
||||||
"time": string(replyElem.Time),
|
"time": strconv.FormatInt(int64(replyElem.Time), 10),
|
||||||
"text": CQCodeEscapeValue(CQCodeEscapeText(ToStringMessage(replyElem.Elements, id))),
|
"text": ToStringMessage(replyElem.Elements, groupID),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
r = append(r, MSG{
|
r = append(r, MSG{
|
||||||
"type": "reply",
|
"type": "reply",
|
||||||
"data": map[string]string{"id": fmt.Sprint(toGlobalID(id, replyElem.ReplySeq))},
|
"data": map[string]string{"id": fmt.Sprint(toGlobalID(rid, replyElem.ReplySeq))},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,7 +149,7 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
|||||||
var m MSG
|
var m MSG
|
||||||
switch o := elem.(type) {
|
switch o := elem.(type) {
|
||||||
case *message.ReplyElement:
|
case *message.ReplyElement:
|
||||||
if conf.Message.RemoveReplyAt && len(e) > i+1 {
|
if RemoveReplyAt && len(e) > i+1 {
|
||||||
elem, ok := e[i+1].(*message.AtElement)
|
elem, ok := e[i+1].(*message.AtElement)
|
||||||
if ok && elem.Target == o.Sender {
|
if ok && elem.Target == o.Sender {
|
||||||
e[i+1] = nil
|
e[i+1] = nil
|
||||||
@ -173,10 +161,6 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
|||||||
"data": map[string]string{"text": o.Content},
|
"data": map[string]string{"text": o.Content},
|
||||||
}
|
}
|
||||||
case *message.LightAppElement:
|
case *message.LightAppElement:
|
||||||
// m = MSG{
|
|
||||||
// "type": "text",
|
|
||||||
// "data": map[string]string{"text": o.Content},
|
|
||||||
// }
|
|
||||||
m = MSG{
|
m = MSG{
|
||||||
"type": "json",
|
"type": "json",
|
||||||
"data": map[string]string{"data": o.Content},
|
"data": map[string]string{"data": o.Content},
|
||||||
@ -209,64 +193,29 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
|||||||
"data": map[string]string{"id": fmt.Sprint(o.Index)},
|
"data": map[string]string{"id": fmt.Sprint(o.Index)},
|
||||||
}
|
}
|
||||||
case *message.VoiceElement:
|
case *message.VoiceElement:
|
||||||
if ur {
|
m = MSG{
|
||||||
m = MSG{
|
"type": "record",
|
||||||
"type": "record",
|
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||||
"data": map[string]string{"file": o.Name},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m = MSG{
|
|
||||||
"type": "record",
|
|
||||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case *message.ShortVideoElement:
|
case *message.ShortVideoElement:
|
||||||
if ur {
|
m = MSG{
|
||||||
m = MSG{
|
"type": "video",
|
||||||
"type": "video",
|
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||||
"data": map[string]string{"file": o.Name},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m = MSG{
|
|
||||||
"type": "video",
|
|
||||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case *message.ImageElement:
|
case *message.ImageElement:
|
||||||
if ur {
|
m = MSG{
|
||||||
m = MSG{
|
"type": "image",
|
||||||
"type": "image",
|
"data": map[string]string{"file": o.Filename, "url": o.Url},
|
||||||
"data": map[string]string{"file": o.Filename},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m = MSG{
|
|
||||||
"type": "image",
|
|
||||||
"data": map[string]string{"file": o.Filename, "url": o.Url},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case *message.GroupImageElement:
|
case *message.GroupImageElement:
|
||||||
if ur {
|
m = MSG{
|
||||||
m = MSG{
|
"type": "image",
|
||||||
"type": "image",
|
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url},
|
||||||
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image"},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m = MSG{
|
|
||||||
"type": "image",
|
|
||||||
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case *message.FriendImageElement:
|
case *message.FriendImageElement:
|
||||||
if ur {
|
m = MSG{
|
||||||
m = MSG{
|
"type": "image",
|
||||||
"type": "image",
|
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url},
|
||||||
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image"},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m = MSG{
|
|
||||||
"type": "image",
|
|
||||||
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case *message.GroupFlashImgElement:
|
case *message.GroupFlashImgElement:
|
||||||
return []MSG{{
|
return []MSG{{
|
||||||
@ -293,13 +242,20 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
|||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r = append(r, m)
|
if m != nil {
|
||||||
|
r = append(r, m)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStringMessage 将消息元素数组转为字符串以用于消息上报
|
// ToStringMessage 将消息元素数组转为字符串以用于消息上报
|
||||||
func ToStringMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r string) {
|
func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r string) {
|
||||||
|
sb := global.NewBuffer()
|
||||||
|
sb.Reset()
|
||||||
|
write := func(format string, a ...interface{}) {
|
||||||
|
_, _ = fmt.Fprintf(sb, format, a...)
|
||||||
|
}
|
||||||
ur := false
|
ur := false
|
||||||
if len(isRaw) != 0 {
|
if len(isRaw) != 0 {
|
||||||
ur = isRaw[0]
|
ur = isRaw[0]
|
||||||
@ -312,67 +268,71 @@ func ToStringMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r st
|
|||||||
})
|
})
|
||||||
if reply != nil {
|
if reply != nil {
|
||||||
replyElem := reply.(*message.ReplyElement)
|
replyElem := reply.(*message.ReplyElement)
|
||||||
if conf.Message.ExtraReplyData {
|
rid := groupID
|
||||||
r += fmt.Sprintf("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
|
if rid == 0 {
|
||||||
toGlobalID(id, replyElem.ReplySeq),
|
rid = replyElem.Sender
|
||||||
|
}
|
||||||
|
if ExtraReplyData {
|
||||||
|
write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
|
||||||
|
toGlobalID(rid, replyElem.ReplySeq),
|
||||||
replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
|
replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
|
||||||
CQCodeEscapeValue(CQCodeEscapeText(ToStringMessage(replyElem.Elements, id))))
|
CQCodeEscapeValue(ToStringMessage(replyElem.Elements, groupID)))
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf("[CQ:reply,id=%d]", toGlobalID(id, replyElem.ReplySeq))
|
write("[CQ:reply,id=%d]", toGlobalID(rid, replyElem.ReplySeq))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, elem := range e {
|
for i, elem := range e {
|
||||||
switch o := elem.(type) {
|
switch o := elem.(type) {
|
||||||
case *message.ReplyElement:
|
case *message.ReplyElement:
|
||||||
if conf.Message.RemoveReplyAt && len(e) > i+1 {
|
if RemoveReplyAt && len(e) > i+1 {
|
||||||
elem, ok := e[i+1].(*message.AtElement)
|
elem, ok := e[i+1].(*message.AtElement)
|
||||||
if ok && elem.Target == o.Sender {
|
if ok && elem.Target == o.Sender {
|
||||||
e[i+1] = nil
|
e[i+1] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *message.TextElement:
|
case *message.TextElement:
|
||||||
r += CQCodeEscapeText(o.Content)
|
sb.WriteString(CQCodeEscapeText(o.Content))
|
||||||
case *message.AtElement:
|
case *message.AtElement:
|
||||||
if o.Target == 0 {
|
if o.Target == 0 {
|
||||||
r += "[CQ:at,qq=all]"
|
write("[CQ:at,qq=all]")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r += fmt.Sprintf("[CQ:at,qq=%d]", o.Target)
|
write("[CQ:at,qq=%d]", o.Target)
|
||||||
case *message.RedBagElement:
|
case *message.RedBagElement:
|
||||||
r += fmt.Sprintf("[CQ:redbag,title=%s]", o.Title)
|
write("[CQ:redbag,title=%s]", o.Title)
|
||||||
case *message.ForwardElement:
|
case *message.ForwardElement:
|
||||||
r += fmt.Sprintf("[CQ:forward,id=%s]", o.ResId)
|
write("[CQ:forward,id=%s]", o.ResId)
|
||||||
case *message.FaceElement:
|
case *message.FaceElement:
|
||||||
r += fmt.Sprintf(`[CQ:face,id=%d]`, o.Index)
|
write(`[CQ:face,id=%d]`, o.Index)
|
||||||
case *message.VoiceElement:
|
case *message.VoiceElement:
|
||||||
if ur {
|
if ur {
|
||||||
r += fmt.Sprintf(`[CQ:record,file=%s]`, o.Name)
|
write(`[CQ:record,file=%s]`, o.Name)
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf(`[CQ:record,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url))
|
write(`[CQ:record,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url))
|
||||||
}
|
}
|
||||||
case *message.ShortVideoElement:
|
case *message.ShortVideoElement:
|
||||||
if ur {
|
if ur {
|
||||||
r += fmt.Sprintf(`[CQ:video,file=%s]`, o.Name)
|
write(`[CQ:video,file=%s]`, o.Name)
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf(`[CQ:video,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url))
|
write(`[CQ:video,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url))
|
||||||
}
|
}
|
||||||
case *message.ImageElement:
|
case *message.ImageElement:
|
||||||
if ur {
|
if ur {
|
||||||
r += fmt.Sprintf(`[CQ:image,file=%s]`, o.Filename)
|
write(`[CQ:image,file=%s]`, o.Filename)
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf(`[CQ:image,file=%s,url=%s]`, o.Filename, CQCodeEscapeValue(o.Url))
|
write(`[CQ:image,file=%s,url=%s]`, o.Filename, CQCodeEscapeValue(o.Url))
|
||||||
}
|
}
|
||||||
case *message.GroupImageElement:
|
case *message.GroupImageElement:
|
||||||
if ur {
|
if ur {
|
||||||
r += fmt.Sprintf("[CQ:image,file=%s]", hex.EncodeToString(o.Md5)+".image")
|
write("[CQ:image,file=%s]", hex.EncodeToString(o.Md5)+".image")
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url))
|
write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url))
|
||||||
}
|
}
|
||||||
case *message.FriendImageElement:
|
case *message.FriendImageElement:
|
||||||
if ur {
|
if ur {
|
||||||
r += fmt.Sprintf("[CQ:image,file=%s]", hex.EncodeToString(o.Md5)+".image")
|
write("[CQ:image,file=%s]", hex.EncodeToString(o.Md5)+".image")
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url))
|
write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url))
|
||||||
}
|
}
|
||||||
case *message.GroupFlashImgElement:
|
case *message.GroupFlashImgElement:
|
||||||
return fmt.Sprintf("[CQ:image,type=flash,file=%s]", o.Filename)
|
return fmt.Sprintf("[CQ:image,type=flash,file=%s]", o.Filename)
|
||||||
@ -380,25 +340,23 @@ func ToStringMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r st
|
|||||||
return fmt.Sprintf("[CQ:image,type=flash,file=%s]", o.Filename)
|
return fmt.Sprintf("[CQ:image,type=flash,file=%s]", o.Filename)
|
||||||
case *message.ServiceElement:
|
case *message.ServiceElement:
|
||||||
if isOk := strings.Contains(o.Content, "<?xml"); isOk {
|
if isOk := strings.Contains(o.Content, "<?xml"); isOk {
|
||||||
r += fmt.Sprintf(`[CQ:xml,data=%s,resid=%d]`, CQCodeEscapeValue(o.Content), o.Id)
|
write(`[CQ:xml,data=%s,resid=%d]`, CQCodeEscapeValue(o.Content), o.Id)
|
||||||
} else {
|
} else {
|
||||||
r += fmt.Sprintf(`[CQ:json,data=%s,resid=%d]`, CQCodeEscapeValue(o.Content), o.Id)
|
write(`[CQ:json,data=%s,resid=%d]`, CQCodeEscapeValue(o.Content), o.Id)
|
||||||
}
|
}
|
||||||
case *message.LightAppElement:
|
case *message.LightAppElement:
|
||||||
r += fmt.Sprintf(`[CQ:json,data=%s]`, CQCodeEscapeValue(o.Content))
|
write(`[CQ:json,data=%s]`, CQCodeEscapeValue(o.Content))
|
||||||
// r += CQCodeEscapeText(o.Content)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r = sb.String() // 内部已拷贝
|
||||||
|
global.PutBuffer(sb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertStringMessage 将消息字符串转为消息元素数组
|
// ConvertStringMessage 将消息字符串转为消息元素数组
|
||||||
func (bot *CQBot) ConvertStringMessage(s string, isGroup bool) (r []message.IMessageElement) {
|
func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IMessageElement) {
|
||||||
var t, key string
|
var t, key string
|
||||||
d := map[string]string{}
|
d := map[string]string{}
|
||||||
ptr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
|
|
||||||
l := len(s)
|
|
||||||
i, j, CQBegin := 0, 0, 0
|
|
||||||
|
|
||||||
saveCQCode := func() {
|
saveCQCode := func() {
|
||||||
if t == "reply" { // reply 特殊处理
|
if t == "reply" { // reply 特殊处理
|
||||||
@ -483,7 +441,11 @@ func (bot *CQBot) ConvertStringMessage(s string, isGroup bool) (r []message.IMes
|
|||||||
}
|
}
|
||||||
elem, err := bot.ToElement(t, d, isGroup)
|
elem, err := bot.ToElement(t, d, isGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
org := s[CQBegin:i]
|
org := "[CQ:" + t
|
||||||
|
for k, v := range d {
|
||||||
|
org += "," + k + "=" + v
|
||||||
|
}
|
||||||
|
org += "]"
|
||||||
if !IgnoreInvalidCQCode {
|
if !IgnoreInvalidCQCode {
|
||||||
log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err)
|
log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err)
|
||||||
r = append(r, message.NewText(org))
|
r = append(r, message.NewText(org))
|
||||||
@ -500,81 +462,65 @@ func (bot *CQBot) ConvertStringMessage(s string, isGroup bool) (r []message.IMes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
S1: // Plain Text
|
for raw != "" {
|
||||||
for ; i < l; i++ {
|
i := 0
|
||||||
if *(*byte)(add(ptr, uintptr(i))) == '[' && i+4 < l &&
|
for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") {
|
||||||
*(*uint32)(add(ptr, uintptr(i))) == magicCQ { // Magic :uint32([]byte("[CQ:"))
|
i++
|
||||||
if i > j {
|
}
|
||||||
if SplitURL {
|
if i > 0 {
|
||||||
for _, str := range global.SplitURL(CQCodeUnescapeText(s[j:i])) {
|
if SplitURL {
|
||||||
r = append(r, message.NewText(str))
|
for _, txt := range global.SplitURL(CQCodeUnescapeText(raw[:i])) {
|
||||||
}
|
r = append(r, message.NewText(txt))
|
||||||
} else {
|
|
||||||
r = append(r, message.NewText(CQCodeUnescapeText(s[j:i])))
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
r = append(r, message.NewText(CQCodeUnescapeText(raw[:i])))
|
||||||
}
|
}
|
||||||
CQBegin = i
|
|
||||||
i += 4
|
|
||||||
j = i
|
|
||||||
goto S2
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
goto End
|
if i+4 > len(raw) {
|
||||||
S2: // CQCode Type
|
return
|
||||||
for k := range d { // 内存复用,减小GC压力
|
|
||||||
delete(d, k)
|
|
||||||
}
|
|
||||||
for ; i < l; i++ {
|
|
||||||
switch *(*byte)(add(ptr, uintptr(i))) {
|
|
||||||
case ',': // CQ Code with params
|
|
||||||
t = s[j:i]
|
|
||||||
i++
|
|
||||||
j = i
|
|
||||||
goto S3
|
|
||||||
case ']': // CQ Code without params
|
|
||||||
t = s[j:i]
|
|
||||||
i++
|
|
||||||
j = i
|
|
||||||
saveCQCode()
|
|
||||||
goto S1
|
|
||||||
}
|
}
|
||||||
}
|
raw = raw[i+4:] // skip "[CQ:"
|
||||||
goto End
|
i = 0
|
||||||
S3: // CQCode param key
|
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||||
for ; i < l; i++ {
|
|
||||||
if *(*byte)(add(ptr, uintptr(i))) == '=' {
|
|
||||||
key = s[j:i]
|
|
||||||
i++
|
i++
|
||||||
j = i
|
|
||||||
goto S4
|
|
||||||
}
|
}
|
||||||
}
|
if i+1 > len(raw) {
|
||||||
goto End
|
return
|
||||||
S4: // CQCode param value
|
|
||||||
for ; i < l; i++ {
|
|
||||||
switch *(*byte)(add(ptr, uintptr(i))) {
|
|
||||||
case ',': // more param
|
|
||||||
d[key] = CQCodeUnescapeValue(s[j:i])
|
|
||||||
i++
|
|
||||||
j = i
|
|
||||||
goto S3
|
|
||||||
case ']':
|
|
||||||
d[key] = CQCodeUnescapeValue(s[j:i])
|
|
||||||
i++
|
|
||||||
j = i
|
|
||||||
saveCQCode()
|
|
||||||
goto S1
|
|
||||||
}
|
}
|
||||||
}
|
t = raw[:i]
|
||||||
goto End
|
for k := range d { // clear the map, reuse it
|
||||||
End:
|
delete(d, k)
|
||||||
if i > j {
|
}
|
||||||
if SplitURL {
|
raw = raw[i:]
|
||||||
for _, str := range global.SplitURL(CQCodeUnescapeText(s[j:i])) {
|
i = 0
|
||||||
r = append(r, message.NewText(str))
|
for {
|
||||||
|
if raw[0] == ']' {
|
||||||
|
saveCQCode()
|
||||||
|
raw = raw[1:]
|
||||||
|
break
|
||||||
}
|
}
|
||||||
} else {
|
raw = raw[1:]
|
||||||
r = append(r, message.NewText(CQCodeUnescapeText(s[j:i])))
|
|
||||||
|
for i < len(raw) && raw[i] != '=' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key = raw[:i]
|
||||||
|
raw = raw[i+1:] // skip "="
|
||||||
|
i = 0
|
||||||
|
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d[key] = CQCodeUnescapeValue(raw[:i])
|
||||||
|
raw = raw[i:]
|
||||||
|
i = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -582,6 +528,7 @@ End:
|
|||||||
|
|
||||||
// ConvertObjectMessage 将消息JSON对象转为消息元素数组
|
// ConvertObjectMessage 将消息JSON对象转为消息元素数组
|
||||||
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []message.IMessageElement) {
|
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []message.IMessageElement) {
|
||||||
|
d := make(map[string]string)
|
||||||
convertElem := func(e gjson.Result) {
|
convertElem := func(e gjson.Result) {
|
||||||
t := e.Get("type").Str
|
t := e.Get("type").Str
|
||||||
if t == "reply" && isGroup {
|
if t == "reply" && isGroup {
|
||||||
@ -665,7 +612,9 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := make(map[string]string)
|
for i := range d {
|
||||||
|
delete(d, i)
|
||||||
|
}
|
||||||
e.Get("data").ForEach(func(key, value gjson.Result) bool {
|
e.Get("data").ForEach(func(key, value gjson.Result) bool {
|
||||||
d[key.Str] = value.String()
|
d[key.Str] = value.String()
|
||||||
return true
|
return true
|
||||||
@ -686,9 +635,10 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
|||||||
return bot.ConvertStringMessage(m.Str, isGroup)
|
return bot.ConvertStringMessage(m.Str, isGroup)
|
||||||
}
|
}
|
||||||
if m.IsArray() {
|
if m.IsArray() {
|
||||||
for _, e := range m.Array() {
|
m.ForEach(func(_, e gjson.Result) bool {
|
||||||
convertElem(e)
|
convertElem(e)
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if m.IsObject() {
|
if m.IsObject() {
|
||||||
convertElem(m)
|
convertElem(m)
|
||||||
@ -747,7 +697,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
if i, ok := img.(*message.GroupImageElement); ok {
|
if i, ok := img.(*message.GroupImageElement); ok {
|
||||||
return &message.GroupShowPicElement{GroupImageElement: *i, EffectId: int32(id)}, nil
|
return &message.GroupShowPicElement{GroupImageElement: *i, EffectId: int32(id)}, nil
|
||||||
}
|
}
|
||||||
return img, nil // 私聊还没做
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
case "poke":
|
case "poke":
|
||||||
@ -774,7 +724,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &message.VoiceElement{Data: data}, nil
|
return &message.VoiceElement{Data: codec.RecodeTo24K(data)}, nil
|
||||||
case "record":
|
case "record":
|
||||||
f := d["file"]
|
f := d["file"]
|
||||||
data, err := global.FindFile(f, d["cache"], global.VoicePath)
|
data, err := global.FindFile(f, d["cache"], global.VoicePath)
|
||||||
@ -819,7 +769,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
if !info.Get("track_info").Exists() {
|
if !info.Get("track_info").Exists() {
|
||||||
return nil, errors.New("song not found")
|
return nil, errors.New("song not found")
|
||||||
}
|
}
|
||||||
aid := strconv.FormatInt(info.Get("track_info.album.id").Int(), 10)
|
|
||||||
name := info.Get("track_info.name").Str
|
name := info.Get("track_info.name").Str
|
||||||
mid := info.Get("track_info.mid").Str
|
mid := info.Get("track_info.mid").Str
|
||||||
albumMid := info.Get("track_info.album.mid").Str
|
albumMid := info.Get("track_info.album.mid").Str
|
||||||
@ -827,9 +776,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare"
|
jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare"
|
||||||
purl := gjson.ParseBytes(pinfo).Get("url_mid.data.midurlinfo.0.purl").Str
|
purl := gjson.ParseBytes(pinfo).Get("url_mid.data.midurlinfo.0.purl").Str
|
||||||
preview := "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg"
|
preview := "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg"
|
||||||
if len(aid) < 2 {
|
|
||||||
return nil, errors.New("song error")
|
|
||||||
}
|
|
||||||
content := info.Get("track_info.singer.0.name").Str
|
content := info.Get("track_info.singer.0.name").Str
|
||||||
if d["content"] != "" {
|
if d["content"] != "" {
|
||||||
content = d["content"]
|
content = d["content"]
|
||||||
@ -870,19 +816,21 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
}
|
}
|
||||||
if d["type"] == "custom" {
|
if d["type"] == "custom" {
|
||||||
if d["subtype"] != "" {
|
if d["subtype"] != "" {
|
||||||
subtype := map[string]int{
|
var subType int
|
||||||
"qq": message.QQMusic,
|
switch d["subtype"] {
|
||||||
"163": message.CloudMusic,
|
default:
|
||||||
"migu": message.MiguMusic,
|
subType = message.QQMusic
|
||||||
"kugou": message.KugouMusic,
|
case "163":
|
||||||
"kuwo": message.KuwoMusic,
|
subType = message.CloudMusic
|
||||||
}
|
case "migu":
|
||||||
musicType := 0
|
subType = message.MiguMusic
|
||||||
if tp, ok := subtype[d["subtype"]]; ok {
|
case "kugou":
|
||||||
musicType = tp
|
subType = message.KugouMusic
|
||||||
|
case "kuwo":
|
||||||
|
subType = message.KuwoMusic
|
||||||
}
|
}
|
||||||
return &message.MusicShareElement{
|
return &message.MusicShareElement{
|
||||||
MusicType: musicType,
|
MusicType: subType,
|
||||||
Title: d["title"],
|
Title: d["title"],
|
||||||
Summary: d["content"],
|
Summary: d["content"],
|
||||||
Url: d["url"],
|
Url: d["url"],
|
||||||
@ -920,22 +868,17 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
source := d["source"]
|
source := d["source"]
|
||||||
icon := d["icon"]
|
icon := d["icon"]
|
||||||
brief := d["brief"]
|
brief := d["brief"]
|
||||||
minWidth, _ := strconv.ParseInt(d["minwidth"], 10, 64)
|
parseIntWithDefault := func(name string, origin int64) int64 {
|
||||||
if minWidth == 0 {
|
v, _ := strconv.ParseInt(d[name], 10, 64)
|
||||||
minWidth = 200
|
if v <= 0 {
|
||||||
}
|
return origin
|
||||||
minHeight, _ := strconv.ParseInt(d["minheight"], 10, 64)
|
}
|
||||||
if minHeight == 0 {
|
return v
|
||||||
minHeight = 200
|
|
||||||
}
|
|
||||||
maxWidth, _ := strconv.ParseInt(d["maxwidth"], 10, 64)
|
|
||||||
if maxWidth == 0 {
|
|
||||||
maxWidth = 500
|
|
||||||
}
|
|
||||||
maxHeight, _ := strconv.ParseInt(d["maxheight"], 10, 64)
|
|
||||||
if maxHeight == 0 {
|
|
||||||
maxHeight = 1000
|
|
||||||
}
|
}
|
||||||
|
minWidth := parseIntWithDefault("minwidth", 200)
|
||||||
|
maxWidth := parseIntWithDefault("maxwidth", 500)
|
||||||
|
minHeight := parseIntWithDefault("minheight", 200)
|
||||||
|
maxHeight := parseIntWithDefault("maxheight", 1000)
|
||||||
img, err := bot.makeImageOrVideoElem(d, false, isGroup)
|
img, err := bot.makeImageOrVideoElem(d, false, isGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("send cardimage faild")
|
return nil, errors.New("send cardimage faild")
|
||||||
@ -1072,7 +1015,7 @@ func CQCodeUnescapeValue(content string) string {
|
|||||||
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用
|
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用
|
||||||
func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (message.IMessageElement, error) {
|
func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (message.IMessageElement, error) {
|
||||||
f := d["file"]
|
f := d["file"]
|
||||||
if strings.HasPrefix(f, "http") || strings.HasPrefix(f, "https") {
|
if strings.HasPrefix(f, "http") {
|
||||||
cache := d["cache"]
|
cache := d["cache"]
|
||||||
c := d["c"]
|
c := d["c"]
|
||||||
if cache == "" {
|
if cache == "" {
|
||||||
@ -1087,10 +1030,11 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
return maxImageSize
|
return maxImageSize
|
||||||
}()
|
}()
|
||||||
thread, _ := strconv.Atoi(c)
|
thread, _ := strconv.Atoi(c)
|
||||||
if global.PathExists(cacheFile) && cache == "1" {
|
exist := global.PathExists(cacheFile)
|
||||||
|
if exist && cache == "1" {
|
||||||
goto hasCacheFile
|
goto hasCacheFile
|
||||||
}
|
}
|
||||||
if global.PathExists(cacheFile) {
|
if exist {
|
||||||
_ = os.Remove(cacheFile)
|
_ = os.Remove(cacheFile)
|
||||||
}
|
}
|
||||||
if err := global.DownloadFileMultiThreading(f, cacheFile, maxSize, thread, nil); err != nil {
|
if err := global.DownloadFileMultiThreading(f, cacheFile, maxSize, thread, nil); err != nil {
|
||||||
@ -1107,7 +1051,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(fu.Path, "/") && runtime.GOOS == `windows` {
|
if runtime.GOOS == `windows` && strings.HasPrefix(fu.Path, "/") {
|
||||||
fu.Path = fu.Path[1:]
|
fu.Path = fu.Path[1:]
|
||||||
}
|
}
|
||||||
info, err := os.Stat(fu.Path)
|
info, err := os.Stat(fu.Path)
|
||||||
@ -1128,6 +1072,13 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
}
|
}
|
||||||
return &LocalImageElement{File: fu.Path}, nil
|
return &LocalImageElement{File: fu.Path}, nil
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(f, "base64") && !video {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(f, "base64://"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &LocalImageElement{Stream: bytes.NewReader(b)}, nil
|
||||||
|
}
|
||||||
rawPath := path.Join(global.ImagePath, f)
|
rawPath := path.Join(global.ImagePath, f)
|
||||||
if video {
|
if video {
|
||||||
rawPath = path.Join(global.VideoPath, f)
|
rawPath = path.Join(global.VideoPath, f)
|
||||||
@ -1148,31 +1099,23 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
}
|
}
|
||||||
return &LocalVideoElement{File: rawPath}, nil
|
return &LocalVideoElement{File: rawPath}, nil
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(f, "base64") {
|
exist := global.PathExists(rawPath)
|
||||||
b, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(f, "base64://", ""))
|
if !exist && global.PathExists(path.Join(global.ImagePathOld, f)) {
|
||||||
if err != nil {
|
exist = true
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &LocalImageElement{Stream: bytes.NewReader(b)}, nil
|
|
||||||
}
|
|
||||||
if !global.PathExists(rawPath) && global.PathExists(path.Join(global.ImagePathOld, f)) {
|
|
||||||
rawPath = path.Join(global.ImagePathOld, f)
|
rawPath = path.Join(global.ImagePathOld, f)
|
||||||
}
|
}
|
||||||
if !global.PathExists(rawPath) && global.PathExists(rawPath+".cqimg") {
|
if !exist && global.PathExists(rawPath+".cqimg") {
|
||||||
|
exist = true
|
||||||
rawPath += ".cqimg"
|
rawPath += ".cqimg"
|
||||||
}
|
}
|
||||||
if !global.PathExists(rawPath) && d["url"] != "" {
|
if !exist && d["url"] != "" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group)
|
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group)
|
||||||
}
|
}
|
||||||
if global.PathExists(rawPath) {
|
if exist {
|
||||||
file, err := os.Open(rawPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if path.Ext(rawPath) != ".image" && path.Ext(rawPath) != ".cqimg" {
|
if path.Ext(rawPath) != ".image" && path.Ext(rawPath) != ".cqimg" {
|
||||||
return &LocalImageElement{Stream: file}, nil
|
return &LocalImageElement{File: rawPath}, nil
|
||||||
}
|
}
|
||||||
b, err := ioutil.ReadAll(file)
|
b, err := os.ReadFile(rawPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1180,9 +1123,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
return nil, errors.New("invalid local file")
|
return nil, errors.New("invalid local file")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
size int32
|
size int32
|
||||||
hash []byte
|
hash []byte
|
||||||
url string
|
imageURL string
|
||||||
)
|
)
|
||||||
if path.Ext(rawPath) == ".cqimg" {
|
if path.Ext(rawPath) == ".cqimg" {
|
||||||
for _, line := range strings.Split(global.ReadAllText(rawPath), "\n") {
|
for _, line := range strings.Split(global.ReadAllText(rawPath), "\n") {
|
||||||
@ -1200,11 +1143,11 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
hash = r.ReadBytes(16)
|
hash = r.ReadBytes(16)
|
||||||
size = r.ReadInt32()
|
size = r.ReadInt32()
|
||||||
r.ReadString()
|
r.ReadString()
|
||||||
url = r.ReadString()
|
imageURL = r.ReadString()
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
if url != "" {
|
if imageURL != "" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": url}, false, group)
|
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
||||||
}
|
}
|
||||||
return nil, errors.New("img size is 0")
|
return nil, errors.New("img size is 0")
|
||||||
}
|
}
|
||||||
@ -1219,8 +1162,8 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
||||||
ok:
|
ok:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if url != "" {
|
if imageURL != "" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": url}, false, group)
|
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var bot = NewQQBot(client.NewClient(1, ""), global.DefaultConfig())
|
var bot = CQBot{}
|
||||||
|
|
||||||
func TestCQBot_ConvertStringMessage(t *testing.T) {
|
func TestCQBot_ConvertStringMessage(t *testing.T) {
|
||||||
for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, false) {
|
for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, false) {
|
||||||
@ -16,10 +15,18 @@ func TestCQBot_ConvertStringMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bench = `asdfqwerqwerqwer[CQ:face,id=115,text=111]asdfasdfasdfasdfasdfasdfasd[CQ:face,id=217]] [CQ:text,text=123] [`
|
var bench = `asdfqwerqwerqwer[CQ:face,id=115,text=111]asdfasdfasdfasdfasdfasdfasd[CQ:face,id=217]] 123 [`
|
||||||
|
var benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`)
|
||||||
|
|
||||||
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
|
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bot.ConvertStringMessage(bench, false)
|
bot.ConvertStringMessage(bench, false)
|
||||||
}
|
}
|
||||||
|
b.SetBytes(int64(len(bench)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCQBot_ConvertObjectMessage(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bot.ConvertObjectMessage(benchArray, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,18 +24,18 @@ func SetMessageFormat(f string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式
|
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式
|
||||||
func ToFormattedMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r interface{}) {
|
func ToFormattedMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r interface{}) {
|
||||||
if format == "string" {
|
if format == "string" {
|
||||||
r = ToStringMessage(e, id, isRaw...)
|
r = ToStringMessage(e, groupID, isRaw...)
|
||||||
} else if format == "array" {
|
} else if format == "array" {
|
||||||
r = ToArrayMessage(e, id, isRaw...)
|
r = ToArrayMessage(e, groupID)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
|
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
|
||||||
bot.checkMedia(m.Elements)
|
bot.checkMedia(m.Elements)
|
||||||
cqm := ToStringMessage(m.Elements, m.Sender.Uin, true)
|
cqm := ToStringMessage(m.Elements, 0, true)
|
||||||
if !m.Sender.IsFriend {
|
if !m.Sender.IsFriend {
|
||||||
bot.oneWayMsgCache.Store(m.Sender.Uin, "")
|
bot.oneWayMsgCache.Store(m.Sender.Uin, "")
|
||||||
}
|
}
|
||||||
@ -45,13 +45,18 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
|
|||||||
}
|
}
|
||||||
log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
|
log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
|
||||||
fm := MSG{
|
fm := MSG{
|
||||||
"post_type": "message",
|
"post_type": func() string {
|
||||||
|
if m.Sender.Uin == bot.Client.Uin {
|
||||||
|
return "message_sent"
|
||||||
|
}
|
||||||
|
return "message"
|
||||||
|
}(),
|
||||||
"message_type": "private",
|
"message_type": "private",
|
||||||
"sub_type": "friend",
|
"sub_type": "friend",
|
||||||
"message_id": id,
|
"message_id": id,
|
||||||
"user_id": m.Sender.Uin,
|
"user_id": m.Sender.Uin,
|
||||||
"target_id": m.Target,
|
"target_id": m.Target,
|
||||||
"message": ToFormattedMessage(m.Elements, m.Sender.Uin, false),
|
"message": ToFormattedMessage(m.Elements, 0, false),
|
||||||
"raw_message": cqm,
|
"raw_message": cqm,
|
||||||
"font": 0,
|
"font": 0,
|
||||||
"self_id": c.Uin,
|
"self_id": c.Uin,
|
||||||
@ -64,6 +69,9 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
bot.dispatchEventMessage(fm)
|
bot.dispatchEventMessage(fm)
|
||||||
|
if m.Sender.Uin != c.Uin {
|
||||||
|
c.MarkPrivateMessageReaded(m.Sender.Uin, int64(m.Time))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) {
|
func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) {
|
||||||
@ -101,12 +109,15 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
|
|||||||
}
|
}
|
||||||
gm["message_id"] = id
|
gm["message_id"] = id
|
||||||
bot.dispatchEventMessage(gm)
|
bot.dispatchEventMessage(gm)
|
||||||
|
if m.Sender.Uin != c.Uin {
|
||||||
|
c.MarkGroupMessageReaded(m.GroupCode, int64(m.Id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
|
func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
|
||||||
m := e.Message
|
m := e.Message
|
||||||
bot.checkMedia(m.Elements)
|
bot.checkMedia(m.Elements)
|
||||||
cqm := ToStringMessage(m.Elements, m.Sender.Uin, true)
|
cqm := ToStringMessage(m.Elements, 0, true)
|
||||||
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
|
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
|
||||||
id := m.Id
|
id := m.Id
|
||||||
if bot.db != nil {
|
if bot.db != nil {
|
||||||
@ -120,7 +131,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
|||||||
"temp_source": e.Session.Source,
|
"temp_source": e.Session.Source,
|
||||||
"message_id": id,
|
"message_id": id,
|
||||||
"user_id": m.Sender.Uin,
|
"user_id": m.Sender.Uin,
|
||||||
"message": ToFormattedMessage(m.Elements, m.Sender.Uin, false),
|
"message": ToFormattedMessage(m.Elements, 0, false),
|
||||||
"raw_message": cqm,
|
"raw_message": cqm,
|
||||||
"font": 0,
|
"font": 0,
|
||||||
"self_id": c.Uin,
|
"self_id": c.Uin,
|
||||||
@ -355,12 +366,12 @@ func (bot *CQBot) memberCardUpdatedEvent(c *client.QQClient, e *client.MemberCar
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) memberJoinEvent(c *client.QQClient, e *client.MemberJoinGroupEvent) {
|
func (bot *CQBot) memberJoinEvent(_ *client.QQClient, e *client.MemberJoinGroupEvent) {
|
||||||
log.Infof("新成员 %v 进入了群 %v.", formatMemberName(e.Member), formatGroupName(e.Group))
|
log.Infof("新成员 %v 进入了群 %v.", formatMemberName(e.Member), formatGroupName(e.Group))
|
||||||
bot.dispatchEventMessage(bot.groupIncrease(e.Group.Code, 0, e.Member.Uin))
|
bot.dispatchEventMessage(bot.groupIncrease(e.Group.Code, 0, e.Member.Uin))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) memberLeaveEvent(c *client.QQClient, e *client.MemberLeaveGroupEvent) {
|
func (bot *CQBot) memberLeaveEvent(_ *client.QQClient, e *client.MemberLeaveGroupEvent) {
|
||||||
if e.Operator != nil {
|
if e.Operator != nil {
|
||||||
log.Infof("成员 %v 被 %v T出了群 %v.", formatMemberName(e.Member), formatMemberName(e.Operator), formatGroupName(e.Group))
|
log.Infof("成员 %v 被 %v T出了群 %v.", formatMemberName(e.Member), formatMemberName(e.Operator), formatGroupName(e.Group))
|
||||||
} else {
|
} else {
|
||||||
@ -539,7 +550,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
w.WriteUInt32(uint32(i.Size))
|
w.WriteUInt32(uint32(i.Size))
|
||||||
w.WriteString(i.Filename)
|
w.WriteString(i.Filename)
|
||||||
w.WriteString(i.Url)
|
w.WriteString(i.Url)
|
||||||
}), 0644)
|
}), 0o644)
|
||||||
}
|
}
|
||||||
i.Filename = filename
|
i.Filename = filename
|
||||||
case *message.GroupImageElement:
|
case *message.GroupImageElement:
|
||||||
@ -550,7 +561,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
w.WriteUInt32(uint32(i.Size))
|
w.WriteUInt32(uint32(i.Size))
|
||||||
w.WriteString(filename)
|
w.WriteString(filename)
|
||||||
w.WriteString(i.Url)
|
w.WriteString(i.Url)
|
||||||
}), 0644)
|
}), 0o644)
|
||||||
}
|
}
|
||||||
case *message.FriendImageElement:
|
case *message.FriendImageElement:
|
||||||
filename := hex.EncodeToString(i.Md5) + ".image"
|
filename := hex.EncodeToString(i.Md5) + ".image"
|
||||||
@ -560,7 +571,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
w.WriteUInt32(uint32(0)) // 发送时会调用url, 大概没事
|
w.WriteUInt32(uint32(0)) // 发送时会调用url, 大概没事
|
||||||
w.WriteString(filename)
|
w.WriteString(filename)
|
||||||
w.WriteString(i.Url)
|
w.WriteString(i.Url)
|
||||||
}), 0644)
|
}), 0o644)
|
||||||
}
|
}
|
||||||
case *message.GroupFlashImgElement:
|
case *message.GroupFlashImgElement:
|
||||||
filename := hex.EncodeToString(i.Md5) + ".image"
|
filename := hex.EncodeToString(i.Md5) + ".image"
|
||||||
@ -570,7 +581,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
w.WriteUInt32(uint32(i.Size))
|
w.WriteUInt32(uint32(i.Size))
|
||||||
w.WriteString(i.Filename)
|
w.WriteString(i.Filename)
|
||||||
w.WriteString("")
|
w.WriteString("")
|
||||||
}), 0644)
|
}), 0o644)
|
||||||
}
|
}
|
||||||
i.Filename = filename
|
i.Filename = filename
|
||||||
case *message.FriendFlashImgElement:
|
case *message.FriendFlashImgElement:
|
||||||
@ -581,7 +592,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
w.WriteUInt32(uint32(i.Size))
|
w.WriteUInt32(uint32(i.Size))
|
||||||
w.WriteString(i.Filename)
|
w.WriteString(i.Filename)
|
||||||
w.WriteString("")
|
w.WriteString("")
|
||||||
}), 0644)
|
}), 0o644)
|
||||||
}
|
}
|
||||||
i.Filename = filename
|
i.Filename = filename
|
||||||
case *message.VoiceElement:
|
case *message.VoiceElement:
|
||||||
@ -593,7 +604,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
log.Warnf("语音文件 %v 下载失败: %v", i.Name, err)
|
log.Warnf("语音文件 %v 下载失败: %v", i.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_ = ioutil.WriteFile(path.Join(global.VoicePath, i.Name), b, 0644)
|
_ = ioutil.WriteFile(path.Join(global.VoicePath, i.Name), b, 0o644)
|
||||||
}
|
}
|
||||||
case *message.ShortVideoElement:
|
case *message.ShortVideoElement:
|
||||||
filename := hex.EncodeToString(i.Md5) + ".video"
|
filename := hex.EncodeToString(i.Md5) + ".video"
|
||||||
@ -605,7 +616,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
|||||||
w.WriteUInt32(uint32(i.ThumbSize))
|
w.WriteUInt32(uint32(i.ThumbSize))
|
||||||
w.WriteString(i.Name)
|
w.WriteString(i.Name)
|
||||||
w.Write(i.Uuid)
|
w.Write(i.Uuid)
|
||||||
}), 0644)
|
}), 0o644)
|
||||||
}
|
}
|
||||||
i.Name = filename
|
i.Name = filename
|
||||||
i.Url = bot.Client.GetShortVideoUrl(i.Uuid, i.Md5)
|
i.Url = bot.Client.GetShortVideoUrl(i.Uuid, i.Md5)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# 事件过滤器
|
# 事件过滤器
|
||||||
|
|
||||||
在go-cqhttp同级目录下新建`filter.json`文件即可开启事件过滤器,启动时会读取该文件中定义的过滤规则(使用 JSON 编写),若文件不存在,或过滤规则语法错误,则不会启用事件过滤器。
|
在配置文件填写对应通信方式的 `middlewares.filter` 即可开启事件过滤器,启动时会读取该文件中定义的过滤规则(使用 JSON 编写),若文件不存在,或过滤规则语法错误,则不会启用事件过滤器。
|
||||||
事件过滤器会处理所有事件(包括心跳事件在内的元事件),请谨慎使用!!
|
事件过滤器会处理所有事件(包括心跳事件在内的元事件),请谨慎使用!!
|
||||||
|
|
||||||
注意: 与客户端建立连接的握手事件**不会**经过事件过滤器
|
注意: 与客户端建立连接的握手事件**不会**经过事件过滤器
|
||||||
@ -114,6 +114,35 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 进阶指南
|
||||||
|
|
||||||
|
1. 对于嵌套的值,可以使用 `.` 进行简化,如
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sender": {
|
||||||
|
"sex": "male"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
与下面的配置文件作用相同
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sender.sex": "male"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 对于数组,可以使用数字索引,如
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message.0.type": "text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
更多进阶语法请参考[GJSON语法](https://github.com/tidwall/gjson/blob/master/SYNTAX.md)
|
||||||
|
|
||||||
## 语法说明
|
## 语法说明
|
||||||
|
|
||||||
过滤规则最外层是一个 JSON 对象,其中的键,如果以 `.`(点号)开头,则表示运算符,其值为运算符的参数,如果不以 `.` 开头,则表示对事件数据对象中相应键的过滤。过滤规则中任何一个对象,只有在它的所有项都匹配的情况下,才会让事件通过(等价于一个 `and` 运算);其中,不以 `.` 开头的键,若其值不是对象,则只有在这个值和事件数据相应值相等的情况下,才会通过(等价于一个 `eq` 运算符)。
|
过滤规则最外层是一个 JSON 对象,其中的键,如果以 `.`(点号)开头,则表示运算符,其值为运算符的参数,如果不以 `.` 开头,则表示对事件数据对象中相应键的过滤。过滤规则中任何一个对象,只有在它的所有项都匹配的情况下,才会让事件通过(等价于一个 `and` 运算);其中,不以 `.` 开头的键,若其值不是对象,则只有在这个值和事件数据相应值相等的情况下,才会通过(等价于一个 `eq` 运算符)。
|
||||||
|
12
docs/README.md
Normal file
12
docs/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# 文档
|
||||||
|
|
||||||
|
> 文档目前依旧保留以便往前兼容
|
||||||
|
\
|
||||||
|
下面的文档更易读以及人性化, 强烈建议您查看下面提供的文档
|
||||||
|
|
||||||
|
目前文档已移动到位于 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs) 的仓库
|
||||||
|
|
||||||
|
您可以在以下其中任意一个链接查看:
|
||||||
|
|
||||||
|
- <https://docs.go-cqhttp.org>
|
||||||
|
- <https://ishkong.github.io/go-cqhttp-docs>
|
@ -15,8 +15,9 @@ account: # 账号相关
|
|||||||
uin: 1233456 # QQ账号
|
uin: 1233456 # QQ账号
|
||||||
password: '' # 密码为空时使用扫码登录
|
password: '' # 密码为空时使用扫码登录
|
||||||
encrypt: false # 是否开启密码加密
|
encrypt: false # 是否开启密码加密
|
||||||
relogin: # 重连设置
|
status: 0 # 在线状态,详情请查看下方的在线状态表
|
||||||
disabled: false
|
relogin: # 重连设置
|
||||||
|
# disabled: false
|
||||||
delay: 3 # 重连延迟, 单位秒
|
delay: 3 # 重连延迟, 单位秒
|
||||||
interval: 0 # 重连间隔
|
interval: 0 # 重连间隔
|
||||||
max-times: 0 # 最大重连次数, 0为无限制
|
max-times: 0 # 最大重连次数, 0为无限制
|
||||||
@ -26,7 +27,7 @@ account: # 账号相关
|
|||||||
use-sso-address: true
|
use-sso-address: true
|
||||||
|
|
||||||
heartbeat:
|
heartbeat:
|
||||||
disabled: false # 是否开启心跳事件上报
|
# disabled: false # 是否开启心跳事件上报
|
||||||
# 心跳频率, 单位秒
|
# 心跳频率, 单位秒
|
||||||
# -1 为关闭心跳
|
# -1 为关闭心跳
|
||||||
interval: 5
|
interval: 5
|
||||||
@ -49,7 +50,7 @@ message:
|
|||||||
report-self-message: false
|
report-self-message: false
|
||||||
|
|
||||||
output:
|
output:
|
||||||
# 日志等级 trace,debug,info,warn,error日志等级 trace,debug,info,warn,error
|
# 日志等级 trace,debug,info,warn,error
|
||||||
log-level: warn
|
log-level: warn
|
||||||
# 是否启用 DEBUG
|
# 是否启用 DEBUG
|
||||||
debug: false # 开启调试模式
|
debug: false # 开启调试模式
|
||||||
@ -73,8 +74,6 @@ default-middlewares: &default
|
|||||||
servers:
|
servers:
|
||||||
# HTTP 通信设置
|
# HTTP 通信设置
|
||||||
- http:
|
- http:
|
||||||
# 是否关闭正向HTTP服务器
|
|
||||||
disabled: false
|
|
||||||
# 服务端监听地址
|
# 服务端监听地址
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
# 服务端监听端口
|
# 服务端监听端口
|
||||||
@ -93,8 +92,6 @@ servers:
|
|||||||
|
|
||||||
# 正向WS设置
|
# 正向WS设置
|
||||||
- ws:
|
- ws:
|
||||||
# 是否禁用正向WS服务器
|
|
||||||
disabled: true
|
|
||||||
# 正向WS服务器监听地址
|
# 正向WS服务器监听地址
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
# 正向WS服务器监听端口
|
# 正向WS服务器监听端口
|
||||||
@ -103,8 +100,6 @@ servers:
|
|||||||
<<: *default # 引用默认中间件
|
<<: *default # 引用默认中间件
|
||||||
|
|
||||||
- ws-reverse:
|
- ws-reverse:
|
||||||
# 是否禁用当前反向WS服务
|
|
||||||
disabled: true
|
|
||||||
# 反向WS Universal 地址
|
# 反向WS Universal 地址
|
||||||
# 注意 设置了此项地址后下面两项将会被忽略
|
# 注意 设置了此项地址后下面两项将会被忽略
|
||||||
universal: ws://your_websocket_universal.server
|
universal: ws://your_websocket_universal.server
|
||||||
@ -116,6 +111,14 @@ servers:
|
|||||||
reconnect-interval: 3000
|
reconnect-interval: 3000
|
||||||
middlewares:
|
middlewares:
|
||||||
<<: *default # 引用默认中间件
|
<<: *default # 引用默认中间件
|
||||||
|
# pprof 性能分析服务器, 一般情况下不需要启用.
|
||||||
|
# 如果遇到性能问题请上传报告给开发者处理
|
||||||
|
# 注意: pprof服务不支持中间件、不支持鉴权. 请不要开放到公网
|
||||||
|
- pprof:
|
||||||
|
# pprof服务器监听地址
|
||||||
|
host: 127.0.0.1
|
||||||
|
# pprof服务器监听端口
|
||||||
|
port: 7700
|
||||||
|
|
||||||
# 可添加更多
|
# 可添加更多
|
||||||
#- ws-reverse:
|
#- ws-reverse:
|
||||||
@ -130,13 +133,42 @@ database: # 数据库相关设置
|
|||||||
enable: true
|
enable: true
|
||||||
````
|
````
|
||||||
|
|
||||||
> 注: 开启密码加密后程序将在每次启动时要求输入解密密钥, 密钥错误会导致登录时提示密码错误.
|
> 注1: 开启密码加密后程序将在每次启动时要求输入解密密钥, 密钥错误会导致登录时提示密码错误.
|
||||||
> 解密后密码的哈希将储存在内存中,用于自动重连等功能. 所以此加密并不能防止内存读取.
|
> 解密后密码的哈希将储存在内存中,用于自动重连等功能. 所以此加密并不能防止内存读取.
|
||||||
> 解密密钥在使用完成后并不会留存在内存中, 所以可用相对简单的字符串作为密钥
|
> 解密密钥在使用完成后并不会留存在内存中, 所以可用相对简单的字符串作为密钥
|
||||||
|
|
||||||
> 注2: 分片发送为原酷Q发送长消息的老方案, 发送速度更优/兼容性更好,但在有发言频率限制的群里,可能无法发送。关闭后将优先使用新方案, 能发送更长的消息, 但发送速度更慢,在部分老客户端将无法解析.
|
> 注2: 对于不需要的通信方式,你可以使用注释将其停用(推荐),或者添加配置 `disabled: true` 将其关闭
|
||||||
|
|
||||||
> 注3:关闭心跳服务可能引起断线,请谨慎关闭
|
> 注3: 分片发送为原酷Q发送长消息的老方案, 发送速度更优/兼容性更好,但在有发言频率限制的群里,可能无法发送。关闭后将优先使用新方案, 能发送更长的消息, 但发送速度更慢,在部分老客户端将无法解析.
|
||||||
|
|
||||||
|
> 注4:关闭心跳服务可能引起断线,请谨慎关闭
|
||||||
|
|
||||||
|
## 在线状态
|
||||||
|
|
||||||
|
| 状态 | 值 |
|
||||||
|
| -----|----|
|
||||||
|
| 在线 | 0 |
|
||||||
|
| 离开 | 1 |
|
||||||
|
| 隐身 | 2 |
|
||||||
|
| 忙 | 3 |
|
||||||
|
| 听歌中 | 4 |
|
||||||
|
| 星座运势 | 5 |
|
||||||
|
| 今日天气 | 6 |
|
||||||
|
| 遇见春天 | 7 |
|
||||||
|
| Timi中 | 8 |
|
||||||
|
| 吃鸡中 | 9 |
|
||||||
|
| 恋爱中 | 10 |
|
||||||
|
| 汪汪汪 | 11 |
|
||||||
|
| 干饭中 | 12 |
|
||||||
|
| 学习中 | 13 |
|
||||||
|
| 熬夜中 | 14 |
|
||||||
|
| 打球中 | 15 |
|
||||||
|
| 信号弱 | 16 |
|
||||||
|
| 在线学习 | 17 |
|
||||||
|
| 游戏中 | 18 |
|
||||||
|
| 度假中 | 19 |
|
||||||
|
| 追剧中 | 20 |
|
||||||
|
| 健身中 | 21 |
|
||||||
|
|
||||||
## 设备信息
|
## 设备信息
|
||||||
|
|
||||||
@ -161,6 +193,7 @@ database: # 数据库相关设置
|
|||||||
| 1 | Android Phone | 无 |
|
| 1 | Android Phone | 无 |
|
||||||
| 2 | Android Watch | 无法接收 `notify` 事件、无法接收口令红包、无法接收撤回消息 |
|
| 2 | Android Watch | 无法接收 `notify` 事件、无法接收口令红包、无法接收撤回消息 |
|
||||||
| 3 | MacOS | 无 |
|
| 3 | MacOS | 无 |
|
||||||
|
| 4 | 企点 | 只能登录企点账号或企点子账号 |
|
||||||
|
|
||||||
> 注意, 根据协议的不同, 各类消息有所限制
|
> 注意, 根据协议的不同, 各类消息有所限制
|
||||||
|
|
||||||
|
@ -1050,6 +1050,32 @@ JSON数组:
|
|||||||
|
|
||||||
`该 API 没有响应数据`
|
`该 API 没有响应数据`
|
||||||
|
|
||||||
|
### 删除好友
|
||||||
|
|
||||||
|
终结点: `/delete_friend`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||||
|
| ---------- | -------- | ------ | -------- |
|
||||||
|
| `id` | int64 | | 好友ID |
|
||||||
|
|
||||||
|
`该 API 没有响应数据`
|
||||||
|
|
||||||
|
### 获取企点账号信息
|
||||||
|
|
||||||
|
> 该API只有企点协议可用
|
||||||
|
|
||||||
|
终结点: `/qidian_get_account_info`
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| ------------------ | ------- | ------------ |
|
||||||
|
| `master_id` | int64 | 父账号ID |
|
||||||
|
| `ext_name` | string | 用户昵称 |
|
||||||
|
| `create_time` | int64 | 账号创建时间 |
|
||||||
|
|
||||||
### 重载事件过滤器
|
### 重载事件过滤器
|
||||||
|
|
||||||
终结点:`/reload_event_filter`
|
终结点:`/reload_event_filter`
|
||||||
|
36
global/all_test.go
Normal file
36
global/all_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVersionNameCompare(t *testing.T) {
|
||||||
|
var tests = [...]struct {
|
||||||
|
current string
|
||||||
|
remote string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
// Normal Tests:
|
||||||
|
{"v0.9.29-fix2", "v0.9.29-fix2", false},
|
||||||
|
{"v0.9.29-fix1", "v0.9.29-fix2", true},
|
||||||
|
{"v0.9.29-fix2", "v0.9.29-fix1", false},
|
||||||
|
{"v0.9.29-fix2", "v0.9.30", true},
|
||||||
|
{"v1.0.0-alpha", "v1.0.0-alpha2", true},
|
||||||
|
{"v1.0.0-alpha2", "v1.0.0-beta1", true},
|
||||||
|
{"v1.0.0", "v1.0.0-beta1", false},
|
||||||
|
{"v1.0.0-alpha", "v1.0.0", true},
|
||||||
|
{"v1.0.0", "v1.0.0", false},
|
||||||
|
{"v1.0.0-alpha", "v1.0.0-rc1", true},
|
||||||
|
|
||||||
|
// Issue Fixes:
|
||||||
|
{"v1.0.0-beta1", "v0.9.40-fix5", false}, // issue #877
|
||||||
|
}
|
||||||
|
for i := 0; i < len(tests); i++ {
|
||||||
|
t.Run("test case "+strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
assert.Equal(t, tests[i].expected, VersionNameCompare(tests[i].current, tests[i].remote))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,20 @@ func EncodeToSilk(record []byte, tempName string, useCache bool) (silkWav []byte
|
|||||||
}
|
}
|
||||||
if useCache {
|
if useCache {
|
||||||
silkPath := path.Join(silkCachePath, tempName+".silk")
|
silkPath := path.Join(silkCachePath, tempName+".silk")
|
||||||
err = ioutil.WriteFile(silkPath, silkWav, 0666)
|
err = ioutil.WriteFile(silkPath, silkWav, 0o666)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecodeTo24K 将silk重新编码为 24000 bit rate
|
||||||
|
func RecodeTo24K(data []byte) []byte {
|
||||||
|
pcm, err := silk.DecodeSilkBuffToPcm(data, 24000)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
data, err = silk.EncodePcmBuffToSilk(pcm, 24000, 24000, true)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
@ -4,7 +4,12 @@ package codec
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
//EncodeToSilk 将音频编码为Silk
|
// EncodeToSilk 将音频编码为Silk
|
||||||
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
||||||
return nil, errors.New("not supported now")
|
return nil, errors.New("not supported now")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecodeTo24K 将silk重新编码为 24000 bit rate
|
||||||
|
func RecodeTo24K(data []byte) []byte {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
@ -4,7 +4,12 @@ package codec
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
//EncodeToSilk 将音频编码为Silk
|
// EncodeToSilk 将音频编码为Silk
|
||||||
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
||||||
return nil, errors.New("not supported now")
|
return nil, errors.New("not supported now")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecodeTo24K 将silk重新编码为 24000 bit rate
|
||||||
|
func RecodeTo24K(data []byte) []byte {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
@ -2,7 +2,12 @@ package codec
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
//EncodeToSilk 将音频编码为Silk
|
// EncodeToSilk 将音频编码为Silk
|
||||||
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
||||||
return nil, errors.New("not supported now")
|
return nil, errors.New("not supported now")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecodeTo24K 将silk重新编码为 24000 bit rate
|
||||||
|
func RecodeTo24K(data []byte) []byte {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
// AccountToken 存储AccountToken供登录使用
|
|
||||||
var AccountToken []byte
|
|
||||||
|
|
||||||
// PasswordHash 存储QQ密码哈希供登录使用
|
|
||||||
var PasswordHash [16]byte
|
|
||||||
|
|
||||||
/*
|
|
||||||
// GetCurrentPath 预留,获取当前目录地址
|
|
||||||
func GetCurrentPath() (string, error) {
|
|
||||||
file, err := exec.LookPath(os.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
fpath, err := filepath.Abs(file)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// fpath = strings.Replace(fpath, "\\", "/", -1)
|
|
||||||
fpath = strings.ReplaceAll(fpath, "\\", "/")
|
|
||||||
}
|
|
||||||
i := strings.LastIndex(fpath, "/")
|
|
||||||
if i < 0 {
|
|
||||||
return "", errors.New("system/path_error,Can't find '/' or '\\'")
|
|
||||||
}
|
|
||||||
return fpath[0 : i+1], nil
|
|
||||||
}
|
|
||||||
*/
|
|
@ -2,17 +2,21 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
_ "embed" // embed the default config file
|
_ "embed" // embed the default config file
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultConfig 默认配置文件
|
// defaultConfig 默认配置文件
|
||||||
//go:embed default_config.yml
|
//go:embed default_config.yml
|
||||||
var DefaultConfig string
|
var defaultConfig string
|
||||||
|
|
||||||
var currentPath = getCurrentPath()
|
var currentPath = getCurrentPath()
|
||||||
|
|
||||||
@ -25,9 +29,10 @@ type Config struct {
|
|||||||
Uin int64 `yaml:"uin"`
|
Uin int64 `yaml:"uin"`
|
||||||
Password string `yaml:"password"`
|
Password string `yaml:"password"`
|
||||||
Encrypt bool `yaml:"encrypt"`
|
Encrypt bool `yaml:"encrypt"`
|
||||||
|
Status int32 `yaml:"status"`
|
||||||
ReLogin struct {
|
ReLogin struct {
|
||||||
Disabled bool `yaml:"disabled"`
|
Disabled bool `yaml:"disabled"`
|
||||||
Delay int `yaml:"delay"`
|
Delay uint `yaml:"delay"`
|
||||||
MaxTimes uint `yaml:"max-times"`
|
MaxTimes uint `yaml:"max-times"`
|
||||||
Interval int `yaml:"interval"`
|
Interval int `yaml:"interval"`
|
||||||
}
|
}
|
||||||
@ -84,6 +89,13 @@ type HTTPServer struct {
|
|||||||
MiddleWares `yaml:"middlewares"`
|
MiddleWares `yaml:"middlewares"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PprofServer pprof性能分析服务器相关配置
|
||||||
|
type PprofServer struct {
|
||||||
|
Disabled bool `yaml:"disabled"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
// WebsocketServer 正向WS相关配置
|
// WebsocketServer 正向WS相关配置
|
||||||
type WebsocketServer struct {
|
type WebsocketServer struct {
|
||||||
Disabled bool `yaml:"disabled"`
|
Disabled bool `yaml:"disabled"`
|
||||||
@ -109,17 +121,25 @@ type LevelDBConfig struct {
|
|||||||
Enable bool `yaml:"enable"`
|
Enable bool `yaml:"enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
config *Config
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
// Get 从默认配置文件路径中获取
|
// Get 从默认配置文件路径中获取
|
||||||
func Get() *Config {
|
func Get() *Config {
|
||||||
file, err := os.Open(DefaultConfigFile)
|
once.Do(func() {
|
||||||
if err != nil {
|
file, err := os.Open(DefaultConfigFile)
|
||||||
log.Error("获取配置文件失败: ", err)
|
if err != nil {
|
||||||
return nil
|
generateConfig()
|
||||||
}
|
os.Exit(0)
|
||||||
config := &Config{}
|
}
|
||||||
if yaml.NewDecoder(file).Decode(config) != nil {
|
defer func() { _ = file.Close() }()
|
||||||
log.Fatal("配置文件不合法!", err)
|
config = &Config{}
|
||||||
}
|
if err = yaml.NewDecoder(file).Decode(config); err != nil {
|
||||||
|
log.Fatal("配置文件不合法!", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,3 +151,91 @@ func getCurrentPath() string {
|
|||||||
}
|
}
|
||||||
return cwd
|
return cwd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateConfig 生成配置文件
|
||||||
|
func generateConfig() {
|
||||||
|
fmt.Println("未找到配置文件,正在为您生成配置文件中!")
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString(defaultConfig)
|
||||||
|
fmt.Print(`请选择你需要的通信方式:
|
||||||
|
> 1: HTTP通信
|
||||||
|
> 2: 正向 Websocket 通信
|
||||||
|
> 3: 反向 Websocket 通信
|
||||||
|
> 4: pprof 性能分析服务器
|
||||||
|
请输入你需要的编号,可输入多个,同一编号也可输入多个(如: 233)
|
||||||
|
您的选择是:`)
|
||||||
|
input := bufio.NewReader(os.Stdin)
|
||||||
|
readString, err := input.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("输入不合法: ", err)
|
||||||
|
}
|
||||||
|
for _, r := range readString {
|
||||||
|
switch r {
|
||||||
|
case '1':
|
||||||
|
sb.WriteString(httpDefault)
|
||||||
|
case '2':
|
||||||
|
sb.WriteString(wsDefault)
|
||||||
|
case '3':
|
||||||
|
sb.WriteString(wsReverseDefault)
|
||||||
|
case '4':
|
||||||
|
sb.WriteString(pprofDefault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = os.WriteFile("config.yml", []byte(sb.String()), 0o644)
|
||||||
|
fmt.Println("默认配置文件已生成,请修改 config.yml 后重新启动!")
|
||||||
|
_, _ = input.ReadString('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpDefault = ` # HTTP 通信设置
|
||||||
|
- http:
|
||||||
|
# 服务端监听地址
|
||||||
|
host: 127.0.0.1
|
||||||
|
# 服务端监听端口
|
||||||
|
port: 5700
|
||||||
|
# 反向HTTP超时时间, 单位秒
|
||||||
|
# 最小值为5,小于5将会忽略本项设置
|
||||||
|
timeout: 5
|
||||||
|
middlewares:
|
||||||
|
<<: *default # 引用默认中间件
|
||||||
|
# 反向HTTP POST地址列表
|
||||||
|
post:
|
||||||
|
#- url: '' # 地址
|
||||||
|
# secret: '' # 密钥
|
||||||
|
#- url: 127.0.0.1:5701 # 地址
|
||||||
|
# secret: '' # 密钥
|
||||||
|
`
|
||||||
|
|
||||||
|
const wsDefault = ` # 正向WS设置
|
||||||
|
- ws:
|
||||||
|
# 正向WS服务器监听地址
|
||||||
|
host: 127.0.0.1
|
||||||
|
# 正向WS服务器监听端口
|
||||||
|
port: 6700
|
||||||
|
middlewares:
|
||||||
|
<<: *default # 引用默认中间件
|
||||||
|
`
|
||||||
|
|
||||||
|
const wsReverseDefault = ` # 反向WS设置
|
||||||
|
- ws-reverse:
|
||||||
|
# 反向WS Universal 地址
|
||||||
|
# 注意 设置了此项地址后下面两项将会被忽略
|
||||||
|
universal: ws://your_websocket_universal.server
|
||||||
|
# 反向WS API 地址
|
||||||
|
api: ws://your_websocket_api.server
|
||||||
|
# 反向WS Event 地址
|
||||||
|
event: ws://your_websocket_event.server
|
||||||
|
# 重连间隔 单位毫秒
|
||||||
|
reconnect-interval: 3000
|
||||||
|
middlewares:
|
||||||
|
<<: *default # 引用默认中间件
|
||||||
|
`
|
||||||
|
|
||||||
|
const pprofDefault = ` # pprof 性能分析服务器, 一般情况下不需要启用.
|
||||||
|
# 如果遇到性能问题请上传报告给开发者处理
|
||||||
|
# 注意: pprof服务不支持中间件、不支持鉴权. 请不要开放到公网
|
||||||
|
- pprof:
|
||||||
|
# pprof服务器监听地址
|
||||||
|
host: 127.0.0.1
|
||||||
|
# pprof服务器监听端口
|
||||||
|
port: 7700
|
||||||
|
`
|
||||||
|
@ -4,10 +4,10 @@ account: # 账号相关
|
|||||||
uin: 1233456 # QQ账号
|
uin: 1233456 # QQ账号
|
||||||
password: '' # 密码为空时使用扫码登录
|
password: '' # 密码为空时使用扫码登录
|
||||||
encrypt: false # 是否开启密码加密
|
encrypt: false # 是否开启密码加密
|
||||||
relogin: # 重连设置
|
status: 0 # 在线状态 请参考 https://docs.go-cqhttp.org/guide/config.html#在线状态
|
||||||
disabled: false
|
relogin: # 重连设置
|
||||||
delay: 3 # 重连延迟, 单位秒
|
delay: 3 # 首次重连延迟, 单位秒
|
||||||
interval: 0 # 重连间隔
|
interval: 3 # 重连间隔
|
||||||
max-times: 0 # 最大重连次数, 0为无限制
|
max-times: 0 # 最大重连次数, 0为无限制
|
||||||
|
|
||||||
# 是否使用服务器下发的新地址进行重连
|
# 是否使用服务器下发的新地址进行重连
|
||||||
@ -15,7 +15,6 @@ account: # 账号相关
|
|||||||
use-sso-address: true
|
use-sso-address: true
|
||||||
|
|
||||||
heartbeat:
|
heartbeat:
|
||||||
disabled: false # 是否开启心跳事件上报
|
|
||||||
# 心跳频率, 单位秒
|
# 心跳频率, 单位秒
|
||||||
# -1 为关闭心跳
|
# -1 为关闭心跳
|
||||||
interval: 5
|
interval: 5
|
||||||
@ -42,7 +41,7 @@ message:
|
|||||||
extra-reply-data: false
|
extra-reply-data: false
|
||||||
|
|
||||||
output:
|
output:
|
||||||
# 日志等级 trace,debug,info,warn,error日志等级 trace,debug,info,warn,error
|
# 日志等级 trace,debug,info,warn,error
|
||||||
log-level: warn
|
log-level: warn
|
||||||
# 是否启用 DEBUG
|
# 是否启用 DEBUG
|
||||||
debug: false # 开启调试模式
|
debug: false # 开启调试模式
|
||||||
@ -63,61 +62,17 @@ default-middlewares: &default
|
|||||||
frequency: 1 # 令牌回复频率, 单位秒
|
frequency: 1 # 令牌回复频率, 单位秒
|
||||||
bucket: 1 # 令牌桶大小
|
bucket: 1 # 令牌桶大小
|
||||||
|
|
||||||
servers:
|
|
||||||
# HTTP 通信设置
|
|
||||||
- http:
|
|
||||||
# 是否关闭正向HTTP服务器
|
|
||||||
disabled: false
|
|
||||||
# 服务端监听地址
|
|
||||||
host: 127.0.0.1
|
|
||||||
# 服务端监听端口
|
|
||||||
port: 5700
|
|
||||||
# 反向HTTP超时时间, 单位秒
|
|
||||||
# 最小值为5,小于5将会忽略本项设置
|
|
||||||
timeout: 5
|
|
||||||
middlewares:
|
|
||||||
<<: *default # 引用默认中间件
|
|
||||||
# 反向HTTP POST地址列表
|
|
||||||
post:
|
|
||||||
#- url: '' # 地址
|
|
||||||
# secret: '' # 密钥
|
|
||||||
#- url: 127.0.0.1:5701 # 地址
|
|
||||||
# secret: '' # 密钥
|
|
||||||
|
|
||||||
# 正向WS设置
|
|
||||||
- ws:
|
|
||||||
# 是否禁用正向WS服务器
|
|
||||||
disabled: true
|
|
||||||
# 正向WS服务器监听地址
|
|
||||||
host: 127.0.0.1
|
|
||||||
# 正向WS服务器监听端口
|
|
||||||
port: 6700
|
|
||||||
middlewares:
|
|
||||||
<<: *default # 引用默认中间件
|
|
||||||
|
|
||||||
- ws-reverse:
|
|
||||||
# 是否禁用当前反向WS服务
|
|
||||||
disabled: true
|
|
||||||
# 反向WS Universal 地址
|
|
||||||
# 注意 设置了此项地址后下面两项将会被忽略
|
|
||||||
universal: ws://your_websocket_universal.server
|
|
||||||
# 反向WS API 地址
|
|
||||||
api: ws://your_websocket_api.server
|
|
||||||
# 反向WS Event 地址
|
|
||||||
event: ws://your_websocket_event.server
|
|
||||||
# 重连间隔 单位毫秒
|
|
||||||
reconnect-interval: 3000
|
|
||||||
middlewares:
|
|
||||||
<<: *default # 引用默认中间件
|
|
||||||
|
|
||||||
# 可添加更多
|
|
||||||
#- ws-reverse:
|
|
||||||
#- ws:
|
|
||||||
#- http:
|
|
||||||
|
|
||||||
database: # 数据库相关设置
|
database: # 数据库相关设置
|
||||||
leveldb:
|
leveldb:
|
||||||
# 是否启用内置leveldb数据库
|
# 是否启用内置leveldb数据库
|
||||||
# 启用将会增加10-20MB的内存占用和一定的磁盘空间
|
# 启用将会增加10-20MB的内存占用和一定的磁盘空间
|
||||||
# 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能
|
# 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能
|
||||||
enable: true
|
enable: true
|
||||||
|
|
||||||
|
# 连接服务列表
|
||||||
|
servers:
|
||||||
|
# 添加方式,同一连接方式可添加多个,具体配置说明请查看文档
|
||||||
|
#- http: # http 通信
|
||||||
|
#- ws: # 正向 Websocket
|
||||||
|
#- ws-reverse: # 反向 Websocket
|
||||||
|
#- pprof: #性能分析服务器
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ func ReadAllText(path string) string {
|
|||||||
|
|
||||||
// WriteAllText 将给定text写入给定path
|
// WriteAllText 将给定text写入给定path
|
||||||
func WriteAllText(path, text string) error {
|
func WriteAllText(path, text string) error {
|
||||||
return ioutil.WriteFile(path, []byte(text), 0644)
|
return ioutil.WriteFile(path, utils.S2B(text), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check 检测err是否为nil
|
// Check 检测err是否为nil
|
||||||
@ -83,7 +84,7 @@ func IsAMRorSILK(b []byte) bool {
|
|||||||
func FindFile(file, cache, p string) (data []byte, err error) {
|
func FindFile(file, cache, p string) (data []byte, err error) {
|
||||||
data, err = nil, ErrSyntax
|
data, err = nil, ErrSyntax
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(file, "http") || strings.HasPrefix(file, "https"):
|
case strings.HasPrefix(file, "http"): // https also has prefix http
|
||||||
if cache == "" {
|
if cache == "" {
|
||||||
cache = "1"
|
cache = "1"
|
||||||
}
|
}
|
||||||
@ -93,12 +94,12 @@ func FindFile(file, cache, p string) (data []byte, err error) {
|
|||||||
return ioutil.ReadFile(cacheFile)
|
return ioutil.ReadFile(cacheFile)
|
||||||
}
|
}
|
||||||
data, err = GetBytes(file)
|
data, err = GetBytes(file)
|
||||||
_ = ioutil.WriteFile(cacheFile, data, 0644)
|
_ = ioutil.WriteFile(cacheFile, data, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case strings.HasPrefix(file, "base64"):
|
case strings.HasPrefix(file, "base64"):
|
||||||
data, err = base64.StdEncoding.DecodeString(strings.ReplaceAll(file, "base64://", ""))
|
data, err = base64.StdEncoding.DecodeString(strings.TrimPrefix(file, "base64://"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ func (hook *LocalHook) pathWrite(entry *logrus.Entry) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := os.OpenFile(hook.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
fd, err := os.OpenFile(hook.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package global
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -49,33 +48,19 @@ var (
|
|||||||
|
|
||||||
// GetBytes 对给定URL发送Get请求,返回响应主体
|
// GetBytes 对给定URL发送Get请求,返回响应主体
|
||||||
func GetBytes(url string) ([]byte, error) {
|
func GetBytes(url string) ([]byte, error) {
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
reader, err := HTTPGetReadCloser(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header["User-Agent"] = []string{UserAgent}
|
defer func() {
|
||||||
resp, err := client.Do(req)
|
_ = reader.Close()
|
||||||
if err != nil {
|
}()
|
||||||
return nil, err
|
return ioutil.ReadAll(reader)
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
|
|
||||||
buffer := bytes.NewBuffer(body)
|
|
||||||
r, _ := gzip.NewReader(buffer)
|
|
||||||
defer r.Close()
|
|
||||||
unCom, err := ioutil.ReadAll(r)
|
|
||||||
return unCom, err
|
|
||||||
}
|
|
||||||
return body, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadFile 将给定URL对应的文件下载至给定Path
|
// DownloadFile 将给定URL对应的文件下载至给定Path
|
||||||
func DownloadFile(url, path string, limit int64, headers map[string]string) error {
|
func DownloadFile(url, path string, limit int64, headers map[string]string) error {
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -123,7 +108,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int,
|
|||||||
// 初始化分块或直接下载
|
// 初始化分块或直接下载
|
||||||
initOrDownload := func() error {
|
initOrDownload := func() error {
|
||||||
copyStream := func(s io.ReadCloser) error {
|
copyStream := func(s io.ReadCloser) error {
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -192,7 +177,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int,
|
|||||||
// 下载分块
|
// 下载分块
|
||||||
downloadBlock := func(block *BlockMetaData) error {
|
downloadBlock := func(block *BlockMetaData) error {
|
||||||
req, _ := http.NewRequest("GET", url, nil)
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -297,3 +282,48 @@ func NeteaseMusicSongInfo(id string) (gjson.Result, error) {
|
|||||||
}
|
}
|
||||||
return gjson.ParseBytes(d).Get("songs.0"), nil
|
return gjson.ParseBytes(d).Get("songs.0"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type gzipCloser struct {
|
||||||
|
f io.Closer
|
||||||
|
r *gzip.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGzipReadCloser 从 io.ReadCloser 创建 gunzip io.ReadCloser
|
||||||
|
func NewGzipReadCloser(reader io.ReadCloser) (io.ReadCloser, error) {
|
||||||
|
gzipReader, err := gzip.NewReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gzipCloser{
|
||||||
|
f: reader,
|
||||||
|
r: gzipReader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read impls io.Reader
|
||||||
|
func (g *gzipCloser) Read(p []byte) (n int, err error) {
|
||||||
|
return g.r.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close impls io.Closer
|
||||||
|
func (g *gzipCloser) Close() error {
|
||||||
|
_ = g.f.Close()
|
||||||
|
return g.r.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPGetReadCloser 从 Http url 获取 io.ReadCloser
|
||||||
|
func HTTPGetReadCloser(url string) (io.ReadCloser, error) {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header["User-Agent"] = []string{UserAgent}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
|
||||||
|
return NewGzipReadCloser(resp.Body)
|
||||||
|
}
|
||||||
|
return resp.Body, err
|
||||||
|
}
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -71,23 +73,44 @@ func EnsureBool(p interface{}, defaultVal bool) bool {
|
|||||||
// v0.9.29-fix2 > v0.9.29-fix1 -> false
|
// v0.9.29-fix2 > v0.9.29-fix1 -> false
|
||||||
//
|
//
|
||||||
// v0.9.29-fix2 < v0.9.30 -> true
|
// v0.9.29-fix2 < v0.9.30 -> true
|
||||||
|
//
|
||||||
|
// v1.0.0-alpha2 < v1.0.0-beta1 -> true
|
||||||
|
//
|
||||||
|
// v1.0.0 > v1.0.0-beta1 -> false
|
||||||
func VersionNameCompare(current, remote string) bool {
|
func VersionNameCompare(current, remote string) bool {
|
||||||
sp := regexp.MustCompile(`[0-9]\d*`)
|
defer func() { // 应该不会panic, 为了保险还是加个
|
||||||
cur := sp.FindAllStringSubmatch(current, -1)
|
if err := recover(); err != nil {
|
||||||
re := sp.FindAllStringSubmatch(remote, -1)
|
log.Warn("检查更新失败!")
|
||||||
for i := 0; i < int(math.Min(float64(len(cur)), float64(len(re)))); i++ {
|
}
|
||||||
curSub, _ := strconv.Atoi(cur[i][0])
|
}()
|
||||||
reSub, _ := strconv.Atoi(re[i][0])
|
sp := regexp.MustCompile(`v(\d+)\.(\d+)\.(\d+)-?(.+)?`)
|
||||||
if curSub < reSub {
|
cur := sp.FindStringSubmatch(current)
|
||||||
return true
|
re := sp.FindStringSubmatch(remote)
|
||||||
|
for i := 1; i <= 3; i++ {
|
||||||
|
curSub, _ := strconv.Atoi(cur[i])
|
||||||
|
reSub, _ := strconv.Atoi(re[i])
|
||||||
|
if curSub != reSub {
|
||||||
|
return curSub < reSub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return len(cur) < len(re)
|
if cur[4] == "" || re[4] == "" {
|
||||||
|
return re[4] == "" && cur[4] != re[4]
|
||||||
|
}
|
||||||
|
return cur[4] < re[4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// once lazy compile the reg
|
||||||
|
once sync.Once
|
||||||
|
// reg is splitURL regex pattern.
|
||||||
|
reg *regexp.Regexp
|
||||||
|
)
|
||||||
|
|
||||||
// SplitURL 将给定URL字符串分割为两部分,用于URL预处理防止风控
|
// SplitURL 将给定URL字符串分割为两部分,用于URL预处理防止风控
|
||||||
func SplitURL(s string) []string {
|
func SplitURL(s string) []string {
|
||||||
reg := regexp.MustCompile(`(?i)[a-z\d][-a-z\d]{0,62}(\.[a-z\d][-a-z\d]{0,62})+\.?`)
|
once.Do(func() { // lazy init.
|
||||||
|
reg = regexp.MustCompile(`(?i)[a-z\d][-a-z\d]{0,62}(\.[a-z\d][-a-z\d]{0,62})+\.?`)
|
||||||
|
})
|
||||||
idx := reg.FindAllStringIndex(s, -1)
|
idx := reg.FindAllStringIndex(s, -1)
|
||||||
if len(idx) == 0 {
|
if len(idx) == 0 {
|
||||||
return []string{s}
|
return []string{s}
|
||||||
|
@ -5,34 +5,31 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/kardianos/osext"
|
"github.com/kardianos/osext"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteCounter 写入量计算实例
|
// WriteSumCounter 写入量计算实例
|
||||||
type WriteCounter struct {
|
type WriteSumCounter struct {
|
||||||
Total uint64
|
Total uint64
|
||||||
|
Hash hash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write 方法将写入的byte长度追加至写入的总长度Total中
|
// Write 方法将写入的byte长度追加至写入的总长度Total中
|
||||||
func (wc *WriteCounter) Write(p []byte) (int, error) {
|
func (wc *WriteSumCounter) Write(p []byte) (int, error) {
|
||||||
n := len(p)
|
n := len(p)
|
||||||
wc.Total += uint64(n)
|
wc.Total += uint64(n)
|
||||||
wc.PrintProgress()
|
wc.Hash.Write(p)
|
||||||
return n, nil
|
fmt.Printf("\r ")
|
||||||
}
|
|
||||||
|
|
||||||
// PrintProgress 方法将打印当前的总写入量
|
|
||||||
func (wc *WriteCounter) PrintProgress() {
|
|
||||||
fmt.Printf("\r%s", strings.Repeat(" ", 35))
|
|
||||||
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
|
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromStream copy form getlantern/go-update
|
// FromStream copy form getlantern/go-update
|
||||||
@ -54,7 +51,7 @@ func FromStream(updateWith io.Reader) (err error, errRecover error) {
|
|||||||
filename := filepath.Base(updatePath)
|
filename := filepath.Base(updatePath)
|
||||||
// Copy the contents of of newbinary to a the new executable file
|
// Copy the contents of of newbinary to a the new executable file
|
||||||
newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename))
|
newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename))
|
||||||
fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,46 +6,47 @@ import (
|
|||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update go-cqhttp自我更新
|
// Update go-cqhttp自我更新
|
||||||
func Update(url string) {
|
func Update(url string, sum []byte) error {
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("更新失败: ", err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
wc := WriteCounter{}
|
wc := WriteSumCounter{
|
||||||
data, err := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
Hash: sha256.New(),
|
||||||
if err != nil {
|
|
||||||
log.Error("更新失败: ", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
gr, err := gzip.NewReader(bytes.NewReader(data))
|
rsp, err := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("更新失败: ", err)
|
return err
|
||||||
return
|
}
|
||||||
|
if !bytes.Equal(wc.Hash.Sum(nil), sum) {
|
||||||
|
return errors.New("文件已损坏")
|
||||||
|
}
|
||||||
|
gr, err := gzip.NewReader(bytes.NewReader(rsp))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
tr := tar.NewReader(gr)
|
tr := tar.NewReader(gr)
|
||||||
for {
|
for {
|
||||||
header, err := tr.Next()
|
header, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
if header.Name == "go-cqhttp" {
|
if header.Name == "go-cqhttp" {
|
||||||
err, _ := FromStream(tr)
|
err, _ := FromStream(tr)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("更新失败!", err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
log.Info("更新完成!")
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,34 +3,39 @@ package update
|
|||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update go-cqhttp自我更新
|
// Update go-cqhttp自我更新
|
||||||
func Update(url string) {
|
func Update(url string, sum []byte) error {
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("更新失败: ", err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
wc := WriteCounter{}
|
wc := WriteSumCounter{
|
||||||
rsp, _ := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
Hash: sha256.New(),
|
||||||
|
}
|
||||||
|
rsp, err := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(wc.Hash.Sum(nil), sum) {
|
||||||
|
return errors.New("文件已损坏")
|
||||||
|
}
|
||||||
reader, _ := zip.NewReader(bytes.NewReader(rsp), resp.ContentLength)
|
reader, _ := zip.NewReader(bytes.NewReader(rsp), resp.ContentLength)
|
||||||
file, err := reader.Open("go-cqhttp.exe")
|
file, err := reader.Open("go-cqhttp.exe")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("更新失败!", err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
err, _ = FromStream(file)
|
err, _ = FromStream(file)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("更新失败!", err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
log.Info("更新完成!")
|
return nil
|
||||||
}
|
}
|
||||||
|
16
go.mod
16
go.mod
@ -4,11 +4,12 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210406093140-5ec6c651b797
|
github.com/Mrs4s/MiraiGo v0.0.0-20210525010101-8f0cd9494d64
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-contrib/pprof v1.3.0
|
||||||
|
github.com/gin-gonic/gin v1.7.1
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/guonaihong/gout v0.1.6
|
github.com/guonaihong/gout v0.1.9
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.10
|
github.com/json-iterator/go v1.1.10
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
@ -18,14 +19,15 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
||||||
github.com/tidwall/gjson v1.7.3
|
github.com/tidwall/gjson v1.7.5
|
||||||
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2
|
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2
|
||||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
||||||
github.com/willf/bitset v1.1.11 // indirect
|
github.com/willf/bitset v1.1.11 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
||||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6
|
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
)
|
)
|
||||||
|
46
go.sum
46
go.sum
@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
|
||||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210406093140-5ec6c651b797 h1:u6jJ6bbYghAEM1DVe0SQKoIVva6od7fsdFRRC15DKMw=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210525010101-8f0cd9494d64 h1:36RqrFJS7TCicTLc995qgJI5NDFy93YR/zrqn4GI6nQ=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210406093140-5ec6c651b797/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210525010101-8f0cd9494d64/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -14,19 +14,23 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
|
||||||
|
github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8=
|
||||||
|
github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@ -38,24 +42,26 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
|||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/guonaihong/gout v0.1.6 h1:Txej4NYvVJLZkW0Xgw1HuWfSWow5BgLF6vqlM2kRdno=
|
github.com/guonaihong/gout v0.1.9 h1:splZ4PPe5Ju40akL29drcTmG75TLtzqVlBv7GeoCzH8=
|
||||||
github.com/guonaihong/gout v0.1.6/go.mod h1:P6P8+0+toYgmhFqzLxVde+9vQbCDHrxn56V9TglC5io=
|
github.com/guonaihong/gout v0.1.9/go.mod h1:JkjNv1G2oRWvFgP/r4DUbYhoeIBB0zMP2j1ID+5CYpU=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||||
@ -114,8 +120,8 @@ github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFd
|
|||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk=
|
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk=
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA=
|
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA=
|
||||||
github.com/tidwall/gjson v1.7.3 h1:9dOulDrkCJf1mwljVMhXNQr9ZL2NvajRX7A1R8c6Qxw=
|
github.com/tidwall/gjson v1.7.5 h1:zmAN/xmX7OtpAkv4Ovfso60r/BiCi5IErCDYGNJu+uc=
|
||||||
github.com/tidwall/gjson v1.7.3/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
github.com/tidwall/gjson v1.7.5/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||||
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
||||||
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
||||||
@ -131,8 +137,9 @@ github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZ
|
|||||||
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
|
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
|
||||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@ -142,6 +149,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
@ -152,6 +160,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -160,8 +169,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
|
golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
|
||||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 h1:EC6+IGYTjPpRfv9a2b/6Puw0W+hLtAhkV1tPsXhutqs=
|
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0=
|
||||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
@ -191,8 +200,10 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
|||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
@ -203,8 +214,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o=
|
modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o=
|
||||||
|
21
login.go
21
login.go
@ -63,7 +63,7 @@ func qrcodeLogin() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_ = ioutil.WriteFile("qrcode.png", rsp.ImageData, 0644)
|
_ = ioutil.WriteFile("qrcode.png", rsp.ImageData, 0o644)
|
||||||
defer func() { _ = os.Remove("qrcode.png") }()
|
defer func() { _ = os.Remove("qrcode.png") }()
|
||||||
log.Infof("请使用手机QQ扫描二维码 (qrcode.png) : ")
|
log.Infof("请使用手机QQ扫描二维码 (qrcode.png) : ")
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
@ -83,21 +83,21 @@ func qrcodeLogin() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
prevState = s.State
|
prevState = s.State
|
||||||
if s.State == client.QRCodeCanceled {
|
switch s.State {
|
||||||
|
case client.QRCodeCanceled:
|
||||||
log.Fatalf("扫码被用户取消.")
|
log.Fatalf("扫码被用户取消.")
|
||||||
}
|
case client.QRCodeTimeout:
|
||||||
if s.State == client.QRCodeTimeout {
|
|
||||||
log.Fatalf("二维码过期")
|
log.Fatalf("二维码过期")
|
||||||
}
|
case client.QRCodeWaitingForConfirm:
|
||||||
if s.State == client.QRCodeWaitingForConfirm {
|
|
||||||
log.Infof("扫码成功, 请在手机端确认登录.")
|
log.Infof("扫码成功, 请在手机端确认登录.")
|
||||||
}
|
case client.QRCodeConfirmed:
|
||||||
if s.State == client.QRCodeConfirmed {
|
|
||||||
res, err := cli.QRCodeLogin(s.LoginInfo)
|
res, err := cli.QRCodeLogin(s.LoginInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return loginResponseProcessor(res)
|
return loginResponseProcessor(res)
|
||||||
|
case client.QRCodeImageFetch, client.QRCodeWaitingForScan:
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
|||||||
switch res.Error {
|
switch res.Error {
|
||||||
case client.SliderNeededError:
|
case client.SliderNeededError:
|
||||||
log.Warnf("登录需要滑条验证码. ")
|
log.Warnf("登录需要滑条验证码. ")
|
||||||
log.Warnf("请参考文档 -> https://github.com/Mrs4s/go-cqhttp/blob/master/docs/slider.md <- 进行处理")
|
log.Warnf("请参考文档 -> https://docs.go-cqhttp.org/faq/slider.html <- 进行处理")
|
||||||
log.Warnf("1. 自行抓包并获取 Ticket 输入.")
|
log.Warnf("1. 自行抓包并获取 Ticket 输入.")
|
||||||
log.Warnf("2. 使用手机QQ扫描二维码登入. (推荐)")
|
log.Warnf("2. 使用手机QQ扫描二维码登入. (推荐)")
|
||||||
log.Warn("请输入(1 - 2) (将在10秒后自动选择2):")
|
log.Warn("请输入(1 - 2) (将在10秒后自动选择2):")
|
||||||
@ -129,10 +129,11 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
|||||||
res, err = cli.SubmitTicket(text)
|
res, err = cli.SubmitTicket(text)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
cli.Disconnect()
|
||||||
return qrcodeLogin()
|
return qrcodeLogin()
|
||||||
case client.NeedCaptcha:
|
case client.NeedCaptcha:
|
||||||
log.Warnf("登录需要验证码.")
|
log.Warnf("登录需要验证码.")
|
||||||
_ = ioutil.WriteFile("captcha.jpg", res.CaptchaImage, 0644)
|
_ = ioutil.WriteFile("captcha.jpg", res.CaptchaImage, 0o644)
|
||||||
log.Warnf("请输入验证码 (captcha.jpg): (Enter 提交)")
|
log.Warnf("请输入验证码 (captcha.jpg): (Enter 提交)")
|
||||||
text = readLine()
|
text = readLine()
|
||||||
global.DelFile("captcha.jpg")
|
global.DelFile("captcha.jpg")
|
||||||
|
313
main.go
313
main.go
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -25,6 +26,7 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/global/update"
|
"github.com/Mrs4s/go-cqhttp/global/update"
|
||||||
"github.com/Mrs4s/go-cqhttp/server"
|
"github.com/Mrs4s/go-cqhttp/server"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
"github.com/guonaihong/gout"
|
"github.com/guonaihong/gout"
|
||||||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||||
@ -41,6 +43,22 @@ var (
|
|||||||
c string
|
c string
|
||||||
d bool
|
d bool
|
||||||
h bool
|
h bool
|
||||||
|
wd string // reset work dir
|
||||||
|
|
||||||
|
// PasswordHash 存储QQ密码哈希供登录使用
|
||||||
|
PasswordHash [16]byte
|
||||||
|
|
||||||
|
// AccountToken 存储AccountToken供登录使用
|
||||||
|
AccountToken []byte
|
||||||
|
|
||||||
|
// 允许通过配置文件设置的状态列表
|
||||||
|
allowStatus = [...]client.UserOnlineStatus{
|
||||||
|
client.StatusOnline, client.StatusAway, client.StatusInvisible, client.StatusBusy,
|
||||||
|
client.StatusListening, client.StatusConstellation, client.StatusWeather, client.StatusMeetSpring,
|
||||||
|
client.StatusTimi, client.StatusEatChicken, client.StatusLoving, client.StatusWangWang, client.StatusCookedRice,
|
||||||
|
client.StatusStudy, client.StatusStayUp, client.StatusPlayBall, client.StatusSignal, client.StatusStudyOnline,
|
||||||
|
client.StatusGaming, client.StatusVacationing, client.StatusWatchingTV, client.StatusFitness,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -49,6 +67,7 @@ func init() {
|
|||||||
flag.BoolVar(&d, "d", false, "running as a daemon")
|
flag.BoolVar(&d, "d", false, "running as a daemon")
|
||||||
flag.BoolVar(&debug, "D", false, "debug mode")
|
flag.BoolVar(&debug, "D", false, "debug mode")
|
||||||
flag.BoolVar(&h, "h", false, "this help")
|
flag.BoolVar(&h, "h", false, "this help")
|
||||||
|
flag.StringVar(&wd, "w", "", "cover the working directory")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// 通过-c 参数替换 配置文件路径
|
// 通过-c 参数替换 配置文件路径
|
||||||
@ -57,6 +76,7 @@ func init() {
|
|||||||
TimestampFormat: "2006-01-02 15:04:05",
|
TimestampFormat: "2006-01-02 15:04:05",
|
||||||
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
||||||
}
|
}
|
||||||
|
|
||||||
w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24))
|
w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("rotatelogs init err: %v", err)
|
log.Errorf("rotatelogs init err: %v", err)
|
||||||
@ -64,13 +84,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conf = config.Get()
|
conf = config.Get()
|
||||||
if conf == nil {
|
|
||||||
_ = os.WriteFile("config.yml", []byte(config.DefaultConfig), 0644)
|
|
||||||
log.Error("未找到配置文件,默认配置文件已生成!")
|
|
||||||
readLine()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
conf.Output.Debug = true
|
conf.Output.Debug = true
|
||||||
}
|
}
|
||||||
@ -78,26 +91,25 @@ func init() {
|
|||||||
if conf.Output.Debug {
|
if conf.Output.Debug {
|
||||||
log.SetReportCaller(true)
|
log.SetReportCaller(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.AddHook(global.NewLocalHook(w, logFormatter, global.GetLogLevel(conf.Output.LogLevel)...))
|
log.AddHook(global.NewLocalHook(w, logFormatter, global.GetLogLevel(conf.Output.LogLevel)...))
|
||||||
|
|
||||||
if !global.PathExists(global.ImagePath) {
|
if !global.PathExists(global.ImagePath) {
|
||||||
if err := os.MkdirAll(global.ImagePath, 0755); err != nil {
|
if err := os.MkdirAll(global.ImagePath, 0o755); err != nil {
|
||||||
log.Fatalf("创建图片缓存文件夹失败: %v", err)
|
log.Fatalf("创建图片缓存文件夹失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !global.PathExists(global.VoicePath) {
|
if !global.PathExists(global.VoicePath) {
|
||||||
if err := os.MkdirAll(global.VoicePath, 0755); err != nil {
|
if err := os.MkdirAll(global.VoicePath, 0o755); err != nil {
|
||||||
log.Fatalf("创建语音缓存文件夹失败: %v", err)
|
log.Fatalf("创建语音缓存文件夹失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !global.PathExists(global.VideoPath) {
|
if !global.PathExists(global.VideoPath) {
|
||||||
if err := os.MkdirAll(global.VideoPath, 0755); err != nil {
|
if err := os.MkdirAll(global.VideoPath, 0o755); err != nil {
|
||||||
log.Fatalf("创建视频缓存文件夹失败: %v", err)
|
log.Fatalf("创建视频缓存文件夹失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !global.PathExists(global.CachePath) {
|
if !global.PathExists(global.CachePath) {
|
||||||
if err := os.MkdirAll(global.CachePath, 0755); err != nil {
|
if err := os.MkdirAll(global.CachePath, 0o755); err != nil {
|
||||||
log.Fatalf("创建发送图片缓存文件夹失败: %v", err)
|
log.Fatalf("创建发送图片缓存文件夹失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,6 +122,9 @@ func main() {
|
|||||||
if d {
|
if d {
|
||||||
server.Daemon()
|
server.Daemon()
|
||||||
}
|
}
|
||||||
|
if wd != "" {
|
||||||
|
resetWorkDir()
|
||||||
|
}
|
||||||
var byteKey []byte
|
var byteKey []byte
|
||||||
arg := os.Args
|
arg := os.Args
|
||||||
if len(arg) > 1 {
|
if len(arg) > 1 {
|
||||||
@ -149,17 +164,12 @@ func main() {
|
|||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
log.Warnf("已开启Debug模式.")
|
log.Warnf("已开启Debug模式.")
|
||||||
log.Debugf("开发交流群: 192548878")
|
log.Debugf("开发交流群: 192548878")
|
||||||
/*
|
|
||||||
if conf.WebUI == nil || !conf.WebUI.Enabled {
|
|
||||||
log.Warnf("警告: 在Debug模式下未启用WebUi服务, 将无法进行性能分析.")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
log.Info("用户交流群: 721829413")
|
log.Info("用户交流群: 721829413")
|
||||||
if !global.PathExists("device.json") {
|
if !global.PathExists("device.json") {
|
||||||
log.Warn("虚拟设备信息不存在, 将自动生成随机设备.")
|
log.Warn("虚拟设备信息不存在, 将自动生成随机设备.")
|
||||||
client.GenRandomDevice()
|
client.GenRandomDevice()
|
||||||
_ = ioutil.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0644)
|
_ = ioutil.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0o644)
|
||||||
log.Info("已生成设备信息并保存到 device.json 文件.")
|
log.Info("已生成设备信息并保存到 device.json 文件.")
|
||||||
} else {
|
} else {
|
||||||
log.Info("将使用 device.json 内的设备信息运行Bot.")
|
log.Info("将使用 device.json 内的设备信息运行Bot.")
|
||||||
@ -177,8 +187,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)")
|
log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)")
|
||||||
byteKey, _ = term.ReadPassword(int(os.Stdin.Fd()))
|
byteKey, _ = term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
global.PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
||||||
_ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(global.PasswordHash[:], byteKey)), 0644)
|
_ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(PasswordHash[:], byteKey)), 0o644)
|
||||||
log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.")
|
log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.")
|
||||||
readLine()
|
readLine()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@ -215,10 +225,10 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("加密存储的密码损坏,请尝试重新配置密码")
|
log.Fatalf("加密存储的密码损坏,请尝试重新配置密码")
|
||||||
}
|
}
|
||||||
copy(global.PasswordHash[:], ph)
|
copy(PasswordHash[:], ph)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
global.PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
||||||
}
|
}
|
||||||
if !isFastStart {
|
if !isFastStart {
|
||||||
log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.")
|
log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.")
|
||||||
@ -235,13 +245,15 @@ func main() {
|
|||||||
return "Android Watch"
|
return "Android Watch"
|
||||||
case client.MacOS:
|
case client.MacOS:
|
||||||
return "MacOS"
|
return "MacOS"
|
||||||
|
case client.QiDian:
|
||||||
|
return "企点"
|
||||||
}
|
}
|
||||||
return "未知"
|
return "未知"
|
||||||
}())
|
}())
|
||||||
cli = client.NewClientEmpty()
|
cli = client.NewClientEmpty()
|
||||||
if conf.Account.Uin != 0 && global.PasswordHash != [16]byte{} {
|
if conf.Account.Uin != 0 && PasswordHash != [16]byte{} {
|
||||||
cli.Uin = conf.Account.Uin
|
cli.Uin = conf.Account.Uin
|
||||||
cli.PasswordMd5 = global.PasswordHash
|
cli.PasswordMd5 = PasswordHash
|
||||||
}
|
}
|
||||||
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
@ -269,33 +281,35 @@ func main() {
|
|||||||
log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ")
|
log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ")
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
/*
|
|
||||||
if conf.WebUI == nil {
|
|
||||||
conf.WebUI = &global.GoCQWebUI{
|
|
||||||
Enabled: true,
|
|
||||||
WebInput: false,
|
|
||||||
Host: "0.0.0.0",
|
|
||||||
WebUIPort: 9999,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if conf.WebUI.WebUIPort <= 0 {
|
|
||||||
conf.WebUI.WebUIPort = 9999
|
|
||||||
}
|
|
||||||
if conf.WebUI.Host == "" {
|
|
||||||
conf.WebUI.Host = "127.0.0.1"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
global.Proxy = conf.Message.ProxyRewrite
|
global.Proxy = conf.Message.ProxyRewrite
|
||||||
// b := server.WebServer.Run(fmt.Sprintf("%s:%d", conf.WebUI.Host, conf.WebUI.WebUIPort), cli)
|
|
||||||
// c := server.Console
|
|
||||||
isQRCodeLogin := (conf.Account.Uin == 0 || len(conf.Account.Password) == 0) && !conf.Account.Encrypt
|
isQRCodeLogin := (conf.Account.Uin == 0 || len(conf.Account.Password) == 0) && !conf.Account.Encrypt
|
||||||
isTokenLogin := false
|
isTokenLogin := false
|
||||||
|
saveToken := func() {
|
||||||
|
AccountToken = cli.GenToken()
|
||||||
|
_ = ioutil.WriteFile("session.token", AccountToken, 0o677)
|
||||||
|
}
|
||||||
if global.PathExists("session.token") {
|
if global.PathExists("session.token") {
|
||||||
token, err := ioutil.ReadFile("session.token")
|
token, err := ioutil.ReadFile("session.token")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if conf.Account.Uin != 0 {
|
||||||
|
r := binary.NewReader(token)
|
||||||
|
cu := r.ReadInt64()
|
||||||
|
if cu != conf.Account.Uin {
|
||||||
|
log.Warnf("警告: 配置文件内的QQ号 (%v) 与缓存内的QQ号 (%v) 不相同", conf.Account.Uin, cu)
|
||||||
|
log.Warnf("1. 使用会话缓存继续.")
|
||||||
|
log.Warnf("2. 删除会话缓存并重启.")
|
||||||
|
log.Warnf("请选择: (5秒后自动选1)")
|
||||||
|
text := readLineTimeout(time.Second*5, "1")
|
||||||
|
if text == "2" {
|
||||||
|
_ = os.Remove("session.token")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if err = cli.TokenLogin(token); err != nil {
|
if err = cli.TokenLogin(token); err != nil {
|
||||||
_ = os.Remove("session.token")
|
_ = os.Remove("session.token")
|
||||||
log.Warnf("恢复会话失败: %v , 尝试使用正常流程登录.", err)
|
log.Warnf("恢复会话失败: %v , 尝试使用正常流程登录.", err)
|
||||||
|
time.Sleep(time.Second)
|
||||||
} else {
|
} else {
|
||||||
isTokenLogin = true
|
isTokenLogin = true
|
||||||
}
|
}
|
||||||
@ -312,41 +326,52 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
saveToken := func() {
|
|
||||||
global.AccountToken = cli.GenToken()
|
|
||||||
_ = ioutil.WriteFile("session.token", global.AccountToken, 0677)
|
|
||||||
}
|
|
||||||
saveToken()
|
|
||||||
var times uint = 1 // 重试次数
|
var times uint = 1 // 重试次数
|
||||||
var reLoginLock sync.Mutex
|
var reLoginLock sync.Mutex
|
||||||
cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) {
|
cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) {
|
||||||
reLoginLock.Lock()
|
reLoginLock.Lock()
|
||||||
defer reLoginLock.Unlock()
|
defer reLoginLock.Unlock()
|
||||||
log.Warnf("Bot已离线: %v", e.Message)
|
times = 1
|
||||||
if conf.Account.ReLogin.Disabled {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if times > conf.Account.ReLogin.MaxTimes && conf.Account.ReLogin.MaxTimes != 0 {
|
|
||||||
log.Fatalf("Bot重连次数超过限制, 停止")
|
|
||||||
}
|
|
||||||
if conf.Account.ReLogin.Interval > 0 {
|
|
||||||
log.Warnf("将在 %v 秒后尝试重连. 重连次数:%v/%v", conf.Account.ReLogin.Interval, times, conf.Account.ReLogin.MaxTimes)
|
|
||||||
}
|
|
||||||
log.Warnf("尝试重连...")
|
|
||||||
if cli.Online {
|
if cli.Online {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := cli.TokenLogin(global.AccountToken); err == nil {
|
log.Warnf("Bot已离线: %v", e.Message)
|
||||||
saveToken()
|
time.Sleep(time.Second * time.Duration(conf.Account.ReLogin.Delay))
|
||||||
return
|
for {
|
||||||
}
|
if conf.Account.ReLogin.Disabled {
|
||||||
if isQRCodeLogin {
|
os.Exit(1)
|
||||||
log.Fatalf("快速重连失败")
|
}
|
||||||
}
|
if times > conf.Account.ReLogin.MaxTimes && conf.Account.ReLogin.MaxTimes != 0 {
|
||||||
if err := commonLogin(); err != nil {
|
log.Fatalf("Bot重连次数超过限制, 停止")
|
||||||
log.Fatalf("登录时发生致命错误: %v", err)
|
}
|
||||||
|
times++
|
||||||
|
if conf.Account.ReLogin.Interval > 0 {
|
||||||
|
log.Warnf("将在 %v 秒后尝试重连. 重连次数:%v/%v", conf.Account.ReLogin.Interval, times, conf.Account.ReLogin.MaxTimes)
|
||||||
|
time.Sleep(time.Second * time.Duration(conf.Account.ReLogin.Interval))
|
||||||
|
} else {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
log.Warnf("尝试重连...")
|
||||||
|
err := cli.TokenLogin(AccountToken)
|
||||||
|
if err == nil {
|
||||||
|
saveToken()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Warnf("快速重连失败: %v", err)
|
||||||
|
if isQRCodeLogin {
|
||||||
|
log.Fatalf("快速重连失败, 扫码登录无法恢复会话.")
|
||||||
|
}
|
||||||
|
log.Warnf("快速重连失败, 尝试普通登录. 这可能是因为其他端强行T下线导致的.")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
if err := commonLogin(); err != nil {
|
||||||
|
log.Errorf("登录时发生致命错误: %v", err)
|
||||||
|
} else {
|
||||||
|
saveToken()
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
saveToken()
|
||||||
cli.AllowSlider = true
|
cli.AllowSlider = true
|
||||||
log.Infof("登录成功 欢迎使用: %v", cli.Nickname)
|
log.Infof("登录成功 欢迎使用: %v", cli.Nickname)
|
||||||
log.Info("开始加载好友列表...")
|
log.Info("开始加载好友列表...")
|
||||||
@ -355,6 +380,10 @@ func main() {
|
|||||||
log.Infof("开始加载群列表...")
|
log.Infof("开始加载群列表...")
|
||||||
global.Check(cli.ReloadGroupList(), true)
|
global.Check(cli.ReloadGroupList(), true)
|
||||||
log.Infof("共加载 %v 个群.", len(cli.GroupList))
|
log.Infof("共加载 %v 个群.", len(cli.GroupList))
|
||||||
|
if conf.Account.Status >= int32(len(allowStatus)) || conf.Account.Status < 0 {
|
||||||
|
conf.Account.Status = 0
|
||||||
|
}
|
||||||
|
cli.SetOnlineStatus(allowStatus[int(conf.Account.Status)])
|
||||||
bot := coolq.NewQQBot(cli, conf)
|
bot := coolq.NewQQBot(cli, conf)
|
||||||
_ = bot.Client
|
_ = bot.Client
|
||||||
if conf.Message.PostFormat != "string" && conf.Message.PostFormat != "array" {
|
if conf.Message.PostFormat != "string" && conf.Message.PostFormat != "array" {
|
||||||
@ -367,6 +396,8 @@ func main() {
|
|||||||
coolq.IgnoreInvalidCQCode = conf.Message.IgnoreInvalidCQCode
|
coolq.IgnoreInvalidCQCode = conf.Message.IgnoreInvalidCQCode
|
||||||
coolq.SplitURL = conf.Message.FixURL
|
coolq.SplitURL = conf.Message.FixURL
|
||||||
coolq.ForceFragmented = conf.Message.ForceFragment
|
coolq.ForceFragmented = conf.Message.ForceFragment
|
||||||
|
coolq.RemoveReplyAt = conf.Message.RemoveReplyAt
|
||||||
|
coolq.ExtraReplyData = conf.Message.ExtraReplyData
|
||||||
for _, m := range conf.Servers {
|
for _, m := range conf.Servers {
|
||||||
if h, ok := m["http"]; ok {
|
if h, ok := m["http"]; ok {
|
||||||
hc := new(config.HTTPServer)
|
hc := new(config.HTTPServer)
|
||||||
@ -379,7 +410,7 @@ func main() {
|
|||||||
if s, ok := m["ws"]; ok {
|
if s, ok := m["ws"]; ok {
|
||||||
sc := new(config.WebsocketServer)
|
sc := new(config.WebsocketServer)
|
||||||
if err := s.Decode(sc); err != nil {
|
if err := s.Decode(sc); err != nil {
|
||||||
log.Warn("读取http配置失败 :", err)
|
log.Warn("读取正向Websocket配置失败 :", err)
|
||||||
} else {
|
} else {
|
||||||
go server.RunWebSocketServer(bot, sc)
|
go server.RunWebSocketServer(bot, sc)
|
||||||
}
|
}
|
||||||
@ -387,11 +418,19 @@ func main() {
|
|||||||
if c, ok := m["ws-reverse"]; ok {
|
if c, ok := m["ws-reverse"]; ok {
|
||||||
rc := new(config.WebsocketReverse)
|
rc := new(config.WebsocketReverse)
|
||||||
if err := c.Decode(rc); err != nil {
|
if err := c.Decode(rc); err != nil {
|
||||||
log.Warn("读取http配置失败 :", err)
|
log.Warn("读取反向Websocket配置失败 :", err)
|
||||||
} else {
|
} else {
|
||||||
go server.RunWebSocketClient(bot, rc)
|
go server.RunWebSocketClient(bot, rc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if p, ok := m["pprof"]; ok {
|
||||||
|
pc := new(config.PprofServer)
|
||||||
|
if err := p.Decode(pc); err != nil {
|
||||||
|
log.Warn("读取pprof配置失败 :", err)
|
||||||
|
} else {
|
||||||
|
go server.RunPprofServer(pc)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Info("资源初始化完成, 开始处理信息.")
|
log.Info("资源初始化完成, 开始处理信息.")
|
||||||
log.Info("アトリは、高性能ですから!")
|
log.Info("アトリは、高性能ですから!")
|
||||||
@ -439,15 +478,11 @@ func checkUpdate() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var res string
|
var res string
|
||||||
if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases").BindBody(&res).Do(); err != nil {
|
if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest").BindBody(&res).Do(); err != nil {
|
||||||
log.Warnf("检查更新失败: %v", err)
|
log.Warnf("检查更新失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
detail := gjson.Parse(res)
|
info := gjson.Parse(res)
|
||||||
if len(detail.Array()) < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info := detail.Array()[0]
|
|
||||||
if global.VersionNameCompare(coolq.Version, info.Get("tag_name").Str) {
|
if global.VersionNameCompare(coolq.Version, info.Get("tag_name").Str) {
|
||||||
log.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/Mrs4s/go-cqhttp/releases 下载.")
|
log.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/Mrs4s/go-cqhttp/releases 下载.")
|
||||||
log.Infof("当前版本: %v 最新版本: %v", coolq.Version, info.Get("tag_name").Str)
|
log.Infof("当前版本: %v 最新版本: %v", coolq.Version, info.Get("tag_name").Str)
|
||||||
@ -457,56 +492,77 @@ func checkUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func selfUpdate(imageURL string) {
|
func selfUpdate(imageURL string) {
|
||||||
console := bufio.NewReader(os.Stdin)
|
|
||||||
readLine := func() (str string) {
|
|
||||||
str, _ = console.ReadString('\n')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Infof("正在检查更新.")
|
log.Infof("正在检查更新.")
|
||||||
var res string
|
var res, r string
|
||||||
if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases").BindBody(&res).Do(); err != nil {
|
if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest").BindBody(&res).Do(); err != nil {
|
||||||
log.Warnf("检查更新失败: %v", err)
|
log.Warnf("检查更新失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
detail := gjson.Parse(res)
|
info := gjson.Parse(res)
|
||||||
if len(detail.Array()) < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info := detail.Array()[0]
|
|
||||||
version := info.Get("tag_name").Str
|
version := info.Get("tag_name").Str
|
||||||
if coolq.Version != version {
|
if coolq.Version == version {
|
||||||
log.Info("当前最新版本为 ", version)
|
|
||||||
log.Warn("是否更新(y/N): ")
|
|
||||||
r := strings.TrimSpace(readLine())
|
|
||||||
if r != "y" && r != "Y" {
|
|
||||||
log.Warn("已取消更新!")
|
|
||||||
} else {
|
|
||||||
log.Info("正在更新,请稍等...")
|
|
||||||
url := fmt.Sprintf(
|
|
||||||
"%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp_%v_%v",
|
|
||||||
func() string {
|
|
||||||
if imageURL != "" {
|
|
||||||
return imageURL
|
|
||||||
}
|
|
||||||
return "https://github.com"
|
|
||||||
}(),
|
|
||||||
version, runtime.GOOS, func() string {
|
|
||||||
if runtime.GOARCH == "arm" {
|
|
||||||
return "armv7"
|
|
||||||
}
|
|
||||||
return runtime.GOARCH
|
|
||||||
}(),
|
|
||||||
)
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
url += ".zip"
|
|
||||||
} else {
|
|
||||||
url += ".tar.gz"
|
|
||||||
}
|
|
||||||
update.Update(url)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info("当前版本已经是最新版本!")
|
log.Info("当前版本已经是最新版本!")
|
||||||
|
goto wait
|
||||||
}
|
}
|
||||||
|
log.Info("当前最新版本为 ", version)
|
||||||
|
log.Warn("是否更新(y/N): ")
|
||||||
|
r = strings.TrimSpace(readLine())
|
||||||
|
if r != "y" && r != "Y" {
|
||||||
|
log.Warn("已取消更新!")
|
||||||
|
} else {
|
||||||
|
log.Info("正在更新,请稍等...")
|
||||||
|
sumURL := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp_checksums.txt",
|
||||||
|
func() string {
|
||||||
|
if imageURL != "" {
|
||||||
|
return imageURL
|
||||||
|
}
|
||||||
|
return "https://github.com"
|
||||||
|
}(), version)
|
||||||
|
closer, err := global.HTTPGetReadCloser(sumURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败: ", err)
|
||||||
|
goto wait
|
||||||
|
}
|
||||||
|
rd := bufio.NewReader(closer)
|
||||||
|
binaryName := fmt.Sprintf("go-cqhttp_%v_%v.%v", runtime.GOOS, func() string {
|
||||||
|
if runtime.GOARCH == "arm" {
|
||||||
|
return "armv7"
|
||||||
|
}
|
||||||
|
return runtime.GOARCH
|
||||||
|
}(), func() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return "zip"
|
||||||
|
}
|
||||||
|
return "tar.gz"
|
||||||
|
}())
|
||||||
|
var sum []byte
|
||||||
|
for {
|
||||||
|
str, err := rd.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
str = strings.TrimSpace(str)
|
||||||
|
if strings.HasSuffix(str, binaryName) {
|
||||||
|
sum, _ = hex.DecodeString(strings.TrimSuffix(str, " "+binaryName))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/%v",
|
||||||
|
func() string {
|
||||||
|
if imageURL != "" {
|
||||||
|
return imageURL
|
||||||
|
}
|
||||||
|
return "https://github.com"
|
||||||
|
}(), version, binaryName)
|
||||||
|
|
||||||
|
err = update.Update(url, sum)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败: ", err)
|
||||||
|
} else {
|
||||||
|
log.Info("更新成功!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait:
|
||||||
log.Info("按 Enter 继续....")
|
log.Info("按 Enter 继续....")
|
||||||
readLine()
|
readLine()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@ -560,3 +616,24 @@ Options:
|
|||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetWorkDir() {
|
||||||
|
args := make([]string, 0, len(os.Args))
|
||||||
|
for i := 1; i < len(os.Args); i++ {
|
||||||
|
if os.Args[i] == "-w" {
|
||||||
|
i++ // skip value field
|
||||||
|
} else if !strings.HasPrefix(os.Args[i], "-w") {
|
||||||
|
args = append(args, os.Args[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proc := exec.Command(os.Args[0], args...)
|
||||||
|
proc.Stdin = os.Stdin
|
||||||
|
proc.Stdout = os.Stdout
|
||||||
|
proc.Stderr = os.Stderr
|
||||||
|
proc.Dir = wd
|
||||||
|
err := proc.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
@ -24,10 +24,18 @@ func getLoginInfo(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
|||||||
return bot.CQGetLoginInfo()
|
return bot.CQGetLoginInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getQiDianAccountInfo(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||||
|
return bot.CQGetQiDianAccountInfo()
|
||||||
|
}
|
||||||
|
|
||||||
func getFriendList(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
func getFriendList(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||||
return bot.CQGetFriendList()
|
return bot.CQGetFriendList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deleteFriend(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQDeleteFriend(p.Get("id").Int())
|
||||||
|
}
|
||||||
|
|
||||||
func getGroupList(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
func getGroupList(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
return bot.CQGetGroupList(p.Get("no_cache").Bool())
|
return bot.CQGetGroupList(p.Get("no_cache").Bool())
|
||||||
}
|
}
|
||||||
@ -145,7 +153,7 @@ func setGroupAdmin(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendGroupNotice(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
func sendGroupNotice(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
return bot.CQSetGroupMemo(p.Get("group_id").Int(), p.Get("content").Str)
|
return bot.CQSetGroupMemo(p.Get("group_id").Int(), p.Get("content").Str, p.Get("image").String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func setGroupLeave(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
func setGroupLeave(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
@ -254,6 +262,18 @@ func uploadGroupFile(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
|||||||
return bot.CQUploadGroupFile(p.Get("group_id").Int(), p.Get("file").Str, p.Get("name").Str, p.Get("folder").Str)
|
return bot.CQUploadGroupFile(p.Get("group_id").Int(), p.Get("file").Str, p.Get("name").Str, p.Get("folder").Str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func groupFileCreateFolder(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQGroupFileCreateFolder(p.Get("group_id").Int(), p.Get("folder_id").Str, p.Get("name").Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteGroupFolder(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQGroupFileDeleteFolder(p.Get("group_id").Int(), p.Get("folder_id").Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteGroupFile(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQGroupFileDeleteFile(p.Get("group_id").Int(), p.Get("folder_id").Str, p.Get("file_id").Str, int32(p.Get("bus_id").Int()))
|
||||||
|
}
|
||||||
|
|
||||||
func getGroupMsgHistory(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
func getGroupMsgHistory(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
return bot.CQGetGroupMessageHistory(p.Get("group_id").Int(), p.Get("message_seq").Int())
|
return bot.CQGetGroupMessageHistory(p.Get("group_id").Int(), p.Get("message_seq").Int())
|
||||||
}
|
}
|
||||||
@ -322,10 +342,19 @@ func handleQuickOperation(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
|||||||
return bot.CQHandleQuickOperation(p.Get("context"), p.Get("operation"))
|
return bot.CQHandleQuickOperation(p.Get("context"), p.Get("operation"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getModelShow(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQGetModelShow(p.Get("model").String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func setModelShow(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQSetModelShow(p.Get("model").String(), p.Get("model_show").String())
|
||||||
|
}
|
||||||
|
|
||||||
// API 是go-cqhttp当前支持的所有api的映射表
|
// API 是go-cqhttp当前支持的所有api的映射表
|
||||||
var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
||||||
"get_login_info": getLoginInfo,
|
"get_login_info": getLoginInfo,
|
||||||
"get_friend_list": getFriendList,
|
"get_friend_list": getFriendList,
|
||||||
|
"delete_friend": deleteFriend,
|
||||||
"get_group_list": getGroupList,
|
"get_group_list": getGroupList,
|
||||||
"get_group_info": getGroupInfo,
|
"get_group_info": getGroupInfo,
|
||||||
"get_group_member_list": getGroupMemberList,
|
"get_group_member_list": getGroupMemberList,
|
||||||
@ -362,6 +391,9 @@ var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
|||||||
"get_group_root_files": getGroupRootFiles,
|
"get_group_root_files": getGroupRootFiles,
|
||||||
"get_group_files_by_folder": getGroupFilesByFolder,
|
"get_group_files_by_folder": getGroupFilesByFolder,
|
||||||
"get_group_file_url": getGroupFileURL,
|
"get_group_file_url": getGroupFileURL,
|
||||||
|
"create_group_file_folder": groupFileCreateFolder,
|
||||||
|
"delete_group_folder": deleteGroupFolder,
|
||||||
|
"delete_group_file": deleteGroupFile,
|
||||||
"upload_group_file": uploadGroupFile,
|
"upload_group_file": uploadGroupFile,
|
||||||
"get_group_msg_history": getGroupMsgHistory,
|
"get_group_msg_history": getGroupMsgHistory,
|
||||||
"_get_vip_info": getVipInfo,
|
"_get_vip_info": getVipInfo,
|
||||||
@ -378,6 +410,9 @@ var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
|||||||
"check_url_safely": checkURLSafely,
|
"check_url_safely": checkURLSafely,
|
||||||
"set_group_anonymous_ban": setGroupAnonymousBan,
|
"set_group_anonymous_ban": setGroupAnonymousBan,
|
||||||
".handle_quick_operation": handleQuickOperation,
|
".handle_quick_operation": handleQuickOperation,
|
||||||
|
"qidian_get_account_info": getQiDianAccountInfo,
|
||||||
|
"_get_model_show": getModelShow,
|
||||||
|
"_set_model_show": setModelShow,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *apiCaller) callAPI(action string, p resultGetter) coolq.MSG {
|
func (api *apiCaller) callAPI(action string, p resultGetter) coolq.MSG {
|
||||||
|
@ -51,8 +51,12 @@ func RunHTTPServerAndClients(bot *coolq.CQBot, conf *config.HTTPServer) {
|
|||||||
var (
|
var (
|
||||||
s = new(httpServer)
|
s = new(httpServer)
|
||||||
authToken = conf.AccessToken
|
authToken = conf.AccessToken
|
||||||
addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
addr string
|
||||||
)
|
)
|
||||||
|
if conf.Host == "" || conf.Port == 0 {
|
||||||
|
goto client
|
||||||
|
}
|
||||||
|
addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
s.engine = gin.New()
|
s.engine = gin.New()
|
||||||
s.bot = bot
|
s.bot = bot
|
||||||
@ -86,16 +90,26 @@ func RunHTTPServerAndClients(bot *coolq.CQBot, conf *config.HTTPServer) {
|
|||||||
if authToken != "" {
|
if authToken != "" {
|
||||||
s.engine.Use(func(c *gin.Context) {
|
s.engine.Use(func(c *gin.Context) {
|
||||||
auth := c.Request.Header.Get("Authorization")
|
auth := c.Request.Header.Get("Authorization")
|
||||||
switch {
|
if auth == "" {
|
||||||
case auth != "":
|
headAuth := c.Query("access_token")
|
||||||
if strings.SplitN(auth, " ", 2)[1] != authToken {
|
switch {
|
||||||
|
case headAuth == "":
|
||||||
c.AbortWithStatus(401)
|
c.AbortWithStatus(401)
|
||||||
|
return
|
||||||
|
case headAuth != authToken:
|
||||||
|
c.AbortWithStatus(403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auth := strings.SplitN(auth, " ", 2)
|
||||||
|
switch {
|
||||||
|
case len(auth) != 2 || auth[1] == "":
|
||||||
|
c.AbortWithStatus(401)
|
||||||
|
return
|
||||||
|
case auth[1] != authToken:
|
||||||
|
c.AbortWithStatus(403)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
case c.Query("access_token") != authToken:
|
|
||||||
c.AbortWithStatus(401)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
c.Next()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -116,7 +130,7 @@ func RunHTTPServerAndClients(bot *coolq.CQBot, conf *config.HTTPServer) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
client:
|
||||||
for _, c := range conf.Post {
|
for _, c := range conf.Post {
|
||||||
if c.URL != "" {
|
if c.URL != "" {
|
||||||
go newHTTPClient().Run(c.URL, c.Secret, conf.Filter, conf.Timeout, bot)
|
go newHTTPClient().Run(c.URL, c.Secret, conf.Filter, conf.Timeout, bot)
|
||||||
@ -189,7 +203,7 @@ func (c *HTTPClient) onBotPushEvent(m *bytes.Buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *httpServer) HandleActions(c *gin.Context) {
|
func (s *httpServer) HandleActions(c *gin.Context) {
|
||||||
action := strings.ReplaceAll(c.Param("action"), "_async", "")
|
action := strings.TrimSuffix(c.Param("action"), "_async")
|
||||||
log.Debugf("HTTPServer接收到API调用: %v", action)
|
log.Debugf("HTTPServer接收到API调用: %v", action)
|
||||||
c.JSON(200, s.api.callAPI(action, httpContext{ctx: c}))
|
c.JSON(200, s.api.callAPI(action, httpContext{ctx: c}))
|
||||||
}
|
}
|
||||||
|
36
server/pprof.go
Normal file
36
server/pprof.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/pprof"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/global/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunPprofServer 启动 pprof 性能分析服务器
|
||||||
|
func RunPprofServer(conf *config.PprofServer) {
|
||||||
|
if conf.Disabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
engine := gin.New()
|
||||||
|
addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||||
|
pprof.Register(engine)
|
||||||
|
go func() {
|
||||||
|
log.Infof("pprof debug 服务器已启动: %v/debug/pprof", addr)
|
||||||
|
log.Warnf("警告: pprof 服务不支持鉴权, 请不要运行在公网.")
|
||||||
|
if err := engine.Run(addr); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Error(err)
|
||||||
|
log.Infof("pprof 服务启动失败, 请检查端口是否被占用.")
|
||||||
|
log.Warnf("将在五秒后退出.")
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
@ -231,7 +231,6 @@ func (c *WebSocketClient) onBotPushEvent(m *bytes.Buffer) {
|
|||||||
log.Debugf("上报Event %v 到 WS客户端 时被过滤.", utils.B2S(m.Bytes()))
|
log.Debugf("上报Event %v 到 WS客户端 时被过滤.", utils.B2S(m.Bytes()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.eventConn != nil {
|
if c.eventConn != nil {
|
||||||
log.Debugf("向WS服务器 %v 推送Event: %v", c.eventConn.RemoteAddr().String(), utils.B2S(m.Bytes()))
|
log.Debugf("向WS服务器 %v 推送Event: %v", c.eventConn.RemoteAddr().String(), utils.B2S(m.Bytes()))
|
||||||
conn := c.eventConn
|
conn := c.eventConn
|
||||||
@ -264,15 +263,29 @@ func (c *WebSocketClient) onBotPushEvent(m *bytes.Buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) {
|
func (s *webSocketServer) auth(r *http.Request) (bool, int) {
|
||||||
if s.conf.AccessToken != "" {
|
if s.token != "" { // s.token == s.conf.AccessToken
|
||||||
if auth := r.URL.Query().Get("access_token"); auth != s.token {
|
var auth string
|
||||||
if auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2); len(auth) != 2 || auth[1] != s.token {
|
if auth = r.URL.Query().Get("access_token"); auth == "" {
|
||||||
log.Warnf("已拒绝 %v 的 WebSocket 请求: Token鉴权失败", r.RemoteAddr)
|
headAuth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||||
w.WriteHeader(401)
|
if len(headAuth) != 2 || headAuth[1] == "" {
|
||||||
return
|
return false, 401
|
||||||
}
|
}
|
||||||
|
auth = headAuth[1]
|
||||||
}
|
}
|
||||||
|
if auth != s.token {
|
||||||
|
log.Warnf("已拒绝 %v 的 WebSocket 请求: Token鉴权失败", r.RemoteAddr)
|
||||||
|
return false, 403
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) {
|
||||||
|
isAuth, errReason := s.auth(r)
|
||||||
|
if !isAuth {
|
||||||
|
w.WriteHeader(errReason)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c, err := upgrader.Upgrade(w, r, nil)
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -296,14 +309,10 @@ func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *webSocketServer) api(w http.ResponseWriter, r *http.Request) {
|
func (s *webSocketServer) api(w http.ResponseWriter, r *http.Request) {
|
||||||
if s.token != "" {
|
isAuth, errReason := s.auth(r)
|
||||||
if auth := r.URL.Query().Get("access_token"); auth != s.token {
|
if !isAuth {
|
||||||
if auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2); len(auth) != 2 || auth[1] != s.token {
|
w.WriteHeader(errReason)
|
||||||
log.Warnf("已拒绝 %v 的 WebSocket 请求: Token鉴权失败", r.RemoteAddr)
|
return
|
||||||
w.WriteHeader(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c, err := upgrader.Upgrade(w, r, nil)
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -319,14 +328,10 @@ func (s *webSocketServer) api(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) {
|
func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) {
|
||||||
if s.token != "" {
|
isAuth, errReason := s.auth(r)
|
||||||
if auth := r.URL.Query().Get("access_token"); auth != s.token {
|
if !isAuth {
|
||||||
if auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2); len(auth) != 2 || auth[1] != s.token {
|
w.WriteHeader(errReason)
|
||||||
log.Warnf("已拒绝 %v 的 WebSocket 请求: Token鉴权失败", r.RemoteAddr)
|
return
|
||||||
w.WriteHeader(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c, err := upgrader.Upgrade(w, r, nil)
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -381,8 +386,8 @@ func (c *webSocketConn) handleRequest(_ *coolq.CQBot, payload []byte) {
|
|||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
j := gjson.ParseBytes(payload)
|
j := gjson.Parse(utils.B2S(payload))
|
||||||
t := strings.ReplaceAll(j.Get("action").Str, "_async", "")
|
t := strings.TrimSuffix(j.Get("action").Str, "_async")
|
||||||
log.Debugf("WS接收到API调用: %v 参数: %v", t, j.Get("params").Raw)
|
log.Debugf("WS接收到API调用: %v 参数: %v", t, j.Get("params").Raw)
|
||||||
ret := c.apiCaller.callAPI(t, j.Get("params"))
|
ret := c.apiCaller.callAPI(t, j.Get("params"))
|
||||||
if j.Get("echo").Exists() {
|
if j.Get("echo").Exists() {
|
||||||
|
Reference in New Issue
Block a user