mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-05-04 19:17:37 +08:00
Merge branch 'dev'
This commit is contained in:
commit
bf77951f8d
116
coolq/api.go
116
coolq/api.go
@ -403,7 +403,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 +414,23 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) MSG {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
var convert func(e gjson.Result) []*message.ForwardNode
|
var 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 +440,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 +450,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 +464,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 端输出")
|
||||||
@ -1408,6 +1418,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, m["time"].(int64))
|
||||||
|
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 +1468,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:
|
||||||
|
58
coolq/bot.go
58
coolq/bot.go
@ -81,9 +81,9 @@ var ForceFragmented = false
|
|||||||
// SkipMimeScan 是否跳过Mime扫描
|
// SkipMimeScan 是否跳过Mime扫描
|
||||||
var SkipMimeScan bool
|
var SkipMimeScan bool
|
||||||
|
|
||||||
var lawfulImageTypes = []string{"image/png", "image/jpeg", "image/gif", "image/bmp"}
|
var lawfulImageTypes = [...]string{"image/png", "image/jpeg", "image/gif", "image/bmp", "image/webp"}
|
||||||
|
|
||||||
var lawfulAudioTypes = []string{
|
var lawfulAudioTypes = [...]string{
|
||||||
"audio/mpeg", "audio/flac", "audio/midi", "audio/ogg",
|
"audio/mpeg", "audio/flac", "audio/midi", "audio/ogg",
|
||||||
"audio/ape", "audio/amr", "audio/wav", "audio/aiff",
|
"audio/ape", "audio/amr", "audio/wav", "audio/aiff",
|
||||||
"audio/mp4", "audio/aac", "audio/x-m4a",
|
"audio/mp4", "audio/aac", "audio/x-m4a",
|
||||||
@ -126,6 +126,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 +212,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
|
||||||
}
|
}
|
||||||
@ -428,7 +426,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 +514,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 +602,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()
|
||||||
}
|
}
|
||||||
|
132
coolq/cqcode.go
132
coolq/cqcode.go
@ -81,7 +81,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 +96,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,
|
||||||
@ -731,9 +735,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 +906,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 +1131,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 +1147,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 方式发送的群消息图片
|
||||||
|
@ -69,9 +69,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 +106,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 +262,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 +280,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)
|
||||||
|
@ -222,7 +222,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).
|
279
docs/cqhttp.md
279
docs/cqhttp.md
@ -7,6 +7,7 @@
|
|||||||
<p>
|
<p>
|
||||||
|
|
||||||
##### CQCode
|
##### CQCode
|
||||||
|
|
||||||
- [图片](#图片)
|
- [图片](#图片)
|
||||||
- [回复](#回复)
|
- [回复](#回复)
|
||||||
- [红包](#红包)
|
- [红包](#红包)
|
||||||
@ -21,6 +22,7 @@
|
|||||||
- [图片](#图片)
|
- [图片](#图片)
|
||||||
|
|
||||||
##### API
|
##### API
|
||||||
|
|
||||||
- [设置群名](#设置群名)
|
- [设置群名](#设置群名)
|
||||||
- [设置群头像](#设置群头像)
|
- [设置群头像](#设置群头像)
|
||||||
- [获取图片信息](#获取图片信息)
|
- [获取图片信息](#获取图片信息)
|
||||||
@ -47,6 +49,7 @@
|
|||||||
- [重载事件过滤器](#重载事件过滤器)
|
- [重载事件过滤器](#重载事件过滤器)
|
||||||
|
|
||||||
##### 事件
|
##### 事件
|
||||||
|
|
||||||
- [群消息撤回](#群消息撤回)
|
- [群消息撤回](#群消息撤回)
|
||||||
- [好友消息撤回](#好友消息撤回)
|
- [好友消息撤回](#好友消息撤回)
|
||||||
- [好友戳一戳](#好友戳一戳)
|
- [好友戳一戳](#好友戳一戳)
|
||||||
@ -112,8 +115,6 @@ 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 +123,11 @@ Type : `reply`
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "music",
|
"type": "music",
|
||||||
"data": {
|
"data": {
|
||||||
"type": "163",
|
"type": "163",
|
||||||
"id": "28949129"
|
"id": "28949129"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -143,13 +144,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 +232,9 @@ Type: `gift`
|
|||||||
| 12 | 我超忙的 |
|
| 12 | 我超忙的 |
|
||||||
| 13 | 爱心口罩 |
|
| 13 | 爱心口罩 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
示例: `[CQ:gift,qq=123456,id=8]`
|
示例: `[CQ:gift,qq=123456,id=8]`
|
||||||
|
|
||||||
### 合并转发
|
### 合并转发
|
||||||
|
|
||||||
Type: `forward`
|
Type: `forward`
|
||||||
|
|
||||||
@ -259,13 +258,14 @@ 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套娃**
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@ -273,18 +273,18 @@ Type: `node`
|
|||||||
|
|
||||||
````json
|
````json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"data": {
|
"data": {
|
||||||
"id": "123"
|
"id": "123"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"data": {
|
|
||||||
"id": "456"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"data": {
|
||||||
|
"id": "456"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
````
|
````
|
||||||
|
|
||||||
@ -292,27 +292,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 +322,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 +354,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 +379,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 +452,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 +490,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]
|
||||||
```
|
```
|
||||||
@ -505,7 +541,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`
|
||||||
|
|
||||||
@ -571,28 +608,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"
|
||||||
}
|
}
|
||||||
````
|
````
|
||||||
|
|
||||||
@ -708,7 +746,6 @@ Type: `tts`
|
|||||||
| `confidence` | int32 | 置信度 |
|
| `confidence` | int32 | 置信度 |
|
||||||
| `coordinates` | vector2 | 坐标 |
|
| `coordinates` | vector2 | 坐标 |
|
||||||
|
|
||||||
|
|
||||||
### 获取群系统消息
|
### 获取群系统消息
|
||||||
|
|
||||||
终结点: `/get_group_system_msg`
|
终结点: `/get_group_system_msg`
|
||||||
@ -720,9 +757,9 @@ Type: `tts`
|
|||||||
| `invited_requests` | InvitedRequest[] | 邀请消息列表 |
|
| `invited_requests` | InvitedRequest[] | 邀请消息列表 |
|
||||||
| `join_requests` | JoinRequest[] | 进群消息列表 |
|
| `join_requests` | JoinRequest[] | 进群消息列表 |
|
||||||
|
|
||||||
> 注意: 如果列表不存在任何消息, 将返回 `null`
|
> 注意: 如果列表不存在任何消息, 将返回 `null`
|
||||||
|
|
||||||
**InvitedRequest**
|
**InvitedRequest**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| -------------- | ------ | ----------------- |
|
| -------------- | ------ | ----------------- |
|
||||||
@ -734,7 +771,7 @@ Type: `tts`
|
|||||||
| `checked` | bool | 是否已被处理 |
|
| `checked` | bool | 是否已被处理 |
|
||||||
| `actor` | int64 | 处理者, 未处理为0 |
|
| `actor` | int64 | 处理者, 未处理为0 |
|
||||||
|
|
||||||
**JoinRequest**
|
**JoinRequest**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------------- | ------ | ----------------- |
|
| ---------------- | ------ | ----------------- |
|
||||||
@ -825,7 +862,7 @@ Type: `tts`
|
|||||||
| ----- | ------ | ------------ |
|
| ----- | ------ | ------------ |
|
||||||
| `url` | string | 文件下载链接 |
|
| `url` | string | 文件下载链接 |
|
||||||
|
|
||||||
**File**
|
**File**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ---------------- | ------ | ---------------------- |
|
| ---------------- | ------ | ---------------------- |
|
||||||
@ -840,7 +877,7 @@ Type: `tts`
|
|||||||
| `uploader` | int64 | 上传者ID |
|
| `uploader` | int64 | 上传者ID |
|
||||||
| `uploader_name` | string | 上传者名字 |
|
| `uploader_name` | string | 上传者名字 |
|
||||||
|
|
||||||
**Folder**
|
**Folder**
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------------ | ------ | ---------- |
|
| ------------------ | ------ | ---------- |
|
||||||
@ -885,7 +922,6 @@ Type: `tts`
|
|||||||
|
|
||||||
**Statistics**
|
**Statistics**
|
||||||
|
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| ------------------ | ------ | ---------------- |
|
| ------------------ | ------ | ---------------- |
|
||||||
| `packet_received` | uint64 | 收到的数据包总数 |
|
| `packet_received` | uint64 | 收到的数据包总数 |
|
||||||
@ -1076,13 +1112,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 +1226,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 | | 新头衔 |
|
||||||
|
|
||||||
### 接收到离线文件
|
### 接收到离线文件
|
||||||
|
|
||||||
**上报数据**
|
**上报数据**
|
||||||
|
42
docs/file.md
42
docs/file.md
@ -2,37 +2,39 @@
|
|||||||
|
|
||||||
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 | 图片下载链接 |
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# 滑块验证码
|
# 滑块验证码
|
||||||
|
|
||||||
|
> 该文档已过期, 最新版本下可直接使用手机扫描二维码通过验证.
|
||||||
|
|
||||||
由于TX最新的限制, 所有协议在陌生设备/IP登录时都有可能被要求通过滑块验证码, 否则将会出现 `当前上网环境异常` 的错误. 目前我们准备了两个临时方案应对该验证码.
|
由于TX最新的限制, 所有协议在陌生设备/IP登录时都有可能被要求通过滑块验证码, 否则将会出现 `当前上网环境异常` 的错误. 目前我们准备了两个临时方案应对该验证码.
|
||||||
|
|
||||||
> 如果您有一台运行Windows的PC/Server 并且不会抓包操作, 我们建议直接使用方案B
|
> 如果您有一台运行Windows的PC/Server 并且不会抓包操作, 我们建议直接使用方案B
|
||||||
|
@ -29,6 +29,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) || race || (!windows && !linux && !darwin) || (windows && arm)
|
||||||
// +build !arm,!arm64,!amd64,!386 race
|
// +build !arm,!arm64,!amd64,!386 race !windows,!linux,!darwin windows,arm
|
||||||
|
|
||||||
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
|
@ -174,6 +174,12 @@ func Get() *Config {
|
|||||||
global.SetAtDefault(&config.Account.ReLogin.Disabled, !global.EnsureBool(os.Getenv("GCQ_RELOGIN"), false), false)
|
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.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))
|
global.SetAtDefault(&config.Account.ReLogin.MaxTimes, uint(toInt64(os.Getenv("GCQ_RELOGIN_MAX_TIMES"))), uint(0))
|
||||||
|
dbConf := &LevelDBConfig{Enable: global.EnsureBool(os.Getenv("GCQ_LEVELDB"), true)}
|
||||||
|
config.Database["leveldb"] = func() yaml.Node {
|
||||||
|
n := &yaml.Node{}
|
||||||
|
_ = n.Encode(dbConf)
|
||||||
|
return *n
|
||||||
|
}()
|
||||||
accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN")
|
accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN")
|
||||||
if os.Getenv("GCQ_HTTP_PORT") != "" {
|
if os.Getenv("GCQ_HTTP_PORT") != "" {
|
||||||
node := &yaml.Node{}
|
node := &yaml.Node{}
|
||||||
|
@ -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 (
|
||||||
|
5
go.mod
5
go.mod
@ -5,9 +5,9 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||||
github.com/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-20210810070836-6614d2383adb
|
||||||
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.1
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
@ -21,7 +21,6 @@ require (
|
|||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/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
|
||||||
|
13
go.sum
13
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-20210810070836-6614d2383adb h1:agCxYd/ZemDwrEYKpnpKqA0cubxfpkQ/b+GpIlIJK0U=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210726103104-1d68826cef0e/go.mod h1:CPaznIPn415uQqxJgjyMHLqGLkvLS6R6+bkW3/fe08Q=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210810070836-6614d2383adb/go.mod h1:5V3f/+mTYtrI/+hLqbdzZQXuLMl2RyLfx0XYYjCQ90Q=
|
||||||
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=
|
||||||
@ -71,7 +71,6 @@ github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMW
|
|||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
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/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=
|
||||||
@ -104,14 +103,12 @@ 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/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 +116,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=
|
||||||
@ -150,7 +145,6 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
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-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 +152,11 @@ 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-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=
|
||||||
|
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()
|
||||||
|
10
main.go
10
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"
|
||||||
@ -79,12 +79,9 @@ 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),
|
||||||
}
|
}
|
||||||
@ -102,7 +99,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) {
|
||||||
@ -263,6 +260,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,6 +350,10 @@ 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,
|
||||||
@ -413,6 +417,7 @@ var API = map[string]func(*coolq.CQBot, resultGetter) coolq.MSG{
|
|||||||
"qidian_get_account_info": getQiDianAccountInfo,
|
"qidian_get_account_info": getQiDianAccountInfo,
|
||||||
"_get_model_show": getModelShow,
|
"_get_model_show": getModelShow,
|
||||||
"_set_model_show": setModelShow,
|
"_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 {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -34,20 +35,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 +65,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) {
|
||||||
@ -125,7 +127,11 @@ func RunLambdaClient(bot *coolq.CQBot, conf *config.LambdaServer) {
|
|||||||
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)
|
writer := lambdaResponseWriter{header: make(http.Header)}
|
||||||
|
server.ServeHTTP(&writer, req)
|
||||||
|
if err := writer.flush(); err != nil {
|
||||||
|
log.Warnf("Lambda 发送响应失败: %v", err)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user