tighten ai_auto_response participation and spam guard
This commit is contained in:
@@ -35,31 +35,31 @@ casual_topic = 0.35
|
||||
|
||||
[flow]
|
||||
enable_flow_state = true
|
||||
flow_decay_per_minute = 12
|
||||
idle_threshold = 30
|
||||
warming_threshold = 48
|
||||
engaged_threshold = 78
|
||||
flow_decay_per_minute = 16
|
||||
idle_threshold = 36
|
||||
warming_threshold = 60
|
||||
engaged_threshold = 96
|
||||
at_bot_boost = 40
|
||||
question_boost = 30
|
||||
followup_boost = 20
|
||||
topic_boost = 10
|
||||
returning_member_boost = 6
|
||||
response_accepted_boost = 10
|
||||
ignored_reply_penalty = 20
|
||||
over_reply_penalty = 22
|
||||
followup_boost = 12
|
||||
topic_boost = 4
|
||||
returning_member_boost = 3
|
||||
response_accepted_boost = 6
|
||||
ignored_reply_penalty = 26
|
||||
over_reply_penalty = 32
|
||||
night_penalty = 30
|
||||
max_bot_reply_streak = 2
|
||||
max_bot_reply_streak = 1
|
||||
|
||||
[cooldown]
|
||||
group_reply_cooldown_sec = 90
|
||||
same_user_followup_cooldown_sec = 18
|
||||
group_reply_cooldown_sec = 150
|
||||
same_user_followup_cooldown_sec = 28
|
||||
at_mention_min_interval_sec = 5
|
||||
at_mention_burst_window_sec = 90
|
||||
at_mention_burst_limit = 5
|
||||
at_mention_burst_limit = 4
|
||||
at_mention_silent_sec = 180
|
||||
directed_burst_window_sec = 240
|
||||
directed_burst_limit = 4
|
||||
directed_burst_silent_sec = 480
|
||||
directed_burst_limit = 3
|
||||
directed_burst_silent_sec = 600
|
||||
night_silent_hours = ["01:00-07:30"]
|
||||
|
||||
[memory]
|
||||
@@ -102,6 +102,11 @@ ignore_prefixes = ["/", "#"]
|
||||
ignore_exact = ["收到", "好的", "嗯", "哦", "6", "1", "?", "?"]
|
||||
min_text_length = 1
|
||||
|
||||
[spam_guard]
|
||||
repeat_window_sec = 10
|
||||
repeat_threshold = 2
|
||||
repeat_min_length = 4
|
||||
|
||||
[logging]
|
||||
debug = true
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
self.cooldown_config = self._config.get("cooldown", {}) or {}
|
||||
self.cooldown = CooldownManager(self.cooldown_config)
|
||||
self.image_config = self._config.get("image", {}) or {}
|
||||
self.spam_config = self._config.get("spam_guard", {}) or {}
|
||||
self._synced_member_context_versions: Dict[str, str] = {}
|
||||
self.log_debug = bool((self._config.get("logging", {}) or {}).get("debug", True))
|
||||
self.LOG.debug(f"[{self.name}] 初始化完成")
|
||||
@@ -197,6 +198,23 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
reply_mode="defense",
|
||||
)
|
||||
return False, "ignored_prompt_attack"
|
||||
if self.dedup.should_skip_repeated_room_content(
|
||||
room_id=room_id,
|
||||
content=content,
|
||||
window_sec=int(self.spam_config.get("repeat_window_sec", 45) or 45),
|
||||
repeat_threshold=int(self.spam_config.get("repeat_threshold", 3) or 3),
|
||||
min_length=int(self.spam_config.get("repeat_min_length", 4) or 4),
|
||||
):
|
||||
self._log_event(
|
||||
"skip",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
reason="repeated_room_content",
|
||||
trigger_type="spam_guard",
|
||||
reply_mode="guard",
|
||||
topic="-",
|
||||
)
|
||||
return False, "repeated_room_content"
|
||||
coding_work_request = is_coding_work_request(content)
|
||||
if coding_work_request and not is_at:
|
||||
return False, "skip_coding_work"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import time
|
||||
from typing import Dict, Set
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
|
||||
class DedupManager:
|
||||
@@ -9,6 +10,7 @@ class DedupManager:
|
||||
self.inflight_message_keys: Set[str] = set()
|
||||
self.recent_message_keys: Dict[str, float] = {}
|
||||
self.recent_reply_signatures: Dict[str, float] = {}
|
||||
self.recent_room_content_hits: Dict[str, List[Tuple[float, str]]] = {}
|
||||
|
||||
def begin_message_processing(self, message_key: str, expiry_sec: int) -> bool:
|
||||
if not message_key:
|
||||
@@ -51,3 +53,31 @@ class DedupManager:
|
||||
return True
|
||||
self.recent_reply_signatures[signature] = now
|
||||
return False
|
||||
|
||||
def should_skip_repeated_room_content(
|
||||
self,
|
||||
*,
|
||||
room_id: str,
|
||||
content: str,
|
||||
window_sec: int,
|
||||
repeat_threshold: int,
|
||||
min_length: int = 4,
|
||||
) -> bool:
|
||||
text = self._normalize_room_content(content)
|
||||
if not room_id or not text or len(text) < max(int(min_length or 4), 1):
|
||||
return False
|
||||
now = time.time()
|
||||
window_sec = max(int(window_sec or 0), 1)
|
||||
repeat_threshold = max(int(repeat_threshold or 0), 2)
|
||||
room_items = self.recent_room_content_hits.get(room_id, [])
|
||||
room_items = [(ts, item_text) for ts, item_text in room_items if now - ts <= window_sec]
|
||||
same_count = sum(1 for _, item_text in room_items if item_text == text)
|
||||
room_items.append((now, text))
|
||||
self.recent_room_content_hits[room_id] = room_items[-80:]
|
||||
return same_count + 1 >= repeat_threshold
|
||||
|
||||
@staticmethod
|
||||
def _normalize_room_content(content: str) -> str:
|
||||
text = str(content or "").strip().lower()
|
||||
text = re.sub(r"\s+", "", text)
|
||||
return text
|
||||
|
||||
Reference in New Issue
Block a user