新增了Zalo用户频道的完整适配器实现,包括: - 基础的适配器初始化与导出结构 - 群组同步与成员获取功能 - 请求限流与退避重试机制 - 健康检查与状态探针 - 消息反应/表情处理工具 - 贴纸缓存与消息去重功能 - 消息ID格式化与追踪 - TTS语音合成支持 - 消息发送权限校验 - 长文本分块发送 - 操作审批流程 - 常量配置与国际化支持 - 图像视觉分析功能 - 贴纸消息处理 - 登录与配置向导 - 群组上下文缓存 - 网关连接管理 - 配置Schema校验 - 状态问题与安全审计 - 内联按钮与交互组件 - 交互式回调分发 - 联系人与群组目录管理 - 富媒体卡片消息支持
81 lines
2.2 KiB
Python
81 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from yuxi.channels.models import ChannelMessage
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
async def analyze_image(
|
|
bridge: Any,
|
|
image_url: str,
|
|
prompt: str = "Describe this image in detail.",
|
|
) -> dict[str, Any]:
|
|
try:
|
|
resp = await bridge.post(
|
|
"/vision/analyze",
|
|
json={
|
|
"image_url": image_url,
|
|
"prompt": prompt,
|
|
},
|
|
)
|
|
return resp.json()
|
|
except Exception as e:
|
|
logger.warning(f"[ZaloUser] Vision analysis failed: {e}")
|
|
return {"error": str(e)}
|
|
|
|
|
|
async def analyze_media(
|
|
bridge: Any,
|
|
media_url: str,
|
|
media_type: str = "image",
|
|
prompt: str = "",
|
|
) -> dict[str, Any]:
|
|
if media_type == "image":
|
|
default_prompt = "Describe this image in detail."
|
|
elif media_type == "video":
|
|
default_prompt = "Describe this video content."
|
|
else:
|
|
default_prompt = "Analyze this media content."
|
|
|
|
return await analyze_image(bridge, media_url, prompt or default_prompt)
|
|
|
|
|
|
def has_vision_support(config: dict[str, Any]) -> bool:
|
|
return config.get("vision", {}).get("enabled", False)
|
|
|
|
|
|
async def augment_message_with_vision(
|
|
bridge: Any,
|
|
message: ChannelMessage,
|
|
config: dict[str, Any],
|
|
) -> ChannelMessage:
|
|
if not has_vision_support(config):
|
|
return message
|
|
|
|
image_attachments = [a for a in message.attachments if a.type == "image"]
|
|
if not image_attachments:
|
|
return message
|
|
|
|
descriptions: list[str] = []
|
|
for att in image_attachments:
|
|
if not att.url:
|
|
continue
|
|
try:
|
|
result = await analyze_image(bridge, att.url)
|
|
if result.get("description"):
|
|
descriptions.append(f"[Image: {result['description']}]")
|
|
elif result.get("text"):
|
|
descriptions.append(f"[Image OCR: {result['text']}]")
|
|
except Exception:
|
|
pass
|
|
|
|
if descriptions:
|
|
vision_text = "\n".join(descriptions)
|
|
if message.content:
|
|
message.content = f"{message.content}\n{vision_text}"
|
|
else:
|
|
message.content = vision_text
|
|
|
|
return message
|