201 lines
8.5 KiB
Python
201 lines
8.5 KiB
Python
|
|
# -*- 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
|