move gotd fork into repo. (#111)
- 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
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
// Package deeplink contains deeplink parsing helpers.
|
||||
package deeplink
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
)
|
||||
|
||||
// Type is an enum type of Telegram deeplinks types.
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// Resolve is deeplink like
|
||||
//
|
||||
// tg:resolve?domain={domain}
|
||||
// tg://resolve?domain={domain}
|
||||
// https://t.me/{domain}
|
||||
// https://telegram.me/{domain}
|
||||
//
|
||||
Resolve Type = "resolve"
|
||||
|
||||
// Join is deeplink like
|
||||
//
|
||||
// tg:join?invite={hash}
|
||||
// tg://join?invite={hash}
|
||||
// https://t.me/joinchat/{hash}
|
||||
// https://telegram.me/joinchat/{hash}
|
||||
// t.me/+{hash}
|
||||
//
|
||||
Join Type = "join"
|
||||
)
|
||||
|
||||
// DeepLink represents Telegram deeplink.
|
||||
type DeepLink struct {
|
||||
Type Type
|
||||
Args url.Values
|
||||
}
|
||||
|
||||
func ensureParam(query url.Values, key string) error {
|
||||
if query.Get(key) == "" {
|
||||
return errors.Errorf("should have %q query parameter", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d DeepLink) validate() error {
|
||||
switch d.Type {
|
||||
case Resolve:
|
||||
return ensureParam(d.Args, "domain")
|
||||
case Join:
|
||||
return ensureParam(d.Args, "invite")
|
||||
default:
|
||||
return errors.Errorf("unsupported deeplink %q", d.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func parseTg(u *url.URL) (DeepLink, error) {
|
||||
query := u.Query()
|
||||
switch Type(u.Hostname()) {
|
||||
case Resolve:
|
||||
return DeepLink{
|
||||
Type: Resolve,
|
||||
Args: query,
|
||||
}, nil
|
||||
case Join:
|
||||
return DeepLink{
|
||||
Type: Join,
|
||||
Args: query,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return DeepLink{}, errors.Errorf("unsupported deeplink %q", u.String())
|
||||
}
|
||||
|
||||
func parseHTTPS(u *url.URL) (DeepLink, error) {
|
||||
cleanInviteHash := func(root string) string {
|
||||
hash := strings.Trim(root, "+ ")
|
||||
if u.RawPath == "" {
|
||||
hash = url.PathEscape(hash)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
p := strings.TrimPrefix(u.Path, "/")
|
||||
p = strings.TrimSuffix(p, "/")
|
||||
split := strings.Split(p, "/")
|
||||
var (
|
||||
root = split[0]
|
||||
base string
|
||||
)
|
||||
if len(split) > 1 {
|
||||
base = split[1]
|
||||
}
|
||||
|
||||
switch root {
|
||||
case "joinchat":
|
||||
query.Set("invite", cleanInviteHash(base))
|
||||
return DeepLink{
|
||||
Type: Join,
|
||||
Args: query,
|
||||
}, nil
|
||||
case "":
|
||||
return DeepLink{}, errors.Errorf("unsupported deeplink %q", u.String())
|
||||
}
|
||||
|
||||
switch root[0] {
|
||||
case ' ', '+':
|
||||
query.Set("invite", cleanInviteHash(root))
|
||||
return DeepLink{
|
||||
Type: Join,
|
||||
Args: query,
|
||||
}, nil
|
||||
default:
|
||||
if err := ValidateDomain(root); err != nil {
|
||||
return DeepLink{}, err
|
||||
}
|
||||
query.Set("domain", root)
|
||||
return DeepLink{
|
||||
Type: Resolve,
|
||||
Args: query,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func hasTelegramPrefix(link string) bool {
|
||||
return strings.HasPrefix(link, "t.me") ||
|
||||
strings.HasPrefix(link, "telegram.me") ||
|
||||
strings.HasPrefix(link, "telegram.dog")
|
||||
}
|
||||
|
||||
// IsDeeplinkLike returns true if string may be a valid deeplink.
|
||||
func IsDeeplinkLike(link string) bool {
|
||||
return strings.HasPrefix(link, "tg:") ||
|
||||
hasTelegramPrefix(link) ||
|
||||
strings.HasPrefix(link, "https://")
|
||||
}
|
||||
|
||||
// Parse parses and returns deeplink.
|
||||
func Parse(link string) (DeepLink, error) {
|
||||
switch {
|
||||
// Normalize case like t.me/gotd.
|
||||
case hasTelegramPrefix(link):
|
||||
link = strings.TrimSuffix("https://"+link, "/")
|
||||
// Normalize case like tg:resolve?domain=gotd.
|
||||
case !strings.HasPrefix(link, "tg://") && strings.HasPrefix(link, "tg:"):
|
||||
link = "tg://" + strings.TrimPrefix(link, "tg:")
|
||||
}
|
||||
|
||||
u, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return DeepLink{}, errors.Wrapf(err, "invalid URL %q", link)
|
||||
}
|
||||
|
||||
var d DeepLink
|
||||
switch {
|
||||
case u.Scheme == "https":
|
||||
switch strings.TrimPrefix(u.Hostname(), "www.") {
|
||||
case "t.me", "telegram.me", "telegram.dog":
|
||||
d, err = parseHTTPS(u)
|
||||
default:
|
||||
return DeepLink{}, errors.Errorf("invalid domain %q", link)
|
||||
}
|
||||
case u.Scheme == "tg":
|
||||
d, err = parseTg(u)
|
||||
default:
|
||||
return DeepLink{}, errors.Errorf("invalid deeplink %q", link)
|
||||
}
|
||||
if err != nil {
|
||||
return DeepLink{}, err
|
||||
}
|
||||
if err := d.validate(); err != nil {
|
||||
return DeepLink{}, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// Expect parses deeplink and check type its type.
|
||||
func Expect(link string, typ Type) (DeepLink, error) {
|
||||
l, err := Parse(link)
|
||||
if err != nil {
|
||||
return l, err
|
||||
}
|
||||
if l.Type != typ {
|
||||
return l, errors.Errorf("unexpected deeplink type %q", l.Type)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
Reference in New Issue
Block a user