github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/client/guild.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"sort"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/Mrs4s/MiraiGo/client/internal/network"
    11  	"github.com/Mrs4s/MiraiGo/topic"
    12  
    13  	"github.com/Mrs4s/MiraiGo/client/pb/qweb"
    14  	"github.com/Mrs4s/MiraiGo/internal/proto"
    15  
    16  	"github.com/pkg/errors"
    17  
    18  	"github.com/Mrs4s/MiraiGo/client/pb/channel"
    19  	"github.com/Mrs4s/MiraiGo/utils"
    20  )
    21  
    22  type (
    23  	// GuildService 频道模块内自身的信息
    24  	GuildService struct {
    25  		TinyId     uint64
    26  		Nickname   string
    27  		AvatarUrl  string
    28  		GuildCount uint32
    29  		// Guilds 由服务器推送的频道列表
    30  		Guilds []*GuildInfo
    31  
    32  		c *QQClient
    33  	}
    34  
    35  	// GuildInfo 频道信息
    36  	GuildInfo struct {
    37  		GuildId   uint64
    38  		GuildCode uint64
    39  		GuildName string
    40  		CoverUrl  string
    41  		AvatarUrl string
    42  		Channels  []*ChannelInfo
    43  		// Bots      []*GuildMemberInfo
    44  		// Members   []*GuildMemberInfo
    45  		// Admins    []*GuildMemberInfo
    46  	}
    47  
    48  	// GuildMeta 频道数据
    49  	GuildMeta struct {
    50  		GuildId        uint64
    51  		GuildName      string
    52  		GuildProfile   string
    53  		MaxMemberCount int64
    54  		MemberCount    int64
    55  		CreateTime     int64
    56  		MaxRobotCount  int32
    57  		MaxAdminCount  int32
    58  		OwnerId        uint64
    59  	}
    60  
    61  	// GuildMemberInfo 频道成员信息, 仅通过频道成员列表API获取
    62  	GuildMemberInfo struct {
    63  		TinyId        uint64
    64  		Title         string
    65  		Nickname      string
    66  		LastSpeakTime int64
    67  		Role          uint64
    68  		RoleName      string
    69  	}
    70  
    71  	// GuildUserProfile 频道系统用户资料
    72  	GuildUserProfile struct {
    73  		TinyId    uint64
    74  		Nickname  string
    75  		AvatarUrl string
    76  		JoinTime  int64 // 只有 GetGuildMemberProfileInfo 函数才会有
    77  		Roles     []*GuildRole
    78  	}
    79  
    80  	// GuildRole 频道身份组信息
    81  	GuildRole struct {
    82  		RoleId      uint64
    83  		RoleName    string
    84  		ArgbColor   uint32
    85  		Independent bool
    86  		Num         int32
    87  		Owned       bool
    88  		Disabled    bool
    89  		MaxNum      int32
    90  	}
    91  
    92  	// ChannelInfo 子频道信息
    93  	ChannelInfo struct {
    94  		ChannelId   uint64
    95  		ChannelName string
    96  		Time        uint64
    97  		EventTime   uint32
    98  		NotifyType  uint32
    99  		ChannelType ChannelType
   100  		AtAllSeq    uint64
   101  		Meta        *ChannelMeta
   102  
   103  		fetchTime int64
   104  	}
   105  
   106  	ChannelMeta struct {
   107  		CreatorUin           int64
   108  		CreatorTinyId        uint64
   109  		CreateTime           int64
   110  		GuildId              uint64
   111  		VisibleType          int32
   112  		TopMessageSeq        uint64
   113  		TopMessageTime       int64
   114  		TopMessageOperatorId uint64
   115  		CurrentSlowMode      int32
   116  		TalkPermission       int32
   117  		SlowModes            []*ChannelSlowModeInfo
   118  	}
   119  
   120  	ChannelSlowModeInfo struct {
   121  		SlowModeKey    int32
   122  		SpeakFrequency int32
   123  		SlowModeCircle int32
   124  		SlowModeText   string
   125  	}
   126  
   127  	FetchGuildMemberListWithRoleResult struct {
   128  		Members        []*GuildMemberInfo
   129  		NextIndex      uint32
   130  		NextRoleId     uint64
   131  		NextQueryParam string
   132  		Finished       bool
   133  	}
   134  
   135  	ChannelType int32
   136  )
   137  
   138  const (
   139  	ChannelTypeText  ChannelType = 1
   140  	ChannelTypeVoice ChannelType = 2
   141  	ChannelTypeLive  ChannelType = 5
   142  	ChannelTypeTopic ChannelType = 7
   143  )
   144  
   145  func init() {
   146  	decoders["trpc.group_pro.synclogic.SyncLogic.PushFirstView"] = decodeGuildPushFirstView
   147  }
   148  
   149  func (s *GuildService) FindGuild(guildId uint64) *GuildInfo {
   150  	for _, i := range s.Guilds {
   151  		if i.GuildId == guildId {
   152  			return i
   153  		}
   154  	}
   155  	return nil
   156  }
   157  
   158  func (g *GuildInfo) FindChannel(channelId uint64) *ChannelInfo {
   159  	for _, c := range g.Channels {
   160  		if c.ChannelId == channelId {
   161  			return c
   162  		}
   163  	}
   164  	return nil
   165  }
   166  
   167  func (g *GuildInfo) removeChannel(id uint64) {
   168  	i := sort.Search(len(g.Channels), func(i int) bool {
   169  		return g.Channels[i].ChannelId >= id
   170  	})
   171  	if i >= len(g.Channels) || g.Channels[i].ChannelId != id {
   172  		return
   173  	}
   174  	g.Channels = append(g.Channels[:i], g.Channels[i+1:]...)
   175  }
   176  
   177  func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) {
   178  	flags := proto.DynamicMessage{}
   179  	for i := 3; i <= 29; i++ {
   180  		flags[uint64(i)] = 1
   181  	}
   182  	flags[99] = 1
   183  	flags[100] = 1
   184  	payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
   185  		1: flags,
   186  		3: tinyId,
   187  		4: 0,
   188  	})
   189  	rsp, err := s.c.sendAndWaitDynamic(s.c.uniPacket("OidbSvcTrpcTcp.0xfc9_1", payload))
   190  	if err != nil {
   191  		return nil, errors.Wrap(err, "send packet error")
   192  	}
   193  	body := new(channel.ChannelOidb0Xfc9Rsp)
   194  	if err = unpackOIDBPackage(rsp, body); err != nil {
   195  		return nil, errors.Wrap(err, "decode packet error")
   196  	}
   197  	// todo: 解析个性档案
   198  	return &GuildUserProfile{
   199  		TinyId:    tinyId,
   200  		Nickname:  body.Profile.Nickname.Unwrap(),
   201  		AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
   202  		JoinTime:  body.Profile.JoinTime.Unwrap(),
   203  	}, nil
   204  }
   205  
   206  // FetchGuildMemberListWithRole 获取频道成员列表
   207  // 第一次请求: startIndex = 0 , roleIdIndex = 2 param = ""
   208  // 后续请求请根据上次请求的返回值进行设置
   209  func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, startIndex uint32, roleIdIndex uint64, param string) (*FetchGuildMemberListWithRoleResult, error) {
   210  	seq := s.c.nextSeq()
   211  	m := proto.DynamicMessage{
   212  		1: guildId, // guild id
   213  		2: 3,
   214  		3: 0,
   215  		4: proto.DynamicMessage{ // unknown param, looks like flags
   216  			1:  1,
   217  			2:  1,
   218  			3:  1,
   219  			4:  1,
   220  			5:  1,
   221  			6:  1,
   222  			7:  1,
   223  			8:  1,
   224  			20: 1,
   225  		},
   226  		6:  startIndex,
   227  		8:  50, // count
   228  		12: channelId,
   229  	}
   230  	if param != "" {
   231  		m[13] = param
   232  	}
   233  	m[14] = roleIdIndex
   234  	packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf5b_1", s.c.packOIDBPackageDynamically(3931, 1, m))
   235  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   236  	if err != nil {
   237  		return nil, errors.Wrap(err, "send packet error")
   238  	}
   239  	body := new(channel.ChannelOidb0Xf5BRsp)
   240  	if err = unpackOIDBPackage(rsp, body); err != nil {
   241  		return nil, errors.Wrap(err, "decode packet error")
   242  	}
   243  	var ret []*GuildMemberInfo
   244  	for _, memberWithRole := range body.MemberWithRoles {
   245  		for _, mem := range memberWithRole.Members {
   246  			ret = append(ret, &GuildMemberInfo{
   247  				TinyId:        mem.TinyId.Unwrap(),
   248  				Title:         mem.Title.Unwrap(),
   249  				Nickname:      mem.Nickname.Unwrap(),
   250  				LastSpeakTime: mem.LastSpeakTime.Unwrap(),
   251  				Role:          memberWithRole.RoleId.Unwrap(),
   252  				RoleName:      memberWithRole.RoleName.Unwrap(),
   253  			})
   254  		}
   255  	}
   256  	for _, mem := range body.Members {
   257  		ret = append(ret, &GuildMemberInfo{
   258  			TinyId:        mem.TinyId.Unwrap(),
   259  			Title:         mem.Title.Unwrap(),
   260  			Nickname:      mem.Nickname.Unwrap(),
   261  			LastSpeakTime: mem.LastSpeakTime.Unwrap(),
   262  			Role:          1,
   263  			RoleName:      "普通成员",
   264  		})
   265  	}
   266  	return &FetchGuildMemberListWithRoleResult{
   267  		Members:        ret,
   268  		NextIndex:      body.NextIndex.Unwrap(),
   269  		NextRoleId:     body.NextRoleIdIndex.Unwrap(),
   270  		NextQueryParam: body.NextQueryParam.Unwrap(),
   271  		Finished:       body.NextIndex.IsNone(),
   272  	}, nil
   273  }
   274  
   275  // FetchGuildMemberProfileInfo 获取单个频道成员资料
   276  func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) {
   277  	seq := s.c.nextSeq()
   278  	flags := proto.DynamicMessage{}
   279  	for i := 3; i <= 29; i++ {
   280  		flags[uint64(i)] = 1
   281  	}
   282  	flags[99] = 1
   283  	flags[100] = 1
   284  	payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
   285  		1: flags,
   286  		3: tinyId,
   287  		4: guildId,
   288  	})
   289  	packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf88_1", payload)
   290  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   291  	if err != nil {
   292  		return nil, errors.Wrap(err, "send packet error")
   293  	}
   294  	body := new(channel.ChannelOidb0Xf88Rsp)
   295  	if err = unpackOIDBPackage(rsp, body); err != nil {
   296  		return nil, errors.Wrap(err, "decode packet error")
   297  	}
   298  	roles, err := s.fetchMemberRoles(guildId, tinyId)
   299  	if err != nil {
   300  		return nil, errors.Wrap(err, "fetch roles error")
   301  	}
   302  	// todo: 解析个性档案
   303  	return &GuildUserProfile{
   304  		TinyId:    tinyId,
   305  		Nickname:  body.Profile.Nickname.Unwrap(),
   306  		AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
   307  		JoinTime:  body.Profile.JoinTime.Unwrap(),
   308  		Roles:     roles,
   309  	}, nil
   310  }
   311  
   312  func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) {
   313  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1019_1",
   314  		s.c.packOIDBPackageDynamically(4121, 1, proto.DynamicMessage{1: guildId}))
   315  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   316  	if err != nil {
   317  		return nil, errors.Wrap(err, "send packet error")
   318  	}
   319  	body := new(channel.ChannelOidb0X1019Rsp)
   320  	if err = unpackOIDBPackage(rsp, body); err != nil {
   321  		return nil, errors.Wrap(err, "decode packet error")
   322  	}
   323  	roles := make([]*GuildRole, 0, len(body.Roles))
   324  	for _, role := range body.Roles {
   325  		roles = append(roles, &GuildRole{
   326  			RoleId:      role.RoleId.Unwrap(),
   327  			RoleName:    role.Name.Unwrap(),
   328  			ArgbColor:   role.ArgbColor.Unwrap(),
   329  			Independent: role.Independent.Unwrap() == 1,
   330  			Num:         role.Num.Unwrap(),
   331  			Owned:       role.Owned.Unwrap() == 1,
   332  			Disabled:    role.Disabled.Unwrap() == 1,
   333  			MaxNum:      role.MaxNum.Unwrap(),
   334  		})
   335  	}
   336  	return roles, nil
   337  }
   338  
   339  func (s *GuildService) CreateGuildRole(guildId uint64, name string, color uint32, independent bool, initialUsers []uint64) (uint64, error) {
   340  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, proto.DynamicMessage{
   341  		1: guildId,
   342  		2: proto.DynamicMessage{ // todo: 未知参数
   343  			1: 1,
   344  			2: 1,
   345  			3: 1,
   346  		},
   347  		3: proto.DynamicMessage{
   348  			1: name,
   349  			2: color,
   350  			3: independent,
   351  		},
   352  		4: initialUsers,
   353  	}))
   354  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   355  	if err != nil {
   356  		return 0, errors.Wrap(err, "send packet error")
   357  	}
   358  	body := new(channel.ChannelOidb0X1016Rsp)
   359  	if err = unpackOIDBPackage(rsp, body); err != nil {
   360  		return 0, errors.Wrap(err, "decode packet error")
   361  	}
   362  	return body.RoleId.Unwrap(), nil
   363  }
   364  
   365  func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
   366  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, proto.DynamicMessage{
   367  		1: guildId,
   368  		2: roleId,
   369  	}))
   370  	_, err := s.c.sendAndWaitDynamic(seq, packet)
   371  	if err != nil {
   372  		return errors.Wrap(err, "send packet error")
   373  	}
   374  	return nil
   375  }
   376  
   377  func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint64, user []uint64) error { // remove => p2 = false
   378  	setOrRemove := proto.DynamicMessage{
   379  		1: roleId,
   380  	}
   381  	if set {
   382  		setOrRemove[2] = user
   383  	} else {
   384  		setOrRemove[3] = user
   385  	}
   386  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, proto.DynamicMessage{
   387  		1: guildId,
   388  		2: setOrRemove,
   389  	}))
   390  	_, err := s.c.sendAndWaitDynamic(seq, packet)
   391  	if err != nil {
   392  		return errors.Wrap(err, "send packet error")
   393  	}
   394  	return nil
   395  }
   396  
   397  func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name string, color uint32, indepedent bool) error {
   398  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, proto.DynamicMessage{
   399  		1: guildId,
   400  		2: roleId,
   401  		3: proto.DynamicMessage{
   402  			1: 1,
   403  			2: 1,
   404  			3: 1,
   405  		},
   406  		4: proto.DynamicMessage{
   407  			1: name,
   408  			2: color,
   409  			3: indepedent,
   410  		},
   411  	}))
   412  	_, err := s.c.sendAndWaitDynamic(seq, packet)
   413  	if err != nil {
   414  		return errors.Wrap(err, "send packet error")
   415  	}
   416  	return nil
   417  }
   418  
   419  func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) {
   420  	payload := s.c.packOIDBPackageDynamically(3927, 9, proto.DynamicMessage{
   421  		1: proto.DynamicMessage{
   422  			1: proto.DynamicMessage{
   423  				2: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 11: 1, 12: 1, 13: 1, 14: 1, 45: 1,
   424  				18: 1, 19: 1, 20: 1, 22: 1, 23: 1, 5002: 1, 5003: 1, 5004: 1, 5005: 1, 10007: 1,
   425  			},
   426  			2: proto.DynamicMessage{
   427  				3: 1, 4: 1, 6: 1, 11: 1, 14: 1, 15: 1, 16: 1, 17: 1,
   428  			},
   429  		},
   430  		2: proto.DynamicMessage{
   431  			1: guildId,
   432  		},
   433  	})
   434  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf57_9", payload)
   435  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   436  	if err != nil {
   437  		return nil, errors.Wrap(err, "send packet error")
   438  	}
   439  	body := new(channel.ChannelOidb0Xf57Rsp)
   440  	if err = unpackOIDBPackage(rsp, body); err != nil {
   441  		return nil, errors.Wrap(err, "decode packet error")
   442  	}
   443  	return &GuildMeta{
   444  		GuildName:      body.Rsp.Meta.Name.Unwrap(),
   445  		GuildProfile:   body.Rsp.Meta.Profile.Unwrap(),
   446  		MaxMemberCount: body.Rsp.Meta.MaxMemberCount.Unwrap(),
   447  		MemberCount:    body.Rsp.Meta.MemberCount.Unwrap(),
   448  		CreateTime:     body.Rsp.Meta.CreateTime.Unwrap(),
   449  		MaxRobotCount:  body.Rsp.Meta.RobotMaxNum.Unwrap(),
   450  		MaxAdminCount:  body.Rsp.Meta.AdminMaxNum.Unwrap(),
   451  		OwnerId:        body.Rsp.Meta.OwnerId.Unwrap(),
   452  	}, nil
   453  }
   454  
   455  func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e error) {
   456  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf5d_1",
   457  		s.c.packOIDBPackageDynamically(3933, 1,
   458  			proto.DynamicMessage{
   459  				1: guildId,
   460  				3: proto.DynamicMessage{
   461  					1: 1,
   462  				},
   463  			}))
   464  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   465  	if err != nil {
   466  		return nil, errors.Wrap(err, "send packet error")
   467  	}
   468  	body := new(channel.ChannelOidb0Xf5DRsp)
   469  	if err = unpackOIDBPackage(rsp, body); err != nil {
   470  		return nil, errors.Wrap(err, "decode packet error")
   471  	}
   472  	for _, info := range body.Rsp.Channels {
   473  		r = append(r, convertChannelInfo(info))
   474  	}
   475  	return
   476  }
   477  
   478  func (s *GuildService) FetchChannelInfo(guildId, channelId uint64) (*ChannelInfo, error) {
   479  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf55_1", s.c.packOIDBPackageDynamically(3925, 1, proto.DynamicMessage{1: guildId, 2: channelId}))
   480  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   481  	if err != nil {
   482  		return nil, errors.Wrap(err, "send packet error")
   483  	}
   484  	body := new(channel.ChannelOidb0Xf55Rsp)
   485  	if err = unpackOIDBPackage(rsp, body); err != nil {
   486  		return nil, errors.Wrap(err, "decode packet error")
   487  	}
   488  	return convertChannelInfo(body.Info), nil
   489  }
   490  
   491  func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic.Feed, error) {
   492  	guild := s.FindGuild(guildId)
   493  	if guild == nil {
   494  		return nil, errors.New("guild not found")
   495  	}
   496  	channelInfo := guild.FindChannel(channelId)
   497  	if channelInfo == nil {
   498  		return nil, errors.New("channel not found")
   499  	}
   500  	if channelInfo.ChannelType != ChannelTypeTopic {
   501  		return nil, errors.New("channel type error")
   502  	}
   503  	req, _ := proto.Marshal(&channel.StGetChannelFeedsReq{
   504  		Count: proto.Uint32(12),
   505  		From:  proto.Uint32(0),
   506  		ChannelSign: &channel.StChannelSign{
   507  			GuildId:   proto.Some(guildId),
   508  			ChannelId: proto.Some(channelId),
   509  		},
   510  		FeedAttchInfo: proto.String(""), // isLoadMore
   511  	})
   512  	payload, _ := proto.Marshal(&qweb.QWebReq{
   513  		Seq:        proto.Int64(s.c.nextQWebSeq()),
   514  		Qua:        proto.String("V1_AND_SQ_8.8.50_2324_YYB_D"),
   515  		DeviceInfo: proto.String(s.c.getWebDeviceInfo()),
   516  		BusiBuff:   req,
   517  		TraceId:    proto.String(fmt.Sprintf("%v_%v_%v", s.c.Uin, time.Now().Format("0102150405"), rand.Int63())),
   518  		Extinfo: []*qweb.COMMEntry{
   519  			{
   520  				Key:   proto.String("fc-appid"),
   521  				Value: proto.String("96"),
   522  			},
   523  			{
   524  				Key:   proto.String("environment_id"),
   525  				Value: proto.String("production"),
   526  			},
   527  			{
   528  				Key:   proto.String("tiny_id"),
   529  				Value: proto.String(fmt.Sprint(s.TinyId)),
   530  			},
   531  		},
   532  	})
   533  	seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commreader.ComReader.GetChannelTimelineFeeds", payload)
   534  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   535  	if err != nil {
   536  		return nil, errors.New("send packet error")
   537  	}
   538  	pkg := new(qweb.QWebRsp)
   539  	body := new(channel.StGetChannelFeedsRsp)
   540  	if err = proto.Unmarshal(rsp, pkg); err != nil {
   541  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   542  	}
   543  	if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
   544  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   545  	}
   546  	feeds := make([]*topic.Feed, 0, len(body.VecFeed))
   547  	for _, f := range body.VecFeed {
   548  		feeds = append(feeds, topic.DecodeFeed(f))
   549  	}
   550  	return feeds, nil
   551  }
   552  
   553  func (s *GuildService) PostTopicChannelFeed(guildId, channelId uint64, feed *topic.Feed) error {
   554  	guild := s.FindGuild(guildId)
   555  	if guild == nil {
   556  		return errors.New("guild not found")
   557  	}
   558  	channelInfo := guild.FindChannel(channelId)
   559  	if channelInfo == nil {
   560  		return errors.New("channel not found")
   561  	}
   562  	if channelInfo.ChannelType != ChannelTypeTopic {
   563  		return errors.New("channel type error")
   564  	}
   565  	feed.Poster = &topic.FeedPoster{
   566  		TinyIdStr: strconv.FormatUint(s.TinyId, 10),
   567  		Nickname:  s.Nickname,
   568  	}
   569  	feed.GuildId = guildId
   570  	feed.ChannelId = channelId
   571  	req, _ := proto.Marshal(&channel.StPublishFeedReq{
   572  		ExtInfo: &channel.StCommonExt{
   573  			MapInfo: []*channel.CommonEntry{
   574  				{
   575  					Key: proto.String("clientkey"), Value: proto.String("GuildMain" + utils.RandomStringRange(14, "0123456789")),
   576  				},
   577  			},
   578  		},
   579  		From:     proto.Int32(0),
   580  		JsonFeed: proto.String(feed.ToSendingPayload(s.c.Uin)),
   581  	})
   582  	payload, _ := proto.Marshal(&qweb.QWebReq{
   583  		Seq:        proto.Int64(s.c.nextQWebSeq()),
   584  		Qua:        proto.String("V1_AND_SQ_8.8.50_2324_YYB_D"),
   585  		DeviceInfo: proto.String(s.c.getWebDeviceInfo()),
   586  		BusiBuff:   req,
   587  		TraceId:    proto.String(fmt.Sprintf("%v_%v_%v", s.c.Uin, time.Now().Format("0102150405"), rand.Int63())),
   588  		Extinfo: []*qweb.COMMEntry{
   589  			{
   590  				Key:   proto.String("fc-appid"),
   591  				Value: proto.String("96"),
   592  			},
   593  			{
   594  				Key:   proto.String("environment_id"),
   595  				Value: proto.String("production"),
   596  			},
   597  			{
   598  				Key:   proto.String("tiny_id"),
   599  				Value: proto.String(fmt.Sprint(s.TinyId)),
   600  			},
   601  		},
   602  	})
   603  	seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed", payload)
   604  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   605  	if err != nil {
   606  		return errors.New("send packet error")
   607  	}
   608  	pkg := new(qweb.QWebRsp)
   609  	body := new(channel.StPublishFeedRsp)
   610  	if err = proto.Unmarshal(rsp, pkg); err != nil {
   611  		return errors.Wrap(err, "failed to unmarshal protobuf message")
   612  	}
   613  	if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
   614  		return errors.Wrap(err, "failed to unmarshal protobuf message")
   615  	}
   616  	if body.Feed != nil && body.Feed.Id.IsNone() {
   617  		return nil
   618  	}
   619  	return errors.New("post feed error")
   620  }
   621  
   622  func (s *GuildService) fetchMemberRoles(guildId uint64, tinyId uint64) ([]*GuildRole, error) {
   623  	seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, proto.DynamicMessage{
   624  		1: guildId,
   625  		2: tinyId,
   626  		4: proto.DynamicMessage{
   627  			1: 1,
   628  			2: 1,
   629  			3: 1,
   630  		},
   631  	}))
   632  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   633  	if err != nil {
   634  		return nil, errors.Wrap(err, "send packet error")
   635  	}
   636  	body := new(channel.ChannelOidb0X1017Rsp)
   637  	if err = unpackOIDBPackage(rsp, body); err != nil {
   638  		return nil, errors.Wrap(err, "decode packet error")
   639  	}
   640  	p1 := body.P1
   641  	if p1 == nil {
   642  		return nil, errors.New("packet OidbSvcTrpcTcp.0x1017_1: decode p1 error")
   643  	}
   644  	roles := make([]*GuildRole, 0, len(p1.Roles))
   645  	for _, role := range p1.Roles {
   646  		roles = append(roles, &GuildRole{
   647  			RoleId:    role.RoleId.Unwrap(),
   648  			RoleName:  role.Name.Unwrap(),
   649  			ArgbColor: role.ArgbColor.Unwrap(),
   650  		})
   651  	}
   652  	return roles, nil
   653  }
   654  
   655  /* need analysis
   656  func (s *GuildService) fetchChannelListState(guildId uint64, channels []*ChannelInfo) {
   657  	seq := s.c.nextSeq()
   658  	var ids []uint64
   659  	for _, info := range channels {
   660  		ids = append(ids, info.ChannelId)
   661  	}
   662  	payload := s.c.packOIDBPackageDynamically(4104, 1, binary.DynamicMessage{
   663  		1: binary.DynamicMessage{
   664  			1: guildId,
   665  			2: ids,
   666  		},
   667  	})
   668  	packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0x1008_1", 1, s.c.SessionId, []byte{}, s.c.sigInfo.d2Key, payload)
   669  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   670  	if err != nil {
   671  		return
   672  	}
   673  	pkg := new(oidb.OIDBSSOPkg)
   674  	if err = proto.Unmarshal(rsp, pkg); err != nil {
   675  		return //nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   676  	}
   677  }
   678  */
   679  
   680  func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo {
   681  	meta := &ChannelMeta{
   682  		CreatorUin:      info.CreatorUin.Unwrap(),
   683  		CreatorTinyId:   info.CreatorTinyId.Unwrap(),
   684  		CreateTime:      info.CreateTime.Unwrap(),
   685  		GuildId:         info.GuildId.Unwrap(),
   686  		VisibleType:     info.VisibleType.Unwrap(),
   687  		CurrentSlowMode: info.CurrentSlowModeKey.Unwrap(),
   688  		TalkPermission:  info.TalkPermission.Unwrap(),
   689  	}
   690  	if info.TopMsg != nil {
   691  		meta.TopMessageSeq = info.TopMsg.TopMsgSeq.Unwrap()
   692  		meta.TopMessageTime = info.TopMsg.TopMsgTime.Unwrap()
   693  		meta.TopMessageOperatorId = info.TopMsg.TopMsgOperatorTinyId.Unwrap()
   694  	}
   695  	for _, slow := range info.SlowModeInfos {
   696  		meta.SlowModes = append(meta.SlowModes, &ChannelSlowModeInfo{
   697  			SlowModeKey:    slow.SlowModeKey.Unwrap(),
   698  			SpeakFrequency: slow.SpeakFrequency.Unwrap(),
   699  			SlowModeCircle: slow.SlowModeCircle.Unwrap(),
   700  			SlowModeText:   slow.SlowModeText.Unwrap(),
   701  		})
   702  	}
   703  	return &ChannelInfo{
   704  		ChannelId:   info.ChannelId.Unwrap(),
   705  		ChannelName: info.ChannelName.Unwrap(),
   706  		NotifyType:  uint32(info.FinalNotifyType.Unwrap()),
   707  		ChannelType: ChannelType(info.ChannelType.Unwrap()),
   708  		Meta:        meta,
   709  		fetchTime:   time.Now().Unix(),
   710  	}
   711  }
   712  
   713  func (c *QQClient) syncChannelFirstView() {
   714  	rsp, err := c.sendAndWaitDynamic(c.buildSyncChannelFirstViewPacket())
   715  	if err != nil {
   716  		c.error("sync channel error: %v", err)
   717  		return
   718  	}
   719  	firstViewRsp := new(channel.FirstViewRsp)
   720  	if err = proto.Unmarshal(rsp, firstViewRsp); err != nil {
   721  		return
   722  	}
   723  	c.GuildService.TinyId = firstViewRsp.SelfTinyid.Unwrap()
   724  	c.GuildService.GuildCount = firstViewRsp.GuildCount.Unwrap()
   725  	if self, err := c.GuildService.GetUserProfile(c.GuildService.TinyId); err == nil {
   726  		c.GuildService.Nickname = self.Nickname
   727  		c.GuildService.AvatarUrl = self.AvatarUrl
   728  	} else {
   729  		c.error("get self guild profile error: %v", err)
   730  	}
   731  }
   732  
   733  func (c *QQClient) buildSyncChannelFirstViewPacket() (uint16, []byte) {
   734  	req := &channel.FirstViewReq{
   735  		LastMsgTime:       proto.Uint64(0),
   736  		Seq:               proto.Uint32(0),
   737  		DirectMessageFlag: proto.Uint32(1),
   738  	}
   739  	payload, _ := proto.Marshal(req)
   740  	return c.uniPacket("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload)
   741  }
   742  
   743  func decodeGuildPushFirstView(c *QQClient, pkt *network.Packet) (any, error) {
   744  	firstViewMsg := new(channel.FirstViewMsg)
   745  	if err := proto.Unmarshal(pkt.Payload, firstViewMsg); err != nil {
   746  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   747  	}
   748  	if len(firstViewMsg.GuildNodes) > 0 {
   749  		c.GuildService.Guilds = []*GuildInfo{}
   750  		for _, guild := range firstViewMsg.GuildNodes {
   751  			info := &GuildInfo{
   752  				GuildId:   guild.GuildId.Unwrap(),
   753  				GuildCode: guild.GuildCode.Unwrap(),
   754  				GuildName: utils.B2S(guild.GuildName),
   755  				CoverUrl:  fmt.Sprintf("https://groupprocover-76483.picgzc.qpic.cn/%v", guild.GuildId.Unwrap()),
   756  				AvatarUrl: fmt.Sprintf("https://groupprohead-76292.picgzc.qpic.cn/%v", guild.GuildId.Unwrap()),
   757  			}
   758  			channels, err := c.GuildService.FetchChannelList(info.GuildId)
   759  			if err != nil {
   760  				c.warning("warning: fetch guild %v channel error %v. will use sync node to fill channel list field", guild.GuildId, err)
   761  				for _, node := range guild.ChannelNodes {
   762  					meta := new(channel.ChannelMsgMeta)
   763  					_ = proto.Unmarshal(node.Meta, meta)
   764  					info.Channels = append(info.Channels, &ChannelInfo{
   765  						ChannelId:   node.ChannelId.Unwrap(),
   766  						ChannelName: utils.B2S(node.ChannelName),
   767  						Time:        node.Time.Unwrap(),
   768  						EventTime:   node.EventTime.Unwrap(),
   769  						NotifyType:  node.NotifyType.Unwrap(),
   770  						ChannelType: ChannelType(node.ChannelType.Unwrap()),
   771  						AtAllSeq:    meta.AtAllSeq.Unwrap(),
   772  					})
   773  				}
   774  			} else {
   775  				info.Channels = channels
   776  			}
   777  			// info.Bots, info.Members, info.Admins, _ = c.GuildService.GetGuildMembers(info.GuildId)
   778  			c.GuildService.Guilds = append(c.GuildService.Guilds, info)
   779  		}
   780  	}
   781  	// if len(firstViewMsg.ChannelMsgs) > 0 { // sync msg
   782  	// }
   783  	return nil, nil
   784  }