Files
tgex-backend/tg_parser/internal/adapter/telegram/resolve_invite.go
2026-01-21 02:41:20 +03:00

131 lines
3.0 KiB
Go

package telegram
import (
"context"
"fmt"
"github.com/TelegramExchange/tgex-backend/tg_parser/internal/domain"
"github.com/google/uuid"
"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)
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 {
username = usernameVal
}
return domain.Channel{
ID: uuid.New(),
TelegramID: domain.ChatIDFromChannelID(channel.ID),
Username: username,
Title: channel.Title,
AccessHash: channel.AccessHash,
Pts: pts,
}, 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
}