diff --git a/client/c2c_processor.go b/client/c2c_processor.go
index ce69979b..87ac410e 100644
--- a/client/c2c_processor.go
+++ b/client/c2c_processor.go
@@ -5,25 +5,31 @@ import (
"sync/atomic"
"time"
+ "github.com/Mrs4s/MiraiGo/binary"
+
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
-var c2cDecoders = map[int32]func(*QQClient, *msg.Message){
+var c2cDecoders = map[int32]func(*QQClient, *msg.Message, *incomingPacketInfo){
33: troopAddMemberBroadcastDecoder,
35: troopSystemMessageDecoder, 36: troopSystemMessageDecoder, 37: troopSystemMessageDecoder,
45: troopSystemMessageDecoder, 46: troopSystemMessageDecoder, 84: troopSystemMessageDecoder,
85: troopSystemMessageDecoder, 86: troopSystemMessageDecoder, 87: troopSystemMessageDecoder,
140: tempSessionDecoder, 141: tempSessionDecoder,
- 166: privateMessageDecoder, 208: privateMessageDecoder,
+ 9: privateMessageDecoder, 10: privateMessageDecoder, 31: privateMessageDecoder,
+ 79: privateMessageDecoder, 97: privateMessageDecoder, 120: privateMessageDecoder,
+ 132: privateMessageDecoder, 133: privateMessageDecoder, 166: privateMessageDecoder,
+ 167: privateMessageDecoder,
+ 208: privatePttDecoder,
187: systemMessageDecoder, 188: systemMessageDecoder, 189: systemMessageDecoder,
190: systemMessageDecoder, 191: systemMessageDecoder,
529: msgType0x211Decoder,
}
-func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse) {
+func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *incomingPacketInfo) {
c.syncCookie = rsp.SyncCookie
c.pubAccountCookie = rsp.PubAccountCookie
c.msgCtrlBuf = rsp.MsgCtrlBuf
@@ -50,11 +56,17 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse) {
}
strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.GetFromUin(), pMsg.Head.GetToUin(), pMsg.Head.GetMsgSeq(), pMsg.Head.GetMsgUid())
if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Minute*5); ok {
+ c.Debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid())
continue
}
c.msgSvcCache.Add(strKey, "", time.Minute*5)
+ if info.Params.bool("init") {
+ continue
+ }
if decoder, ok := c2cDecoders[pMsg.Head.GetMsgType()]; ok {
- decoder(c, pMsg)
+ decoder(c, pMsg, info)
+ } else {
+ c.Debug("unknown msg type on c2c processor: %v", pMsg.Head.GetMsgType())
}
/*
switch pMsg.Head.GetMsgType() {
@@ -144,44 +156,63 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse) {
_, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems))
if rsp.GetSyncFlag() != msg.SyncFlag_STOP {
c.Debug("continue sync with flag: %v", rsp.SyncFlag.String())
- _, _ = c.sendAndWait(c.buildGetMessageRequestPacket(rsp.GetSyncFlag(), time.Now().Unix()))
+ seq, pkt := c.buildGetMessageRequestPacket(rsp.GetSyncFlag(), time.Now().Unix())
+ _, _ = c.sendAndWait(seq, pkt, info.Params)
}
}
-func privateMessageDecoder(c *QQClient, pMsg *msg.Message) {
- if pMsg.Head.GetFromUin() == c.Uin {
- for {
- frdSeq := atomic.LoadInt32(&c.friendSeq)
- if frdSeq < pMsg.Head.GetMsgSeq() {
- if atomic.CompareAndSwapInt32(&c.friendSeq, frdSeq, pMsg.Head.GetMsgSeq()) {
+func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
+ switch pMsg.Head.GetC2CCmd() {
+ case 11, 175: // friend msg
+ if pMsg.Head.GetFromUin() == c.Uin {
+ for {
+ frdSeq := atomic.LoadInt32(&c.friendSeq)
+ if frdSeq < pMsg.Head.GetMsgSeq() {
+ if atomic.CompareAndSwapInt32(&c.friendSeq, frdSeq, pMsg.Head.GetMsgSeq()) {
+ break
+ }
+ } else {
break
}
- } else {
- break
}
}
+ if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil {
+ return
+ }
+ c.dispatchFriendMessage(c.parsePrivateMessage(pMsg))
+ default:
+ c.Debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd())
}
- if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil {
+}
+
+func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
+ if pMsg.Body == nil || pMsg.Body.RichText == nil || pMsg.Body.RichText.Ptt == nil {
return
}
+ if len(pMsg.Body.RichText.Ptt.Reserve) != 0 {
+ // m := binary.NewReader(pMsg.Body.RichText.Ptt.Reserve[1:]).ReadTlvMap(1)
+ // T3 -> timestamp T8 -> voiceType T9 -> voiceLength T10 -> PbReserveStruct
+ }
c.dispatchFriendMessage(c.parsePrivateMessage(pMsg))
}
-func tempSessionDecoder(c *QQClient, pMsg *msg.Message) {
- if pMsg.Head.C2CTmpMsgHead == nil {
+func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
+ if pMsg.Head.C2CTmpMsgHead == nil || pMsg.Body == nil {
return
}
- group := c.FindGroupByUin(pMsg.Head.C2CTmpMsgHead.GetGroupUin())
- if group == nil {
- return
+ if pMsg.Head.GetMsgType() == 529 && pMsg.Head.GetC2CCmd() == 6 {
+ group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GetGroupCode())
+ if group == nil {
+ return
+ }
+ if pMsg.Head.GetFromUin() == c.Uin {
+ return
+ }
+ c.dispatchTempMessage(c.parseTempMessage(pMsg))
}
- if pMsg.Head.GetFromUin() == c.Uin {
- return
- }
- c.dispatchTempMessage(c.parseTempMessage(pMsg))
}
-func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message) {
+func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
groupJoinLock.Lock()
defer groupJoinLock.Unlock()
group := c.FindGroupByUin(pMsg.Head.GetFromUin())
@@ -207,16 +238,27 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message) {
}
}
-func systemMessageDecoder(c *QQClient, pMsg *msg.Message) {
+func systemMessageDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
_, pkt := c.buildSystemMsgNewFriendPacket()
_ = c.send(pkt)
}
-func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message) {
- c.exceptAndDispatchGroupSysMsg()
+func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info *incomingPacketInfo) {
+ if !info.Params.bool("used_reg_proxy") && pMsg.Head.GetMsgType() != 85 && pMsg.Head.GetMsgType() != 36 {
+ c.exceptAndDispatchGroupSysMsg()
+ }
+ if len(pMsg.Body.GetMsgContent()) == 0 {
+ return
+ }
+ reader := binary.NewReader(pMsg.GetBody().GetMsgContent())
+ groupCode := uint32(reader.ReadInt32())
+ if info := c.FindGroup(int64(groupCode)); info != nil && pMsg.Head.GetGroupName() != "" && info.Name != pMsg.Head.GetGroupName() {
+ c.Debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GetGroupName())
+ info.Name = pMsg.Head.GetGroupName()
+ }
}
-func msgType0x211Decoder(c *QQClient, pMsg *msg.Message) {
+func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
sub4 := msg.SubMsgType0X4Body{}
if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil {
err = errors.Wrap(err, "unmarshal sub msg 0x4 error")
diff --git a/client/client.go b/client/client.go
index 9f770fd9..010e26e7 100644
--- a/client/client.go
+++ b/client/client.go
@@ -28,7 +28,7 @@ import (
var json = jsoniter.ConfigFastest
-//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]func(i interface{}, err error)"
+//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]*handlerInfo"
type QQClient struct {
Uin int64
@@ -121,32 +121,36 @@ type loginSigInfo struct {
pt4TokenMap map[string][]byte
}
-var decoders = map[string]func(*QQClient, uint16, []byte) (interface{}, error){
- "wtlogin.login": decodeLoginResponse,
- "wtlogin.exchange_emp": decodeExchangeEmpResponse,
- "StatSvc.register": decodeClientRegisterResponse,
- "StatSvc.ReqMSFOffline": decodeMSFOfflinePacket,
- "MessageSvc.PushNotify": decodeSvcNotify,
- "OnlinePush.ReqPush": decodeOnlinePushReqPacket,
- "OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket,
- "ConfigPushSvc.PushReq": decodePushReqPacket,
- "MessageSvc.PbGetMsg": decodeMessageSvcPacket,
- "MessageSvc.PushForceOffline": decodeForceOfflinePacket,
- "PbMessageSvc.PbMsgWithDraw": decodeMsgWithDrawResponse,
- "friendlist.getFriendGroupList": decodeFriendGroupListResponse,
- "friendlist.GetTroopListReqV2": decodeGroupListResponse,
- "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse,
- "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse,
- "PttStore.GroupPttUp": decodeGroupPttStoreResponse,
- "LongConn.OffPicUp": decodeOffPicUpResponse,
- "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket,
- "ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket,
- "OidbSvc.0xe07_0": decodeImageOcrResponse,
- "OidbSvc.0xd79": decodeWordSegmentation,
- "OidbSvc.0x990": decodeTranslateResponse,
- "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
- "LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
- "PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500": decodePrivatePttStoreResponse,
+type handlerInfo struct {
+ fun func(i interface{}, err error)
+ params requestParams
+}
+
+var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interface{}, error){
+ "wtlogin.login": decodeLoginResponse,
+ "wtlogin.exchange_emp": decodeExchangeEmpResponse,
+ "StatSvc.register": decodeClientRegisterResponse,
+ "StatSvc.ReqMSFOffline": decodeMSFOfflinePacket,
+ "MessageSvc.PushNotify": decodeSvcNotify,
+ "OnlinePush.ReqPush": decodeOnlinePushReqPacket,
+ "OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket,
+ "ConfigPushSvc.PushReq": decodePushReqPacket,
+ "MessageSvc.PbGetMsg": decodeMessageSvcPacket,
+ "MessageSvc.PushForceOffline": decodeForceOfflinePacket,
+ "PbMessageSvc.PbMsgWithDraw": decodeMsgWithDrawResponse,
+ "friendlist.getFriendGroupList": decodeFriendGroupListResponse,
+ "friendlist.GetTroopListReqV2": decodeGroupListResponse,
+ "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse,
+ "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse,
+ "PttStore.GroupPttUp": decodeGroupPttStoreResponse,
+ "LongConn.OffPicUp": decodeOffPicUpResponse,
+ "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket,
+ "ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket,
+ "OidbSvc.0xe07_0": decodeImageOcrResponse,
+ "OidbSvc.0xd79": decodeWordSegmentation,
+ "OidbSvc.0x990": decodeTranslateResponse,
+ "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
+ "LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
}
func init() {
@@ -304,6 +308,9 @@ func (c *QQClient) RequestSMS() bool {
}
func (c *QQClient) init() {
+ if len(c.g) == 0 {
+ c.Warning("device lock is disable. http api may fail.")
+ }
c.Online = true
_ = c.registerClient()
c.groupSysMsgCache, _ = c.GetGroupSystemMessages()
@@ -311,7 +318,8 @@ func (c *QQClient) init() {
go c.doHeartbeat()
}
_ = c.RefreshStatus()
- _, _ = c.SyncSessions()
+ seq, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix())
+ _, _ = c.sendAndWait(seq, pkt, requestParams{"used_reg_proxy": true, "init": true})
c.stat.once.Do(func() {
c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) {
c.stat.MessageReceived++
@@ -629,7 +637,7 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) {
}
func (c *QQClient) getSKey() string {
- if c.sigInfo.sKeyExpiredTime < time.Now().Unix() {
+ if c.sigInfo.sKeyExpiredTime < time.Now().Unix() && len(c.g) > 0 {
c.Debug("skey expired. refresh...")
_, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket())
}
@@ -793,7 +801,7 @@ func (c *QQClient) send(pkt []byte) error {
return errors.Wrap(err, "Packet failed to send")
}
-func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) {
+func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...requestParams) (interface{}, error) {
type T struct {
Response interface{}
Error error
@@ -806,12 +814,20 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) {
ch := make(chan T)
defer close(ch)
- c.handlers.Store(seq, func(i interface{}, err error) {
+
+ p := func() requestParams {
+ if len(params) == 0 {
+ return nil
+ }
+ return params[0]
+ }()
+
+ c.handlers.Store(seq, &handlerInfo{fun: func(i interface{}, err error) {
ch <- T{
Response: i,
Error: err,
}
- })
+ }, params: p})
retry := 0
for true {
@@ -913,18 +929,28 @@ func (c *QQClient) netLoop() {
if decoder, ok := decoders[pkt.CommandName]; ok {
// found predefined decoder
- rsp, err := decoder(c, pkt.SequenceId, payload)
+ info, ok := c.handlers.LoadAndDelete(pkt.SequenceId)
+ rsp, err := decoder(c, &incomingPacketInfo{
+ SequenceId: pkt.SequenceId,
+ CommandName: pkt.CommandName,
+ Params: func() requestParams {
+ if !ok {
+ return nil
+ }
+ return info.params
+ }(),
+ }, payload)
if err != nil {
c.Debug("decode pkt %v error: %+v", pkt.CommandName, err)
}
- if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok {
- f(rsp, err)
+ if ok {
+ info.fun(rsp, err)
} else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait
f.(func(interface{}, error))(rsp, err)
}
} else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok {
// does not need decoder
- f(nil, nil)
+ f.fun(nil, nil)
} else {
c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId)
}
diff --git a/client/decoders.go b/client/decoders.go
index 26efd817..caaa341b 100644
--- a/client/decoders.go
+++ b/client/decoders.go
@@ -4,7 +4,6 @@ import (
"crypto/md5"
"encoding/hex"
"fmt"
- log "github.com/sirupsen/logrus"
"net"
"strconv"
"strings"
@@ -33,7 +32,7 @@ var (
)
// wtlogin.login
-func decodeLoginResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
reader := binary.NewReader(payload)
reader.ReadUInt16() // sub command
t := reader.ReadByte()
@@ -182,7 +181,7 @@ func decodeLoginResponse(c *QQClient, _ uint16, payload []byte) (interface{}, er
}
// StatSvc.register
-func decodeClientRegisterResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeClientRegisterResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -199,7 +198,7 @@ func decodeClientRegisterResponse(c *QQClient, _ uint16, payload []byte) (interf
}
// wtlogin.exchange_emp
-func decodeExchangeEmpResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeExchangeEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
reader := binary.NewReader(payload)
cmd := reader.ReadUInt16()
t := reader.ReadByte()
@@ -216,7 +215,7 @@ func decodeExchangeEmpResponse(c *QQClient, _ uint16, payload []byte) (interface
}
// ConfigPushSvc.PushReq
-func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodePushReqPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -289,24 +288,24 @@ func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, er
}
// MessageSvc.PbGetMsg
-func decodeMessageSvcPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMessageSvcPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := msg.GetMessageResponse{}
err := proto.Unmarshal(payload, &rsp)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
- c.c2cMessageSyncProcessor(&rsp)
+ c.c2cMessageSyncProcessor(&rsp, info)
return nil, nil
}
// MessageSvc.PushNotify
-func decodeSvcNotify(c *QQClient, _ uint16, _ []byte) (interface{}, error) {
+func decodeSvcNotify(c *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) {
_, err := c.sendAndWait(c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix()))
return nil, err
}
// SummaryCard.ReqSummaryCard
-func decodeSummaryCardResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -355,7 +354,7 @@ func decodeSummaryCardResponse(_ *QQClient, _ uint16, payload []byte) (interface
}
// friendlist.getFriendGroupList
-func decodeFriendGroupListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeFriendGroupListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion3{}
@@ -381,7 +380,7 @@ func decodeFriendGroupListResponse(_ *QQClient, _ uint16, payload []byte) (inter
}
// friendlist.GetTroopListReqV2
-func decodeGroupListResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupListResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion3{}
@@ -415,7 +414,7 @@ func decodeGroupListResponse(c *QQClient, _ uint16, payload []byte) (interface{}
}
// friendlist.GetTroopMemberListReq
-func decodeGroupMemberListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupMemberListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion3{}
@@ -451,7 +450,7 @@ func decodeGroupMemberListResponse(_ *QQClient, _ uint16, payload []byte) (inter
}
// group_member_card.get_group_member_card_info
-func decodeGroupMemberInfoResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupMemberInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := pb.GroupMemberRspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -484,7 +483,7 @@ func decodeGroupMemberInfoResponse(c *QQClient, _ uint16, payload []byte) (inter
}
// LongConn.OffPicUp
-func decodeOffPicUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOffPicUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := cmd0x352.RspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -522,7 +521,7 @@ func decodeOffPicUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
}
// OnlinePush.ReqPush
-func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interface{}, error) {
+func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -531,7 +530,7 @@ func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interfa
msgInfos := []jce.PushMessageInfo{}
uin := jr.ReadInt64(0)
jr.ReadSlice(&msgInfos, 2)
- _ = c.send(c.buildDeleteOnlinePushPacket(uin, seq, msgInfos))
+ _ = c.send(c.buildDeleteOnlinePushPacket(uin, info.SequenceId, msgInfos))
for _, m := range msgInfos {
k := fmt.Sprintf("%v%v%v", m.MsgSeq, m.MsgTime, m.MsgUid)
if _, ok := c.onlinePushCache.Get(k); ok {
@@ -657,7 +656,6 @@ func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interfa
if m.DelFriend != nil {
frdUin := m.DelFriend.Uins[0]
if frd := c.FindFriend(int64(frdUin)); frd != nil {
- log.Infof("好友被删除: %v(%v)", frd.Nickname, frd.Uin)
if err := c.ReloadFriendList(); err != nil {
return nil, errors.Wrap(err, "failed to reload friend list")
}
@@ -722,7 +720,7 @@ func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interfa
}
// OnlinePush.PbPushTransMsg
-func decodeOnlinePushTransPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
info := msg.TransMsgInfo{}
err := proto.Unmarshal(payload, &info)
if err != nil {
@@ -829,7 +827,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ uint16, payload []byte) (interfa
}
// ProfileService.Pb.ReqSystemMsgNew.Friend
-func decodeSystemMsgFriendPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeSystemMsgFriendPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := structmsg.RspSystemMsgNew{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -851,7 +849,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ uint16, payload []byte) (interfa
}
// MessageSvc.PushForceOffline
-func decodeForceOfflinePacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeForceOfflinePacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -865,7 +863,7 @@ func decodeForceOfflinePacket(c *QQClient, _ uint16, payload []byte) (interface{
}
// StatSvc.ReqMSFOffline
-func decodeMSFOfflinePacket(c *QQClient, _ uint16, _ []byte) (interface{}, error) {
+func decodeMSFOfflinePacket(c *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) {
c.lastLostMsg = "服务器端强制下线."
c.NetLooping = false
c.Online = false
@@ -873,7 +871,7 @@ func decodeMSFOfflinePacket(c *QQClient, _ uint16, _ []byte) (interface{}, error
}
// OidbSvc.0xd79
-func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeWordSegmentation(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := &oidb.D79RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -889,7 +887,7 @@ func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{},
}
// OidbSvc.0xe07_0
-func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeImageOcrResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.DE07RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -926,7 +924,7 @@ func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
}
// LightAppSvc.mini_app_info.GetAppInfoById
-func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := qweb.QWebRsp{}
rsp := qweb.GetAppInfoByIdRsp{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -941,6 +939,6 @@ func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
return rsp.AppInfo, nil
}
-func ignoreDecoder(_ *QQClient, _ uint16, _ []byte) (interface{}, error) {
+func ignoreDecoder(_ *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) {
return nil, nil
}
diff --git a/client/global.go b/client/global.go
index 7f83313a..89e66ef4 100644
--- a/client/global.go
+++ b/client/global.go
@@ -24,94 +24,104 @@ import (
"google.golang.org/protobuf/proto"
)
-type DeviceInfo struct {
- Display []byte
- Product []byte
- Device []byte
- Board []byte
- Brand []byte
- Model []byte
- Bootloader []byte
- FingerPrint []byte
- BootId []byte
- ProcVersion []byte
- BaseBand []byte
- SimInfo []byte
- OSType []byte
- MacAddress []byte
- IpAddress []byte
- WifiBSSID []byte
- WifiSSID []byte
- IMSIMd5 []byte
- IMEI string
- AndroidId []byte
- APN []byte
- VendorName []byte
- VendorOSName []byte
- Guid []byte
- TgtgtKey []byte
- Protocol ClientProtocol
- Version *Version
-}
+type (
+ DeviceInfo struct {
+ Display []byte
+ Product []byte
+ Device []byte
+ Board []byte
+ Brand []byte
+ Model []byte
+ Bootloader []byte
+ FingerPrint []byte
+ BootId []byte
+ ProcVersion []byte
+ BaseBand []byte
+ SimInfo []byte
+ OSType []byte
+ MacAddress []byte
+ IpAddress []byte
+ WifiBSSID []byte
+ WifiSSID []byte
+ IMSIMd5 []byte
+ IMEI string
+ AndroidId []byte
+ APN []byte
+ VendorName []byte
+ VendorOSName []byte
+ Guid []byte
+ TgtgtKey []byte
+ Protocol ClientProtocol
+ Version *Version
+ }
-type Version struct {
- Incremental []byte
- Release []byte
- CodeName []byte
- Sdk uint32
-}
+ Version struct {
+ Incremental []byte
+ Release []byte
+ CodeName []byte
+ Sdk uint32
+ }
-type DeviceInfoFile struct {
- Display string `json:"display"`
- Product string `json:"product"`
- Device string `json:"device"`
- Board string `json:"board"`
- Model string `json:"model"`
- FingerPrint string `json:"finger_print"`
- BootId string `json:"boot_id"`
- ProcVersion string `json:"proc_version"`
- Protocol int `json:"protocol"` // 0: Pad 1: Phone 2: Watch
- IMEI string `json:"imei"`
- Brand string `json:"brand"`
- Bootloader string `json:"bootloader"`
- BaseBand string `json:"base_band"`
- Version *VersionFile `json:"version"`
- SimInfo string `json:"sim_info"`
- OsType string `json:"os_type"`
- MacAddress string `json:"mac_address"`
- IpAddress []int32 `json:"ip_address"`
- WifiBSSID string `json:"wifi_bssid"`
- WifiSSID string `json:"wifi_ssid"`
- ImsiMd5 string `json:"imsi_md5"`
- AndroidId string `json:"android_id"`
- Apn string `json:"apn"`
- VendorName string `json:"vendor_name"`
- VendorOSName string `json:"vendor_os_name"`
-}
+ DeviceInfoFile struct {
+ Display string `json:"display"`
+ Product string `json:"product"`
+ Device string `json:"device"`
+ Board string `json:"board"`
+ Model string `json:"model"`
+ FingerPrint string `json:"finger_print"`
+ BootId string `json:"boot_id"`
+ ProcVersion string `json:"proc_version"`
+ Protocol int `json:"protocol"` // 0: Pad 1: Phone 2: Watch
+ IMEI string `json:"imei"`
+ Brand string `json:"brand"`
+ Bootloader string `json:"bootloader"`
+ BaseBand string `json:"base_band"`
+ Version *VersionFile `json:"version"`
+ SimInfo string `json:"sim_info"`
+ OsType string `json:"os_type"`
+ MacAddress string `json:"mac_address"`
+ IpAddress []int32 `json:"ip_address"`
+ WifiBSSID string `json:"wifi_bssid"`
+ WifiSSID string `json:"wifi_ssid"`
+ ImsiMd5 string `json:"imsi_md5"`
+ AndroidId string `json:"android_id"`
+ Apn string `json:"apn"`
+ VendorName string `json:"vendor_name"`
+ VendorOSName string `json:"vendor_os_name"`
+ }
-type VersionFile struct {
- Incremental string `json:"incremental"`
- Release string `json:"release"`
- Codename string `json:"codename"`
- Sdk uint32 `json:"sdk"`
-}
+ VersionFile struct {
+ Incremental string `json:"incremental"`
+ Release string `json:"release"`
+ Codename string `json:"codename"`
+ Sdk uint32 `json:"sdk"`
+ }
-type groupMessageBuilder struct {
- MessageSlices []*msg.Message
-}
+ groupMessageBuilder struct {
+ MessageSlices []*msg.Message
+ }
-type versionInfo struct {
- ApkSign []byte
- ApkId string
- SortVersionName string
- SdkVersion string
- AppId uint32
- BuildTime uint32
- SSOVersion uint32
- MiscBitmap uint32
- SubSigmap uint32
- MainSigMap uint32
-}
+ versionInfo struct {
+ ApkSign []byte
+ ApkId string
+ SortVersionName string
+ SdkVersion string
+ AppId uint32
+ BuildTime uint32
+ SSOVersion uint32
+ MiscBitmap uint32
+ SubSigmap uint32
+ MainSigMap uint32
+ }
+
+ incomingPacketInfo struct {
+ CommandName string
+ SequenceId uint16
+ Params requestParams
+ }
+
+ requestParams map[string]interface{}
+)
// default
var SystemDeviceInfo = &DeviceInfo{
@@ -577,6 +587,17 @@ func genLongTemplate(resId, brief string, ts int64) *message.ServiceElement {
}
}
+func (p requestParams) bool(k string) bool {
+ if p == nil {
+ return false
+ }
+ i, ok := p[k]
+ if !ok {
+ return false
+ }
+ return i.(bool)
+}
+
func (c *QQClient) packOIDBPackage(cmd, serviceType int32, body []byte) []byte {
pkg := &oidb.OIDBSSOPkg{
Command: cmd,
diff --git a/client/group_file.go b/client/group_file.go
index 04f0207f..d0aac56e 100644
--- a/client/group_file.go
+++ b/client/group_file.go
@@ -8,6 +8,7 @@ import (
"math/rand"
"os"
"runtime/debug"
+ "sync"
"google.golang.org/protobuf/proto"
@@ -54,6 +55,8 @@ type (
}
)
+var fileSingleFlight = sync.Map{}
+
func init() {
decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response
decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response
@@ -156,11 +159,22 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderId string) ([]*GroupFile, []*G
}
func (fs *GroupFileSystem) UploadFile(p, name, folderId string) error {
+ // 同文件等待其他线程上传
+ if wg, ok := fileSingleFlight.Load(p); ok {
+ wg.(*sync.WaitGroup).Wait()
+ } else {
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+ fileSingleFlight.Store(p, wg)
+ defer wg.Done()
+ defer fileSingleFlight.Delete(p)
+ }
+
file, err := os.OpenFile(p, os.O_RDONLY, 0666)
if err != nil {
return errors.Wrap(err, "open file error")
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
md5Hash, size := utils.ComputeMd5AndLength(file)
_, _ = file.Seek(0, io.SeekStart)
sha1H := sha1.New()
@@ -387,7 +401,7 @@ func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId
return seq, packet
}
-func decodeOIDB6d81Response(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOIDB6d81Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D8RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -400,7 +414,7 @@ func decodeOIDB6d81Response(c *QQClient, _ uint16, payload []byte) (interface{},
}
// OidbSvc.0x6d6_2
-func decodeOIDB6d62Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOIDB6d62Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -417,7 +431,7 @@ func decodeOIDB6d62Response(_ *QQClient, _ uint16, payload []byte) (interface{},
return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil
}
-func decodeOIDB6d63Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOIDB6d63Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -432,7 +446,7 @@ func decodeOIDB6d63Response(_ *QQClient, _ uint16, payload []byte) (interface{},
return rsp.DeleteFileRsp.ClientWording, nil
}
-func decodeOIDB6d60Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOIDB6d60Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
diff --git a/client/group_info.go b/client/group_info.go
index 0655748c..8109e99e 100644
--- a/client/group_info.go
+++ b/client/group_info.go
@@ -183,7 +183,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
}
// SummaryCard.ReqSearch
-func decodeGroupSearchResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -219,7 +219,7 @@ func decodeGroupSearchResponse(_ *QQClient, _ uint16, payload []byte) (interface
}
// OidbSvc.0x88d_0
-func decodeGroupInfoResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D88DRspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
diff --git a/client/group_msg.go b/client/group_msg.go
index a3951b2b..baf58b8a 100644
--- a/client/group_msg.go
+++ b/client/group_msg.go
@@ -304,7 +304,7 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byt
}
// OnlinePush.PbPushGroupMsg
-func decodeGroupMessagePacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupMessagePacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkt := msg.PushMessagePacket{}
err := proto.Unmarshal(payload, &pkt)
if err != nil {
@@ -345,7 +345,7 @@ func decodeGroupMessagePacket(c *QQClient, _ uint16, payload []byte) (interface{
return nil, nil
}
-func decodeMsgSendResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMsgSendResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := msg.SendMessageResponse{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -356,7 +356,7 @@ func decodeMsgSendResponse(c *QQClient, _ uint16, payload []byte) (interface{},
return nil, nil
}
-func decodeGetGroupMsgResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGetGroupMsgResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := msg.GetGroupMsgResp{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -377,7 +377,7 @@ func decodeGetGroupMsgResponse(c *QQClient, _ uint16, payload []byte) (interface
return ret, nil
}
-func decodeAtAllRemainResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeAtAllRemainResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D8A7RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
@@ -573,7 +573,7 @@ func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand
}
// OidbSvc.0xeac_1/2
-func decodeEssenceMsgResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeEssenceMsgResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := &oidb.EACRspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
diff --git a/client/handler_map_gen.go b/client/handler_map_gen.go
index 2d9f5b8c..d88a52fb 100644
--- a/client/handler_map_gen.go
+++ b/client/handler_map_gen.go
@@ -70,9 +70,7 @@ type readOnlyHandlerMap struct {
// expunged is an arbitrary pointer that marks entries which have been deleted
// from the dirty map.
-var expungedHandlerMap = unsafe.Pointer(new(func(i interface{}, err error,
-
-)))
+var expungedHandlerMap = unsafe.Pointer(new(*handlerInfo))
// An entry is a slot in the map corresponding to a particular key.
type entryHandlerMap struct {
@@ -97,18 +95,14 @@ type entryHandlerMap struct {
p unsafe.Pointer // *interface{}
}
-func newEntryHandlerMap(i func(i interface{}, err error,
-
-)) *entryHandlerMap {
+func newEntryHandlerMap(i *handlerInfo) *entryHandlerMap {
return &entryHandlerMap{p: unsafe.Pointer(&i)}
}
// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
-func (m *HandlerMap) Load(key uint16) (value func(i interface{}, err error,
-
-), ok bool) {
+func (m *HandlerMap) Load(key uint16) (value *handlerInfo, ok bool) {
read, _ := m.read.Load().(readOnlyHandlerMap)
e, ok := read.m[key]
if !ok && read.amended {
@@ -133,22 +127,16 @@ func (m *HandlerMap) Load(key uint16) (value func(i interface{}, err error,
return e.load()
}
-func (e *entryHandlerMap) load() (value func(i interface{}, err error,
-
-), ok bool) {
+func (e *entryHandlerMap) load() (value *handlerInfo, ok bool) {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expungedHandlerMap {
return value, false
}
- return *(*func(i interface{}, err error,
-
- ))(p), true
+ return *(**handlerInfo)(p), true
}
// Store sets the value for a key.
-func (m *HandlerMap) Store(key uint16, value func(i interface{}, err error,
-
-)) {
+func (m *HandlerMap) Store(key uint16, value *handlerInfo) {
read, _ := m.read.Load().(readOnlyHandlerMap)
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
@@ -181,9 +169,7 @@ func (m *HandlerMap) Store(key uint16, value func(i interface{}, err error,
//
// If the entry is expunged, tryStore returns false and leaves the entry
// unchanged.
-func (e *entryHandlerMap) tryStore(i *func(i interface{}, err error,
-
-)) bool {
+func (e *entryHandlerMap) tryStore(i **handlerInfo) bool {
for {
p := atomic.LoadPointer(&e.p)
if p == expungedHandlerMap {
@@ -206,20 +192,14 @@ func (e *entryHandlerMap) unexpungeLocked() (wasExpunged bool) {
// storeLocked unconditionally stores a value to the entry.
//
// The entry must be known not to be expunged.
-func (e *entryHandlerMap) storeLocked(i *func(i interface{}, err error,
-
-)) {
+func (e *entryHandlerMap) storeLocked(i **handlerInfo) {
atomic.StorePointer(&e.p, unsafe.Pointer(i))
}
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
-func (m *HandlerMap) LoadOrStore(key uint16, value func(i interface{}, err error,
-
-)) (actual func(i interface{}, err error,
-
-), loaded bool) {
+func (m *HandlerMap) LoadOrStore(key uint16, value *handlerInfo) (actual *handlerInfo, loaded bool) {
// Avoid locking if it's a clean hit.
read, _ := m.read.Load().(readOnlyHandlerMap)
if e, ok := read.m[key]; ok {
@@ -259,19 +239,13 @@ func (m *HandlerMap) LoadOrStore(key uint16, value func(i interface{}, err error
//
// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and
// returns with ok==false.
-func (e *entryHandlerMap) tryLoadOrStore(i func(i interface{}, err error,
-
-)) (actual func(i interface{}, err error,
-
-), loaded, ok bool) {
+func (e *entryHandlerMap) tryLoadOrStore(i *handlerInfo) (actual *handlerInfo, loaded, ok bool) {
p := atomic.LoadPointer(&e.p)
if p == expungedHandlerMap {
return actual, false, false
}
if p != nil {
- return *(*func(i interface{}, err error,
-
- ))(p), true, true
+ return *(**handlerInfo)(p), true, true
}
// Copy the interface after the first load to make this method more amenable
@@ -287,18 +261,14 @@ func (e *entryHandlerMap) tryLoadOrStore(i func(i interface{}, err error,
return actual, false, false
}
if p != nil {
- return *(*func(i interface{}, err error,
-
- ))(p), true, true
+ return *(**handlerInfo)(p), true, true
}
}
}
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
-func (m *HandlerMap) LoadAndDelete(key uint16) (value func(i interface{}, err error,
-
-), loaded bool) {
+func (m *HandlerMap) LoadAndDelete(key uint16) (value *handlerInfo, loaded bool) {
read, _ := m.read.Load().(readOnlyHandlerMap)
e, ok := read.m[key]
if !ok && read.amended {
@@ -326,18 +296,14 @@ func (m *HandlerMap) Delete(key uint16) {
m.LoadAndDelete(key)
}
-func (e *entryHandlerMap) delete() (value func(i interface{}, err error,
-
-), ok bool) {
+func (e *entryHandlerMap) delete() (value *handlerInfo, ok bool) {
for {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expungedHandlerMap {
return value, false
}
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
- return *(*func(i interface{}, err error,
-
- ))(p), true
+ return *(**handlerInfo)(p), true
}
}
}
@@ -352,9 +318,7 @@ func (e *entryHandlerMap) delete() (value func(i interface{}, err error,
//
// Range may be O(N) with the number of elements in the map even if f returns
// false after a constant number of calls.
-func (m *HandlerMap) Range(f func(key uint16, value func(i interface{}, err error,
-
-)) bool) {
+func (m *HandlerMap) Range(f func(key uint16, value *handlerInfo) bool) {
// We need to be able to iterate over all of the keys that were already
// present at the start of the call to Range.
// If read.amended is false, then read.m satisfies that property without
diff --git a/client/image.go b/client/image.go
index 522392ce..b27c2eae 100644
--- a/client/image.go
+++ b/client/image.go
@@ -65,7 +65,7 @@ func (c *QQClient) UploadGroupImageByFile(groupCode int64, path string) (*messag
if err != nil {
return nil, err
}
- defer img.Close()
+ defer func() { _ = img.Close() }()
fh, length := utils.ComputeMd5AndLength(img)
seq, pkt := c.buildGroupImageStorePacket(groupCode, fh[:], int32(length))
r, err := c.sendAndWait(seq, pkt)
@@ -213,7 +213,7 @@ func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size
}
// ImgStore.GroupPicUp
-func decodeGroupImageStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupImageStoreResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkt := pb.D388RespBody{}
err := proto.Unmarshal(payload, &pkt)
if err != nil {
diff --git a/client/multimsg.go b/client/multimsg.go
index 1afabe30..33bf62c3 100644
--- a/client/multimsg.go
+++ b/client/multimsg.go
@@ -43,7 +43,7 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, grou
}
// MultiMsg.ApplyUp
-func decodeMultiApplyUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMultiApplyUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(payload, &body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -85,7 +85,7 @@ func (c *QQClient) buildMultiApplyDownPacket(resId string) (uint16, []byte) {
}
// MultiMsg.ApplyDown
-func decodeMultiApplyDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMultiApplyDownResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(payload, &body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
diff --git a/client/offline_file.go b/client/offline_file.go
index 63d2db09..9cf33a70 100644
--- a/client/offline_file.go
+++ b/client/offline_file.go
@@ -32,7 +32,7 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [
return seq, packet
}
-func decodeOfflineFileDownloadResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeOfflineFileDownloadResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := cmd0x346.C346RspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
c.Error("unmarshal cmd0x346 rsp body error: %v", err)
diff --git a/client/pb/data.pb.go b/client/pb/data.pb.go
index d00a7138..17e11ed2 100644
--- a/client/pb/data.pb.go
+++ b/client/pb/data.pb.go
@@ -7,11 +7,12 @@
package pb
import (
+ reflect "reflect"
+ sync "sync"
+
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
- reflect "reflect"
- sync "sync"
)
const (
diff --git a/client/ptt.go b/client/ptt.go
index ca2fc950..fc361c8e 100644
--- a/client/ptt.go
+++ b/client/ptt.go
@@ -1,6 +1,7 @@
package client
import (
+ "bytes"
"crypto/md5"
"encoding/hex"
"io"
@@ -60,33 +61,31 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag
// UploadPrivatePtt 将语音数据使用好友语音通道上传到服务器, 返回 message.PrivateVoiceElement 可直接发送
func (c *QQClient) UploadPrivatePtt(target int64, voice []byte) (*message.PrivateVoiceElement, error) {
h := md5.Sum(voice)
- i, err := c.sendAndWait(c.buildPrivatePttStorePacket(target, h[:], int32(len(voice)), int32(len(voice))))
+ ext := c.buildC2CPttStoreBDHExt(target, h[:], int32(len(voice)), int32(len(voice)))
+ rsp, err := c.highwayUploadByBDH(bytes.NewReader(voice), 26, c.highwaySession.SigSession, ext, false)
if err != nil {
return nil, err
}
- rsp := i.(pttUploadResponse)
- if rsp.IsExists {
- goto ok
+ if len(rsp) == 0 {
+ return nil, errors.New("miss rsp")
}
- for i, ip := range rsp.UploadIp {
- err := c.uploadPtt(ip, rsp.UploadPort[i], rsp.UploadKey, rsp.FileKey, voice, h[:])
- if err != nil {
- continue
- }
- goto ok
+ pkt := cmd0x346.C346RspBody{}
+ if err = proto.Unmarshal(rsp, &pkt); err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
+ }
+ if pkt.ApplyUploadRsp == nil {
+ return nil, errors.New("miss apply upload rsp")
}
- return nil, errors.New("upload failed")
-ok:
return &message.PrivateVoiceElement{
Ptt: &msg.Ptt{
- FileType: proto.Int32(4),
- SrcUin: &c.Uin,
- FileMd5: h[:],
- FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"),
- FileSize: proto.Int32(int32(len(voice))),
- FileKey: rsp.FileKey,
+ FileType: proto.Int32(4),
+ SrcUin: &c.Uin,
+ FileUuid: pkt.ApplyUploadRsp.Uuid,
+ FileMd5: h[:],
+ FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"),
+ FileSize: proto.Int32(int32(len(voice))),
+ // Reserve: constructPTTExtraInfo(1, int32(len(voice))), // todo length
BoolValid: proto.Bool(true),
- PbReserve: []byte{8, 0, 40, 0, 56, 0},
}}, nil
}
@@ -261,7 +260,7 @@ func (c *QQClient) buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash [
}
// PttStore.GroupPttUp
-func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupPttStoreResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkt := pb.D388RespBody{}
err := proto.Unmarshal(payload, &pkt)
if err != nil {
@@ -291,7 +290,7 @@ func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interfa
}
// PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500
-func (c *QQClient) buildPrivatePttStorePacket(target int64, md5 []byte, size, voiceLength int32) (uint16, []byte) {
+func (c *QQClient) buildC2CPttStoreBDHExt(target int64, md5 []byte, size, voiceLength int32) []byte {
seq := c.nextSeq()
req := &cmd0x346.C346ReqBody{
Cmd: 500,
@@ -315,42 +314,11 @@ func (c *QQClient) buildPrivatePttStorePacket(target int64, md5 []byte, size, vo
},
}
payload, _ := proto.Marshal(req)
- packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
- return seq, packet
-}
-
-// PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500
-func decodePrivatePttStoreResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
- rsp := cmd0x346.C346RspBody{}
- if err := proto.Unmarshal(payload, &rsp); err != nil {
- c.Error("unmarshal cmd0x346 rsp body error: %v", err)
- return nil, errors.Wrap(err, "unmarshal cmd0x346 rsp body error")
- }
- if rsp.ApplyUploadRsp == nil {
- c.Error("decode apply upload 500 error: apply rsp is nil.")
- return nil, errors.New("apply rsp is nil")
- }
- if rsp.ApplyUploadRsp.RetCode != 0 {
- c.Error("decode apply upload 500 error: %v", rsp.ApplyUploadRsp.RetCode)
- return nil, errors.Errorf("apply upload rsp error: %d", rsp.ApplyUploadRsp.RetCode)
- }
- if rsp.ApplyUploadRsp.BoolFileExist {
- return pttUploadResponse{IsExists: true}, nil
- }
- var port []int32
- for range rsp.ApplyUploadRsp.UploadipList {
- port = append(port, rsp.ApplyUploadRsp.UploadPort)
- }
- return pttUploadResponse{
- UploadKey: rsp.ApplyUploadRsp.UploadKey,
- UploadIp: rsp.ApplyUploadRsp.UploadipList,
- UploadPort: port,
- FileKey: rsp.ApplyUploadRsp.Uuid,
- }, nil
+ return payload
}
// PttCenterSvr.ShortVideoDownReq
-func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodePttShortVideoDownResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := pttcenter.ShortVideoRspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -362,7 +330,7 @@ func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (int
}
// PttCenterSvr.GroupShortVideoUpReq
-func decodeGroupShortVideoUploadResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeGroupShortVideoUploadResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := pttcenter.ShortVideoRspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -375,3 +343,19 @@ func decodeGroupShortVideoUploadResponse(_ *QQClient, _ uint16, payload []byte)
}
return rsp.PttShortVideoUploadRsp, nil
}
+
+func constructPTTExtraInfo(codec, length int32) []byte {
+ return binary.NewWriterF(func(w *binary.Writer) {
+ w.WriteByte(3)
+ w.WriteByte(8)
+ w.WriteUInt16(4)
+ w.WriteUInt32(uint32(codec))
+ w.WriteByte(9)
+ w.WriteUInt16(4)
+ w.WriteUInt32(uint32(14)) // length 时间
+ w.WriteByte(10)
+ info := []byte{0x08, 0x00, 0x28, 0x00, 0x38, 0x00} // todo
+ w.WriteUInt16(uint16(len(info)))
+ w.Write(info)
+ })
+}
diff --git a/client/recall.go b/client/recall.go
index 7ed78fea..36c9fb24 100644
--- a/client/recall.go
+++ b/client/recall.go
@@ -89,7 +89,7 @@ func (c *QQClient) buildPrivateRecallPacket(uin, ts int64, msgSeq, random int32)
return seq, packet
}
-func decodeMsgWithDrawResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMsgWithDrawResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := msg.MsgWithDrawResp{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
diff --git a/client/security.go b/client/security.go
index 3dbdbe5f..72421b61 100644
--- a/client/security.go
+++ b/client/security.go
@@ -49,7 +49,7 @@ func (c *QQClient) buildUrlCheckRequest(url string) (uint16, []byte) {
return seq, packet
}
-func decodeUrlCheckResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeUrlCheckResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := &oidb.OIDBSSOPkg{}
rsp := &oidb.DBCBRspBody{}
if err := proto.Unmarshal(payload, pkg); err != nil {
diff --git a/client/sync.go b/client/sync.go
index 0ffb74d2..ffa3833f 100644
--- a/client/sync.go
+++ b/client/sync.go
@@ -29,7 +29,6 @@ func init() {
}
type (
-
// SessionSyncResponse 会话同步结果
SessionSyncResponse struct {
GroupSessions []*GroupSessionInfo
@@ -101,7 +100,7 @@ func (c *QQClient) SyncSessions() (*SessionSyncResponse, error) {
select {
case <-notifyChan:
stop()
- case <-time.After(time.Second * 5):
+ case <-time.After(time.Second * 20):
stop()
}
return ret, nil
@@ -274,7 +273,7 @@ func (c *QQClient) buildGroupMsgReadedPacket(groupCode, msgSeq int64) (uint16, [
}
// StatSvc.GetDevLoginInfo
-func decodeDevListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeDevListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -297,7 +296,7 @@ func decodeDevListResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
}
// RegPrxySvc.PushParam
-func decodePushParamPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodePushParamPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
@@ -342,7 +341,7 @@ func decodePushParamPacket(c *QQClient, _ uint16, payload []byte) (interface{},
}
// RegPrxySvc.PbSyncMsg
-func decodeMsgSyncResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMsgSyncResponse(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := &msf.SvcRegisterProxyMsgResp{}
if err := proto.Unmarshal(payload, rsp); err != nil {
return nil, err
@@ -376,13 +375,13 @@ func decodeMsgSyncResponse(c *QQClient, _ uint16, payload []byte) (interface{},
if len(rsp.C2CMsg) > 4 {
c2cRsp := &msg.GetMessageResponse{}
if proto.Unmarshal(rsp.C2CMsg[4:], c2cRsp) == nil {
- c.c2cMessageSyncProcessor(c2cRsp) // todo rewrite c2c processor
+ c.c2cMessageSyncProcessor(c2cRsp, info)
}
}
return ret, nil
}
-func decodeMsgReadedResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeMsgReadedResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := msg.PbMsgReadedReportResp{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@@ -396,7 +395,7 @@ func decodeMsgReadedResponse(c *QQClient, _ uint16, payload []byte) (interface{}
var loginNotifyLock sync.Mutex
// StatSvc.SvcReqMSFLoginNotify
-func decodeLoginNotifyPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeLoginNotifyPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
data := &jce.RequestDataVersion2{}
diff --git a/client/system_msg.go b/client/system_msg.go
index 0dccfee0..97e13bcb 100644
--- a/client/system_msg.go
+++ b/client/system_msg.go
@@ -200,7 +200,7 @@ func (c *QQClient) buildSystemMsgFriendActionPacket(reqId, requester int64, acce
}
// ProfileService.Pb.ReqSystemMsgNew.Group
-func decodeSystemMsgGroupPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeSystemMsgGroupPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := structmsg.RspSystemMsgNew{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, err
diff --git a/client/translate.go b/client/translate.go
index b4afd1a1..85ede66a 100644
--- a/client/translate.go
+++ b/client/translate.go
@@ -42,7 +42,7 @@ func (c *QQClient) Translate(src, dst, text string) (string, error) {
}
// OidbSvc.0x990
-func decodeTranslateResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
+func decodeTranslateResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.TranslateRspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
diff --git a/go.mod b/go.mod
index 41ee143e..595b4c09 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/Mrs4s/MiraiGo
go 1.15
require (
+ github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97 // indirect
github.com/golang/protobuf v1.4.3
github.com/json-iterator/go v1.1.10
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
diff --git a/go.sum b/go.sum
index 0f710ceb..99881c34 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
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/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97 h1:QJIAdw5m5tNUy7fjBxgg73+YUs/AkeESeqdJ1L3lN10=
+github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97/go.mod h1:f3iF7/3t9i9hsYF8DPgT0XeIVyNzevhMCKf2445Q6pE=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -59,6 +61,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/message/elements.go b/message/elements.go
index 324dbfbb..04647d03 100644
--- a/message/elements.go
+++ b/message/elements.go
@@ -253,11 +253,16 @@ func NewReply(m *GroupMessage) *ReplyElement {
}
func NewUrlShare(url, title, content, image string) *ServiceElement {
- template := fmt.Sprintf(`- %s%s
`,
- url, url, image, title, content,
+ template := fmt.Sprintf(`- %v%v
`,
+ title, url, image, title, content,
)
+ /*
+ template := fmt.Sprintf(`- %s%s
`,
+ url, url, image, title, content,
+ )
+ */
return &ServiceElement{
- Id: 33,
+ Id: 1,
Content: template,
ResId: url,
SubType: "UrlShare",
diff --git a/message/pack.go b/message/pack.go
index 2450cddf..b8a79232 100644
--- a/message/pack.go
+++ b/message/pack.go
@@ -128,7 +128,7 @@ func (e *FriendImageElement) Pack() (r []*msg.Elem) {
func (e *ServiceElement) Pack() (r []*msg.Elem) {
r = []*msg.Elem{}
// id =35 已移至 ForwardElement
- if e.Id == 33 {
+ if e.Id == 1 {
r = append(r, &msg.Elem{
Text: &msg.Text{Str: &e.ResId},
})