Source code for astra.models.user

# -----------------------------------------------------------
# Astra - WhatsApp Client Framework
# Licensed under the Apache License 2.0.
# -----------------------------------------------------------

"""
This module defines the identity and user representation in Astra.
It encapsulates WhatsApp JIDs and contact metadata.
"""

from dataclasses import dataclass
from typing import Optional, Dict, Any

[docs] @dataclass(frozen=True) class JID: """ Represents a WhatsApp Jabber ID (JID). A JID uniquely identifies a user, group, or newsletter on WhatsApp. Example: '1234567890@c.us' (User) or '1234567890@g.us' (Group). """ user: str server: str serialized: str
[docs] @classmethod def parse(cls, raw: str) -> "JID": """ Parses a raw JID string into a structured JID object. Args: raw: The raw WhatsApp ID string (e.g., "12345@c.us"). Returns: A structured JID instance. Example: >>> jid = JID.parse("12345@c.us") >>> print(jid.user) "12345" """ if not raw: return cls(user="unknown", server="unknown", serialized="unknown@unknown") if "@" not in raw: # Default to user server if missing return cls(user=raw, server="c.us", serialized=f"{raw}@c.us") username, server = raw.split("@", 1) return cls(user=username, server=server, serialized=raw)
@property def primary(self) -> str: """ Returns the primary (base) JID without device suffixes. Example: '12345:4@lid' -> '12345@lid' """ if ":" in self.user: base_user = self.user.split(":", 1)[0] return f"{base_user}@{self.server}" return self.serialized def __str__(self) -> str: return self.serialized
[docs] @dataclass class User: """ Represents a WhatsApp Actor - a person, group participant, or yourself. This class holds metadata about a contact or user, such as their display name and business status. """ id: JID name: Optional[str] = None push_name: Optional[str] = None is_me: bool = False is_business: bool = False is_my_contact: bool = False verified_name: Optional[str] = None _client: Any = None # Bound Astra Client @property def title(self) -> str: """Standardized title property for entity resolution.""" return self.name or self.push_name or self.verified_name or self.id.user
[docs] async def send_message(self, text: str, **kwargs) -> Any: """Sends a private message to this user.""" if not self._client: raise RuntimeError("User is not bound to a client.") return await self._client.chat.send_message(self.id.serialized, text, **kwargs)
[docs] async def block(self) -> bool: """Blocks this user.""" if not self._client: raise RuntimeError("User is not bound to a client.") return await self._client.bridge.call("blockContact", self.id.serialized)
[docs] @classmethod def from_payload(cls, data: Dict[str, Any], client: Any = None) -> "User": """ Creates a User instance from a raw dictionary payload. This is typically used when receiving data from the WhatsApp Web bridge. Args: data: The raw dictionary containing user data. Returns: A populated User instance. """ return cls( id=JID.parse(data.get("id", "")), name=data.get("name"), push_name=data.get("pushname"), is_me=data.get("isMe", False), is_business=data.get("isBusiness", False), is_my_contact=data.get("isMyContact", False), verified_name=data.get("verifiedName"), _client=client )