feat: add multi-chat routing and bill-specific dashboard countdown
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
@@ -29,13 +30,13 @@ class OutageService:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
bill_ids: tuple[str, ...],
|
||||
bill_chat_ids: Mapping[str, tuple[str, ...]],
|
||||
lookahead_days: int,
|
||||
saapa: SaapaClient,
|
||||
notifier: Notifier,
|
||||
state_repository: StateRepository,
|
||||
) -> None:
|
||||
self._bill_ids = bill_ids
|
||||
self._bill_chat_ids = dict(bill_chat_ids)
|
||||
self._lookahead_days = lookahead_days
|
||||
self._saapa = saapa
|
||||
self._notifier = notifier
|
||||
@@ -46,14 +47,14 @@ class OutageService:
|
||||
result = RunResult()
|
||||
from_date, to_date = jalali_query_window(now, self._lookahead_days)
|
||||
|
||||
for bill_id in self._bill_ids:
|
||||
for bill_id, chat_ids in self._bill_chat_ids.items():
|
||||
try:
|
||||
outages = self._saapa.fetch(bill_id, from_date, to_date)
|
||||
except ExternalServiceError as error:
|
||||
result.errors.append(str(error))
|
||||
LOGGER.error("%s", error)
|
||||
continue
|
||||
self._process_bill(state, bill_id, outages, now, result)
|
||||
self._process_bill(state, bill_id, chat_ids, outages, now, result)
|
||||
|
||||
if self._prune_expired(state, now):
|
||||
result.state_changed = True
|
||||
@@ -66,6 +67,7 @@ class OutageService:
|
||||
self,
|
||||
state: dict[str, Any],
|
||||
bill_id: str,
|
||||
chat_ids: tuple[str, ...],
|
||||
outages: list[Outage],
|
||||
now: datetime,
|
||||
result: RunResult,
|
||||
@@ -82,20 +84,26 @@ class OutageService:
|
||||
for event_id, outage in current.items():
|
||||
record = records.get(event_id)
|
||||
if record is None:
|
||||
if self._notify(new_outage_message(bill_id, outage, now), result):
|
||||
if self._notify(chat_ids, new_outage_message(bill_id, outage, now), result):
|
||||
records[event_id] = _record(outage, now)
|
||||
result.state_changed = True
|
||||
continue
|
||||
|
||||
previous = Outage.from_dict(record["snapshot"])
|
||||
if record["status"] == "cancelled":
|
||||
if self._notify(reactivated_outage_message(bill_id, outage, now), result):
|
||||
if self._notify(
|
||||
chat_ids, reactivated_outage_message(bill_id, outage, now), result
|
||||
):
|
||||
records[event_id] = _record(outage, now)
|
||||
result.state_changed = True
|
||||
continue
|
||||
|
||||
if outage.fingerprint() != record["notified_hash"]:
|
||||
if self._notify(updated_outage_message(bill_id, previous, outage, now), result):
|
||||
if self._notify(
|
||||
chat_ids,
|
||||
updated_outage_message(bill_id, previous, outage, now),
|
||||
result,
|
||||
):
|
||||
records[event_id] = _record(outage, now)
|
||||
result.state_changed = True
|
||||
continue
|
||||
@@ -118,7 +126,9 @@ class OutageService:
|
||||
if record["missing_checks"] == 0:
|
||||
record["missing_checks"] = 1
|
||||
result.state_changed = True
|
||||
elif self._notify(cancelled_outage_message(bill_id, previous, now), result):
|
||||
elif self._notify(
|
||||
chat_ids, cancelled_outage_message(bill_id, previous, now), result
|
||||
):
|
||||
record["status"] = "cancelled"
|
||||
record["missing_checks"] = 2
|
||||
record["notified_at"] = now.isoformat()
|
||||
@@ -127,15 +137,18 @@ class OutageService:
|
||||
if records and bill_id not in bills:
|
||||
bills[bill_id] = bill_state
|
||||
|
||||
def _notify(self, message: str, result: RunResult) -> bool:
|
||||
try:
|
||||
self._notifier.send(message)
|
||||
except ExternalServiceError as error:
|
||||
result.errors.append(str(error))
|
||||
LOGGER.error("%s", error)
|
||||
return False
|
||||
result.sent += 1
|
||||
return True
|
||||
def _notify(self, chat_ids: tuple[str, ...], message: str, result: RunResult) -> bool:
|
||||
delivered_to_all = True
|
||||
for chat_id in chat_ids:
|
||||
try:
|
||||
self._notifier.send(chat_id, message)
|
||||
except ExternalServiceError as error:
|
||||
result.errors.append(str(error))
|
||||
LOGGER.error("%s", error)
|
||||
delivered_to_all = False
|
||||
continue
|
||||
result.sent += 1
|
||||
return delivered_to_all
|
||||
|
||||
@staticmethod
|
||||
def _prune_expired(state: dict[str, Any], now: datetime) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user