# -*- 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