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