ForcePilot/backend/package/yuxi/channel/extensions/weibo/passive_reply.py
Kris 7215418610 feat(channel): 添加企业微信、微博、WhatsApp 和 Workplace 渠道扩展
新增企业微信、微博、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
2026-05-21 12:01:56 +08:00

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()