新增 Mattermost 渠道完整实现,包含适配器核心、消息处理、交互回调、命令支持、安全校验、多账号管理等功能,支持机器人消息发送、交互按钮、命令注册、投票功能以及配置动态修改等特性。
46 lines
1.5 KiB
Python
46 lines
1.5 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import time
|
|
|
|
|
|
class InboundDebouncer:
|
|
"""入站消息防抖 — 按 channel + thread key 去重。
|
|
|
|
防止 Mattermost WebSocket 重复推送导致的多次触发。
|
|
"""
|
|
|
|
def __init__(self, ttl_ms: int = 1000, max_entries: int = 2000):
|
|
self._ttl_ms = ttl_ms
|
|
self._max_entries = max_entries
|
|
self._entries: dict[str, float] = {}
|
|
|
|
def should_process(self, channel_id: str, thread_id: str, content: str) -> bool:
|
|
key = self._build_key(channel_id, thread_id, content)
|
|
now = time.monotonic()
|
|
|
|
if key in self._entries:
|
|
if now - self._entries[key] < self._ttl_ms / 1000:
|
|
return False
|
|
|
|
self._entries[key] = now
|
|
self._prune(now)
|
|
return True
|
|
|
|
def _build_key(self, channel_id: str, thread_id: str, content: str) -> str:
|
|
raw = f"{channel_id}:{thread_id}:{content[:100]}"
|
|
return hashlib.md5(raw.encode()).hexdigest()
|
|
|
|
def _prune(self, now: float) -> None:
|
|
if len(self._entries) > self._max_entries:
|
|
cutoff = now - self._ttl_ms / 1000
|
|
self._entries = {k: v for k, v in self._entries.items() if v > cutoff}
|
|
if len(self._entries) > self._max_entries:
|
|
overflow = len(self._entries) - self._max_entries
|
|
keys = sorted(self._entries, key=lambda k: self._entries[k])[:overflow]
|
|
for k in keys:
|
|
del self._entries[k]
|
|
|
|
def clear(self) -> None:
|
|
self._entries.clear()
|