Files
WuKongIM/internal/pusher/handler/event_pushonline.go

248 lines
7.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"context"
"time"
"github.com/WuKongIM/WuKongIM/internal/eventbus"
"github.com/WuKongIM/WuKongIM/internal/options"
"github.com/WuKongIM/WuKongIM/internal/service"
"github.com/WuKongIM/WuKongIM/internal/track"
"github.com/WuKongIM/WuKongIM/internal/types"
"github.com/WuKongIM/WuKongIM/internal/types/pluginproto"
"github.com/WuKongIM/WuKongIM/pkg/wklog"
"github.com/WuKongIM/WuKongIM/pkg/wkutil"
wkproto "github.com/WuKongIM/WuKongIMGoProto"
"go.uber.org/zap"
)
func (h *Handler) pushOnline(ctx *eventbus.PushContext) {
h.processChannelPush(ctx.Events)
}
// 以频道为单位推送消息
func (h *Handler) processChannelPush(events []*eventbus.Event) {
// fakeChannelId, channelType := wkutil.ChannelFromlKey(channelKey)
for _, e := range events {
if options.G.IsSystemUid(e.ToUid) {
continue
}
fromUid := e.Conn.Uid
// 如果发送者是系统账号,则不显示发送者
if options.G.IsSystemUid(fromUid) {
fromUid = ""
}
// 是否是AI
// if fromUid != e.ToUid && h.isAI(e.ToUid) {
// // 处理AI推送
// h.processAIPush(e.ToUid, e)
// }
toConns := eventbus.User.AuthedConnsByUid(e.ToUid)
if len(toConns) == 0 {
continue
}
// 记录消息轨迹
e.Track.Record(track.PositionPushOnline)
sendPacket := e.Frame.(*wkproto.SendPacket)
fakeChannelId := e.ChannelId
channelType := e.ChannelType
for _, toConn := range toConns {
if toConn.Uid == e.Conn.Uid && toConn.NodeId == e.Conn.NodeId && toConn.ConnId == e.Conn.ConnId { // 自己发的不处理
continue
}
recvPacket := &wkproto.RecvPacket{}
recvPacket.Framer = wkproto.Framer{
RedDot: sendPacket.GetRedDot(),
SyncOnce: sendPacket.GetsyncOnce(),
NoPersist: sendPacket.GetNoPersist(),
}
recvPacket.Setting = sendPacket.Setting
recvPacket.MessageID = e.MessageId
recvPacket.MessageSeq = uint32(e.MessageSeq)
recvPacket.ClientMsgNo = sendPacket.ClientMsgNo
recvPacket.StreamNo = sendPacket.StreamNo
recvPacket.StreamId = uint64(e.MessageId)
recvPacket.StreamFlag = e.StreamFlag
recvPacket.FromUID = fromUid
recvPacket.Expire = sendPacket.Expire
recvPacket.ChannelID = sendPacket.ChannelID
recvPacket.ChannelType = sendPacket.ChannelType
recvPacket.Topic = sendPacket.Topic
recvPacket.Timestamp = int32(time.Now().Unix())
recvPacket.ClientSeq = sendPacket.ClientSeq
// 这里需要把channelID改成fromUID 比如A给B发消息B收到的消息channelID应该是A A收到的消息channelID应该是B
recvPacket.ChannelID = sendPacket.ChannelID
if recvPacket.ChannelType == wkproto.ChannelTypePerson &&
recvPacket.ChannelID == toConn.Uid {
recvPacket.ChannelID = recvPacket.FromUID
}
// 红点设置
recvPacket.RedDot = sendPacket.RedDot
if toConn.Uid == recvPacket.FromUID { // 如果是自己则不显示红点
recvPacket.RedDot = false
}
var finalPayload []byte
var err error
// 根据配置决定是否加密消息负载
if !options.G.DisableEncryption {
if len(toConn.AesIV) == 0 || len(toConn.AesKey) == 0 {
h.Error("aesIV or aesKey is empty, cannot encrypt payload",
zap.String("uid", toConn.Uid),
zap.String("deviceId", toConn.DeviceId),
zap.String("channelId", recvPacket.ChannelID),
zap.Uint8("channelType", recvPacket.ChannelType),
)
continue // 跳过此连接的推送
}
finalPayload, err = encryptMessagePayload(sendPacket.Payload, toConn)
if err != nil {
h.Error("加密payload失败",
zap.Error(err),
zap.String("uid", toConn.Uid),
zap.String("channelId", recvPacket.ChannelID),
zap.Uint8("channelType", recvPacket.ChannelType),
)
continue // 跳过此连接的推送
}
} else {
// 如果禁用了加密,则直接使用原始 Payload
finalPayload = sendPacket.Payload
}
recvPacket.Payload = finalPayload // 设置最终的 Payload (可能加密也可能未加密)
// ---- MsgKey 的生成逻辑也需要考虑加密是否禁用 ----
if !options.G.DisableEncryption {
// 只有启用了加密才生成 MsgKey
signStr := recvPacket.VerityString() // VerityString 可能依赖 Payload
msgKey, err := makeMsgKey(signStr, toConn) // makeMsgKey 内部会使用 AES 加密
if err != nil {
h.Error("生成MsgKey失败", zap.Error(err))
continue
}
recvPacket.MsgKey = msgKey
} else {
// 如果禁用了加密,则 MsgKey 为空
recvPacket.MsgKey = ""
}
if !recvPacket.NoPersist { // 只有存储的消息才重试
service.RetryManager.AddRetry(&types.RetryMessage{
ChannelId: fakeChannelId,
ChannelType: channelType,
Uid: toConn.Uid,
ConnId: toConn.ConnId,
FromNode: toConn.NodeId,
MessageId: e.MessageId,
RecvPacket: recvPacket,
})
}
eventbus.User.ConnWrite(toConn, recvPacket)
}
eventbus.User.Advance(e.ToUid)
}
if options.G.Logger.TraceOn {
if len(events) < options.G.Logger.TraceMaxMsgCount { // 消息数小于指定数量才打印,要不然日志太多了
for _, e := range events {
sendPacket := e.Frame.(*wkproto.SendPacket)
// 记录消息轨迹
e.Track.Record(track.PositionPushOnlineEnd)
connCount := eventbus.User.ConnCountByUid(e.ToUid)
h.Trace(e.Track.String(),
"pushOnline",
zap.Int64("messageId", e.MessageId),
zap.Uint64("messageSeq", e.MessageSeq),
zap.Uint64("clientSeq", sendPacket.ClientSeq),
zap.String("clientMsgNo", sendPacket.ClientMsgNo),
zap.String("toUid", e.ToUid),
zap.Int("toConnCount", connCount),
zap.String("conn.uid", e.Conn.Uid),
zap.String("conn.deviceId", e.Conn.DeviceId),
zap.Uint64("conn.nodeId", e.Conn.NodeId),
zap.Int64("conn.connId", e.Conn.ConnId),
)
}
}
}
}
// 处理AI推送
func (h *Handler) processAIPush(uid string, e *eventbus.Event) {
pluginNo, err := h.getAIPluginNo(uid)
if err != nil {
h.Error("获取AI插件编号失败", zap.Error(err), zap.String("uid", uid))
return
}
if len(pluginNo) == 0 {
h.Debug("AI插件编号为空", zap.String("uid", uid))
return
}
pluginObj := service.PluginManager.Plugin(pluginNo)
if pluginObj == nil {
h.Debug("AI插件不存在", zap.String("pluginNo", pluginNo), zap.String("uid", uid))
return
}
sendPacket := e.Frame.(*wkproto.SendPacket)
err = pluginObj.Receive(context.TODO(), &pluginproto.RecvPacket{
FromUid: e.Conn.Uid,
ToUid: uid,
ChannelId: sendPacket.ChannelID,
ChannelType: uint32(sendPacket.ChannelType),
Payload: sendPacket.Payload,
})
if err != nil {
h.Error("AI插件回复失败", zap.Error(err), zap.String("pluginNo", pluginNo), zap.String("uid", uid))
}
}
// 是否是AI
func (h *Handler) isAI(uid string) bool {
return service.PluginManager.UserIsAI(uid)
}
// 获取用户AI插件编号
func (h *Handler) getAIPluginNo(uid string) (string, error) {
return service.PluginManager.GetUserPluginNo(uid)
}
// 加密消息
func encryptMessagePayload(payload []byte, conn *eventbus.Conn) ([]byte, error) {
aesKey, aesIV := conn.AesKey, conn.AesIV
// 加密payload
payloadEnc, err := wkutil.AesEncryptPkcs7Base64(payload, aesKey, aesIV)
if err != nil {
return nil, err
}
return payloadEnc, nil
}
func makeMsgKey(signStr string, conn *eventbus.Conn) (string, error) {
aesKey, aesIV := conn.AesKey, conn.AesIV
// 生成MsgKey
msgKeyBytes, err := wkutil.AesEncryptPkcs7Base64([]byte(signStr), []byte(aesKey), []byte(aesIV))
if err != nil {
wklog.Error("生成MsgKey失败", zap.Error(err))
return "", err
}
return wkutil.MD5(string(msgKeyBytes)), nil
}