package members import ( "context" "time" "github.com/go-faster/errors" "go.mau.fi/mautrix-telegram/pkg/gotd/telegram/peers" "go.mau.fi/mautrix-telegram/pkg/gotd/tg" ) // ChannelMembers is channel Members. type ChannelMembers struct { m *peers.Manager filter tg.ChannelParticipantsFilterClass channel peers.Channel } func (c *ChannelMembers) query(ctx context.Context, offset, limit int) (*tg.ChannelsChannelParticipants, error) { raw := c.m.API() p, err := raw.ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{ Channel: c.channel.InputChannel(), Filter: c.filter, Offset: offset, Limit: limit, }) if err != nil { return nil, errors.Wrap(err, "get members") } m, ok := p.AsModified() if !ok { return nil, errors.Errorf("unexpected type %T", p) } if err := c.m.Apply(ctx, m.Users, m.Chats); err != nil { return nil, errors.Wrap(err, "apply entities") } return m, nil } // ForEach calls cb for every member of channel. // // May return ChannelInfoUnavailableError. func (c *ChannelMembers) ForEach(ctx context.Context, cb Callback) error { const limit = 100 full, err := c.channel.FullRaw(ctx) if err != nil { return errors.Wrap(err, "get full") } if !full.CanViewParticipants { return &ChannelInfoUnavailableError{} } channelDate := time.Unix(int64(c.channel.Raw().Date), 0) offset := 0 for { m, err := c.query(ctx, offset, limit) if err != nil { return errors.Wrap(err, "query") } if len(m.Participants) < 1 { return nil } for i, member := range m.Participants { var ( userID int64 inviterID int64 err error ) switch p := member.(type) { case *tg.ChannelParticipant: userID = p.UserID case *tg.ChannelParticipantSelf: userID = p.UserID inviterID = p.InviterID case *tg.ChannelParticipantCreator: userID = p.UserID case *tg.ChannelParticipantAdmin: userID = p.UserID inviterID = p.InviterID case *tg.ChannelParticipantBanned: userPeer, ok := p.Peer.(*tg.PeerUser) if !ok { return errors.Errorf("unexpected type %T", p.Peer) } userID = userPeer.UserID case *tg.ChannelParticipantLeft: userPeer, ok := p.Peer.(*tg.PeerUser) if !ok { return errors.Errorf("unexpected type %T", p.Peer) } userID = userPeer.UserID default: return errors.Errorf("unexpected type %T", p) } user, err := c.m.ResolveUserID(ctx, userID) if err != nil { return errors.Wrapf(err, "get member %d", userID) } chm := ChannelMember{ parent: c, creatorDate: channelDate, user: user, inviter: peers.User{}, raw: member, } if inviterID != 0 { inviter, err := c.m.ResolveUserID(ctx, inviterID) if err != nil { return errors.Wrapf(err, "get inviter %d", inviterID) } chm.inviter = inviter } if err := cb(chm); err != nil { return errors.Wrapf(err, "callback (index: %d)", i) } } offset += limit } } // Count returns total count of members. func (c *ChannelMembers) Count(ctx context.Context) (int, error) { m, err := c.query(ctx, 0, 1) if err != nil { return 0, errors.Wrap(err, "query") } return m.Count, nil } // Peer returns chat object. func (c *ChannelMembers) Peer() peers.Peer { return c.channel } // Kick kicks user member. // // Needed for parity with ChatMembers to define common interface. // // If revokeHistory is set, will delete all messages from this member. func (c *ChannelMembers) Kick(ctx context.Context, member tg.InputUserClass, revokeHistory bool) error { p := convertInputUserToInputPeer(member) if revokeHistory { if _, err := c.m.API().ChannelsDeleteParticipantHistory(ctx, &tg.ChannelsDeleteParticipantHistoryRequest{ Channel: c.channel.InputChannel(), Participant: p, }); err != nil { return errors.Wrap(err, "revoke history") } } return c.KickMember(ctx, p) } // KickMember kicks member. // // Unlike Kick, KickMember can be used to kick chat member that uses send-as-channel mode. func (c *ChannelMembers) KickMember(ctx context.Context, member tg.InputPeerClass) error { return c.EditMemberRights(ctx, member, MemberRights{ DenyViewMessages: true, }) } // EditMemberRights edits member rights in this channel. func (c *ChannelMembers) EditMemberRights( ctx context.Context, member tg.InputPeerClass, options MemberRights, ) error { return c.editMemberRights(ctx, member, options) } func (c *ChannelMembers) editMemberRights(ctx context.Context, p tg.InputPeerClass, options MemberRights) error { if _, err := c.m.API().ChannelsEditBanned(ctx, &tg.ChannelsEditBannedRequest{ Channel: c.channel.InputChannel(), Participant: p, BannedRights: options.IntoChatBannedRights(), }); err != nil { return errors.Wrap(err, "edit member rights") } return nil } // EditRights edits rights of all members in this channel. func (c *ChannelMembers) EditRights(ctx context.Context, options MemberRights) error { return editDefaultRights(ctx, c.m.API(), c.channel.InputPeer(), options) } // EditAdminRights edits admin rights of given user in this channel. func (c *ChannelMembers) EditAdminRights( ctx context.Context, admin tg.InputUserClass, options AdminRights, ) error { if _, err := c.m.API().ChannelsEditAdmin(ctx, &tg.ChannelsEditAdminRequest{ Channel: c.channel.InputChannel(), UserID: admin, AdminRights: options.IntoChatAdminRights(), Rank: options.Rank, }); err != nil { return errors.Wrap(err, "edit admin rights") } return nil } // Channel returns recent channel members. func Channel(channel peers.Channel) *ChannelMembers { return ChannelQuery{Channel: channel}.Recent() }