Skip to content

Commit d79a17e

Browse files
committed
Decouple notification delivery sink
1 parent 1ddf077 commit d79a17e

4 files changed

Lines changed: 132 additions & 8 deletions

File tree

application/rebalance_service.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
build_reconciliation_record,
1111
write_reconciliation_record,
1212
)
13+
from notifications.events import NotificationPublisher, RenderedNotification
1314

1415

1516
_ZH_REASON_REPLACEMENTS = (
@@ -455,6 +456,10 @@ def run_strategy_core(
455456
reconciliation_output_path=None,
456457
result_hook=None,
457458
):
459+
notification_publisher = NotificationPublisher(
460+
log_message=lambda message: print(message, flush=True),
461+
send_message=send_tg_message,
462+
)
458463
ib = None
459464
try:
460465
ib = connect_ib()
@@ -524,8 +529,12 @@ def run_strategy_core(
524529
body_lines=[no_op_text],
525530
dashboard_text=strategy_dashboard,
526531
)
527-
print(detailed_message, flush=True)
528-
send_tg_message(compact_message)
532+
notification_publisher.publish(
533+
RenderedNotification(
534+
detailed_text=detailed_message,
535+
compact_text=compact_message,
536+
)
537+
)
529538
if callable(result_hook):
530539
result_hook(
531540
{
@@ -614,8 +623,12 @@ def run_strategy_core(
614623
dashboard_text=strategy_dashboard,
615624
)
616625

617-
print(detailed_message, flush=True)
618-
send_tg_message(compact_message)
626+
notification_publisher.publish(
627+
RenderedNotification(
628+
detailed_text=detailed_message,
629+
compact_text=compact_message,
630+
)
631+
)
619632
if callable(result_hook):
620633
result_hook(
621634
{

main.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
except ImportError:
1717
compute_v1 = None
1818

19+
from notifications.events import NotificationPublisher, RenderedNotification
1920
from notifications.telegram import build_strategy_display_name, build_translator, send_telegram_message
2021
from quant_platform_kit.common.models import OrderIntent
2122
from quant_platform_kit.common.runtime_reports import (
@@ -302,6 +303,19 @@ def send_tg_message(message):
302303
)
303304

304305

306+
def publish_notification(*, detailed_text, compact_text):
307+
publisher = NotificationPublisher(
308+
log_message=lambda message: print(message, flush=True),
309+
send_message=send_tg_message,
310+
)
311+
publisher.publish(
312+
RenderedNotification(
313+
detailed_text=detailed_text,
314+
compact_text=compact_text,
315+
)
316+
)
317+
318+
305319
def connect_ib():
306320
host = get_ib_host()
307321
last_error = None
@@ -573,8 +587,7 @@ def run_paper_liquidation_cycle():
573587
f"{SEPARATOR}\n"
574588
f"{_format_liquidation_orders(summary.get('orders_submitted'))}"
575589
)
576-
send_tg_message(message)
577-
print(message, flush=True)
590+
publish_notification(detailed_text=message, compact_text=message)
578591
global LAST_CYCLE_DETAILS
579592
LAST_CYCLE_DETAILS = {"execution_summary": summary}
580593
return "OK"
@@ -716,8 +729,7 @@ def handle_request():
716729
error_message=str(exc),
717730
)
718731
error_msg = f"{t('error_title')}\n{traceback.format_exc()}"
719-
send_tg_message(error_msg)
720-
print(error_msg, flush=True)
732+
publish_notification(detailed_text=error_msg, compact_text=error_msg)
721733
return "Error", 500
722734
finally:
723735
if lock_acquired:

notifications/events.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Notification event envelope and delivery helpers."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Callable
6+
from dataclasses import dataclass
7+
8+
9+
@dataclass(frozen=True)
10+
class RenderedNotification:
11+
"""Rendered notification payload split by sink."""
12+
13+
detailed_text: str
14+
compact_text: str
15+
16+
17+
@dataclass(frozen=True)
18+
class NotificationPublisher:
19+
"""Publish rendered notifications to the configured sinks."""
20+
21+
log_message: Callable[[str], None]
22+
send_message: Callable[[str], None]
23+
24+
def publish(self, notification: RenderedNotification) -> None:
25+
publish_rendered_notification(
26+
notification,
27+
log_message=self.log_message,
28+
send_message=self.send_message,
29+
)
30+
31+
32+
def publish_rendered_notification(
33+
notification: RenderedNotification,
34+
*,
35+
log_message: Callable[[str], None],
36+
send_message: Callable[[str], None],
37+
) -> None:
38+
"""Write the detailed log copy and send the compact user notification."""
39+
detailed = str(notification.detailed_text or "").strip()
40+
compact = str(notification.compact_text or "").strip()
41+
if detailed:
42+
log_message(detailed)
43+
if compact:
44+
send_message(compact)

tests/test_notification_events.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from notifications.events import (
2+
NotificationPublisher,
3+
RenderedNotification,
4+
publish_rendered_notification,
5+
)
6+
7+
8+
def test_publish_rendered_notification_splits_log_and_send_sinks():
9+
logged = []
10+
sent = []
11+
12+
publish_rendered_notification(
13+
RenderedNotification(
14+
detailed_text="detailed copy",
15+
compact_text="compact copy",
16+
),
17+
log_message=logged.append,
18+
send_message=sent.append,
19+
)
20+
21+
assert logged == ["detailed copy"]
22+
assert sent == ["compact copy"]
23+
24+
25+
def test_publish_rendered_notification_skips_empty_sinks():
26+
logged = []
27+
sent = []
28+
29+
publish_rendered_notification(
30+
RenderedNotification(detailed_text=" ", compact_text=""),
31+
log_message=logged.append,
32+
send_message=sent.append,
33+
)
34+
35+
assert logged == []
36+
assert sent == []
37+
38+
39+
def test_notification_publisher_uses_configured_sinks():
40+
logged = []
41+
sent = []
42+
publisher = NotificationPublisher(
43+
log_message=logged.append,
44+
send_message=sent.append,
45+
)
46+
47+
publisher.publish(
48+
RenderedNotification(
49+
detailed_text="detailed copy",
50+
compact_text="compact copy",
51+
)
52+
)
53+
54+
assert logged == ["detailed copy"]
55+
assert sent == ["compact copy"]

0 commit comments

Comments
 (0)