新增企业微信、微博、WhatsApp、Workplace 四个渠道扩展。 企业微信渠道扩展主要模块:config, gateway, webhook, webhook_bot, outbound, streaming, pairing, security, crypto, dedupe, persistent_dedupe, card, directory, events, externalcontact, media, mentions, menu, message, oauth, status 微博渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, passive_reply, broadcast, message, menu, media, subscription, template, status WhatsApp 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, monitor, status Workplace 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, challenge, groups, media, mentions, menu, monitor, persona, quick_reply, signature, subscriptions, template, threading, users, status
74 lines
2.2 KiB
Python
74 lines
2.2 KiB
Python
import asyncio
|
|
import logging
|
|
import time
|
|
from dataclasses import dataclass, field
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MAX_RETRY = 3
|
|
WAIT_WINDOW_SEC = 4.0
|
|
POLL_INTERVAL_SEC = 0.1
|
|
|
|
_passive_state: "PassiveReplyState | None" = None
|
|
|
|
|
|
def _get_passive_state() -> "PassiveReplyState":
|
|
global _passive_state
|
|
if _passive_state is None:
|
|
_passive_state = PassiveReplyState()
|
|
return _passive_state
|
|
|
|
|
|
def _reset_passive_state() -> None:
|
|
global _passive_state
|
|
_passive_state = None
|
|
|
|
|
|
@dataclass
|
|
class PassiveReplyState:
|
|
cache: dict[str, list[tuple[str, str]]] = field(default_factory=dict)
|
|
running: set[str] = field(default_factory=set)
|
|
request_cnt: dict[str, int] = field(default_factory=dict)
|
|
|
|
def is_new_request(self, from_user: str, message_id: str, content: str) -> bool:
|
|
has_cache = from_user in self.cache and bool(self.cache[from_user])
|
|
is_running = from_user in self.running
|
|
is_command = content.startswith("#")
|
|
is_new_msg = message_id not in self.request_cnt
|
|
return (not has_cache and not is_running) or (is_command and is_new_msg)
|
|
|
|
def mark_running(self, from_user: str):
|
|
self.running.add(from_user)
|
|
|
|
def mark_done(self, from_user: str):
|
|
self.running.discard(from_user)
|
|
|
|
def incr_request(self, message_id: str):
|
|
self.request_cnt[message_id] = self.request_cnt.get(message_id, 0) + 1
|
|
|
|
def is_done(self, from_user: str) -> bool:
|
|
return from_user not in self.running
|
|
|
|
def reap(self, from_user: str) -> tuple[str, str] | None:
|
|
entries = self.cache.get(from_user)
|
|
if not entries:
|
|
return None
|
|
result = entries.pop(0)
|
|
if not entries:
|
|
del self.cache[from_user]
|
|
return result
|
|
|
|
def add_cache(self, from_user: str, reply_type: str, content: str):
|
|
if from_user not in self.cache:
|
|
self.cache[from_user] = []
|
|
self.cache[from_user].append((reply_type, content))
|
|
|
|
def cleanup_user(self, from_user: str):
|
|
self.cache.pop(from_user, None)
|
|
self.running.discard(from_user)
|
|
|
|
def reset(self):
|
|
self.cache.clear()
|
|
self.running.clear()
|
|
self.request_cnt.clear()
|