fix(openclaw): route wakeup phrases back to menu
All checks were successful
CD Pipeline / deploy (push) Successful in 2m55s
All checks were successful
CD Pipeline / deploy (push) Successful in 2m55s
This commit is contained in:
2
app.py
2
app.py
@@ -96,7 +96,7 @@ except Exception as e:
|
||||
|
||||
# 🚩 系統版本定義 (備份與顯示用)
|
||||
# 🚩 2026-05-01 V10.76: Move monthly analysis report onto V2 shell
|
||||
SYSTEM_VERSION = "V10.76"
|
||||
SYSTEM_VERSION = "V10.77"
|
||||
|
||||
# ==========================================
|
||||
# 🔒 SQL Injection 防護函數
|
||||
|
||||
@@ -4166,6 +4166,63 @@ _HELP_KEYWORDS = (
|
||||
'使用說明', '操作說明', 'help', '幫助',
|
||||
)
|
||||
|
||||
_WAKEUP_KEYWORDS = (
|
||||
'小o',
|
||||
'小o小龍蝦',
|
||||
'小o_小龍蝦',
|
||||
'小龍蝦',
|
||||
'openclaw',
|
||||
)
|
||||
|
||||
_BUSINESS_KEYWORDS = (
|
||||
'業績', '營收', '銷售', '銷量', '熱銷', '商品', '廠商', '目標',
|
||||
'報表', '趨勢', '分析', '簡報', '比價', '競品', '補貨', '促銷',
|
||||
'異常', '健康', '分類', '策略', '天氣', '新聞', '匯率', '節慶',
|
||||
'價格', '今日', '昨日', '今天', '本週', '上週', '本月', '上月', '今年',
|
||||
)
|
||||
|
||||
_GREETING_KEYWORDS = (
|
||||
'你好', '嗨', '哈囉', '早安', '午安', '晚安', '在嗎', '你有空', '在不在',
|
||||
'hello', 'hi', 'hey',
|
||||
)
|
||||
|
||||
|
||||
def _normalize_nl_query(q: str) -> str:
|
||||
"""V-Fix:去除 NL 句尾符號與空白,降低誤判率。"""
|
||||
return re.sub(r'[\s_\-,:;,.,。!?!?"“”\(\)\[\]<>]+', '', (q or '').lower())
|
||||
|
||||
|
||||
def _contains_business_signal(q: str) -> bool:
|
||||
"""V-Fix:是否包含可直接進 AI 查詢邏輯的業務關鍵字。"""
|
||||
ql = (q or '').lower()
|
||||
return any(kw in ql for kw in _BUSINESS_KEYWORDS)
|
||||
|
||||
|
||||
def _looks_like_wakeup_prompt(q: str) -> bool:
|
||||
"""V-Fix:防止只叫名字/打招呼時走自由生成。"""
|
||||
ql = (q or '').lower().strip()
|
||||
if not ql:
|
||||
return False
|
||||
|
||||
compact = _normalize_nl_query(ql)
|
||||
if not compact:
|
||||
return False
|
||||
|
||||
if compact in _WAKEUP_KEYWORDS:
|
||||
return True
|
||||
|
||||
if (
|
||||
any(name in compact for name in _WAKEUP_KEYWORDS)
|
||||
and not _contains_business_signal(ql)
|
||||
and len(compact) <= 18
|
||||
):
|
||||
return True
|
||||
|
||||
if compact in _GREETING_KEYWORDS and len(compact) <= 10:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _is_help_question(q: str) -> bool:
|
||||
ql = q.lower()
|
||||
return any(kw in ql for kw in _HELP_KEYWORDS)
|
||||
@@ -4392,6 +4449,17 @@ def openclaw_answer(question: str, chat_id: int = None):
|
||||
today_str = now.strftime("%Y/%m/%d")
|
||||
history_ctx = openclaw_session.history_as_prompt(chat_id) if chat_id else ""
|
||||
|
||||
# ── 只叫名 / 問候:先導回主選單,避免題外市場回覆 ───────────────
|
||||
if _looks_like_wakeup_prompt(question):
|
||||
wakeup_text = (
|
||||
"👋 *OpenClaw(小O)* 在!\n\n"
|
||||
"你可以直接點下面按鈕,或直接問我:\n"
|
||||
" 「今天業績如何?」\n"
|
||||
" 「怎麼查看熱銷商品?」\n"
|
||||
" 「有什麼市場情報?」"
|
||||
)
|
||||
return wakeup_text, quick_menu_keyboard()
|
||||
|
||||
# ── 功能說明直接導 help ───────────────────────────────────
|
||||
if _is_help_question(question):
|
||||
help_text = (
|
||||
|
||||
@@ -38,6 +38,24 @@ def test_webhook_menu_command_handles_bot_suffix(monkeypatch):
|
||||
assert calls == [("menu", "", -200, 55)]
|
||||
|
||||
|
||||
def test_openclaw_answer_wakeup_query_returns_menu():
|
||||
from routes import openclaw_bot_routes as bot
|
||||
|
||||
text, kb = bot.openclaw_answer("小龍蝦")
|
||||
|
||||
assert "OpenClaw(小O)" in text
|
||||
assert kb == bot.quick_menu_keyboard()
|
||||
|
||||
|
||||
def test_openclaw_answer_variants_are_menu_wakeup():
|
||||
from routes import openclaw_bot_routes as bot
|
||||
|
||||
assert bot._looks_like_wakeup_prompt("小O_小龍蝦") is True
|
||||
assert bot._looks_like_wakeup_prompt("Hello, 小O") is True
|
||||
assert bot._looks_like_wakeup_prompt("今天業績") is False
|
||||
assert bot._looks_like_wakeup_prompt("你好") is True
|
||||
|
||||
|
||||
def test_private_menu_command_is_allowed_when_no_whitelist_and_fallback_enabled(monkeypatch):
|
||||
from routes import openclaw_bot_routes as bot
|
||||
|
||||
|
||||
Reference in New Issue
Block a user