package telegram import ( "context" "fmt" "github.com/TelegramExchange/tgex-backend/tg_parser/internal/domain" "github.com/gotd/td/telegram/deeplink" "github.com/gotd/td/tg" ) func (t *Telegram) ParseChannelMetaByInvite(ctx context.Context, inviteLink string) (domain.Channel, error) { link, err := deeplink.Parse(inviteLink) if err != nil { return domain.Channel{}, fmt.Errorf("parse invite link: %w", err) } if link.Type != deeplink.Join { return domain.Channel{}, fmt.Errorf("invite link is not a join link") } hash := link.Args.Get("invite") if hash == "" { return domain.Channel{}, fmt.Errorf("invite link missing hash") } info, err := t.API().MessagesCheckChatInvite(ctx, hash) if err != nil { return domain.Channel{}, fmt.Errorf("check invite: %w", err) } var channel *tg.Channel switch v := info.(type) { case *tg.ChatInviteAlready: channel = extractChannelFromChat(v.Chat) case *tg.ChatInvite: updates, err := t.API().MessagesImportChatInvite(ctx, hash) if err != nil { return domain.Channel{}, fmt.Errorf("import invite: %w", err) } channel = extractChannelFromUpdates(updates) case *tg.ChatInvitePeek: // ChatInvitePeek means we can preview the channel without joining (public channels) channel = extractChannelFromChat(v.Chat) default: return domain.Channel{}, fmt.Errorf("unexpected invite response: %T", v) } if channel == nil { return domain.Channel{}, fmt.Errorf("no channel in invite response") } if channel.AccessHash == 0 { return domain.Channel{}, fmt.Errorf("channel access hash missing") } pts, err := getChannelPTS(ctx, t.API(), channel) if err != nil { return domain.Channel{}, fmt.Errorf("get peer dialogs: %w", err) } username := "" if usernameVal, ok := channel.GetUsername(); ok && usernameVal != "" { username = usernameVal } return domain.Channel{ TelegramID: domain.ChatIDFromChannelID(channel.ID), Username: username, Title: channel.Title, AccessHash: channel.AccessHash, Pts: pts, InviteLink: inviteLink, IsAccessible: true, }, nil } func extractChannelFromChat(chat tg.ChatClass) *tg.Channel { switch v := chat.(type) { case *tg.Channel: return v case *tg.ChannelForbidden: return &tg.Channel{ ID: v.ID, AccessHash: v.AccessHash, Title: v.Title, } default: return nil } } func extractChannelFromUpdates(updates tg.UpdatesClass) *tg.Channel { var chats []tg.ChatClass switch v := updates.(type) { case *tg.Updates: chats = v.Chats case *tg.UpdatesCombined: chats = v.Chats default: return nil } for _, chat := range chats { if channel := extractChannelFromChat(chat); channel != nil { return channel } } return nil } func getChannelPTS(ctx context.Context, api *tg.Client, channel *tg.Channel) (int, error) { req := []tg.InputDialogPeerClass{ &tg.InputDialogPeer{ Peer: &tg.InputPeerChannel{ ChannelID: channel.ID, AccessHash: channel.AccessHash, }, }, } dialogs, err := api.MessagesGetPeerDialogs(ctx, req) if err != nil { return 0, err } if len(dialogs.Dialogs) > 0 { if d, ok := dialogs.Dialogs[0].(*tg.Dialog); ok { return d.Pts, nil } } return 0, nil }