mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-06-30 20:03:24 +00:00
Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
ff75b0a2a9 | |||
4a13f35003 | |||
8d0a0b7f4d | |||
4c570bdfe6 | |||
d4e8a3df4f | |||
307a09dd11 | |||
4445af6ff2 | |||
dbd0f85025 | |||
0bacbde4d7 | |||
c92dd39191 | |||
7da1918c0c | |||
89d466d3e1 | |||
5b57aeb1d1 | |||
08567ec240 | |||
08dea6dcf1 | |||
252293afb9 | |||
ca0b931fa4 | |||
01dca0305c | |||
6053918ee3 | |||
e7547ed257 | |||
96a036201d | |||
ccee8ac77a | |||
ae806c53bb | |||
a9619b25bd | |||
69cda4e029 | |||
dc18944a98 | |||
717f816294 | |||
430e734ff6 | |||
21531fd70a | |||
2214570c4f | |||
18a839b699 | |||
23b90e288c | |||
22c9a6728b | |||
81ff71ee3b | |||
bf65b576f3 | |||
817bb0e493 | |||
d2529a04c9 | |||
13df58331c | |||
61c9939f03 | |||
e19e797aac | |||
aa54ed5610 | |||
360f7188e1 |
106
coolq/api.go
106
coolq/api.go
@ -62,7 +62,22 @@ func (bot *CQBot) CQGetGroupList(noCache bool) MSG {
|
|||||||
func (bot *CQBot) CQGetGroupInfo(groupId int64, noCache bool) MSG {
|
func (bot *CQBot) CQGetGroupInfo(groupId int64, noCache bool) MSG {
|
||||||
group := bot.Client.FindGroup(groupId)
|
group := bot.Client.FindGroup(groupId)
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在")
|
gid := strconv.FormatInt(groupId, 10)
|
||||||
|
info, err := bot.Client.SearchGroupByKeyword(gid)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "GROUP_SEARCH_ERROR", "群聊搜索失败")
|
||||||
|
}
|
||||||
|
for _, g := range info {
|
||||||
|
if g.Code == groupId {
|
||||||
|
return OK(MSG{
|
||||||
|
"group_id": g.Code,
|
||||||
|
"group_name": g.Name,
|
||||||
|
"max_member_count": 0,
|
||||||
|
"member_count": 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在失败")
|
||||||
}
|
}
|
||||||
if noCache {
|
if noCache {
|
||||||
var err error
|
var err error
|
||||||
@ -166,6 +181,26 @@ func (bot *CQBot) CQGetGroupFileUrl(groupId int64, fileId string, busId int32) M
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bot *CQBot) CQUploadGroupFile(groupId int64, file, name, folder string) MSG {
|
||||||
|
if !global.PathExists(file) {
|
||||||
|
log.Errorf("上传群文件 %v 失败: 文件不存在", file)
|
||||||
|
return Failed(100, "FILE_NOT_FOUND", "文件不存在")
|
||||||
|
}
|
||||||
|
fs, err := bot.Client.GetGroupFileSystem(groupId)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取群 %v 文件系统信息失败: %v", groupId, err)
|
||||||
|
return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
if folder == "" {
|
||||||
|
folder = "/"
|
||||||
|
}
|
||||||
|
if err = fs.UploadFile(file, name, folder); err != nil {
|
||||||
|
log.Errorf("上传群 %v 文件 %v 失败: %v", groupId, file, err)
|
||||||
|
return Failed(100, "FILE_SYSTEM_UPLOAD_API_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (bot *CQBot) CQGetWordSlices(content string) MSG {
|
func (bot *CQBot) CQGetWordSlices(content string) MSG {
|
||||||
slices, err := bot.Client.GetWordSegmentation(content)
|
slices, err := bot.Client.GetWordSegmentation(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -867,6 +902,7 @@ func (bot *CQBot) CQGetGroupMessageHistory(groupId int64, seq int64) MSG {
|
|||||||
var ms []MSG
|
var ms []MSG
|
||||||
for _, m := range msg {
|
for _, m := range msg {
|
||||||
id := m.Id
|
id := m.Id
|
||||||
|
bot.checkMedia(m.Elements)
|
||||||
if bot.db != nil {
|
if bot.db != nil {
|
||||||
id = bot.InsertGroupMessage(m)
|
id = bot.InsertGroupMessage(m)
|
||||||
}
|
}
|
||||||
@ -972,6 +1008,74 @@ func (bot *CQBot) CQGetStatus() MSG {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQSetEssenceMessage 设置精华消息
|
||||||
|
func (bot *CQBot) CQSetEssenceMessage(messageID int32) MSG {
|
||||||
|
msg := bot.GetMessage(messageID)
|
||||||
|
if msg == nil {
|
||||||
|
return Failed(100, "MESSAGE_NOT_FOUND", "消息不存在")
|
||||||
|
}
|
||||||
|
if _, ok := msg["group"]; ok {
|
||||||
|
if err := bot.Client.SetEssenceMessage(msg["group"].(int64), msg["message-id"].(int32), msg["internal-id"].(int32)); err != nil {
|
||||||
|
log.Warnf("设置精华消息 %v 失败: %v", messageID, err)
|
||||||
|
return Failed(100, "SET_ESSENCE_MSG_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warnf("设置精华消息 %v 失败: 非群聊", messageID)
|
||||||
|
return Failed(100, "SET_ESSENCE_MSG_ERROR", "非群聊")
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQDeleteEssenceMessage 移出精华消息
|
||||||
|
func (bot *CQBot) CQDeleteEssenceMessage(messageID int32) MSG {
|
||||||
|
msg := bot.GetMessage(messageID)
|
||||||
|
if msg == nil {
|
||||||
|
return Failed(100, "MESSAGE_NOT_FOUND", "消息不存在")
|
||||||
|
}
|
||||||
|
if _, ok := msg["group"]; ok {
|
||||||
|
if err := bot.Client.DeleteEssenceMessage(msg["group"].(int64), msg["message-id"].(int32), msg["internal-id"].(int32)); err != nil {
|
||||||
|
log.Warnf("移出精华消息 %v 失败: %v", messageID, err)
|
||||||
|
return Failed(100, "DEL_ESSENCE_MSG_ERROR", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warnf("移出精华消息 %v 失败: 非群聊", messageID)
|
||||||
|
return Failed(100, "DEL_ESSENCE_MSG_ERROR", "非群聊")
|
||||||
|
}
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQGetEssenceMessageList 获取精华消息列表
|
||||||
|
func (bot *CQBot) CQGetEssenceMessageList(groupCode int64) MSG {
|
||||||
|
g := bot.Client.FindGroup(groupCode)
|
||||||
|
if g == nil {
|
||||||
|
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在")
|
||||||
|
}
|
||||||
|
msgList, err := bot.Client.GetGroupEssenceMsgList(groupCode)
|
||||||
|
if err != nil {
|
||||||
|
return Failed(100, "GET_ESSENCE_LIST_FOUND", err.Error())
|
||||||
|
}
|
||||||
|
list := make([]MSG, 0)
|
||||||
|
for _, m := range msgList {
|
||||||
|
var msg = MSG{
|
||||||
|
"sender_nick": m.SenderNick,
|
||||||
|
"sender_time": m.SenderTime,
|
||||||
|
"operator_time": m.AddDigestTime,
|
||||||
|
"operator_nick": m.AddDigestNick,
|
||||||
|
}
|
||||||
|
msg["sender_id"], _ = strconv.ParseUint(m.SenderUin, 10, 64)
|
||||||
|
msg["operator_id"], _ = strconv.ParseUint(m.AddDigestUin, 10, 64)
|
||||||
|
msg["message_id"] = ToGlobalId(groupCode, int32(m.MessageID))
|
||||||
|
list = append(list, msg)
|
||||||
|
}
|
||||||
|
return OK(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *CQBot) CQCheckUrlSafely(url string) MSG {
|
||||||
|
return OK(MSG{
|
||||||
|
"level": bot.Client.CheckUrlSafely(url),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (bot *CQBot) CQGetVersionInfo() MSG {
|
func (bot *CQBot) CQGetVersionInfo() MSG {
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
return OK(MSG{
|
return OK(MSG{
|
||||||
|
107
coolq/bot.go
107
coolq/bot.go
@ -78,6 +78,7 @@ func NewQQBot(cli *client.QQClient, conf *global.JSONConfig) *CQBot {
|
|||||||
bot.Client.OnGroupInvited(bot.groupInvitedEvent)
|
bot.Client.OnGroupInvited(bot.groupInvitedEvent)
|
||||||
bot.Client.OnUserWantJoinGroup(bot.groupJoinReqEvent)
|
bot.Client.OnUserWantJoinGroup(bot.groupJoinReqEvent)
|
||||||
bot.Client.OnOtherClientStatusChanged(bot.otherClientStatusChangedEvent)
|
bot.Client.OnOtherClientStatusChanged(bot.otherClientStatusChangedEvent)
|
||||||
|
bot.Client.OnGroupDigest(bot.groupEssenceMsg)
|
||||||
go func() {
|
go func() {
|
||||||
i := conf.HeartbeatInterval
|
i := conf.HeartbeatInterval
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
@ -201,61 +202,8 @@ func (bot *CQBot) SendGroupMessage(groupId int64, m *message.SendingMessage) int
|
|||||||
bot.Client.SendGroupGift(uint64(groupId), uint64(i.Target), i.GiftId)
|
bot.Client.SendGroupGift(uint64(groupId), uint64(i.Target), i.GiftId)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if i, ok := elem.(*QQMusicElement); ok {
|
if i, ok := elem.(*message.MusicShareElement); ok {
|
||||||
var msgStyle uint32 = 4
|
ret, err := bot.Client.SendGroupMusicShare(groupId, i)
|
||||||
if i.MusicUrl == "" {
|
|
||||||
msgStyle = 0 // fix vip song
|
|
||||||
}
|
|
||||||
ret, err := bot.Client.SendGroupRichMessage(groupId, 100497308, 1, msgStyle, client.RichClientInfo{
|
|
||||||
Platform: 1,
|
|
||||||
SdkVersion: "0.0.0",
|
|
||||||
PackageName: "com.tencent.qqmusic",
|
|
||||||
Signature: "cbd27cd7c861227d013a25b2d10f0799",
|
|
||||||
}, &message.RichMessage{
|
|
||||||
Title: i.Title,
|
|
||||||
Summary: i.Summary,
|
|
||||||
Url: i.Url,
|
|
||||||
PictureUrl: i.PictureUrl,
|
|
||||||
MusicUrl: i.MusicUrl,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("警告: 群 %v 富文本消息发送失败: %v", groupId, err)
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return bot.InsertGroupMessage(ret)
|
|
||||||
}
|
|
||||||
if i, ok := elem.(*CloudMusicElement); ok {
|
|
||||||
ret, err := bot.Client.SendGroupRichMessage(groupId, 100495085, 1, 4, client.RichClientInfo{
|
|
||||||
Platform: 1,
|
|
||||||
SdkVersion: "0.0.0",
|
|
||||||
PackageName: "com.netease.cloudmusic",
|
|
||||||
Signature: "da6b069da1e2982db3e386233f68d76d",
|
|
||||||
}, &message.RichMessage{
|
|
||||||
Title: i.Title,
|
|
||||||
Summary: i.Summary,
|
|
||||||
Url: i.Url,
|
|
||||||
PictureUrl: i.PictureUrl,
|
|
||||||
MusicUrl: i.MusicUrl,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("警告: 群 %v 富文本消息发送失败: %v", groupId, err)
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return bot.InsertGroupMessage(ret)
|
|
||||||
}
|
|
||||||
if i, ok := elem.(*MiguMusicElement); ok {
|
|
||||||
ret, err := bot.Client.SendGroupRichMessage(groupId, 1101053067, 1, 4, client.RichClientInfo{
|
|
||||||
Platform: 1,
|
|
||||||
SdkVersion: "0.0.0",
|
|
||||||
PackageName: "cmccwm.mobilemusic",
|
|
||||||
Signature: "6cdc72a439cef99a3418d2a78aa28c73",
|
|
||||||
}, &message.RichMessage{
|
|
||||||
Title: i.Title,
|
|
||||||
Summary: i.Summary,
|
|
||||||
Url: i.Url,
|
|
||||||
PictureUrl: i.PictureUrl,
|
|
||||||
MusicUrl: i.MusicUrl,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("警告: 群 %v 富文本消息发送失败: %v", groupId, err)
|
log.Warnf("警告: 群 %v 富文本消息发送失败: %v", groupId, err)
|
||||||
return -1
|
return -1
|
||||||
@ -312,53 +260,8 @@ func (bot *CQBot) SendPrivateMessage(target int64, m *message.SendingMessage) in
|
|||||||
newElem = append(newElem, gv)
|
newElem = append(newElem, gv)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i, ok := elem.(*QQMusicElement); ok {
|
if i, ok := elem.(*message.MusicShareElement); ok {
|
||||||
var msgStyle uint32 = 4
|
bot.Client.SendFriendMusicShare(target, i)
|
||||||
if i.MusicUrl == "" {
|
|
||||||
msgStyle = 0 // fix vip song
|
|
||||||
}
|
|
||||||
bot.Client.SendFriendRichMessage(target, 100497308, 1, msgStyle, client.RichClientInfo{
|
|
||||||
Platform: 1,
|
|
||||||
SdkVersion: "0.0.0",
|
|
||||||
PackageName: "com.tencent.qqmusic",
|
|
||||||
Signature: "cbd27cd7c861227d013a25b2d10f0799",
|
|
||||||
}, &message.RichMessage{
|
|
||||||
Title: i.Title,
|
|
||||||
Summary: i.Summary,
|
|
||||||
Url: i.Url,
|
|
||||||
PictureUrl: i.PictureUrl,
|
|
||||||
MusicUrl: i.MusicUrl,
|
|
||||||
})
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if i, ok := elem.(*CloudMusicElement); ok {
|
|
||||||
bot.Client.SendFriendRichMessage(target, 100495085, 1, 4, client.RichClientInfo{
|
|
||||||
Platform: 1,
|
|
||||||
SdkVersion: "0.0.0",
|
|
||||||
PackageName: "com.netease.cloudmusic",
|
|
||||||
Signature: "da6b069da1e2982db3e386233f68d76d",
|
|
||||||
}, &message.RichMessage{
|
|
||||||
Title: i.Title,
|
|
||||||
Summary: i.Summary,
|
|
||||||
Url: i.Url,
|
|
||||||
PictureUrl: i.PictureUrl,
|
|
||||||
MusicUrl: i.MusicUrl,
|
|
||||||
})
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if i, ok := elem.(*MiguMusicElement); ok {
|
|
||||||
bot.Client.SendFriendRichMessage(target, 1101053067, 1, 4, client.RichClientInfo{
|
|
||||||
Platform: 1,
|
|
||||||
SdkVersion: "0.0.0",
|
|
||||||
PackageName: "cmccwm.mobilemusic",
|
|
||||||
Signature: "6cdc72a439cef99a3418d2a78aa28c73",
|
|
||||||
}, &message.RichMessage{
|
|
||||||
Title: i.Title,
|
|
||||||
Summary: i.Summary,
|
|
||||||
Url: i.Url,
|
|
||||||
PictureUrl: i.PictureUrl,
|
|
||||||
MusicUrl: i.MusicUrl,
|
|
||||||
})
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
newElem = append(newElem, elem)
|
newElem = append(newElem, elem)
|
||||||
|
@ -49,26 +49,6 @@ type GiftElement struct {
|
|||||||
GiftId message.GroupGift
|
GiftId message.GroupGift
|
||||||
}
|
}
|
||||||
|
|
||||||
type MusicElement struct {
|
|
||||||
Title string
|
|
||||||
Summary string
|
|
||||||
Url string
|
|
||||||
PictureUrl string
|
|
||||||
MusicUrl string
|
|
||||||
}
|
|
||||||
|
|
||||||
type QQMusicElement struct {
|
|
||||||
MusicElement
|
|
||||||
}
|
|
||||||
|
|
||||||
type CloudMusicElement struct {
|
|
||||||
MusicElement
|
|
||||||
}
|
|
||||||
|
|
||||||
type MiguMusicElement struct {
|
|
||||||
MusicElement
|
|
||||||
}
|
|
||||||
|
|
||||||
type LocalImageElement struct {
|
type LocalImageElement struct {
|
||||||
message.ImageElement
|
message.ImageElement
|
||||||
Stream io.ReadSeeker
|
Stream io.ReadSeeker
|
||||||
@ -90,10 +70,6 @@ func (e *GiftElement) Type() message.ElementType {
|
|||||||
return message.At
|
return message.At
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *MusicElement) Type() message.ElementType {
|
|
||||||
return message.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
var GiftId = [...]message.GroupGift{
|
var GiftId = [...]message.GroupGift{
|
||||||
message.SweetWink,
|
message.SweetWink,
|
||||||
message.HappyCola,
|
message.HappyCola,
|
||||||
@ -699,7 +675,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (m interf
|
|||||||
return nil, errors.New("song not found")
|
return nil, errors.New("song not found")
|
||||||
}
|
}
|
||||||
aid := strconv.FormatInt(info.Get("track_info.album.id").Int(), 10)
|
aid := strconv.FormatInt(info.Get("track_info.album.id").Int(), 10)
|
||||||
name := info.Get("track_info.name").Str + " - " + info.Get("track_info.singer.0.name").Str
|
name := info.Get("track_info.name").Str
|
||||||
mid := info.Get("track_info.mid").Str
|
mid := info.Get("track_info.mid").Str
|
||||||
albumMid := info.Get("track_info.album.mid").Str
|
albumMid := info.Get("track_info.album.mid").Str
|
||||||
pinfo, _ := global.GetBytes("http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576")
|
pinfo, _ := global.GetBytes("http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576")
|
||||||
@ -709,17 +685,18 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (m interf
|
|||||||
if len(aid) < 2 {
|
if len(aid) < 2 {
|
||||||
return nil, errors.New("song error")
|
return nil, errors.New("song error")
|
||||||
}
|
}
|
||||||
content := "来自go-cqhttp"
|
content := info.Get("track_info.singer.0.name").Str
|
||||||
if d["content"] != "" {
|
if d["content"] != "" {
|
||||||
content = d["content"]
|
content = d["content"]
|
||||||
}
|
}
|
||||||
return &QQMusicElement{MusicElement: MusicElement{
|
return &message.MusicShareElement{
|
||||||
|
MusicType: message.QQMusic,
|
||||||
Title: name,
|
Title: name,
|
||||||
Summary: content,
|
Summary: content,
|
||||||
Url: jumpUrl,
|
Url: jumpUrl,
|
||||||
PictureUrl: preview,
|
PictureUrl: preview,
|
||||||
MusicUrl: purl,
|
MusicUrl: purl,
|
||||||
}}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if d["type"] == "163" {
|
if d["type"] == "163" {
|
||||||
info, err := global.NeteaseMusicSongInfo(d["id"])
|
info, err := global.NeteaseMusicSongInfo(d["id"])
|
||||||
@ -737,41 +714,36 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (m interf
|
|||||||
if info.Get("artists.0").Exists() {
|
if info.Get("artists.0").Exists() {
|
||||||
artistName = info.Get("artists.0.name").Str
|
artistName = info.Get("artists.0.name").Str
|
||||||
}
|
}
|
||||||
return &CloudMusicElement{MusicElement{
|
return &message.MusicShareElement{
|
||||||
|
MusicType: message.CloudMusic,
|
||||||
Title: name,
|
Title: name,
|
||||||
Summary: artistName,
|
Summary: artistName,
|
||||||
Url: jumpUrl,
|
Url: jumpUrl,
|
||||||
PictureUrl: picUrl,
|
PictureUrl: picUrl,
|
||||||
MusicUrl: musicUrl,
|
MusicUrl: musicUrl,
|
||||||
}}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if d["type"] == "custom" {
|
if d["type"] == "custom" {
|
||||||
if d["subtype"] == "qq" {
|
if d["subtype"] != "" {
|
||||||
return &QQMusicElement{MusicElement{
|
var subtype = map[string]int{
|
||||||
Title: d["title"],
|
"qq": message.QQMusic,
|
||||||
Summary: d["content"],
|
"163": message.CloudMusic,
|
||||||
Url: d["url"],
|
"migu": message.MiguMusic,
|
||||||
PictureUrl: d["image"],
|
"kugou": message.KugouMusic,
|
||||||
MusicUrl: d["purl"],
|
"kuwo": message.KuwoMusic,
|
||||||
}}, nil
|
|
||||||
}
|
}
|
||||||
if d["subtype"] == "163" {
|
var musicType = 0
|
||||||
return &CloudMusicElement{MusicElement{
|
if tp, ok := subtype[d["subtype"]]; ok {
|
||||||
Title: d["title"],
|
musicType = tp
|
||||||
Summary: d["content"],
|
|
||||||
Url: d["url"],
|
|
||||||
PictureUrl: d["image"],
|
|
||||||
MusicUrl: d["purl"],
|
|
||||||
}}, nil
|
|
||||||
}
|
}
|
||||||
if d["subtype"] == "migu" {
|
return &message.MusicShareElement{
|
||||||
return &MiguMusicElement{MusicElement{
|
MusicType: musicType,
|
||||||
Title: d["title"],
|
Title: d["title"],
|
||||||
Summary: d["content"],
|
Summary: d["content"],
|
||||||
Url: d["url"],
|
Url: d["url"],
|
||||||
PictureUrl: d["image"],
|
PictureUrl: d["image"],
|
||||||
MusicUrl: d["purl"],
|
MusicUrl: d["purl"],
|
||||||
}}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
xml := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="2" templateID="1" action="web" brief="[分享] %s" sourceMsgId="0" url="%s" flag="0" adverSign="0" multiMsgFlag="0"><item layout="2"><audio cover="%s" src="%s"/><title>%s</title><summary>%s</summary></item><source name="音乐" 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 := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="2" templateID="1" action="web" brief="[分享] %s" sourceMsgId="0" url="%s" flag="0" adverSign="0" multiMsgFlag="0"><item layout="2"><audio cover="%s" src="%s"/><title>%s</title><summary>%s</summary></item><source name="音乐" 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>`,
|
||||||
XmlEscape(d["title"]), d["url"], d["image"], d["audio"], XmlEscape(d["title"]), XmlEscape(d["content"]))
|
XmlEscape(d["title"]), d["url"], d["image"], d["audio"], XmlEscape(d["title"]), XmlEscape(d["content"]))
|
||||||
|
@ -431,7 +431,47 @@ func (bot *CQBot) otherClientStatusChangedEvent(c *client.QQClient, e *client.Ot
|
|||||||
"self_id": c.Uin,
|
"self_id": c.Uin,
|
||||||
"time": time.Now().Unix(),
|
"time": time.Now().Unix(),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *CQBot) groupEssenceMsg(c *client.QQClient, e *client.GroupDigestEvent) {
|
||||||
|
g := c.FindGroup(e.GroupCode)
|
||||||
|
gid := ToGlobalId(e.GroupCode, e.MessageID)
|
||||||
|
if e.OperationType == 1 {
|
||||||
|
log.Infof(
|
||||||
|
"群 %v 内 %v 将 %v 的消息(%v)设为了精华消息.",
|
||||||
|
formatGroupName(g),
|
||||||
|
formatMemberName(g.FindMember(e.OperatorUin)),
|
||||||
|
formatMemberName(g.FindMember(e.SenderUin)),
|
||||||
|
gid,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
log.Infof(
|
||||||
|
"群 %v 内 %v 将 %v 的消息(%v)移出了精华消息.",
|
||||||
|
formatGroupName(g),
|
||||||
|
formatMemberName(g.FindMember(e.OperatorUin)),
|
||||||
|
formatMemberName(g.FindMember(e.SenderUin)),
|
||||||
|
gid,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if e.OperatorUin == bot.Client.Uin {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bot.dispatchEventMessage(MSG{
|
||||||
|
"post_type": "notice",
|
||||||
|
"group_id": e.GroupCode,
|
||||||
|
"notice_type": "essence",
|
||||||
|
"sub_type": func() string {
|
||||||
|
if e.OperationType == 1 {
|
||||||
|
return "add"
|
||||||
|
}
|
||||||
|
return "delete"
|
||||||
|
}(),
|
||||||
|
"self_id": c.Uin,
|
||||||
|
"sender_id": e.SenderUin,
|
||||||
|
"operator_id": e.OperatorUin,
|
||||||
|
"time": time.Now().Unix(),
|
||||||
|
"message_id": gid,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) groupIncrease(groupCode, operatorUin, userUin int64) MSG {
|
func (bot *CQBot) groupIncrease(groupCode, operatorUin, userUin int64) MSG {
|
||||||
|
151
docs/cqhttp.md
151
docs/cqhttp.md
@ -39,6 +39,9 @@
|
|||||||
- [获取群子目录文件列表](#设置群名)
|
- [获取群子目录文件列表](#设置群名)
|
||||||
- [获取用户VIP信息](#获取用户VIP信息)
|
- [获取用户VIP信息](#获取用户VIP信息)
|
||||||
- [发送群公告](#发送群公告)
|
- [发送群公告](#发送群公告)
|
||||||
|
- [设置精华消息](#设置精华消息)
|
||||||
|
- [移出精华消息](#移出精华消息)
|
||||||
|
- [获取精华消息列表](#获取精华消息列表)
|
||||||
- [重载事件过滤器](#重载事件过滤器)
|
- [重载事件过滤器](#重载事件过滤器)
|
||||||
|
|
||||||
##### 事件
|
##### 事件
|
||||||
@ -50,6 +53,7 @@
|
|||||||
- [群成员荣誉变更提示](#群成员荣誉变更提示)
|
- [群成员荣誉变更提示](#群成员荣誉变更提示)
|
||||||
- [群成员名片更新](#群成员名片更新)
|
- [群成员名片更新](#群成员名片更新)
|
||||||
- [接收到离线文件](#接收到离线文件)
|
- [接收到离线文件](#接收到离线文件)
|
||||||
|
- [群精华消息](#精华消息)
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
</details>
|
</details>
|
||||||
@ -111,6 +115,54 @@ Type : `reply`
|
|||||||
\
|
\
|
||||||
自定义回复示例: `[CQ:reply,text=Hello World,qq=10086,time=3376656000]`
|
自定义回复示例: `[CQ:reply,text=Hello World,qq=10086,time=3376656000]`
|
||||||
|
|
||||||
|
### 音乐分享 <Badge text="发"/>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "music",
|
||||||
|
"data": {
|
||||||
|
"type": "163",
|
||||||
|
"id": "28949129"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
[CQ:music,type=163,id=28949129]
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `type` | | ✓ | `qq` `163` | 分别表示使用 QQ 音乐、网易云音乐 |
|
||||||
|
| `id` | | ✓ | - | 歌曲 ID |
|
||||||
|
|
||||||
|
### 音乐自定义分享 <Badge text="发"/>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "music",
|
||||||
|
"data": {
|
||||||
|
"type": "custom",
|
||||||
|
"url": "http://baidu.com",
|
||||||
|
"audio": "http://baidu.com/1.mp3",
|
||||||
|
"title": "音乐标题"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
[CQ:music,type=custom,url=http://baidu.com,audio=http://baidu.com/1.mp3,title=音乐标题]
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `type` | | ✓ | `custom` | 表示音乐自定义分享 |
|
||||||
|
| `subtype` | | ✓ | `qq,163,migu,kugou,kuwo` | 表示分享类型,不填写发送为xml卡片,推荐填写提高稳定性 |
|
||||||
|
| `url` | | ✓ | - | 点击后跳转目标 URL |
|
||||||
|
| `audio` | | ✓ | - | 音乐 URL |
|
||||||
|
| `title` | | ✓ | - | 标题 |
|
||||||
|
| `content` | | ✓ | - | 内容描述 |
|
||||||
|
| `image` | | ✓ | - | 图片 URL |
|
||||||
|
|
||||||
### 红包
|
### 红包
|
||||||
|
|
||||||
@ -572,11 +624,63 @@ Type: `tts`
|
|||||||
| -------- | -------- | ---- |
|
| -------- | -------- | ---- |
|
||||||
| `slices` | string[] | 词组 |
|
| `slices` | string[] | 词组 |
|
||||||
|
|
||||||
|
### 设置精华消息
|
||||||
|
|
||||||
|
终结点: `/set_essence_msg`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --------- | ------ | ---- |
|
||||||
|
| `message_id` | int32 | 消息ID |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
无
|
||||||
|
|
||||||
|
### 移出精华消息
|
||||||
|
|
||||||
|
终结点: `/delete_essence_msg`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --------- | ------ | ---- |
|
||||||
|
| `message_id` | int32 | 消息ID |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
无
|
||||||
|
|
||||||
|
### 获取精华消息列表
|
||||||
|
|
||||||
|
终结点: `/get_essence_msg_list`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --------- | ------ | ---- |
|
||||||
|
| `group_id` | int64 | 群号 |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
响应内容为 JSON 数组,每个元素如下:
|
||||||
|
|
||||||
|
| 字段名 | 数据类型 | 说明 |
|
||||||
|
| ----- | ------- | --- |
|
||||||
|
| `sender_id` |int64 | 发送者QQ 号 |
|
||||||
|
| `sender_nick` | string | 发送者昵称 |
|
||||||
|
| `sender_time` | int64 | 消息发送时间 |
|
||||||
|
| `operator_id` |int64 | 发送者QQ 号 |
|
||||||
|
| `operator_nick` | string | 发送者昵称 |
|
||||||
|
| `operator_time` | int64 | 消息发送时间|
|
||||||
|
| `message_id` | int32 | 消息ID |
|
||||||
|
|
||||||
### 图片OCR
|
### 图片OCR
|
||||||
|
|
||||||
> 注意: 目前图片OCR接口仅支持接受的图片
|
> 注意: 目前图片OCR接口仅支持接受的图片
|
||||||
|
|
||||||
终结点: `/.ocr_image`
|
终结点: `/ocr_image`
|
||||||
|
|
||||||
**参数**
|
**参数**
|
||||||
|
|
||||||
@ -742,6 +846,22 @@ Type: `tts`
|
|||||||
| `creator_name` | string | 创建者名字 |
|
| `creator_name` | string | 创建者名字 |
|
||||||
| `total_file_count` | int32 | 子文件数量 |
|
| `total_file_count` | int32 | 子文件数量 |
|
||||||
|
|
||||||
|
### 上传群文件
|
||||||
|
|
||||||
|
终结点: `/upload_group_file`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| ---------- | ------ | ------------------------- |
|
||||||
|
| `group_id` | int64 | 群号 |
|
||||||
|
| `file` | string | 本地文件路径 |
|
||||||
|
| `name` | string | 储存名称 |
|
||||||
|
| `folder` | string | 父目录ID |
|
||||||
|
|
||||||
|
> 在不提供 `folder` 参数的情况下默认上传到根目录
|
||||||
|
> 只能上传本地文件, 需要上传 `http` 文件的话请先调用 `download_file` API下载
|
||||||
|
|
||||||
### 获取状态
|
### 获取状态
|
||||||
|
|
||||||
终结点: `/get_status`
|
终结点: `/get_status`
|
||||||
@ -874,6 +994,22 @@ JSON数组:
|
|||||||
| `device_name` | string | 设备名称 |
|
| `device_name` | string | 设备名称 |
|
||||||
| `device_kind` | string | 设备类型 |
|
| `device_kind` | string | 设备类型 |
|
||||||
|
|
||||||
|
### 检查链接安全性
|
||||||
|
|
||||||
|
终结点:`/check_url_safely`
|
||||||
|
|
||||||
|
**参数**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| ---------- | ------ | ------------------------- |
|
||||||
|
| `url` | string | 需要检查的链接 |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| ---------- | ---------- | ------------ |
|
||||||
|
| `level` | int | 安全等级, 1: 安全 2: 未知 3: 危险 |
|
||||||
|
|
||||||
### 获取用户VIP信息
|
### 获取用户VIP信息
|
||||||
|
|
||||||
终结点:`/_get_vip_info`
|
终结点:`/_get_vip_info`
|
||||||
@ -1048,3 +1184,16 @@ JSON数组:
|
|||||||
| `notice_type` | string | `client_status` | 消息类型 |
|
| `notice_type` | string | `client_status` | 消息类型 |
|
||||||
| `client` | Device | | 客户端信息 |
|
| `client` | Device | | 客户端信息 |
|
||||||
| `online` | bool | | 当前是否在线 |
|
| `online` | bool | | 当前是否在线 |
|
||||||
|
|
||||||
|
### 精华消息
|
||||||
|
|
||||||
|
**上报数据**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 可能的值 | 说明 |
|
||||||
|
| ------------- | ------ | -------------- | -------- |
|
||||||
|
| `post_type` | string | `notice` | 上报类型 |
|
||||||
|
| `notice_type` | string | `essence` | 消息类型 |
|
||||||
|
| `sub_type` | string | `add`,`delete` | 添加为`add`,移出为`delete` |
|
||||||
|
| `sender_id` | int64 | | 消息发送者ID |
|
||||||
|
| `operator_id` | int64 | | 操作者ID |
|
||||||
|
| `message_id` | int32 | | 消息ID |
|
||||||
|
@ -2,13 +2,13 @@ package global
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/global/codec"
|
"github.com/Mrs4s/go-cqhttp/global/codec"
|
||||||
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ func EncoderSilk(data []byte) ([]byte, error) {
|
|||||||
h := md5.New()
|
h := md5.New()
|
||||||
_, err := h.Write(data)
|
_, err := h.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "calc md5 failed")
|
||||||
}
|
}
|
||||||
tempName := fmt.Sprintf("%x", h.Sum(nil))
|
tempName := fmt.Sprintf("%x", h.Sum(nil))
|
||||||
if silkPath := path.Join("data/cache", tempName+".silk"); PathExists(silkPath) {
|
if silkPath := path.Join("data/cache", tempName+".silk"); PathExists(silkPath) {
|
||||||
@ -40,7 +40,7 @@ func EncoderSilk(data []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
slk, err := codec.EncodeToSilk(data, tempName, true)
|
slk, err := codec.EncodeToSilk(data, tempName, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "encode silk failed")
|
||||||
}
|
}
|
||||||
return slk, nil
|
return slk, nil
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ func EncodeMP4(src string, dst string) error { // -y 覆盖文件
|
|||||||
err := cmd1.Run()
|
err := cmd1.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd2 := exec.Command("ffmpeg", "-i", src, "-y", "-c:v", "h264", "-c:a", "mp3", dst)
|
cmd2 := exec.Command("ffmpeg", "-i", src, "-y", "-c:v", "h264", "-c:a", "mp3", dst)
|
||||||
return cmd2.Run()
|
return errors.Wrap(cmd2.Run(), "convert mp4 failed")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -59,5 +59,5 @@ func EncodeMP4(src string, dst string) error { // -y 覆盖文件
|
|||||||
//ExtractCover 获取给定视频文件的Cover
|
//ExtractCover 获取给定视频文件的Cover
|
||||||
func ExtractCover(src string, target string) error {
|
func ExtractCover(src string, target string) error {
|
||||||
cmd := exec.Command("ffmpeg", "-i", src, "-y", "-r", "1", "-f", "image2", target)
|
cmd := exec.Command("ffmpeg", "-i", src, "-y", "-r", "1", "-f", "image2", target)
|
||||||
return cmd.Run()
|
return errors.Wrap(cmd.Run(), "extract video cover failed")
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
package codec
|
package codec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"github.com/pkg/errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -63,7 +63,7 @@ func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error)
|
|||||||
rawPath := path.Join(silkCachePath, tempName+".wav")
|
rawPath := path.Join(silkCachePath, tempName+".wav")
|
||||||
err := ioutil.WriteFile(rawPath, record, os.ModePerm)
|
err := ioutil.WriteFile(rawPath, record, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "write temp file error")
|
||||||
}
|
}
|
||||||
defer os.Remove(rawPath)
|
defer os.Remove(rawPath)
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error)
|
|||||||
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 err = cmd.Run(); err != nil {
|
if err = cmd.Run(); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "convert pcm file error")
|
||||||
}
|
}
|
||||||
defer os.Remove(pcmPath)
|
defer os.Remove(pcmPath)
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error)
|
|||||||
silkPath := path.Join(silkCachePath, tempName+".silk")
|
silkPath := path.Join(silkCachePath, tempName+".silk")
|
||||||
cmd = exec.Command(getEncoderFilePath(), pcmPath, silkPath, "-rate", "24000", "-quiet", "-tencent")
|
cmd = exec.Command(getEncoderFilePath(), pcmPath, silkPath, "-rate", "24000", "-quiet", "-tencent")
|
||||||
if err = cmd.Run(); err != nil {
|
if err = cmd.Run(); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "convert silk file error")
|
||||||
}
|
}
|
||||||
if !useCache {
|
if !useCache {
|
||||||
defer os.Remove(silkPath)
|
defer os.Remove(silkPath)
|
||||||
|
@ -119,8 +119,8 @@ var DefaultConfigWithComments = `
|
|||||||
use_sso_address: false
|
use_sso_address: false
|
||||||
// 是否启用 DEBUG
|
// 是否启用 DEBUG
|
||||||
debug: false
|
debug: false
|
||||||
// 日志等级
|
// 日志等级 trace,debug,info,warn,error
|
||||||
log_level: ""
|
log_level: "info"
|
||||||
// WebUi 设置
|
// WebUi 设置
|
||||||
web_ui: {
|
web_ui: {
|
||||||
// 是否启用 WebUi
|
// 是否启用 WebUi
|
||||||
@ -135,6 +135,9 @@ var DefaultConfigWithComments = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
//PasswordHash 存储QQ密码哈希供登录使用
|
||||||
|
var PasswordHash [16]byte
|
||||||
|
|
||||||
//JSONConfig Config对应的结构体
|
//JSONConfig Config对应的结构体
|
||||||
type JSONConfig struct {
|
type JSONConfig struct {
|
||||||
Uin int64 `json:"uin"`
|
Uin int64 `json:"uin"`
|
||||||
@ -274,8 +277,8 @@ func DefaultConfig() *JSONConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load 加载配置文件
|
//LoadConfig 加载配置文件
|
||||||
func Load(p string) *JSONConfig {
|
func LoadConfig(p string) *JSONConfig {
|
||||||
if !PathExists(p) {
|
if !PathExists(p) {
|
||||||
log.Warnf("尝试加载配置文件 %v 失败: 文件不存在", p)
|
log.Warnf("尝试加载配置文件 %v 失败: 文件不存在", p)
|
||||||
return nil
|
return nil
|
||||||
|
@ -25,12 +25,18 @@ func (m MSG) Get(s string) MSG {
|
|||||||
}
|
}
|
||||||
return MSG{"__str__": v} // 用这个名字应该没问题吧
|
return MSG{"__str__": v} // 用这个名字应该没问题吧
|
||||||
}
|
}
|
||||||
return MSG{}
|
return nil // 不存在为空
|
||||||
}
|
}
|
||||||
|
|
||||||
//String 将消息Map转化为String。若Map存在key "__str__",则返回此key对应的值,否则将输出整张消息Map对应的JSON字符串
|
//String 将消息Map转化为String。若Map存在key "__str__",则返回此key对应的值,否则将输出整张消息Map对应的JSON字符串
|
||||||
func (m MSG) String() string {
|
func (m MSG) String() string {
|
||||||
|
if m == nil {
|
||||||
|
return "" // 空 JSON
|
||||||
|
}
|
||||||
if str, ok := m["__str__"]; ok {
|
if str, ok := m["__str__"]; ok {
|
||||||
|
if str == nil {
|
||||||
|
return "" // 空 JSON
|
||||||
|
}
|
||||||
return fmt.Sprint(str)
|
return fmt.Sprint(str)
|
||||||
}
|
}
|
||||||
str, _ := json.MarshalToString(m)
|
str, _ := json.MarshalToString(m)
|
||||||
|
153
global/log_hook.go
Normal file
153
global/log_hook.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocalHook struct {
|
||||||
|
lock *sync.Mutex
|
||||||
|
levels []logrus.Level // hook级别
|
||||||
|
formatter logrus.Formatter // 格式
|
||||||
|
path string // 写入path
|
||||||
|
writer io.Writer // io
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref: logrus/hooks.go. impl Hook interface
|
||||||
|
func (hook *LocalHook) Levels() []logrus.Level {
|
||||||
|
if len(hook.levels) == 0 {
|
||||||
|
return logrus.AllLevels
|
||||||
|
}
|
||||||
|
return hook.levels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *LocalHook) ioWrite(entry *logrus.Entry) error {
|
||||||
|
log, err := hook.formatter.Format(entry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = hook.writer.Write(log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *LocalHook) pathWrite(entry *logrus.Entry) error {
|
||||||
|
dir := filepath.Dir(hook.path)
|
||||||
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := os.OpenFile(hook.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
log, err := hook.formatter.Format(entry)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fd.Write(log)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *LocalHook) Fire(entry *logrus.Entry) error {
|
||||||
|
hook.lock.Lock()
|
||||||
|
defer hook.lock.Unlock()
|
||||||
|
|
||||||
|
if hook.writer != nil {
|
||||||
|
return hook.ioWrite(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hook.path != "" {
|
||||||
|
return hook.pathWrite(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *LocalHook) SetFormatter(formatter logrus.Formatter) {
|
||||||
|
hook.lock.Lock()
|
||||||
|
defer hook.lock.Unlock()
|
||||||
|
|
||||||
|
if formatter == nil {
|
||||||
|
// 用默认的
|
||||||
|
formatter = &logrus.TextFormatter{DisableColors: true}
|
||||||
|
} else {
|
||||||
|
switch f := formatter.(type) {
|
||||||
|
case *logrus.TextFormatter:
|
||||||
|
textFormatter := f
|
||||||
|
textFormatter.DisableColors = true
|
||||||
|
default:
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.SetFormatter(formatter)
|
||||||
|
hook.formatter = formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *LocalHook) SetWriter(writer io.Writer) {
|
||||||
|
hook.lock.Lock()
|
||||||
|
defer hook.lock.Unlock()
|
||||||
|
hook.writer = writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *LocalHook) SetPath(path string) {
|
||||||
|
hook.lock.Lock()
|
||||||
|
defer hook.lock.Unlock()
|
||||||
|
hook.path = path
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalHook(args interface{}, formatter logrus.Formatter, levels ...logrus.Level) *LocalHook {
|
||||||
|
hook := &LocalHook{
|
||||||
|
lock: new(sync.Mutex),
|
||||||
|
}
|
||||||
|
hook.SetFormatter(formatter)
|
||||||
|
hook.levels = append(hook.levels, levels...)
|
||||||
|
|
||||||
|
switch arg := args.(type) {
|
||||||
|
case string:
|
||||||
|
hook.SetPath(arg)
|
||||||
|
case io.Writer:
|
||||||
|
hook.SetWriter(arg)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported type: %v", reflect.TypeOf(args)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return hook
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogLevel(level string) []logrus.Level {
|
||||||
|
switch level {
|
||||||
|
case "trace":
|
||||||
|
return []logrus.Level{logrus.TraceLevel, logrus.DebugLevel,
|
||||||
|
logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel,
|
||||||
|
logrus.FatalLevel, logrus.PanicLevel}
|
||||||
|
case "debug":
|
||||||
|
return []logrus.Level{logrus.DebugLevel, logrus.InfoLevel,
|
||||||
|
logrus.WarnLevel, logrus.ErrorLevel,
|
||||||
|
logrus.FatalLevel, logrus.PanicLevel}
|
||||||
|
case "info":
|
||||||
|
return []logrus.Level{logrus.InfoLevel, logrus.WarnLevel,
|
||||||
|
logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}
|
||||||
|
case "warn":
|
||||||
|
return []logrus.Level{logrus.WarnLevel, logrus.ErrorLevel,
|
||||||
|
logrus.FatalLevel, logrus.PanicLevel}
|
||||||
|
case "error":
|
||||||
|
return []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel,
|
||||||
|
logrus.PanicLevel}
|
||||||
|
default:
|
||||||
|
return []logrus.Level{logrus.InfoLevel, logrus.WarnLevel,
|
||||||
|
logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}
|
||||||
|
}
|
||||||
|
}
|
5
go.mod
5
go.mod
@ -3,24 +3,25 @@ module github.com/Mrs4s/go-cqhttp
|
|||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210124065645-9549a32d954a
|
github.com/Mrs4s/MiraiGo v0.0.0-20210206134348-800bf525ed0e
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/gin-contrib/pprof v1.3.0
|
github.com/gin-contrib/pprof v1.3.0
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-gonic/gin v1.6.3
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/guonaihong/gout v0.1.4
|
github.com/guonaihong/gout v0.1.4
|
||||||
github.com/hjson/hjson-go v3.1.0+incompatible
|
github.com/hjson/hjson-go v3.1.0+incompatible
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.10
|
github.com/json-iterator/go v1.1.10
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
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/lestrrat-go/strftime v1.0.4 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
||||||
github.com/tidwall/gjson v1.6.7
|
github.com/tidwall/gjson v1.6.7
|
||||||
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189
|
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
)
|
)
|
||||||
|
8
go.sum
8
go.sum
@ -1,7 +1,7 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210124065645-9549a32d954a h1:ov5QCDpZpsr+geKLVON7E63UqrpNF0oTiKuZwbKwEmo=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210206134348-800bf525ed0e h1:SnN+nyRdqN7sULnHUWCofP+Jxs3VJN/y8AlMpcz0nbk=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210124065645-9549a32d954a/go.mod h1:9V7DdSwpEfCKQNvLZhRnFJFkelTU0tPLfwR5l6UFF1Y=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210206134348-800bf525ed0e/go.mod h1:yhqA0NyKxUf7I/0HR/1OMchveFggX8wde04gqdGrNfU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -55,6 +55,7 @@ github.com/guonaihong/gout v0.1.4/go.mod h1:0rFYAYyzbcxEg11eY2qUbffJs7hHRPeugAnl
|
|||||||
github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw=
|
github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw=
|
||||||
github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||||
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/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
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 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
@ -84,8 +85,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
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/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
|
|
||||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
@ -110,6 +109,7 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
|
|||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189 h1:4UJw9if55Fu3HOwbfcaQlJ27p3oeJU2JZqoeT3ITJQk=
|
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189 h1:4UJw9if55Fu3HOwbfcaQlJ27p3oeJU2JZqoeT3ITJQk=
|
||||||
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189/go.mod h1:rIrm5geMiBhPQkdfUm8gDFi/WiHneOp1i9KjmJqc+9I=
|
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189/go.mod h1:rIrm5geMiBhPQkdfUm8gDFi/WiHneOp1i9KjmJqc+9I=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
287
main.go
287
main.go
@ -2,8 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/aes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -22,6 +25,7 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/server"
|
"github.com/Mrs4s/go-cqhttp/server"
|
||||||
"github.com/guonaihong/gout"
|
"github.com/guonaihong/gout"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
@ -30,42 +34,15 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||||
"github.com/rifflock/lfshook"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
easy "github.com/t-tomalak/logrus-easy-formatter"
|
easy "github.com/t-tomalak/logrus-easy-formatter"
|
||||||
)
|
)
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
var conf *global.JSONConfig
|
||||||
|
var isFastStart = false
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.SetFormatter(&easy.Formatter{
|
|
||||||
TimestampFormat: "2006-01-02 15:04:05",
|
|
||||||
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
|
||||||
})
|
|
||||||
w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24))
|
|
||||||
if err == nil {
|
|
||||||
log.SetOutput(io.MultiWriter(os.Stderr, w))
|
|
||||||
}
|
|
||||||
if !global.PathExists(global.ImagePath) {
|
|
||||||
if err := os.MkdirAll(global.ImagePath, 0755); err != nil {
|
|
||||||
log.Fatalf("创建图片缓存文件夹失败: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !global.PathExists(global.VoicePath) {
|
|
||||||
if err := os.MkdirAll(global.VoicePath, 0755); err != nil {
|
|
||||||
log.Fatalf("创建语音缓存文件夹失败: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !global.PathExists(global.VideoPath) {
|
|
||||||
if err := os.MkdirAll(global.VideoPath, 0755); err != nil {
|
|
||||||
log.Fatalf("创建视频缓存文件夹失败: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !global.PathExists(global.CachePath) {
|
|
||||||
if err := os.MkdirAll(global.CachePath, 0755); err != nil {
|
|
||||||
log.Fatalf("创建发送图片缓存文件夹失败: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if global.PathExists("cqhttp.json") {
|
if global.PathExists("cqhttp.json") {
|
||||||
log.Info("发现 cqhttp.json 将在五秒后尝试导入配置,按 Ctrl+C 取消.")
|
log.Info("发现 cqhttp.json 将在五秒后尝试导入配置,按 Ctrl+C 取消.")
|
||||||
log.Warn("警告: 该操作会删除 cqhttp.json 并覆盖 config.hjson 文件.")
|
log.Warn("警告: 该操作会删除 cqhttp.json 并覆盖 config.hjson 文件.")
|
||||||
@ -95,12 +72,54 @@ func init() {
|
|||||||
}
|
}
|
||||||
_ = os.Remove("cqhttp.json")
|
_ = os.Remove("cqhttp.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf = getConfig()
|
||||||
|
if conf == nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFormatter := &easy.Formatter{
|
||||||
|
TimestampFormat: "2006-01-02 15:04:05",
|
||||||
|
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
||||||
|
}
|
||||||
|
w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("rotatelogs init err: %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在debug模式下,将在标准输出中打印当前执行行数
|
||||||
|
if conf.Debug {
|
||||||
|
log.SetReportCaller(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.AddHook(global.NewLocalHook(w, logFormatter, global.GetLogLevel(conf.LogLevel)...))
|
||||||
|
|
||||||
|
if !global.PathExists(global.ImagePath) {
|
||||||
|
if err := os.MkdirAll(global.ImagePath, 0755); err != nil {
|
||||||
|
log.Fatalf("创建图片缓存文件夹失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !global.PathExists(global.VoicePath) {
|
||||||
|
if err := os.MkdirAll(global.VoicePath, 0755); err != nil {
|
||||||
|
log.Fatalf("创建语音缓存文件夹失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !global.PathExists(global.VideoPath) {
|
||||||
|
if err := os.MkdirAll(global.VideoPath, 0755); err != nil {
|
||||||
|
log.Fatalf("创建视频缓存文件夹失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !global.PathExists(global.CachePath) {
|
||||||
|
if err := os.MkdirAll(global.CachePath, 0755); err != nil {
|
||||||
|
log.Fatalf("创建发送图片缓存文件夹失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var byteKey []byte
|
var byteKey []byte
|
||||||
var isFastStart = false
|
|
||||||
arg := os.Args
|
arg := os.Args
|
||||||
if len(arg) > 1 {
|
if len(arg) > 1 {
|
||||||
for i := range arg {
|
for i := range arg {
|
||||||
@ -122,93 +141,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var conf *global.JSONConfig
|
|
||||||
if global.PathExists("config.json") {
|
|
||||||
conf = global.Load("config.json")
|
|
||||||
_ = conf.Save("config.hjson")
|
|
||||||
_ = os.Remove("config.json")
|
|
||||||
} else if os.Getenv("UIN") != "" {
|
|
||||||
log.Infof("将从环境变量加载配置.")
|
|
||||||
uin, _ := strconv.ParseInt(os.Getenv("UIN"), 10, 64)
|
|
||||||
pwd := os.Getenv("PASS")
|
|
||||||
post := os.Getenv("HTTP_POST")
|
|
||||||
conf = &global.JSONConfig{
|
|
||||||
Uin: uin,
|
|
||||||
Password: pwd,
|
|
||||||
HTTPConfig: &global.GoCQHTTPConfig{
|
|
||||||
Enabled: true,
|
|
||||||
Host: "0.0.0.0",
|
|
||||||
Port: 5700,
|
|
||||||
PostUrls: map[string]string{},
|
|
||||||
},
|
|
||||||
WSConfig: &global.GoCQWebSocketConfig{
|
|
||||||
Enabled: true,
|
|
||||||
Host: "0.0.0.0",
|
|
||||||
Port: 6700,
|
|
||||||
},
|
|
||||||
PostMessageFormat: "string",
|
|
||||||
Debug: os.Getenv("DEBUG") == "true",
|
|
||||||
}
|
|
||||||
if post != "" {
|
|
||||||
conf.HTTPConfig.PostUrls[post] = os.Getenv("HTTP_SECRET")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
conf = global.Load("config.hjson")
|
|
||||||
}
|
|
||||||
if conf == nil {
|
|
||||||
err := global.WriteAllText("config.hjson", global.DefaultConfigWithComments)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("创建默认配置文件时出现错误: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Infof("默认配置文件已生成, 请编辑 config.hjson 后重启程序.")
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "") {
|
if conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "") {
|
||||||
log.Warnf("请修改 config.hjson 以添加账号密码.")
|
log.Warnf("请修改 config.hjson 以添加账号密码.")
|
||||||
|
if !isFastStart {
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// log classified by level
|
|
||||||
// Collect all records up to the specified level (default level: warn)
|
|
||||||
logLevel := conf.LogLevel
|
|
||||||
if logLevel != "" {
|
|
||||||
date := time.Now().Format("2006-01-02")
|
|
||||||
var logPathMap lfshook.PathMap
|
|
||||||
switch conf.LogLevel {
|
|
||||||
case "warn":
|
|
||||||
logPathMap = lfshook.PathMap{
|
|
||||||
log.WarnLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
log.ErrorLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
log.FatalLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
log.PanicLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
}
|
|
||||||
case "error":
|
|
||||||
logPathMap = lfshook.PathMap{
|
|
||||||
log.ErrorLevel: path.Join("logs", date+"-error.log"),
|
|
||||||
log.FatalLevel: path.Join("logs", date+"-error.log"),
|
|
||||||
log.PanicLevel: path.Join("logs", date+"-error.log"),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logPathMap = lfshook.PathMap{
|
|
||||||
log.WarnLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
log.ErrorLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
log.FatalLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
log.PanicLevel: path.Join("logs", date+"-warn.log"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.AddHook(lfshook.NewHook(
|
|
||||||
logPathMap,
|
|
||||||
&easy.Formatter{
|
|
||||||
TimestampFormat: "2006-01-02 15:04:05",
|
|
||||||
LogFormat: "[%time%] [%lvl%]: %msg% \n",
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("当前版本:", coolq.Version)
|
log.Info("当前版本:", coolq.Version)
|
||||||
if conf.Debug {
|
if conf.Debug {
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
@ -233,15 +173,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
if conf.EncryptPassword && conf.PasswordEncrypted == "" {
|
if conf.EncryptPassword && conf.PasswordEncrypted == "" {
|
||||||
log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)")
|
log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)")
|
||||||
byteKey, _ := term.ReadPassword(int(os.Stdin.Fd()))
|
byteKey, _ = term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
key := md5.Sum(byteKey)
|
global.PasswordHash = md5.Sum([]byte(conf.Password))
|
||||||
if encrypted := EncryptPwd(conf.Password, key[:]); encrypted != "" {
|
|
||||||
conf.Password = ""
|
conf.Password = ""
|
||||||
conf.PasswordEncrypted = encrypted
|
conf.PasswordEncrypted = "AES:" + PasswordHashEncrypt(global.PasswordHash[:], byteKey)
|
||||||
_ = conf.Save("config.hjson")
|
_ = conf.Save("config.hjson")
|
||||||
} else {
|
|
||||||
log.Warnf("加密时出现问题.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if conf.PasswordEncrypted != "" {
|
if conf.PasswordEncrypted != "" {
|
||||||
if len(byteKey) == 0 {
|
if len(byteKey) == 0 {
|
||||||
@ -262,8 +198,25 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
log.Infof("密码加密已启用, 使用运行时传递的参数进行解密,按 Ctrl+C 取消.")
|
log.Infof("密码加密已启用, 使用运行时传递的参数进行解密,按 Ctrl+C 取消.")
|
||||||
}
|
}
|
||||||
key := md5.Sum(byteKey)
|
|
||||||
conf.Password = DecryptPwd(conf.PasswordEncrypted, key[:])
|
//升级客户端密码加密方案,MD5+TEA 加密密码 -> PBKDF2+AES 加密 MD5
|
||||||
|
//升级后的 PasswordEncrypted 字符串以"AES:"开始,其后为 Hex 编码的16字节加密 MD5
|
||||||
|
if !strings.HasPrefix(conf.PasswordEncrypted, "AES:") {
|
||||||
|
password := OldPasswordDecrypt(conf.PasswordEncrypted, byteKey)
|
||||||
|
passwordHash := md5.Sum([]byte(password))
|
||||||
|
newPasswordHash := PasswordHashEncrypt(passwordHash[:], byteKey)
|
||||||
|
conf.PasswordEncrypted = "AES:" + newPasswordHash
|
||||||
|
_ = conf.Save("config.hjson")
|
||||||
|
log.Debug("密码加密方案升级完成")
|
||||||
|
}
|
||||||
|
|
||||||
|
ph, err := PasswordHashDecrypt(conf.PasswordEncrypted[4:], byteKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("加密存储的密码损坏,请尝试重新配置密码")
|
||||||
|
}
|
||||||
|
copy(global.PasswordHash[:], ph)
|
||||||
|
} else {
|
||||||
|
global.PasswordHash = md5.Sum([]byte(conf.Password))
|
||||||
}
|
}
|
||||||
if !isFastStart {
|
if !isFastStart {
|
||||||
log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.")
|
log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.")
|
||||||
@ -283,7 +236,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return "未知"
|
return "未知"
|
||||||
}())
|
}())
|
||||||
cli := client.NewClient(conf.Uin, conf.Password)
|
cli := client.NewClientMd5(conf.Uin, global.PasswordHash)
|
||||||
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case "INFO":
|
case "INFO":
|
||||||
@ -340,27 +293,50 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//EncryptPwd 通过给定key加密给定pwd
|
// PasswordHashEncrypt 使用key加密给定passwordHash
|
||||||
func EncryptPwd(pwd string, key []byte) string {
|
func PasswordHashEncrypt(passwordHash []byte, key []byte) string {
|
||||||
tea := binary.NewTeaCipher(key)
|
if len(passwordHash) != 16 {
|
||||||
if tea == nil {
|
panic("密码加密参数错误")
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
return base64.StdEncoding.EncodeToString(tea.Encrypt([]byte(pwd)))
|
|
||||||
|
key = pbkdf2.Key(key, key, 114514, 32, sha1.New)
|
||||||
|
|
||||||
|
cipher, _ := aes.NewCipher(key)
|
||||||
|
result := make([]byte, 16)
|
||||||
|
cipher.Encrypt(result, passwordHash)
|
||||||
|
|
||||||
|
return hex.EncodeToString(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
//DecryptPwd 通过给定key解密给定ePwd
|
// PasswordHashDecrypt 使用key解密给定passwordHash
|
||||||
func DecryptPwd(ePwd string, key []byte) string {
|
func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, error) {
|
||||||
|
ciphertext, err := hex.DecodeString(encryptedPasswordHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key = pbkdf2.Key(key, key, 114514, 32, sha1.New)
|
||||||
|
|
||||||
|
cipher, _ := aes.NewCipher(key)
|
||||||
|
result := make([]byte, 16)
|
||||||
|
cipher.Decrypt(result, ciphertext)
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldPasswordDecrypt 使用key解密老password,仅供兼容使用
|
||||||
|
func OldPasswordDecrypt(encryptedPassword string, key []byte) string {
|
||||||
defer func() {
|
defer func() {
|
||||||
if pan := recover(); pan != nil {
|
if pan := recover(); pan != nil {
|
||||||
log.Fatalf("密码解密失败: %v", pan)
|
log.Fatalf("密码解密失败: %v", pan)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
encrypted, err := base64.StdEncoding.DecodeString(ePwd)
|
encKey := md5.Sum(key)
|
||||||
|
encrypted, err := base64.StdEncoding.DecodeString(encryptedPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
tea := binary.NewTeaCipher(key)
|
tea := binary.NewTeaCipher(encKey[:])
|
||||||
if tea == nil {
|
if tea == nil {
|
||||||
panic("密钥错误")
|
panic("密钥错误")
|
||||||
}
|
}
|
||||||
@ -491,3 +467,52 @@ func restart(Args []string) {
|
|||||||
}
|
}
|
||||||
_ = cmd.Start()
|
_ = cmd.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getConfig() *global.JSONConfig {
|
||||||
|
var conf *global.JSONConfig
|
||||||
|
if global.PathExists("config.json") {
|
||||||
|
conf = global.LoadConfig("config.json")
|
||||||
|
_ = conf.Save("config.hjson")
|
||||||
|
_ = os.Remove("config.json")
|
||||||
|
} else if os.Getenv("UIN") != "" {
|
||||||
|
log.Infof("将从环境变量加载配置.")
|
||||||
|
uin, _ := strconv.ParseInt(os.Getenv("UIN"), 10, 64)
|
||||||
|
pwd := os.Getenv("PASS")
|
||||||
|
post := os.Getenv("HTTP_POST")
|
||||||
|
conf = &global.JSONConfig{
|
||||||
|
Uin: uin,
|
||||||
|
Password: pwd,
|
||||||
|
HTTPConfig: &global.GoCQHTTPConfig{
|
||||||
|
Enabled: true,
|
||||||
|
Host: "0.0.0.0",
|
||||||
|
Port: 5700,
|
||||||
|
PostUrls: map[string]string{},
|
||||||
|
},
|
||||||
|
WSConfig: &global.GoCQWebSocketConfig{
|
||||||
|
Enabled: true,
|
||||||
|
Host: "0.0.0.0",
|
||||||
|
Port: 6700,
|
||||||
|
},
|
||||||
|
PostMessageFormat: "string",
|
||||||
|
Debug: os.Getenv("DEBUG") == "true",
|
||||||
|
}
|
||||||
|
if post != "" {
|
||||||
|
conf.HTTPConfig.PostUrls[post] = os.Getenv("HTTP_SECRET")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conf = global.LoadConfig("config.hjson")
|
||||||
|
}
|
||||||
|
if conf == nil {
|
||||||
|
err := global.WriteAllText("config.hjson", global.DefaultConfigWithComments)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("创建默认配置文件时出现错误: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Infof("默认配置文件已生成, 请编辑 config.hjson 后重启程序.")
|
||||||
|
if !isFastStart {
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
@ -333,7 +333,7 @@ func GetConf() *global.JSONConfig {
|
|||||||
if JSONConfig != nil {
|
if JSONConfig != nil {
|
||||||
return JSONConfig
|
return JSONConfig
|
||||||
}
|
}
|
||||||
conf := global.Load("config.hjson")
|
conf := global.LoadConfig("config.hjson")
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +215,11 @@ func GetGroupFileUrl(s *httpServer, c *gin.Context) {
|
|||||||
c.JSON(200, s.bot.CQGetGroupFileUrl(gid, fid, int32(busid)))
|
c.JSON(200, s.bot.CQGetGroupFileUrl(gid, fid, int32(busid)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UploadGroupFile(s *httpServer, c *gin.Context) {
|
||||||
|
gid, _ := strconv.ParseInt(getParam(c, "group_id"), 10, 64)
|
||||||
|
c.JSON(200, s.bot.CQUploadGroupFile(gid, getParam(c, "file"), getParam(c, "name"), getParam(c, "folder")))
|
||||||
|
}
|
||||||
|
|
||||||
func SendMessage(s *httpServer, c *gin.Context) {
|
func SendMessage(s *httpServer, c *gin.Context) {
|
||||||
if getParam(c, "message_type") == "private" {
|
if getParam(c, "message_type") == "private" {
|
||||||
SendPrivateMessage(s, c)
|
SendPrivateMessage(s, c)
|
||||||
@ -486,6 +491,25 @@ func SetGroupPortrait(s *httpServer, c *gin.Context) {
|
|||||||
c.JSON(200, s.bot.CQSetGroupPortrait(gid, file, cache))
|
c.JSON(200, s.bot.CQSetGroupPortrait(gid, file, cache))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetEssenceMsg(s *httpServer, c *gin.Context) {
|
||||||
|
mid, _ := strconv.ParseInt(getParam(c, "message_id"), 10, 64)
|
||||||
|
c.JSON(200, s.bot.CQSetEssenceMessage(int32(mid)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteEssenceMsg(s *httpServer, c *gin.Context) {
|
||||||
|
mid, _ := strconv.ParseInt(getParam(c, "message_id"), 10, 64)
|
||||||
|
c.JSON(200, s.bot.CQDeleteEssenceMessage(int32(mid)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEssenceMsgList(s *httpServer, c *gin.Context) {
|
||||||
|
gid, _ := strconv.ParseInt(getParam(c, "group_id"), 10, 64)
|
||||||
|
c.JSON(200, s.bot.CQGetEssenceMessageList(gid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckUrlSafely(s *httpServer, c *gin.Context) {
|
||||||
|
c.JSON(200, s.bot.CQCheckUrlSafely(getParam(c, "url")))
|
||||||
|
}
|
||||||
|
|
||||||
func getParamOrDefault(c *gin.Context, k, def string) string {
|
func getParamOrDefault(c *gin.Context, k, def string) string {
|
||||||
r := getParam(c, k)
|
r := getParam(c, k)
|
||||||
if r != "" {
|
if r != "" {
|
||||||
@ -545,11 +569,14 @@ var httpApi = map[string]func(s *httpServer, c *gin.Context){
|
|||||||
"get_group_root_files": GetGroupRootFiles,
|
"get_group_root_files": GetGroupRootFiles,
|
||||||
"get_group_files_by_folder": GetGroupFilesByFolderId,
|
"get_group_files_by_folder": GetGroupFilesByFolderId,
|
||||||
"get_group_file_url": GetGroupFileUrl,
|
"get_group_file_url": GetGroupFileUrl,
|
||||||
|
"upload_group_file": UploadGroupFile,
|
||||||
|
"get_essence_msg_list": GetEssenceMsgList,
|
||||||
"send_msg": SendMessage,
|
"send_msg": SendMessage,
|
||||||
"send_group_msg": SendGroupMessage,
|
"send_group_msg": SendGroupMessage,
|
||||||
"send_group_forward_msg": SendGroupForwardMessage,
|
"send_group_forward_msg": SendGroupForwardMessage,
|
||||||
"send_private_msg": SendPrivateMessage,
|
"send_private_msg": SendPrivateMessage,
|
||||||
"delete_msg": DeleteMessage,
|
"delete_msg": DeleteMessage,
|
||||||
|
"delete_essence_msg": DeleteEssenceMsg,
|
||||||
"set_friend_add_request": ProcessFriendRequest,
|
"set_friend_add_request": ProcessFriendRequest,
|
||||||
"set_group_add_request": ProcessGroupRequest,
|
"set_group_add_request": ProcessGroupRequest,
|
||||||
"set_group_card": SetGroupCard,
|
"set_group_card": SetGroupCard,
|
||||||
@ -559,6 +586,7 @@ var httpApi = map[string]func(s *httpServer, c *gin.Context){
|
|||||||
"set_group_whole_ban": SetWholeBan,
|
"set_group_whole_ban": SetWholeBan,
|
||||||
"set_group_name": SetGroupName,
|
"set_group_name": SetGroupName,
|
||||||
"set_group_admin": SetGroupAdmin,
|
"set_group_admin": SetGroupAdmin,
|
||||||
|
"set_essence_msg": SetEssenceMsg,
|
||||||
"set_restart": SetRestart,
|
"set_restart": SetRestart,
|
||||||
"_send_group_notice": SendGroupNotice,
|
"_send_group_notice": SendGroupNotice,
|
||||||
"set_group_leave": SetGroupLeave,
|
"set_group_leave": SetGroupLeave,
|
||||||
@ -577,6 +605,7 @@ var httpApi = map[string]func(s *httpServer, c *gin.Context){
|
|||||||
"set_group_portrait": SetGroupPortrait,
|
"set_group_portrait": SetGroupPortrait,
|
||||||
"set_group_anonymous_ban": SetGroupAnonymousBan,
|
"set_group_anonymous_ban": SetGroupAnonymousBan,
|
||||||
"get_group_msg_history": GetGroupMessageHistory,
|
"get_group_msg_history": GetGroupMessageHistory,
|
||||||
|
"check_url_safely": CheckUrlSafely,
|
||||||
"download_file": DownloadFile,
|
"download_file": DownloadFile,
|
||||||
".handle_quick_operation": HandleQuickOperation,
|
".handle_quick_operation": HandleQuickOperation,
|
||||||
".ocr_image": OcrImage,
|
".ocr_image": OcrImage,
|
||||||
|
@ -562,6 +562,9 @@ var wsAPI = map[string]func(*coolq.CQBot, gjson.Result) coolq.MSG{
|
|||||||
"get_group_file_url": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
"get_group_file_url": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
return bot.CQGetGroupFileUrl(p.Get("group_id").Int(), p.Get("file_id").Str, int32(p.Get("busid").Int()))
|
return bot.CQGetGroupFileUrl(p.Get("group_id").Int(), p.Get("file_id").Str, int32(p.Get("busid").Int()))
|
||||||
},
|
},
|
||||||
|
"upload_group_file": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
|
return bot.CQUploadGroupFile(p.Get("group_id").Int(), p.Get("file").Str, p.Get("name").Str, p.Get("folder").Str)
|
||||||
|
},
|
||||||
"get_group_msg_history": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
"get_group_msg_history": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
return bot.CQGetGroupMessageHistory(p.Get("group_id").Int(), p.Get("message_seq").Int())
|
return bot.CQGetGroupMessageHistory(p.Get("group_id").Int(), p.Get("message_seq").Int())
|
||||||
},
|
},
|
||||||
@ -589,6 +592,18 @@ var wsAPI = map[string]func(*coolq.CQBot, gjson.Result) coolq.MSG{
|
|||||||
"set_group_portrait": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
"set_group_portrait": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
return bot.CQSetGroupPortrait(p.Get("group_id").Int(), p.Get("file").String(), p.Get("cache").String())
|
return bot.CQSetGroupPortrait(p.Get("group_id").Int(), p.Get("file").String(), p.Get("cache").String())
|
||||||
},
|
},
|
||||||
|
"set_essence_msg": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
|
return bot.CQSetEssenceMessage(int32(p.Get("message_id").Int()))
|
||||||
|
},
|
||||||
|
"delete_essence_msg": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
|
return bot.CQDeleteEssenceMessage(int32(p.Get("message_id").Int()))
|
||||||
|
},
|
||||||
|
"get_essence_msg_list": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
|
return bot.CQGetEssenceMessageList(p.Get("group_id").Int())
|
||||||
|
},
|
||||||
|
"check_url_safely": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
|
return bot.CQCheckUrlSafely(p.Get("url").String())
|
||||||
|
},
|
||||||
"set_group_anonymous_ban": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
"set_group_anonymous_ban": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
|
||||||
obj := p.Get("anonymous")
|
obj := p.Get("anonymous")
|
||||||
flag := p.Get("anonymous_flag")
|
flag := p.Get("anonymous_flag")
|
||||||
|
Reference in New Issue
Block a user