ForcePilot/backend/package/yuxi/channel/constants.py

109 lines
3.3 KiB
Python
Raw Normal View History

2026-07-15 12:30:58 +08:00
"""多渠道网关共享常量与 Redis Key 模板。"""
from __future__ import annotations
from enum import StrEnum
class UserChannelType(StrEnum):
"""用户渠道来源类型。"""
FEISHU = "feishu"
DINGTALK = "dingtalk"
WECOM = "wecom"
SLACK = "slack"
TELEGRAM = "telegram"
OPEN_API = "open_api"
MANUAL = "manual"
SSO = "sso"
class DeliveryStatus(StrEnum):
"""消息投递状态。"""
COMPLETE = "complete"
PARTIAL_FAILED = "partial_failed"
FAILED = "failed"
DEAD_LETTER = "dead_letter"
PENDING = "pending"
class DispatchResult(StrEnum):
"""出站调度结果。"""
SUCCESS = "success"
PERMANENT_FAILURE = "permanent_failure"
RETRYABLE = "retryable"
class InboundRejectionReason(StrEnum):
"""入站消息拒绝原因。"""
DUPLICATE = "duplicate"
UNKNOWN_CHANNEL = "unknown_channel"
CONFIG_ERROR = "config_error"
SECURITY_REJECTED = "security_rejected"
DM_NOT_ALLOWED = "dm_not_allowed"
DM_PAIRING_REQUIRED = "dm_pairing_required"
GROUP_NOT_ALLOWED = "group_not_allowed"
BOT_LOOP_DETECTED = "bot_loop_detected"
RATE_LIMITED = "rate_limited"
INTERNAL_ERROR = "internal_error"
# Redis Stream
CHANNEL_STREAM_KEY = "ch:messages"
CHANNEL_CONSUMER_GROUP = "ch:dispatchers"
# Redis Pub/Sub
CHANNEL_CONFIG_CHANGE_CHANNEL = "ch:config:changed"
# Redis Key 模板
CHANNEL_PROCESSED_KEY_TEMPLATE = "ch:processed:{message_id}"
CHANNEL_DELIVERED_KEY_TEMPLATE = "ch:delivered:{message_id}"
CHANNEL_PROCESSING_KEY_TEMPLATE = "ch:processing:{message_id}"
CHANNEL_PUBLISHING_KEY_TEMPLATE = "ch:publishing:{message_id}"
CHANNEL_COMPENSATE_LOCK_KEY = "ch:compensate:lock"
CHANNEL_RATE_LIMIT_KEY_TEMPLATE = "ch:rate:{channel_type}:{account_id}:{actor_id}"
# TTL / 时间常量(秒)
CHANNEL_DELIVERED_TTL_SECONDS = 7 * 24 * 3600 # 已投递标记保留 7 天
CHANNEL_PROCESSING_LOCK_TTL_SECONDS = 300 # processing 锁初始 TTL
CHANNEL_PROCESSING_LOCK_RENEW_INTERVAL_SECONDS = 60 # 锁续期间隔
CHANNEL_PUBLISHING_LOCK_TTL_SECONDS = 300
CHANNEL_PROCESSED_TTL_SECONDS = 300 # 入站去重默认 TTL
CHANNEL_COMPENSATE_LOCK_TTL_SECONDS = 300
# 指数退避
CHANNEL_MAX_DELIVERY_ATTEMPTS = 5
CHANNEL_MAX_COMPENSATE_ATTEMPTS = 5
CHANNEL_BACKOFF_BASE_SECONDS = 2
CHANNEL_BACKOFF_MAX_SECONDS = 3600
# 消息认领pending 条目空闲多久后视为死消费者(毫秒)
CHANNEL_PENDING_CLAIM_MIN_IDLE_MS = (CHANNEL_PROCESSING_LOCK_TTL_SECONDS + 10) * 1000
def channel_processed_key(message_id: str | int | None) -> str:
return CHANNEL_PROCESSED_KEY_TEMPLATE.format(message_id=message_id or "unknown")
def channel_delivered_key(message_id: str | int | None) -> str:
return CHANNEL_DELIVERED_KEY_TEMPLATE.format(message_id=message_id or "unknown")
def channel_processing_key(message_id: str | int | None) -> str:
return CHANNEL_PROCESSING_KEY_TEMPLATE.format(message_id=message_id or "unknown")
def channel_publishing_key(message_id: str | int | None) -> str:
return CHANNEL_PUBLISHING_KEY_TEMPLATE.format(message_id=message_id or "unknown")
def channel_rate_limit_key(channel_type: str, account_id: str, actor_id: str) -> str:
return CHANNEL_RATE_LIMIT_KEY_TEMPLATE.format(
channel_type=channel_type,
account_id=account_id,
actor_id=actor_id,
)