Update Signal alert handling

This commit is contained in:
Richie committed 2026-09-30 07:58:30 -04:00
1 parent 2422bb3664
commit b3d8df0c37
5 files changed
+99 -65

No files matched your search

+27 -19
View File
@@ -5,23 +5,28 @@ from __future__ import annotations
import json
import logging
import socket
from os import getenv
logger = logging.getLogger(__name__)
def signal_alert(body: str) -> None:
"""Send a signal alert.
class SignalRPCError(RuntimeError):
"""signal-cli returned an error or closed without responding."""
def signal_alert(body: str, from_phone: str, to_phone: str, *, timeout: float = 4.0) -> None:
"""Send a Signal alert through the local signal-cli daemon.
Args:
body (str): The body of the alert.
"""
from_phone = getenv("SIGNAL_ALERT_FROM_PHONE")
to_phone = getenv("SIGNAL_ALERT_TO_PHONE")
if not from_phone or not to_phone:
logger.info("SIGNAL_ALERT_FROM_PHONE or SIGNAL_ALERT_TO_PHONE not set")
return
body: The body of the alert.
from_phone: The Signal account sending the alert.
to_phone: The Signal account receiving the alert.
timeout: Seconds to wait on each socket operation.
Raises:
SignalRPCError: If signal-cli returns an error or closes the
connection before responding.
OSError: If the socket is unreachable or an operation times out.
"""
signal_rpc_id = "signal-alert"
request = {
"jsonrpc": "2.0",
@@ -36,7 +41,7 @@ def signal_alert(body: str) -> None:
try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
client.settimeout(4.0)
client.settimeout(timeout)
client.connect("/run/signal-cli/socket")
client.sendall(json.dumps(request).encode() + b"\n")
@@ -46,11 +51,14 @@ def signal_alert(body: str) -> None:
while b"\n" in buffer:
line, buffer = buffer.split(b"\n", maxsplit=1)
response = json.loads(line)
if response.get("id") == signal_rpc_id:
if error := response.get("error"):
logger.error("Signal RPC returned an error: %s", error)
return
logger.error("Signal RPC socket closed before returning a response")
except OSError, json.JSONDecodeError:
logger.exception("Unable to communicate with signal-cli")
if response.get("id") != signal_rpc_id:
continue
if rpc_error := response.get("error"):
error = f"Signal RPC returned an error: {rpc_error}"
raise SignalRPCError(error)
return
error = "Signal RPC socket closed before returning a response"
raise SignalRPCError(error)
except (OSError, json.JSONDecodeError) as exc:
error = f"Signal RPC failed: {exc!r}"
raise SignalRPCError(error) from exc
+18 -4
View File
@@ -6,6 +6,7 @@ import logging
import sys
import tomllib
from functools import cache
from os import getenv
from pathlib import Path # noqa: TC003 This is required for the typer CLI
from re import compile as re_compile
from re import search
@@ -24,6 +25,15 @@ def main(config_file: Path) -> None:
configure_logger(level="DEBUG")
logger.info("Starting snapshot_manager")
from_phone = getenv("SIGNAL_ALERT_FROM_PHONE")
to_phone = getenv("SIGNAL_ALERT_TO_PHONE")
if not from_phone:
error = "SIGNAL_ALERT_FROM_PHONE environment variable is not set"
raise ValueError(error)
if not to_phone:
error = "SIGNAL_ALERT_TO_PHONE environment variable is not set"
raise ValueError(error)
try:
time_stamp = get_time_stamp()
@@ -33,14 +43,14 @@ def main(config_file: Path) -> None:
if status != "snapshot created":
msg = f"{dataset.name} failed to create snapshot {time_stamp}"
logger.error(msg)
signal_alert(msg)
signal_alert(msg, from_phone, to_phone)
continue
count_lookup = get_count_lookup(config_file, dataset.name)
logger.info(f"using {count_lookup} for {dataset.name}")
get_snapshots_to_delete(dataset, count_lookup)
get_snapshots_to_delete(dataset, count_lookup, from_phone, to_phone)
except Exception:
logger.exception("snapshot_manager failed")
signal_alert("snapshot_manager failed")
signal_alert("snapshot_manager failed", from_phone, to_phone)
sys.exit(1)
else:
logger.info("snapshot_manager completed")
@@ -92,12 +102,16 @@ def load_config_data(config_file: Path) -> dict[str, dict[str, int]]:
def get_snapshots_to_delete(
dataset: Dataset,
count_lookup: dict[str, int],
from_phone: str,
to_phone: str,
) -> None:
"""Get snapshots to delete.
Args:
dataset (Dataset): the dataset
count_lookup (dict[str, int]): the count lookup
from_phone (str): The Signal account sending alerts.
to_phone (str): The Signal account receiving alerts.
"""
snapshots = dataset.get_snapshots()
@@ -127,7 +141,7 @@ def get_snapshots_to_delete(
for snapshot in snapshots_being_deleted:
if error := dataset.delete_snapshot(snapshot):
error_message = f"{dataset.name}@{snapshot} failed to delete: {error}"
signal_alert(error_message)
signal_alert(error_message, from_phone, to_phone)
logger.error(error_message)