"""多渠道网关共享常量与 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, )