snc: use local caches for resolving identifiers
Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
|||||||
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *TelegramClient) getResolveIdentifierResponseForUserID(ctx context.Context, user tg.UserClass) (*bridgev2.ResolveIdentifierResponse, error) {
|
func (t *TelegramClient) getResolveIdentifierResponseForUser(ctx context.Context, user tg.UserClass) (*bridgev2.ResolveIdentifierResponse, error) {
|
||||||
networkUserID := ids.MakeUserID(user.GetID())
|
networkUserID := ids.MakeUserID(user.GetID())
|
||||||
if userInfo, err := t.getUserInfoFromTelegramUser(ctx, user); err != nil {
|
if userInfo, err := t.getUserInfoFromTelegramUser(ctx, user); err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user info: %w", err)
|
return nil, fmt.Errorf("failed to get user info: %w", err)
|
||||||
@@ -32,6 +32,26 @@ func (t *TelegramClient) getResolveIdentifierResponseForUserID(ctx context.Conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TelegramClient) getResolveIdentifierResponseForUserID(ctx context.Context, userID int64) (resp *bridgev2.ResolveIdentifierResponse, err error) {
|
||||||
|
networkUserID := ids.MakeUserID(userID)
|
||||||
|
resp = &bridgev2.ResolveIdentifierResponse{
|
||||||
|
UserID: networkUserID,
|
||||||
|
Chat: &bridgev2.CreateChatResponse{
|
||||||
|
PortalKey: ids.PeerTypeUser.AsPortalKey(userID, t.loginID),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp.Ghost, err = t.main.Bridge.GetExistingGhostByID(ctx, networkUserID)
|
||||||
|
if err != nil {
|
||||||
|
// Try to fetch the user from Telegram
|
||||||
|
if user, err := t.getSingleUser(ctx, userID); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get user with ID %d: %w", userID, err)
|
||||||
|
} else {
|
||||||
|
return t.getResolveIdentifierResponseForUser(ctx, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Parses usernames with or without the @ sign in front of the username.
|
// Parses usernames with or without the @ sign in front of the username.
|
||||||
// This verifies the following restrictions:
|
// This verifies the following restrictions:
|
||||||
// - Usernames must be at least 5 characters long
|
// - Usernames must be at least 5 characters long
|
||||||
@@ -52,39 +72,42 @@ func (t *TelegramClient) ResolveIdentifier(ctx context.Context, identifier strin
|
|||||||
return nil, fmt.Errorf("failed to get user ID by phone number: %w", err)
|
return nil, fmt.Errorf("failed to get user ID by phone number: %w", err)
|
||||||
} else if userID == 0 {
|
} else if userID == 0 {
|
||||||
return nil, fmt.Errorf("no user found with phone number '%s'", normalized)
|
return nil, fmt.Errorf("no user found with phone number '%s'", normalized)
|
||||||
} else if user, err := t.getSingleUser(ctx, userID); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get user with ID %d: %w", userID, err)
|
|
||||||
} else {
|
} else {
|
||||||
return t.getResolveIdentifierResponseForUserID(ctx, user)
|
return t.getResolveIdentifierResponseForUserID(ctx, userID)
|
||||||
}
|
}
|
||||||
} else if userID, err := strconv.ParseInt(identifier, 10, 64); err == nil {
|
} else if userID, err := strconv.ParseInt(identifier, 10, 64); err == nil {
|
||||||
// This is an integer, try and parse it as a Telegram User ID
|
// This is an integer, try and parse it as a Telegram User ID
|
||||||
if user, err := t.getSingleUser(ctx, userID); err != nil {
|
return t.getResolveIdentifierResponseForUserID(ctx, userID)
|
||||||
return nil, fmt.Errorf("failed to get user with ID %d: %w", userID, err)
|
|
||||||
} else {
|
|
||||||
return t.getResolveIdentifierResponseForUserID(ctx, user)
|
|
||||||
}
|
|
||||||
} else if match := usernameRe.FindStringSubmatch(identifier); match != nil && !strings.Contains(identifier, "__") {
|
} else if match := usernameRe.FindStringSubmatch(identifier); match != nil && !strings.Contains(identifier, "__") {
|
||||||
resolved, err := APICallWithUpdates(ctx, t, func() (*tg.ContactsResolvedPeer, error) {
|
// This is a username
|
||||||
return t.client.API().ContactsResolveUsername(ctx, match[1])
|
userID, err := t.ScopedStore.GetUserIDByUsername(ctx, match[1])
|
||||||
})
|
if err == nil || userID != 0 {
|
||||||
if err != nil {
|
// We know this username.
|
||||||
if tg.IsUsernameNotOccupied(err) {
|
return t.getResolveIdentifierResponseForUserID(ctx, userID)
|
||||||
return nil, fmt.Errorf("no user found with username '%s'", match[1])
|
} else {
|
||||||
} else {
|
// We don't know this username, try to resolve the username from
|
||||||
return nil, fmt.Errorf("failed to resolve username: %w", err)
|
// Telegram.
|
||||||
|
resolved, err := APICallWithUpdates(ctx, t, func() (*tg.ContactsResolvedPeer, error) {
|
||||||
|
return t.client.API().ContactsResolveUsername(ctx, match[1])
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if tg.IsUsernameNotOccupied(err) {
|
||||||
|
return nil, fmt.Errorf("no user found with username '%s'", match[1])
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("failed to resolve username: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
peer, ok := resolved.GetPeer().(*tg.PeerUser)
|
||||||
peer, ok := resolved.GetPeer().(*tg.PeerUser)
|
if !ok {
|
||||||
if !ok {
|
return nil, fmt.Errorf("unexpected peer type: %T", resolved.GetPeer())
|
||||||
return nil, fmt.Errorf("unexpected peer type: %T", resolved.GetPeer())
|
|
||||||
}
|
|
||||||
for _, user := range resolved.GetUsers() {
|
|
||||||
if user.GetID() == peer.GetUserID() {
|
|
||||||
return t.getResolveIdentifierResponseForUserID(ctx, user)
|
|
||||||
}
|
}
|
||||||
|
for _, user := range resolved.GetUsers() {
|
||||||
|
if user.GetID() == peer.GetUserID() {
|
||||||
|
return t.getResolveIdentifierResponseForUser(ctx, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("peer user not found in contact resolved response")
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("peer user not found in contact resolved response")
|
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("invalid identifier: %s (must be a phone number, username, or Telegram user ID)", identifier)
|
return nil, fmt.Errorf("invalid identifier: %s (must be a phone number, username, or Telegram user ID)", identifier)
|
||||||
}
|
}
|
||||||
@@ -103,20 +126,18 @@ func (t *TelegramClient) SearchUsers(ctx context.Context, query string) (resp []
|
|||||||
}
|
}
|
||||||
|
|
||||||
addResult := func(p tg.PeerClass) error {
|
addResult := func(p tg.PeerClass) error {
|
||||||
peer, ok := p.(*tg.PeerUser)
|
if peer, ok := p.(*tg.PeerUser); !ok {
|
||||||
if !ok {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
} else if user, ok := users[peer.GetUserID()]; ok {
|
||||||
if user, ok := users[peer.GetUserID()]; ok {
|
if r, err := t.getResolveIdentifierResponseForUser(ctx, user); err != nil {
|
||||||
if r, err := t.getResolveIdentifierResponseForUserID(ctx, user); err != nil {
|
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
resp = append(resp, r)
|
resp = append(resp, r)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("peer user not found in contact search response")
|
return fmt.Errorf("peer user not found in contact search response")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range contactsFound.MyResults {
|
for _, p := range contactsFound.MyResults {
|
||||||
@@ -160,7 +181,7 @@ func (t *TelegramClient) GetContactList(ctx context.Context) (resp []*bridgev2.R
|
|||||||
|
|
||||||
for _, contact := range contacts.Contacts {
|
for _, contact := range contacts.Contacts {
|
||||||
if user, ok := users[contact.UserID]; ok {
|
if user, ok := users[contact.UserID]; ok {
|
||||||
if r, err := t.getResolveIdentifierResponseForUserID(ctx, user); err != nil {
|
if r, err := t.getResolveIdentifierResponseForUser(ctx, user); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
resp = append(resp, r)
|
resp = append(resp, r)
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const (
|
|||||||
VALUES ($1, $2)
|
VALUES ($1, $2)
|
||||||
ON CONFLICT (username) DO UPDATE SET entity_id=excluded.entity_id
|
ON CONFLICT (username) DO UPDATE SET entity_id=excluded.entity_id
|
||||||
`
|
`
|
||||||
|
getByUsernameQuery = "SELECT entity_id FROM telegram_username WHERE LOWER(username)=$1"
|
||||||
clearUsernameQuery = `DELETE FROM telegram_username WHERE entity_id=$1`
|
clearUsernameQuery = `DELETE FROM telegram_username WHERE entity_id=$1`
|
||||||
|
|
||||||
// User Phone Number Queries
|
// User Phone Number Queries
|
||||||
@@ -196,6 +197,14 @@ func (s *ScopedStore) SetUsername(ctx context.Context, userID int64, username st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ScopedStore) GetUserIDByUsername(ctx context.Context, username string) (userID int64, err error) {
|
||||||
|
err = s.db.QueryRow(ctx, getByUsernameQuery, username).Scan(&userID)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ScopedStore) GetUserIDByPhoneNumber(ctx context.Context, phoneNumber string) (userID int64, err error) {
|
func (s *ScopedStore) GetUserIDByPhoneNumber(ctx context.Context, phoneNumber string) (userID int64, err error) {
|
||||||
err = s.db.QueryRow(ctx, getEntityIDForPhoneNumber, phoneNumber).Scan(&userID)
|
err = s.db.QueryRow(ctx, getEntityIDForPhoneNumber, phoneNumber).Scan(&userID)
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ CREATE TABLE telegram_username (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX telegram_username_entity_idx ON telegram_username (entity_id);
|
CREATE INDEX telegram_username_entity_idx ON telegram_username (entity_id);
|
||||||
|
CREATE INDEX telegram_username_username_idx ON telegram_username (LOWER(username));
|
||||||
|
|
||||||
CREATE TABLE telegram_phone_number (
|
CREATE TABLE telegram_phone_number (
|
||||||
phone_number TEXT NOT NULL,
|
phone_number TEXT NOT NULL,
|
||||||
|
|||||||
Reference in New Issue
Block a user