mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-14 12:23:18 +08:00
Nudge WhatsApp group ops asks that skip @mention.
When require_mention drops an ops short-intent, send one throttled @sender tip instead of silent ignore; casual chat stays quiet. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
94a1b5b26c
commit
6c5536b758
4 changed files with 201 additions and 0 deletions
|
|
@ -3,6 +3,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
_BOT_MENTION_RE = re.compile(r"@\S+")
|
||||
|
|
@ -108,9 +110,78 @@ def maybe_ops_short_intent_system_hint(*, text: str, lang: str = "en") -> str:
|
|||
return build_ops_short_intent_hint(intent=intent, lang=lang)
|
||||
|
||||
|
||||
def build_group_mention_nudge_text(
|
||||
*,
|
||||
intent: str | None = None,
|
||||
lang: str = "en",
|
||||
triggers: list[str] | None = None,
|
||||
) -> str:
|
||||
"""Tell field users why a group ops ask was ignored (require @bot / trigger)."""
|
||||
extra = ""
|
||||
trigs = [str(x or "").strip() for x in (triggers or []) if str(x or "").strip()]
|
||||
if trigs:
|
||||
shown = ", ".join(trigs[:3])
|
||||
if str(lang or "").strip().lower().startswith("zh"):
|
||||
extra = f"或发送触发词({shown})"
|
||||
else:
|
||||
extra = f" or use a trigger ({shown})"
|
||||
label = str(intent or "").strip().replace("_", " ")
|
||||
if str(lang or "").strip().lower().startswith("zh"):
|
||||
topic = f"(识别到:{label})" if label else ""
|
||||
return (
|
||||
f"群里需要先 @我{extra} 才会处理运维请求{topic}。"
|
||||
"请带上 @ 后重发(断纤/离线/告警/Excel/license 等)。"
|
||||
)
|
||||
topic = f" (detected: {label})" if label else ""
|
||||
return (
|
||||
f"In this group I only answer when @mentioned{extra}{topic}. "
|
||||
"Please re-send with @me for ops asks (fiber / offline / alarms / excel / license)."
|
||||
)
|
||||
|
||||
|
||||
_MENTION_NUDGE_LOCK = threading.Lock()
|
||||
_MENTION_NUDGE_LAST: dict[str, float] = {}
|
||||
_MENTION_NUDGE_TTL_S = 12 * 60.0
|
||||
|
||||
|
||||
def should_send_group_mention_nudge(
|
||||
*,
|
||||
account_id: str,
|
||||
chat_id: str,
|
||||
user_id: str,
|
||||
now: float | None = None,
|
||||
ttl_s: float | None = None,
|
||||
) -> bool:
|
||||
"""Throttle one nudge per sender/chat for a few minutes."""
|
||||
key = f"{str(account_id or '').strip()}|{str(chat_id or '').strip()}|{str(user_id or '').strip()}"
|
||||
if not key.strip("|"):
|
||||
return False
|
||||
ts = float(now if now is not None else time.monotonic())
|
||||
window = float(ttl_s if ttl_s is not None else _MENTION_NUDGE_TTL_S)
|
||||
with _MENTION_NUDGE_LOCK:
|
||||
prev = _MENTION_NUDGE_LAST.get(key)
|
||||
if prev is not None and (ts - float(prev)) < window:
|
||||
return False
|
||||
_MENTION_NUDGE_LAST[key] = ts
|
||||
if len(_MENTION_NUDGE_LAST) > 512:
|
||||
cutoff = ts - window
|
||||
stale = [k for k, v in _MENTION_NUDGE_LAST.items() if float(v) < cutoff]
|
||||
for k in stale[:128]:
|
||||
_MENTION_NUDGE_LAST.pop(k, None)
|
||||
return True
|
||||
|
||||
|
||||
def reset_group_mention_nudge_throttle_for_tests() -> None:
|
||||
with _MENTION_NUDGE_LOCK:
|
||||
_MENTION_NUDGE_LAST.clear()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"build_group_mention_nudge_text",
|
||||
"build_ops_short_intent_hint",
|
||||
"detect_ops_short_intent",
|
||||
"maybe_ops_short_intent_system_hint",
|
||||
"normalize_ops_user_text",
|
||||
"reset_group_mention_nudge_throttle_for_tests",
|
||||
"should_send_group_mention_nudge",
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue