refactor(zalo_oa): 整理导入顺序并修复部分代码逻辑
1. 调整多个文件的导入排序与导入项 2. 为命令注册添加名称小写处理与空命令拦截 3. 新增link消息、reaction和已读回执的事件映射 4. 实现消息发送熔断器与流式发送支持 5. 修复导入顺序与异常捕获细节
This commit is contained in:
parent
ba060ca9c5
commit
c35f37bd31
@ -1,15 +1,9 @@
|
|||||||
|
from yuxi.channels.adapters.zalo_oa.accounts import get_default_account_id, list_account_ids, resolve_account
|
||||||
from yuxi.channels.adapters.zalo_oa.adapter import ZaloOAAdapter, get_webhook_path_refs
|
from yuxi.channels.adapters.zalo_oa.adapter import ZaloOAAdapter, get_webhook_path_refs
|
||||||
from yuxi.channels.adapters.zalo_oa.security import DMPolicy, check_dm_allowed, load_allowlist, resolve_dm_policy
|
from yuxi.channels.adapters.zalo_oa.approval import normalize_approver_id, resolve_approvers
|
||||||
from yuxi.channels.adapters.zalo_oa.pairing import PairingStore
|
from yuxi.channels.adapters.zalo_oa.audit import AuditEventType, AuditLogger
|
||||||
from yuxi.channels.adapters.zalo_oa.dedup import EventDeduplicator
|
from yuxi.channels.adapters.zalo_oa.cache import SentMessageCache
|
||||||
from yuxi.channels.adapters.zalo_oa.webhook import register_webhook, unregister_webhook, get_webhook_info
|
|
||||||
from yuxi.channels.adapters.zalo_oa.webhook_ratelimit import WebhookRateLimiter, build_rate_limit_key, resolve_client_ip
|
|
||||||
from yuxi.channels.adapters.zalo_oa.webhook_anomaly import WebhookAnomalyTracker
|
|
||||||
from yuxi.channels.adapters.zalo_oa.directory import ZaloOADirectory
|
|
||||||
from yuxi.channels.adapters.zalo_oa.status_issues import collect_status_issues
|
|
||||||
from yuxi.channels.adapters.zalo_oa.chunking import chunk_text
|
from yuxi.channels.adapters.zalo_oa.chunking import chunk_text
|
||||||
from yuxi.channels.adapters.zalo_oa.messaging import normalize_messaging_target, looks_like_user_id
|
|
||||||
from yuxi.channels.adapters.zalo_oa.accounts import list_account_ids, resolve_account, get_default_account_id
|
|
||||||
from yuxi.channels.adapters.zalo_oa.commands import (
|
from yuxi.channels.adapters.zalo_oa.commands import (
|
||||||
CommandHandler,
|
CommandHandler,
|
||||||
CommandRegistry,
|
CommandRegistry,
|
||||||
@ -19,12 +13,12 @@ from yuxi.channels.adapters.zalo_oa.commands import (
|
|||||||
handle_command,
|
handle_command,
|
||||||
register_command,
|
register_command,
|
||||||
)
|
)
|
||||||
from yuxi.channels.adapters.zalo_oa.approval import resolve_approvers, normalize_approver_id
|
from yuxi.channels.adapters.zalo_oa.dedup import EventDeduplicator
|
||||||
from yuxi.channels.adapters.zalo_oa.cache import SentMessageCache
|
from yuxi.channels.adapters.zalo_oa.directory import ZaloOADirectory
|
||||||
from yuxi.channels.adapters.zalo_oa.message_actions import describe_actions, is_action_supported
|
from yuxi.channels.adapters.zalo_oa.doctor import run_doctor
|
||||||
from yuxi.channels.adapters.zalo_oa.audit import AuditEventType, AuditLogger
|
|
||||||
from yuxi.channels.adapters.zalo_oa.media_vision import MediaVisionCache, MediaVisionProcessor
|
from yuxi.channels.adapters.zalo_oa.media_vision import MediaVisionCache, MediaVisionProcessor
|
||||||
from yuxi.channels.adapters.zalo_oa.setup_entry import ZaloOASetupPlugin, get_setup_plugin, verify_channel_setup
|
from yuxi.channels.adapters.zalo_oa.message_actions import describe_actions, is_action_supported
|
||||||
|
from yuxi.channels.adapters.zalo_oa.messaging import looks_like_user_id, normalize_messaging_target
|
||||||
from yuxi.channels.adapters.zalo_oa.outbound_media import (
|
from yuxi.channels.adapters.zalo_oa.outbound_media import (
|
||||||
OutboundMediaHost,
|
OutboundMediaHost,
|
||||||
cleanup_media_cache,
|
cleanup_media_cache,
|
||||||
@ -32,7 +26,13 @@ from yuxi.channels.adapters.zalo_oa.outbound_media import (
|
|||||||
resolve_attachment,
|
resolve_attachment,
|
||||||
store_media,
|
store_media,
|
||||||
)
|
)
|
||||||
from yuxi.channels.adapters.zalo_oa.doctor import run_doctor
|
from yuxi.channels.adapters.zalo_oa.pairing import PairingStore
|
||||||
|
from yuxi.channels.adapters.zalo_oa.security import DMPolicy, check_dm_allowed, load_allowlist, resolve_dm_policy
|
||||||
|
from yuxi.channels.adapters.zalo_oa.setup_entry import ZaloOASetupPlugin, get_setup_plugin, verify_channel_setup
|
||||||
|
from yuxi.channels.adapters.zalo_oa.status_issues import collect_status_issues
|
||||||
|
from yuxi.channels.adapters.zalo_oa.webhook import get_webhook_info, register_webhook, unregister_webhook
|
||||||
|
from yuxi.channels.adapters.zalo_oa.webhook_anomaly import WebhookAnomalyTracker
|
||||||
|
from yuxi.channels.adapters.zalo_oa.webhook_ratelimit import WebhookRateLimiter, build_rate_limit_key, resolve_client_ip
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"ZaloOAAdapter",
|
"ZaloOAAdapter",
|
||||||
|
|||||||
@ -7,35 +7,36 @@ import time
|
|||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from typing import Any, ClassVar
|
from typing import Any, ClassVar
|
||||||
|
|
||||||
from yuxi.channels.adapters.zalo_oa.approval import check_approval_required, build_approval_request
|
from yuxi.channels.adapters.zalo_oa.approval import build_approval_request, check_approval_required
|
||||||
from yuxi.channels.adapters.zalo_oa.audit import AuditEventType, AuditLogger
|
from yuxi.channels.adapters.zalo_oa.audit import AuditEventType, AuditLogger
|
||||||
from yuxi.channels.adapters.zalo_oa.client import ZaloOAClient
|
from yuxi.channels.adapters.zalo_oa.client import ZaloOAClient
|
||||||
from yuxi.channels.adapters.zalo_oa.dedup import EventDeduplicator
|
from yuxi.channels.adapters.zalo_oa.dedup import EventDeduplicator
|
||||||
|
from yuxi.channels.adapters.zalo_oa.directory import ZaloOADirectory
|
||||||
from yuxi.channels.adapters.zalo_oa.formatter import ZaloOAMessageFormatter
|
from yuxi.channels.adapters.zalo_oa.formatter import ZaloOAMessageFormatter
|
||||||
|
from yuxi.channels.adapters.zalo_oa.media_vision import MediaVisionProcessor
|
||||||
from yuxi.channels.adapters.zalo_oa.normalizer import SkipMessageError, ZaloOAEventNormalizer
|
from yuxi.channels.adapters.zalo_oa.normalizer import SkipMessageError, ZaloOAEventNormalizer
|
||||||
from yuxi.channels.adapters.zalo_oa.pairing import PairingStore, send_pairing_message, send_pairing_success
|
from yuxi.channels.adapters.zalo_oa.pairing import PairingStore, send_pairing_message, send_pairing_success
|
||||||
|
from yuxi.channels.adapters.zalo_oa.polling import ZaloOAPoller
|
||||||
from yuxi.channels.adapters.zalo_oa.probe import probe_zalo_oa
|
from yuxi.channels.adapters.zalo_oa.probe import probe_zalo_oa
|
||||||
from yuxi.channels.adapters.zalo_oa.security import DMPolicy, check_dm_allowed, load_allowlist, resolve_dm_policy
|
from yuxi.channels.adapters.zalo_oa.security import DMPolicy, check_dm_allowed, load_allowlist, resolve_dm_policy
|
||||||
from yuxi.channels.adapters.zalo_oa.send import ZaloOASender
|
from yuxi.channels.adapters.zalo_oa.send import ZaloOASender
|
||||||
from yuxi.channels.adapters.zalo_oa.signature import verify_zalo_oa_signature
|
from yuxi.channels.adapters.zalo_oa.signature import verify_zalo_oa_signature
|
||||||
|
from yuxi.channels.adapters.zalo_oa.status_issues import collect_status_issues
|
||||||
|
from yuxi.channels.adapters.zalo_oa.voice import ZaloOAVoice
|
||||||
from yuxi.channels.adapters.zalo_oa.webhook import register_webhook, unregister_webhook
|
from yuxi.channels.adapters.zalo_oa.webhook import register_webhook, unregister_webhook
|
||||||
|
from yuxi.channels.adapters.zalo_oa.webhook_anomaly import WebhookAnomalyTracker
|
||||||
from yuxi.channels.adapters.zalo_oa.webhook_ratelimit import (
|
from yuxi.channels.adapters.zalo_oa.webhook_ratelimit import (
|
||||||
WebhookRateLimiter,
|
WebhookRateLimiter,
|
||||||
build_rate_limit_key,
|
build_rate_limit_key,
|
||||||
resolve_client_ip,
|
resolve_client_ip,
|
||||||
)
|
)
|
||||||
from yuxi.channels.adapters.zalo_oa.webhook_anomaly import WebhookAnomalyTracker
|
|
||||||
from yuxi.channels.adapters.zalo_oa.directory import ZaloOADirectory
|
|
||||||
from yuxi.channels.adapters.zalo_oa.polling import ZaloOAPoller
|
|
||||||
from yuxi.channels.adapters.zalo_oa.voice import ZaloOAVoice
|
|
||||||
from yuxi.channels.adapters.zalo_oa.media_vision import MediaVisionProcessor
|
|
||||||
from yuxi.channels.adapters.zalo_oa.status_issues import collect_status_issues
|
|
||||||
from yuxi.channels.base import BaseChannelAdapter
|
from yuxi.channels.base import BaseChannelAdapter
|
||||||
from yuxi.channels.capabilities import ChannelCapabilities
|
from yuxi.channels.capabilities import ChannelCapabilities
|
||||||
from yuxi.channels.exceptions import (
|
from yuxi.channels.exceptions import (
|
||||||
ChannelAuthenticationError,
|
ChannelAuthenticationError,
|
||||||
MessageFormatError,
|
MessageFormatError,
|
||||||
)
|
)
|
||||||
|
from yuxi.channels.infra.circuit_breaker import CircuitBreaker, CircuitBreakerOpenError
|
||||||
from yuxi.channels.meta import ChannelMeta
|
from yuxi.channels.meta import ChannelMeta
|
||||||
from yuxi.channels.models import (
|
from yuxi.channels.models import (
|
||||||
ChannelMessage,
|
ChannelMessage,
|
||||||
@ -92,7 +93,7 @@ class ZaloOAAdapter(BaseChannelAdapter):
|
|||||||
reply_to_mode: ClassVar[str] = "off"
|
reply_to_mode: ClassVar[str] = "off"
|
||||||
|
|
||||||
capabilities = ChannelCapabilities(
|
capabilities = ChannelCapabilities(
|
||||||
chat_types=["direct"], # Zalo OA API 仅支持 direct(1对1私聊),不支持 group
|
chat_types=["direct"],
|
||||||
reply=False,
|
reply=False,
|
||||||
unsend=True,
|
unsend=True,
|
||||||
media=True,
|
media=True,
|
||||||
@ -109,6 +110,7 @@ class ZaloOAAdapter(BaseChannelAdapter):
|
|||||||
streaming_modes=["off"],
|
streaming_modes=["off"],
|
||||||
text_chunk_limit=2000,
|
text_chunk_limit=2000,
|
||||||
max_media_size_mb=10,
|
max_media_size_mb=10,
|
||||||
|
typing=True,
|
||||||
)
|
)
|
||||||
meta = ChannelMeta(
|
meta = ChannelMeta(
|
||||||
id="zalo_oa",
|
id="zalo_oa",
|
||||||
@ -161,6 +163,7 @@ class ZaloOAAdapter(BaseChannelAdapter):
|
|||||||
self._heartbeat_count = 0
|
self._heartbeat_count = 0
|
||||||
self._heartbeat_failures = 0
|
self._heartbeat_failures = 0
|
||||||
self._config_hash = self._compute_config_hash()
|
self._config_hash = self._compute_config_hash()
|
||||||
|
self._circuit_breaker = CircuitBreaker(failure_threshold=5, recovery_timeout=60, channel_id="zalo_oa")
|
||||||
|
|
||||||
def _compute_config_hash(self) -> str:
|
def _compute_config_hash(self) -> str:
|
||||||
"""计算当前配置的哈希值,用于检测配置变更."""
|
"""计算当前配置的哈希值,用于检测配置变更."""
|
||||||
@ -383,18 +386,85 @@ class ZaloOAAdapter(BaseChannelAdapter):
|
|||||||
if self._status != ChannelStatus.CONNECTED or not self._sender:
|
if self._status != ChannelStatus.CONNECTED or not self._sender:
|
||||||
return DeliveryResult(success=False, error="Zalo OA not connected")
|
return DeliveryResult(success=False, error="Zalo OA not connected")
|
||||||
|
|
||||||
result = await self._sender.send(response)
|
async def _do_send() -> DeliveryResult:
|
||||||
self._last_outbound_at = time.time()
|
result = await self._sender.send(response)
|
||||||
self._outbound_count += 1
|
self._last_outbound_at = time.time()
|
||||||
self._audit.record(
|
self._outbound_count += 1
|
||||||
AuditEventType.MESSAGE_SENT if result.success else AuditEventType.MESSAGE_FAILED,
|
self._audit.record(
|
||||||
{
|
AuditEventType.MESSAGE_SENT if result.success else AuditEventType.MESSAGE_FAILED,
|
||||||
"recipient": response.identity.channel_user_id,
|
{
|
||||||
"message_id": result.message_id,
|
"recipient": response.identity.channel_user_id,
|
||||||
"success": result.success,
|
"message_id": result.message_id,
|
||||||
},
|
"success": result.success,
|
||||||
)
|
},
|
||||||
return result
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
try:
|
||||||
|
return await self._circuit_breaker.call(_do_send)
|
||||||
|
except CircuitBreakerOpenError:
|
||||||
|
return DeliveryResult(success=False, error="Circuit breaker open")
|
||||||
|
|
||||||
|
async def send_stream_chunk(
|
||||||
|
self,
|
||||||
|
chat_id: str,
|
||||||
|
msg_id: str,
|
||||||
|
chunk: str,
|
||||||
|
finished: bool = False,
|
||||||
|
) -> DeliveryResult:
|
||||||
|
if finished:
|
||||||
|
from yuxi.channels.models import ChannelIdentity, ChannelResponse
|
||||||
|
|
||||||
|
identity = ChannelIdentity(
|
||||||
|
channel_id=self.channel_id,
|
||||||
|
channel_type=self.channel_type,
|
||||||
|
channel_chat_id=chat_id,
|
||||||
|
channel_user_id=chat_id,
|
||||||
|
)
|
||||||
|
response = ChannelResponse(identity=identity, content=chunk)
|
||||||
|
return await self.send(response)
|
||||||
|
|
||||||
|
if not self._sender:
|
||||||
|
return DeliveryResult(success=False, error="Sender not initialized")
|
||||||
|
|
||||||
|
streaming_cfg = self.config.get("streaming", {})
|
||||||
|
if not isinstance(streaming_cfg, dict):
|
||||||
|
streaming_cfg = {}
|
||||||
|
|
||||||
|
if self.config.get("auto_typing", True):
|
||||||
|
await self._sender.send_typing_indicator(chat_id)
|
||||||
|
|
||||||
|
from yuxi.channels.adapters.zalo_oa.chunking import chunk_text
|
||||||
|
from yuxi.channels.models import ChannelIdentity, ChannelResponse
|
||||||
|
|
||||||
|
text_limit = self.text_chunk_limit
|
||||||
|
fallback_cfg = streaming_cfg.get("fallback", {})
|
||||||
|
if isinstance(fallback_cfg, dict):
|
||||||
|
text_limit = fallback_cfg.get("chunk_size", text_limit)
|
||||||
|
|
||||||
|
chunks = chunk_text(chunk, text_limit)
|
||||||
|
show_progress = streaming_cfg.get("progress_indicator", True)
|
||||||
|
total = len(chunks)
|
||||||
|
|
||||||
|
for i, ch in enumerate(chunks, 1):
|
||||||
|
content = ch
|
||||||
|
if show_progress and total > 1:
|
||||||
|
content = f"[{i}/{total}] {ch}"
|
||||||
|
identity = ChannelIdentity(
|
||||||
|
channel_id=self.channel_id,
|
||||||
|
channel_type=self.channel_type,
|
||||||
|
channel_chat_id=chat_id,
|
||||||
|
channel_user_id=chat_id,
|
||||||
|
)
|
||||||
|
response = ChannelResponse(identity=identity, content=content)
|
||||||
|
await self.send(response)
|
||||||
|
|
||||||
|
if i < total:
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
await asyncio.sleep(0.3)
|
||||||
|
|
||||||
|
return DeliveryResult(success=True)
|
||||||
|
|
||||||
def validate_webhook_request(self, headers: dict, body: bytes) -> None:
|
def validate_webhook_request(self, headers: dict, body: bytes) -> None:
|
||||||
content_type = headers.get("content-type", "")
|
content_type = headers.get("content-type", "")
|
||||||
@ -440,6 +510,7 @@ class ZaloOAAdapter(BaseChannelAdapter):
|
|||||||
"user_send_video",
|
"user_send_video",
|
||||||
"user_send_audio",
|
"user_send_audio",
|
||||||
"user_forward_message",
|
"user_forward_message",
|
||||||
|
"user_send_link",
|
||||||
):
|
):
|
||||||
if not self._dedup.claim(data):
|
if not self._dedup.claim(data):
|
||||||
logger.debug("[ZaloOA] Duplicate event skipped")
|
logger.debug("[ZaloOA] Duplicate event skipped")
|
||||||
@ -583,7 +654,7 @@ class ZaloOAAdapter(BaseChannelAdapter):
|
|||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
raw, headers, future = await asyncio.wait_for(queue.get(), timeout=idle_timeout)
|
raw, headers, future = await asyncio.wait_for(queue.get(), timeout=idle_timeout)
|
||||||
except asyncio.TimeoutError:
|
except TimeoutError:
|
||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
if headers:
|
if headers:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
COMMAND_PREFIXES = ("/", "!")
|
COMMAND_PREFIXES = ("/", "!")
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ class CommandRegistry:
|
|||||||
description: str = "",
|
description: str = "",
|
||||||
handler: CommandHandler | None = None,
|
handler: CommandHandler | None = None,
|
||||||
):
|
):
|
||||||
|
name = name.lower()
|
||||||
self._commands[name] = {
|
self._commands[name] = {
|
||||||
"description": description,
|
"description": description,
|
||||||
"handler": handler or self._make_default_handler(name),
|
"handler": handler or self._make_default_handler(name),
|
||||||
@ -91,7 +92,9 @@ def extract_command(text: str) -> tuple[str | None, str]:
|
|||||||
text = text.strip()
|
text = text.strip()
|
||||||
for prefix in COMMAND_PREFIXES:
|
for prefix in COMMAND_PREFIXES:
|
||||||
if text.startswith(prefix):
|
if text.startswith(prefix):
|
||||||
cmd_text = text[len(prefix) :]
|
cmd_text = text[len(prefix) :].strip()
|
||||||
|
if not cmd_text:
|
||||||
|
return "", ""
|
||||||
parts = cmd_text.split(None, 1)
|
parts = cmd_text.split(None, 1)
|
||||||
cmd = parts[0].lower()
|
cmd = parts[0].lower()
|
||||||
args = parts[1] if len(parts) > 1 else ""
|
args = parts[1] if len(parts) > 1 else ""
|
||||||
|
|||||||
@ -71,11 +71,14 @@ class ZaloOAEventNormalizer:
|
|||||||
"user_send_gif": EventType.MESSAGE_RECEIVED,
|
"user_send_gif": EventType.MESSAGE_RECEIVED,
|
||||||
"user_send_location": EventType.MESSAGE_RECEIVED,
|
"user_send_location": EventType.MESSAGE_RECEIVED,
|
||||||
"user_send_business_card": EventType.MESSAGE_RECEIVED,
|
"user_send_business_card": EventType.MESSAGE_RECEIVED,
|
||||||
|
"user_send_link": EventType.MESSAGE_RECEIVED,
|
||||||
"user_submit_form": EventType.CARD_ACTION,
|
"user_submit_form": EventType.CARD_ACTION,
|
||||||
"user_click_button": EventType.CARD_ACTION,
|
"user_click_button": EventType.CARD_ACTION,
|
||||||
"user_send_video": EventType.MESSAGE_RECEIVED,
|
"user_send_video": EventType.MESSAGE_RECEIVED,
|
||||||
"user_send_audio": EventType.MESSAGE_RECEIVED,
|
"user_send_audio": EventType.MESSAGE_RECEIVED,
|
||||||
"user_forward_message": EventType.MESSAGE_RECEIVED,
|
"user_forward_message": EventType.MESSAGE_RECEIVED,
|
||||||
|
"reaction": EventType.REACTION_ADDED,
|
||||||
|
"read_receipt": EventType.READ_RECEIPT,
|
||||||
}
|
}
|
||||||
return mapping.get(event_name, EventType.MESSAGE_RECEIVED)
|
return mapping.get(event_name, EventType.MESSAGE_RECEIVED)
|
||||||
|
|
||||||
@ -89,6 +92,7 @@ class ZaloOAEventNormalizer:
|
|||||||
"user_send_gif": MessageType.IMAGE,
|
"user_send_gif": MessageType.IMAGE,
|
||||||
"user_send_location": MessageType.LOCATION,
|
"user_send_location": MessageType.LOCATION,
|
||||||
"user_send_business_card": MessageType.CARD,
|
"user_send_business_card": MessageType.CARD,
|
||||||
|
"user_send_link": MessageType.TEXT,
|
||||||
"user_send_video": MessageType.VIDEO,
|
"user_send_video": MessageType.VIDEO,
|
||||||
"user_send_audio": MessageType.AUDIO,
|
"user_send_audio": MessageType.AUDIO,
|
||||||
"user_submit_form": MessageType.TEXT,
|
"user_submit_form": MessageType.TEXT,
|
||||||
@ -167,6 +171,22 @@ class ZaloOAEventNormalizer:
|
|||||||
)
|
)
|
||||||
elif att_type == "location":
|
elif att_type == "location":
|
||||||
text = f"Location: ({payload.get('latitude')}, {payload.get('longitude')})"
|
text = f"Location: ({payload.get('latitude')}, {payload.get('longitude')})"
|
||||||
|
elif att_type == "link":
|
||||||
|
url = payload.get("url", "")
|
||||||
|
title = payload.get("title", "")
|
||||||
|
description = payload.get("description", "")
|
||||||
|
text_parts = [f"[Link] {url}"]
|
||||||
|
if title:
|
||||||
|
text_parts.append(f"Title: {title}")
|
||||||
|
if description:
|
||||||
|
text_parts.append(f"Description: {description}")
|
||||||
|
text = "\n".join(text_parts)
|
||||||
|
elif att_type == "business_card":
|
||||||
|
contact_name = payload.get("name", payload.get("contact_name", ""))
|
||||||
|
phone = payload.get("phone", "")
|
||||||
|
text = f"[Business Card] {contact_name}"
|
||||||
|
if phone:
|
||||||
|
text += f" ({phone})"
|
||||||
else:
|
else:
|
||||||
text = text or str(payload)[:500]
|
text = text or str(payload)[:500]
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
from typing import Any
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from yuxi.utils.logging_config import logger
|
from yuxi.utils.logging_config import logger
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
from typing import Any
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from yuxi.channels.adapters.zalo_oa.cache import SentMessageCache
|
from yuxi.channels.adapters.zalo_oa.cache import SentMessageCache
|
||||||
from yuxi.channels.adapters.zalo_oa.client import ZaloOAClient
|
from yuxi.channels.adapters.zalo_oa.client import ZaloOAClient
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from yuxi.channels.adapters.zalo_oa.security import DMPolicy, resolve_dm_policy, collect_security_warnings
|
from yuxi.channels.adapters.zalo_oa.security import DMPolicy, collect_security_warnings, resolve_dm_policy
|
||||||
|
|
||||||
|
|
||||||
def collect_status_issues(config: dict[str, Any], oa_info: dict[str, Any]) -> list[dict[str, Any]]:
|
def collect_status_issues(config: dict[str, Any], oa_info: dict[str, Any]) -> list[dict[str, Any]]:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user