"""청약 매칭 알림 — 텔레그램 메시지 포맷터 + 인라인 키보드 빌더.""" import os from html import escape as _h from typing import Optional DASHBOARD_URL = os.getenv("REALESTATE_DASHBOARD_URL", "https://example.com/realestate") def _format_one_compact(m: dict) -> str: score = m.get("match_score", 0) name = _h(m.get("house_nm") or "(제목 없음)") district = m.get("district") or "" region = m.get("region_name") or "" where = f"{region.split()[0] if region else ''} {district}".strip() or "위치 미상" rstart = m.get("receipt_start") or "" rend = m.get("receipt_end") or "" return ( f"⭐ {score}점 — {name}\n" f"📍 {_h(where)} 📅 {_h(rstart)} ~ {_h(rend)}" ) def _format_one_full(m: dict) -> str: score = m.get("match_score", 0) name = _h(m.get("house_nm") or "(제목 없음)") district = m.get("district") or "" region = m.get("region_name") or "" flags = [] if m.get("is_speculative_area") == "Y": flags.append("투기과열") if m.get("is_price_cap") == "Y": flags.append("분양가상한제") flag_str = f" ({', '.join(flags)})" if flags else "" rstart = m.get("receipt_start") or "" rend = m.get("receipt_end") or "" elig = m.get("eligible_types") or [] reasons = m.get("match_reasons") or [] where = f"{region.split()[0] if region else ''} {district}".strip() or "위치 미상" lines = [ f"⭐ {score}점 — {name}", f"📍 {_h(where)}{_h(flag_str)}", f"📅 청약 {_h(rstart)} ~ {_h(rend)}", ] if elig: lines.append(f"✓ 자격: {_h(', '.join(elig))}") if reasons: lines.append(f"💡 {_h(' / '.join(reasons[:4]))}") return "\n".join(lines) def format_realestate_matches(matches: list[dict]) -> str: """매칭 목록을 텔레그램 HTML 메시지로 변환. 1~2건은 풀 카드, 3건 이상은 묶음 카드(상위 5건). """ if not matches: return "🏢 새 청약 매칭이 없습니다." if len(matches) <= 2: body = "\n\n".join(_format_one_full(m) for m in matches) return f"🏢 새 청약 매칭 {len(matches)}건\n━━━━━━━━━━\n\n{body}" top = matches[:5] body = "\n\n".join(_format_one_compact(m) for m in top) suffix = f"\n\n…외 {len(matches) - 5}건" if len(matches) > 5 else "" return f"🏢 새 청약 매칭 {len(matches)}건\n━━━━━━━━━━\n\n{body}{suffix}" def build_match_keyboard(matches: list[dict]) -> Optional[dict]: """1~2건: 매치별 [북마크][공고 보기] 행. 3건 이상: [전체 보기] 단일 행.""" if not matches: return None if len(matches) <= 2: rows = [] for m in matches: buttons = [{ "text": "🔖 북마크", "callback_data": f"realestate_bookmark_{m['id']}", }] url = m.get("pblanc_url") if url: buttons.append({"text": "📄 공고 보기", "url": url}) rows.append(buttons) return {"inline_keyboard": rows} return { "inline_keyboard": [[ {"text": "📋 전체 보기", "url": DASHBOARD_URL}, ]], }