Implement WhatsApp linking

This commit is contained in:
2026-02-15 23:21:18 +00:00
parent 88224d972c
commit 10af1e4d6b
3 changed files with 63 additions and 36 deletions

View File

@@ -1,4 +1,5 @@
import asyncio
import os
import re
import time
from urllib.parse import quote_plus
@@ -49,6 +50,8 @@ class WhatsAppClient(ClientBase):
self.database_url = str(
getattr(settings, "WHATSAPP_DATABASE_URL", "")
).strip()
safe_name = re.sub(r"[^a-zA-Z0-9_.-]+", "_", self.client_name) or "gia_whatsapp"
self.session_db = self.database_url or f"/tmp/{safe_name}.db"
transport.register_runtime_client(self.service, self)
self._publish_state(
@@ -60,6 +63,7 @@ class WhatsAppClient(ClientBase):
),
accounts=[],
last_event="init",
session_db=self.session_db,
)
def _publish_state(self, **updates):
@@ -78,6 +82,7 @@ class WhatsAppClient(ClientBase):
async def _run(self):
try:
import neonize.aioze.client as wa_client_mod
from neonize.aioze.client import NewAClient
from neonize.aioze import events as wa_events
try:
@@ -100,9 +105,41 @@ class WhatsAppClient(ClientBase):
self.log.warning("whatsapp neonize import failed: %s", exc)
return
# Neonize async module ships with its own global event loop object.
# In this runtime we already have a live asyncio loop; bind Neonize's
# globals to it so QR/pair callbacks and connect tasks actually execute.
try:
wa_events.event_global_loop = self.loop
wa_client_mod.event_global_loop = self.loop
self._publish_state(
neonize_loop_bound=True,
neonize_loop_type=str(type(self.loop).__name__),
last_event="neonize_loop_bound",
)
except Exception as exc:
self._publish_state(
neonize_loop_bound=False,
last_event="neonize_loop_bind_failed",
last_error=str(exc),
)
self.log.warning("failed binding neonize loop: %s", exc)
self._build_jid = wa_build_jid
self._chat_presence = ChatPresence
self._chat_presence_media = ChatPresenceMedia
try:
db_dir = os.path.dirname(self.session_db)
if db_dir:
os.makedirs(db_dir, exist_ok=True)
except Exception as exc:
self._publish_state(
connected=False,
warning=f"WhatsApp DB path setup failed: {exc}",
last_event="db_path_setup_failed",
last_error=str(exc),
)
self.log.warning("whatsapp db path setup failed: %s", exc)
return
self._client = self._build_client(NewAClient)
if self._client is None:
self._publish_state(
@@ -216,8 +253,13 @@ class WhatsAppClient(ClientBase):
return
now_ts = int(time.time())
try:
if hasattr(self._client, "is_connected"):
connected_value = await self._maybe_await(self._client.is_connected())
check_connected = getattr(self._client, "is_connected", None)
if check_connected is not None:
connected_value = (
await self._maybe_await(check_connected())
if callable(check_connected)
else await self._maybe_await(check_connected)
)
if connected_value:
self._connected = True
self._publish_state(
@@ -322,6 +364,7 @@ class WhatsAppClient(ClientBase):
last_event="qr_handler",
pair_status="qr_ready",
qr_received_at=int(time.time()),
qr_probe_result="event",
last_error="",
)
@@ -359,23 +402,16 @@ class WhatsAppClient(ClientBase):
return str(raw_payload).strip()
def _build_client(self, cls):
candidates = []
if self.database_url:
candidates.append((self.client_name, self.database_url))
candidates.append((self.client_name,))
for args in candidates:
try:
return cls(*args)
except TypeError:
continue
except Exception as exc:
self.log.warning("whatsapp client init failed for args %s: %s", args, exc)
# NewAClient first arg is the SQLite filename / DB string.
try:
if self.database_url:
return cls(name=self.client_name, database=self.database_url)
return cls(name=self.client_name)
return cls(self.session_db)
except Exception as exc:
self.log.warning("whatsapp client init failed: %s", exc)
self.log.warning("whatsapp client init failed (%s): %s", self.session_db, exc)
self._publish_state(
last_event="client_init_exception",
last_error=str(exc),
session_db=self.session_db,
)
return None
def _register_event_handlers(self, wa_events):
@@ -459,6 +495,7 @@ class WhatsAppClient(ClientBase):
last_event="pair_status_qr",
pair_status="qr_ready",
qr_received_at=int(time.time()),
qr_probe_result="event",
last_error="",
)
status_raw = self._pluck(event, "Status")
@@ -501,6 +538,7 @@ class WhatsAppClient(ClientBase):
last_event="qr_event",
pair_status="qr_ready",
qr_received_at=int(time.time()),
qr_probe_result="event",
last_error="",
)