get_today_status
Check today's ad account status: cumulative spend, hourly pace, expected daily spend, and ad performance metrics like impressions and clicks.
Instructions
오늘 광고계정 상태: 누적 소진·시간당 페이스·마감 예상 소진율·노출/클릭·잔액.
이런 질문에 사용하세요: • "오늘 일예산 얼마나 썼어?" / "오늘 소진액 얼마야?" • "지금 페이스 어때?" / "이대로 가면 오늘 얼마 쓸 것 같아?" • "오늘 광고 잘 돌고 있어?" / "오늘 노출/클릭 어때?" • "오늘 마감 예상 소진율" / "현재 시간당 평균 얼마야?" ⚠️ 어제/지난주 같은 과거 기간은 get_performance_report 로.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- Core handler: Fetches today's ad account status (spend, impressions, clicks, hourly pace, projected EOD spend, balance) from Kakao API.
async def get_today_status(client: KakaoMomentClient) -> dict[str, Any]: """오늘 일예산 소진율, 시간대별 페이스, 마감 예상 소진율.""" today = date.today() today_str = to_yyyymmdd(today) # 1) 오늘 총 소진 (계정 단위) report = await client.get( "/openapi/v4/adAccounts/report", params={"dateFrom": today_str, "dateTo": today_str}, ) rows = report.get("data") if isinstance(report, dict) and "data" in report else report spend = 0.0 impressions = 0 clicks = 0 if isinstance(rows, list): for r in rows: flat = _unwrap_metrics(r) spend += float(flat.get("cost", 0) or 0) impressions += int(flat.get("imp", 0) or 0) clicks += int(flat.get("click", 0) or 0) elif isinstance(rows, dict): flat = _unwrap_metrics(rows) spend = float(flat.get("cost", 0) or 0) impressions = int(flat.get("imp", 0) or 0) clicks = int(flat.get("click", 0) or 0) # 2) 시간대별 페이스 hourly: list[dict[str, Any]] = [] try: hour_report = await client.get( "/openapi/v4/adAccounts/report", params={ "dateFrom": today_str, "dateTo": today_str, "metricsGroup": "HOUR", }, ) hour_rows = ( hour_report.get("data") if isinstance(hour_report, dict) and "data" in hour_report else hour_report ) if isinstance(hour_rows, list): for r in hour_rows: hourly.append( { "hour": r.get("hour") or r.get("start") or r.get("date"), "cost": float(r.get("cost", 0) or 0), "impressions": int(r.get("imp", 0) or 0), "clicks": int(r.get("click", 0) or 0), } ) except Exception: # noqa: BLE001 — 시간대별 권한 없으면 생략 hourly = [] now = datetime.now() elapsed_hours = max(now.hour + now.minute / 60.0, 0.25) # 새벽 0시에도 0이 되지 않도록 pace_per_hour = spend / elapsed_hours if elapsed_hours else 0.0 projected_eod_spend = pace_per_hour * 24 # 3) 잔액 account = await get_ad_account_info(client) bizmoney = await get_bizmoney(client) summary = ( f"오늘 {now.hour:02d}시 기준 소진 {_fmt_won(spend)} " f"(시간당 평균 {_fmt_won(pace_per_hour)}). " f"현재 페이스 유지 시 마감 예상 {_fmt_won(projected_eod_spend)}." ) return { "date": today.isoformat(), "now": now.isoformat(timespec="minutes"), "account": {"id": account.get("id"), "name": account.get("name")}, "spend_today": round(spend, 2), "impressions_today": impressions, "clicks_today": clicks, "hourly_pace": hourly, "elapsed_hours": round(elapsed_hours, 2), "pace_per_hour": round(pace_per_hour, 2), "projected_eod_spend": round(projected_eod_spend, 2), "bizmoney_balance": bizmoney.get("balance"), "summary": summary, } - Helper function _fmt_won formats currency amounts, and get_bizmoney/get_ad_account_info are called by get_today_status to enrich the response.
return { "balance": balance, "free_cash": body.get("freeCash"), "deferred_pay_amount": body.get("deferredPayAmount"), "recent_spend_7d": recent_spend, "weekly_total_cost": round(weekly_total, 2), "avg_daily_cost_7d": round(avg_daily, 2), "days_left_estimate": days_left, "summary": " · ".join(summary_parts), "raw": body, - get_ad_account_info helper called by get_today_status to retrieve account name and ID.
async def get_ad_account_info(client: KakaoMomentClient) -> dict[str, Any]: """광고계정 정보 (이름, 상태, 멤버 권한 등).""" data = await client.get(f"/openapi/v4/adAccounts/{client.ad_account_id}") body = data.get("data") if isinstance(data, dict) and "data" in data else data if not isinstance(body, dict): return {"raw": data, "summary": "광고계정 정보를 해석할 수 없습니다."} name = body.get("name") or "(이름 미상)" status = body.get("status") or body.get("config") or "-" return { "id": body.get("id") or body.get("adAccountId"), "name": name, "status": status, "owner": body.get("masterUserId") or body.get("ownerId"), "summary": f"광고계정 「{name}」 상태: {status}", "raw": body, } - get_bizmoney helper called by get_today_status to retrieve bizmoney balance information.
async def get_bizmoney(client: KakaoMomentClient) -> dict[str, Any]: """비즈머니 잔액 + 최근 7일 소진 추이.""" data = await client.get("/openapi/v4/adAccounts/balance") body = data.get("data") if isinstance(data, dict) and "data" in data else data if not isinstance(body, dict): return {"raw": data, "summary": "비즈머니 정보를 해석할 수 없습니다."} balance = body.get("cash") or body.get("balance") or body.get("totalAmount") # 최근 7일 소진 (오늘 제외 7일 전 ~ 어제). 광고계정 일별 리포트 사용. today = date.today() week_ago = today - timedelta(days=7) yesterday = today - timedelta(days=1) recent_spend: list[dict[str, Any]] = [] weekly_total: float = 0.0 try: report = await client.get( "/openapi/v4/adAccounts/report", params={ "dateFrom": to_yyyymmdd(week_ago), "dateTo": to_yyyymmdd(yesterday), "metricsGroups": "DAY", }, ) rows = report.get("data") if isinstance(report, dict) and "data" in report else report if isinstance(rows, list): for r in rows: flat = _unwrap_metrics(r) cost = float(flat.get("cost", 0) or 0) weekly_total += cost recent_spend.append( {"date": flat.get("start") or flat.get("date"), "cost": cost} ) except Exception: # noqa: BLE001 — 리포트 권한이 없는 경우에도 잔액은 반환 recent_spend = [] avg_daily = (weekly_total / len(recent_spend)) if recent_spend else 0.0 days_left: float | None = None if balance and avg_daily > 0: try: days_left = round(float(balance) / avg_daily, 1) except (TypeError, ValueError): days_left = None summary_parts = [f"비즈머니 잔액 {_fmt_won(balance)}"] if recent_spend: summary_parts.append(f"최근 7일 평균 일소진 {_fmt_won(avg_daily)}") if days_left is not None: summary_parts.append(f"현재 페이스로 약 {days_left}일 잔여") return { "balance": balance, "free_cash": body.get("freeCash"), "deferred_pay_amount": body.get("deferredPayAmount"), "recent_spend_7d": recent_spend, "weekly_total_cost": round(weekly_total, 2), "avg_daily_cost_7d": round(avg_daily, 2), "days_left_estimate": days_left, "summary": " · ".join(summary_parts), "raw": body, }