mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-06-30 03:43:25 +00:00
Compare commits
66 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 |
9
.github/workflows/golint.yml
vendored
9
.github/workflows/golint.yml
vendored
@ -9,11 +9,20 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Tests
|
||||
run: |
|
||||
go test $(go list ./...)
|
||||
|
||||
- name: Commit back
|
||||
if: github.repository_owner == 'Mrs4s'
|
||||
continue-on-error: true
|
||||
|
11
.github/workflows/suggester.yml
vendored
11
.github/workflows/suggester.yml
vendored
@ -9,12 +9,21 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Tests
|
||||
run: |
|
||||
go test $(go list ./...)
|
||||
|
||||
- name: Suggester
|
||||
uses: reviewdog/action-suggester@v1
|
||||
with:
|
||||
tool_name: golangci-lint
|
||||
tool_name: golangci-lint
|
||||
|
@ -33,14 +33,14 @@ linters:
|
||||
#- goconst
|
||||
- gocritic
|
||||
#- gocyclo
|
||||
- gofumpt
|
||||
- gofmt
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
#- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
#- misspell
|
||||
- nolintlint
|
||||
- rowserrcheck
|
||||
- staticcheck
|
||||
@ -55,7 +55,7 @@ linters:
|
||||
- prealloc
|
||||
- predeclared
|
||||
- asciicheck
|
||||
- golint
|
||||
- revive
|
||||
- forbidigo
|
||||
- makezero
|
||||
#- 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:tts] | [文本转语音] |
|
||||
|
||||
[图片]: docs/cqhttp.md#%E5%9B%BE%E7%89%87
|
||||
[红包]: docs/cqhttp.md#%E7%BA%A2%E5%8C%85
|
||||
[戳一戳]: docs/cqhttp.md#%E6%88%B3%E4%B8%80%E6%88%B3
|
||||
[礼物]: docs/cqhttp.md#%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
|
||||
[一种 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
|
||||
[文本转语音]: docs/cqhttp.md#%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3
|
||||
[图片]: https://docs.go-cqhttp.org/cqcode/#%E5%9B%BE%E7%89%87
|
||||
[红包]: https://docs.go-cqhttp.org/cqcode/#%E7%BA%A2%E5%8C%85
|
||||
[戳一戳]: https://docs.go-cqhttp.org/cqcode/#%E6%88%B3%E4%B8%80%E6%88%B3
|
||||
[礼物]: https://docs.go-cqhttp.org/cqcode/#%E7%A4%BC%E7%89%A9
|
||||
[合并转发消息节点]: 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 的图片消息(装逼大图)]: https://docs.go-cqhttp.org/cqcode/#cardimage
|
||||
[文本转语音]: https://docs.go-cqhttp.org/cqcode/#%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3
|
||||
|
||||
</details>
|
||||
|
||||
@ -200,19 +200,19 @@ go-cqhttp兼容[OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v1
|
||||
| /get_group_file_url | [获取群文件资源链接] |
|
||||
| /get_status | [获取状态] |
|
||||
|
||||
[设置群头像]: docs/cqhttp.md#%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
|
||||
[获取消息]: docs/cqhttp.md#%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
|
||||
[发送合并转发(群)]: docs/cqhttp.md#%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
|
||||
[图片 ocr]: docs/cqhttp.md#%E5%9B%BE%E7%89%87ocr
|
||||
[获取群系统消息]: docs/cqhttp.md#%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
|
||||
[获取群根目录文件列表]: 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
|
||||
[获取群子目录文件列表]: 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
|
||||
[获取群文件资源链接]: 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
|
||||
[获取状态]: docs/cqhttp.md#%E8%8E%B7%E5%8F%96%E7%8A%B6%E6%80%81
|
||||
[设置群头像]: https://docs.go-cqhttp.org/api/#%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%A4%B4%E5%83%8F
|
||||
[获取图片信息]: https://docs.go-cqhttp.org/api/#%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%E6%B6%88%E6%81%AF
|
||||
[获取合并转发内容]: 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
|
||||
[发送合并转发(群)]: 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
|
||||
[获取中文分词]: 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]: https://docs.go-cqhttp.org/api/#%E5%9B%BE%E7%89%87-ocr
|
||||
[获取群系统消息]: 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
|
||||
[获取群文件系统信息]: 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
|
||||
[获取群根目录文件列表]: 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
|
||||
[获取群子目录文件列表]: 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
|
||||
[获取群文件资源链接]: 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
|
||||
[获取状态]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%8A%B6%E6%80%81
|
||||
|
||||
</details>
|
||||
|
||||
@ -260,12 +260,14 @@ go-cqhttp兼容[OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v1
|
||||
| 事件类型 | 拓展 Event |
|
||||
| -------- | ---------------- |
|
||||
| 通知事件 | [好友戳一戳] |
|
||||
| 通知事件 | [群内戳一戳] |
|
||||
| 通知事件 | [群成员名片更新] |
|
||||
| 通知事件 | [接收到离线文件] |
|
||||
|
||||
[好友戳一戳]: docs/cqhttp.md#%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
|
||||
[接收到离线文件]: 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/#%E5%A5%BD%E5%8F%8B%E6%88%B3%E4%B8%80%E6%88%B3
|
||||
[群内戳一戳]: https://docs.go-cqhttp.org/event/#%E7%BE%A4%E5%86%85%E6%88%B3%E4%B8%80%E6%88%B3
|
||||
[群成员名片更新]: 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>
|
||||
|
||||
|
146
coolq/api.go
146
coolq/api.go
@ -46,6 +46,18 @@ func (bot *CQBot) CQGetLoginInfo() MSG {
|
||||
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 获取好友列表
|
||||
//
|
||||
// https://git.io/Jtz1L
|
||||
@ -61,6 +73,20 @@ func (bot *CQBot) CQGetFriendList() MSG {
|
||||
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 获取群列表
|
||||
//
|
||||
// https://git.io/Jtz1t
|
||||
@ -260,6 +286,54 @@ func (bot *CQBot) CQUploadGroupFile(groupID int64, file, name, folder string) MS
|
||||
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-获取中文分词
|
||||
//
|
||||
// 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
|
||||
@ -302,9 +376,9 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, i interface{}, autoEscape bo
|
||||
fixAt(elem)
|
||||
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
||||
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})
|
||||
}
|
||||
str = func() string {
|
||||
@ -329,7 +403,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, i interface{}, autoEscape bo
|
||||
fixAt(elem)
|
||||
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
||||
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)
|
||||
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})
|
||||
if ret == nil || ret.Id == -1 {
|
||||
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考输出")
|
||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||
}
|
||||
return OK(MSG{
|
||||
"message_id": bot.InsertGroupMessage(ret),
|
||||
@ -475,7 +549,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, i interface{
|
||||
elem := bot.ConvertObjectMessage(m, false)
|
||||
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
||||
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)
|
||||
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})
|
||||
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)
|
||||
return OK(MSG{"message_id": mid})
|
||||
@ -546,12 +620,26 @@ func (bot *CQBot) CQSetGroupName(groupID int64, name string) MSG {
|
||||
// CQSetGroupMemo 扩展API-发送群公告
|
||||
//
|
||||
// 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.SelfPermission() == client.Member {
|
||||
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 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)))
|
||||
if !global.PathExists(local) {
|
||||
if data, err := global.GetBytes(msg["url"].(string)); err == nil {
|
||||
_ = ioutil.WriteFile(local, data, 0644)
|
||||
f, _ := os.OpenFile(local, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o0644)
|
||||
if body, err := global.HTTPGetReadCloser(msg["url"].(string)); err == nil {
|
||||
_, _ = f.ReadFrom(body)
|
||||
_ = body.Close()
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
msg["file"] = local
|
||||
return OK(msg)
|
||||
@ -1042,7 +1133,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) MSG {
|
||||
if isGroup {
|
||||
return gid.(int64)
|
||||
}
|
||||
return sender.Uin
|
||||
return 0
|
||||
}(), false),
|
||||
})
|
||||
}
|
||||
@ -1308,6 +1399,8 @@ func (bot *CQBot) CQGetVersionInfo() MSG {
|
||||
return 2
|
||||
case client.MacOS:
|
||||
return 3
|
||||
case client.QiDian:
|
||||
return 4
|
||||
default:
|
||||
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 生成成功返回值
|
||||
func OK(data interface{}) MSG {
|
||||
return MSG{"data": data, "retcode": 0, "status": "ok"}
|
||||
|
27
coolq/bot.go
27
coolq/bot.go
@ -23,6 +23,7 @@ import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
)
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
@ -58,7 +59,9 @@ func NewQQBot(cli *client.QQClient, conf *config.Config) *CQBot {
|
||||
}
|
||||
if enableLevelDB {
|
||||
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 {
|
||||
log.Fatalf("打开数据库失败, 如果频繁遇到此问题请清理 data/leveldb 文件夹或关闭数据库功能。")
|
||||
}
|
||||
@ -129,10 +132,7 @@ func (bot *CQBot) GetMessage(mid int32) MSG {
|
||||
m := MSG{}
|
||||
data, err := bot.db.Get(binary.ToBytes(mid), nil)
|
||||
if err == nil {
|
||||
buff := global.NewBuffer()
|
||||
defer global.PutBuffer(buff)
|
||||
buff.Write(binary.GZipUncompress(data))
|
||||
err = gob.NewDecoder(buff).Decode(&m)
|
||||
err = gob.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||
if err == nil {
|
||||
return m
|
||||
}
|
||||
@ -271,7 +271,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
||||
return 0
|
||||
}
|
||||
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 {
|
||||
log.Warnf("警告: 私聊 %v 消息语音上传失败: %v", target, err)
|
||||
continue
|
||||
@ -343,9 +343,6 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
||||
}
|
||||
log.Errorf("错误: 请先添加 %v(%v) 为好友", nickname, target)
|
||||
}
|
||||
if id == -1 {
|
||||
return -1
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
@ -368,7 +365,7 @@ func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 {
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
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)
|
||||
return -1
|
||||
}
|
||||
@ -384,7 +381,7 @@ func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 {
|
||||
"target": m.Target,
|
||||
"sender": m.Sender,
|
||||
"time": m.Time,
|
||||
"message": ToStringMessage(m.Elements, m.Sender.Uin, true),
|
||||
"message": ToStringMessage(m.Elements, 0, true),
|
||||
}
|
||||
id := toGlobalID(m.Sender.Uin, m.Id)
|
||||
if bot.db != nil {
|
||||
@ -394,7 +391,7 @@ func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 {
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
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)
|
||||
return -1
|
||||
}
|
||||
@ -411,8 +408,8 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
||||
"group-name": m.GroupName,
|
||||
"target": target,
|
||||
"sender": m.Sender,
|
||||
"time": time.Now().Unix(),
|
||||
"message": ToStringMessage(m.Elements, m.Sender.Uin, true),
|
||||
"time": int32(time.Now().Unix()),
|
||||
"message": ToStringMessage(m.Elements, 0, true),
|
||||
}
|
||||
id := toGlobalID(m.Sender.Uin, m.Id)
|
||||
if bot.db != nil {
|
||||
@ -422,7 +419,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
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)
|
||||
return -1
|
||||
}
|
||||
|
419
coolq/cqcode.go
419
coolq/cqcode.go
@ -14,12 +14,10 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
@ -28,6 +26,7 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
"github.com/Mrs4s/go-cqhttp/global/codec"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -48,19 +47,6 @@ var IgnoreInvalidCQCode = false
|
||||
// SplitURL 是否分割URL
|
||||
var SplitURL = false
|
||||
|
||||
// magicCQ 代表 uint32([]byte("[CQ:"))
|
||||
var magicCQ = uint32(0)
|
||||
|
||||
func init() {
|
||||
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 (
|
||||
maxImageSize = 1024 * 1024 * 30 // 30MB
|
||||
maxVideoSize = 1024 * 1024 * 100 // 100MB
|
||||
@ -128,12 +114,8 @@ func (e *PokeElement) Type() message.ElementType {
|
||||
}
|
||||
|
||||
// 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))
|
||||
ur := false
|
||||
if len(isRaw) != 0 {
|
||||
ur = isRaw[0]
|
||||
}
|
||||
m := &message.SendingMessage{Elements: e}
|
||||
reply := m.FirstOrNil(func(e message.IMessageElement) bool {
|
||||
_, ok := e.(*message.ReplyElement)
|
||||
@ -141,21 +123,25 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
||||
})
|
||||
if reply != nil {
|
||||
replyElem := reply.(*message.ReplyElement)
|
||||
rid := groupID
|
||||
if rid == 0 {
|
||||
rid = replyElem.Sender
|
||||
}
|
||||
if ExtraReplyData {
|
||||
r = append(r, MSG{
|
||||
"type": "reply",
|
||||
"data": map[string]string{
|
||||
"id": fmt.Sprint(toGlobalID(id, reply.(*message.ReplyElement).ReplySeq)),
|
||||
"seq": string(replyElem.ReplySeq),
|
||||
"id": fmt.Sprint(toGlobalID(rid, replyElem.ReplySeq)),
|
||||
"seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10),
|
||||
"qq": strconv.FormatInt(replyElem.Sender, 10),
|
||||
"time": string(replyElem.Time),
|
||||
"text": CQCodeEscapeValue(CQCodeEscapeText(ToStringMessage(replyElem.Elements, id))),
|
||||
"time": strconv.FormatInt(int64(replyElem.Time), 10),
|
||||
"text": ToStringMessage(replyElem.Elements, groupID),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
r = append(r, MSG{
|
||||
"type": "reply",
|
||||
"data": map[string]string{"id": fmt.Sprint(toGlobalID(id, replyElem.ReplySeq))},
|
||||
"data": map[string]string{"id": fmt.Sprint(toGlobalID(rid, replyElem.ReplySeq))},
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -175,10 +161,6 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
||||
"data": map[string]string{"text": o.Content},
|
||||
}
|
||||
case *message.LightAppElement:
|
||||
// m = MSG{
|
||||
// "type": "text",
|
||||
// "data": map[string]string{"text": o.Content},
|
||||
// }
|
||||
m = MSG{
|
||||
"type": "json",
|
||||
"data": map[string]string{"data": o.Content},
|
||||
@ -211,64 +193,29 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
||||
"data": map[string]string{"id": fmt.Sprint(o.Index)},
|
||||
}
|
||||
case *message.VoiceElement:
|
||||
if ur {
|
||||
m = MSG{
|
||||
"type": "record",
|
||||
"data": map[string]string{"file": o.Name},
|
||||
}
|
||||
} else {
|
||||
m = MSG{
|
||||
"type": "record",
|
||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||
}
|
||||
m = MSG{
|
||||
"type": "record",
|
||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||
}
|
||||
case *message.ShortVideoElement:
|
||||
if ur {
|
||||
m = MSG{
|
||||
"type": "video",
|
||||
"data": map[string]string{"file": o.Name},
|
||||
}
|
||||
} else {
|
||||
m = MSG{
|
||||
"type": "video",
|
||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||
}
|
||||
m = MSG{
|
||||
"type": "video",
|
||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||
}
|
||||
case *message.ImageElement:
|
||||
if ur {
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"data": map[string]string{"file": o.Filename},
|
||||
}
|
||||
} else {
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"data": map[string]string{"file": o.Filename, "url": o.Url},
|
||||
}
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"data": map[string]string{"file": o.Filename, "url": o.Url},
|
||||
}
|
||||
case *message.GroupImageElement:
|
||||
if ur {
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"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},
|
||||
}
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url},
|
||||
}
|
||||
case *message.FriendImageElement:
|
||||
if ur {
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"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},
|
||||
}
|
||||
m = MSG{
|
||||
"type": "image",
|
||||
"data": map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url},
|
||||
}
|
||||
case *message.GroupFlashImgElement:
|
||||
return []MSG{{
|
||||
@ -303,7 +250,12 @@ func ToArrayMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r []M
|
||||
}
|
||||
|
||||
// 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
|
||||
if len(isRaw) != 0 {
|
||||
ur = isRaw[0]
|
||||
@ -316,13 +268,17 @@ func ToStringMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r st
|
||||
})
|
||||
if reply != nil {
|
||||
replyElem := reply.(*message.ReplyElement)
|
||||
rid := groupID
|
||||
if rid == 0 {
|
||||
rid = replyElem.Sender
|
||||
}
|
||||
if ExtraReplyData {
|
||||
r += fmt.Sprintf("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
|
||||
toGlobalID(id, replyElem.ReplySeq),
|
||||
write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
|
||||
toGlobalID(rid, replyElem.ReplySeq),
|
||||
replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
|
||||
CQCodeEscapeValue(CQCodeEscapeText(ToStringMessage(replyElem.Elements, id))))
|
||||
CQCodeEscapeValue(ToStringMessage(replyElem.Elements, groupID)))
|
||||
} 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 {
|
||||
@ -335,48 +291,48 @@ func ToStringMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r st
|
||||
}
|
||||
}
|
||||
case *message.TextElement:
|
||||
r += CQCodeEscapeText(o.Content)
|
||||
sb.WriteString(CQCodeEscapeText(o.Content))
|
||||
case *message.AtElement:
|
||||
if o.Target == 0 {
|
||||
r += "[CQ:at,qq=all]"
|
||||
write("[CQ:at,qq=all]")
|
||||
continue
|
||||
}
|
||||
r += fmt.Sprintf("[CQ:at,qq=%d]", o.Target)
|
||||
write("[CQ:at,qq=%d]", o.Target)
|
||||
case *message.RedBagElement:
|
||||
r += fmt.Sprintf("[CQ:redbag,title=%s]", o.Title)
|
||||
write("[CQ:redbag,title=%s]", o.Title)
|
||||
case *message.ForwardElement:
|
||||
r += fmt.Sprintf("[CQ:forward,id=%s]", o.ResId)
|
||||
write("[CQ:forward,id=%s]", o.ResId)
|
||||
case *message.FaceElement:
|
||||
r += fmt.Sprintf(`[CQ:face,id=%d]`, o.Index)
|
||||
write(`[CQ:face,id=%d]`, o.Index)
|
||||
case *message.VoiceElement:
|
||||
if ur {
|
||||
r += fmt.Sprintf(`[CQ:record,file=%s]`, o.Name)
|
||||
write(`[CQ:record,file=%s]`, o.Name)
|
||||
} 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:
|
||||
if ur {
|
||||
r += fmt.Sprintf(`[CQ:video,file=%s]`, o.Name)
|
||||
write(`[CQ:video,file=%s]`, o.Name)
|
||||
} 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:
|
||||
if ur {
|
||||
r += fmt.Sprintf(`[CQ:image,file=%s]`, o.Filename)
|
||||
write(`[CQ:image,file=%s]`, o.Filename)
|
||||
} 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:
|
||||
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 {
|
||||
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:
|
||||
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 {
|
||||
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:
|
||||
return fmt.Sprintf("[CQ:image,type=flash,file=%s]", o.Filename)
|
||||
@ -384,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)
|
||||
case *message.ServiceElement:
|
||||
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 {
|
||||
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:
|
||||
r += fmt.Sprintf(`[CQ:json,data=%s]`, CQCodeEscapeValue(o.Content))
|
||||
// r += CQCodeEscapeText(o.Content)
|
||||
write(`[CQ:json,data=%s]`, CQCodeEscapeValue(o.Content))
|
||||
}
|
||||
}
|
||||
r = sb.String() // 内部已拷贝
|
||||
global.PutBuffer(sb)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
d := map[string]string{}
|
||||
ptr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
|
||||
l := len(s)
|
||||
i, j, CQBegin := 0, 0, 0
|
||||
|
||||
saveCQCode := func() {
|
||||
if t == "reply" { // reply 特殊处理
|
||||
@ -487,7 +441,11 @@ func (bot *CQBot) ConvertStringMessage(s string, isGroup bool) (r []message.IMes
|
||||
}
|
||||
elem, err := bot.ToElement(t, d, isGroup)
|
||||
if err != nil {
|
||||
org := s[CQBegin:i]
|
||||
org := "[CQ:" + t
|
||||
for k, v := range d {
|
||||
org += "," + k + "=" + v
|
||||
}
|
||||
org += "]"
|
||||
if !IgnoreInvalidCQCode {
|
||||
log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err)
|
||||
r = append(r, message.NewText(org))
|
||||
@ -504,81 +462,65 @@ func (bot *CQBot) ConvertStringMessage(s string, isGroup bool) (r []message.IMes
|
||||
}
|
||||
}
|
||||
|
||||
S1: // Plain Text
|
||||
for ; i < l; i++ {
|
||||
if *(*byte)(add(ptr, uintptr(i))) == '[' && i+4 < l &&
|
||||
*(*uint32)(add(ptr, uintptr(i))) == magicCQ { // Magic :uint32([]byte("[CQ:"))
|
||||
if i > j {
|
||||
if SplitURL {
|
||||
for _, str := range global.SplitURL(CQCodeUnescapeText(s[j:i])) {
|
||||
r = append(r, message.NewText(str))
|
||||
}
|
||||
} else {
|
||||
r = append(r, message.NewText(CQCodeUnescapeText(s[j:i])))
|
||||
for raw != "" {
|
||||
i := 0
|
||||
for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") {
|
||||
i++
|
||||
}
|
||||
if i > 0 {
|
||||
if SplitURL {
|
||||
for _, txt := range global.SplitURL(CQCodeUnescapeText(raw[:i])) {
|
||||
r = append(r, message.NewText(txt))
|
||||
}
|
||||
} else {
|
||||
r = append(r, message.NewText(CQCodeUnescapeText(raw[:i])))
|
||||
}
|
||||
CQBegin = i
|
||||
i += 4
|
||||
j = i
|
||||
goto S2
|
||||
}
|
||||
}
|
||||
goto End
|
||||
S2: // CQCode Type
|
||||
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
|
||||
|
||||
if i+4 > len(raw) {
|
||||
return
|
||||
}
|
||||
}
|
||||
goto End
|
||||
S3: // CQCode param key
|
||||
for ; i < l; i++ {
|
||||
if *(*byte)(add(ptr, uintptr(i))) == '=' {
|
||||
key = s[j:i]
|
||||
raw = raw[i+4:] // skip "[CQ:"
|
||||
i = 0
|
||||
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||
i++
|
||||
j = i
|
||||
goto S4
|
||||
}
|
||||
}
|
||||
goto End
|
||||
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
|
||||
if i+1 > len(raw) {
|
||||
return
|
||||
}
|
||||
}
|
||||
goto End
|
||||
End:
|
||||
if i > j {
|
||||
if SplitURL {
|
||||
for _, str := range global.SplitURL(CQCodeUnescapeText(s[j:i])) {
|
||||
r = append(r, message.NewText(str))
|
||||
t = raw[:i]
|
||||
for k := range d { // clear the map, reuse it
|
||||
delete(d, k)
|
||||
}
|
||||
raw = raw[i:]
|
||||
i = 0
|
||||
for {
|
||||
if raw[0] == ']' {
|
||||
saveCQCode()
|
||||
raw = raw[1:]
|
||||
break
|
||||
}
|
||||
} else {
|
||||
r = append(r, message.NewText(CQCodeUnescapeText(s[j:i])))
|
||||
raw = raw[1:]
|
||||
|
||||
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
|
||||
@ -586,6 +528,7 @@ End:
|
||||
|
||||
// ConvertObjectMessage 将消息JSON对象转为消息元素数组
|
||||
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []message.IMessageElement) {
|
||||
d := make(map[string]string)
|
||||
convertElem := func(e gjson.Result) {
|
||||
t := e.Get("type").Str
|
||||
if t == "reply" && isGroup {
|
||||
@ -669,7 +612,9 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
||||
}
|
||||
return
|
||||
}
|
||||
d := make(map[string]string)
|
||||
for i := range d {
|
||||
delete(d, i)
|
||||
}
|
||||
e.Get("data").ForEach(func(key, value gjson.Result) bool {
|
||||
d[key.Str] = value.String()
|
||||
return true
|
||||
@ -690,9 +635,10 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
||||
return bot.ConvertStringMessage(m.Str, isGroup)
|
||||
}
|
||||
if m.IsArray() {
|
||||
for _, e := range m.Array() {
|
||||
m.ForEach(func(_, e gjson.Result) bool {
|
||||
convertElem(e)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
if m.IsObject() {
|
||||
convertElem(m)
|
||||
@ -751,7 +697,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
||||
if i, ok := img.(*message.GroupImageElement); ok {
|
||||
return &message.GroupShowPicElement{GroupImageElement: *i, EffectId: int32(id)}, nil
|
||||
}
|
||||
return img, nil // 私聊还没做
|
||||
return img, nil
|
||||
}
|
||||
|
||||
case "poke":
|
||||
@ -778,7 +724,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &message.VoiceElement{Data: data}, nil
|
||||
return &message.VoiceElement{Data: codec.RecodeTo24K(data)}, nil
|
||||
case "record":
|
||||
f := d["file"]
|
||||
data, err := global.FindFile(f, d["cache"], global.VoicePath)
|
||||
@ -823,7 +769,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
||||
if !info.Get("track_info").Exists() {
|
||||
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
|
||||
mid := info.Get("track_info.mid").Str
|
||||
albumMid := info.Get("track_info.album.mid").Str
|
||||
@ -831,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"
|
||||
purl := gjson.ParseBytes(pinfo).Get("url_mid.data.midurlinfo.0.purl").Str
|
||||
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
|
||||
if d["content"] != "" {
|
||||
content = d["content"]
|
||||
@ -874,19 +816,21 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
||||
}
|
||||
if d["type"] == "custom" {
|
||||
if d["subtype"] != "" {
|
||||
subtype := map[string]int{
|
||||
"qq": message.QQMusic,
|
||||
"163": message.CloudMusic,
|
||||
"migu": message.MiguMusic,
|
||||
"kugou": message.KugouMusic,
|
||||
"kuwo": message.KuwoMusic,
|
||||
}
|
||||
musicType := 0
|
||||
if tp, ok := subtype[d["subtype"]]; ok {
|
||||
musicType = tp
|
||||
var subType int
|
||||
switch d["subtype"] {
|
||||
default:
|
||||
subType = message.QQMusic
|
||||
case "163":
|
||||
subType = message.CloudMusic
|
||||
case "migu":
|
||||
subType = message.MiguMusic
|
||||
case "kugou":
|
||||
subType = message.KugouMusic
|
||||
case "kuwo":
|
||||
subType = message.KuwoMusic
|
||||
}
|
||||
return &message.MusicShareElement{
|
||||
MusicType: musicType,
|
||||
MusicType: subType,
|
||||
Title: d["title"],
|
||||
Summary: d["content"],
|
||||
Url: d["url"],
|
||||
@ -924,22 +868,17 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
||||
source := d["source"]
|
||||
icon := d["icon"]
|
||||
brief := d["brief"]
|
||||
minWidth, _ := strconv.ParseInt(d["minwidth"], 10, 64)
|
||||
if minWidth == 0 {
|
||||
minWidth = 200
|
||||
}
|
||||
minHeight, _ := strconv.ParseInt(d["minheight"], 10, 64)
|
||||
if minHeight == 0 {
|
||||
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
|
||||
parseIntWithDefault := func(name string, origin int64) int64 {
|
||||
v, _ := strconv.ParseInt(d[name], 10, 64)
|
||||
if v <= 0 {
|
||||
return origin
|
||||
}
|
||||
return v
|
||||
}
|
||||
minWidth := parseIntWithDefault("minwidth", 200)
|
||||
maxWidth := parseIntWithDefault("maxwidth", 500)
|
||||
minHeight := parseIntWithDefault("minheight", 200)
|
||||
maxHeight := parseIntWithDefault("maxheight", 1000)
|
||||
img, err := bot.makeImageOrVideoElem(d, false, isGroup)
|
||||
if err != nil {
|
||||
return nil, errors.New("send cardimage faild")
|
||||
@ -1076,7 +1015,7 @@ func CQCodeUnescapeValue(content string) string {
|
||||
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用
|
||||
func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (message.IMessageElement, error) {
|
||||
f := d["file"]
|
||||
if strings.HasPrefix(f, "http") || strings.HasPrefix(f, "https") {
|
||||
if strings.HasPrefix(f, "http") {
|
||||
cache := d["cache"]
|
||||
c := d["c"]
|
||||
if cache == "" {
|
||||
@ -1091,10 +1030,11 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
return maxImageSize
|
||||
}()
|
||||
thread, _ := strconv.Atoi(c)
|
||||
if global.PathExists(cacheFile) && cache == "1" {
|
||||
exist := global.PathExists(cacheFile)
|
||||
if exist && cache == "1" {
|
||||
goto hasCacheFile
|
||||
}
|
||||
if global.PathExists(cacheFile) {
|
||||
if exist {
|
||||
_ = os.Remove(cacheFile)
|
||||
}
|
||||
if err := global.DownloadFileMultiThreading(f, cacheFile, maxSize, thread, nil); err != nil {
|
||||
@ -1111,7 +1051,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.HasPrefix(fu.Path, "/") && runtime.GOOS == `windows` {
|
||||
if runtime.GOOS == `windows` && strings.HasPrefix(fu.Path, "/") {
|
||||
fu.Path = fu.Path[1:]
|
||||
}
|
||||
info, err := os.Stat(fu.Path)
|
||||
@ -1132,6 +1072,13 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
}
|
||||
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)
|
||||
if video {
|
||||
rawPath = path.Join(global.VideoPath, f)
|
||||
@ -1152,31 +1099,23 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
}
|
||||
return &LocalVideoElement{File: rawPath}, nil
|
||||
}
|
||||
if strings.HasPrefix(f, "base64") {
|
||||
b, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(f, "base64://", ""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LocalImageElement{Stream: bytes.NewReader(b)}, nil
|
||||
}
|
||||
if !global.PathExists(rawPath) && global.PathExists(path.Join(global.ImagePathOld, f)) {
|
||||
exist := global.PathExists(rawPath)
|
||||
if !exist && global.PathExists(path.Join(global.ImagePathOld, f)) {
|
||||
exist = true
|
||||
rawPath = path.Join(global.ImagePathOld, f)
|
||||
}
|
||||
if !global.PathExists(rawPath) && global.PathExists(rawPath+".cqimg") {
|
||||
if !exist && global.PathExists(rawPath+".cqimg") {
|
||||
exist = true
|
||||
rawPath += ".cqimg"
|
||||
}
|
||||
if !global.PathExists(rawPath) && d["url"] != "" {
|
||||
if !exist && d["url"] != "" {
|
||||
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group)
|
||||
}
|
||||
if global.PathExists(rawPath) {
|
||||
file, err := os.Open(rawPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -1184,9 +1123,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
return nil, errors.New("invalid local file")
|
||||
}
|
||||
var (
|
||||
size int32
|
||||
hash []byte
|
||||
url string
|
||||
size int32
|
||||
hash []byte
|
||||
imageURL string
|
||||
)
|
||||
if path.Ext(rawPath) == ".cqimg" {
|
||||
for _, line := range strings.Split(global.ReadAllText(rawPath), "\n") {
|
||||
@ -1204,11 +1143,11 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
hash = r.ReadBytes(16)
|
||||
size = r.ReadInt32()
|
||||
r.ReadString()
|
||||
url = r.ReadString()
|
||||
imageURL = r.ReadString()
|
||||
}
|
||||
if size == 0 {
|
||||
if url != "" {
|
||||
return bot.makeImageOrVideoElem(map[string]string{"file": url}, false, group)
|
||||
if imageURL != "" {
|
||||
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
||||
}
|
||||
return nil, errors.New("img size is 0")
|
||||
}
|
||||
@ -1223,8 +1162,8 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
||||
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
||||
ok:
|
||||
if err != nil {
|
||||
if url != "" {
|
||||
return bot.makeImageOrVideoElem(map[string]string{"file": url}, false, group)
|
||||
if imageURL != "" {
|
||||
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package coolq
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var bot = CQBot{}
|
||||
@ -13,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) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
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所定义的消息上报格式
|
||||
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" {
|
||||
r = ToStringMessage(e, id, isRaw...)
|
||||
r = ToStringMessage(e, groupID, isRaw...)
|
||||
} else if format == "array" {
|
||||
r = ToArrayMessage(e, id, isRaw...)
|
||||
r = ToArrayMessage(e, groupID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
|
||||
bot.checkMedia(m.Elements)
|
||||
cqm := ToStringMessage(m.Elements, m.Sender.Uin, true)
|
||||
cqm := ToStringMessage(m.Elements, 0, true)
|
||||
if !m.Sender.IsFriend {
|
||||
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)
|
||||
fm := MSG{
|
||||
"post_type": "message",
|
||||
"post_type": func() string {
|
||||
if m.Sender.Uin == bot.Client.Uin {
|
||||
return "message_sent"
|
||||
}
|
||||
return "message"
|
||||
}(),
|
||||
"message_type": "private",
|
||||
"sub_type": "friend",
|
||||
"message_id": id,
|
||||
"user_id": m.Sender.Uin,
|
||||
"target_id": m.Target,
|
||||
"message": ToFormattedMessage(m.Elements, m.Sender.Uin, false),
|
||||
"message": ToFormattedMessage(m.Elements, 0, false),
|
||||
"raw_message": cqm,
|
||||
"font": 0,
|
||||
"self_id": c.Uin,
|
||||
@ -64,6 +69,9 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
|
||||
},
|
||||
}
|
||||
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) {
|
||||
@ -101,12 +109,15 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
|
||||
}
|
||||
gm["message_id"] = id
|
||||
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) {
|
||||
m := e.Message
|
||||
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)
|
||||
id := m.Id
|
||||
if bot.db != nil {
|
||||
@ -120,7 +131,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
||||
"temp_source": e.Session.Source,
|
||||
"message_id": id,
|
||||
"user_id": m.Sender.Uin,
|
||||
"message": ToFormattedMessage(m.Elements, m.Sender.Uin, false),
|
||||
"message": ToFormattedMessage(m.Elements, 0, false),
|
||||
"raw_message": cqm,
|
||||
"font": 0,
|
||||
"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))
|
||||
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 {
|
||||
log.Infof("成员 %v 被 %v T出了群 %v.", formatMemberName(e.Member), formatMemberName(e.Operator), formatGroupName(e.Group))
|
||||
} else {
|
||||
@ -539,7 +550,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
w.WriteUInt32(uint32(i.Size))
|
||||
w.WriteString(i.Filename)
|
||||
w.WriteString(i.Url)
|
||||
}), 0644)
|
||||
}), 0o644)
|
||||
}
|
||||
i.Filename = filename
|
||||
case *message.GroupImageElement:
|
||||
@ -550,7 +561,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
w.WriteUInt32(uint32(i.Size))
|
||||
w.WriteString(filename)
|
||||
w.WriteString(i.Url)
|
||||
}), 0644)
|
||||
}), 0o644)
|
||||
}
|
||||
case *message.FriendImageElement:
|
||||
filename := hex.EncodeToString(i.Md5) + ".image"
|
||||
@ -560,7 +571,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
w.WriteUInt32(uint32(0)) // 发送时会调用url, 大概没事
|
||||
w.WriteString(filename)
|
||||
w.WriteString(i.Url)
|
||||
}), 0644)
|
||||
}), 0o644)
|
||||
}
|
||||
case *message.GroupFlashImgElement:
|
||||
filename := hex.EncodeToString(i.Md5) + ".image"
|
||||
@ -570,7 +581,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
w.WriteUInt32(uint32(i.Size))
|
||||
w.WriteString(i.Filename)
|
||||
w.WriteString("")
|
||||
}), 0644)
|
||||
}), 0o644)
|
||||
}
|
||||
i.Filename = filename
|
||||
case *message.FriendFlashImgElement:
|
||||
@ -581,7 +592,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
w.WriteUInt32(uint32(i.Size))
|
||||
w.WriteString(i.Filename)
|
||||
w.WriteString("")
|
||||
}), 0644)
|
||||
}), 0o644)
|
||||
}
|
||||
i.Filename = filename
|
||||
case *message.VoiceElement:
|
||||
@ -593,7 +604,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
log.Warnf("语音文件 %v 下载失败: %v", i.Name, err)
|
||||
continue
|
||||
}
|
||||
_ = ioutil.WriteFile(path.Join(global.VoicePath, i.Name), b, 0644)
|
||||
_ = ioutil.WriteFile(path.Join(global.VoicePath, i.Name), b, 0o644)
|
||||
}
|
||||
case *message.ShortVideoElement:
|
||||
filename := hex.EncodeToString(i.Md5) + ".video"
|
||||
@ -605,7 +616,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement) {
|
||||
w.WriteUInt32(uint32(i.ThumbSize))
|
||||
w.WriteString(i.Name)
|
||||
w.Write(i.Uuid)
|
||||
}), 0644)
|
||||
}), 0o644)
|
||||
}
|
||||
i.Name = filename
|
||||
i.Url = bot.Client.GetShortVideoUrl(i.Uuid, i.Md5)
|
||||
|
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>
|
@ -17,7 +17,7 @@ account: # 账号相关
|
||||
encrypt: false # 是否开启密码加密
|
||||
status: 0 # 在线状态,详情请查看下方的在线状态表
|
||||
relogin: # 重连设置
|
||||
disabled: false
|
||||
# disabled: false
|
||||
delay: 3 # 重连延迟, 单位秒
|
||||
interval: 0 # 重连间隔
|
||||
max-times: 0 # 最大重连次数, 0为无限制
|
||||
@ -27,7 +27,7 @@ account: # 账号相关
|
||||
use-sso-address: true
|
||||
|
||||
heartbeat:
|
||||
disabled: false # 是否开启心跳事件上报
|
||||
# disabled: false # 是否开启心跳事件上报
|
||||
# 心跳频率, 单位秒
|
||||
# -1 为关闭心跳
|
||||
interval: 5
|
||||
@ -74,8 +74,6 @@ default-middlewares: &default
|
||||
servers:
|
||||
# HTTP 通信设置
|
||||
- http:
|
||||
# 是否关闭正向HTTP服务器
|
||||
disabled: false
|
||||
# 服务端监听地址
|
||||
host: 127.0.0.1
|
||||
# 服务端监听端口
|
||||
@ -94,8 +92,6 @@ servers:
|
||||
|
||||
# 正向WS设置
|
||||
- ws:
|
||||
# 是否禁用正向WS服务器
|
||||
disabled: true
|
||||
# 正向WS服务器监听地址
|
||||
host: 127.0.0.1
|
||||
# 正向WS服务器监听端口
|
||||
@ -104,8 +100,6 @@ servers:
|
||||
<<: *default # 引用默认中间件
|
||||
|
||||
- ws-reverse:
|
||||
# 是否禁用当前反向WS服务
|
||||
disabled: true
|
||||
# 反向WS Universal 地址
|
||||
# 注意 设置了此项地址后下面两项将会被忽略
|
||||
universal: ws://your_websocket_universal.server
|
||||
@ -117,6 +111,14 @@ servers:
|
||||
reconnect-interval: 3000
|
||||
middlewares:
|
||||
<<: *default # 引用默认中间件
|
||||
# pprof 性能分析服务器, 一般情况下不需要启用.
|
||||
# 如果遇到性能问题请上传报告给开发者处理
|
||||
# 注意: pprof服务不支持中间件、不支持鉴权. 请不要开放到公网
|
||||
- pprof:
|
||||
# pprof服务器监听地址
|
||||
host: 127.0.0.1
|
||||
# pprof服务器监听端口
|
||||
port: 7700
|
||||
|
||||
# 可添加更多
|
||||
#- ws-reverse:
|
||||
@ -131,13 +133,15 @@ database: # 数据库相关设置
|
||||
enable: true
|
||||
````
|
||||
|
||||
> 注: 开启密码加密后程序将在每次启动时要求输入解密密钥, 密钥错误会导致登录时提示密码错误.
|
||||
> 注1: 开启密码加密后程序将在每次启动时要求输入解密密钥, 密钥错误会导致登录时提示密码错误.
|
||||
> 解密后密码的哈希将储存在内存中,用于自动重连等功能. 所以此加密并不能防止内存读取.
|
||||
> 解密密钥在使用完成后并不会留存在内存中, 所以可用相对简单的字符串作为密钥
|
||||
|
||||
> 注2: 分片发送为原酷Q发送长消息的老方案, 发送速度更优/兼容性更好,但在有发言频率限制的群里,可能无法发送。关闭后将优先使用新方案, 能发送更长的消息, 但发送速度更慢,在部分老客户端将无法解析.
|
||||
> 注2: 对于不需要的通信方式,你可以使用注释将其停用(推荐),或者添加配置 `disabled: true` 将其关闭
|
||||
|
||||
> 注3:关闭心跳服务可能引起断线,请谨慎关闭
|
||||
> 注3: 分片发送为原酷Q发送长消息的老方案, 发送速度更优/兼容性更好,但在有发言频率限制的群里,可能无法发送。关闭后将优先使用新方案, 能发送更长的消息, 但发送速度更慢,在部分老客户端将无法解析.
|
||||
|
||||
> 注4:关闭心跳服务可能引起断线,请谨慎关闭
|
||||
|
||||
## 在线状态
|
||||
|
||||
@ -189,6 +193,7 @@ database: # 数据库相关设置
|
||||
| 1 | Android Phone | 无 |
|
||||
| 2 | Android Watch | 无法接收 `notify` 事件、无法接收口令红包、无法接收撤回消息 |
|
||||
| 3 | MacOS | 无 |
|
||||
| 4 | 企点 | 只能登录企点账号或企点子账号 |
|
||||
|
||||
> 注意, 根据协议的不同, 各类消息有所限制
|
||||
|
||||
|
@ -1050,6 +1050,32 @@ JSON数组:
|
||||
|
||||
`该 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`
|
||||
|
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 {
|
||||
silkPath := path.Join(silkCachePath, tempName+".silk")
|
||||
err = ioutil.WriteFile(silkPath, silkWav, 0666)
|
||||
err = ioutil.WriteFile(silkPath, silkWav, 0o666)
|
||||
}
|
||||
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"
|
||||
|
||||
//EncodeToSilk 将音频编码为Silk
|
||||
// EncodeToSilk 将音频编码为Silk
|
||||
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
||||
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"
|
||||
|
||||
//EncodeToSilk 将音频编码为Silk
|
||||
// EncodeToSilk 将音频编码为Silk
|
||||
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
||||
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"
|
||||
|
||||
//EncodeToSilk 将音频编码为Silk
|
||||
// EncodeToSilk 将音频编码为Silk
|
||||
func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) {
|
||||
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,18 +2,21 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
_ "embed" // embed the default config file
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// DefaultConfig 默认配置文件
|
||||
// defaultConfig 默认配置文件
|
||||
//go:embed default_config.yml
|
||||
var DefaultConfig string
|
||||
var defaultConfig string
|
||||
|
||||
var currentPath = getCurrentPath()
|
||||
|
||||
@ -29,7 +32,7 @@ type Config struct {
|
||||
Status int32 `yaml:"status"`
|
||||
ReLogin struct {
|
||||
Disabled bool `yaml:"disabled"`
|
||||
Delay int `yaml:"delay"`
|
||||
Delay uint `yaml:"delay"`
|
||||
MaxTimes uint `yaml:"max-times"`
|
||||
Interval int `yaml:"interval"`
|
||||
}
|
||||
@ -128,10 +131,10 @@ func Get() *Config {
|
||||
once.Do(func() {
|
||||
file, err := os.Open(DefaultConfigFile)
|
||||
if err != nil {
|
||||
log.Error("获取配置文件失败: ", err)
|
||||
return
|
||||
generateConfig()
|
||||
os.Exit(0)
|
||||
}
|
||||
defer file.Close()
|
||||
defer func() { _ = file.Close() }()
|
||||
config = &Config{}
|
||||
if err = yaml.NewDecoder(file).Decode(config); err != nil {
|
||||
log.Fatal("配置文件不合法!", err)
|
||||
@ -148,3 +151,91 @@ func getCurrentPath() string {
|
||||
}
|
||||
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,11 +4,10 @@ account: # 账号相关
|
||||
uin: 1233456 # QQ账号
|
||||
password: '' # 密码为空时使用扫码登录
|
||||
encrypt: false # 是否开启密码加密
|
||||
status: 0 # 在线状态 请参考 https://github.com/Mrs4s/go-cqhttp/blob/dev/docs/config.md#在线状态
|
||||
status: 0 # 在线状态 请参考 https://docs.go-cqhttp.org/guide/config.html#在线状态
|
||||
relogin: # 重连设置
|
||||
disabled: false
|
||||
delay: 3 # 重连延迟, 单位秒
|
||||
interval: 0 # 重连间隔
|
||||
delay: 3 # 首次重连延迟, 单位秒
|
||||
interval: 3 # 重连间隔
|
||||
max-times: 0 # 最大重连次数, 0为无限制
|
||||
|
||||
# 是否使用服务器下发的新地址进行重连
|
||||
@ -16,7 +15,6 @@ account: # 账号相关
|
||||
use-sso-address: true
|
||||
|
||||
heartbeat:
|
||||
disabled: false # 是否开启心跳事件上报
|
||||
# 心跳频率, 单位秒
|
||||
# -1 为关闭心跳
|
||||
interval: 5
|
||||
@ -64,72 +62,17 @@ default-middlewares: &default
|
||||
frequency: 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 # 引用默认中间件
|
||||
# pprof 性能分析服务器, 一般情况下不需要启用.
|
||||
# 如果遇到性能问题请上传报告给开发者处理
|
||||
# 注意: pprof服务不支持中间件、不支持鉴权. 请不要开放到公网
|
||||
- pprof:
|
||||
# 是否禁用pprof性能分析服务器
|
||||
disabled: true
|
||||
# pprof服务器监听地址
|
||||
host: 127.0.0.1
|
||||
# pprof服务器监听端口
|
||||
port: 7700
|
||||
|
||||
# 可添加更多
|
||||
#- ws-reverse:
|
||||
#- ws:
|
||||
#- http:
|
||||
#- pprof:
|
||||
|
||||
database: # 数据库相关设置
|
||||
leveldb:
|
||||
# 是否启用内置leveldb数据库
|
||||
# 启用将会增加10-20MB的内存占用和一定的磁盘空间
|
||||
# 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能
|
||||
enable: true
|
||||
|
||||
# 连接服务列表
|
||||
servers:
|
||||
# 添加方式,同一连接方式可添加多个,具体配置说明请查看文档
|
||||
#- http: # http 通信
|
||||
#- ws: # 正向 Websocket
|
||||
#- ws-reverse: # 反向 Websocket
|
||||
#- pprof: #性能分析服务器
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -60,7 +61,7 @@ func ReadAllText(path string) string {
|
||||
|
||||
// WriteAllText 将给定text写入给定path
|
||||
func WriteAllText(path, text string) error {
|
||||
return ioutil.WriteFile(path, []byte(text), 0644)
|
||||
return ioutil.WriteFile(path, utils.S2B(text), 0o644)
|
||||
}
|
||||
|
||||
// Check 检测err是否为nil
|
||||
@ -83,7 +84,7 @@ func IsAMRorSILK(b []byte) bool {
|
||||
func FindFile(file, cache, p string) (data []byte, err error) {
|
||||
data, err = nil, ErrSyntax
|
||||
switch {
|
||||
case strings.HasPrefix(file, "http") || strings.HasPrefix(file, "https"):
|
||||
case strings.HasPrefix(file, "http"): // https also has prefix http
|
||||
if cache == "" {
|
||||
cache = "1"
|
||||
}
|
||||
@ -93,12 +94,12 @@ func FindFile(file, cache, p string) (data []byte, err error) {
|
||||
return ioutil.ReadFile(cacheFile)
|
||||
}
|
||||
data, err = GetBytes(file)
|
||||
_ = ioutil.WriteFile(cacheFile, data, 0644)
|
||||
_ = ioutil.WriteFile(cacheFile, data, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ func (hook *LocalHook) pathWrite(entry *logrus.Entry) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package global
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -49,33 +48,19 @@ var (
|
||||
|
||||
// GetBytes 对给定URL发送Get请求,返回响应主体
|
||||
func GetBytes(url string) ([]byte, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
reader, err := HTTPGetReadCloser(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header["User-Agent"] = []string{UserAgent}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
defer func() {
|
||||
_ = reader.Close()
|
||||
}()
|
||||
return ioutil.ReadAll(reader)
|
||||
}
|
||||
|
||||
// DownloadFile 将给定URL对应的文件下载至给定Path
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -123,7 +108,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int,
|
||||
// 初始化分块或直接下载
|
||||
initOrDownload := func() 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 {
|
||||
return err
|
||||
}
|
||||
@ -192,7 +177,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int,
|
||||
// 下载分块
|
||||
downloadBlock := func(block *BlockMetaData) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -297,3 +282,48 @@ func NeteaseMusicSongInfo(id string) (gjson.Result, error) {
|
||||
}
|
||||
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"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"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.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 {
|
||||
sp := regexp.MustCompile(`[0-9]\d*`)
|
||||
cur := sp.FindAllStringSubmatch(current, -1)
|
||||
re := sp.FindAllStringSubmatch(remote, -1)
|
||||
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])
|
||||
if curSub < reSub {
|
||||
return true
|
||||
defer func() { // 应该不会panic, 为了保险还是加个
|
||||
if err := recover(); err != nil {
|
||||
log.Warn("检查更新失败!")
|
||||
}
|
||||
}()
|
||||
sp := regexp.MustCompile(`v(\d+)\.(\d+)\.(\d+)-?(.+)?`)
|
||||
cur := sp.FindStringSubmatch(current)
|
||||
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预处理防止风控
|
||||
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)
|
||||
if len(idx) == 0 {
|
||||
return []string{s}
|
||||
|
@ -5,34 +5,31 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/kardianos/osext"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// WriteCounter 写入量计算实例
|
||||
type WriteCounter struct {
|
||||
// WriteSumCounter 写入量计算实例
|
||||
type WriteSumCounter struct {
|
||||
Total uint64
|
||||
Hash hash.Hash
|
||||
}
|
||||
|
||||
// Write 方法将写入的byte长度追加至写入的总长度Total中
|
||||
func (wc *WriteCounter) Write(p []byte) (int, error) {
|
||||
func (wc *WriteSumCounter) Write(p []byte) (int, error) {
|
||||
n := len(p)
|
||||
wc.Total += uint64(n)
|
||||
wc.PrintProgress()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// PrintProgress 方法将打印当前的总写入量
|
||||
func (wc *WriteCounter) PrintProgress() {
|
||||
fmt.Printf("\r%s", strings.Repeat(" ", 35))
|
||||
wc.Hash.Write(p)
|
||||
fmt.Printf("\r ")
|
||||
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// FromStream copy form getlantern/go-update
|
||||
@ -54,7 +51,7 @@ func FromStream(updateWith io.Reader) (err error, errRecover error) {
|
||||
filename := filepath.Base(updatePath)
|
||||
// Copy the contents of of newbinary to a the new executable file
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -6,46 +6,47 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Update go-cqhttp自我更新
|
||||
func Update(url string) {
|
||||
func Update(url string, sum []byte) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Error("更新失败: ", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
wc := WriteCounter{}
|
||||
data, err := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||
if err != nil {
|
||||
log.Error("更新失败: ", err)
|
||||
return
|
||||
wc := WriteSumCounter{
|
||||
Hash: sha256.New(),
|
||||
}
|
||||
gr, err := gzip.NewReader(bytes.NewReader(data))
|
||||
rsp, err := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||
if err != nil {
|
||||
log.Error("更新失败: ", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
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)
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
return
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if header.Name == "go-cqhttp" {
|
||||
err, _ := FromStream(tr)
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
log.Error("更新失败!", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
log.Info("更新完成!")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,34 +3,39 @@ package update
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Update go-cqhttp自我更新
|
||||
func Update(url string) {
|
||||
func Update(url string, sum []byte) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Error("更新失败: ", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
wc := WriteCounter{}
|
||||
rsp, _ := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||
wc := WriteSumCounter{
|
||||
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)
|
||||
file, err := reader.Open("go-cqhttp.exe")
|
||||
if err != nil {
|
||||
log.Error("更新失败!", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
err, _ = FromStream(file)
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
log.Error("更新失败!", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
log.Info("更新完成!")
|
||||
return nil
|
||||
}
|
||||
|
18
go.mod
18
go.mod
@ -4,12 +4,12 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20210411120128-5a90a592d071
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20210525010101-8f0cd9494d64
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/gin-contrib/pprof v1.3.0 // indirect
|
||||
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/guonaihong/gout v0.1.6
|
||||
github.com/guonaihong/gout v0.1.9
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
@ -19,15 +19,15 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
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/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/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
||||
github.com/willf/bitset v1.1.11 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect
|
||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
||||
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed
|
||||
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
|
||||
)
|
||||
|
45
go.sum
45
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/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20210411120128-5a90a592d071 h1:+1TizhfM/MkdPjnVS0VKBd2ZMML9UGFgewn3p/xCKxg=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20210411120128-5a90a592d071/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20210525010101-8f0cd9494d64 h1:36RqrFJS7TCicTLc995qgJI5NDFy93YR/zrqn4GI6nQ=
|
||||
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -20,16 +20,17 @@ 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-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
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/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/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/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.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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -41,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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
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.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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
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.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.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.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/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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/guonaihong/gout v0.1.6 h1:Txej4NYvVJLZkW0Xgw1HuWfSWow5BgLF6vqlM2kRdno=
|
||||
github.com/guonaihong/gout v0.1.6/go.mod h1:P6P8+0+toYgmhFqzLxVde+9vQbCDHrxn56V9TglC5io=
|
||||
github.com/guonaihong/gout v0.1.9 h1:splZ4PPe5Ju40akL29drcTmG75TLtzqVlBv7GeoCzH8=
|
||||
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/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
@ -117,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/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/tidwall/gjson v1.7.3 h1:9dOulDrkCJf1mwljVMhXNQr9ZL2NvajRX7A1R8c6Qxw=
|
||||
github.com/tidwall/gjson v1.7.3/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||
github.com/tidwall/gjson v1.7.5 h1:zmAN/xmX7OtpAkv4Ovfso60r/BiCi5IErCDYGNJu+uc=
|
||||
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/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
||||
@ -134,11 +137,10 @@ 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/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-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@ -147,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-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-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-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@ -157,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-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-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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -165,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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0=
|
||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
@ -196,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.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.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.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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
@ -208,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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
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-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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o=
|
||||
|
21
login.go
21
login.go
@ -63,7 +63,7 @@ func qrcodeLogin() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = ioutil.WriteFile("qrcode.png", rsp.ImageData, 0644)
|
||||
_ = ioutil.WriteFile("qrcode.png", rsp.ImageData, 0o644)
|
||||
defer func() { _ = os.Remove("qrcode.png") }()
|
||||
log.Infof("请使用手机QQ扫描二维码 (qrcode.png) : ")
|
||||
time.Sleep(time.Second)
|
||||
@ -83,21 +83,21 @@ func qrcodeLogin() error {
|
||||
continue
|
||||
}
|
||||
prevState = s.State
|
||||
if s.State == client.QRCodeCanceled {
|
||||
switch s.State {
|
||||
case client.QRCodeCanceled:
|
||||
log.Fatalf("扫码被用户取消.")
|
||||
}
|
||||
if s.State == client.QRCodeTimeout {
|
||||
case client.QRCodeTimeout:
|
||||
log.Fatalf("二维码过期")
|
||||
}
|
||||
if s.State == client.QRCodeWaitingForConfirm {
|
||||
case client.QRCodeWaitingForConfirm:
|
||||
log.Infof("扫码成功, 请在手机端确认登录.")
|
||||
}
|
||||
if s.State == client.QRCodeConfirmed {
|
||||
case client.QRCodeConfirmed:
|
||||
res, err := cli.QRCodeLogin(s.LoginInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return loginResponseProcessor(res)
|
||||
case client.QRCodeImageFetch, client.QRCodeWaitingForScan:
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,7 +115,7 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
||||
switch res.Error {
|
||||
case client.SliderNeededError:
|
||||
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("2. 使用手机QQ扫描二维码登入. (推荐)")
|
||||
log.Warn("请输入(1 - 2) (将在10秒后自动选择2):")
|
||||
@ -129,10 +129,11 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
||||
res, err = cli.SubmitTicket(text)
|
||||
continue
|
||||
}
|
||||
cli.Disconnect()
|
||||
return qrcodeLogin()
|
||||
case client.NeedCaptcha:
|
||||
log.Warnf("登录需要验证码.")
|
||||
_ = ioutil.WriteFile("captcha.jpg", res.CaptchaImage, 0644)
|
||||
_ = ioutil.WriteFile("captcha.jpg", res.CaptchaImage, 0o644)
|
||||
log.Warnf("请输入验证码 (captcha.jpg): (Enter 提交)")
|
||||
text = readLine()
|
||||
global.DelFile("captcha.jpg")
|
||||
|
176
main.go
176
main.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/aes"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"runtime"
|
||||
@ -17,8 +19,6 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
"github.com/Mrs4s/go-cqhttp/global/config"
|
||||
@ -26,6 +26,7 @@ import (
|
||||
"github.com/Mrs4s/go-cqhttp/global/update"
|
||||
"github.com/Mrs4s/go-cqhttp/server"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client"
|
||||
"github.com/guonaihong/gout"
|
||||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||
@ -42,6 +43,13 @@ var (
|
||||
c string
|
||||
d bool
|
||||
h bool
|
||||
wd string // reset work dir
|
||||
|
||||
// PasswordHash 存储QQ密码哈希供登录使用
|
||||
PasswordHash [16]byte
|
||||
|
||||
// AccountToken 存储AccountToken供登录使用
|
||||
AccountToken []byte
|
||||
|
||||
// 允许通过配置文件设置的状态列表
|
||||
allowStatus = [...]client.UserOnlineStatus{
|
||||
@ -59,6 +67,7 @@ func init() {
|
||||
flag.BoolVar(&d, "d", false, "running as a daemon")
|
||||
flag.BoolVar(&debug, "D", false, "debug mode")
|
||||
flag.BoolVar(&h, "h", false, "this help")
|
||||
flag.StringVar(&wd, "w", "", "cover the working directory")
|
||||
flag.Parse()
|
||||
|
||||
// 通过-c 参数替换 配置文件路径
|
||||
@ -67,6 +76,7 @@ func init() {
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
||||
}
|
||||
|
||||
w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24))
|
||||
if err != nil {
|
||||
log.Errorf("rotatelogs init err: %v", err)
|
||||
@ -74,13 +84,6 @@ func init() {
|
||||
}
|
||||
|
||||
conf = config.Get()
|
||||
if conf == nil {
|
||||
_ = os.WriteFile("config.yml", []byte(config.DefaultConfig), 0644)
|
||||
log.Error("未找到配置文件,默认配置文件已生成!")
|
||||
readLine()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if debug {
|
||||
conf.Output.Debug = true
|
||||
}
|
||||
@ -88,26 +91,25 @@ func init() {
|
||||
if conf.Output.Debug {
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
|
||||
log.AddHook(global.NewLocalHook(w, logFormatter, global.GetLogLevel(conf.Output.LogLevel)...))
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -120,6 +122,9 @@ func main() {
|
||||
if d {
|
||||
server.Daemon()
|
||||
}
|
||||
if wd != "" {
|
||||
resetWorkDir()
|
||||
}
|
||||
var byteKey []byte
|
||||
arg := os.Args
|
||||
if len(arg) > 1 {
|
||||
@ -164,7 +169,7 @@ func main() {
|
||||
if !global.PathExists("device.json") {
|
||||
log.Warn("虚拟设备信息不存在, 将自动生成随机设备.")
|
||||
client.GenRandomDevice()
|
||||
_ = ioutil.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0644)
|
||||
_ = ioutil.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0o644)
|
||||
log.Info("已生成设备信息并保存到 device.json 文件.")
|
||||
} else {
|
||||
log.Info("将使用 device.json 内的设备信息运行Bot.")
|
||||
@ -182,8 +187,8 @@ func main() {
|
||||
}
|
||||
log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)")
|
||||
byteKey, _ = term.ReadPassword(int(os.Stdin.Fd()))
|
||||
global.PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
||||
_ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(global.PasswordHash[:], byteKey)), 0644)
|
||||
PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
||||
_ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(PasswordHash[:], byteKey)), 0o644)
|
||||
log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.")
|
||||
readLine()
|
||||
os.Exit(0)
|
||||
@ -220,10 +225,10 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("加密存储的密码损坏,请尝试重新配置密码")
|
||||
}
|
||||
copy(global.PasswordHash[:], ph)
|
||||
copy(PasswordHash[:], ph)
|
||||
}
|
||||
} else {
|
||||
global.PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
||||
PasswordHash = md5.Sum([]byte(conf.Account.Password))
|
||||
}
|
||||
if !isFastStart {
|
||||
log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.")
|
||||
@ -240,13 +245,15 @@ func main() {
|
||||
return "Android Watch"
|
||||
case client.MacOS:
|
||||
return "MacOS"
|
||||
case client.QiDian:
|
||||
return "企点"
|
||||
}
|
||||
return "未知"
|
||||
}())
|
||||
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.PasswordMd5 = global.PasswordHash
|
||||
cli.PasswordMd5 = PasswordHash
|
||||
}
|
||||
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
||||
switch e.Type {
|
||||
@ -278,8 +285,8 @@ func main() {
|
||||
isQRCodeLogin := (conf.Account.Uin == 0 || len(conf.Account.Password) == 0) && !conf.Account.Encrypt
|
||||
isTokenLogin := false
|
||||
saveToken := func() {
|
||||
global.AccountToken = cli.GenToken()
|
||||
_ = ioutil.WriteFile("session.token", global.AccountToken, 0677)
|
||||
AccountToken = cli.GenToken()
|
||||
_ = ioutil.WriteFile("session.token", AccountToken, 0o677)
|
||||
}
|
||||
if global.PathExists("session.token") {
|
||||
token, err := ioutil.ReadFile("session.token")
|
||||
@ -345,7 +352,7 @@ func main() {
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
log.Warnf("尝试重连...")
|
||||
err := cli.TokenLogin(global.AccountToken)
|
||||
err := cli.TokenLogin(AccountToken)
|
||||
if err == nil {
|
||||
saveToken()
|
||||
return
|
||||
@ -403,7 +410,7 @@ func main() {
|
||||
if s, ok := m["ws"]; ok {
|
||||
sc := new(config.WebsocketServer)
|
||||
if err := s.Decode(sc); err != nil {
|
||||
log.Warn("读取http配置失败 :", err)
|
||||
log.Warn("读取正向Websocket配置失败 :", err)
|
||||
} else {
|
||||
go server.RunWebSocketServer(bot, sc)
|
||||
}
|
||||
@ -411,7 +418,7 @@ func main() {
|
||||
if c, ok := m["ws-reverse"]; ok {
|
||||
rc := new(config.WebsocketReverse)
|
||||
if err := c.Decode(rc); err != nil {
|
||||
log.Warn("读取http配置失败 :", err)
|
||||
log.Warn("读取反向Websocket配置失败 :", err)
|
||||
} else {
|
||||
go server.RunWebSocketClient(bot, rc)
|
||||
}
|
||||
@ -419,7 +426,7 @@ func main() {
|
||||
if p, ok := m["pprof"]; ok {
|
||||
pc := new(config.PprofServer)
|
||||
if err := p.Decode(pc); err != nil {
|
||||
log.Warn("读取http配置失败 :", err)
|
||||
log.Warn("读取pprof配置失败 :", err)
|
||||
} else {
|
||||
go server.RunPprofServer(pc)
|
||||
}
|
||||
@ -486,46 +493,76 @@ func checkUpdate() {
|
||||
|
||||
func selfUpdate(imageURL string) {
|
||||
log.Infof("正在检查更新.")
|
||||
var res string
|
||||
var res, r string
|
||||
if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest").BindBody(&res).Do(); err != nil {
|
||||
log.Warnf("检查更新失败: %v", err)
|
||||
return
|
||||
}
|
||||
info := gjson.Parse(res)
|
||||
version := info.Get("tag_name").Str
|
||||
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 {
|
||||
if coolq.Version == version {
|
||||
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 继续....")
|
||||
readLine()
|
||||
os.Exit(0)
|
||||
@ -579,3 +616,24 @@ Options:
|
||||
flag.PrintDefaults()
|
||||
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()
|
||||
}
|
||||
|
||||
func getQiDianAccountInfo(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||
return bot.CQGetQiDianAccountInfo()
|
||||
}
|
||||
|
||||
func getFriendList(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@ -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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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"))
|
||||
}
|
||||
|
||||
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的映射表
|
||||
var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
||||
"get_login_info": getLoginInfo,
|
||||
"get_friend_list": getFriendList,
|
||||
"delete_friend": deleteFriend,
|
||||
"get_group_list": getGroupList,
|
||||
"get_group_info": getGroupInfo,
|
||||
"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_files_by_folder": getGroupFilesByFolder,
|
||||
"get_group_file_url": getGroupFileURL,
|
||||
"create_group_file_folder": groupFileCreateFolder,
|
||||
"delete_group_folder": deleteGroupFolder,
|
||||
"delete_group_file": deleteGroupFile,
|
||||
"upload_group_file": uploadGroupFile,
|
||||
"get_group_msg_history": getGroupMsgHistory,
|
||||
"_get_vip_info": getVipInfo,
|
||||
@ -378,6 +410,9 @@ var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
||||
"check_url_safely": checkURLSafely,
|
||||
"set_group_anonymous_ban": setGroupAnonymousBan,
|
||||
".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 {
|
||||
|
@ -51,8 +51,12 @@ func RunHTTPServerAndClients(bot *coolq.CQBot, conf *config.HTTPServer) {
|
||||
var (
|
||||
s = new(httpServer)
|
||||
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)
|
||||
s.engine = gin.New()
|
||||
s.bot = bot
|
||||
@ -126,7 +130,7 @@ func RunHTTPServerAndClients(bot *coolq.CQBot, conf *config.HTTPServer) {
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
client:
|
||||
for _, c := range conf.Post {
|
||||
if c.URL != "" {
|
||||
go newHTTPClient().Run(c.URL, c.Secret, conf.Filter, conf.Timeout, bot)
|
||||
@ -199,7 +203,7 @@ func (c *HTTPClient) onBotPushEvent(m *bytes.Buffer) {
|
||||
}
|
||||
|
||||
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)
|
||||
c.JSON(200, s.api.callAPI(action, httpContext{ctx: c}))
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ 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)
|
||||
|
@ -231,7 +231,6 @@ func (c *WebSocketClient) onBotPushEvent(m *bytes.Buffer) {
|
||||
log.Debugf("上报Event %v 到 WS客户端 时被过滤.", utils.B2S(m.Bytes()))
|
||||
return
|
||||
}
|
||||
|
||||
if c.eventConn != nil {
|
||||
log.Debugf("向WS服务器 %v 推送Event: %v", c.eventConn.RemoteAddr().String(), utils.B2S(m.Bytes()))
|
||||
conn := c.eventConn
|
||||
@ -387,8 +386,8 @@ func (c *webSocketConn) handleRequest(_ *coolq.CQBot, payload []byte) {
|
||||
_ = c.Close()
|
||||
}
|
||||
}()
|
||||
j := gjson.ParseBytes(payload)
|
||||
t := strings.ReplaceAll(j.Get("action").Str, "_async", "")
|
||||
j := gjson.Parse(utils.B2S(payload))
|
||||
t := strings.TrimSuffix(j.Get("action").Str, "_async")
|
||||
log.Debugf("WS接收到API调用: %v 参数: %v", t, j.Get("params").Raw)
|
||||
ret := c.apiCaller.callAPI(t, j.Get("params"))
|
||||
if j.Get("echo").Exists() {
|
||||
|
Reference in New Issue
Block a user