该提交实现了完整的钉钉聊天渠道插件,包含: 1. 基础配置、账号管理与凭证校验 2. WebSocket长连接网关与消息去重 3. 消息接收/解析/分发与安全校验 4. 媒体文件上传下载与缓存 5. 互动卡片流式更新与回调处理 6. 群管理、命令支持与诊断工具 7. 完整的插件元数据与依赖声明
51 lines
1.4 KiB
Python
51 lines
1.4 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
from collections import OrderedDict
|
|
|
|
DEFAULT_TTL = 3600
|
|
|
|
|
|
class ExpiredDict:
|
|
def __init__(self, ttl: int = DEFAULT_TTL):
|
|
self._ttl = ttl
|
|
self._data: OrderedDict[str, tuple[object, float]] = OrderedDict()
|
|
|
|
def __contains__(self, key: str) -> bool:
|
|
self._cleanup_expired()
|
|
return key in self._data
|
|
|
|
def add(self, key: str, value: object = True) -> None:
|
|
self._cleanup_expired()
|
|
self._data[key] = (value, time.time() + self._ttl)
|
|
self._data.move_to_end(key)
|
|
|
|
def _cleanup_expired(self) -> None:
|
|
now = time.time()
|
|
while self._data:
|
|
_, (_, expires) = next(iter(self._data.items()))
|
|
if expires > now:
|
|
break
|
|
self._data.popitem(last=False)
|
|
|
|
|
|
class DingTalkDeduplicator:
|
|
def __init__(self, ttl: int = DEFAULT_TTL, hot_reload: bool = False):
|
|
self._received = ExpiredDict(ttl)
|
|
self._hot_reload = hot_reload
|
|
self._start_time = time.time()
|
|
|
|
def is_duplicate(self, msg_id: str, create_time: float | None = None) -> bool:
|
|
if msg_id in self._received:
|
|
return True
|
|
|
|
if self._hot_reload and create_time:
|
|
if create_time < self._start_time - 60:
|
|
return True
|
|
|
|
self._received.add(msg_id)
|
|
return False
|
|
|
|
def is_my_msg(self, msg) -> bool:
|
|
return getattr(msg, "my_msg", False)
|