mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-06-30 11:53:25 +00:00
Compare commits
79 Commits
v1.0.0-bet
...
v1.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
4da5d9ebfb | |||
a6f82d85be | |||
c62f193005 | |||
f386a9b94e | |||
48afb44287 | |||
75fe0294ac | |||
1290a3dd10 | |||
59209068bf | |||
757661bcf7 | |||
b8bf3f9711 | |||
eadd688e5a | |||
8c89d3c432 | |||
cfaa18b131 | |||
c975975e30 | |||
ec4ecb1093 | |||
b01ea99d1a | |||
8c94c810d6 | |||
7485b51c48 | |||
449ae96c8f | |||
7f26df3ac7 | |||
bfea93312a | |||
f8dfa8db2c | |||
74fd4bbf35 | |||
16db68e054 | |||
346e01c4e9 | |||
a69d52821f | |||
d70c2a0b70 | |||
46d0d58865 | |||
d1ca68ed32 | |||
c8958b2a42 | |||
d98ad55826 | |||
8ac460dde9 | |||
520cdd90bb | |||
5fb3233a44 | |||
c61a913c49 | |||
24c1192fa6 | |||
8773e19d2c | |||
8d6978a60d | |||
385443ee2d | |||
7f0826b594 | |||
dc48958292 | |||
e820a2a152 | |||
022eb9fd3b | |||
11a5dbb64a | |||
78d76f55e2 | |||
bf77951f8d | |||
971cb5d854 | |||
e13a5bdad0 | |||
bf4f7fb41e | |||
eaa8154a33 | |||
6240e875ce | |||
3c9433d7b7 | |||
4b4193c6e3 | |||
a1e3c57488 | |||
b43bdc1da5 | |||
aa46ab0119 | |||
92ad7d5938 | |||
ae04d26f51 | |||
d3b22a7a46 | |||
87d16f0e15 | |||
c355549c8d | |||
75e82eaf35 | |||
4c8b2e9f13 | |||
84488f9bf1 | |||
5b705273c2 | |||
4bcfe9b8f1 | |||
1863c44d11 | |||
c6f19016e1 | |||
d88c30a3c7 | |||
fd10f0d0aa | |||
a6a666fe31 | |||
c423f6d6bb | |||
605e572b87 | |||
e506622399 | |||
3326b2aa2f | |||
a38b86b763 | |||
b44f9545a8 | |||
38fff9bac0 | |||
38865584ec |
2
.github/ISSUE_TEMPLATE/bug--.md
vendored
2
.github/ISSUE_TEMPLATE/bug--.md
vendored
@ -37,6 +37,8 @@ go-cqhttp版本:
|
|||||||
连接方式:
|
连接方式:
|
||||||
使用协议:
|
使用协议:
|
||||||
|
|
||||||
|
**在最新的release版本中能否复现**
|
||||||
|
|
||||||
**bug内容**
|
**bug内容**
|
||||||
<!-- 请在这里详细描述bug的内容 -->
|
<!-- 请在这里详细描述bug的内容 -->
|
||||||
|
|
||||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -22,15 +22,13 @@ jobs:
|
|||||||
goarch: arm
|
goarch: arm
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: "386"
|
goarch: "386"
|
||||||
- goos: windows
|
|
||||||
goarch: arm64
|
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v2.1.3
|
uses: actions/setup-go@v2.1.3
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.17
|
||||||
- name: Cache downloaded module
|
- name: Cache downloaded module
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/golint.yml
vendored
2
.github/workflows/golint.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v2.1.3
|
uses: actions/setup-go@v2.1.3
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.17
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.16.2'
|
go-version: '1.17'
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v2
|
uses: goreleaser/goreleaser-action@v2
|
||||||
|
@ -38,6 +38,7 @@ builds:
|
|||||||
- 386
|
- 386
|
||||||
- amd64
|
- amd64
|
||||||
- arm
|
- arm
|
||||||
|
- arm64
|
||||||
goarm:
|
goarm:
|
||||||
- 7
|
- 7
|
||||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.16-alpine AS builder
|
FROM golang:1.17-alpine AS builder
|
||||||
|
|
||||||
RUN go env -w GO111MODULE=auto \
|
RUN go env -w GO111MODULE=auto \
|
||||||
&& go env -w CGO_ENABLED=0 \
|
&& go env -w CGO_ENABLED=0 \
|
||||||
|
230
coolq/api.go
230
coolq/api.go
@ -3,6 +3,7 @@ package coolq
|
|||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
@ -70,6 +72,47 @@ func (bot *CQBot) CQGetFriendList() MSG {
|
|||||||
return OK(fs)
|
return OK(fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQGetUnidirectionalFriendList 获取单向好友列表
|
||||||
|
//
|
||||||
|
//
|
||||||
|
func (bot *CQBot) CQGetUnidirectionalFriendList() MSG {
|
||||||
|
list, err := bot.Client.GetUnidirectionalFriendList()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取单向好友列表时出现错误: %v", err)
|
||||||
|
return Failed(100, "API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
fs := make([]MSG, 0, len(list))
|
||||||
|
for _, f := range list {
|
||||||
|
fs = append(fs, MSG{
|
||||||
|
"nickname": f.Nickname,
|
||||||
|
"user_id": f.Uin,
|
||||||
|
"source": f.Source,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return OK(fs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQDeleteUnidirectionalFriend 删除单向好友
|
||||||
|
//
|
||||||
|
//
|
||||||
|
func (bot *CQBot) CQDeleteUnidirectionalFriend(uin int64) MSG {
|
||||||
|
list, err := bot.Client.GetUnidirectionalFriendList()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取单向好友列表时出现错误: %v", err)
|
||||||
|
return Failed(100, "API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
for _, f := range list {
|
||||||
|
if f.Uin == uin {
|
||||||
|
if err = bot.Client.DeleteUnidirectionalFriend(uin); err != nil {
|
||||||
|
log.Errorf("删除单向好友时出现错误: %v", err)
|
||||||
|
return Failed(100, "API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Failed(100, "FRIEND_NOT_FOUND", "好友不存在")
|
||||||
|
}
|
||||||
|
|
||||||
// CQDeleteFriend 删除好友
|
// CQDeleteFriend 删除好友
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
@ -366,33 +409,27 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Type == gjson.JSON {
|
|
||||||
elem := bot.ConvertObjectMessage(m, true)
|
|
||||||
fixAt(elem)
|
|
||||||
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
|
||||||
if mid == -1 {
|
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
|
||||||
}
|
|
||||||
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(ToStringMessage(elem, groupID)), mid)
|
|
||||||
return OK(MSG{"message_id": mid})
|
|
||||||
}
|
|
||||||
str := m.String()
|
|
||||||
if str == "" {
|
|
||||||
log.Warn("群消息发送失败: 信息为空.")
|
|
||||||
return Failed(100, "EMPTY_MSG_ERROR", "消息为空")
|
|
||||||
}
|
|
||||||
var elem []message.IMessageElement
|
var elem []message.IMessageElement
|
||||||
if autoEscape {
|
if m.Type == gjson.JSON {
|
||||||
elem = append(elem, message.NewText(str))
|
elem = bot.ConvertObjectMessage(m, true)
|
||||||
} else {
|
} else {
|
||||||
elem = bot.ConvertStringMessage(str, true)
|
str := m.String()
|
||||||
|
if str == "" {
|
||||||
|
log.Warn("群消息发送失败: 信息为空.")
|
||||||
|
return Failed(100, "EMPTY_MSG_ERROR", "消息为空")
|
||||||
|
}
|
||||||
|
if autoEscape {
|
||||||
|
elem = []message.IMessageElement{message.NewText(str)}
|
||||||
|
} else {
|
||||||
|
elem = bot.ConvertStringMessage(str, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fixAt(elem)
|
fixAt(elem)
|
||||||
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem})
|
||||||
if mid == -1 {
|
if mid == -1 {
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(str), mid)
|
log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(m.String()), mid)
|
||||||
return OK(MSG{"message_id": mid})
|
return OK(MSG{"message_id": mid})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,7 +440,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
if m.Type != gjson.JSON {
|
if m.Type != gjson.JSON {
|
||||||
return Failed(100)
|
return Failed(100)
|
||||||
}
|
}
|
||||||
var sendNodes []*message.ForwardNode
|
fm := message.NewForwardMessage()
|
||||||
ts := time.Now().Add(-time.Minute * 5)
|
ts := time.Now().Add(-time.Minute * 5)
|
||||||
hasCustom := false
|
hasCustom := false
|
||||||
m.ForEach(func(_, item gjson.Result) bool {
|
m.ForEach(func(_, item gjson.Result) bool {
|
||||||
@ -414,8 +451,23 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
var convert func(e gjson.Result) []*message.ForwardNode
|
resolveElement := func(elems []message.IMessageElement) []message.IMessageElement {
|
||||||
convert = func(e gjson.Result) (nodes []*message.ForwardNode) {
|
for i, elem := range elems {
|
||||||
|
switch elem.(type) {
|
||||||
|
case *LocalImageElement, *LocalVideoElement:
|
||||||
|
gm, err := bot.uploadMedia(elem, groupID, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("警告: 群 %d %s上传失败: %v", groupID, elem.Type().String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
elems[i] = gm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return elems
|
||||||
|
}
|
||||||
|
|
||||||
|
var convert func(e gjson.Result) *message.ForwardNode
|
||||||
|
convert = func(e gjson.Result) *message.ForwardNode {
|
||||||
if e.Get("type").Str != "node" {
|
if e.Get("type").Str != "node" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -425,7 +477,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
m := bot.GetMessage(int32(i))
|
m := bot.GetMessage(int32(i))
|
||||||
if m != nil {
|
if m != nil {
|
||||||
sender := m["sender"].(message.Sender)
|
sender := m["sender"].(message.Sender)
|
||||||
nodes = append(nodes, &message.ForwardNode{
|
return &message.ForwardNode{
|
||||||
SenderId: sender.Uin,
|
SenderId: sender.Uin,
|
||||||
SenderName: (&sender).DisplayName(),
|
SenderName: (&sender).DisplayName(),
|
||||||
Time: func() int32 {
|
Time: func() int32 {
|
||||||
@ -435,12 +487,11 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
}
|
}
|
||||||
return msgTime
|
return msgTime
|
||||||
}(),
|
}(),
|
||||||
Message: bot.ConvertStringMessage(m["message"].(string), true),
|
Message: resolveElement(bot.ConvertStringMessage(m["message"].(string), true)),
|
||||||
})
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str)
|
log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
uin := e.Get("data.[user_id,uin].0").Int()
|
uin := e.Get("data.[user_id,uin].0").Int()
|
||||||
msgTime := e.Get("data.time").Int()
|
msgTime := e.Get("data.time").Int()
|
||||||
@ -450,63 +501,59 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
name := e.Get("data.name").Str
|
name := e.Get("data.name").Str
|
||||||
c := e.Get("data.content")
|
c := e.Get("data.content")
|
||||||
if c.IsArray() {
|
if c.IsArray() {
|
||||||
flag := false
|
nested := false
|
||||||
c.ForEach(func(_, value gjson.Result) bool {
|
c.ForEach(func(_, value gjson.Result) bool {
|
||||||
if value.Get("type").Str == "node" {
|
if value.Get("type").Str == "node" {
|
||||||
flag = true
|
nested = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
if flag {
|
if nested { // 处理嵌套
|
||||||
var taowa []*message.ForwardNode
|
nest := message.NewForwardMessage()
|
||||||
for _, item := range c.Array() {
|
for _, item := range c.Array() {
|
||||||
taowa = append(taowa, convert(item)...)
|
node := convert(item)
|
||||||
|
if node != nil {
|
||||||
|
nest.AddNode(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nodes = append(nodes, &message.ForwardNode{
|
elem := bot.Client.UploadGroupForwardMessage(groupID, nest)
|
||||||
|
return &message.ForwardNode{
|
||||||
SenderId: uin,
|
SenderId: uin,
|
||||||
SenderName: name,
|
SenderName: name,
|
||||||
Time: int32(msgTime),
|
Time: int32(msgTime),
|
||||||
Message: []message.IMessageElement{bot.Client.UploadGroupForwardMessage(groupID, &message.ForwardMessage{Nodes: taowa})},
|
Message: []message.IMessageElement{elem},
|
||||||
})
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content := bot.ConvertObjectMessage(e.Get("data.content"), true)
|
content := bot.ConvertObjectMessage(e.Get("data.content"), true)
|
||||||
if uin != 0 && name != "" && len(content) > 0 {
|
if uin != 0 && name != "" && len(content) > 0 {
|
||||||
var newElem []message.IMessageElement
|
return &message.ForwardNode{
|
||||||
for _, elem := range content {
|
|
||||||
switch elem.(type) {
|
|
||||||
case *LocalImageElement, *LocalVideoElement:
|
|
||||||
gm, err := bot.uploadMedia(elem, groupID, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("警告: 群 %d %s上传失败: %v", groupID, elem.Type().String(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
elem = gm
|
|
||||||
}
|
|
||||||
newElem = append(newElem, elem)
|
|
||||||
}
|
|
||||||
nodes = append(nodes, &message.ForwardNode{
|
|
||||||
SenderId: uin,
|
SenderId: uin,
|
||||||
SenderName: name,
|
SenderName: name,
|
||||||
Time: int32(msgTime),
|
Time: int32(msgTime),
|
||||||
Message: newElem,
|
Message: resolveElement(content),
|
||||||
})
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content))
|
log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content))
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if m.IsArray() {
|
if m.IsArray() {
|
||||||
for _, item := range m.Array() {
|
for _, item := range m.Array() {
|
||||||
sendNodes = append(sendNodes, convert(item)...)
|
node := convert(item)
|
||||||
|
if node != nil {
|
||||||
|
fm.AddNode(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendNodes = convert(m)
|
node := convert(m)
|
||||||
|
if node != nil {
|
||||||
|
fm.AddNode(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(sendNodes) > 0 {
|
if fm.Length() > 0 {
|
||||||
ret := bot.Client.SendGroupForwardMessage(groupID, &message.ForwardMessage{Nodes: sendNodes})
|
fe := bot.Client.UploadGroupForwardMessage(groupID, fm)
|
||||||
|
ret := bot.Client.SendGroupForwardMessage(groupID, fe)
|
||||||
if ret == nil || ret.Id == -1 {
|
if ret == nil || ret.Id == -1 {
|
||||||
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.")
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
@ -522,30 +569,25 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
//
|
//
|
||||||
// https://git.io/Jtz1l
|
// https://git.io/Jtz1l
|
||||||
func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) MSG {
|
func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) MSG {
|
||||||
if m.Type == gjson.JSON {
|
|
||||||
elem := bot.ConvertObjectMessage(m, false)
|
|
||||||
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
|
||||||
if mid == -1 {
|
|
||||||
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})
|
|
||||||
}
|
|
||||||
str := m.String()
|
|
||||||
if str == "" {
|
|
||||||
return Failed(100, "EMPTY_MSG_ERROR", "消息为空")
|
|
||||||
}
|
|
||||||
var elem []message.IMessageElement
|
var elem []message.IMessageElement
|
||||||
if autoEscape {
|
if m.Type == gjson.JSON {
|
||||||
elem = append(elem, message.NewText(str))
|
elem = bot.ConvertObjectMessage(m, false)
|
||||||
} else {
|
} else {
|
||||||
elem = bot.ConvertStringMessage(str, false)
|
str := m.String()
|
||||||
|
if str == "" {
|
||||||
|
return Failed(100, "EMPTY_MSG_ERROR", "消息为空")
|
||||||
|
}
|
||||||
|
if autoEscape {
|
||||||
|
elem = []message.IMessageElement{message.NewText(str)}
|
||||||
|
} else {
|
||||||
|
elem = bot.ConvertStringMessage(str, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
||||||
if mid == -1 {
|
if mid == -1 {
|
||||||
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出")
|
||||||
}
|
}
|
||||||
log.Infof("发送好友 %v(%v) 的消息: %v (%v)", userID, userID, limitedString(str), mid)
|
log.Infof("发送好友 %v(%v) 的消息: %v (%v)", userID, userID, limitedString(m.String()), mid)
|
||||||
return OK(MSG{"message_id": mid})
|
return OK(MSG{"message_id": mid})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -929,7 +971,7 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) MSG {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
err := json.UnmarshalFromString(reply.Raw, &replySegments)
|
err := json.Unmarshal(utils.S2B(reply.Raw), &replySegments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Warnf("处理 at_sender 过程中发生错误")
|
log.WithError(err).Warnf("处理 at_sender 过程中发生错误")
|
||||||
return Failed(-1, "处理 at_sender 过程中发生错误", err.Error())
|
return Failed(-1, "处理 at_sender 过程中发生错误", err.Error())
|
||||||
@ -937,13 +979,13 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) MSG {
|
|||||||
|
|
||||||
segments = append(segments, replySegments...)
|
segments = append(segments, replySegments...)
|
||||||
|
|
||||||
modified, err := json.MarshalToString(segments)
|
modified, err := json.Marshal(segments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Warnf("处理 at_sender 过程中发生错误")
|
log.WithError(err).Warnf("处理 at_sender 过程中发生错误")
|
||||||
return Failed(-1, "处理 at_sender 过程中发生错误", err.Error())
|
return Failed(-1, "处理 at_sender 过程中发生错误", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = gjson.Parse(modified)
|
reply = gjson.Parse(utils.B2S(modified))
|
||||||
} else if at && reply.Type == gjson.String {
|
} else if at && reply.Type == gjson.String {
|
||||||
reply = gjson.Parse(fmt.Sprintf(
|
reply = gjson.Parse(fmt.Sprintf(
|
||||||
"\"[CQ:at,qq=%d]%s\"",
|
"\"[CQ:at,qq=%d]%s\"",
|
||||||
@ -981,10 +1023,10 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) MSG {
|
|||||||
reqType := context.Get("request_type").Str
|
reqType := context.Get("request_type").Str
|
||||||
if operation.Get("approve").Exists() {
|
if operation.Get("approve").Exists() {
|
||||||
if reqType == "friend" {
|
if reqType == "friend" {
|
||||||
bot.CQProcessFriendRequest(context.Get("flag").Str, operation.Get("approve").Bool())
|
bot.CQProcessFriendRequest(context.Get("flag").String(), operation.Get("approve").Bool())
|
||||||
}
|
}
|
||||||
if reqType == "group" {
|
if reqType == "group" {
|
||||||
bot.CQProcessGroupRequest(context.Get("flag").Str, context.Get("sub_type").Str, operation.Get("reason").Str, operation.Get("approve").Bool())
|
bot.CQProcessGroupRequest(context.Get("flag").String(), context.Get("sub_type").Str, operation.Get("reason").Str, operation.Get("approve").Bool())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1408,6 +1450,23 @@ func (bot *CQBot) CQSetModelShow(modelName string, modelShow string) MSG {
|
|||||||
return OK(nil)
|
return OK(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQMarkMessageAsRead 标记消息已读
|
||||||
|
func (bot *CQBot) CQMarkMessageAsRead(msgID int32) MSG {
|
||||||
|
m := bot.GetMessage(msgID)
|
||||||
|
if m == nil {
|
||||||
|
return Failed(100, "MSG_NOT_FOUND", "消息不存在")
|
||||||
|
}
|
||||||
|
if _, ok := m["group"]; ok {
|
||||||
|
bot.Client.MarkGroupMessageReaded(m["group"].(int64), int64(m["message-id"].(int32)))
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
if _, ok := m["from-group"]; ok {
|
||||||
|
return Failed(100, "MSG_TYPE_ERROR", "不支持标记临时会话")
|
||||||
|
}
|
||||||
|
bot.Client.MarkPrivateMessageReaded(m["sender"].(message.Sender).Uin, int64(m["time"].(int32)))
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// OK 生成成功返回值
|
// OK 生成成功返回值
|
||||||
func OK(data interface{}) MSG {
|
func OK(data interface{}) MSG {
|
||||||
return MSG{"data": data, "retcode": 0, "status": "ok"}
|
return MSG{"data": data, "retcode": 0, "status": "ok"}
|
||||||
@ -1441,11 +1500,12 @@ func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) MSG {
|
|||||||
// unknown = 0xff
|
// unknown = 0xff
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}(),
|
}(),
|
||||||
"age": 0,
|
"age": 0,
|
||||||
"area": "",
|
"area": "",
|
||||||
"join_time": m.JoinTime,
|
"join_time": m.JoinTime,
|
||||||
"last_sent_time": m.LastSpeakTime,
|
"last_sent_time": m.LastSpeakTime,
|
||||||
"level": strconv.FormatInt(int64(m.Level), 10),
|
"shut_up_timestamp": m.ShutUpTimestamp,
|
||||||
|
"level": strconv.FormatInt(int64(m.Level), 10),
|
||||||
"role": func() string {
|
"role": func() string {
|
||||||
switch m.Permission {
|
switch m.Permission {
|
||||||
case client.Owner:
|
case client.Owner:
|
||||||
|
114
coolq/bot.go
114
coolq/bot.go
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io"
|
"io"
|
||||||
@ -19,7 +20,6 @@ import (
|
|||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
@ -29,8 +29,6 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/global/config"
|
"github.com/Mrs4s/go-cqhttp/global/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
|
|
||||||
// CQBot CQBot结构体,存储Bot实例相关配置
|
// CQBot CQBot结构体,存储Bot实例相关配置
|
||||||
type CQBot struct {
|
type CQBot struct {
|
||||||
Client *client.QQClient
|
Client *client.QQClient
|
||||||
@ -41,7 +39,6 @@ type CQBot struct {
|
|||||||
db *leveldb.DB
|
db *leveldb.DB
|
||||||
friendReqCache sync.Map
|
friendReqCache sync.Map
|
||||||
tempSessionCache sync.Map
|
tempSessionCache sync.Map
|
||||||
oneWayMsgCache sync.Map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MSG 消息Map
|
// MSG 消息Map
|
||||||
@ -81,12 +78,27 @@ var ForceFragmented = false
|
|||||||
// SkipMimeScan 是否跳过Mime扫描
|
// SkipMimeScan 是否跳过Mime扫描
|
||||||
var SkipMimeScan bool
|
var SkipMimeScan bool
|
||||||
|
|
||||||
var lawfulImageTypes = []string{"image/png", "image/jpeg", "image/gif", "image/bmp"}
|
// keep sync with /docs/file.md#MINE
|
||||||
|
var lawfulImageTypes = [...]string{
|
||||||
|
"image/bmp",
|
||||||
|
"image/gif",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/webp",
|
||||||
|
}
|
||||||
|
|
||||||
var lawfulAudioTypes = []string{
|
var lawfulAudioTypes = [...]string{
|
||||||
"audio/mpeg", "audio/flac", "audio/midi", "audio/ogg",
|
"audio/aac",
|
||||||
"audio/ape", "audio/amr", "audio/wav", "audio/aiff",
|
"audio/aiff",
|
||||||
"audio/mp4", "audio/aac", "audio/x-m4a",
|
"audio/amr",
|
||||||
|
"audio/ape",
|
||||||
|
"audio/flac",
|
||||||
|
"audio/midi",
|
||||||
|
"audio/mp4",
|
||||||
|
"audio/mpeg",
|
||||||
|
"audio/ogg",
|
||||||
|
"audio/wav",
|
||||||
|
"audio/x-m4a",
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQQBot 初始化一个QQBot实例
|
// NewQQBot 初始化一个QQBot实例
|
||||||
@ -126,6 +138,7 @@ func NewQQBot(cli *client.QQClient, conf *config.Config) *CQBot {
|
|||||||
bot.Client.OnGroupMessageRecalled(bot.groupRecallEvent)
|
bot.Client.OnGroupMessageRecalled(bot.groupRecallEvent)
|
||||||
bot.Client.OnGroupNotify(bot.groupNotifyEvent)
|
bot.Client.OnGroupNotify(bot.groupNotifyEvent)
|
||||||
bot.Client.OnFriendNotify(bot.friendNotifyEvent)
|
bot.Client.OnFriendNotify(bot.friendNotifyEvent)
|
||||||
|
bot.Client.OnMemberSpecialTitleUpdated(bot.memberTitleUpdatedEvent)
|
||||||
bot.Client.OnFriendMessageRecalled(bot.friendRecallEvent)
|
bot.Client.OnFriendMessageRecalled(bot.friendRecallEvent)
|
||||||
bot.Client.OnReceivedOfflineFile(bot.offlineFileEvent)
|
bot.Client.OnReceivedOfflineFile(bot.offlineFileEvent)
|
||||||
bot.Client.OnJoinGroup(bot.joinGroupEvent)
|
bot.Client.OnJoinGroup(bot.joinGroupEvent)
|
||||||
@ -211,35 +224,32 @@ func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElemen
|
|||||||
|
|
||||||
// UploadLocalVideo 上传本地短视频至群聊
|
// UploadLocalVideo 上传本地短视频至群聊
|
||||||
func (bot *CQBot) UploadLocalVideo(target int64, v *LocalVideoElement) (*message.ShortVideoElement, error) {
|
func (bot *CQBot) UploadLocalVideo(target int64, v *LocalVideoElement) (*message.ShortVideoElement, error) {
|
||||||
if v.File != "" {
|
video, err := os.Open(v.File)
|
||||||
video, err := os.Open(v.File)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer video.Close()
|
|
||||||
hash, _ := utils.ComputeMd5AndLength(io.MultiReader(video, v.thumb))
|
|
||||||
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".cache")
|
|
||||||
_, _ = video.Seek(0, io.SeekStart)
|
|
||||||
_, _ = v.thumb.Seek(0, io.SeekStart)
|
|
||||||
return bot.Client.UploadGroupShortVideo(target, video, v.thumb, cacheFile)
|
|
||||||
}
|
}
|
||||||
return &v.ShortVideoElement, nil
|
defer video.Close()
|
||||||
|
hash, _ := utils.ComputeMd5AndLength(io.MultiReader(video, v.thumb))
|
||||||
|
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".cache")
|
||||||
|
_, _ = video.Seek(0, io.SeekStart)
|
||||||
|
_, _ = v.thumb.Seek(0, io.SeekStart)
|
||||||
|
return bot.Client.UploadGroupShortVideo(target, video, v.thumb, cacheFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadLocalImageAsPrivate 上传本地图片至私聊
|
// UploadLocalImageAsPrivate 上传本地图片至私聊
|
||||||
func (bot *CQBot) UploadLocalImageAsPrivate(userID int64, img *LocalImageElement) (i *message.FriendImageElement, err error) {
|
func (bot *CQBot) UploadLocalImageAsPrivate(userID int64, img *LocalImageElement) (i *message.FriendImageElement, err error) {
|
||||||
if img.Stream != nil {
|
if img.File != "" {
|
||||||
i, err = bot.Client.UploadPrivateImage(userID, img.Stream)
|
f, err := os.Open(img.File)
|
||||||
} else {
|
if err != nil {
|
||||||
// need update.
|
return nil, errors.Wrap(err, "open image error")
|
||||||
f, e := os.Open(img.File)
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() { _ = f.Close() }()
|
||||||
i, err = bot.Client.UploadPrivateImage(userID, f)
|
img.Stream = f
|
||||||
}
|
}
|
||||||
|
if lawful, mime := IsLawfulImage(img.Stream); !lawful {
|
||||||
|
return nil, errors.New("image type error: " + mime)
|
||||||
|
}
|
||||||
|
i, err = bot.Client.UploadPrivateImage(userID, img.Stream)
|
||||||
if i != nil {
|
if i != nil {
|
||||||
i.Flash = img.Flash
|
i.Flash = img.Flash
|
||||||
}
|
}
|
||||||
@ -303,7 +313,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
for _, e := range m.Elements {
|
for _, e := range m.Elements {
|
||||||
switch i := e.(type) {
|
switch i := e.(type) {
|
||||||
case *LocalImageElement, *message.VoiceElement, *LocalVideoElement:
|
case *LocalImageElement, *message.VoiceElement, *LocalVideoElement:
|
||||||
i, err := bot.uploadMedia(i, groupID, false)
|
i, err := bot.uploadMedia(i, target, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("警告: 私聊 %d 消息%s上传失败: %v", target, e.Type().String(), err)
|
log.Warnf("警告: 私聊 %d 消息%s上传失败: %v", target, e.Type().String(), err)
|
||||||
continue
|
continue
|
||||||
@ -313,7 +323,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
bot.Client.SendFriendPoke(i.Target)
|
bot.Client.SendFriendPoke(i.Target)
|
||||||
return 0
|
return 0
|
||||||
case *message.MusicShareElement:
|
case *message.MusicShareElement:
|
||||||
bot.Client.SendFriendMusicShare(groupID, i)
|
bot.Client.SendFriendMusicShare(target, i)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
newElem = append(newElem, e)
|
newElem = append(newElem, e)
|
||||||
@ -324,13 +334,31 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
}
|
}
|
||||||
m.Elements = newElem
|
m.Elements = newElem
|
||||||
bot.checkMedia(newElem)
|
bot.checkMedia(newElem)
|
||||||
|
|
||||||
|
// 单向好友是否存在
|
||||||
|
unidirectionalFriendExists := func() bool {
|
||||||
|
list, err := bot.Client.GetUnidirectionalFriendList()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, f := range list {
|
||||||
|
if f.Uin == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
session, ok := bot.tempSessionCache.Load(target)
|
||||||
var id int32 = -1
|
var id int32 = -1
|
||||||
if bot.Client.FindFriend(target) != nil { // 双向好友
|
|
||||||
|
switch {
|
||||||
|
case bot.Client.FindFriend(target) != nil: // 双向好友
|
||||||
msg := bot.Client.SendPrivateMessage(target, m)
|
msg := bot.Client.SendPrivateMessage(target, m)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
id = bot.InsertPrivateMessage(msg)
|
id = bot.InsertPrivateMessage(msg)
|
||||||
}
|
}
|
||||||
} else if session, ok := bot.tempSessionCache.Load(target); ok || groupID != 0 { // 临时会话
|
case ok || groupID != 0: // 临时会话
|
||||||
switch {
|
switch {
|
||||||
case groupID != 0 && bot.Client.FindGroup(groupID) == nil:
|
case groupID != 0 && bot.Client.FindGroup(groupID) == nil:
|
||||||
log.Errorf("错误: 找不到群(%v)", groupID)
|
log.Errorf("错误: 找不到群(%v)", groupID)
|
||||||
@ -355,12 +383,12 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
id = bot.InsertTempMessage(target, msg)
|
id = bot.InsertTempMessage(target, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if _, ok := bot.oneWayMsgCache.Load(target); ok { // 单向好友
|
case unidirectionalFriendExists(): // 单向好友
|
||||||
msg := bot.Client.SendPrivateMessage(target, m)
|
msg := bot.Client.SendPrivateMessage(target, m)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
id = bot.InsertPrivateMessage(msg)
|
id = bot.InsertPrivateMessage(msg)
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
nickname := "Unknown"
|
nickname := "Unknown"
|
||||||
if summaryInfo, _ := bot.Client.GetSummaryInfo(target); summaryInfo != nil {
|
if summaryInfo, _ := bot.Client.GetSummaryInfo(target); summaryInfo != nil {
|
||||||
nickname = summaryInfo.Nickname
|
nickname = summaryInfo.Nickname
|
||||||
@ -428,7 +456,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
|||||||
val := MSG{
|
val := MSG{
|
||||||
"message-id": m.Id,
|
"message-id": m.Id,
|
||||||
// FIXME(InsertTempMessage) InternalId missing
|
// FIXME(InsertTempMessage) InternalId missing
|
||||||
"group": m.GroupCode,
|
"from-group": m.GroupCode,
|
||||||
"group-name": m.GroupName,
|
"group-name": m.GroupName,
|
||||||
"target": target,
|
"target": target,
|
||||||
"sender": m.Sender,
|
"sender": m.Sender,
|
||||||
@ -516,7 +544,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) MSG {
|
|||||||
"user_id": m.Sender.Uin,
|
"user_id": m.Sender.Uin,
|
||||||
},
|
},
|
||||||
"sub_type": "normal",
|
"sub_type": "normal",
|
||||||
"time": time.Now().Unix(),
|
"time": m.Time,
|
||||||
"user_id": m.Sender.Uin,
|
"user_id": m.Sender.Uin,
|
||||||
}
|
}
|
||||||
if m.Sender.IsAnonymous() {
|
if m.Sender.IsAnonymous() {
|
||||||
@ -604,6 +632,10 @@ func IsLawfulImage(r io.ReadSeeker) (bool, string) {
|
|||||||
log.Debugf("扫描 Mime 时出现问题: %v", err)
|
log.Debugf("扫描 Mime 时出现问题: %v", err)
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
mime := t.String()
|
for _, lt := range lawfulImageTypes {
|
||||||
return mimetype.EqualsAny(mime, lawfulImageTypes...), mime
|
if t.Is(lt) {
|
||||||
|
return true, t.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, t.String()
|
||||||
}
|
}
|
||||||
|
144
coolq/cqcode.go
144
coolq/cqcode.go
@ -3,7 +3,6 @@ package coolq
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
xml2 "encoding/xml"
|
xml2 "encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
@ -18,11 +17,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gabriel-vasile/mimetype"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
|
"github.com/gabriel-vasile/mimetype"
|
||||||
|
"github.com/segmentio/asm/base64"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
@ -81,7 +80,6 @@ type LocalVoiceElement struct {
|
|||||||
|
|
||||||
// LocalVideoElement 本地视频
|
// LocalVideoElement 本地视频
|
||||||
type LocalVideoElement struct {
|
type LocalVideoElement struct {
|
||||||
message.ShortVideoElement
|
|
||||||
File string
|
File string
|
||||||
thumb io.ReadSeeker
|
thumb io.ReadSeeker
|
||||||
}
|
}
|
||||||
@ -97,6 +95,11 @@ func (e *GiftElement) Type() message.ElementType {
|
|||||||
return message.At
|
return message.At
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type impl message.IMessageElement
|
||||||
|
func (e *LocalVideoElement) Type() message.ElementType {
|
||||||
|
return message.Video
|
||||||
|
}
|
||||||
|
|
||||||
// GiftID 礼物ID数组
|
// GiftID 礼物ID数组
|
||||||
var GiftID = [...]message.GroupGift{
|
var GiftID = [...]message.GroupGift{
|
||||||
message.SweetWink,
|
message.SweetWink,
|
||||||
@ -157,7 +160,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []MSG) {
|
|||||||
var m MSG
|
var m MSG
|
||||||
switch o := elem.(type) {
|
switch o := elem.(type) {
|
||||||
case *message.ReplyElement:
|
case *message.ReplyElement:
|
||||||
if RemoveReplyAt && i+1 < len(e) && e[i+1].Type() == message.At {
|
if RemoveReplyAt && i+1 < len(e) {
|
||||||
elem, ok := e[i+1].(*message.AtElement)
|
elem, ok := e[i+1].(*message.AtElement)
|
||||||
if ok && elem.Target == o.Sender {
|
if ok && elem.Target == o.Sender {
|
||||||
e[i+1] = nil
|
e[i+1] = nil
|
||||||
@ -211,7 +214,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []MSG) {
|
|||||||
"data": map[string]string{"file": o.Name, "url": o.Url},
|
"data": map[string]string{"file": o.Name, "url": o.Url},
|
||||||
}
|
}
|
||||||
case *message.GroupImageElement:
|
case *message.GroupImageElement:
|
||||||
data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url}
|
data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url, "subType": strconv.FormatInt(int64(o.ImageBizType), 10)}
|
||||||
switch {
|
switch {
|
||||||
case o.Flash:
|
case o.Flash:
|
||||||
data["type"] = "flash"
|
data["type"] = "flash"
|
||||||
@ -328,6 +331,7 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
|
|||||||
} else if o.EffectID != 0 {
|
} else if o.EffectID != 0 {
|
||||||
arg = ",type=show,id=" + strconv.FormatInt(int64(o.EffectID), 10)
|
arg = ",type=show,id=" + strconv.FormatInt(int64(o.EffectID), 10)
|
||||||
}
|
}
|
||||||
|
arg += ",subType=" + strconv.FormatInt(int64(o.ImageBizType), 10)
|
||||||
if ur {
|
if ur {
|
||||||
write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg)
|
write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg)
|
||||||
} else {
|
} else {
|
||||||
@ -692,6 +696,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
case *message.GroupImageElement:
|
case *message.GroupImageElement:
|
||||||
img.Flash = flash
|
img.Flash = flash
|
||||||
img.EffectID = int32(id)
|
img.EffectID = int32(id)
|
||||||
|
i, _ := strconv.ParseInt(d["subType"], 10, 64)
|
||||||
|
img.ImageBizType = message.ImageBizType(i)
|
||||||
case *message.FriendImageElement:
|
case *message.FriendImageElement:
|
||||||
img.Flash = flash
|
img.Flash = flash
|
||||||
}
|
}
|
||||||
@ -731,9 +737,16 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !SkipMimeScan && !global.IsAMRorSILK(data) {
|
if !SkipMimeScan && !global.IsAMRorSILK(data) {
|
||||||
mt := mimetype.Detect(data).String()
|
mt := mimetype.Detect(data)
|
||||||
if !mimetype.EqualsAny(mt, lawfulAudioTypes...) {
|
lawful := false
|
||||||
return nil, errors.New("audio type error: " + mt)
|
for _, lt := range lawfulAudioTypes {
|
||||||
|
if mt.Is(lt) {
|
||||||
|
lawful = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !lawful {
|
||||||
|
return nil, errors.New("audio type error: " + mt.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !global.IsAMRorSILK(data) {
|
if !global.IsAMRorSILK(data) {
|
||||||
@ -895,7 +908,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v := file.(*LocalVideoElement)
|
v, ok := file.(*LocalVideoElement)
|
||||||
|
if !ok {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
if v.File == "" {
|
if v.File == "" {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
@ -1117,14 +1133,14 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
if path.Ext(rawPath) == ".video" {
|
if path.Ext(rawPath) == ".video" {
|
||||||
b, _ := os.ReadFile(rawPath)
|
b, _ := os.ReadFile(rawPath)
|
||||||
r := binary.NewReader(b)
|
r := binary.NewReader(b)
|
||||||
return &LocalVideoElement{ShortVideoElement: message.ShortVideoElement{ // todo 检查缓存是否有效
|
return &message.ShortVideoElement{ // todo 检查缓存是否有效
|
||||||
Md5: r.ReadBytes(16),
|
Md5: r.ReadBytes(16),
|
||||||
ThumbMd5: r.ReadBytes(16),
|
ThumbMd5: r.ReadBytes(16),
|
||||||
Size: r.ReadInt32(),
|
Size: r.ReadInt32(),
|
||||||
ThumbSize: r.ReadInt32(),
|
ThumbSize: r.ReadInt32(),
|
||||||
Name: r.ReadString(),
|
Name: r.ReadString(),
|
||||||
Uuid: r.ReadAvailable(),
|
Uuid: r.ReadAvailable(),
|
||||||
}}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return &LocalVideoElement{File: rawPath}, nil
|
return &LocalVideoElement{File: rawPath}, nil
|
||||||
}
|
}
|
||||||
@ -1133,72 +1149,50 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
|
|||||||
exist = true
|
exist = true
|
||||||
rawPath = path.Join(global.ImagePathOld, f)
|
rawPath = path.Join(global.ImagePathOld, f)
|
||||||
}
|
}
|
||||||
if !exist && global.PathExists(rawPath+".cqimg") {
|
if !exist {
|
||||||
exist = true
|
if d["url"] != "" {
|
||||||
rawPath += ".cqimg"
|
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group)
|
||||||
|
}
|
||||||
|
return nil, errors.New("invalid image")
|
||||||
}
|
}
|
||||||
if !exist && d["url"] != "" {
|
if path.Ext(rawPath) != ".image" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group)
|
return &LocalImageElement{File: rawPath}, nil
|
||||||
}
|
}
|
||||||
if exist {
|
b, err := os.ReadFile(rawPath)
|
||||||
if path.Ext(rawPath) != ".image" && path.Ext(rawPath) != ".cqimg" {
|
if err != nil {
|
||||||
return &LocalImageElement{File: rawPath}, nil
|
return nil, err
|
||||||
}
|
|
||||||
b, err := os.ReadFile(rawPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(b) < 20 {
|
|
||||||
return nil, errors.New("invalid local file")
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
size int32
|
|
||||||
hash []byte
|
|
||||||
imageURL string
|
|
||||||
)
|
|
||||||
if path.Ext(rawPath) == ".cqimg" {
|
|
||||||
for _, line := range strings.Split(global.ReadAllText(rawPath), "\n") {
|
|
||||||
kv := strings.SplitN(line, "=", 2)
|
|
||||||
switch kv[0] {
|
|
||||||
case "md5":
|
|
||||||
hash, _ = hex.DecodeString(strings.ReplaceAll(kv[1], "\r", ""))
|
|
||||||
case "size":
|
|
||||||
t, _ := strconv.Atoi(strings.ReplaceAll(kv[1], "\r", ""))
|
|
||||||
size = int32(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r := binary.NewReader(b)
|
|
||||||
hash = r.ReadBytes(16)
|
|
||||||
size = r.ReadInt32()
|
|
||||||
r.ReadString()
|
|
||||||
imageURL = r.ReadString()
|
|
||||||
}
|
|
||||||
if size == 0 {
|
|
||||||
if imageURL != "" {
|
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
|
||||||
}
|
|
||||||
return nil, errors.New("img size is 0")
|
|
||||||
}
|
|
||||||
if len(hash) != 16 {
|
|
||||||
return nil, errors.New("invalid hash")
|
|
||||||
}
|
|
||||||
var rsp message.IMessageElement
|
|
||||||
if group {
|
|
||||||
rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size)
|
|
||||||
goto ok
|
|
||||||
}
|
|
||||||
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
|
||||||
ok:
|
|
||||||
if err != nil {
|
|
||||||
if imageURL != "" {
|
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rsp, nil
|
|
||||||
}
|
}
|
||||||
return nil, errors.New("invalid image")
|
if len(b) < 20 {
|
||||||
|
return nil, errors.New("invalid local file")
|
||||||
|
}
|
||||||
|
r := binary.NewReader(b)
|
||||||
|
hash := r.ReadBytes(16)
|
||||||
|
size := r.ReadInt32()
|
||||||
|
r.ReadString()
|
||||||
|
imageURL := r.ReadString()
|
||||||
|
if size == 0 {
|
||||||
|
if imageURL != "" {
|
||||||
|
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
||||||
|
}
|
||||||
|
return nil, errors.New("img size is 0")
|
||||||
|
}
|
||||||
|
if len(hash) != 16 {
|
||||||
|
return nil, errors.New("invalid hash")
|
||||||
|
}
|
||||||
|
var rsp message.IMessageElement
|
||||||
|
if group {
|
||||||
|
rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size)
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
||||||
|
ok:
|
||||||
|
if err != nil {
|
||||||
|
if imageURL != "" {
|
||||||
|
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rsp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeShowPic 一种xml 方式发送的群消息图片
|
// makeShowPic 一种xml 方式发送的群消息图片
|
||||||
|
@ -18,8 +18,10 @@ func TestCQBot_ConvertStringMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bench = `asdfqwerqwerqwer[CQ:face,id=115,text=111]asdfasdfasdfasdfasdfasdfasd[CQ:face,id=217]] 123 [`
|
var (
|
||||||
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":" ["}}]`)
|
bench = `asdfqwerqwerqwer[CQ:face,id=115,text=111]asdfasdfasdfasdfasdfasdfasd[CQ:face,id=217]] 123 [`
|
||||||
|
benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`)
|
||||||
|
)
|
||||||
|
|
||||||
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
|
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
@ -36,9 +36,6 @@ func ToFormattedMessage(e []message.IMessageElement, groupID int64, isRaw ...boo
|
|||||||
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
|
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
|
||||||
bot.checkMedia(m.Elements)
|
bot.checkMedia(m.Elements)
|
||||||
cqm := ToStringMessage(m.Elements, 0, true)
|
cqm := ToStringMessage(m.Elements, 0, true)
|
||||||
if !m.Sender.IsFriend {
|
|
||||||
bot.oneWayMsgCache.Store(m.Sender.Uin, "")
|
|
||||||
}
|
|
||||||
id := m.Id
|
id := m.Id
|
||||||
if bot.db != nil {
|
if bot.db != nil {
|
||||||
id = bot.InsertPrivateMessage(m)
|
id = bot.InsertPrivateMessage(m)
|
||||||
@ -69,9 +66,6 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
bot.dispatchEventMessage(fm)
|
bot.dispatchEventMessage(fm)
|
||||||
if m.Sender.Uin != c.Uin {
|
|
||||||
c.MarkPrivateMessageReaded(m.Sender.Uin, int64(m.Time))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) {
|
func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) {
|
||||||
@ -109,9 +103,6 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
|
|||||||
}
|
}
|
||||||
gm["message_id"] = id
|
gm["message_id"] = id
|
||||||
bot.dispatchEventMessage(gm)
|
bot.dispatchEventMessage(gm)
|
||||||
if m.Sender.Uin != c.Uin {
|
|
||||||
c.MarkGroupMessageReaded(m.GroupCode, int64(m.Id))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
|
func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
|
||||||
@ -268,7 +259,11 @@ func (bot *CQBot) groupNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
|||||||
func (bot *CQBot) friendNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
func (bot *CQBot) friendNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
||||||
friend := c.FindFriend(e.From())
|
friend := c.FindFriend(e.From())
|
||||||
if notify, ok := e.(*client.FriendPokeNotifyEvent); ok {
|
if notify, ok := e.(*client.FriendPokeNotifyEvent); ok {
|
||||||
log.Infof("好友 %v 戳了戳你.", friend.Nickname)
|
if notify.Receiver == notify.Sender {
|
||||||
|
log.Infof("好友 %v 戳了戳自己.", friend.Nickname)
|
||||||
|
} else {
|
||||||
|
log.Infof("好友 %v 戳了戳你.", friend.Nickname)
|
||||||
|
}
|
||||||
bot.dispatchEventMessage(MSG{
|
bot.dispatchEventMessage(MSG{
|
||||||
"post_type": "notice",
|
"post_type": "notice",
|
||||||
"notice_type": "notify",
|
"notice_type": "notify",
|
||||||
@ -282,6 +277,22 @@ func (bot *CQBot) friendNotifyEvent(c *client.QQClient, e client.INotifyEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bot *CQBot) memberTitleUpdatedEvent(c *client.QQClient, e *client.MemberSpecialTitleUpdatedEvent) {
|
||||||
|
group := c.FindGroup(e.GroupCode)
|
||||||
|
mem := group.FindMember(e.Uin)
|
||||||
|
log.Infof("群 %v(%v) 内成员 %v(%v) 获得了新的头衔: %v", group.Name, group.Code, mem.DisplayName(), mem.Uin, e.NewTitle)
|
||||||
|
bot.dispatchEventMessage(MSG{
|
||||||
|
"post_type": "notice",
|
||||||
|
"notice_type": "notify",
|
||||||
|
"sub_type": "title",
|
||||||
|
"group_id": group.Code,
|
||||||
|
"self_id": c.Uin,
|
||||||
|
"user_id": e.Uin,
|
||||||
|
"time": time.Now().Unix(),
|
||||||
|
"title": e.NewTitle,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (bot *CQBot) friendRecallEvent(c *client.QQClient, e *client.FriendMessageRecalledEvent) {
|
func (bot *CQBot) friendRecallEvent(c *client.QQClient, e *client.FriendMessageRecalledEvent) {
|
||||||
f := c.FindFriend(e.FriendUin)
|
f := c.FindFriend(e.FriendUin)
|
||||||
gid := toGlobalID(e.FriendUin, e.MessageId)
|
gid := toGlobalID(e.FriendUin, e.MessageId)
|
||||||
|
@ -32,7 +32,6 @@ heartbeat:
|
|||||||
# -1 为关闭心跳
|
# -1 为关闭心跳
|
||||||
interval: 5
|
interval: 5
|
||||||
|
|
||||||
message:
|
|
||||||
# 上报数据类型
|
# 上报数据类型
|
||||||
# 可选: string,array
|
# 可选: string,array
|
||||||
post-format: string
|
post-format: string
|
||||||
@ -48,6 +47,12 @@ message:
|
|||||||
proxy-rewrite: ''
|
proxy-rewrite: ''
|
||||||
# 是否上报自身消息
|
# 是否上报自身消息
|
||||||
report-self-message: false
|
report-self-message: false
|
||||||
|
# 移除服务端的Reply附带的At
|
||||||
|
remove-reply-at: false
|
||||||
|
# 为Reply附加更多信息
|
||||||
|
extra-reply-data: false
|
||||||
|
# 跳过 Mime 扫描, 忽略错误数据
|
||||||
|
skip-mime-scan: false
|
||||||
|
|
||||||
output:
|
output:
|
||||||
# 日志等级 trace,debug,info,warn,error
|
# 日志等级 trace,debug,info,warn,error
|
||||||
@ -149,6 +154,8 @@ database: # 数据库相关设置
|
|||||||
|
|
||||||
> 注4:关闭心跳服务可能引起断线,请谨慎关闭
|
> 注4:关闭心跳服务可能引起断线,请谨慎关闭
|
||||||
|
|
||||||
|
> 注5:关于MINE扫描, 详见[MINE](file.md#MINE)
|
||||||
|
|
||||||
## 在线状态
|
## 在线状态
|
||||||
|
|
||||||
| 状态 | 值 |
|
| 状态 | 值 |
|
||||||
@ -222,7 +229,7 @@ database: # 数据库相关设置
|
|||||||
在部署前,请在本地完成登录,并将 `config.yml` , `device.json` ,`bootstrap` 和 `go-cqhttp`
|
在部署前,请在本地完成登录,并将 `config.yml` , `device.json` ,`bootstrap` 和 `go-cqhttp`
|
||||||
一起打包。
|
一起打包。
|
||||||
|
|
||||||
在触发器中创建一个API网关触发器,并启用继承响应, 创建完成后即可通过api网关访问go-cqhttp(建议配置 AccessToken)。
|
在触发器中创建一个API网关触发器,并启用集成响应,创建完成后即可通过api网关访问go-cqhttp(建议配置 AccessToken)。
|
||||||
|
|
||||||
> scripts/bootstrap 中使用的工作路径为 /tmp, 这个目录最大能容下500M文件, 如需长期使用,
|
> scripts/bootstrap 中使用的工作路径为 /tmp, 这个目录最大能容下500M文件, 如需长期使用,
|
||||||
> 请挂载文件存储(CFS).
|
> 请挂载文件存储(CFS).
|
||||||
|
385
docs/cqhttp.md
385
docs/cqhttp.md
@ -7,6 +7,7 @@
|
|||||||
<p>
|
<p>
|
||||||
|
|
||||||
##### CQCode
|
##### CQCode
|
||||||
|
|
||||||
- [图片](#图片)
|
- [图片](#图片)
|
||||||
- [回复](#回复)
|
- [回复](#回复)
|
||||||
- [红包](#红包)
|
- [红包](#红包)
|
||||||
@ -21,6 +22,7 @@
|
|||||||
- [图片](#图片)
|
- [图片](#图片)
|
||||||
|
|
||||||
##### API
|
##### API
|
||||||
|
|
||||||
- [设置群名](#设置群名)
|
- [设置群名](#设置群名)
|
||||||
- [设置群头像](#设置群头像)
|
- [设置群头像](#设置群头像)
|
||||||
- [获取图片信息](#获取图片信息)
|
- [获取图片信息](#获取图片信息)
|
||||||
@ -47,6 +49,7 @@
|
|||||||
- [重载事件过滤器](#重载事件过滤器)
|
- [重载事件过滤器](#重载事件过滤器)
|
||||||
|
|
||||||
##### 事件
|
##### 事件
|
||||||
|
|
||||||
- [群消息撤回](#群消息撤回)
|
- [群消息撤回](#群消息撤回)
|
||||||
- [好友消息撤回](#好友消息撤回)
|
- [好友消息撤回](#好友消息撤回)
|
||||||
- [好友戳一戳](#好友戳一戳)
|
- [好友戳一戳](#好友戳一戳)
|
||||||
@ -72,12 +75,13 @@ Type : `image`
|
|||||||
|
|
||||||
| 参数名 | 可能的值 | 说明 |
|
| 参数名 | 可能的值 | 说明 |
|
||||||
| ------- | --------------- | ---------------------------------------------------------------------- |
|
| ------- | --------------- | ---------------------------------------------------------------------- |
|
||||||
| `file` | - | 图片文件名 |
|
| `file` | - | 图片文件名 |
|
||||||
| `type` | `flash`,`show` | 图片类型,`flash` 表示闪照,`show` 表示秀图,默认普通图片 |
|
| `type` | `flash`,`show` | 图片类型,`flash` 表示闪照,`show` 表示秀图,默认普通图片 |
|
||||||
| `url` | - | 图片 URL |
|
| `subType`| - | 图片子类型, 只出现在群聊. |
|
||||||
| `cache` | `0` `1` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `1` |
|
| `url` | - | 图片 URL |
|
||||||
| `id` | - | 发送秀图时的特效id,默认为40000 |
|
| `cache` | `0` `1` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `1` |
|
||||||
| `c` | `2` `3` | 通过网络下载图片时的线程数, 默认单线程. (在资源不支持并发时会自动处理) |
|
| `id` | - | 发送秀图时的特效id,默认为40000 |
|
||||||
|
| `c` | `2` `3` | 通过网络下载图片时的线程数, 默认单线程. (在资源不支持并发时会自动处理) |
|
||||||
|
|
||||||
可用的特效ID:
|
可用的特效ID:
|
||||||
|
|
||||||
@ -90,6 +94,21 @@ Type : `image`
|
|||||||
| 40004 | 爱你 |
|
| 40004 | 爱你 |
|
||||||
| 40005 | 征友 |
|
| 40005 | 征友 |
|
||||||
|
|
||||||
|
子类型列表:
|
||||||
|
|
||||||
|
| value | 说明 |
|
||||||
|
| ----- | ---- |
|
||||||
|
| 0 | 正常图片 |
|
||||||
|
| 1 | 表情包, 在客户端会被分类到表情包图片并缩放显示 |
|
||||||
|
| 2 | 热图 |
|
||||||
|
| 3 | 斗图 |
|
||||||
|
| 4 | 智图? |
|
||||||
|
| 7 | 贴图 |
|
||||||
|
| 8 | 自拍 |
|
||||||
|
| 9 | 贴图广告? |
|
||||||
|
| 10 | 有待测试 |
|
||||||
|
| 13 | 热搜图 |
|
||||||
|
|
||||||
示例: `[CQ:image,file=http://baidu.com/1.jpg,type=show,id=40004]`
|
示例: `[CQ:image,file=http://baidu.com/1.jpg,type=show,id=40004]`
|
||||||
|
|
||||||
> 注意:图片总大小不能超过30MB,gif总帧数不能超过300帧
|
> 注意:图片总大小不能超过30MB,gif总帧数不能超过300帧
|
||||||
@ -112,9 +131,7 @@ Type : `reply`
|
|||||||
| `time` | int64 | 可选. 自定义回复时的时间, 格式为Unix时间 |
|
| `time` | int64 | 可选. 自定义回复时的时间, 格式为Unix时间 |
|
||||||
| `seq` | int64 | 起始消息序号, 可通过 `get_msg` 获得 |
|
| `seq` | int64 | 起始消息序号, 可通过 `get_msg` 获得 |
|
||||||
|
|
||||||
|
示例: `[CQ:reply,id=123456]`
|
||||||
|
|
||||||
示例: `[CQ:reply,id=123456]`
|
|
||||||
\
|
\
|
||||||
自定义回复示例: `[CQ:reply,text=Hello World,qq=10086,time=3376656000,seq=5123]`
|
自定义回复示例: `[CQ:reply,text=Hello World,qq=10086,time=3376656000,seq=5123]`
|
||||||
|
|
||||||
@ -122,11 +139,11 @@ Type : `reply`
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "music",
|
"type": "music",
|
||||||
"data": {
|
"data": {
|
||||||
"type": "163",
|
"type": "163",
|
||||||
"id": "28949129"
|
"id": "28949129"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -143,13 +160,13 @@ Type : `reply`
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "music",
|
"type": "music",
|
||||||
"data": {
|
"data": {
|
||||||
"type": "custom",
|
"type": "custom",
|
||||||
"url": "http://baidu.com",
|
"url": "http://baidu.com",
|
||||||
"audio": "http://baidu.com/1.mp3",
|
"audio": "http://baidu.com/1.mp3",
|
||||||
"title": "音乐标题"
|
"title": "音乐标题"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -231,11 +248,9 @@ Type: `gift`
|
|||||||
| 12 | 我超忙的 |
|
| 12 | 我超忙的 |
|
||||||
| 13 | 爱心口罩 |
|
| 13 | 爱心口罩 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
示例: `[CQ:gift,qq=123456,id=8]`
|
示例: `[CQ:gift,qq=123456,id=8]`
|
||||||
|
|
||||||
### 合并转发
|
### 合并转发
|
||||||
|
|
||||||
Type: `forward`
|
Type: `forward`
|
||||||
|
|
||||||
@ -259,32 +274,33 @@ Type: `node`
|
|||||||
|
|
||||||
| 参数名 | 类型 | 说明 | 特殊说明 |
|
| 参数名 | 类型 | 说明 | 特殊说明 |
|
||||||
| --------- | ------- | -------------- | -------------------------------------------------------------------------------------- |
|
| --------- | ------- | -------------- | -------------------------------------------------------------------------------------- |
|
||||||
| `id` | int32 | 转发消息id | 直接引用他人的消息合并转发, 实际查看顺序为原消息发送顺序 **与下面的自定义消息二选一** |
|
| `id` | int32 | 转发消息id | 直接引用他人的消息合并转发, 实际查看顺序为原消息发送顺序 **与下面的自定义消息二选一** |
|
||||||
| `name` | string | 发送者显示名字 | 用于自定义消息 (自定义消息并合并转发,实际查看顺序为自定义消息段顺序) |
|
| `name` | string | 发送者显示名字 | 用于自定义消息 (自定义消息并合并转发,实际查看顺序为自定义消息段顺序) |
|
||||||
| `uin` | int64 | 发送者QQ号 | 用于自定义消息 |
|
| `uin` | int64 | 发送者QQ号 | 用于自定义消息 |
|
||||||
| `content` | message | 具体消息 | 用于自定义消息 |
|
| `content` | message | 具体消息 | 用于自定义消息 |
|
||||||
| `seq` | message | 具体消息 | 用于自定义消息 |
|
| `seq` | message | 具体消息 | 用于自定义消息 |
|
||||||
|
|
||||||
特殊说明: **需要使用单独的API `/send_group_forward_msg` 发送,并且由于消息段较为复杂,仅支持Array形式入参。 如果引用消息和自定义消息同时出现,实际查看顺序将取消息段顺序. 另外按 [CQHTTP](https://git.io/JtxtN) 文档说明, `data` 应全为字符串, 但由于需要接收`message` 类型的消息, 所以 *仅限此Type的content字段* 支持Array套娃**
|
特殊说明: **需要使用单独的API `/send_group_forward_msg` 发送,并且由于消息段较为复杂,仅支持Array形式入参。 如果引用消息和自定义消息同时出现,实际查看顺序将取消息段顺序.
|
||||||
|
另外按 [CQHTTP](https://git.io/JtxtN) 文档说明, `data` 应全为字符串, 但由于需要接收`message` 类型的消息, 所以 *仅限此Type的content字段* 支持Array套娃**
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
直接引用消息合并转发:
|
直接引用消息合并转发:
|
||||||
|
|
||||||
````json
|
````json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"data": {
|
"data": {
|
||||||
"id": "123"
|
"id": "123"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"data": {
|
|
||||||
"id": "456"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"data": {
|
||||||
|
"id": "456"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
````
|
````
|
||||||
|
|
||||||
@ -292,27 +308,29 @@ Type: `node`
|
|||||||
|
|
||||||
````json
|
````json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "消息发送者A",
|
"name": "消息发送者A",
|
||||||
"uin": "10086",
|
"uin": "10086",
|
||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"data": {"text": "测试消息1"}
|
"data": {
|
||||||
}
|
"text": "测试消息1"
|
||||||
]
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"data": {
|
|
||||||
"name": "消息发送者B",
|
|
||||||
"uin": "10087",
|
|
||||||
"content": "[CQ:image,file=xxxxx]测试消息2"
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"data": {
|
||||||
|
"name": "消息发送者B",
|
||||||
|
"uin": "10087",
|
||||||
|
"content": "[CQ:image,file=xxxxx]测试消息2"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
````
|
````
|
||||||
|
|
||||||
@ -320,24 +338,25 @@ Type: `node`
|
|||||||
|
|
||||||
````json
|
````json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "自定义发送者",
|
"name": "自定义发送者",
|
||||||
"uin": "10086",
|
"uin": "10086",
|
||||||
"content": "我是自定义消息",
|
"content": "我是自定义消息",
|
||||||
"seq": "5123",
|
"seq": "5123",
|
||||||
"time": "3376656000"
|
"time": "3376656000"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"data": {
|
|
||||||
"id": "123"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"data": {
|
||||||
|
"id": "123"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
````
|
````
|
||||||
|
|
||||||
### 短视频消息
|
### 短视频消息
|
||||||
|
|
||||||
Type: `video`
|
Type: `video`
|
||||||
@ -351,6 +370,7 @@ Type: `video`
|
|||||||
| `file` | string | 支持http和file发送 |
|
| `file` | string | 支持http和file发送 |
|
||||||
| `cover` | string | 视频封面,支持http,file和base64发送,格式必须为jpg |
|
| `cover` | string | 视频封面,支持http,file和base64发送,格式必须为jpg |
|
||||||
| `c` | `2` `3` | 通过网络下载视频时的线程数, 默认单线程. (在资源不支持并发时会自动处理) |
|
| `c` | `2` `3` | 通过网络下载视频时的线程数, 默认单线程. (在资源不支持并发时会自动处理) |
|
||||||
|
|
||||||
示例: `[CQ:video,file=file:///C:\\Users\Richard\Videos\1.mp4]`
|
示例: `[CQ:video,file=file:///C:\\Users\Richard\Videos\1.mp4]`
|
||||||
|
|
||||||
### XML 消息
|
### XML 消息
|
||||||
@ -375,30 +395,61 @@ Type: `xml`
|
|||||||
#### qq音乐
|
#### qq音乐
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="2" templateID="1" action="web" brief="[分享] 十年" sourceMsgId="0" url="https://i.y.qq.com/v8/playsong.html?_wv=1&songid=4830342&souce=qqshare&source=qqshare&ADTAG=qqshare" flag="0" adverSign="0" multiMsgFlag="0" ><item layout="2"><audio cover="http://imgcache.qq.com/music/photo/album_500/26/500_albumpic_89526_0.jpg" src="http://ws.stream.qqmusic.qq.com/C400003mAan70zUy5O.m4a?guid=1535153710&vkey=D5315B8C0603653592AD4879A8A3742177F59D582A7A86546E24DD7F282C3ACF81526C76E293E57EA1E42CF19881C561275D919233333ADE&uin=&fromtag=3" /><title>十年</title><summary>陈奕迅</summary></item><source name="QQ音乐" icon="https://i.gtimg.cn/open/app_icon/01/07/98/56/1101079856_100_m.png" url="http://web.p.qq.com/qqmpmobile/aio/app.html?id=1101079856" action="app" a_actionData="com.tencent.qqmusic" i_actionData="tencent1101079856://" appid="1101079856" /></msg>
|
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||||
|
<msg serviceID="2" templateID="1" action="web" brief="[分享] 十年" sourceMsgId="0"
|
||||||
|
url="https://i.y.qq.com/v8/playsong.html?_wv=1&songid=4830342&souce=qqshare&source=qqshare&ADTAG=qqshare"
|
||||||
|
flag="0" adverSign="0" multiMsgFlag="0">
|
||||||
|
<item layout="2">
|
||||||
|
<audio cover="http://imgcache.qq.com/music/photo/album_500/26/500_albumpic_89526_0.jpg"
|
||||||
|
src="http://ws.stream.qqmusic.qq.com/C400003mAan70zUy5O.m4a?guid=1535153710&vkey=D5315B8C0603653592AD4879A8A3742177F59D582A7A86546E24DD7F282C3ACF81526C76E293E57EA1E42CF19881C561275D919233333ADE&uin=&fromtag=3"/>
|
||||||
|
<title>十年</title>
|
||||||
|
<summary>陈奕迅</summary>
|
||||||
|
</item>
|
||||||
|
<source name="QQ音乐" icon="https://i.gtimg.cn/open/app_icon/01/07/98/56/1101079856_100_m.png"
|
||||||
|
url="http://web.p.qq.com/qqmpmobile/aio/app.html?id=1101079856" action="app"
|
||||||
|
a_actionData="com.tencent.qqmusic" i_actionData="tencent1101079856://" appid="1101079856"/>
|
||||||
|
</msg>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 网易音乐
|
#### 网易音乐
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="2" templateID="1" action="web" brief="[分享] 十年" sourceMsgId="0" url="http://music.163.com/m/song/409650368" flag="0" adverSign="0" multiMsgFlag="0" ><item layout="2"><audio cover="http://p2.music.126.net/g-Qgb9ibk9Wp_0HWra0xQQ==/16636710440565853.jpg?param=90y90" src="https://music.163.com/song/media/outer/url?id=409650368.mp3" /><title>十年</title><summary>黄梦之</summary></item><source name="网易云音乐" icon="https://pic.rmb.bdstatic.com/911423bee2bef937975b29b265d737b3.png" url="http://web.p.qq.com/qqmpmobile/aio/app.html?id=1101079856" action="app" a_actionData="com.netease.cloudmusic" i_actionData="tencent100495085://" appid="100495085" /></msg>
|
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||||
|
<msg serviceID="2" templateID="1" action="web" brief="[分享] 十年" sourceMsgId="0"
|
||||||
|
url="http://music.163.com/m/song/409650368" flag="0" adverSign="0" multiMsgFlag="0">
|
||||||
|
<item layout="2">
|
||||||
|
<audio cover="http://p2.music.126.net/g-Qgb9ibk9Wp_0HWra0xQQ==/16636710440565853.jpg?param=90y90"
|
||||||
|
src="https://music.163.com/song/media/outer/url?id=409650368.mp3"/>
|
||||||
|
<title>十年</title>
|
||||||
|
<summary>黄梦之</summary>
|
||||||
|
</item>
|
||||||
|
<source name="网易云音乐" icon="https://pic.rmb.bdstatic.com/911423bee2bef937975b29b265d737b3.png"
|
||||||
|
url="http://web.p.qq.com/qqmpmobile/aio/app.html?id=1101079856" action="app"
|
||||||
|
a_actionData="com.netease.cloudmusic" i_actionData="tencent100495085://" appid="100495085"/>
|
||||||
|
</msg>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 卡片消息1
|
#### 卡片消息1
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<msg serviceID="1">
|
<msg serviceID="1">
|
||||||
<item><title>生死8秒!女司机高速急刹,他一个操作救下一车性命</title></item>
|
<item>
|
||||||
<source name="官方认证消息" icon="https://qzs.qq.com/ac/qzone_v5/client/auth_icon.png" action="" appid="-1" />
|
<title>生死8秒!女司机高速急刹,他一个操作救下一车性命</title>
|
||||||
|
</item>
|
||||||
|
<source name="官方认证消息" icon="https://qzs.qq.com/ac/qzone_v5/client/auth_icon.png" action="" appid="-1"/>
|
||||||
</msg>
|
</msg>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 卡片消息2
|
#### 卡片消息2
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<msg serviceID="1">
|
<msg serviceID="1">
|
||||||
<item layout="4">
|
<item layout="4">
|
||||||
<title>test title</title>
|
<title>test title</title>
|
||||||
<picture cover="http://url.cn/5CEwIUy"/>
|
<picture cover="http://url.cn/5CEwIUy"/>
|
||||||
</item>
|
</item>
|
||||||
</msg>
|
</msg>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -417,23 +468,24 @@ Type: `json`
|
|||||||
|
|
||||||
json中的字符串需要进行转义:
|
json中的字符串需要进行转义:
|
||||||
|
|
||||||
>","=> `,`
|
> ","=> `,`
|
||||||
|
|
||||||
>"&"=> `&`
|
> "&"=> `&`
|
||||||
|
|
||||||
>"["=> `[`
|
> "["=> `[`
|
||||||
|
|
||||||
>"]"=> `]`
|
> "]"=> `]`
|
||||||
|
|
||||||
否则无法正确得到解析
|
否则无法正确得到解析
|
||||||
|
|
||||||
示例json 的cq码:
|
示例json 的cq码:
|
||||||
|
|
||||||
```test
|
```test
|
||||||
[CQ:json,data={"app":"com.tencent.miniapp","desc":"","view":"notification","ver":"0.0.0.1","prompt":"[应用]","appID":"","sourceName":"","actionData":"","actionData_A":"","sourceUrl":"","meta":{"notification":{"appInfo":{"appName":"全国疫情数据统计","appType":4,"appid":1109659848,"iconUrl":"http:\/\/gchat.qpic.cn\/gchatpic_new\/719328335\/-2010394141-6383A777BEB79B70B31CE250142D740F\/0"},"data":[{"title":"确诊","value":"80932"},{"title":"今日确诊","value":"28"},{"title":"疑似","value":"72"},{"title":"今日疑似","value":"5"},{"title":"治愈","value":"60197"},{"title":"今日治愈","value":"1513"},{"title":"死亡","value":"3140"},{"title":"今**亡","value":"17"}],"title":"中国加油,武汉加油","button":[{"name":"病毒:SARS-CoV-2,其导致疾病命名 COVID-19","action":""},{"name":"传染源:新冠肺炎的患者。无症状感染者也可能成为传染源。","action":""}],"emphasis_keyword":""}},"text":"","sourceAd":""}]
|
[CQ:json,data={"app":"com.tencent.miniapp","desc":"","view":"notification","ver":"0.0.0.1","prompt":"[应用]","appID":"","sourceName":"","actionData":"","actionData_A":"","sourceUrl":"","meta":{"notification":{"appInfo":{"appName":"全国疫情数据统计","appType":4,"appid":1109659848,"iconUrl":"http:\/\/gchat.qpic.cn\/gchatpic_new\/719328335\/-2010394141-6383A777BEB79B70B31CE250142D740F\/0"},"data":[{"title":"确诊","value":"80932"},{"title":"今日确诊","value":"28"},{"title":"疑似","value":"72"},{"title":"今日疑似","value":"5"},{"title":"治愈","value":"60197"},{"title":"今日治愈","value":"1513"},{"title":"死亡","value":"3140"},{"title":"今**亡","value":"17"}],"title":"中国加油,武汉加油","button":[{"name":"病毒:SARS-CoV-2,其导致疾病命名 COVID-19","action":""},{"name":"传染源:新冠肺炎的患者。无症状感染者也可能成为传染源。","action":""}],"emphasis_keyword":""}},"text":"","sourceAd":""}]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### cardimage
|
### cardimage
|
||||||
|
|
||||||
一种xml的图片消息(装逼大图)
|
一种xml的图片消息(装逼大图)
|
||||||
|
|
||||||
ps: xml 接口的消息都存在风控风险,请自行兼容发送失败后的处理(可以失败后走普通图片模式)
|
ps: xml 接口的消息都存在风控风险,请自行兼容发送失败后的处理(可以失败后走普通图片模式)
|
||||||
@ -454,8 +506,8 @@ Type: `cardimage`
|
|||||||
| `source` | string | 分享来源的名称,可以留空 |
|
| `source` | string | 分享来源的名称,可以留空 |
|
||||||
| `icon` | string | 分享来源的icon图标url,可以留空 |
|
| `icon` | string | 分享来源的icon图标url,可以留空 |
|
||||||
|
|
||||||
|
|
||||||
示例cardimage 的cq码:
|
示例cardimage 的cq码:
|
||||||
|
|
||||||
```test
|
```test
|
||||||
[CQ:cardimage,file=https://i.pixiv.cat/img-master/img/2020/03/25/00/00/08/80334602_p0_master1200.jpg]
|
[CQ:cardimage,file=https://i.pixiv.cat/img-master/img/2020/03/25/00/00/08/80334602_p0_master1200.jpg]
|
||||||
```
|
```
|
||||||
@ -480,9 +532,9 @@ Type: `tts`
|
|||||||
|
|
||||||
### 设置群名
|
### 设置群名
|
||||||
|
|
||||||
终结点: `/set_group_name`
|
终结点: `/set_group_name`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------ | ------ | ---- |
|
| ------------ | ------ | ---- |
|
||||||
@ -491,9 +543,9 @@ Type: `tts`
|
|||||||
|
|
||||||
### 设置群头像
|
### 设置群头像
|
||||||
|
|
||||||
终结点: `/set_group_portrait`
|
终结点: `/set_group_portrait`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ------ | ------------------------ |
|
| ---------- | ------ | ------------------------ |
|
||||||
@ -505,7 +557,8 @@ Type: `tts`
|
|||||||
|
|
||||||
- 绝对路径,例如 `file:///C:\\Users\Richard\Pictures\1.png`,格式使用 [`file` URI](https://tools.ietf.org/html/rfc8089)
|
- 绝对路径,例如 `file:///C:\\Users\Richard\Pictures\1.png`,格式使用 [`file` URI](https://tools.ietf.org/html/rfc8089)
|
||||||
- 网络 URL,例如 `http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg`
|
- 网络 URL,例如 `http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg`
|
||||||
- Base64 编码,例如 `base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==`
|
- Base64
|
||||||
|
编码,例如 `base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==`
|
||||||
|
|
||||||
[2]`cache`参数: 通过网络 URL 发送时有效,`1`表示使用缓存,`0`关闭关闭缓存,默认 为`1`
|
[2]`cache`参数: 通过网络 URL 发送时有效,`1`表示使用缓存,`0`关闭关闭缓存,默认 为`1`
|
||||||
|
|
||||||
@ -513,7 +566,7 @@ Type: `tts`
|
|||||||
|
|
||||||
### 获取图片信息
|
### 获取图片信息
|
||||||
|
|
||||||
终结点: `/get_image`
|
终结点: `/get_image`
|
||||||
|
|
||||||
> 该接口为 CQHTTP 接口修改
|
> 该接口为 CQHTTP 接口修改
|
||||||
|
|
||||||
@ -533,7 +586,7 @@ Type: `tts`
|
|||||||
|
|
||||||
### 获取消息
|
### 获取消息
|
||||||
|
|
||||||
终结点: `/get_msg`
|
终结点: `/get_msg`
|
||||||
|
|
||||||
参数
|
参数
|
||||||
|
|
||||||
@ -571,28 +624,29 @@ Type: `tts`
|
|||||||
|
|
||||||
````json5
|
````json5
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"content": "合并转发1",
|
"content": "合并转发1",
|
||||||
"sender": {
|
"sender": {
|
||||||
"nickname": "发送者A",
|
"nickname": "发送者A",
|
||||||
"user_id": 10086
|
"user_id": 10086
|
||||||
},
|
},
|
||||||
"time": 1595694374
|
"time": 1595694374
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"content": "合并转发2[CQ:image,file=xxxx,url=xxxx]",
|
"content": "合并转发2[CQ:image,file=xxxx,url=xxxx]",
|
||||||
"sender": {
|
"sender": {
|
||||||
"nickname": "发送者B",
|
"nickname": "发送者B",
|
||||||
"user_id": 10087
|
"user_id": 10087
|
||||||
},
|
},
|
||||||
"time": 1595694393 // 可选
|
"time": 1595694393
|
||||||
}
|
// 可选
|
||||||
]
|
}
|
||||||
},
|
]
|
||||||
"retcode": 0,
|
},
|
||||||
"status": "ok"
|
"retcode": 0,
|
||||||
|
"status": "ok"
|
||||||
}
|
}
|
||||||
````
|
````
|
||||||
|
|
||||||
@ -600,7 +654,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/send_group_forward_msg`
|
终结点: `/send_group_forward_msg`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | -------------- | ---------------------------- |
|
| ---------- | -------------- | ---------------------------- |
|
||||||
@ -608,16 +662,16 @@ Type: `tts`
|
|||||||
| `messages` | forward node[] | 自定义转发消息, 具体看 [CQCode](https://github.com/Mrs4s/go-cqhttp/blob/master/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) |
|
| `messages` | forward node[] | 自定义转发消息, 具体看 [CQCode](https://github.com/Mrs4s/go-cqhttp/blob/master/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) |
|
||||||
|
|
||||||
响应数据
|
响应数据
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------ | ------ | ------ |
|
| ------------ | ------ | ------ |
|
||||||
| `message_id` | string | 消息id |
|
| `message_id` | string | 消息id |
|
||||||
|
|
||||||
### 获取中文分词
|
### 获取中文分词
|
||||||
|
|
||||||
终结点: `/.get_word_slices`
|
终结点: `/.get_word_slices`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| --------- | ------ | ---- |
|
| --------- | ------ | ---- |
|
||||||
@ -685,9 +739,9 @@ Type: `tts`
|
|||||||
|
|
||||||
> 注意: 目前图片OCR接口仅支持接受的图片
|
> 注意: 目前图片OCR接口仅支持接受的图片
|
||||||
|
|
||||||
终结点: `/ocr_image`
|
终结点: `/ocr_image`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------- | ------ | ------ |
|
| ------- | ------ | ------ |
|
||||||
@ -708,7 +762,6 @@ Type: `tts`
|
|||||||
| `confidence` | int32 | 置信度 |
|
| `confidence` | int32 | 置信度 |
|
||||||
| `coordinates` | vector2 | 坐标 |
|
| `coordinates` | vector2 | 坐标 |
|
||||||
|
|
||||||
|
|
||||||
### 获取群系统消息
|
### 获取群系统消息
|
||||||
|
|
||||||
终结点: `/get_group_system_msg`
|
终结点: `/get_group_system_msg`
|
||||||
@ -720,9 +773,9 @@ Type: `tts`
|
|||||||
| `invited_requests` | InvitedRequest[] | 邀请消息列表 |
|
| `invited_requests` | InvitedRequest[] | 邀请消息列表 |
|
||||||
| `join_requests` | JoinRequest[] | 进群消息列表 |
|
| `join_requests` | JoinRequest[] | 进群消息列表 |
|
||||||
|
|
||||||
> 注意: 如果列表不存在任何消息, 将返回 `null`
|
> 注意: 如果列表不存在任何消息, 将返回 `null`
|
||||||
|
|
||||||
**InvitedRequest**
|
**InvitedRequest**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| -------------- | ------ | ----------------- |
|
| -------------- | ------ | ----------------- |
|
||||||
@ -734,7 +787,7 @@ Type: `tts`
|
|||||||
| `checked` | bool | 是否已被处理 |
|
| `checked` | bool | 是否已被处理 |
|
||||||
| `actor` | int64 | 处理者, 未处理为0 |
|
| `actor` | int64 | 处理者, 未处理为0 |
|
||||||
|
|
||||||
**JoinRequest**
|
**JoinRequest**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------------- | ------ | ----------------- |
|
| ---------------- | ------ | ----------------- |
|
||||||
@ -751,7 +804,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/get_group_file_system_info`
|
终结点: `/get_group_file_system_info`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ----- | ---- |
|
| ---------- | ----- | ---- |
|
||||||
@ -772,7 +825,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/get_group_root_files`
|
终结点: `/get_group_root_files`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ----- | ---- |
|
| ---------- | ----- | ---- |
|
||||||
@ -791,7 +844,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/get_group_files_by_folder`
|
终结点: `/get_group_files_by_folder`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ----------- | ------ | --------------------------- |
|
| ----------- | ------ | --------------------------- |
|
||||||
@ -811,7 +864,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/get_group_file_url`
|
终结点: `/get_group_file_url`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ------ | ------------------------- |
|
| ---------- | ------ | ------------------------- |
|
||||||
@ -825,7 +878,7 @@ Type: `tts`
|
|||||||
| ----- | ------ | ------------ |
|
| ----- | ------ | ------------ |
|
||||||
| `url` | string | 文件下载链接 |
|
| `url` | string | 文件下载链接 |
|
||||||
|
|
||||||
**File**
|
**File**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------------- | ------ | ---------------------- |
|
| ---------------- | ------ | ---------------------- |
|
||||||
@ -840,7 +893,7 @@ Type: `tts`
|
|||||||
| `uploader` | int64 | 上传者ID |
|
| `uploader` | int64 | 上传者ID |
|
||||||
| `uploader_name` | string | 上传者名字 |
|
| `uploader_name` | string | 上传者名字 |
|
||||||
|
|
||||||
**Folder**
|
**Folder**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------------ | ------ | ---------- |
|
| ------------------ | ------ | ---------- |
|
||||||
@ -855,7 +908,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/upload_group_file`
|
终结点: `/upload_group_file`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ------ | ------------------------- |
|
| ---------- | ------ | ------------------------- |
|
||||||
@ -885,7 +938,6 @@ Type: `tts`
|
|||||||
|
|
||||||
**Statistics**
|
**Statistics**
|
||||||
|
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------------ | ------ | ---------------- |
|
| ------------------ | ------ | ---------------- |
|
||||||
| `packet_received` | uint64 | 收到的数据包总数 |
|
| `packet_received` | uint64 | 收到的数据包总数 |
|
||||||
@ -902,7 +954,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/get_group_at_all_remain`
|
终结点: `/get_group_at_all_remain`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ----- | ---- |
|
| ---------- | ----- | ---- |
|
||||||
@ -920,7 +972,7 @@ Type: `tts`
|
|||||||
|
|
||||||
终结点: `/download_file`
|
终结点: `/download_file`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| -------------- | --------------- | ------------ |
|
| -------------- | --------------- | ------------ |
|
||||||
@ -960,7 +1012,7 @@ JSON数组:
|
|||||||
|
|
||||||
终结点:`/get_group_msg_history`
|
终结点:`/get_group_msg_history`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------- | ----- | ----------------------------------- |
|
| ------------- | ----- | ----------------------------------- |
|
||||||
@ -979,7 +1031,7 @@ JSON数组:
|
|||||||
|
|
||||||
终结点:`/get_online_clients`
|
终结点:`/get_online_clients`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ---- | ------------ |
|
| ---------- | ---- | ------------ |
|
||||||
@ -1003,7 +1055,7 @@ JSON数组:
|
|||||||
|
|
||||||
终结点:`/check_url_safely`
|
终结点:`/check_url_safely`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------- | ------ | ------------------------- |
|
| ---------- | ------ | ------------------------- |
|
||||||
@ -1050,6 +1102,34 @@ JSON数组:
|
|||||||
|
|
||||||
`该 API 没有响应数据`
|
`该 API 没有响应数据`
|
||||||
|
|
||||||
|
### 获取单向好友列表
|
||||||
|
|
||||||
|
终结点: `/get_unidirectional_friend_list`
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
数组信息:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| ------------- | ------ | -------- |
|
||||||
|
| `nickname` | string | 昵称 |
|
||||||
|
| `user_id` | int64 | 用户QQ号 |
|
||||||
|
| `source` | string | 添加途径 |
|
||||||
|
|
||||||
|
> 添加途径为用户显示内容, 如 `精确查找` `QQ群 - xxxx`
|
||||||
|
|
||||||
|
### 删除单向好友
|
||||||
|
|
||||||
|
终结点: `/delete_unidirectional_friend`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||||
|
| ---------- | -------- | ------ | -------- |
|
||||||
|
| `user_id` | int64 | | 好友ID |
|
||||||
|
|
||||||
|
`该 API 没有响应数据`
|
||||||
|
|
||||||
### 删除好友
|
### 删除好友
|
||||||
|
|
||||||
终结点: `/delete_friend`
|
终结点: `/delete_friend`
|
||||||
@ -1058,7 +1138,7 @@ JSON数组:
|
|||||||
|
|
||||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||||
| ---------- | -------- | ------ | -------- |
|
| ---------- | -------- | ------ | -------- |
|
||||||
| `id` | int64 | | 好友ID |
|
| `user_id` | int64 | | 好友ID |
|
||||||
|
|
||||||
`该 API 没有响应数据`
|
`该 API 没有响应数据`
|
||||||
|
|
||||||
@ -1076,13 +1156,22 @@ JSON数组:
|
|||||||
| `ext_name` | string | 用户昵称 |
|
| `ext_name` | string | 用户昵称 |
|
||||||
| `create_time` | int64 | 账号创建时间 |
|
| `create_time` | int64 | 账号创建时间 |
|
||||||
|
|
||||||
|
### 标记消息已读
|
||||||
|
|
||||||
|
终结点: `/mark_msg_as_read`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||||
|
| ---------- | -------- | ------ | -------- |
|
||||||
|
| `message_id` | int32 | | 消息ID |
|
||||||
|
|
||||||
### 重载事件过滤器
|
### 重载事件过滤器
|
||||||
|
|
||||||
终结点:`/reload_event_filter`
|
终结点:`/reload_event_filter`
|
||||||
|
|
||||||
`该 API 无需参数也没有响应数据`
|
`该 API 无需参数也没有响应数据`
|
||||||
|
|
||||||
|
|
||||||
## 事件
|
## 事件
|
||||||
|
|
||||||
### 群消息撤回
|
### 群消息撤回
|
||||||
@ -1181,11 +1270,23 @@ JSON数组:
|
|||||||
| `notice_type` | string | `group_card` | 消息类型 |
|
| `notice_type` | string | `group_card` | 消息类型 |
|
||||||
| `group_id` | int64 | | 群号 |
|
| `group_id` | int64 | | 群号 |
|
||||||
| `user_id` | int64 | | 成员id |
|
| `user_id` | int64 | | 成员id |
|
||||||
| `card_new` | int64 | | 新名片 |
|
| `card_new` | string | | 新名片 |
|
||||||
| `card_old` | int64 | | 旧名片 |
|
| `card_old` | string | | 旧名片 |
|
||||||
|
|
||||||
> PS: 当名片为空时 `card_xx` 字段为空字符串, 并不是昵称
|
> PS: 当名片为空时 `card_xx` 字段为空字符串, 并不是昵称
|
||||||
|
|
||||||
|
### 群成员头衔更新事件
|
||||||
|
|
||||||
|
**上报数据**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 可能的值 | 说明 |
|
||||||
|
| ------------- | ------ | ------------ | -------- |
|
||||||
|
| `post_type` | string | `notice` | 上报类型 |
|
||||||
|
| `notice_type` | string | `notify` | 消息类型 |
|
||||||
|
| `group_id` | int64 | | 群号 |
|
||||||
|
| `user_id` | int64 | | 成员id |
|
||||||
|
| `title` | string | | 新头衔 |
|
||||||
|
|
||||||
### 接收到离线文件
|
### 接收到离线文件
|
||||||
|
|
||||||
**上报数据**
|
**上报数据**
|
||||||
|
70
docs/file.md
70
docs/file.md
@ -1,38 +1,68 @@
|
|||||||
# 文件
|
# 文件
|
||||||
|
|
||||||
go-cqhttp 默认生成的文件树如下所示:
|
go-cqhttp 默认生成的文件树如下所示:
|
||||||
|
|
||||||
````
|
```
|
||||||
.
|
.
|
||||||
├── go-cqhttp
|
├── go-cqhttp
|
||||||
├── config.hjson
|
├── config.yml
|
||||||
├── device.json
|
├── device.json
|
||||||
├── logs
|
├── logs
|
||||||
│ └── xx-xx-xx.log
|
│ └── xx-xx-xx.log
|
||||||
└── data
|
└── data
|
||||||
├── images
|
├── images
|
||||||
│ └── xxxx.image
|
│ └── xxxx.image
|
||||||
└── db
|
└── levleldb
|
||||||
````
|
```
|
||||||
|
|
||||||
| 文件 | 用途 |
|
| 文件 | 用途 |
|
||||||
| ----------- | ------------------- |
|
| ------------ | -------------------- |
|
||||||
| go-cqhttp | go-cqhttp可执行文件 |
|
| go-cqhttp | go-cqhttp 可执行文件 |
|
||||||
| config.hjson | 运行配置文件 |
|
| config.yml | 运行配置文件 |
|
||||||
| device.json | 虚拟设备配置文件 |
|
| device.json | 虚拟设备配置文件 |
|
||||||
| logs | 日志存放目录 |
|
| logs | 日志存放目录 |
|
||||||
| data | 数据目录 |
|
| data | 数据目录 |
|
||||||
| data/images | 图片缓存目录 |
|
| data/leveldb | 数据库目录 |
|
||||||
| data/db | 数据库目录 |
|
| data/images | 图片缓存目录 |
|
||||||
|
| data/voices | 语音缓存目录 |
|
||||||
|
| data/videos | 视频缓存目录 |
|
||||||
|
| data/cache | 发送图片缓存目录 |
|
||||||
|
|
||||||
## 图片缓存文件
|
## 图片缓存文件
|
||||||
|
|
||||||
出于性能考虑,go-cqhttp 并不会将图片源文件下载到本地,而是生成一个可以和QQ服务器对应的缓存文件 (.image),该缓存文件结构如下:
|
出于性能考虑,go-cqhttp 并不会将图片源文件下载到本地,而是生成一个可以和 QQ 服务器对应的缓存文件 (.image),该缓存文件结构如下:
|
||||||
|
|
||||||
| 偏移 | 类型 | 说明 |
|
| 偏移 | 类型 | 说明 |
|
||||||
| --------------- | -------- | ------------------ |
|
| --------------- | -------- | -------------------- |
|
||||||
| 0x00 | [16]byte | 图片源文件MD5 HASH |
|
| 0x00 | [16]byte | 图片源文件 MD5 HASH |
|
||||||
| 0x10 | uint32 | 图片源文件大小 |
|
| 0x10 | uint32 | 图片源文件大小 |
|
||||||
| 0x14 | string | 图片原名(QQ内部ID) |
|
| 0x14 | string | 图片原名(QQ内部ID) |
|
||||||
| 0x14 + 原名长度 | string | 图片下载链接 |
|
| 0x14 + 原名长度 | string | 图片下载链接 |
|
||||||
|
|
||||||
|
# MINE
|
||||||
|
|
||||||
|
启用MINE检查可以及时发现媒体资源格式错误引起的上传失败(通常表现为,请求网页图片,但服务端返回404.html)
|
||||||
|
|
||||||
|
在配置文件中设置 `skip-mine-scan: false`后 ,go-cqhttp 会在上传媒体资源(视频暂不支持)前对MINE进行检查,
|
||||||
|
详细允许类型如下所示:
|
||||||
|
|
||||||
|
图片:
|
||||||
|
> image/bmp
|
||||||
|
> image/gif
|
||||||
|
> image/jpeg
|
||||||
|
> image/png
|
||||||
|
> image/webp
|
||||||
|
|
||||||
|
语音:
|
||||||
|
> audio/aac
|
||||||
|
> audio/aiff
|
||||||
|
> audio/amr
|
||||||
|
> audio/ape
|
||||||
|
> audio/flac
|
||||||
|
> audio/midi
|
||||||
|
> audio/mp4
|
||||||
|
> audio/mpeg
|
||||||
|
> audio/ogg
|
||||||
|
> audio/wav
|
||||||
|
> audio/x-m4a
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# 滑块验证码
|
# 滑块验证码
|
||||||
|
|
||||||
|
> 该文档已过期, 最新版本下可直接使用手机扫描二维码通过验证.
|
||||||
|
|
||||||
由于TX最新的限制, 所有协议在陌生设备/IP登录时都有可能被要求通过滑块验证码, 否则将会出现 `当前上网环境异常` 的错误. 目前我们准备了两个临时方案应对该验证码.
|
由于TX最新的限制, 所有协议在陌生设备/IP登录时都有可能被要求通过滑块验证码, 否则将会出现 `当前上网环境异常` 的错误. 目前我们准备了两个临时方案应对该验证码.
|
||||||
|
|
||||||
> 如果您有一台运行Windows的PC/Server 并且不会抓包操作, 我们建议直接使用方案B
|
> 如果您有一台运行Windows的PC/Server 并且不会抓包操作, 我们建议直接使用方案B
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestVersionNameCompare(t *testing.T) {
|
func TestVersionNameCompare(t *testing.T) {
|
||||||
var tests = [...]struct {
|
tests := [...]struct {
|
||||||
current string
|
current string
|
||||||
remote string
|
remote string
|
||||||
expected bool
|
expected bool
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
//go:build (linux || (windows && !arm) || darwin) && (386 || amd64 || arm || arm64) && !race
|
//go:build (linux || (windows && !arm && !arm64) || darwin) && (386 || amd64 || arm || arm64) && !race && !nosilk
|
||||||
// +build linux windows,!arm darwin
|
// +build linux windows,!arm,!arm64 darwin
|
||||||
// +build 386 amd64 arm arm64
|
// +build 386 amd64 arm arm64
|
||||||
// +build !race
|
// +build !race
|
||||||
|
// +build !nosilk
|
||||||
|
|
||||||
package codec
|
package codec
|
||||||
|
|
||||||
@ -29,6 +30,10 @@ func EncodeToSilk(record []byte, tempName string, useCache bool) (silkWav []byte
|
|||||||
// 2.转换pcm
|
// 2.转换pcm
|
||||||
pcmPath := path.Join(silkCachePath, tempName+".pcm")
|
pcmPath := path.Join(silkCachePath, tempName+".pcm")
|
||||||
cmd := exec.Command("ffmpeg", "-i", rawPath, "-f", "s16le", "-ar", "24000", "-ac", "1", pcmPath)
|
cmd := exec.Command("ffmpeg", "-i", rawPath, "-f", "s16le", "-ar", "24000", "-ac", "1", pcmPath)
|
||||||
|
if Debug {
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
if err = cmd.Run(); err != nil {
|
if err = cmd.Run(); err != nil {
|
||||||
return nil, errors.Wrap(err, "convert pcm file error")
|
return nil, errors.Wrap(err, "convert pcm file error")
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//go:build (!arm && !arm64 && !amd64 && !386) || race
|
//go:build (!arm && !arm64 && !amd64 && !386) || (!windows && !linux && !darwin) || (windows && arm) || (windows && arm64) || race || nosilk
|
||||||
// +build !arm,!arm64,!amd64,!386 race
|
// +build !arm,!arm64,!amd64,!386 !windows,!linux,!darwin windows,arm windows,arm64 race nosilk
|
||||||
|
|
||||||
package codec
|
package codec
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
//go:build !windows && !linux && !darwin
|
|
||||||
// +build !windows,!linux,!darwin
|
|
||||||
|
|
||||||
package codec
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// 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,13 +0,0 @@
|
|||||||
package codec
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// 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
global/codec/stubs.go
Normal file
4
global/codec/stubs.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package codec
|
||||||
|
|
||||||
|
// Debug mode controls the ffmpeg output.
|
||||||
|
var Debug bool
|
@ -160,70 +160,80 @@ func Get() *Config {
|
|||||||
generateConfig()
|
generateConfig()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
if hasEnvironmentConf {
|
||||||
|
// type convert tools
|
||||||
|
toInt64 := func(str string) int64 {
|
||||||
|
i, _ := strconv.ParseInt(str, 10, 64)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
// type convert tools
|
// load config from environment variable
|
||||||
toInt64 := func(str string) int64 {
|
global.SetAtDefault(&config.Account.Uin, toInt64(os.Getenv("GCQ_UIN")), int64(0))
|
||||||
i, _ := strconv.ParseInt(str, 10, 64)
|
global.SetAtDefault(&config.Account.Password, os.Getenv("GCQ_PWD"), "")
|
||||||
return i
|
global.SetAtDefault(&config.Account.Status, int32(toInt64(os.Getenv("GCQ_STATUS"))), int32(0))
|
||||||
}
|
global.SetAtDefault(&config.Account.ReLogin.Disabled, !global.EnsureBool(os.Getenv("GCQ_RELOGIN_DISABLED"), true), false)
|
||||||
|
global.SetAtDefault(&config.Account.ReLogin.Delay, uint(toInt64(os.Getenv("GCQ_RELOGIN_DELAY"))), uint(0))
|
||||||
// load config from environment variable
|
global.SetAtDefault(&config.Account.ReLogin.MaxTimes, uint(toInt64(os.Getenv("GCQ_RELOGIN_MAX_TIMES"))), uint(0))
|
||||||
global.SetAtDefault(&config.Account.Uin, toInt64(os.Getenv("GCQ_UIN")), int64(0))
|
dbConf := &LevelDBConfig{Enable: global.EnsureBool(os.Getenv("GCQ_LEVELDB"), true)}
|
||||||
global.SetAtDefault(&config.Account.Password, os.Getenv("GCQ_PWD"), "")
|
if config.Database == nil {
|
||||||
global.SetAtDefault(&config.Account.Status, int32(toInt64(os.Getenv("GCQ_STATUS"))), int32(0))
|
config.Database = make(map[string]yaml.Node)
|
||||||
global.SetAtDefault(&config.Account.ReLogin.Disabled, !global.EnsureBool(os.Getenv("GCQ_RELOGIN"), false), false)
|
|
||||||
global.SetAtDefault(&config.Account.ReLogin.Delay, uint(toInt64(os.Getenv("GCQ_RELOGIN_DELAY"))), uint(0))
|
|
||||||
global.SetAtDefault(&config.Account.ReLogin.MaxTimes, uint(toInt64(os.Getenv("GCQ_RELOGIN_MAX_TIMES"))), uint(0))
|
|
||||||
accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN")
|
|
||||||
if os.Getenv("GCQ_HTTP_PORT") != "" {
|
|
||||||
node := &yaml.Node{}
|
|
||||||
httpConf := &HTTPServer{
|
|
||||||
Host: "0.0.0.0",
|
|
||||||
Port: 5700,
|
|
||||||
MiddleWares: MiddleWares{
|
|
||||||
AccessToken: accessTokenEnv,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
global.SetExcludeDefault(&httpConf.Disabled, global.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false)
|
config.Database["leveldb"] = func() yaml.Node {
|
||||||
global.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "")
|
n := &yaml.Node{}
|
||||||
global.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0)
|
_ = n.Encode(dbConf)
|
||||||
if os.Getenv("GCQ_HTTP_POST_URL") != "" {
|
return *n
|
||||||
httpConf.Post = append(httpConf.Post, struct {
|
}()
|
||||||
URL string `yaml:"url"`
|
accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN")
|
||||||
Secret string `yaml:"secret"`
|
if os.Getenv("GCQ_HTTP_PORT") != "" {
|
||||||
}{os.Getenv("GCQ_HTTP_POST_URL"), os.Getenv("GCQ_HTTP_POST_SECRET")})
|
node := &yaml.Node{}
|
||||||
|
httpConf := &HTTPServer{
|
||||||
|
Host: "0.0.0.0",
|
||||||
|
Port: 5700,
|
||||||
|
MiddleWares: MiddleWares{
|
||||||
|
AccessToken: accessTokenEnv,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
global.SetExcludeDefault(&httpConf.Disabled, global.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false)
|
||||||
|
global.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "")
|
||||||
|
global.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0)
|
||||||
|
if os.Getenv("GCQ_HTTP_POST_URL") != "" {
|
||||||
|
httpConf.Post = append(httpConf.Post, struct {
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
Secret string `yaml:"secret"`
|
||||||
|
}{os.Getenv("GCQ_HTTP_POST_URL"), os.Getenv("GCQ_HTTP_POST_SECRET")})
|
||||||
|
}
|
||||||
|
_ = node.Encode(httpConf)
|
||||||
|
config.Servers = append(config.Servers, map[string]yaml.Node{"http": *node})
|
||||||
}
|
}
|
||||||
_ = node.Encode(httpConf)
|
if os.Getenv("GCQ_WS_PORT") != "" {
|
||||||
config.Servers = append(config.Servers, map[string]yaml.Node{"http": *node})
|
node := &yaml.Node{}
|
||||||
}
|
wsServerConf := &WebsocketServer{
|
||||||
if os.Getenv("GCQ_WS_PORT") != "" {
|
Host: "0.0.0.0",
|
||||||
node := &yaml.Node{}
|
Port: 6700,
|
||||||
wsServerConf := &WebsocketServer{
|
MiddleWares: MiddleWares{
|
||||||
Host: "0.0.0.0",
|
AccessToken: accessTokenEnv,
|
||||||
Port: 6700,
|
},
|
||||||
MiddleWares: MiddleWares{
|
}
|
||||||
AccessToken: accessTokenEnv,
|
global.SetExcludeDefault(&wsServerConf.Disabled, global.EnsureBool(os.Getenv("GCQ_WS_DISABLE"), false), false)
|
||||||
},
|
global.SetExcludeDefault(&wsServerConf.Host, os.Getenv("GCQ_WS_HOST"), "")
|
||||||
|
global.SetExcludeDefault(&wsServerConf.Port, int(toInt64(os.Getenv("GCQ_WS_PORT"))), 0)
|
||||||
|
_ = node.Encode(wsServerConf)
|
||||||
|
config.Servers = append(config.Servers, map[string]yaml.Node{"ws": *node})
|
||||||
}
|
}
|
||||||
global.SetExcludeDefault(&wsServerConf.Disabled, global.EnsureBool(os.Getenv("GCQ_WS_DISABLE"), false), false)
|
if os.Getenv("GCQ_RWS_API") != "" || os.Getenv("GCQ_RWS_EVENT") != "" || os.Getenv("GCQ_RWS_UNIVERSAL") != "" {
|
||||||
global.SetExcludeDefault(&wsServerConf.Host, os.Getenv("GCQ_WS_HOST"), "")
|
node := &yaml.Node{}
|
||||||
global.SetExcludeDefault(&wsServerConf.Port, int(toInt64(os.Getenv("GCQ_WS_PORT"))), 0)
|
rwsConf := &WebsocketReverse{
|
||||||
_ = node.Encode(wsServerConf)
|
MiddleWares: MiddleWares{
|
||||||
config.Servers = append(config.Servers, map[string]yaml.Node{"ws": *node})
|
AccessToken: accessTokenEnv,
|
||||||
}
|
},
|
||||||
if os.Getenv("GCQ_RWS_API") != "" || os.Getenv("GCQ_RWS_EVENT") != "" || os.Getenv("GCQ_RWS_UNIVERSAL") != "" {
|
}
|
||||||
node := &yaml.Node{}
|
global.SetExcludeDefault(&rwsConf.Disabled, global.EnsureBool(os.Getenv("GCQ_RWS_DISABLE"), false), false)
|
||||||
rwsConf := &WebsocketReverse{
|
global.SetExcludeDefault(&rwsConf.API, os.Getenv("GCQ_RWS_API"), "")
|
||||||
MiddleWares: MiddleWares{
|
global.SetExcludeDefault(&rwsConf.Event, os.Getenv("GCQ_RWS_EVENT"), "")
|
||||||
AccessToken: accessTokenEnv,
|
global.SetExcludeDefault(&rwsConf.Universal, os.Getenv("GCQ_RWS_UNIVERSAL"), "")
|
||||||
},
|
_ = node.Encode(rwsConf)
|
||||||
|
config.Servers = append(config.Servers, map[string]yaml.Node{"ws-reverse": *node})
|
||||||
}
|
}
|
||||||
global.SetExcludeDefault(&rwsConf.Disabled, global.EnsureBool(os.Getenv("GCQ_RWS_DISABLE"), false), false)
|
|
||||||
global.SetExcludeDefault(&rwsConf.API, os.Getenv("GCQ_RWS_API"), "")
|
|
||||||
global.SetExcludeDefault(&rwsConf.Event, os.Getenv("GCQ_RWS_EVENT"), "")
|
|
||||||
global.SetExcludeDefault(&rwsConf.Universal, os.Getenv("GCQ_RWS_UNIVERSAL"), "")
|
|
||||||
_ = node.Encode(rwsConf)
|
|
||||||
config.Servers = append(config.Servers, map[string]yaml.Node{"ws-reverse": *node})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return config
|
return config
|
||||||
|
@ -3,7 +3,6 @@ package global
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
@ -15,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
|
"github.com/segmentio/asm/base64"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ var (
|
|||||||
// PathExists 判断给定path是否存在
|
// PathExists 判断给定path是否存在
|
||||||
func PathExists(path string) bool {
|
func PathExists(path string) bool {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
return err == nil || os.IsExist(err)
|
return err == nil || errors.Is(err, os.ErrExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAllText 读取给定path对应文件,无法读取时返回空值
|
// ReadAllText 读取给定path对应文件,无法读取时返回空值
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -174,3 +175,21 @@ func GetLogLevel(level string) []logrus.Level {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogFormat specialize for go-cqhttp
|
||||||
|
type LogFormat struct{}
|
||||||
|
|
||||||
|
// Format implements logrus.Formatter
|
||||||
|
func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||||
|
buf := NewBuffer()
|
||||||
|
defer PutBuffer(buf)
|
||||||
|
buf.WriteByte('[')
|
||||||
|
buf.WriteString(entry.Time.Format("2006-01-02 15:04:05"))
|
||||||
|
buf.WriteString("] [")
|
||||||
|
buf.WriteString(strings.ToUpper(entry.Level.String()))
|
||||||
|
buf.WriteString("]: ")
|
||||||
|
buf.WriteString(entry.Message)
|
||||||
|
buf.WriteString(" \n")
|
||||||
|
ret := append([]byte(nil), buf.Bytes()...) // copy buffer
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
@ -96,13 +96,14 @@ func SetAtDefault(variable, value, defaultValue interface{}) {
|
|||||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if v.Elem().Interface() != defaultValue {
|
v = v.Elem()
|
||||||
|
if v.Interface() != defaultValue {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if v.Elem().Kind() != v2.Kind() {
|
if v.Kind() != v2.Kind() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v.Elem().Set(v2)
|
v.Set(v2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetExcludeDefault 在目标值 value 不为默认值 defaultValue 时修改 variable 为 value
|
// SetExcludeDefault 在目标值 value 不为默认值 defaultValue 时修改 variable 为 value
|
||||||
@ -112,13 +113,14 @@ func SetExcludeDefault(variable, value, defaultValue interface{}) {
|
|||||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if v2.Elem().Interface() != defaultValue {
|
v = v.Elem()
|
||||||
|
if reflect.Indirect(v2).Interface() != defaultValue {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if v.Elem().Kind() != v2.Kind() {
|
if v.Kind() != v2.Kind() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v.Elem().Set(v2)
|
v.Set(v2)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -18,11 +18,9 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var validTasks = map[string]func(){
|
||||||
validTasks = map[string]func(){
|
"dumpstack": dumpStack,
|
||||||
"dumpstack": dumpStack,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetupMainSignalHandler is for main to use at last
|
// SetupMainSignalHandler is for main to use at last
|
||||||
func SetupMainSignalHandler() <-chan struct{} {
|
func SetupMainSignalHandler() <-chan struct{} {
|
||||||
|
@ -7,3 +7,8 @@ package terminal
|
|||||||
func RunningByDoubleClick() bool {
|
func RunningByDoubleClick() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoMoreDoubleClick 提示用户不要双击运行,非Windows系统永远返回nil
|
||||||
|
func NoMoreDoubleClick() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunningByDoubleClick 检查是否通过双击直接运行
|
// RunningByDoubleClick 检查是否通过双击直接运行
|
||||||
@ -22,3 +25,43 @@ func RunningByDoubleClick() bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoMoreDoubleClick 提示用户不要双击运行,并生成安全启动脚本
|
||||||
|
func NoMoreDoubleClick() error {
|
||||||
|
r := boxW(0, "请勿通过双击直接运行本程序, 这将导致一些非预料的后果.\n请在shell中运行./go-cqhttp.exe\n点击确认将释出安全启动脚本,点击取消则关闭程序", "警告", 0x00000030|0x00000001)
|
||||||
|
if r == 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r = boxW(0, "点击确认将覆盖go-cqhttp.bat,点击取消则关闭程序", "警告", 0x00000030|0x00000001)
|
||||||
|
if r == 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile("go-cqhttp.bat", os.O_CREATE|os.O_RDWR, 0o666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("打开go-cqhttp.bat失败: %v", err)
|
||||||
|
}
|
||||||
|
_ = f.Truncate(0)
|
||||||
|
_, err = f.WriteString("%Created by go-cqhttp. DO NOT EDIT ME!%\nstart cmd /K go-cqhttp.exe")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("写入go-cqhttp.bat失败: %v", err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
boxW(0, "安全启动脚本已生成,请双击go-cqhttp.bat启动", "提示", 0x00000040|0x00000000)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoxW of Win32 API. Check https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw for more detail.
|
||||||
|
func boxW(hwnd uintptr, caption, title string, flags uint) int {
|
||||||
|
captionPtr, _ := syscall.UTF16PtrFromString(caption)
|
||||||
|
titlePtr, _ := syscall.UTF16PtrFromString(title)
|
||||||
|
ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
|
||||||
|
hwnd,
|
||||||
|
uintptr(unsafe.Pointer(captionPtr)),
|
||||||
|
uintptr(unsafe.Pointer(titlePtr)),
|
||||||
|
uintptr(flags))
|
||||||
|
|
||||||
|
return int(ret)
|
||||||
|
}
|
||||||
|
50
go.mod
50
go.mod
@ -1,36 +1,56 @@
|
|||||||
module github.com/Mrs4s/go-cqhttp
|
module github.com/Mrs4s/go-cqhttp
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
|
replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||||
github.com/Microsoft/go-winio v0.5.0
|
github.com/Microsoft/go-winio v0.5.0
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210726103104-1d68826cef0e
|
github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/gabriel-vasile/mimetype v1.3.1 // indirect
|
github.com/gabriel-vasile/mimetype v1.3.1
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/guonaihong/gout v0.2.1
|
github.com/guonaihong/gout v0.2.4
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.11
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||||
github.com/lestrrat-go/strftime v1.0.4 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/segmentio/asm v1.0.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
|
||||||
github.com/tidwall/gjson v1.8.1
|
github.com/tidwall/gjson v1.8.1
|
||||||
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2
|
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2
|
||||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
||||||
github.com/willf/bitset v1.2.0 // indirect
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
|
||||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
|
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2.0
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||||
|
github.com/google/uuid v1.1.1 // indirect
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
||||||
|
github.com/lestrrat-go/strftime v1.0.5 // indirect
|
||||||
|
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
|
github.com/tidwall/match v1.0.3 // indirect
|
||||||
|
github.com/tidwall/pretty v1.1.0 // indirect
|
||||||
|
github.com/willf/bitset v1.2.0 // indirect
|
||||||
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||||
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||||
|
modernc.org/libc v1.8.1 // indirect
|
||||||
|
modernc.org/mathutil v1.2.2 // indirect
|
||||||
|
modernc.org/memory v1.0.4 // indirect
|
||||||
|
)
|
||||||
|
59
go.sum
59
go.sum
@ -4,8 +4,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
|
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
|
||||||
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210726103104-1d68826cef0e h1:PgFshw1L5TVdiDRLgr/bSotPGaGXYzbtn5cDBBvpL6U=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82 h1:VAav2Z5n41cp2bjskooPXAg9W39qQeEEj3FmYlnS3+E=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210726103104-1d68826cef0e/go.mod h1:CPaznIPn415uQqxJgjyMHLqGLkvLS6R6+bkW3/fe08Q=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww=
|
||||||
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
||||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
@ -22,15 +22,15 @@ github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX
|
|||||||
github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.6.0 h1:Lb3veSYoGaNck69fV2+Vf2juLSsHpMTf3Vk5+X+EDJg=
|
github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU=
|
||||||
github.com/gin-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@ -42,15 +42,15 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
|||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@ -59,27 +59,26 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/guonaihong/gout v0.2.1 h1:XnaS92y/Mpcu4MOQU5cx4hTKhbqFznbIE/uUI5sMY9E=
|
github.com/guonaihong/gout v0.2.4 h1:BlWpWWay/Q1LkyIwupEWBZE3PMl4xzzAgMHw+OrZxBs=
|
||||||
github.com/guonaihong/gout v0.2.1/go.mod h1:JkjNv1G2oRWvFgP/r4DUbYhoeIBB0zMP2j1ID+5CYpU=
|
github.com/guonaihong/gout v0.2.4/go.mod h1:ISabiAAj0z1h3bOFUKzfRqPMvX0wmcYzIh6i4xIxMPo=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
||||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
||||||
github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8=
|
github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE=
|
||||||
github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
|
github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
|
||||||
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA=
|
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA=
|
||||||
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA=
|
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
@ -88,9 +87,8 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@ -104,14 +102,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/segmentio/asm v1.0.0 h1:GMF7/2eLkte13LERSOmPI766AJXJDRlsqqiN8T7Mfmk=
|
||||||
|
github.com/segmentio/asm v1.0.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@ -119,8 +117,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk=
|
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA=
|
|
||||||
github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU=
|
github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU=
|
||||||
github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||||
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
||||||
@ -136,8 +132,9 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
|
|||||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4=
|
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4=
|
||||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko=
|
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||||
@ -149,8 +146,8 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8=
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8=
|
||||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
@ -158,10 +155,12 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -179,8 +178,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs=
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@ -203,10 +202,10 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
|
|||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.25.0/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-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=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
16
login.go
16
login.go
@ -117,21 +117,7 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
|||||||
var text string
|
var text string
|
||||||
switch res.Error {
|
switch res.Error {
|
||||||
case client.SliderNeededError:
|
case client.SliderNeededError:
|
||||||
log.Warnf("登录需要滑条验证码. ")
|
log.Warnf("登录需要滑条验证码, 请使用手机QQ扫描二维码以继续登录.")
|
||||||
log.Warnf("请参考文档 -> https://docs.go-cqhttp.org/faq/slider.html <- 进行处理")
|
|
||||||
log.Warnf("1. 自行抓包并获取 Ticket 输入.")
|
|
||||||
log.Warnf("2. 使用手机QQ扫描二维码登入. (推荐)")
|
|
||||||
log.Warn("请输入(1 - 2) (将在10秒后自动选择2):")
|
|
||||||
text = readLineTimeout(time.Second*10, "2")
|
|
||||||
if strings.Contains(text, "1") {
|
|
||||||
println()
|
|
||||||
log.Warnf("请用浏览器打开 -> %v <- 并获取Ticket.", res.VerifyUrl)
|
|
||||||
println()
|
|
||||||
log.Warn("请输入Ticket: (Enter 提交)")
|
|
||||||
text = readLine()
|
|
||||||
res, err = cli.SubmitTicket(text)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cli.Disconnect()
|
cli.Disconnect()
|
||||||
cli.Release()
|
cli.Release()
|
||||||
cli = client.NewClientEmpty()
|
cli = client.NewClientEmpty()
|
||||||
|
30
main.go
30
main.go
@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/global/codec"
|
||||||
"github.com/Mrs4s/go-cqhttp/global/config"
|
"github.com/Mrs4s/go-cqhttp/global/config"
|
||||||
"github.com/Mrs4s/go-cqhttp/global/terminal"
|
"github.com/Mrs4s/go-cqhttp/global/terminal"
|
||||||
"github.com/Mrs4s/go-cqhttp/global/update"
|
"github.com/Mrs4s/go-cqhttp/global/update"
|
||||||
@ -29,7 +30,6 @@ import (
|
|||||||
"github.com/guonaihong/gout"
|
"github.com/guonaihong/gout"
|
||||||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
easy "github.com/t-tomalak/logrus-easy-formatter"
|
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
@ -55,7 +55,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := flag.String("c", config.DefaultConfigFile, "configuration filename default is config.hjson")
|
c := flag.String("c", config.DefaultConfigFile, "configuration filename")
|
||||||
d := flag.Bool("d", false, "running as a daemon")
|
d := flag.Bool("d", false, "running as a daemon")
|
||||||
h := flag.Bool("h", false, "this help")
|
h := flag.Bool("h", false, "this help")
|
||||||
wd := flag.String("w", "", "cover the working directory")
|
wd := flag.String("w", "", "cover the working directory")
|
||||||
@ -79,18 +79,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
if conf.Output.Debug {
|
if conf.Output.Debug {
|
||||||
log.SetReportCaller(true)
|
log.SetReportCaller(true)
|
||||||
|
codec.Debug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
logFormatter := &easy.Formatter{
|
|
||||||
TimestampFormat: "2006-01-02 15:04:05",
|
|
||||||
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
|
||||||
}
|
|
||||||
rotateOptions := []rotatelogs.Option{
|
rotateOptions := []rotatelogs.Option{
|
||||||
rotatelogs.WithRotationTime(time.Hour * 24),
|
rotatelogs.WithRotationTime(time.Hour * 24),
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.Output.LogAging > 0 {
|
if conf.Output.LogAging > 0 {
|
||||||
rotateOptions = append(rotateOptions, rotatelogs.WithMaxAge(time.Hour*24*time.Duration(conf.Output.LogAging)))
|
rotateOptions = append(rotateOptions, rotatelogs.WithMaxAge(time.Hour*24*time.Duration(conf.Output.LogAging)))
|
||||||
|
} else {
|
||||||
|
rotateOptions = append(rotateOptions, rotatelogs.WithMaxAge(time.Hour*24*365*10))
|
||||||
}
|
}
|
||||||
if conf.Output.LogForceNew {
|
if conf.Output.LogForceNew {
|
||||||
rotateOptions = append(rotateOptions, rotatelogs.ForceNewFile())
|
rotateOptions = append(rotateOptions, rotatelogs.ForceNewFile())
|
||||||
@ -102,7 +101,7 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.AddHook(global.NewLocalHook(w, logFormatter, global.GetLogLevel(conf.Output.LogLevel)...))
|
log.AddHook(global.NewLocalHook(w, global.LogFormat{}, global.GetLogLevel(conf.Output.LogLevel)...))
|
||||||
|
|
||||||
mkCacheDir := func(path string, _type string) {
|
mkCacheDir := func(path string, _type string) {
|
||||||
if !global.PathExists(path) {
|
if !global.PathExists(path) {
|
||||||
@ -137,9 +136,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if terminal.RunningByDoubleClick() && !isFastStart {
|
if terminal.RunningByDoubleClick() && !isFastStart {
|
||||||
log.Warning("警告: 强烈不推荐通过双击直接运行本程序, 这将导致一些非预料的后果.")
|
err := terminal.NoMoreDoubleClick()
|
||||||
log.Warning("将等待10s后启动")
|
if err != nil {
|
||||||
time.Sleep(time.Second * 10)
|
log.Errorf("遇到错误: %v", err)
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf.Account.Uin == 0 || (conf.Account.Password == "" && !conf.Account.Encrypt)) && !global.PathExists("session.token") {
|
if (conf.Account.Uin == 0 || (conf.Account.Password == "" && !conf.Account.Encrypt)) && !global.PathExists("session.token") {
|
||||||
@ -263,6 +265,7 @@ func main() {
|
|||||||
text := readLineTimeout(time.Second*5, "1")
|
text := readLineTimeout(time.Second*5, "1")
|
||||||
if text == "2" {
|
if text == "2" {
|
||||||
_ = os.Remove("session.token")
|
_ = os.Remove("session.token")
|
||||||
|
log.Infof("缓存已删除.")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,6 +310,7 @@ func main() {
|
|||||||
time.Sleep(time.Second * time.Duration(conf.Account.ReLogin.Delay))
|
time.Sleep(time.Second * time.Duration(conf.Account.ReLogin.Delay))
|
||||||
for {
|
for {
|
||||||
if conf.Account.ReLogin.Disabled {
|
if conf.Account.ReLogin.Disabled {
|
||||||
|
log.Warnf("未启用自动重连, 将退出.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if times > conf.Account.ReLogin.MaxTimes && conf.Account.ReLogin.MaxTimes != 0 {
|
if times > conf.Account.ReLogin.MaxTimes && conf.Account.ReLogin.MaxTimes != 0 {
|
||||||
@ -319,6 +323,10 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
if cli.Online {
|
||||||
|
log.Infof("登录已完成")
|
||||||
|
break
|
||||||
|
}
|
||||||
log.Warnf("尝试重连...")
|
log.Warnf("尝试重连...")
|
||||||
err := cli.TokenLogin(AccountToken)
|
err := cli.TokenLogin(AccountToken)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -629,7 +637,7 @@ func newClient() *client.QQClient {
|
|||||||
log.Infof("检测到 address.txt 文件. 将覆盖目标IP.")
|
log.Infof("检测到 address.txt 文件. 将覆盖目标IP.")
|
||||||
addr := global.ReadAddrFile("address.txt")
|
addr := global.ReadAddrFile("address.txt")
|
||||||
if len(addr) > 0 {
|
if len(addr) > 0 {
|
||||||
cli.SetCustomServer(addr)
|
c.SetCustomServer(addr)
|
||||||
}
|
}
|
||||||
log.Infof("读取到 %v 个自定义地址.", len(addr))
|
log.Infof("读取到 %v 个自定义地址.", len(addr))
|
||||||
}
|
}
|
||||||
|
139
server/api.go
139
server/api.go
@ -32,8 +32,16 @@ func getFriendList(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
|||||||
return bot.CQGetFriendList()
|
return bot.CQGetFriendList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUnidirectionalFriendList(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||||
|
return bot.CQGetUnidirectionalFriendList()
|
||||||
|
}
|
||||||
|
|
||||||
func deleteFriend(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
func deleteFriend(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
return bot.CQDeleteFriend(p.Get("id").Int())
|
return bot.CQDeleteFriend(p.Get("[user_id,id]").Int())
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteUnidirectionalFriend(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQDeleteUnidirectionalFriend(p.Get("user_id").Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGroupList(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
func getGroupList(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
@ -350,69 +358,76 @@ func setModelShow(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
|||||||
return bot.CQSetModelShow(p.Get("model").String(), p.Get("model_show").String())
|
return bot.CQSetModelShow(p.Get("model").String(), p.Get("model_show").String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func markMSGAsRead(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
||||||
|
return bot.CQMarkMessageAsRead(int32(p.Get("message_id").Int()))
|
||||||
|
}
|
||||||
|
|
||||||
// API 是go-cqhttp当前支持的所有api的映射表
|
// API 是go-cqhttp当前支持的所有api的映射表
|
||||||
var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
||||||
"get_login_info": getLoginInfo,
|
"get_login_info": getLoginInfo,
|
||||||
"get_friend_list": getFriendList,
|
"get_friend_list": getFriendList,
|
||||||
"delete_friend": deleteFriend,
|
"get_unidirectional_friend_list": getUnidirectionalFriendList,
|
||||||
"get_group_list": getGroupList,
|
"delete_friend": deleteFriend,
|
||||||
"get_group_info": getGroupInfo,
|
"delete_unidirectional_friend": deleteUnidirectionalFriend,
|
||||||
"get_group_member_list": getGroupMemberList,
|
"get_group_list": getGroupList,
|
||||||
"get_group_member_info": getGroupMemberInfo,
|
"get_group_info": getGroupInfo,
|
||||||
"send_msg": sendMSG,
|
"get_group_member_list": getGroupMemberList,
|
||||||
"send_group_msg": sendGroupMSG,
|
"get_group_member_info": getGroupMemberInfo,
|
||||||
"send_group_forward_msg": sendGroupForwardMSG,
|
"send_msg": sendMSG,
|
||||||
"send_private_msg": sendPrivateMSG,
|
"send_group_msg": sendGroupMSG,
|
||||||
"delete_msg": deleteMSG,
|
"send_group_forward_msg": sendGroupForwardMSG,
|
||||||
"set_friend_add_request": setFriendAddRequest,
|
"send_private_msg": sendPrivateMSG,
|
||||||
"set_group_add_request": setGroupAddRequest,
|
"delete_msg": deleteMSG,
|
||||||
"set_group_card": setGroupCard,
|
"set_friend_add_request": setFriendAddRequest,
|
||||||
"set_group_special_title": setGroupSpecialTitle,
|
"set_group_add_request": setGroupAddRequest,
|
||||||
"set_group_kick": setGroupKick,
|
"set_group_card": setGroupCard,
|
||||||
"set_group_ban": setGroupBan,
|
"set_group_special_title": setGroupSpecialTitle,
|
||||||
"set_group_whole_ban": setGroupWholeBan,
|
"set_group_kick": setGroupKick,
|
||||||
"set_group_name": setGroupName,
|
"set_group_ban": setGroupBan,
|
||||||
"set_group_admin": setGroupAdmin,
|
"set_group_whole_ban": setGroupWholeBan,
|
||||||
"_send_group_notice": sendGroupNotice,
|
"set_group_name": setGroupName,
|
||||||
"set_group_leave": setGroupLeave,
|
"set_group_admin": setGroupAdmin,
|
||||||
"get_image": getImage,
|
"_send_group_notice": sendGroupNotice,
|
||||||
"get_forward_msg": getForwardMSG,
|
"set_group_leave": setGroupLeave,
|
||||||
"get_msg": getMSG,
|
"get_image": getImage,
|
||||||
"download_file": downloadFile,
|
"get_forward_msg": getForwardMSG,
|
||||||
"get_group_honor_info": getGroupHonorInfo,
|
"get_msg": getMSG,
|
||||||
"set_restart": setRestart,
|
"download_file": downloadFile,
|
||||||
"can_send_image": canSendImage,
|
"get_group_honor_info": getGroupHonorInfo,
|
||||||
"can_send_record": canSendRecord,
|
"set_restart": setRestart,
|
||||||
"get_stranger_info": getStrangerInfo,
|
"can_send_image": canSendImage,
|
||||||
"get_status": getStatus,
|
"can_send_record": canSendRecord,
|
||||||
"get_version_info": getVersionInfo,
|
"get_stranger_info": getStrangerInfo,
|
||||||
"get_group_system_msg": getGroupSystemMSG,
|
"get_status": getStatus,
|
||||||
"get_group_file_system_info": getGroupFileSystemInfo,
|
"get_version_info": getVersionInfo,
|
||||||
"get_group_root_files": getGroupRootFiles,
|
"get_group_system_msg": getGroupSystemMSG,
|
||||||
"get_group_files_by_folder": getGroupFilesByFolder,
|
"get_group_file_system_info": getGroupFileSystemInfo,
|
||||||
"get_group_file_url": getGroupFileURL,
|
"get_group_root_files": getGroupRootFiles,
|
||||||
"create_group_file_folder": groupFileCreateFolder,
|
"get_group_files_by_folder": getGroupFilesByFolder,
|
||||||
"delete_group_folder": deleteGroupFolder,
|
"get_group_file_url": getGroupFileURL,
|
||||||
"delete_group_file": deleteGroupFile,
|
"create_group_file_folder": groupFileCreateFolder,
|
||||||
"upload_group_file": uploadGroupFile,
|
"delete_group_folder": deleteGroupFolder,
|
||||||
"get_group_msg_history": getGroupMsgHistory,
|
"delete_group_file": deleteGroupFile,
|
||||||
"_get_vip_info": getVipInfo,
|
"upload_group_file": uploadGroupFile,
|
||||||
"reload_event_filter": reloadEventFilter,
|
"get_group_msg_history": getGroupMsgHistory,
|
||||||
".ocr_image": ocrImage,
|
"_get_vip_info": getVipInfo,
|
||||||
"ocr_image": ocrImage,
|
"reload_event_filter": reloadEventFilter,
|
||||||
"get_group_at_all_remain": getGroupAtAllRemain,
|
".ocr_image": ocrImage,
|
||||||
"get_online_clients": getOnlineClients,
|
"ocr_image": ocrImage,
|
||||||
".get_word_slices": getWordSlices,
|
"get_group_at_all_remain": getGroupAtAllRemain,
|
||||||
"set_group_portrait": setGroupPortrait,
|
"get_online_clients": getOnlineClients,
|
||||||
"set_essence_msg": setEssenceMSG,
|
".get_word_slices": getWordSlices,
|
||||||
"delete_essence_msg": deleteEssenceMSG,
|
"set_group_portrait": setGroupPortrait,
|
||||||
"get_essence_msg_list": getEssenceMsgList,
|
"set_essence_msg": setEssenceMSG,
|
||||||
"check_url_safely": checkURLSafely,
|
"delete_essence_msg": deleteEssenceMSG,
|
||||||
"set_group_anonymous_ban": setGroupAnonymousBan,
|
"get_essence_msg_list": getEssenceMsgList,
|
||||||
".handle_quick_operation": handleQuickOperation,
|
"check_url_safely": checkURLSafely,
|
||||||
"qidian_get_account_info": getQiDianAccountInfo,
|
"set_group_anonymous_ban": setGroupAnonymousBan,
|
||||||
"_get_model_show": getModelShow,
|
".handle_quick_operation": handleQuickOperation,
|
||||||
"_set_model_show": setModelShow,
|
"qidian_get_account_info": getQiDianAccountInfo,
|
||||||
|
"_get_model_show": getModelShow,
|
||||||
|
"_set_model_show": setModelShow,
|
||||||
|
"mark_msg_as_read": markMSGAsRead,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *apiCaller) callAPI(action string, p resultGetter) coolq.MSG {
|
func (api *apiCaller) callAPI(action string, p resultGetter) coolq.MSG {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -16,7 +17,6 @@ import (
|
|||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
"github.com/guonaihong/gout"
|
"github.com/guonaihong/gout"
|
||||||
"github.com/guonaihong/gout/dataflow"
|
"github.com/guonaihong/gout/dataflow"
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
@ -30,8 +30,6 @@ type httpServer struct {
|
|||||||
accessToken string
|
accessToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
|
|
||||||
// HTTPClient 反向HTTP上报客户端
|
// HTTPClient 反向HTTP上报客户端
|
||||||
type HTTPClient struct {
|
type HTTPClient struct {
|
||||||
bot *coolq.CQBot
|
bot *coolq.CQBot
|
||||||
@ -53,13 +51,22 @@ func (h *httpCtx) Get(s string) gjson.Result {
|
|||||||
if j.Exists() {
|
if j.Exists() {
|
||||||
return j
|
return j
|
||||||
}
|
}
|
||||||
|
validJSONParam := func(p string) bool {
|
||||||
|
return (strings.HasPrefix(p, "{") || strings.HasPrefix(p, "[")) && gjson.Valid(p)
|
||||||
|
}
|
||||||
if h.postForm != nil {
|
if h.postForm != nil {
|
||||||
if form := h.postForm.Get(s); form != "" {
|
if form := h.postForm.Get(s); form != "" {
|
||||||
|
if validJSONParam(form) {
|
||||||
|
return gjson.Result{Type: gjson.JSON, Raw: form}
|
||||||
|
}
|
||||||
return gjson.Result{Type: gjson.String, Str: form}
|
return gjson.Result{Type: gjson.String, Str: form}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if h.query != nil {
|
if h.query != nil {
|
||||||
if query := h.query.Get(s); query != "" {
|
if query := h.query.Get(s); query != "" {
|
||||||
|
if validJSONParam(query) {
|
||||||
|
return gjson.Result{Type: gjson.JSON, Raw: query}
|
||||||
|
}
|
||||||
return gjson.Result{Type: gjson.String, Str: query}
|
return gjson.Result{Type: gjson.String, Str: query}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
@ -73,19 +75,39 @@ func longPolling(bot *coolq.CQBot, maxSize int) handler {
|
|||||||
if action != "get_updates" {
|
if action != "get_updates" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mutex.Lock()
|
var (
|
||||||
defer mutex.Unlock()
|
ok int32
|
||||||
if queue.Len() == 0 {
|
ch = make(chan []interface{}, 1)
|
||||||
cond.Wait()
|
timeout = time.Duration(p.Get("timeout").Int()) * time.Second
|
||||||
|
)
|
||||||
|
defer close(ch)
|
||||||
|
go func() {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
if queue.Len() == 0 {
|
||||||
|
cond.Wait()
|
||||||
|
}
|
||||||
|
if atomic.CompareAndSwapInt32(&ok, 0, 1) {
|
||||||
|
limit := int(p.Get("limit").Int())
|
||||||
|
if limit <= 0 || queue.Len() < limit {
|
||||||
|
limit = queue.Len()
|
||||||
|
}
|
||||||
|
ret := make([]interface{}, limit)
|
||||||
|
for i := 0; i < limit; i++ {
|
||||||
|
ret[i] = queue.Remove(queue.Front())
|
||||||
|
}
|
||||||
|
ch <- ret
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if timeout != 0 {
|
||||||
|
select {
|
||||||
|
case <-time.After(timeout):
|
||||||
|
atomic.StoreInt32(&ok, 1)
|
||||||
|
return coolq.OK([]interface{}{})
|
||||||
|
case ret := <-ch:
|
||||||
|
return coolq.OK(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
limit := int(p.Get("limit").Int())
|
return coolq.OK(<-ch)
|
||||||
if limit <= 0 || queue.Len() < limit {
|
|
||||||
limit = queue.Len()
|
|
||||||
}
|
|
||||||
ret := make([]interface{}, limit)
|
|
||||||
for i := 0; i < limit; i++ {
|
|
||||||
ret[i] = queue.Remove(queue.Front())
|
|
||||||
}
|
|
||||||
return coolq.OK(ret)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -34,20 +36,22 @@ type lambdaResponse struct {
|
|||||||
|
|
||||||
type lambdaResponseWriter struct {
|
type lambdaResponseWriter struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
|
buf bytes.Buffer
|
||||||
header http.Header
|
header http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *lambdaResponseWriter) Write(p []byte) (n int, err error) {
|
||||||
|
return l.buf.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
func (l *lambdaResponseWriter) Header() http.Header {
|
func (l *lambdaResponseWriter) Header() http.Header {
|
||||||
return l.header
|
return l.header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lambdaResponseWriter) Write(data []byte) (int, error) {
|
func (l *lambdaResponseWriter) flush() error {
|
||||||
buffer := global.NewBuffer()
|
buffer := global.NewBuffer()
|
||||||
defer global.PutBuffer(buffer)
|
defer global.PutBuffer(buffer)
|
||||||
body := ""
|
body := utils.B2S(l.buf.Bytes())
|
||||||
if data != nil {
|
|
||||||
body = utils.B2S(data)
|
|
||||||
}
|
|
||||||
header := make(map[string]string)
|
header := make(map[string]string)
|
||||||
for k, v := range l.header {
|
for k, v := range l.header {
|
||||||
header[k] = v[0]
|
header[k] = v[0]
|
||||||
@ -62,10 +66,9 @@ func (l *lambdaResponseWriter) Write(data []byte) (int, error) {
|
|||||||
r, _ := http.NewRequest("POST", cli.responseURL, buffer)
|
r, _ := http.NewRequest("POST", cli.responseURL, buffer)
|
||||||
do, err := cli.client.Do(r)
|
do, err := cli.client.Do(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return err
|
||||||
}
|
}
|
||||||
_ = do.Body.Close()
|
return do.Body.Close()
|
||||||
return len(data), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lambdaResponseWriter) WriteHeader(statusCode int) {
|
func (l *lambdaResponseWriter) WriteHeader(statusCode int) {
|
||||||
@ -84,8 +87,7 @@ func RunLambdaClient(bot *coolq.CQBot, conf *config.LambdaServer) {
|
|||||||
case "scf": // tencent serverless function
|
case "scf": // tencent serverless function
|
||||||
base := fmt.Sprintf("http://%s:%s/runtime/",
|
base := fmt.Sprintf("http://%s:%s/runtime/",
|
||||||
os.Getenv("SCF_RUNTIME_API"),
|
os.Getenv("SCF_RUNTIME_API"),
|
||||||
os.Getenv("SCF_RUNTIME_API_PORT"),
|
os.Getenv("SCF_RUNTIME_API_PORT"))
|
||||||
)
|
|
||||||
cli.nextURL = base + "invocation/next"
|
cli.nextURL = base + "invocation/next"
|
||||||
cli.responseURL = base + "invocation/response"
|
cli.responseURL = base + "invocation/response"
|
||||||
post, err := http.Post(base+"init/ready", "", nil)
|
post, err := http.Post(base+"init/ready", "", nil)
|
||||||
@ -114,19 +116,20 @@ func RunLambdaClient(bot *coolq.CQBot, conf *config.LambdaServer) {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
req := cli.next()
|
req := cli.next()
|
||||||
if req == nil {
|
writer := lambdaResponseWriter{statusCode: 200, header: make(http.Header)}
|
||||||
writer := lambdaResponseWriter{statusCode: 200}
|
|
||||||
_, _ = writer.Write(nil)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
func() {
|
func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
log.Warnf("Lambda 出现不可恢复错误: %v\n%s", e, debug.Stack())
|
log.Warnf("Lambda 出现不可恢复错误: %v\n%s", e, debug.Stack())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
server.ServeHTTP(&lambdaResponseWriter{header: make(http.Header)}, req)
|
if req != nil {
|
||||||
|
server.ServeHTTP(&writer, req)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
if err := writer.flush(); err != nil {
|
||||||
|
log.Warnf("Lambda 发送响应失败: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,8 +157,8 @@ func (c *lambdaClient) next() *http.Request {
|
|||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var req = new(http.Request)
|
req := new(http.Request)
|
||||||
var invoke = new(lambdaInvoke)
|
invoke := new(lambdaInvoke)
|
||||||
_ = json.NewDecoder(resp.Body).Decode(invoke)
|
_ = json.NewDecoder(resp.Body).Decode(invoke)
|
||||||
if invoke.HTTPMethod == "" { // 不是 api 网关
|
if invoke.HTTPMethod == "" { // 不是 api 网关
|
||||||
return nil
|
return nil
|
||||||
|
@ -160,7 +160,12 @@ func (c *websocketClient) connectEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("已连接到反向WebSocket Event服务器 %v", c.conf.Event)
|
log.Infof("已连接到反向WebSocket Event服务器 %v", c.conf.Event)
|
||||||
c.eventConn = &webSocketConn{Conn: conn, apiCaller: newAPICaller(c.bot)}
|
if c.eventConn == nil {
|
||||||
|
wrappedConn := &webSocketConn{Conn: conn, apiCaller: newAPICaller(c.bot)}
|
||||||
|
c.eventConn = wrappedConn
|
||||||
|
} else {
|
||||||
|
c.eventConn.Conn = conn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *websocketClient) connectUniversal() {
|
func (c *websocketClient) connectUniversal() {
|
||||||
@ -189,12 +194,16 @@ func (c *websocketClient) connectUniversal() {
|
|||||||
log.Warnf("反向WebSocket 握手时出现错误: %v", err)
|
log.Warnf("反向WebSocket 握手时出现错误: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wrappedConn := &webSocketConn{Conn: conn, apiCaller: newAPICaller(c.bot)}
|
if c.universalConn == nil {
|
||||||
if c.conf.RateLimit.Enabled {
|
wrappedConn := &webSocketConn{Conn: conn, apiCaller: newAPICaller(c.bot)}
|
||||||
wrappedConn.apiCaller.use(rateLimit(c.conf.RateLimit.Frequency, c.conf.RateLimit.Bucket))
|
if c.conf.RateLimit.Enabled {
|
||||||
|
wrappedConn.apiCaller.use(rateLimit(c.conf.RateLimit.Frequency, c.conf.RateLimit.Bucket))
|
||||||
|
}
|
||||||
|
c.universalConn = wrappedConn
|
||||||
|
} else {
|
||||||
|
c.universalConn.Conn = conn
|
||||||
}
|
}
|
||||||
go c.listenAPI(wrappedConn, true)
|
go c.listenAPI(c.universalConn, true)
|
||||||
c.universalConn = wrappedConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *websocketClient) listenAPI(conn *webSocketConn, u bool) {
|
func (c *websocketClient) listenAPI(conn *webSocketConn, u bool) {
|
||||||
|
Reference in New Issue
Block a user