Implement search by phone number
Searching by phone number will check for a contact on WhatsApp, and if existing, automatically add that contact to the roster. However, contact names are not resolved until after they've been added to the roster and been communicated with.
This commit is contained in:
parent
5ee1a02463
commit
f4ecbee5d2
|
@ -3,7 +3,7 @@ from pathlib import Path
|
|||
from shelve import open
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from slidge import BaseGateway, GatewayUser, global_config
|
||||
from slidge import BaseGateway, FormField, GatewayUser, global_config
|
||||
|
||||
from . import config
|
||||
from .generated import whatsapp
|
||||
|
@ -33,6 +33,10 @@ class Gateway(BaseGateway):
|
|||
WELCOME_MESSAGE = WELCOME_MESSAGE
|
||||
REGISTRATION_FIELDS = []
|
||||
|
||||
SEARCH_FIELDS = [
|
||||
FormField(var="phone", label="Phone number", required=True),
|
||||
]
|
||||
|
||||
ROSTER_GROUP = "WhatsApp"
|
||||
|
||||
MARK_ALL_MESSAGES = True
|
||||
|
|
|
@ -435,6 +435,10 @@ func (s *Session) GetGroups() ([]Group, error) {
|
|||
// is also given, GetAvatar will return an empty [Avatar] instance with no error if the remote state
|
||||
// for the given ID has not changed.
|
||||
func (s *Session) GetAvatar(resourceID, avatarID string) (Avatar, error) {
|
||||
if s.client == nil || s.client.Store.ID == nil {
|
||||
return Avatar{}, fmt.Errorf("Cannot get avatar for unauthenticated session")
|
||||
}
|
||||
|
||||
jid, err := types.ParseJID(resourceID)
|
||||
if err != nil {
|
||||
return Avatar{}, fmt.Errorf("Could not parse JID for avatar: %s", err)
|
||||
|
@ -454,6 +458,10 @@ func (s *Session) GetAvatar(resourceID, avatarID string) (Avatar, error) {
|
|||
// profile picture for our own user by providing an empty JID. The unique picture ID is returned,
|
||||
// typically used as a cache reference or in providing to future calls for [Session.GetAvatar].
|
||||
func (s *Session) SetAvatar(resourceID, avatarPath string) (string, error) {
|
||||
if s.client == nil || s.client.Store.ID == nil {
|
||||
return "", fmt.Errorf("Cannot set avatar for unauthenticated session")
|
||||
}
|
||||
|
||||
var jid types.JID
|
||||
var err error
|
||||
|
||||
|
@ -485,6 +493,27 @@ func (s *Session) SetAvatar(resourceID, avatarPath string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// FindContact attempts to check for a registered contact on WhatsApp corresponding to the given
|
||||
// phone number, returning a concrete instance if found; typically, only the contact JID is set. No
|
||||
// error is returned if no contact was found, but any unexpected errors will otherwise be returned
|
||||
// directly.
|
||||
func (s *Session) FindContact(phone string) (Contact, error) {
|
||||
if s.client == nil || s.client.Store.ID == nil {
|
||||
return Contact{}, fmt.Errorf("Cannot find contact for unauthenticated session")
|
||||
}
|
||||
|
||||
resp, err := s.client.IsOnWhatsApp([]string{phone})
|
||||
if err != nil {
|
||||
return Contact{}, fmt.Errorf("Failed looking up contact '%s': %s", phone, err)
|
||||
} else if len(resp) != 1 {
|
||||
return Contact{}, fmt.Errorf("Failed looking up contact '%s': invalid response", phone)
|
||||
} else if !resp[0].IsIn || resp[0].JID.IsEmpty() {
|
||||
return Contact{}, nil
|
||||
}
|
||||
|
||||
return Contact{JID: resp[0].JID.ToNonAD().String()}, nil
|
||||
}
|
||||
|
||||
// SetEventHandler assigns the given handler function for propagating internal events into the Python
|
||||
// gateway. Note that the event handler function is not entirely safe to use directly, and all calls
|
||||
// should instead be made via the [propagateEvent] function.
|
||||
|
|
|
@ -8,12 +8,13 @@ from re import search
|
|||
from shelve import open
|
||||
from tempfile import mkstemp
|
||||
from threading import Lock
|
||||
from typing import Optional, Union
|
||||
from typing import Optional, Union, cast
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from linkpreview import Link, LinkPreview
|
||||
from slidge import BaseSession, GatewayUser, global_config
|
||||
from slidge import BaseSession, FormField, GatewayUser, SearchResult, global_config
|
||||
from slidge.contact.roster import ContactIsUser
|
||||
from slidge.util import is_valid_phone_number
|
||||
from slidge.util.types import (
|
||||
LegacyAttachment,
|
||||
MessageReference,
|
||||
|
@ -467,7 +468,25 @@ class Session(BaseSession[str, Recipient]):
|
|||
self.whatsapp.SetAvatar("", await get_bytes_temp(bytes_) if bytes_ else "")
|
||||
|
||||
async def search(self, form_values: dict[str, str]):
|
||||
self.send_gateway_message("Searching on WhatsApp has not been implemented yet.")
|
||||
"""
|
||||
Searches for, and automatically adds, WhatsApp contact based on phone number. Phone numbers
|
||||
not registered on WhatsApp will be ignored with no error.
|
||||
"""
|
||||
phone = form_values.get("phone")
|
||||
if not is_valid_phone_number(phone):
|
||||
raise ValueError("Not a valid phone number", phone)
|
||||
|
||||
data = self.whatsapp.FindContact(phone)
|
||||
if not data.JID:
|
||||
return
|
||||
|
||||
await self.contacts.add_whatsapp_contact(data)
|
||||
contact = await self.contacts.by_legacy_id(data.JID)
|
||||
|
||||
return SearchResult(
|
||||
fields=[FormField("phone"), FormField("jid", type="jid-single")],
|
||||
items=[{"phone": cast(str, phone), "jid": contact.jid.bare}],
|
||||
)
|
||||
|
||||
async def get_contact_or_participant(
|
||||
self, legacy_contact_id: str, legacy_group_jid: str
|
||||
|
|
Loading…
Reference in New Issue