mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-05-08 04:55:55 +08:00
feat: multi database support - leveldb.
This commit is contained in:
parent
efdd6bd16a
commit
66266f0d5e
128
coolq/api.go
128
coolq/api.go
@ -5,6 +5,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Mrs4s/go-cqhttp/db"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
@ -474,20 +475,19 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa
|
||||
ts.Add(time.Second)
|
||||
if e.Get("data.id").Exists() {
|
||||
i := e.Get("data.id").Int()
|
||||
m := bot.GetMessage(int32(i))
|
||||
m, _ := bot.db.GetGroupMessageByGlobalID(int32(i))
|
||||
if m != nil {
|
||||
sender := m["sender"].(message.Sender)
|
||||
return &message.ForwardNode{
|
||||
SenderId: sender.Uin,
|
||||
SenderName: (&sender).DisplayName(),
|
||||
SenderId: m.Attribute.SenderUin,
|
||||
SenderName: m.Attribute.SenderName,
|
||||
Time: func() int32 {
|
||||
msgTime := m["time"].(int32)
|
||||
msgTime := m.Attribute.Timestamp
|
||||
if hasCustom && msgTime == 0 {
|
||||
return int32(ts.Unix())
|
||||
}
|
||||
return msgTime
|
||||
return int32(msgTime)
|
||||
}(),
|
||||
Message: resolveElement(bot.ConvertStringMessage(m["message"].(string), true)),
|
||||
Message: resolveElement(bot.ConvertContentMessage(m.Content, true)),
|
||||
}
|
||||
}
|
||||
log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str)
|
||||
@ -791,29 +791,28 @@ func (bot *CQBot) CQProcessGroupRequest(flag, subType, reason string, approve bo
|
||||
//
|
||||
// https:// git.io/Jtz1y
|
||||
func (bot *CQBot) CQDeleteMessage(messageID int32) global.MSG {
|
||||
msg := bot.GetMessage(messageID)
|
||||
if msg == nil {
|
||||
msg, err := bot.db.GetMessageByGlobalID(messageID)
|
||||
if err != nil {
|
||||
log.Warnf("撤回消息时出现错误: %v", err)
|
||||
return Failed(100, "MESSAGE_NOT_FOUND", "消息不存在")
|
||||
}
|
||||
if _, ok := msg["group"]; ok {
|
||||
if msg["internal-id"] == nil {
|
||||
// TODO 撤回临时对话消息
|
||||
log.Warnf("撤回 %v 失败: 无法撤回临时对话消息", messageID)
|
||||
return Failed(100, "CANNOT_RECALL_TEMP_MSG", "无法撤回临时对话消息")
|
||||
}
|
||||
if err := bot.Client.RecallGroupMessage(msg["group"].(int64), msg["message-id"].(int32), msg["internal-id"].(int32)); err != nil {
|
||||
switch o := msg.(type) {
|
||||
case *db.StoredGroupMessage:
|
||||
if err = bot.Client.RecallGroupMessage(o.GroupCode, o.Attribute.MessageSeq, o.Attribute.InternalID); err != nil {
|
||||
log.Warnf("撤回 %v 失败: %v", messageID, err)
|
||||
return Failed(100, "RECALL_API_ERROR", err.Error())
|
||||
}
|
||||
} else {
|
||||
if msg["sender"].(message.Sender).Uin != bot.Client.Uin {
|
||||
case *db.StoredPrivateMessage:
|
||||
if o.Attribute.SenderUin != bot.Client.Uin {
|
||||
log.Warnf("撤回 %v 失败: 好友会话无法撤回对方消息.", messageID)
|
||||
return Failed(100, "CANNOT_RECALL_FRIEND_MSG", "无法撤回对方消息")
|
||||
}
|
||||
if err := bot.Client.RecallPrivateMessage(msg["target"].(int64), int64(msg["time"].(int32)), msg["message-id"].(int32), msg["internal-id"].(int32)); err != nil {
|
||||
if err = bot.Client.RecallPrivateMessage(o.TargetUin, o.Attribute.Timestamp, o.Attribute.MessageSeq, o.Attribute.InternalID); err != nil {
|
||||
log.Warnf("撤回 %v 失败: %v", messageID, err)
|
||||
return Failed(100, "RECALL_API_ERROR", err.Error())
|
||||
}
|
||||
default:
|
||||
return Failed(100, "UNKNOWN_ERROR")
|
||||
}
|
||||
return OK(nil)
|
||||
}
|
||||
@ -1115,38 +1114,32 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG {
|
||||
//
|
||||
// https://git.io/Jtz1b
|
||||
func (bot *CQBot) CQGetMessage(messageID int32) global.MSG {
|
||||
msg := bot.GetMessage(messageID)
|
||||
if msg == nil {
|
||||
msg, err := bot.db.GetMessageByGlobalID(messageID)
|
||||
if err != nil {
|
||||
log.Warnf("获取消息时出现错误: %v", err)
|
||||
return Failed(100, "MSG_NOT_FOUND", "消息不存在")
|
||||
}
|
||||
sender := msg["sender"].(message.Sender)
|
||||
gid, isGroup := msg["group"]
|
||||
raw := msg["message"].(string)
|
||||
return OK(global.MSG{
|
||||
"message_id": messageID,
|
||||
"real_id": msg["message-id"],
|
||||
"message_seq": msg["message-id"],
|
||||
"group": isGroup,
|
||||
"group_id": gid,
|
||||
"message_type": func() string {
|
||||
if isGroup {
|
||||
return "group"
|
||||
}
|
||||
return "private"
|
||||
}(),
|
||||
m := global.MSG{
|
||||
"message_id": msg.GetGlobalID(),
|
||||
"message_id_v2": msg.GetID(),
|
||||
"message_type": msg.GetType(),
|
||||
"real_id": msg.GetAttribute().MessageSeq,
|
||||
"message_seq": msg.GetAttribute().MessageSeq,
|
||||
"group": msg.GetType() == "group",
|
||||
"sender": global.MSG{
|
||||
"user_id": sender.Uin,
|
||||
"nickname": sender.Nickname,
|
||||
"user_id": msg.GetAttribute().SenderUin,
|
||||
"nickname": msg.GetAttribute().SenderName,
|
||||
},
|
||||
"time": msg["time"],
|
||||
"raw_message": raw,
|
||||
"message": ToFormattedMessage(bot.ConvertStringMessage(raw, isGroup), func() int64 {
|
||||
if isGroup {
|
||||
return gid.(int64)
|
||||
"time": msg.GetAttribute().Timestamp,
|
||||
}
|
||||
return 0
|
||||
}(), false),
|
||||
})
|
||||
switch o := msg.(type) {
|
||||
case *db.StoredGroupMessage:
|
||||
m["group_id"] = o.GroupCode
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, true), o.GroupCode, false)
|
||||
case *db.StoredPrivateMessage:
|
||||
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, false), 0, false)
|
||||
}
|
||||
return OK(m)
|
||||
}
|
||||
|
||||
// CQGetGroupSystemMessages 扩展API-获取群文件系统消息
|
||||
@ -1308,19 +1301,14 @@ func (bot *CQBot) CQGetStatus() global.MSG {
|
||||
//
|
||||
// https://docs.go-cqhttp.org/api/#%E8%AE%BE%E7%BD%AE%E7%B2%BE%E5%8D%8E%E6%B6%88%E6%81%AF
|
||||
func (bot *CQBot) CQSetEssenceMessage(messageID int32) global.MSG {
|
||||
msg := bot.GetMessage(messageID)
|
||||
if msg == nil {
|
||||
msg, err := bot.db.GetGroupMessageByGlobalID(messageID)
|
||||
if err != 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 {
|
||||
if err := bot.Client.SetEssenceMessage(msg.GroupCode, msg.Attribute.MessageSeq, msg.Attribute.InternalID); 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)
|
||||
}
|
||||
|
||||
@ -1328,18 +1316,13 @@ func (bot *CQBot) CQSetEssenceMessage(messageID int32) global.MSG {
|
||||
//
|
||||
// https://docs.go-cqhttp.org/api/#%E7%A7%BB%E5%87%BA%E7%B2%BE%E5%8D%8E%E6%B6%88%E6%81%AF
|
||||
func (bot *CQBot) CQDeleteEssenceMessage(messageID int32) global.MSG {
|
||||
msg := bot.GetMessage(messageID)
|
||||
if msg == nil {
|
||||
msg, err := bot.db.GetGroupMessageByGlobalID(messageID)
|
||||
if err != 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", "非群聊")
|
||||
if err := bot.Client.DeleteEssenceMessage(msg.GroupCode, msg.Attribute.MessageSeq, msg.Attribute.InternalID); err != nil {
|
||||
log.Warnf("删除精华消息 %v 失败: %v", messageID, err)
|
||||
return Failed(100, "SET_ESSENCE_MSG_ERROR", err.Error())
|
||||
}
|
||||
return OK(nil)
|
||||
}
|
||||
@ -1366,7 +1349,7 @@ func (bot *CQBot) CQGetEssenceMessageList(groupCode int64) global.MSG {
|
||||
"sender_id": m.SenderUin,
|
||||
"operator_id": m.AddDigestUin,
|
||||
}
|
||||
msg["message_id"] = toGlobalID(groupCode, int32(m.MessageID))
|
||||
msg["message_id"] = db.ToGlobalID(groupCode, int32(m.MessageID))
|
||||
list = append(list, msg)
|
||||
}
|
||||
return OK(list)
|
||||
@ -1452,18 +1435,17 @@ func (bot *CQBot) CQSetModelShow(modelName string, modelShow string) global.MSG
|
||||
|
||||
// CQMarkMessageAsRead 标记消息已读
|
||||
func (bot *CQBot) CQMarkMessageAsRead(msgID int32) global.MSG {
|
||||
m := bot.GetMessage(msgID)
|
||||
if m == nil {
|
||||
m, err := bot.db.GetMessageByGlobalID(msgID)
|
||||
if err != nil {
|
||||
return Failed(100, "MSG_NOT_FOUND", "消息不存在")
|
||||
}
|
||||
if _, ok := m["group"]; ok {
|
||||
bot.Client.MarkGroupMessageReaded(m["group"].(int64), int64(m["message-id"].(int32)))
|
||||
switch o := m.(type) {
|
||||
case *db.StoredGroupMessage:
|
||||
bot.Client.MarkGroupMessageReaded(o.GroupCode, int64(o.Attribute.MessageSeq))
|
||||
return OK(nil)
|
||||
case *db.StoredPrivateMessage:
|
||||
bot.Client.MarkPrivateMessageReaded(o.SessionUin, o.Attribute.Timestamp)
|
||||
}
|
||||
if _, ok := m["from-group"]; ok {
|
||||
return Failed(100, "MSG_TYPE_ERROR", "不支持标记临时会话")
|
||||
}
|
||||
bot.Client.MarkPrivateMessageReaded(m["sender"].(message.Sender).Uin, int64(m["time"].(int32)))
|
||||
return OK(nil)
|
||||
}
|
||||
|
||||
|
167
coolq/bot.go
167
coolq/bot.go
@ -2,11 +2,10 @@ package coolq
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"github.com/Mrs4s/go-cqhttp/db"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@ -22,8 +21,6 @@ import (
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
"github.com/Mrs4s/go-cqhttp/global/config"
|
||||
@ -36,7 +33,7 @@ type CQBot struct {
|
||||
lock sync.RWMutex
|
||||
events []func(*Event)
|
||||
|
||||
db *leveldb.DB
|
||||
db db.IDatabase
|
||||
friendReqCache sync.Map
|
||||
tempSessionCache sync.Map
|
||||
}
|
||||
@ -111,15 +108,11 @@ func NewQQBot(cli *client.QQClient, conf *config.Config) *CQBot {
|
||||
enableLevelDB = lconf.Enable
|
||||
}
|
||||
if enableLevelDB {
|
||||
p := path.Join("data", "leveldb")
|
||||
db, err := leveldb.OpenFile(p, &opt.Options{
|
||||
WriteBuffer: 128 * opt.KiB,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("打开数据库失败, 如果频繁遇到此问题请清理 data/leveldb 文件夹或关闭数据库功能。")
|
||||
level := db.UseLevelDB()
|
||||
if err := level.Open(); err != nil {
|
||||
log.Fatalf("打开数据库失败: %v", err)
|
||||
}
|
||||
bot.db = db
|
||||
gob.Register(message.Sender{})
|
||||
bot.db = level
|
||||
log.Info("信息数据库初始化完成.")
|
||||
} else {
|
||||
log.Warn("警告: 信息数据库已关闭,将无法使用 [回复/撤回] 等功能。")
|
||||
@ -182,22 +175,6 @@ func (bot *CQBot) OnEventPush(f func(e *Event)) {
|
||||
bot.lock.Unlock()
|
||||
}
|
||||
|
||||
// GetMessage 获取给定消息id对应的消息
|
||||
func (bot *CQBot) GetMessage(mid int32) global.MSG {
|
||||
if bot.db != nil {
|
||||
m := global.MSG{}
|
||||
data, err := bot.db.Get(binary.ToBytes(mid), nil)
|
||||
if err == nil {
|
||||
err = gob.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||
if err == nil {
|
||||
return m
|
||||
}
|
||||
}
|
||||
log.Warnf("获取信息时出现错误: %v id: %v", err, mid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UploadLocalImageAsGroup 上传本地图片至群聊
|
||||
func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElement) (i *message.GroupImageElement, err error) {
|
||||
if img.File != "" {
|
||||
@ -367,7 +344,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
||||
if session == nil && groupID != 0 {
|
||||
msg := bot.Client.SendGroupTempMessage(groupID, target, m)
|
||||
if msg != nil {
|
||||
id = bot.InsertTempMessage(target, msg)
|
||||
// id = bot.InsertTempMessage(target, msg)
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -377,7 +354,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
||||
break
|
||||
}
|
||||
if msg != nil {
|
||||
id = bot.InsertTempMessage(target, msg)
|
||||
// id = bot.InsertTempMessage(target, msg)
|
||||
}
|
||||
}
|
||||
case unidirectionalFriendExists(): // 单向好友
|
||||
@ -397,57 +374,91 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
||||
|
||||
// InsertGroupMessage 群聊消息入数据库
|
||||
func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 {
|
||||
val := global.MSG{
|
||||
"message-id": m.Id,
|
||||
"internal-id": m.InternalId,
|
||||
"group": m.GroupCode,
|
||||
"group-name": m.GroupName,
|
||||
"sender": m.Sender,
|
||||
"time": m.Time,
|
||||
"message": ToStringMessage(m.Elements, m.GroupCode, true),
|
||||
t := &message.SendingMessage{Elements: m.Elements}
|
||||
replyElem := t.FirstOrNil(func(e message.IMessageElement) bool {
|
||||
_, ok := e.(*message.ReplyElement)
|
||||
return ok
|
||||
})
|
||||
msg := &db.StoredGroupMessage{
|
||||
ID: encodeMessageId(m.GroupCode, m.Id),
|
||||
GlobalID: db.ToGlobalID(m.GroupCode, m.Id),
|
||||
SubType: "normal",
|
||||
Attribute: &db.StoredMessageAttribute{
|
||||
MessageSeq: m.Id,
|
||||
InternalID: m.InternalId,
|
||||
SenderUin: m.Sender.Uin,
|
||||
SenderName: m.Sender.DisplayName(),
|
||||
Timestamp: int64(m.Time),
|
||||
},
|
||||
GroupCode: m.GroupCode,
|
||||
AnonymousID: func() string {
|
||||
if m.Sender.IsAnonymous() {
|
||||
return m.Sender.AnonymousInfo.AnonymousId
|
||||
}
|
||||
id := toGlobalID(m.GroupCode, m.Id)
|
||||
if bot.db != nil {
|
||||
buf := global.NewBuffer()
|
||||
defer global.PutBuffer(buf)
|
||||
if err := gob.NewEncoder(buf).Encode(val); err != nil {
|
||||
return ""
|
||||
}(),
|
||||
Content: ToMessageContent(m.Elements),
|
||||
}
|
||||
if replyElem != nil {
|
||||
reply := replyElem.(*message.ReplyElement)
|
||||
msg.SubType = "quote"
|
||||
msg.QuotedInfo = &db.QuotedInfo{
|
||||
PrevID: encodeMessageId(m.GroupCode, reply.ReplySeq),
|
||||
PrevGlobalID: db.ToGlobalID(m.GroupCode, reply.ReplySeq),
|
||||
QuotedContent: ToMessageContent(reply.Elements),
|
||||
}
|
||||
}
|
||||
if err := bot.db.InsertGroupMessage(msg); err != nil {
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
return -1
|
||||
}
|
||||
if err := bot.db.Put(binary.ToBytes(id), buf.Bytes(), nil); err != nil {
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return id
|
||||
return msg.GlobalID
|
||||
}
|
||||
|
||||
// InsertPrivateMessage 私聊消息入数据库
|
||||
func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 {
|
||||
val := global.MSG{
|
||||
"message-id": m.Id,
|
||||
"internal-id": m.InternalId,
|
||||
"target": m.Target,
|
||||
"sender": m.Sender,
|
||||
"time": m.Time,
|
||||
"message": ToStringMessage(m.Elements, 0, true),
|
||||
t := &message.SendingMessage{Elements: m.Elements}
|
||||
replyElem := t.FirstOrNil(func(e message.IMessageElement) bool {
|
||||
_, ok := e.(*message.ReplyElement)
|
||||
return ok
|
||||
})
|
||||
msg := &db.StoredPrivateMessage{
|
||||
ID: encodeMessageId(m.Sender.Uin, m.Id),
|
||||
GlobalID: db.ToGlobalID(m.Sender.Uin, m.Id),
|
||||
SubType: "normal",
|
||||
Attribute: &db.StoredMessageAttribute{
|
||||
MessageSeq: m.Id,
|
||||
InternalID: m.InternalId,
|
||||
SenderUin: m.Sender.Uin,
|
||||
SenderName: m.Sender.DisplayName(),
|
||||
Timestamp: int64(m.Time),
|
||||
},
|
||||
SessionUin: func() int64 {
|
||||
if m.Sender.Uin == m.Self {
|
||||
return m.Target
|
||||
}
|
||||
id := toGlobalID(m.Sender.Uin, m.Id)
|
||||
if bot.db != nil {
|
||||
buf := global.NewBuffer()
|
||||
defer global.PutBuffer(buf)
|
||||
if err := gob.NewEncoder(buf).Encode(val); err != nil {
|
||||
return m.Sender.Uin
|
||||
}(),
|
||||
TargetUin: m.Target,
|
||||
Content: ToMessageContent(m.Elements),
|
||||
}
|
||||
if replyElem != nil {
|
||||
reply := replyElem.(*message.ReplyElement)
|
||||
msg.SubType = "quote"
|
||||
msg.QuotedInfo = &db.QuotedInfo{
|
||||
PrevID: encodeMessageId(reply.Sender, reply.ReplySeq),
|
||||
PrevGlobalID: db.ToGlobalID(reply.Sender, reply.ReplySeq),
|
||||
QuotedContent: ToMessageContent(m.Elements),
|
||||
}
|
||||
}
|
||||
if err := bot.db.InsertPrivateMessage(msg); err != nil {
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
return -1
|
||||
}
|
||||
if err := bot.db.Put(binary.ToBytes(id), buf.Bytes(), nil); err != nil {
|
||||
log.Warnf("记录聊天数据时出现错误: %v", err)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return id
|
||||
return msg.GlobalID
|
||||
}
|
||||
|
||||
/*
|
||||
// InsertTempMessage 临时消息入数据库
|
||||
func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32 {
|
||||
val := global.MSG{
|
||||
@ -460,7 +471,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
||||
"time": int32(time.Now().Unix()),
|
||||
"message": ToStringMessage(m.Elements, 0, true),
|
||||
}
|
||||
id := toGlobalID(m.Sender.Uin, m.Id)
|
||||
id := db.ToGlobalID(m.Sender.Uin, m.Id)
|
||||
if bot.db != nil {
|
||||
buf := global.NewBuffer()
|
||||
defer global.PutBuffer(buf)
|
||||
@ -475,17 +486,11 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// toGlobalID 构建`code`-`msgID`的字符串并返回其CRC32 Checksum的值
|
||||
func toGlobalID(code int64, msgID int32) int32 {
|
||||
return int32(crc32.ChecksumIEEE([]byte(fmt.Sprintf("%d-%d", code, msgID))))
|
||||
}
|
||||
*/
|
||||
|
||||
// Release 释放Bot实例
|
||||
func (bot *CQBot) Release() {
|
||||
if bot.db != nil {
|
||||
_ = bot.db.Close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (bot *CQBot) dispatchEventMessage(m global.MSG) {
|
||||
@ -636,3 +641,11 @@ func IsLawfulImage(r io.ReadSeeker) (bool, string) {
|
||||
}
|
||||
return false, t.String()
|
||||
}
|
||||
|
||||
// encodeMessageId 临时先这样, 暂时用不上
|
||||
func encodeMessageId(target int64, seq int32) string {
|
||||
return hex.EncodeToString(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt64(uint64(target))
|
||||
w.WriteUInt32(uint32(seq))
|
||||
}))
|
||||
}
|
||||
|
226
coolq/cqcode.go
226
coolq/cqcode.go
@ -7,6 +7,7 @@ import (
|
||||
xml2 "encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Mrs4s/go-cqhttp/db"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
@ -141,7 +142,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG)
|
||||
r = append(r, global.MSG{
|
||||
"type": "reply",
|
||||
"data": map[string]string{
|
||||
"id": strconv.FormatInt(int64(toGlobalID(rid, replyElem.ReplySeq)), 10),
|
||||
"id": strconv.FormatInt(int64(db.ToGlobalID(rid, replyElem.ReplySeq)), 10),
|
||||
"seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10),
|
||||
"qq": strconv.FormatInt(replyElem.Sender, 10),
|
||||
"time": strconv.FormatInt(int64(replyElem.Time), 10),
|
||||
@ -151,7 +152,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG)
|
||||
} else {
|
||||
r = append(r, global.MSG{
|
||||
"type": "reply",
|
||||
"data": map[string]string{"id": strconv.FormatInt(int64(toGlobalID(rid, replyElem.ReplySeq)), 10)},
|
||||
"data": map[string]string{"id": strconv.FormatInt(int64(db.ToGlobalID(rid, replyElem.ReplySeq)), 10)},
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -281,11 +282,11 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
|
||||
}
|
||||
if ExtraReplyData {
|
||||
write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
|
||||
toGlobalID(rid, replyElem.ReplySeq),
|
||||
db.ToGlobalID(rid, replyElem.ReplySeq),
|
||||
replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
|
||||
CQCodeEscapeValue(ToStringMessage(replyElem.Elements, groupID)))
|
||||
} else {
|
||||
write("[CQ:reply,id=%d]", toGlobalID(rid, replyElem.ReplySeq))
|
||||
write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq))
|
||||
}
|
||||
}
|
||||
for i, elem := range e {
|
||||
@ -361,6 +362,110 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
|
||||
return
|
||||
}
|
||||
|
||||
// ToMessageContent 将消息转换成 Content. 忽略 Reply
|
||||
// 不同于 onebot 的 Array Message, 此函数转换出来的 Content 的 data 段为实际类型
|
||||
// 方便数据库查询
|
||||
func ToMessageContent(e []message.IMessageElement) (r []global.MSG) {
|
||||
for _, elem := range e {
|
||||
var m global.MSG
|
||||
switch o := elem.(type) {
|
||||
case *message.TextElement:
|
||||
m = global.MSG{
|
||||
"type": "text",
|
||||
"data": global.MSG{"text": o.Content},
|
||||
}
|
||||
case *message.LightAppElement:
|
||||
m = global.MSG{
|
||||
"type": "json",
|
||||
"data": global.MSG{"data": o.Content},
|
||||
}
|
||||
case *message.AtElement:
|
||||
if o.Target == 0 {
|
||||
m = global.MSG{
|
||||
"type": "at",
|
||||
"data": global.MSG{
|
||||
"subType": "all",
|
||||
},
|
||||
}
|
||||
} else {
|
||||
m = global.MSG{
|
||||
"type": "at",
|
||||
"data": global.MSG{
|
||||
"subType": "user",
|
||||
"target": o.Target,
|
||||
"display": o.Display,
|
||||
},
|
||||
}
|
||||
}
|
||||
case *message.RedBagElement:
|
||||
m = global.MSG{
|
||||
"type": "redbag",
|
||||
"data": global.MSG{"title": o.Title, "type": o.MsgType},
|
||||
}
|
||||
case *message.ForwardElement:
|
||||
m = global.MSG{
|
||||
"type": "forward",
|
||||
"data": global.MSG{"id": o.ResId},
|
||||
}
|
||||
case *message.FaceElement:
|
||||
m = global.MSG{
|
||||
"type": "face",
|
||||
"data": global.MSG{"id": o.Index},
|
||||
}
|
||||
case *message.VoiceElement:
|
||||
m = global.MSG{
|
||||
"type": "record",
|
||||
"data": global.MSG{"file": o.Name, "url": o.Url},
|
||||
}
|
||||
case *message.ShortVideoElement:
|
||||
m = global.MSG{
|
||||
"type": "video",
|
||||
"data": global.MSG{"file": o.Name, "url": o.Url},
|
||||
}
|
||||
case *message.GroupImageElement:
|
||||
data := global.MSG{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url, "subType": uint32(o.ImageBizType)}
|
||||
switch {
|
||||
case o.Flash:
|
||||
data["type"] = "flash"
|
||||
case o.EffectID != 0:
|
||||
data["type"] = "show"
|
||||
data["id"] = o.EffectID
|
||||
}
|
||||
m = global.MSG{
|
||||
"type": "image",
|
||||
"data": data,
|
||||
}
|
||||
case *message.FriendImageElement:
|
||||
data := global.MSG{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url}
|
||||
if o.Flash {
|
||||
data["type"] = "flash"
|
||||
}
|
||||
m = global.MSG{
|
||||
"type": "image",
|
||||
"data": data,
|
||||
}
|
||||
case *message.ServiceElement:
|
||||
if isOk := strings.Contains(o.Content, "<?xml"); isOk {
|
||||
m = global.MSG{
|
||||
"type": "xml",
|
||||
"data": global.MSG{"data": o.Content, "resid": o.Id},
|
||||
}
|
||||
} else {
|
||||
m = global.MSG{
|
||||
"type": "json",
|
||||
"data": global.MSG{"data": o.Content, "resid": o.Id},
|
||||
}
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if m != nil {
|
||||
r = append(r, m)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ConvertStringMessage 将消息字符串转为消息元素数组
|
||||
func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IMessageElement) {
|
||||
var t, key string
|
||||
@ -379,7 +484,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
|
||||
switch {
|
||||
case customText != "":
|
||||
var elem *message.ReplyElement
|
||||
var org global.MSG
|
||||
var org db.IStoredMessage
|
||||
sender, senderErr := strconv.ParseInt(d["qq"], 10, 64)
|
||||
if senderErr != nil && err != nil {
|
||||
log.Warnf("警告: 自定义 Reply 元素中必须包含 Uin 或 id")
|
||||
@ -391,13 +496,13 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
|
||||
}
|
||||
messageSeq, seqErr := strconv.ParseInt(d["seq"], 10, 64)
|
||||
if err == nil {
|
||||
org = bot.GetMessage(int32(mid))
|
||||
org, _ = bot.db.GetMessageByGlobalID(int32(mid))
|
||||
}
|
||||
if org != nil {
|
||||
elem = &message.ReplyElement{
|
||||
ReplySeq: org["message-id"].(int32),
|
||||
Sender: org["sender"].(message.Sender).Uin,
|
||||
Time: org["time"].(int32),
|
||||
ReplySeq: org.GetAttribute().MessageSeq,
|
||||
Sender: org.GetAttribute().SenderUin,
|
||||
Time: int32(org.GetAttribute().Timestamp),
|
||||
Elements: bot.ConvertStringMessage(customText, isGroup),
|
||||
}
|
||||
if senderErr != nil {
|
||||
@ -419,14 +524,14 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
|
||||
}
|
||||
r = append([]message.IMessageElement{elem}, r...)
|
||||
case err == nil:
|
||||
org := bot.GetMessage(int32(mid))
|
||||
if org != nil {
|
||||
org, err := bot.db.GetMessageByGlobalID(int32(mid))
|
||||
if err == nil {
|
||||
r = append([]message.IMessageElement{
|
||||
&message.ReplyElement{
|
||||
ReplySeq: org["message-id"].(int32),
|
||||
Sender: org["sender"].(message.Sender).Uin,
|
||||
Time: org["time"].(int32),
|
||||
Elements: bot.ConvertStringMessage(org["message"].(string), isGroup),
|
||||
ReplySeq: org.GetAttribute().MessageSeq,
|
||||
Sender: org.GetAttribute().SenderUin,
|
||||
Time: int32(org.GetAttribute().Timestamp),
|
||||
Elements: bot.ConvertContentMessage(org.GetContent(), isGroup),
|
||||
},
|
||||
}, r...)
|
||||
}
|
||||
@ -551,7 +656,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
||||
switch {
|
||||
case customText != "":
|
||||
var elem *message.ReplyElement
|
||||
var org global.MSG
|
||||
var org db.IStoredMessage
|
||||
sender, senderErr := strconv.ParseInt(e.Get("data.[user_id,qq]").String(), 10, 64)
|
||||
if senderErr != nil && err != nil {
|
||||
log.Warnf("警告: 自定义 Reply 元素中必须包含 user_id 或 id")
|
||||
@ -563,13 +668,13 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
||||
}
|
||||
messageSeq, seqErr := strconv.ParseInt(e.Get("data.seq").String(), 10, 64)
|
||||
if err == nil {
|
||||
org = bot.GetMessage(int32(mid))
|
||||
org, _ = bot.db.GetMessageByGlobalID(int32(mid))
|
||||
}
|
||||
if org != nil {
|
||||
elem = &message.ReplyElement{
|
||||
ReplySeq: org["message-id"].(int32),
|
||||
Sender: org["sender"].(message.Sender).Uin,
|
||||
Time: org["time"].(int32),
|
||||
ReplySeq: org.GetAttribute().MessageSeq,
|
||||
Sender: org.GetAttribute().SenderUin,
|
||||
Time: int32(org.GetAttribute().Timestamp),
|
||||
Elements: bot.ConvertStringMessage(customText, isGroup),
|
||||
}
|
||||
if senderErr != nil {
|
||||
@ -591,14 +696,14 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
||||
}
|
||||
r = append([]message.IMessageElement{elem}, r...)
|
||||
case err == nil:
|
||||
org := bot.GetMessage(int32(mid))
|
||||
if org != nil {
|
||||
org, err := bot.db.GetMessageByGlobalID(int32(mid))
|
||||
if err == nil {
|
||||
r = append([]message.IMessageElement{
|
||||
&message.ReplyElement{
|
||||
ReplySeq: org["message-id"].(int32),
|
||||
Sender: org["sender"].(message.Sender).Uin,
|
||||
Time: org["time"].(int32),
|
||||
Elements: bot.ConvertStringMessage(org["message"].(string), isGroup),
|
||||
ReplySeq: org.GetAttribute().MessageSeq,
|
||||
Sender: org.GetAttribute().SenderUin,
|
||||
Time: int32(org.GetAttribute().Timestamp),
|
||||
Elements: bot.ConvertContentMessage(org.GetContent(), isGroup),
|
||||
},
|
||||
}, r...)
|
||||
}
|
||||
@ -654,6 +759,75 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
|
||||
return
|
||||
}
|
||||
|
||||
// ConvertContentMessage 将数据库用的 content 转换为消息元素数组
|
||||
func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []message.IMessageElement) {
|
||||
for _, c := range content {
|
||||
data := c["data"].(global.MSG)
|
||||
switch c["type"] {
|
||||
case "text":
|
||||
r = append(r, message.NewText(data["text"].(string)))
|
||||
case "image":
|
||||
e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, false, group)
|
||||
if err != nil {
|
||||
log.Warnf("make image elem error: %v", err)
|
||||
continue
|
||||
}
|
||||
flash, id := false, int32(0)
|
||||
if t, ok := data["type"]; ok {
|
||||
if t.(string) == "flash" {
|
||||
flash = true
|
||||
}
|
||||
if t.(string) == "show" {
|
||||
id = data["id"].(int32)
|
||||
if id < 40000 || id >= 40006 {
|
||||
id = 40000
|
||||
}
|
||||
}
|
||||
}
|
||||
switch img := e.(type) {
|
||||
case *LocalImageElement:
|
||||
img.Flash = flash
|
||||
img.EffectID = id
|
||||
case *message.GroupImageElement:
|
||||
img.Flash = flash
|
||||
img.EffectID = id
|
||||
img.ImageBizType = message.ImageBizType(data["subType"].(uint32))
|
||||
case *message.FriendImageElement:
|
||||
img.Flash = flash
|
||||
}
|
||||
r = append(r, e)
|
||||
case "at":
|
||||
switch data["subType"].(string) {
|
||||
case "all":
|
||||
r = append(r, message.NewAt(0))
|
||||
case "user":
|
||||
r = append(r, message.NewAt(data["target"].(int64), data["display"].(string)))
|
||||
default:
|
||||
continue
|
||||
}
|
||||
case "redbag":
|
||||
r = append(r, &message.RedBagElement{
|
||||
MsgType: message.RedBagMessageType(data["type"].(int)),
|
||||
Title: data["title"].(string),
|
||||
})
|
||||
case "forward":
|
||||
r = append(r, &message.ForwardElement{
|
||||
ResId: data["id"].(string),
|
||||
})
|
||||
case "face":
|
||||
r = append(r, message.NewFace(data["id"].(int32)))
|
||||
case "video":
|
||||
e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, group)
|
||||
if err != nil {
|
||||
log.Warnf("make image elem error: %v", err)
|
||||
continue
|
||||
}
|
||||
r = append(r, e)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToElement 将解码后的CQCode转换为Element.
|
||||
//
|
||||
// 返回 interface{} 存在三种类型
|
||||
|
@ -2,6 +2,7 @@ package coolq
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/Mrs4s/go-cqhttp/db"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
@ -112,7 +113,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
||||
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
|
||||
id := m.Id
|
||||
if bot.db != nil {
|
||||
id = bot.InsertTempMessage(m.Sender.Uin, m)
|
||||
// id = bot.InsertTempMessage(m.Sender.Uin, m)
|
||||
}
|
||||
log.Infof("收到来自群 %v(%v) 内 %v(%v) 的临时会话消息: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm)
|
||||
tm := global.MSG{
|
||||
@ -178,7 +179,7 @@ func (bot *CQBot) groupMutedEvent(c *client.QQClient, e *client.GroupMuteEvent)
|
||||
|
||||
func (bot *CQBot) groupRecallEvent(c *client.QQClient, e *client.GroupMessageRecalledEvent) {
|
||||
g := c.FindGroup(e.GroupCode)
|
||||
gid := toGlobalID(e.GroupCode, e.MessageId)
|
||||
gid := db.ToGlobalID(e.GroupCode, e.MessageId)
|
||||
log.Infof("群 %v 内 %v 撤回了 %v 的消息: %v.",
|
||||
formatGroupName(g), formatMemberName(g.FindMember(e.OperatorUin)), formatMemberName(g.FindMember(e.AuthorUin)), gid)
|
||||
bot.dispatchEventMessage(global.MSG{
|
||||
@ -295,7 +296,7 @@ func (bot *CQBot) memberTitleUpdatedEvent(c *client.QQClient, e *client.MemberSp
|
||||
|
||||
func (bot *CQBot) friendRecallEvent(c *client.QQClient, e *client.FriendMessageRecalledEvent) {
|
||||
f := c.FindFriend(e.FriendUin)
|
||||
gid := toGlobalID(e.FriendUin, e.MessageId)
|
||||
gid := db.ToGlobalID(e.FriendUin, e.MessageId)
|
||||
if f != nil {
|
||||
log.Infof("好友 %v(%v) 撤回了消息: %v", f.Nickname, f.Uin, gid)
|
||||
} else {
|
||||
@ -472,7 +473,7 @@ func (bot *CQBot) otherClientStatusChangedEvent(c *client.QQClient, e *client.Ot
|
||||
|
||||
func (bot *CQBot) groupEssenceMsg(c *client.QQClient, e *client.GroupDigestEvent) {
|
||||
g := c.FindGroup(e.GroupCode)
|
||||
gid := toGlobalID(e.GroupCode, e.MessageID)
|
||||
gid := db.ToGlobalID(e.GroupCode, e.MessageID)
|
||||
if e.OperationType == 1 {
|
||||
log.Infof(
|
||||
"群 %v 内 %v 将 %v 的消息(%v)设为了精华消息.",
|
||||
|
104
db/database.go
Normal file
104
db/database.go
Normal file
@ -0,0 +1,104 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
type (
|
||||
// IDatabase 数据库操作接口定义
|
||||
IDatabase interface {
|
||||
// Open 初始化数据库
|
||||
Open() error
|
||||
|
||||
// GetMessageByGlobalID 通过 GlobalID 来获取消息
|
||||
GetMessageByGlobalID(int32) (IStoredMessage, error)
|
||||
// GetGroupMessageByGlobalID 通过 GlobalID 来获取群消息
|
||||
GetGroupMessageByGlobalID(int32) (*StoredGroupMessage, error)
|
||||
// GetPrivateMessageByGlobalID 通过 GlobalID 来获取私聊消息
|
||||
GetPrivateMessageByGlobalID(int32) (*StoredPrivateMessage, error)
|
||||
|
||||
// InsertGroupMessage 向数据库写入新的群消息
|
||||
InsertGroupMessage(*StoredGroupMessage) error
|
||||
// InsertPrivateMessage 向数据库写入新的私聊消息
|
||||
InsertPrivateMessage(*StoredPrivateMessage) error
|
||||
}
|
||||
|
||||
IStoredMessage interface {
|
||||
GetID() string
|
||||
GetType() string
|
||||
GetGlobalID() int32
|
||||
GetAttribute() *StoredMessageAttribute
|
||||
GetContent() []global.MSG
|
||||
}
|
||||
|
||||
// StoredGroupMessage 持久化群消息
|
||||
StoredGroupMessage struct {
|
||||
ID string `bson:"_id"`
|
||||
GlobalID int32 `bson:"globalId"`
|
||||
Attribute *StoredMessageAttribute `bson:"attribute"`
|
||||
SubType string `bson:"subType"`
|
||||
QuotedInfo *QuotedInfo `bson:"quotedInfo"`
|
||||
GroupCode int64 `bson:"groupCode"`
|
||||
AnonymousID string `bson:"anonymousId"`
|
||||
Content []global.MSG `bson:"content"`
|
||||
}
|
||||
|
||||
// StoredPrivateMessage 持久化私聊消息
|
||||
StoredPrivateMessage struct {
|
||||
ID string `bson:"_id"`
|
||||
GlobalID int32 `bson:"globalId"`
|
||||
Attribute *StoredMessageAttribute `bson:"attribute"`
|
||||
SubType string `bson:"subType"`
|
||||
QuotedInfo *QuotedInfo `bson:"quotedInfo"`
|
||||
SessionUin int64 `bson:"sessionUin"`
|
||||
TargetUin int64 `bson:"targetUin"`
|
||||
Content []global.MSG `bson:"content"`
|
||||
}
|
||||
|
||||
// StoredMessageAttribute 持久化消息属性
|
||||
StoredMessageAttribute struct {
|
||||
MessageSeq int32 `bson:"messageSeq"`
|
||||
InternalID int32 `bson:"internalId"`
|
||||
SenderUin int64 `bson:"senderUin"`
|
||||
SenderName string `bson:"senderName"`
|
||||
Timestamp int64 `bson:"timestamp"`
|
||||
}
|
||||
|
||||
// QuotedInfo 引用回复
|
||||
QuotedInfo struct {
|
||||
PrevID string `bson:"prevId"`
|
||||
PrevGlobalID int32 `bson:"prevGlobalId"`
|
||||
QuotedContent []global.MSG `bson:"quotedContent"`
|
||||
}
|
||||
|
||||
// MultiDatabase todo
|
||||
MultiDatabase struct {
|
||||
}
|
||||
)
|
||||
|
||||
// ToGlobalID 构建`code`-`msgID`的字符串并返回其CRC32 Checksum的值
|
||||
func ToGlobalID(code int64, msgID int32) int32 {
|
||||
return int32(crc32.ChecksumIEEE([]byte(fmt.Sprintf("%d-%d", code, msgID))))
|
||||
}
|
||||
|
||||
func (m *StoredGroupMessage) GetID() string { return m.ID }
|
||||
|
||||
func (m *StoredGroupMessage) GetType() string { return "group" }
|
||||
|
||||
func (m *StoredGroupMessage) GetGlobalID() int32 { return m.GlobalID }
|
||||
|
||||
func (m *StoredGroupMessage) GetAttribute() *StoredMessageAttribute { return m.Attribute }
|
||||
|
||||
func (m *StoredGroupMessage) GetContent() []global.MSG { return m.Content }
|
||||
|
||||
func (m *StoredPrivateMessage) GetID() string { return m.ID }
|
||||
|
||||
func (m *StoredPrivateMessage) GetType() string { return "private" }
|
||||
|
||||
func (m *StoredPrivateMessage) GetGlobalID() int32 { return m.GlobalID }
|
||||
|
||||
func (m *StoredPrivateMessage) GetAttribute() *StoredMessageAttribute { return m.Attribute }
|
||||
|
||||
func (m *StoredPrivateMessage) GetContent() []global.MSG { return m.Content }
|
116
db/leveldb.go
Normal file
116
db/leveldb.go
Normal file
@ -0,0 +1,116 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/go-cqhttp/global"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"path"
|
||||
)
|
||||
|
||||
type LevelDBImpl struct {
|
||||
db *leveldb.DB
|
||||
}
|
||||
|
||||
const (
|
||||
group byte = 0x0
|
||||
private byte = 0x1
|
||||
)
|
||||
|
||||
func UseLevelDB() IDatabase {
|
||||
gob.Register(StoredMessageAttribute{})
|
||||
gob.Register(QuotedInfo{})
|
||||
gob.Register(global.MSG{})
|
||||
gob.Register(StoredGroupMessage{})
|
||||
gob.Register(StoredPrivateMessage{})
|
||||
return &LevelDBImpl{}
|
||||
}
|
||||
|
||||
func (db *LevelDBImpl) Open() error {
|
||||
p := path.Join("data", "leveldb-v2")
|
||||
d, err := leveldb.OpenFile(p, &opt.Options{
|
||||
WriteBuffer: 128 * opt.KiB,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open level db error")
|
||||
}
|
||||
db.db = d
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *LevelDBImpl) GetMessageByGlobalID(id int32) (IStoredMessage, error) {
|
||||
v, err := db.db.Get(binary.ToBytes(id), nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get value error")
|
||||
}
|
||||
r := binary.NewReader(v)
|
||||
switch r.ReadByte() {
|
||||
case group:
|
||||
g := &StoredGroupMessage{}
|
||||
if err = gob.NewDecoder(bytes.NewReader(r.ReadAvailable())).Decode(g); err != nil {
|
||||
return nil, errors.Wrap(err, "decode message error")
|
||||
}
|
||||
return g, nil
|
||||
case private:
|
||||
p := &StoredPrivateMessage{}
|
||||
if err = gob.NewDecoder(bytes.NewReader(r.ReadAvailable())).Decode(p); err != nil {
|
||||
return nil, errors.Wrap(err, "decode message error")
|
||||
}
|
||||
return p, nil
|
||||
default:
|
||||
return nil, errors.New("unknown message flag")
|
||||
}
|
||||
}
|
||||
|
||||
func (db *LevelDBImpl) GetGroupMessageByGlobalID(id int32) (*StoredGroupMessage, error) {
|
||||
i, err := db.GetMessageByGlobalID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, ok := i.(*StoredGroupMessage)
|
||||
if !ok {
|
||||
return nil, errors.New("message type error")
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (db *LevelDBImpl) GetPrivateMessageByGlobalID(id int32) (*StoredPrivateMessage, error) {
|
||||
i, err := db.GetMessageByGlobalID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, ok := i.(*StoredPrivateMessage)
|
||||
if !ok {
|
||||
return nil, errors.New("message type error")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (db *LevelDBImpl) InsertGroupMessage(msg *StoredGroupMessage) error {
|
||||
buf := global.NewBuffer()
|
||||
defer global.PutBuffer(buf)
|
||||
if err := gob.NewEncoder(buf).Encode(msg); err != nil {
|
||||
return errors.Wrap(err, "encode message error")
|
||||
}
|
||||
err := db.db.Put(binary.ToBytes(msg.GlobalID), binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteByte(group)
|
||||
w.Write(buf.Bytes())
|
||||
}), nil)
|
||||
return errors.Wrap(err, "put data error")
|
||||
}
|
||||
|
||||
func (db *LevelDBImpl) InsertPrivateMessage(msg *StoredPrivateMessage) error {
|
||||
buf := global.NewBuffer()
|
||||
defer global.PutBuffer(buf)
|
||||
if err := gob.NewEncoder(buf).Encode(msg); err != nil {
|
||||
return errors.Wrap(err, "encode message error")
|
||||
}
|
||||
err := db.db.Put(binary.ToBytes(msg.GlobalID), binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteByte(private)
|
||||
w.Write(buf.Bytes())
|
||||
}), nil)
|
||||
return errors.Wrap(err, "put data error")
|
||||
}
|
@ -75,7 +75,7 @@ func (h *httpCtx) Get(s string) gjson.Result {
|
||||
|
||||
func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
var ctx httpCtx
|
||||
contentType := request.Header.Get("Content-Type")
|
||||
contentType := request.Header.Get("Content-SubType")
|
||||
switch request.Method {
|
||||
case http.MethodPost:
|
||||
if strings.Contains(contentType, "application/json") {
|
||||
@ -119,7 +119,7 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request
|
||||
log.Debugf("HTTPServer接收到API调用: %v", action)
|
||||
ret := s.api.callAPI(action, &ctx)
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.Header().Set("Content-SubType", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(writer).Encode(ret)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user