refactor(signal): use native signal-cli service

This commit is contained in:
2026-09-29 20:03:12 -04:00
parent 8196d6fc98
commit 932e6c9ab1
7 changed files with 129 additions and 86 deletions
+32 -26
View File
@@ -2,23 +2,19 @@
from __future__ import annotations
import json
import logging
import socket
from os import getenv
import httpx
logger = logging.getLogger(__name__)
SIGNAL_API_URL = "http://localhost:8989/v2/send"
SIGNAL_API_TIMEOUT = 4.0
def signal_alert(body: str, title: str = "") -> None:
def signal_alert(body: str) -> None:
"""Send a signal alert.
Args:
body (str): The body of the alert.
title (str, optional): The title of the alert. Defaults to "".
"""
from_phone = getenv("SIGNAL_ALERT_FROM_PHONE")
to_phone = getenv("SIGNAL_ALERT_TO_PHONE")
@@ -26,25 +22,35 @@ def signal_alert(body: str, title: str = "") -> None:
logger.info("SIGNAL_ALERT_FROM_PHONE or SIGNAL_ALERT_TO_PHONE not set")
return
# Apprise's Signal integration did not support titles, so preserve that behavior.
if title:
logger.debug("Signal does not support notification titles; ignoring title")
signal_rpc_id = "signal-alert"
request = {
"jsonrpc": "2.0",
"method": "send",
"params": {
"account": from_phone,
"recipient": [to_phone],
"message": body,
},
"id": signal_rpc_id,
}
try:
response = httpx.post(
SIGNAL_API_URL,
json={
"message": body,
"number": from_phone,
"recipients": [to_phone],
"text_mode": "normal",
},
timeout=SIGNAL_API_TIMEOUT,
follow_redirects=True,
)
except httpx.HTTPError:
logger.exception("Unable to contact the Signal API")
return
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
client.settimeout(4.0)
client.connect("/run/signal-cli/socket")
client.sendall(json.dumps(request).encode() + b"\n")
if response.status_code not in {httpx.codes.OK, httpx.codes.CREATED}:
logger.error("Signal API returned HTTP status %d", response.status_code)
buffer = b""
while chunk := client.recv(65536):
buffer += chunk
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")