auto: 일일 백업 2026-10-08 02:00

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-10-08 02:00:06 +09:00
parent 114a1ef816
commit 4f63ec56dc
114 changed files with 1667 additions and 1290 deletions
@@ -3,6 +3,7 @@ from __future__ import annotations
import argparse
import json
import subprocess
import sys
from datetime import date, datetime
from decimal import Decimal, ROUND_HALF_UP
@@ -140,6 +141,53 @@ def format_alert(kind: str, row: dict, detail: dict) -> str:
return '\n\n'.join(sections)
GAHEE_CRED = Path('/Users/snowoyh/.openclaw/credentials/gahee_imessage.json')
IPO_SUBSCRIBED = Path('/Users/snowoyh/.openclaw/agents/stock/workspace/state/behive_ipo_subscribed.json')
def gahee_listing_message(target: date, rows: list[tuple[dict, dict]] | None) -> tuple[list[str], str]:
"""가희가 청약한 종목 중 target 상장분 → (ipoCode 목록, 문자 본문). 없으면 ([], '').
상장일은 API 상장 목록 ∪ 청약기록 lcal_date(네이버 조회 실패해도 기록으로 보낸다)."""
try:
subscribed = json.loads(IPO_SUBSCRIBED.read_text())
except Exception:
return [], ''
by_code = {(r.get('ipoCode') or r.get('itemCode') or '').strip(): (r, d) for r, d in (rows or [])}
codes, blocks = [], []
for code, rec in subscribed.items():
broker = ((rec.get('by') or {}).get('gahee') or {}).get('broker')
if not broker or (code not in by_code and rec.get('lcal_date') != target.isoformat()):
continue
row, detail = by_code.get(code, ({}, None))
if detail is None:
try:
detail = _detail(code)
except Exception:
detail = {}
price = next((b['price'] for b in _manager_blocks(row, detail) if b.get('price') is not None), None)
lines = [f"[공모주 상장] 오늘 {rec.get('name') or row.get('compName') or code} 상장일이에요",
f'· 청약 증권사: {broker}']
if price is not None:
lines.append(f'· 확정 공모가: {price:,}원')
lines.append(f'· 09:00 거래 시작 — 배정 주식은 {broker} 앱에서 확인하세요')
codes.append(code)
blocks.append('\n'.join(lines))
return codes, '\n\n'.join(blocks)
def send_gahee_imessage(text: str) -> bool:
"""가희 리마인더(gahee_reminder._send_imessage)와 같은 방식 — imessage 강제(sms 는 이 수신자에게 3전 3패).
⚠️ rc=0 은 Messages 에 넘겼다는 뜻일 뿐 전달 확인이 아니다. 코디 셸에선 AppleEvents 거부로 안 되고 launchd 경유로만."""
handle = json.loads(GAHEE_CRED.read_text()).get('handle')
if not handle:
return False
p = subprocess.run(['imsg', 'send', '--to', handle, '--text', text, '--service', 'imessage'],
capture_output=True, text=True, timeout=30)
if p.returncode != 0:
print(f'gahee imsg send rc={p.returncode}: {p.stderr[:200]}', file=sys.stderr)
return p.returncode == 0
def collect_for_date(kind: str, target: date) -> list[tuple[dict, dict]]:
payload = json.loads(fetch(NAVER_IPO_API_URL, encoding='utf-8'))
matches = []
@@ -178,6 +226,30 @@ def main() -> int:
print(f'IPO interests added: {", ".join(added)}')
except Exception as exc:
print(f'IPO interests add failed: {type(exc).__name__}: {exc}', file=sys.stderr)
if not args.dry_run:
# 비하이브 '공모청약' 영상 요약 → 자산웹 공모주 탭. 새 영상 없으면 채널 페이지 1콜로 끝난다.
try:
from ipo_youtube_digest import run as run_ipo_youtube
print(f'IPO youtube digest: {json.dumps(run_ipo_youtube(), ensure_ascii=False)}')
except Exception as exc:
print(f'IPO youtube digest failed: {type(exc).__name__}: {exc}', file=sys.stderr)
if args.kind == 'listing':
# 가희가 청약한 종목의 상장일 아침 iMessage(2026-10-07 관리자님 요청). 같은 날 여러 종목이면 한 통.
try:
codes, text = gahee_listing_message(target, rows)
state = load_state()
key = f'gahee_listing|{target.isoformat()}|{",".join(sorted(codes))}'
if not codes:
pass
elif args.dry_run:
print(f'[dry-run] 가희 상장 문자:\n{text}\n')
elif key not in state.get('sent', {}):
if send_gahee_imessage(text):
state.setdefault('sent', {})[key] = datetime.now(KST).isoformat()
save_state(state)
print(f'gahee listing imessage sent: {",".join(codes)}')
except Exception as exc:
print(f'gahee listing imessage failed: {type(exc).__name__}: {exc}', file=sys.stderr)
if rows is None:
return 1