Files
ewoooc/services/openclaw_bot/menu_keyboards.py
OoO dd35306399
All checks were successful
CD Pipeline / deploy (push) Successful in 1m46s
refactor(openclaw): 抽出選單鍵盤 builders
2026-04-30 23:17:19 +08:00

241 lines
10 KiB
Python

"""OpenClaw Telegram inline keyboard builders."""
from datetime import datetime, timedelta, timezone
TAIPEI_TZ = timezone(timedelta(hours=8))
_GOALS = {}
_latest_date_provider = lambda: None
_BACK = [{'text': '← 返回主選單', 'callback_data': 'menu:main'}]
def configure_menu_keyboards(latest_date_provider=None, goals=None, taipei_tz=None):
"""Inject runtime dependencies owned by the route module."""
global _latest_date_provider, _GOALS, TAIPEI_TZ
if latest_date_provider is not None:
_latest_date_provider = latest_date_provider
if goals is not None:
_GOALS = goals
if taipei_tz is not None:
TAIPEI_TZ = taipei_tz
def _latest_date():
return _latest_date_provider() or ''
def _yesterday_from(date_str):
if not date_str:
return ''
try:
return (
datetime.strptime(date_str.replace('/', '-'), '%Y-%m-%d')
- timedelta(days=1)
).strftime('%Y/%m/%d')
except Exception:
return ''
def main_menu_keyboard():
"""第一層主選單 — 7大功能類別"""
return [
[{'text': '📊 業績查詢', 'callback_data': 'menu:sales'},
{'text': '🏆 商品廠商', 'callback_data': 'menu:products'}],
[{'text': '🎯 目標管理', 'callback_data': 'menu:goals'},
{'text': '📈 智能分析', 'callback_data': 'menu:analysis'}],
[{'text': '📄 簡報報表', 'callback_data': 'menu:reports'},
{'text': '🌐 市場情報', 'callback_data': 'menu:market'}],
[{'text': '🔍 競品日報', 'callback_data': 'menu:competitor'}],
[{'text': '❓ 使用說明', 'callback_data': 'cmd:help'}],
]
def _submenu_sales():
ld = _latest_date()
yesterday = _yesterday_from(ld)
current_month = datetime.now(TAIPEI_TZ).strftime('%Y/%m')
d_label = ld[-5:] if ld else '-'
y_label = yesterday[-5:] if yesterday else '-'
return [
[{'text': f'📊 今日 ({d_label})', 'callback_data': f'cmd:sales:{ld}'},
{'text': f'⬅ 昨日 ({y_label})', 'callback_data': f'cmd:sales:{yesterday}'}],
[{'text': '📅 每週業績', 'callback_data': 'cmd:trend:week'},
{'text': '📅 每月業績', 'callback_data': f'cmd:history:{current_month}'}],
[{'text': '📅 每季業績', 'callback_data': 'cmd:trend:quarter'},
{'text': '📅 近半年', 'callback_data': 'cmd:trend:half'}],
[{'text': '📈 趨勢分析', 'callback_data': 'menu:trend'},
{'text': '🔄 同期比較', 'callback_data': f'cmd:compare:{ld}'}],
[{'text': '🗂 分類業績', 'callback_data': f'cmd:category:{ld}'},
{'text': '📅 日期/區間', 'callback_data': 'await:date_range_sales'}],
[{'text': '🗃 月份覽', 'callback_data': 'cmd:history'}],
_BACK,
]
def _submenu_products():
ld = _latest_date()
yesterday = _yesterday_from(ld)
d_label = ld[-5:] if ld else '-'
y_label = yesterday[-5:] if yesterday else '-'
return [
[{'text': f'🏆 熱銷商品 ({d_label})', 'callback_data': f'cmd:top:{ld}'},
{'text': f'🏭 熱銷廠商 ({d_label})', 'callback_data': f'cmd:vendor:{ld}'}],
[{'text': f'⬅ 昨日商品 ({y_label})', 'callback_data': f'cmd:top:{yesterday}'},
{'text': '🧬 商品健康', 'callback_data': f'cmd:health:{ld}'}],
[{'text': '📦 補貨預測', 'callback_data': 'cmd:restock'},
{'text': '🗂 分類鑽取', 'callback_data': 'menu:category'}],
[{'text': '📅 指定日期', 'callback_data': 'await:date_top'}],
_BACK,
]
def _submenu_goals():
dg = _GOALS.get('daily', 0)
mg = _GOALS.get('monthly', 0)
qg = _GOALS.get('quarterly', 0)
hg = _GOALS.get('half', 0)
yg = _GOALS.get('yearly', 0)
def _fmt(v):
return f'{v/10000:.0f}' if v else '未設'
return [
[{'text': '📋 查看達成率', 'callback_data': 'cmd:goal'}],
[{'text': f'日目標 ({_fmt(dg)})', 'callback_data': 'await:goal_daily'},
{'text': f'月目標 ({_fmt(mg)})', 'callback_data': 'await:goal_monthly'}],
[{'text': f'季目標 ({_fmt(qg)})', 'callback_data': 'await:goal_quarterly'},
{'text': f'半年目標 ({_fmt(hg)})', 'callback_data': 'await:goal_half'}],
[{'text': f'年目標 ({_fmt(yg)})', 'callback_data': 'await:goal_yearly'}],
_BACK,
]
def _submenu_analysis():
ld = _latest_date()
return [
[{'text': '🎲 策略矩陣', 'callback_data': f'cmd:strategy:{ld}'},
{'text': '📈 業績趨勢', 'callback_data': 'menu:trend'}],
[{'text': '🧬 商品健康', 'callback_data': f'cmd:health:{ld}'},
{'text': '🗂 分類業績', 'callback_data': f'cmd:category:{ld}'}],
[{'text': '🎉 促銷追蹤', 'callback_data': 'await:promo_range'},
{'text': '📦 補貨預測', 'callback_data': 'cmd:restock'}],
[{'text': '📊 趨勢圖表', 'callback_data': 'cmd:chart'},
{'text': '🔄 同期比較', 'callback_data': f'cmd:compare:{ld}'}],
[{'text': '📅 指定日期', 'callback_data': 'await:date_analysis'}],
_BACK,
]
def _submenu_category():
"""分類業績鑽取 — 顯示 L1 固定分類按鈕"""
ld = _latest_date()
cats = [
('美妝保養', '💄'), ('保健食品/用品', '💊'), ('母嬰', '👶'),
('食品飲料', '🍱'), ('家電', '🏠'), ('服裝內著', '👕'),
('個人清潔', '🧴'), ('運動用品/器材', '🏃'), ('寵物', '🐾'), ('其他', '📦'),
]
rows = []
for i in range(0, len(cats), 2):
pair = []
for cat, icon in cats[i:i + 2]:
pair.append({'text': f'{icon} {cat}', 'callback_data': f'cmd:catdetail:{cat}:{ld}'})
rows.append(pair)
rows.append([{'text': '🗂 全分類清單', 'callback_data': f'cmd:category:{ld}'}])
rows.append(_BACK)
return rows
def _submenu_trend():
return [
[{'text': '📅 近7日', 'callback_data': 'cmd:trend:7'},
{'text': '📅 近1個月', 'callback_data': 'cmd:trend:month'}],
[{'text': '📅 近3個月', 'callback_data': 'cmd:trend:quarter'},
{'text': '📅 近半年', 'callback_data': 'cmd:trend:half'}],
[{'text': '📅 本年度', 'callback_data': 'cmd:trend:year'},
{'text': '📅 指定月份', 'callback_data': 'await:date_trend_month'}],
[{'text': '📅 指定年份', 'callback_data': 'await:date_trend_year'},
{'text': '📅 指定季度', 'callback_data': 'await:date_trend_quarter'}],
[{'text': '← 返回業績查詢', 'callback_data': 'menu:sales'}],
]
def _submenu_reports():
return [
[{'text': '📄 日報', 'callback_data': 'cmd:ppt:daily'},
{'text': '📈 週報', 'callback_data': 'cmd:ppt:weekly'}],
[{'text': '📅 月報', 'callback_data': 'cmd:ppt:monthly'},
{'text': '📋 下載報表', 'callback_data': 'cmd:report'}],
[{'text': '🧩 策略(日)', 'callback_data': 'cmd:ppt:strategy'},
{'text': '🧩 策略(週)', 'callback_data': 'cmd:ppt:strategy weekly'}],
[{'text': '🧩 策略(月)', 'callback_data': 'cmd:ppt:strategy monthly'},
{'text': '🧩 策略(季)', 'callback_data': 'cmd:ppt:strategy quarterly'}],
[{'text': '🧩 策略(半年)', 'callback_data': 'cmd:ppt:strategy half'},
{'text': '🧩 策略(年)', 'callback_data': 'cmd:ppt:strategy yearly'}],
[{'text': '🎉 促銷效益簡報', 'callback_data': 'await:promo_range'},
{'text': '🔍 競品比較', 'callback_data': 'menu:competitor'}],
[{'text': '📅 指定日期日報', 'callback_data': 'await:date_ppt_daily'},
{'text': '📅 指定月份月報', 'callback_data': 'await:date_ppt_monthly'}],
_BACK,
]
def _submenu_market():
return [
[{'text': '📰 電商新聞', 'callback_data': 'cmd:news'},
{'text': '🌤 台北天氣', 'callback_data': 'cmd:weather'}],
[{'text': '🔥 Google熱搜', 'callback_data': 'cmd:trends'},
{'text': '💬 Dcard口碑', 'callback_data': 'cmd:dcard'}],
[{'text': '💱 台銀匯率', 'callback_data': 'cmd:exchange'},
{'text': '📅 電商節慶', 'callback_data': 'cmd:calendar'}],
[{'text': '▶️ YouTube爆紅商品', 'callback_data': 'cmd:youtube'},
{'text': '🧠 AI學習狀態', 'callback_data': 'cmd:learn'}],
[{'text': '🔍 關鍵字比價', 'callback_data': 'await:search_compare'},
{'text': '📷 圖片比價說明', 'callback_data': 'cmd:photo_search_help'}],
_BACK,
]
def _submenu_competitor():
"""競品日報第二層:所有選項直接產 PPT"""
today = datetime.now(TAIPEI_TZ).date()
yesterday = today - timedelta(days=1)
td_str = today.strftime('%Y/%m/%d')
yd_str = yesterday.strftime('%Y/%m/%d')
td_label = today.strftime('%m/%d')
yd_label = yesterday.strftime('%m/%d')
return [
[{'text': f'📊 今日簡報 ({td_label})', 'callback_data': f'cmd:ppt:competitor {td_str}'},
{'text': f'📊 昨日簡報 ({yd_label})', 'callback_data': f'cmd:ppt:competitor {yd_str}'}],
[{'text': '📈 本週比較', 'callback_data': 'cmd:ppt:competitor weekly'},
{'text': '📆 本月比較', 'callback_data': 'cmd:ppt:competitor monthly'}],
[{'text': '🗃 本季比較', 'callback_data': 'cmd:ppt:competitor quarterly'},
{'text': '📅 指定日期', 'callback_data': 'await:date_competitor'}],
[{'text': '📄 更多週期 →', 'callback_data': 'menu:competitor_ppt'}],
_BACK,
]
def _submenu_competitor_ppt():
"""競品 PPT 長週期選單(第三層)— 半年/年;日/週/月/季已在第二層"""
return [
[{'text': '📆 半年比較', 'callback_data': 'cmd:ppt:competitor half'},
{'text': '🗓 年比較', 'callback_data': 'cmd:ppt:competitor yearly'}],
[{'text': '← 返回競品日報', 'callback_data': 'menu:competitor'}],
]
_SUBMENUS = {
'main': main_menu_keyboard,
'sales': _submenu_sales,
'products': _submenu_products,
'goals': _submenu_goals,
'analysis': _submenu_analysis,
'trend': _submenu_trend,
'reports': _submenu_reports,
'market': _submenu_market,
'competitor': _submenu_competitor,
'competitor_ppt': _submenu_competitor_ppt,
'category': _submenu_category,
}