Files
mautrix-telegram/pkg/gotd/telegram/peers/members/channel.go
T
2025-06-27 20:03:37 -07:00

215 lines
5.6 KiB
Go

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()
}