7a04f298d2
- update to latest telegram layer - remove some references to fields in tg.Entities that don't exist in the schema - originally added here: https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e - referenced here - https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3 - https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
240 lines
5.2 KiB
Go
240 lines
5.2 KiB
Go
package peers
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/go-faster/errors"
|
|
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/ascii"
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/internal/deeplink"
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
|
)
|
|
|
|
// Resolve uses given string to create new peer promise.
|
|
//
|
|
// Input examples:
|
|
//
|
|
// @telegram
|
|
// telegram
|
|
// t.me/telegram
|
|
// https://t.me/telegram
|
|
// tg:resolve?domain=telegram
|
|
// tg://resolve?domain=telegram
|
|
// +13115552368
|
|
// +1 (311) 555-0123
|
|
// +1 311 555-6162
|
|
// 13115556162
|
|
func (m *Manager) Resolve(ctx context.Context, from string) (Peer, error) {
|
|
from = strings.TrimSpace(from)
|
|
|
|
if deeplink.IsDeeplinkLike(from) {
|
|
return m.ResolveDeeplink(ctx, from)
|
|
}
|
|
if isPhoneNumber(from) {
|
|
return m.ResolvePhone(ctx, from)
|
|
}
|
|
return m.ResolveDomain(ctx, from)
|
|
}
|
|
|
|
func isPhoneNumber(s string) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
r := rune(s[0])
|
|
return r == '+' || ascii.IsDigit(r)
|
|
}
|
|
|
|
func cleanupPhone(phone string) string {
|
|
var needClean bool
|
|
for _, ch := range phone {
|
|
if !ascii.IsDigit(ch) {
|
|
needClean = true
|
|
break
|
|
}
|
|
}
|
|
if !needClean {
|
|
return phone
|
|
}
|
|
|
|
clean := strings.Builder{}
|
|
clean.Grow(len(phone) + 1)
|
|
|
|
for _, ch := range phone {
|
|
if ascii.IsDigit(ch) {
|
|
clean.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
return clean.String()
|
|
}
|
|
|
|
// ResolvePhone uses given phone to resolve User.
|
|
//
|
|
// Input example:
|
|
//
|
|
// +13115552368
|
|
// +1 (311) 555-0123
|
|
// +1 311 555-6162
|
|
// 13115556162
|
|
//
|
|
// Note that Telegram represents phone numbers according to the E.164 standard
|
|
// without the plus sign (”+”) prefix. The resolver therefore takes an easy
|
|
// route and just deletes any non-digit symbols from phone number string.
|
|
func (m *Manager) ResolvePhone(ctx context.Context, phone string) (User, error) {
|
|
tried := false
|
|
|
|
phone = cleanupPhone(phone)
|
|
for {
|
|
if tried {
|
|
return User{}, &PhoneNotFoundError{Phone: phone}
|
|
}
|
|
|
|
key, v, found, err := m.storage.FindPhone(ctx, phone)
|
|
if err != nil {
|
|
return User{}, errors.Wrap(err, "find by phone")
|
|
}
|
|
if found {
|
|
return m.GetUser(ctx, &tg.InputUser{
|
|
UserID: key.ID,
|
|
AccessHash: v.AccessHash,
|
|
})
|
|
}
|
|
if m.selfIsBot() {
|
|
return User{}, &PhoneNotFoundError{Phone: phone}
|
|
}
|
|
tried = true
|
|
|
|
users, err := m.updateContacts(ctx)
|
|
if err != nil {
|
|
return User{}, errors.Wrap(err, "update contacts")
|
|
}
|
|
|
|
for _, user := range users {
|
|
if u, ok := user.AsNotEmpty(); ok && u.Phone == phone {
|
|
return m.User(u), nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateDomain(domain string) error {
|
|
return deeplink.ValidateDomain(domain)
|
|
}
|
|
|
|
func (m *Manager) findPeerClass(p tg.PeerClass, users []tg.UserClass, chats []tg.ChatClass) (Peer, bool) {
|
|
switch p := p.(type) {
|
|
case *tg.PeerUser:
|
|
for _, user := range users {
|
|
u, ok := user.AsNotEmpty()
|
|
if ok && u.ID == p.UserID {
|
|
return m.User(u), true
|
|
}
|
|
}
|
|
case *tg.PeerChat:
|
|
for _, chat := range chats {
|
|
c, ok := chat.(*tg.Chat)
|
|
if ok && c.ID == p.ChatID {
|
|
return m.Chat(c), true
|
|
}
|
|
}
|
|
case *tg.PeerChannel:
|
|
for _, chat := range chats {
|
|
c, ok := chat.(*tg.Channel)
|
|
if ok && c.ID == p.ChannelID {
|
|
return m.Channel(c), true
|
|
}
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// ResolveDomain uses given domain to create new peer promise.
|
|
//
|
|
// May be prefixed with @ or not.
|
|
//
|
|
// Input examples:
|
|
//
|
|
// @telegram
|
|
// telegram
|
|
func (m *Manager) ResolveDomain(ctx context.Context, domain string) (Peer, error) {
|
|
domain = strings.TrimPrefix(domain, "@")
|
|
|
|
if err := validateDomain(domain); err != nil {
|
|
return nil, errors.Wrap(err, "validate domain")
|
|
}
|
|
|
|
ch := m.sg.DoChan(domain, func() (interface{}, error) {
|
|
result, err := m.api.ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
|
|
Username: domain,
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "resolve")
|
|
}
|
|
return result, nil
|
|
})
|
|
|
|
var result *tg.ContactsResolvedPeer
|
|
select {
|
|
case r := <-ch:
|
|
if err := r.Err; err != nil {
|
|
return nil, r.Err
|
|
}
|
|
result = r.Val.(*tg.ContactsResolvedPeer)
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
|
|
if err := m.applyEntities(ctx, result.Users, result.Chats); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p, ok := m.findPeerClass(result.Peer, result.Users, result.Chats)
|
|
if !ok {
|
|
return nil, &PeerNotFoundError{Peer: result.Peer}
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
// ResolveDeeplink uses given deeplink to create new peer promise.
|
|
//
|
|
// Input examples:
|
|
//
|
|
// t.me/telegram
|
|
// https://t.me/telegram
|
|
// tg:resolve?domain=telegram
|
|
// tg://resolve?domain=telegram
|
|
func (m *Manager) ResolveDeeplink(ctx context.Context, u string) (Peer, error) {
|
|
link, err := deeplink.Expect(u, deeplink.Resolve)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
domain := link.Args.Get("domain")
|
|
|
|
if err := validateDomain(domain); err != nil {
|
|
return nil, errors.Wrap(err, "validate domain")
|
|
}
|
|
|
|
return m.ResolveDomain(ctx, domain)
|
|
}
|
|
|
|
func (m *Manager) ResolveDeeplinkJoin(ctx context.Context, u string) (tg.ChatInviteClass, error) {
|
|
link, err := deeplink.Expect(u, deeplink.Join)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
domain := link.Args.Get("domain")
|
|
|
|
if err := validateDomain(domain); err != nil {
|
|
return nil, errors.Wrap(err, "validate domain")
|
|
}
|
|
|
|
inviteInfo, err := m.api.MessagesCheckChatInvite(ctx, link.Args.Get("invite"))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "check invite")
|
|
}
|
|
|
|
return inviteInfo, nil
|
|
}
|