tighten ai_auto_response participation and spam guard
This commit is contained in:
@@ -35,31 +35,31 @@ casual_topic = 0.35
|
|||||||
|
|
||||||
[flow]
|
[flow]
|
||||||
enable_flow_state = true
|
enable_flow_state = true
|
||||||
flow_decay_per_minute = 12
|
flow_decay_per_minute = 16
|
||||||
idle_threshold = 30
|
idle_threshold = 36
|
||||||
warming_threshold = 48
|
warming_threshold = 60
|
||||||
engaged_threshold = 78
|
engaged_threshold = 96
|
||||||
at_bot_boost = 40
|
at_bot_boost = 40
|
||||||
question_boost = 30
|
question_boost = 30
|
||||||
followup_boost = 20
|
followup_boost = 12
|
||||||
topic_boost = 10
|
topic_boost = 4
|
||||||
returning_member_boost = 6
|
returning_member_boost = 3
|
||||||
response_accepted_boost = 10
|
response_accepted_boost = 6
|
||||||
ignored_reply_penalty = 20
|
ignored_reply_penalty = 26
|
||||||
over_reply_penalty = 22
|
over_reply_penalty = 32
|
||||||
night_penalty = 30
|
night_penalty = 30
|
||||||
max_bot_reply_streak = 2
|
max_bot_reply_streak = 1
|
||||||
|
|
||||||
[cooldown]
|
[cooldown]
|
||||||
group_reply_cooldown_sec = 90
|
group_reply_cooldown_sec = 150
|
||||||
same_user_followup_cooldown_sec = 18
|
same_user_followup_cooldown_sec = 28
|
||||||
at_mention_min_interval_sec = 5
|
at_mention_min_interval_sec = 5
|
||||||
at_mention_burst_window_sec = 90
|
at_mention_burst_window_sec = 90
|
||||||
at_mention_burst_limit = 5
|
at_mention_burst_limit = 4
|
||||||
at_mention_silent_sec = 180
|
at_mention_silent_sec = 180
|
||||||
directed_burst_window_sec = 240
|
directed_burst_window_sec = 240
|
||||||
directed_burst_limit = 4
|
directed_burst_limit = 3
|
||||||
directed_burst_silent_sec = 480
|
directed_burst_silent_sec = 600
|
||||||
night_silent_hours = ["01:00-07:30"]
|
night_silent_hours = ["01:00-07:30"]
|
||||||
|
|
||||||
[memory]
|
[memory]
|
||||||
@@ -102,6 +102,11 @@ ignore_prefixes = ["/", "#"]
|
|||||||
ignore_exact = ["收到", "好的", "嗯", "哦", "6", "1", "?", "?"]
|
ignore_exact = ["收到", "好的", "嗯", "哦", "6", "1", "?", "?"]
|
||||||
min_text_length = 1
|
min_text_length = 1
|
||||||
|
|
||||||
|
[spam_guard]
|
||||||
|
repeat_window_sec = 10
|
||||||
|
repeat_threshold = 2
|
||||||
|
repeat_min_length = 4
|
||||||
|
|
||||||
[logging]
|
[logging]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
|||||||
self.cooldown_config = self._config.get("cooldown", {}) or {}
|
self.cooldown_config = self._config.get("cooldown", {}) or {}
|
||||||
self.cooldown = CooldownManager(self.cooldown_config)
|
self.cooldown = CooldownManager(self.cooldown_config)
|
||||||
self.image_config = self._config.get("image", {}) or {}
|
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._synced_member_context_versions: Dict[str, str] = {}
|
||||||
self.log_debug = bool((self._config.get("logging", {}) or {}).get("debug", True))
|
self.log_debug = bool((self._config.get("logging", {}) or {}).get("debug", True))
|
||||||
self.LOG.debug(f"[{self.name}] 初始化完成")
|
self.LOG.debug(f"[{self.name}] 初始化完成")
|
||||||
@@ -197,6 +198,23 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
|||||||
reply_mode="defense",
|
reply_mode="defense",
|
||||||
)
|
)
|
||||||
return False, "ignored_prompt_attack"
|
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)
|
coding_work_request = is_coding_work_request(content)
|
||||||
if coding_work_request and not is_at:
|
if coding_work_request and not is_at:
|
||||||
return False, "skip_coding_work"
|
return False, "skip_coding_work"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from typing import Dict, Set
|
from typing import Dict, List, Set, Tuple
|
||||||
|
|
||||||
|
|
||||||
class DedupManager:
|
class DedupManager:
|
||||||
@@ -9,6 +10,7 @@ class DedupManager:
|
|||||||
self.inflight_message_keys: Set[str] = set()
|
self.inflight_message_keys: Set[str] = set()
|
||||||
self.recent_message_keys: Dict[str, float] = {}
|
self.recent_message_keys: Dict[str, float] = {}
|
||||||
self.recent_reply_signatures: 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:
|
def begin_message_processing(self, message_key: str, expiry_sec: int) -> bool:
|
||||||
if not message_key:
|
if not message_key:
|
||||||
@@ -51,3 +53,31 @@ class DedupManager:
|
|||||||
return True
|
return True
|
||||||
self.recent_reply_signatures[signature] = now
|
self.recent_reply_signatures[signature] = now
|
||||||
return False
|
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