Begin WhatsApp integration
This commit is contained in:
@@ -238,7 +238,23 @@ async def send_message_raw(service: str, recipient: str, text=None, attachments=
|
||||
if service_key == "signal":
|
||||
return await signalapi.send_message_raw(recipient, text, attachments or [])
|
||||
|
||||
if service_key in {"whatsapp", "instagram"}:
|
||||
if service_key == "whatsapp":
|
||||
runtime_client = get_runtime_client(service_key)
|
||||
if runtime_client and hasattr(runtime_client, "send_message_raw"):
|
||||
try:
|
||||
runtime_result = await runtime_client.send_message_raw(
|
||||
recipient,
|
||||
text=text,
|
||||
attachments=attachments or [],
|
||||
)
|
||||
if runtime_result is not False and runtime_result is not None:
|
||||
return runtime_result
|
||||
except Exception as exc:
|
||||
log.warning("%s runtime send failed: %s", service_key, exc)
|
||||
log.warning("whatsapp send skipped: runtime is unavailable or not paired")
|
||||
return False
|
||||
|
||||
if service_key == "instagram":
|
||||
runtime_client = get_runtime_client(service_key)
|
||||
if runtime_client and hasattr(runtime_client, "send_message_raw"):
|
||||
try:
|
||||
@@ -269,7 +285,18 @@ async def start_typing(service: str, recipient: str):
|
||||
await signalapi.start_typing(recipient)
|
||||
return True
|
||||
|
||||
if service_key in {"whatsapp", "instagram"}:
|
||||
if service_key == "whatsapp":
|
||||
runtime_client = get_runtime_client(service_key)
|
||||
if runtime_client and hasattr(runtime_client, "start_typing"):
|
||||
try:
|
||||
result = await runtime_client.start_typing(recipient)
|
||||
if result:
|
||||
return True
|
||||
except Exception as exc:
|
||||
log.warning("%s runtime start_typing failed: %s", service_key, exc)
|
||||
return False
|
||||
|
||||
if service_key == "instagram":
|
||||
runtime_client = get_runtime_client(service_key)
|
||||
if runtime_client and hasattr(runtime_client, "start_typing"):
|
||||
try:
|
||||
@@ -288,7 +315,18 @@ async def stop_typing(service: str, recipient: str):
|
||||
await signalapi.stop_typing(recipient)
|
||||
return True
|
||||
|
||||
if service_key in {"whatsapp", "instagram"}:
|
||||
if service_key == "whatsapp":
|
||||
runtime_client = get_runtime_client(service_key)
|
||||
if runtime_client and hasattr(runtime_client, "stop_typing"):
|
||||
try:
|
||||
result = await runtime_client.stop_typing(recipient)
|
||||
if result:
|
||||
return True
|
||||
except Exception as exc:
|
||||
log.warning("%s runtime stop_typing failed: %s", service_key, exc)
|
||||
return False
|
||||
|
||||
if service_key == "instagram":
|
||||
runtime_client = get_runtime_client(service_key)
|
||||
if runtime_client and hasattr(runtime_client, "stop_typing"):
|
||||
try:
|
||||
|
||||
@@ -30,6 +30,8 @@ class WhatsAppClient(ClientBase):
|
||||
self._connected = False
|
||||
self._last_qr_payload = ""
|
||||
self._accounts = []
|
||||
self._chat_presence = None
|
||||
self._chat_presence_media = None
|
||||
|
||||
self.enabled = bool(
|
||||
str(getattr(settings, "WHATSAPP_ENABLED", "false")).lower()
|
||||
@@ -71,6 +73,11 @@ class WhatsAppClient(ClientBase):
|
||||
try:
|
||||
from neonize.aioze.client import NewAClient
|
||||
from neonize.aioze import events as wa_events
|
||||
try:
|
||||
from neonize.utils.enum import ChatPresence, ChatPresenceMedia
|
||||
except Exception:
|
||||
ChatPresence = None
|
||||
ChatPresenceMedia = None
|
||||
try:
|
||||
from neonize.utils import build_jid as wa_build_jid
|
||||
except Exception:
|
||||
@@ -85,6 +92,8 @@ class WhatsAppClient(ClientBase):
|
||||
return
|
||||
|
||||
self._build_jid = wa_build_jid
|
||||
self._chat_presence = ChatPresence
|
||||
self._chat_presence_media = ChatPresenceMedia
|
||||
self._client = self._build_client(NewAClient)
|
||||
if self._client is None:
|
||||
self._publish_state(
|
||||
@@ -113,6 +122,59 @@ class WhatsAppClient(ClientBase):
|
||||
while not self._stopping:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def _register_event(self, event_cls, callback):
|
||||
if event_cls is None:
|
||||
return
|
||||
if self._client is None:
|
||||
return
|
||||
event_hook = getattr(self._client, "event", None)
|
||||
if not callable(event_hook):
|
||||
return
|
||||
try:
|
||||
decorator = event_hook(event_cls)
|
||||
decorator(callback)
|
||||
except Exception as exc:
|
||||
self.log.warning(
|
||||
"whatsapp event registration failed (%s): %s",
|
||||
getattr(event_cls, "__name__", str(event_cls)),
|
||||
exc,
|
||||
)
|
||||
|
||||
def _register_qr_handler(self):
|
||||
if self._client is None or not hasattr(self._client, "qr"):
|
||||
return
|
||||
|
||||
async def on_qr(client, raw_payload):
|
||||
qr_payload = self._decode_qr_payload(raw_payload)
|
||||
if not qr_payload:
|
||||
return
|
||||
self._last_qr_payload = qr_payload
|
||||
self._publish_state(
|
||||
connected=False,
|
||||
pair_qr=qr_payload,
|
||||
warning="Scan QR in WhatsApp Linked Devices.",
|
||||
)
|
||||
|
||||
try:
|
||||
self._client.qr(on_qr)
|
||||
except Exception as exc:
|
||||
self.log.warning("whatsapp qr handler registration failed: %s", exc)
|
||||
|
||||
def _decode_qr_payload(self, raw_payload):
|
||||
if raw_payload is None:
|
||||
return ""
|
||||
if isinstance(raw_payload, memoryview):
|
||||
raw_payload = raw_payload.tobytes()
|
||||
if isinstance(raw_payload, bytes):
|
||||
return raw_payload.decode("utf-8", errors="ignore").strip()
|
||||
if isinstance(raw_payload, (list, tuple)):
|
||||
for item in raw_payload:
|
||||
candidate = self._decode_qr_payload(item)
|
||||
if candidate:
|
||||
return candidate
|
||||
return ""
|
||||
return str(raw_payload).strip()
|
||||
|
||||
def _build_client(self, cls):
|
||||
candidates = []
|
||||
if self.database_url:
|
||||
@@ -137,8 +199,12 @@ class WhatsAppClient(ClientBase):
|
||||
connected_ev = getattr(wa_events, "ConnectedEv", None)
|
||||
message_ev = getattr(wa_events, "MessageEv", None)
|
||||
receipt_ev = getattr(wa_events, "ReceiptEv", None)
|
||||
chat_presence_ev = getattr(wa_events, "ChatPresenceEv", None)
|
||||
presence_ev = getattr(wa_events, "PresenceEv", None)
|
||||
pair_ev = getattr(wa_events, "PairStatusEv", None)
|
||||
qr_ev = getattr(wa_events, "QREv", None)
|
||||
|
||||
self._register_qr_handler()
|
||||
|
||||
if connected_ev is not None:
|
||||
|
||||
@@ -149,30 +215,38 @@ class WhatsAppClient(ClientBase):
|
||||
connected=True,
|
||||
warning="",
|
||||
accounts=[account] if account else [self.client_name],
|
||||
pair_qr="",
|
||||
)
|
||||
|
||||
self._client.event(on_connected)
|
||||
self._register_event(connected_ev, on_connected)
|
||||
|
||||
if message_ev is not None:
|
||||
|
||||
async def on_message(client, event: message_ev):
|
||||
await self._handle_message_event(event)
|
||||
|
||||
self._client.event(on_message)
|
||||
self._register_event(message_ev, on_message)
|
||||
|
||||
if receipt_ev is not None:
|
||||
|
||||
async def on_receipt(client, event: receipt_ev):
|
||||
await self._handle_receipt_event(event)
|
||||
|
||||
self._client.event(on_receipt)
|
||||
self._register_event(receipt_ev, on_receipt)
|
||||
|
||||
if chat_presence_ev is not None:
|
||||
|
||||
async def on_chat_presence(client, event: chat_presence_ev):
|
||||
await self._handle_chat_presence_event(event)
|
||||
|
||||
self._register_event(chat_presence_ev, on_chat_presence)
|
||||
|
||||
if presence_ev is not None:
|
||||
|
||||
async def on_presence(client, event: presence_ev):
|
||||
await self._handle_presence_event(event)
|
||||
|
||||
self._client.event(on_presence)
|
||||
self._register_event(presence_ev, on_presence)
|
||||
|
||||
if pair_ev is not None:
|
||||
|
||||
@@ -182,10 +256,41 @@ class WhatsAppClient(ClientBase):
|
||||
self._last_qr_payload = qr_payload
|
||||
self._publish_state(
|
||||
pair_qr=qr_payload,
|
||||
warning="Scan QR to pair WhatsApp account.",
|
||||
warning="Scan QR in WhatsApp Linked Devices.",
|
||||
)
|
||||
status_raw = self._pluck(event, "Status")
|
||||
status_text = str(status_raw or "").strip().lower()
|
||||
if status_text in {"2", "success"}:
|
||||
account = await self._resolve_account_identifier()
|
||||
self._connected = True
|
||||
self._publish_state(
|
||||
connected=True,
|
||||
warning="",
|
||||
accounts=[account] if account else [self.client_name],
|
||||
pair_qr="",
|
||||
)
|
||||
elif status_text in {"1", "error"}:
|
||||
error_text = str(self._pluck(event, "Error") or "").strip()
|
||||
self._publish_state(
|
||||
warning=error_text or "WhatsApp pairing failed. Retry scan.",
|
||||
)
|
||||
|
||||
self._client.event(on_pair_status)
|
||||
self._register_event(pair_ev, on_pair_status)
|
||||
|
||||
if qr_ev is not None:
|
||||
|
||||
async def on_qr_event(client, event: qr_ev):
|
||||
qr_payload = self._extract_pair_qr(event)
|
||||
if not qr_payload:
|
||||
return
|
||||
self._last_qr_payload = qr_payload
|
||||
self._publish_state(
|
||||
connected=False,
|
||||
pair_qr=qr_payload,
|
||||
warning="Scan QR in WhatsApp Linked Devices.",
|
||||
)
|
||||
|
||||
self._register_event(qr_ev, on_qr_event)
|
||||
|
||||
async def _maybe_await(self, value):
|
||||
if asyncio.iscoroutine(value):
|
||||
@@ -202,6 +307,13 @@ class WhatsAppClient(ClientBase):
|
||||
except Exception:
|
||||
return self.client_name
|
||||
# Support both dict-like and object-like payloads.
|
||||
for path in (
|
||||
("JID",),
|
||||
("jid",),
|
||||
):
|
||||
value = self._jid_to_identifier(self._pluck(me, *path))
|
||||
if value:
|
||||
return value
|
||||
for path in (
|
||||
("JID", "User"),
|
||||
("jid",),
|
||||
@@ -242,7 +354,7 @@ class WhatsAppClient(ClientBase):
|
||||
def _normalize_identifier_candidates(self, *values):
|
||||
out = set()
|
||||
for value in values:
|
||||
raw = str(value or "").strip()
|
||||
raw = self._jid_to_identifier(value)
|
||||
if not raw:
|
||||
continue
|
||||
out.add(raw)
|
||||
@@ -255,6 +367,19 @@ class WhatsAppClient(ClientBase):
|
||||
out.add(f"+{digits}")
|
||||
return out
|
||||
|
||||
def _jid_to_identifier(self, value):
|
||||
raw = str(value or "").strip()
|
||||
if not raw:
|
||||
return ""
|
||||
if not isinstance(value, str):
|
||||
user = self._pluck(value, "User") or self._pluck(value, "user")
|
||||
server = self._pluck(value, "Server") or self._pluck(value, "server")
|
||||
if user and server:
|
||||
return f"{user}@{server}"
|
||||
if user:
|
||||
return str(user)
|
||||
return raw
|
||||
|
||||
def _is_media_message(self, message_obj):
|
||||
media_fields = (
|
||||
"imageMessage",
|
||||
@@ -277,7 +402,7 @@ class WhatsAppClient(ClientBase):
|
||||
async def _download_event_media(self, event):
|
||||
if not self._client:
|
||||
return []
|
||||
msg_obj = self._pluck(event, "message")
|
||||
msg_obj = self._pluck(event, "message") or self._pluck(event, "Message")
|
||||
if msg_obj is None or not self._is_media_message(msg_obj):
|
||||
return []
|
||||
if not hasattr(self._client, "download_any"):
|
||||
@@ -324,27 +449,42 @@ class WhatsAppClient(ClientBase):
|
||||
]
|
||||
|
||||
async def _handle_message_event(self, event):
|
||||
msg_obj = self._pluck(event, "message")
|
||||
msg_obj = self._pluck(event, "message") or self._pluck(event, "Message")
|
||||
text = (
|
||||
self._pluck(msg_obj, "conversation")
|
||||
or self._pluck(msg_obj, "Conversation")
|
||||
or self._pluck(msg_obj, "extendedTextMessage", "text")
|
||||
or self._pluck(msg_obj, "ExtendedTextMessage", "Text")
|
||||
or self._pluck(msg_obj, "extended_text_message", "text")
|
||||
or ""
|
||||
)
|
||||
|
||||
sender = (
|
||||
self._pluck(event, "info", "message_source", "sender")
|
||||
or self._pluck(event, "info", "messageSource", "sender")
|
||||
or ""
|
||||
source = (
|
||||
self._pluck(event, "Info", "MessageSource")
|
||||
or self._pluck(event, "info", "message_source")
|
||||
or self._pluck(event, "info", "messageSource")
|
||||
)
|
||||
chat = (
|
||||
self._pluck(event, "info", "message_source", "chat")
|
||||
or self._pluck(event, "info", "messageSource", "chat")
|
||||
or ""
|
||||
is_from_me = bool(
|
||||
self._pluck(source, "IsFromMe")
|
||||
or self._pluck(source, "isFromMe")
|
||||
)
|
||||
if is_from_me:
|
||||
return
|
||||
|
||||
sender = self._jid_to_identifier(
|
||||
self._pluck(source, "Sender")
|
||||
or self._pluck(source, "sender")
|
||||
or self._pluck(source, "SenderAlt")
|
||||
or self._pluck(source, "senderAlt")
|
||||
)
|
||||
chat = self._jid_to_identifier(
|
||||
self._pluck(source, "Chat")
|
||||
or self._pluck(source, "chat")
|
||||
)
|
||||
raw_ts = (
|
||||
self._pluck(event, "info", "timestamp")
|
||||
self._pluck(event, "Info", "Timestamp")
|
||||
or self._pluck(event, "info", "timestamp")
|
||||
or self._pluck(event, "info", "message_timestamp")
|
||||
or self._pluck(event, "Timestamp")
|
||||
or self._pluck(event, "timestamp")
|
||||
)
|
||||
ts = self._normalize_timestamp(raw_ts)
|
||||
@@ -401,26 +541,33 @@ class WhatsAppClient(ClientBase):
|
||||
)
|
||||
|
||||
async def _handle_receipt_event(self, event):
|
||||
sender = (
|
||||
self._pluck(event, "info", "message_source", "sender")
|
||||
or self._pluck(event, "info", "messageSource", "sender")
|
||||
or ""
|
||||
source = (
|
||||
self._pluck(event, "MessageSource")
|
||||
or self._pluck(event, "info", "message_source")
|
||||
or self._pluck(event, "info", "messageSource")
|
||||
)
|
||||
chat = (
|
||||
self._pluck(event, "info", "message_source", "chat")
|
||||
or self._pluck(event, "info", "messageSource", "chat")
|
||||
or ""
|
||||
sender = self._jid_to_identifier(
|
||||
self._pluck(source, "Sender")
|
||||
or self._pluck(source, "sender")
|
||||
)
|
||||
chat = self._jid_to_identifier(
|
||||
self._pluck(source, "Chat") or self._pluck(source, "chat")
|
||||
)
|
||||
timestamps = []
|
||||
raw_ids = self._pluck(event, "message_ids") or []
|
||||
if isinstance(raw_ids, list):
|
||||
raw_ids = self._pluck(event, "MessageIDs") or self._pluck(event, "message_ids") or []
|
||||
if isinstance(raw_ids, (list, tuple, set)):
|
||||
for item in raw_ids:
|
||||
try:
|
||||
value = int(item)
|
||||
timestamps.append(value * 1000 if value < 10**12 else value)
|
||||
except Exception:
|
||||
continue
|
||||
read_ts = self._normalize_timestamp(self._pluck(event, "timestamp") or int(time.time() * 1000))
|
||||
read_ts = self._normalize_timestamp(
|
||||
self._pluck(event, "Timestamp")
|
||||
or self._pluck(event, "timestamp")
|
||||
or int(time.time() * 1000)
|
||||
)
|
||||
receipt_type = str(self._pluck(event, "Type") or "").strip()
|
||||
|
||||
for candidate in self._normalize_identifier_candidates(sender, chat):
|
||||
await self.ur.message_read(
|
||||
@@ -429,37 +576,68 @@ class WhatsAppClient(ClientBase):
|
||||
message_timestamps=timestamps,
|
||||
read_ts=read_ts,
|
||||
read_by=sender or chat,
|
||||
payload={"event": "receipt", "sender": str(sender), "chat": str(chat)},
|
||||
payload={
|
||||
"event": "receipt",
|
||||
"type": receipt_type,
|
||||
"sender": str(sender),
|
||||
"chat": str(chat),
|
||||
},
|
||||
)
|
||||
|
||||
async def _handle_presence_event(self, event):
|
||||
sender = (
|
||||
self._pluck(event, "message_source", "sender")
|
||||
or self._pluck(event, "info", "message_source", "sender")
|
||||
or ""
|
||||
async def _handle_chat_presence_event(self, event):
|
||||
source = (
|
||||
self._pluck(event, "MessageSource")
|
||||
or self._pluck(event, "message_source")
|
||||
or {}
|
||||
)
|
||||
chat = (
|
||||
self._pluck(event, "message_source", "chat")
|
||||
or self._pluck(event, "info", "message_source", "chat")
|
||||
or ""
|
||||
sender = self._jid_to_identifier(
|
||||
self._pluck(source, "Sender")
|
||||
or self._pluck(source, "sender")
|
||||
)
|
||||
presence = str(self._pluck(event, "presence") or "").strip().lower()
|
||||
chat = self._jid_to_identifier(
|
||||
self._pluck(source, "Chat") or self._pluck(source, "chat")
|
||||
)
|
||||
state = self._pluck(event, "State") or self._pluck(event, "state")
|
||||
state_text = str(state or "").strip().lower()
|
||||
is_typing = state_text in {"1", "composing", "chat_presence_composing"}
|
||||
|
||||
for candidate in self._normalize_identifier_candidates(sender, chat):
|
||||
if presence in {"composing", "typing", "recording"}:
|
||||
if is_typing:
|
||||
await self.ur.started_typing(
|
||||
self.service,
|
||||
identifier=candidate,
|
||||
payload={"presence": presence, "sender": str(sender), "chat": str(chat)},
|
||||
payload={"presence": state_text, "sender": str(sender), "chat": str(chat)},
|
||||
)
|
||||
elif presence:
|
||||
else:
|
||||
await self.ur.stopped_typing(
|
||||
self.service,
|
||||
identifier=candidate,
|
||||
payload={"presence": presence, "sender": str(sender), "chat": str(chat)},
|
||||
payload={"presence": state_text, "sender": str(sender), "chat": str(chat)},
|
||||
)
|
||||
|
||||
async def _handle_presence_event(self, event):
|
||||
sender = self._jid_to_identifier(
|
||||
self._pluck(event, "From", "User")
|
||||
or self._pluck(event, "from", "user")
|
||||
)
|
||||
is_unavailable = bool(
|
||||
self._pluck(event, "Unavailable") or self._pluck(event, "unavailable")
|
||||
)
|
||||
|
||||
for candidate in self._normalize_identifier_candidates(sender):
|
||||
if is_unavailable:
|
||||
await self.ur.stopped_typing(
|
||||
self.service,
|
||||
identifier=candidate,
|
||||
payload={"presence": "offline", "sender": str(sender)},
|
||||
)
|
||||
|
||||
def _extract_pair_qr(self, event):
|
||||
codes = self._pluck(event, "Codes") or self._pluck(event, "codes") or []
|
||||
decoded_codes = self._decode_qr_payload(codes)
|
||||
if decoded_codes:
|
||||
return decoded_codes
|
||||
|
||||
for path in (
|
||||
("qr",),
|
||||
("qr_code",),
|
||||
@@ -537,6 +715,7 @@ class WhatsAppClient(ClientBase):
|
||||
return False
|
||||
|
||||
sent_any = False
|
||||
sent_ts = 0
|
||||
for attachment in attachments or []:
|
||||
payload = await self._fetch_attachment_payload(attachment)
|
||||
if not payload:
|
||||
@@ -547,13 +726,17 @@ class WhatsAppClient(ClientBase):
|
||||
|
||||
try:
|
||||
if mime.startswith("image/") and hasattr(self._client, "send_image"):
|
||||
await self._maybe_await(self._client.send_image(jid, data, caption=""))
|
||||
response = await self._maybe_await(
|
||||
self._client.send_image(jid, data, caption="")
|
||||
)
|
||||
elif mime.startswith("video/") and hasattr(self._client, "send_video"):
|
||||
await self._maybe_await(self._client.send_video(jid, data, caption=""))
|
||||
response = await self._maybe_await(
|
||||
self._client.send_video(jid, data, caption="")
|
||||
)
|
||||
elif mime.startswith("audio/") and hasattr(self._client, "send_audio"):
|
||||
await self._maybe_await(self._client.send_audio(jid, data))
|
||||
response = await self._maybe_await(self._client.send_audio(jid, data))
|
||||
elif hasattr(self._client, "send_document"):
|
||||
await self._maybe_await(
|
||||
response = await self._maybe_await(
|
||||
self._client.send_document(
|
||||
jid,
|
||||
data,
|
||||
@@ -562,22 +745,36 @@ class WhatsAppClient(ClientBase):
|
||||
caption="",
|
||||
)
|
||||
)
|
||||
else:
|
||||
response = None
|
||||
sent_ts = max(
|
||||
sent_ts,
|
||||
self._normalize_timestamp(self._pluck(response, "Timestamp") or 0),
|
||||
)
|
||||
sent_any = True
|
||||
except Exception as exc:
|
||||
self.log.warning("whatsapp attachment send failed: %s", exc)
|
||||
|
||||
if text:
|
||||
try:
|
||||
await self._maybe_await(self._client.send_message(jid, text))
|
||||
response = await self._maybe_await(self._client.send_message(jid, text))
|
||||
sent_any = True
|
||||
except TypeError:
|
||||
await self._maybe_await(self._client.send_message(jid, message=text))
|
||||
response = await self._maybe_await(
|
||||
self._client.send_message(jid, message=text)
|
||||
)
|
||||
sent_any = True
|
||||
except Exception as exc:
|
||||
self.log.warning("whatsapp text send failed: %s", exc)
|
||||
return False
|
||||
sent_ts = max(
|
||||
sent_ts,
|
||||
self._normalize_timestamp(self._pluck(response, "Timestamp") or 0),
|
||||
)
|
||||
|
||||
return int(time.time() * 1000) if sent_any else False
|
||||
if not sent_any:
|
||||
return False
|
||||
return sent_ts or int(time.time() * 1000)
|
||||
|
||||
async def start_typing(self, identifier):
|
||||
if not self._client:
|
||||
@@ -585,14 +782,28 @@ class WhatsAppClient(ClientBase):
|
||||
jid = self._to_jid(identifier)
|
||||
if not jid:
|
||||
return False
|
||||
for method_name in ("send_chat_presence", "set_chat_presence"):
|
||||
if hasattr(self._client, method_name):
|
||||
method = getattr(self._client, method_name)
|
||||
try:
|
||||
await self._maybe_await(method(jid, "composing"))
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
if (
|
||||
hasattr(self._client, "send_chat_presence")
|
||||
and self._chat_presence is not None
|
||||
and self._chat_presence_media is not None
|
||||
):
|
||||
try:
|
||||
await self._maybe_await(
|
||||
self._client.send_chat_presence(
|
||||
jid,
|
||||
self._chat_presence.CHAT_PRESENCE_COMPOSING,
|
||||
self._chat_presence_media.CHAT_PRESENCE_MEDIA_TEXT,
|
||||
)
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(self._client, "set_chat_presence"):
|
||||
try:
|
||||
await self._maybe_await(self._client.set_chat_presence(jid, "composing"))
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def stop_typing(self, identifier):
|
||||
@@ -601,14 +812,28 @@ class WhatsAppClient(ClientBase):
|
||||
jid = self._to_jid(identifier)
|
||||
if not jid:
|
||||
return False
|
||||
for method_name in ("send_chat_presence", "set_chat_presence"):
|
||||
if hasattr(self._client, method_name):
|
||||
method = getattr(self._client, method_name)
|
||||
try:
|
||||
await self._maybe_await(method(jid, "paused"))
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
if (
|
||||
hasattr(self._client, "send_chat_presence")
|
||||
and self._chat_presence is not None
|
||||
and self._chat_presence_media is not None
|
||||
):
|
||||
try:
|
||||
await self._maybe_await(
|
||||
self._client.send_chat_presence(
|
||||
jid,
|
||||
self._chat_presence.CHAT_PRESENCE_PAUSED,
|
||||
self._chat_presence_media.CHAT_PRESENCE_MEDIA_TEXT,
|
||||
)
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(self._client, "set_chat_presence"):
|
||||
try:
|
||||
await self._maybe_await(self._client.set_chat_presence(jid, "paused"))
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def fetch_attachment(self, attachment_ref):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from django.conf import settings
|
||||
from django.shortcuts import render
|
||||
from django.views import View
|
||||
from mixins.views import ObjectList, ObjectRead
|
||||
@@ -56,9 +55,6 @@ class WhatsAppAccounts(SuperUserRequiredMixin, ObjectList):
|
||||
"service_label": "WhatsApp",
|
||||
"account_add_url_name": "whatsapp_account_add",
|
||||
"show_contact_actions": False,
|
||||
"endpoint_base": str(
|
||||
getattr(settings, "WHATSAPP_HTTP_URL", "http://whatsapp:8080")
|
||||
).rstrip("/"),
|
||||
"service_warning": transport.get_service_warning("whatsapp"),
|
||||
}
|
||||
return self._normalize_accounts(transport.list_accounts("whatsapp"))
|
||||
|
||||
Reference in New Issue
Block a user