Files
aza/AzA march 2026/aza_empfang_host_poll.py

201 lines
8.5 KiB
Python
Raw Normal View History

2026-06-13 22:47:31 +02:00
# -*- coding: utf-8 -*-
"""Gemeinsame Nachrichten-Poll- und Popup-Helfer fuer Office- und Chat-Host."""
from __future__ import annotations
import threading
import time
import requests
class EmpfangHostPollMixin:
def _empfang_background_poll(self):
"""Prueft im Hintergrund auf neue Empfangs-Nachrichten und zeigt Toast."""
if getattr(self, "_shutdown_in_progress", False):
return
def _check():
try:
bu = self.get_backend_url()
r = requests.get(f"{bu}/empfang/messages",
headers=self._empfang_headers(), timeout=8)
if r.status_code == 200:
msgs = r.json().get("messages", [])
current_ids = {m["id"] for m in msgs if m.get("status") == "offen"}
new_ids = current_ids - self._empfang_last_seen_ids
if self._empfang_last_seen_ids and new_ids:
new_msgs = [
m for m in msgs
if m.get("id") in new_ids
and self._empfang_native_alert_is_incoming(m)
and str(m.get("id") or "") not in getattr(
self, "_empfang_dismissed_notification_ids", set()
)
]
if new_msgs:
def _ts_key(mm: dict) -> str:
return (
(mm.get("empfangen") or mm.get("zeitstempel") or "")
or ""
)
new_msgs.sort(key=_ts_key, reverse=True)
m = new_msgs[0]
snap_k = str(m.get("id") or "").strip()
snap_prev = (m.get("kommentar", "") or "")[:80]
snap_snd = (
(m.get("absender", "") or "Empfang").strip() or "Empfang"
)
ex0 = (
m.get("extras")
if isinstance(m.get("extras"), dict)
else {}
)
snap_peer = str(ex0.get("sender_user_id") or "").strip()
snap_pdn = snap_snd.split("(", 1)[0].strip()
snap_ext = bool(ex0.get("external_dm"))
if not self._empfang_should_suppress_native_popup(snap_peer):
self.after(
0,
lambda prev=snap_prev,
snd=snap_snd,
mid=snap_k,
puid=snap_peer,
pdn=snap_pdn,
ext=snap_ext: self._show_empfang_chat_message_alert(
preview=prev,
sender_label=snd,
message_id=mid,
peer_user_id=puid,
peer_display_name=pdn,
external_dm=ext,
),
)
snap_new = set(new_ids)
snap_msgs = list(msgs)
self.after(
0,
lambda sn=snap_new, sm=snap_msgs: self._empfang_maybe_autopopup_from_poll(
sn, sm,
),
)
now_wall = time.time()
_last_dp = getattr(self, "_empfang_last_device_poll_wall", 0.0)
if now_wall - float(_last_dp) > 120:
setattr(self, "_empfang_last_device_poll_wall", now_wall)
r_inf = requests.get(
f"{bu}/empfang/practice/info",
headers=self._empfang_headers(),
timeout=8,
)
if r_inf.status_code == 200:
jd = r_inf.json() or {}
if int(jd.get("pending_new_device_count") or 0) > 0:
self.after(
400,
lambda snap=dict(jd): self._empfang_show_pending_device_notice(
snap,
),
)
self._empfang_last_seen_ids = current_ids
except Exception:
pass
threading.Thread(target=_check, daemon=True).start()
self.after(15000, self._empfang_background_poll)
def _dismiss_empfang_notification(self, message_id: str) -> None:
"""Quittiert eine Popup-Benachrichtigung lokal.
Fuegt die message_id zur dismissed-Menge hinzu, damit das Polling
dieselbe Nachricht nicht sofort wieder als Popup anzeigt.
Begrenzt die Menge auf 500 Eintraege (aelteste werden verworfen).
"""
mid = (message_id or "").strip()
if not mid:
return
try:
self._empfang_dismissed_notification_ids.add(mid)
if len(self._empfang_dismissed_notification_ids) > 500:
self._empfang_dismissed_notification_ids = set(
list(self._empfang_dismissed_notification_ids)[-300:]
)
except Exception:
pass
def _empfang_ack_message(self, message_id: str, *, external_dm: bool = False) -> None:
"""Serverseitiges OK/Quittieren (wie Web-Chat-OK, thread_until_message)."""
mid = (message_id or "").strip()
if not mid:
return
def _worker():
try:
bu = self.get_backend_url()
path = (
f"/empfang/external-messages/{mid}/chat-ack"
if external_dm
else f"/empfang/messages/{mid}/chat-ack"
)
hdrs = dict(self._empfang_headers())
hdrs["Content-Type"] = "application/json"
r = requests.post(
f"{bu}{path}",
json={"ack": True, "scope": "thread_until_message"},
headers=hdrs,
timeout=(3, 12),
)
if r.status_code != 200:
self._debug_log(
f"EMPfang_ACK_FAIL mid={mid} status={r.status_code}"
)
except Exception as exc:
try:
self._debug_log(
f"EMPfang_ACK_FAIL mid={mid} err={type(exc).__name__}"
)
except Exception:
pass
threading.Thread(target=_worker, daemon=True).start()
def _empfang_quit_notification(
self, message_id: str, *, external_dm: bool = False
) -> None:
"""Lokal unterdruecken und serverseitig quittieren (Best-Effort)."""
self._dismiss_empfang_notification(message_id)
self._empfang_ack_message(message_id, external_dm=external_dm)
def _empfang_should_suppress_native_popup(self, peer_user_id: str) -> bool:
try:
from aza_empfang_incoming_popup import should_suppress_incoming_popup
return bool(should_suppress_incoming_popup(self, peer_user_id))
except Exception:
return False
def _show_empfang_chat_message_alert(
self,
*,
preview: str,
sender_label: str,
message_id: str,
peer_user_id: str,
peer_display_name: str,
external_dm: bool = False,
) -> None:
"""Schlichtes Toplevel bei eingehender Empfang-/Chatnachricht."""
try:
from aza_empfang_incoming_popup import show_incoming_message_popup
show_incoming_message_popup(
self,
preview=preview,
sender_label=sender_label,
message_id=message_id,
peer_user_id=peer_user_id,
peer_display_name=peer_display_name,
external_dm=external_dm,
)
except Exception:
pass