diff --git a/README.md b/README.md index 39f7efef..c47a8eb9 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,6 @@ qq-android协议的golang实现 移植于Mirai - [x] 撤回群消息 - [ ] 群公告设置 - [ ] 群设置 -- [ ] 修改群成员Card +- [x] 修改群成员Card - [ ] 群成员邀请 - [ ] T出群成员 diff --git a/binary/jce/structs.go b/binary/jce/structs.go index 41e737e2..b7877ad1 100644 --- a/binary/jce/structs.go +++ b/binary/jce/structs.go @@ -28,6 +28,7 @@ type ( } SvcReqRegister struct { + IJceStruct Uin int64 `jceId:"0"` Bid int64 `jceId:"1"` ConnType byte `jceId:"2"` @@ -86,6 +87,7 @@ type ( } SvcRespPushMsg struct { + IJceStruct Uin int64 `jceId:"0"` DelInfos []IJceStruct `jceId:"1"` Svrip int32 `jceId:"2"` @@ -94,6 +96,7 @@ type ( } DelMsgInfo struct { + IJceStruct FromUin int64 `jceId:"0"` MsgTime int64 `jceId:"1"` MsgSeq int16 `jceId:"2"` @@ -108,6 +111,7 @@ type ( } FriendListRequest struct { + IJceStruct Reqtype int32 `jceId:"0"` IfReflush byte `jceId:"1"` Uin int64 `jceId:"2"` @@ -189,6 +193,7 @@ type ( } TroopListRequest struct { + IJceStruct Uin int64 `jceId:"0"` GetMSFMsgFlag byte `jceId:"1"` Cookies []byte `jceId:"2"` @@ -239,6 +244,7 @@ type ( } TroopMemberListRequest struct { + IJceStruct Uin int64 `jceId:"0"` GroupCode int64 `jceId:"1"` NextUin int64 `jceId:"2"` @@ -286,6 +292,25 @@ type ( Nameplate int64 `jceId:"38"` GroupHonor []byte `jceId:"39"` } + + ModifyGroupCardRequest struct { + IJceStruct + Zero int64 `jceId:"0"` + GroupCode int64 `jceId:"1"` + NewSeq int64 `jceId:"2"` + UinInfo []IJceStruct `jceId:"3"` + } + + UinInfo struct { + IJceStruct + Uin int64 `jceId:"0"` + Flag int64 `jceId:"1"` + Name string `jceId:"2"` + Gender byte `jceId:"3"` + Phone string `jceId:"4"` + Email string `jceId:"5"` + Remark string `jceId:"6"` + } ) func (pkt *RequestPacket) ToBytes() []byte { @@ -339,20 +364,12 @@ func (pkt *SvcReqRegister) ToBytes() []byte { return w.Bytes() } -func (pkt *SvcReqRegister) ReadFrom(r *JceReader) { - -} - func (pkt *FriendListRequest) ToBytes() []byte { w := NewJceWriter() w.WriteJceStructRaw(pkt) return w.Bytes() } -func (pkt *FriendListRequest) ReadFrom(r *JceReader) { - -} - func (pkt *FriendInfo) ReadFrom(r *JceReader) { pkt.FriendUin = r.ReadInt64(0) pkt.GroupId = r.ReadByte(1) @@ -373,10 +390,6 @@ func (pkt *TroopListRequest) ToBytes() []byte { return w.Bytes() } -func (pkt *TroopListRequest) ReadFrom(r *JceReader) { - -} - func (pkt *TroopNumber) ReadFrom(r *JceReader) { pkt.GroupUin = r.ReadInt64(0) pkt.GroupCode = r.ReadInt64(1) @@ -393,10 +406,6 @@ func (pkt *TroopMemberListRequest) ToBytes() []byte { return w.Bytes() } -func (pkt *TroopMemberListRequest) ReadFrom(r *JceReader) { - -} - func (pkt *TroopMemberInfo) ReadFrom(r *JceReader) { pkt.MemberUin = r.ReadInt64(0) pkt.FaceId = r.ReadInt16(1) @@ -431,8 +440,8 @@ func (pkt *SvcRespPushMsg) ToBytes() []byte { return w.Bytes() } -func (pkt *SvcRespPushMsg) ReadFrom(r *JceReader) { -} - -func (pkt *DelMsgInfo) ReadFrom(r *JceReader) { +func (pkt *ModifyGroupCardRequest) ToBytes() []byte { + w := NewJceWriter() + w.WriteJceStructRaw(pkt) + return w.Bytes() } diff --git a/client/builders.go b/client/builders.go index df03fb36..e7e90e39 100644 --- a/client/builders.go +++ b/client/builders.go @@ -678,6 +678,33 @@ func (c *QQClient) buildGroupRecallPacket(groupCode int64, msgSeq, msgRan int32) return seq, packet } +// friendlist.ModifyGroupCardReq +func (c *QQClient) buildEditGroupTagPacket(groupCode, memberUin int64, newTag string) (uint16, []byte) { + seq := c.nextSeq() + req := &jce.ModifyGroupCardRequest{ + GroupCode: groupCode, + UinInfo: []jce.IJceStruct{ + &jce.UinInfo{ + Uin: memberUin, + Flag: 31, + Name: newTag, + }, + }, + } + buf := &jce.RequestDataVersion3{Map: map[string][]byte{"MGCREQ": packRequestDataV3(req.ToBytes())}} + pkt := &jce.RequestPacket{ + IVersion: 3, + IRequestId: c.nextPacketSeq(), + SServantName: "mqq.IMService.FriendListServiceServantObj", + SFuncName: "ModifyGroupCardReq", + SBuffer: buf.ToBytes(), + Context: map[string]string{}, + Status: map[string]string{}, + } + packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.ModifyGroupCardReq", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes()) + return seq, packet +} + /* func (c *QQClient) buildMultiMsgDownRequestPacket() (uint16, []byte){ seq := c.nextSeq() diff --git a/client/client.go b/client/client.go index 9555e9ed..c68815b9 100644 --- a/client/client.go +++ b/client/client.go @@ -373,9 +373,9 @@ func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error) rsp := data.(groupMemberListResponse) nextUin = rsp.NextUin for _, m := range rsp.list { + m.Group = group if m.Uin == group.OwnerUin { m.Permission = Owner - break } } list = append(list, rsp.list...) @@ -431,6 +431,10 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) { _ = c.send(pkt) } +func (g *GroupInfo) SelfPermission() MemberPermission { + return g.FindMember(g.bot.Uin).Permission +} + func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo { for _, m := range g.Members { f := m @@ -441,6 +445,10 @@ func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo { return nil } +func (c *QQClient) EditMemberCard(groupCode, memberUin int64, card string) { + _, _ = c.sendAndWait(c.buildEditGroupTagPacket(groupCode, memberUin, card)) +} + func (g *GroupInfo) removeMember(uin int64) { if g.memLock == nil { g.memLock = new(sync.Mutex) @@ -518,6 +526,7 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) { return nil, err } ch := make(chan T) + defer close(ch) c.handlers[seq] = func(i interface{}, err error) { ch <- T{ Response: i, diff --git a/client/decoders.go b/client/decoders.go index bad357fd..ad29776e 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -311,7 +311,7 @@ func decodeFriendGroupListResponse(_ *QQClient, _ uint16, payload []byte) (inter return rsp, nil } -func decodeGroupListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupListResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -329,6 +329,7 @@ func decodeGroupListResponse(_ *QQClient, _ uint16, payload []byte) (interface{} OwnerUin: g.GroupOwnerUin, MemberCount: uint16(g.MemberNum), MaxMemberCount: uint16(g.MaxGroupMemberNum), + bot: c, }) } return l, nil diff --git a/client/entities.go b/client/entities.go index 6664c6a1..ec345287 100644 --- a/client/entities.go +++ b/client/entities.go @@ -51,10 +51,12 @@ type ( MaxMemberCount uint16 Members []*GroupMemberInfo + bot *QQClient memLock *sync.Mutex } GroupMemberInfo struct { + Group *GroupInfo Uin int64 Nickname string CardName string @@ -179,6 +181,24 @@ func (m *GroupMemberInfo) DisplayName() string { return m.CardName } +func (m *GroupMemberInfo) EditCard(card string) { + if m.Manageable() { + m.Group.bot.EditMemberCard(m.Group.Code, m.Uin, card) + m.CardName = card + } +} + +func (m *GroupMemberInfo) Manageable() bool { + if m.Uin == m.Group.bot.Uin { + return true + } + self := m.Group.SelfPermission() + if self == Member || m.Permission == Owner { + return false + } + return m.Permission != Administrator +} + func (r *UserJoinGroupRequest) Accept() { r.client.SolveGroupJoinRequest(r, true) }