Implement executing tasks

This commit is contained in:
2026-03-03 16:41:28 +00:00
parent d6bd56dace
commit 9c14e51b43
42 changed files with 3410 additions and 121 deletions

View File

@@ -2355,8 +2355,29 @@ class WhatsAppClient(ClientBase):
return "application/octet-stream"
def _extract_reaction_event(self, message_obj):
node = self._pluck(message_obj, "reactionMessage") or self._pluck(
message_obj, "reaction_message"
node = (
self._pluck(message_obj, "reactionMessage")
or self._pluck(message_obj, "reaction_message")
or self._pluck(message_obj, "ephemeralMessage", "message", "reactionMessage")
or self._pluck(message_obj, "ephemeral_message", "message", "reaction_message")
or self._pluck(message_obj, "viewOnceMessage", "message", "reactionMessage")
or self._pluck(message_obj, "view_once_message", "message", "reaction_message")
or self._pluck(message_obj, "viewOnceMessageV2", "message", "reactionMessage")
or self._pluck(message_obj, "view_once_message_v2", "message", "reaction_message")
or self._pluck(
message_obj,
"viewOnceMessageV2Extension",
"message",
"reactionMessage",
)
or self._pluck(
message_obj,
"view_once_message_v2_extension",
"message",
"reaction_message",
)
or self._pluck(message_obj, "protocolMessage", "reactionMessage")
or self._pluck(message_obj, "protocol_message", "reaction_message")
)
if not node:
return None
@@ -2366,17 +2387,34 @@ class WhatsAppClient(ClientBase):
target_msg_id = str(
self._pluck(node, "key", "id")
or self._pluck(node, "key", "ID")
or self._pluck(node, "messageKey", "id")
or self._pluck(node, "message_key", "id")
or self._pluck(node, "targetMessageKey", "id")
or self._pluck(node, "target_message_key", "id")
or self._pluck(node, "stanzaId")
or self._pluck(node, "stanza_id")
or ""
).strip()
remove = bool(not emoji)
target_ts = self._normalize_timestamp(
self._pluck(node, "key", "messageTimestamp")
or self._pluck(node, "targetMessageKey", "messageTimestamp")
or self._pluck(node, "target_message_key", "message_timestamp")
or self._pluck(node, "targetTimestamp")
or self._pluck(node, "target_timestamp")
or 0
)
explicit_remove = self._pluck(node, "remove") or self._pluck(node, "isRemove")
if explicit_remove is None:
explicit_remove = self._pluck(node, "is_remove")
remove = bool(explicit_remove) if explicit_remove is not None else bool(not emoji)
if not target_msg_id:
return None
return {
"emoji": emoji,
"target_message_id": target_msg_id,
"remove": remove,
"target_ts": int(target_ts or 0),
"raw": self._proto_to_dict(node) or dict(node or {}) if isinstance(node, dict) else {},
}
async def _download_event_media(self, event):
@@ -2438,6 +2476,10 @@ class WhatsAppClient(ClientBase):
async def _handle_message_event(self, event):
event_obj = self._proto_to_dict(event) or event
msg_obj = self._pluck(event_obj, "message") or self._pluck(event_obj, "Message")
if self._pluck(msg_obj, "protocolMessage") or self._pluck(
msg_obj, "protocol_message"
):
return
text = self._message_text(msg_obj, event_obj)
if not text:
self.log.debug(
@@ -2482,7 +2524,7 @@ class WhatsAppClient(ClientBase):
).strip()
ts = self._normalize_timestamp(raw_ts)
reaction_payload = self._extract_reaction_event(msg_obj)
reaction_payload = self._extract_reaction_event(msg_obj or event_obj)
if reaction_payload:
self.log.debug(
"reaction-bridge whatsapp-inbound msg_id=%s target_id=%s emoji=%s remove=%s sender=%s chat=%s",
@@ -2508,6 +2550,26 @@ class WhatsAppClient(ClientBase):
)
)
for identifier in identifiers:
try:
await history.apply_reaction(
identifier.user,
identifier,
target_message_id=str(
reaction_payload.get("target_message_id") or ""
),
target_ts=int(reaction_payload.get("target_ts") or 0),
emoji=str(reaction_payload.get("emoji") or ""),
source_service="whatsapp",
actor=str(sender or chat or ""),
remove=bool(reaction_payload.get("remove")),
payload={
"event": "reaction",
"message_id": msg_id,
"raw": reaction_payload.get("raw") or {},
},
)
except Exception as exc:
self.log.warning("whatsapp reaction local apply failed: %s", exc)
try:
await self.ur.xmpp.client.apply_external_reaction(
identifier.user,
@@ -2527,6 +2589,21 @@ class WhatsAppClient(ClientBase):
)
except Exception as exc:
self.log.warning("whatsapp reaction relay to XMPP failed: %s", exc)
try:
await self.ur.presence_changed(
self.service,
identifier=identifier.identifier,
state="available",
confidence=0.9,
ts=int(ts or int(time.time() * 1000)),
payload={
"event": "reaction",
"inferred_from": "reaction",
"message_id": msg_id,
},
)
except Exception:
pass
return
self._remember_contact(
@@ -2907,14 +2984,42 @@ class WhatsAppClient(ClientBase):
is_unavailable = bool(
self._pluck(event, "Unavailable") or self._pluck(event, "unavailable")
)
last_seen_raw = (
self._pluck(event, "LastSeen")
or self._pluck(event, "lastSeen")
or self._pluck(event, "last_seen")
or self._pluck(event, "Timestamp")
or self._pluck(event, "timestamp")
or 0
)
last_seen_ts = self._normalize_timestamp(last_seen_raw)
self._remember_contact(sender, jid=sender)
for candidate in self._normalize_identifier_candidates(sender):
try:
await self.ur.presence_changed(
self.service,
identifier=candidate,
state=("unavailable" if is_unavailable else "available"),
confidence=0.9 if not is_unavailable else 0.8,
ts=int(last_seen_ts or int(time.time() * 1000)),
payload={
"presence": ("offline" if is_unavailable else "online"),
"sender": str(sender),
"last_seen_ts": int(last_seen_ts or 0),
},
)
except Exception:
pass
if is_unavailable:
await self.ur.stopped_typing(
self.service,
identifier=candidate,
payload={"presence": "offline", "sender": str(sender)},
payload={
"presence": "offline",
"sender": str(sender),
"last_seen_ts": int(last_seen_ts or 0),
},
)
def _extract_pair_qr(self, event):