From 57a4a72ff1a3af9425b0954a3ec2f2c2a76533ac Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 11 May 2026 08:53:10 +0900 Subject: [PATCH] =?UTF-8?q?feat(curator):=20=ED=85=94=EB=A0=88=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=ED=81=90=EB=A0=88=EC=9D=B4=EC=85=98=C2=B7=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=20=EC=95=8C=EB=A6=BC=20=ED=8F=AC=EB=A7=B7=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent-office/app/notifiers/__init__.py | 0 agent-office/app/notifiers/telegram_lotto.py | 61 +++++++++++++++++++ .../tests/test_telegram_lotto_format.py | 44 +++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 agent-office/app/notifiers/__init__.py create mode 100644 agent-office/app/notifiers/telegram_lotto.py create mode 100644 agent-office/tests/test_telegram_lotto_format.py diff --git a/agent-office/app/notifiers/__init__.py b/agent-office/app/notifiers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/agent-office/app/notifiers/telegram_lotto.py b/agent-office/app/notifiers/telegram_lotto.py new file mode 100644 index 0000000..967c933 --- /dev/null +++ b/agent-office/app/notifiers/telegram_lotto.py @@ -0,0 +1,61 @@ +"""로또 큐레이션·당첨 알림 — 텔레그램 푸시.""" +import logging +from typing import Dict, Any + +# 기존 에이전트들과 동일한 패턴: send_raw(text, reply_markup=None, chat_id=None) +# chat_id 생략 시 기본 TELEGRAM_CHAT_ID로 자동 발송. +from ..telegram.messaging import send_raw + +logger = logging.getLogger("agent-office") + +LOTTO_URL = "https://gahusb.synology.me/lotto" + + +def _format_briefing(payload: Dict[str, Any]) -> str: + draw_no = payload["draw_no"] + nar = payload["narrative"] + conf = payload["confidence"] + + # 분배 칩 — core 5세트의 risk_tag 빈도 + core = payload["picks"]["core"] + role_count = {"안정": 0, "균형": 0, "공격": 0} + for p in core: + role_count[p["risk_tag"]] = role_count.get(p["risk_tag"], 0) + 1 + chip = " · ".join(f"{k} {v}" for k, v in role_count.items() if v) + + msg = [ + f"🎟 {draw_no}회 · 큐레이션 떴음", + "", + f"\"{nar['headline']}\"", + f"신뢰도 {conf} · 분배 {chip}", + ] + retro = nar.get("retrospective") or "" + if retro: + msg += ["", f"▸ 회고: {retro}"] + msg += ["", f"👉 결정 카드 보러가기 ({LOTTO_URL})"] + return "\n".join(msg) + + +def _format_prize_alert(event: Dict[str, Any]) -> str: + return ( + "🚨 로또 당첨 가능성!\n" + f"{event['draw_no']}회 — {event['match_count']}개 일치\n" + f"번호: {', '.join(str(n) for n in event['numbers'])}\n" + "동행복권에서 즉시 확인하세요." + ) + + +async def send_curator_briefing(payload: Dict[str, Any]) -> None: + text = _format_briefing(payload) + try: + await send_raw(text) + except Exception as e: + logger.warning(f"[telegram_lotto] briefing send failed: {e}") + + +async def send_prize_alert(event: Dict[str, Any]) -> None: + text = _format_prize_alert(event) + try: + await send_raw(text) + except Exception as e: + logger.warning(f"[telegram_lotto] prize alert send failed: {e}") diff --git a/agent-office/tests/test_telegram_lotto_format.py b/agent-office/tests/test_telegram_lotto_format.py new file mode 100644 index 0000000..c858616 --- /dev/null +++ b/agent-office/tests/test_telegram_lotto_format.py @@ -0,0 +1,44 @@ +import sys, os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + +from app.notifiers.telegram_lotto import _format_briefing, _format_prize_alert + + +def test_briefing_with_retrospective(): + payload = { + "draw_no": 1154, + "confidence": 72, + "narrative": { + "headline": "안정 +1, 콜드 누적 보강", + "summary_3lines": ["a", "b", "c"], + "retrospective": "너 2.0 / 나 1.8 — 저번호 편향", + }, + "picks": { + "core": [ + {"risk_tag": "안정"}, {"risk_tag": "안정"}, {"risk_tag": "안정"}, + {"risk_tag": "균형"}, {"risk_tag": "공격"}, + ], + "bonus": [], "extended": [], "pool": [], + }, + } + text = _format_briefing(payload) + assert "1154회" in text + assert "신뢰도 72" in text + assert "안정 3" in text + assert "회고: 너 2.0" in text + + +def test_briefing_without_retrospective(): + payload = { + "draw_no": 1, "confidence": 50, + "narrative": {"headline": "h", "summary_3lines": ["a","b","c"], "retrospective": ""}, + "picks": {"core": [{"risk_tag":"안정"}]*5, "bonus":[],"extended":[],"pool":[]}, + } + text = _format_briefing(payload) + assert "회고" not in text + + +def test_prize_alert(): + text = _format_prize_alert({"draw_no": 1154, "match_count": 5, "numbers": [3,11,17,25,33,8]}) + assert "5개 일치" in text + assert "3, 11, 17, 25, 33, 8" in text