ForcePilot/backend/package/yuxi/channel/extensions/feishu/media.py
Kris 5e91bb9985 feat(feishu): 新增飞书渠道完整插件实现
新增飞书渠道插件,包含基础配置、事件处理、消息收发、工具调用、权限管理等完整功能模块,支持WebSocket长连接、卡片消息、流式响应、群管理、审批通知等能力
2026-05-21 10:46:42 +08:00

138 lines
4.5 KiB
Python

from __future__ import annotations
import logging
import re
from yuxi.channel.extensions.feishu.utils import safe_filename, recover_latin1_filename
logger = logging.getLogger(__name__)
def detect_file_type(filename: str) -> str:
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
type_map = {
"opus": "opus", "ogg": "opus",
"mp4": "mp4", "mov": "mp4", "avi": "mp4",
"pdf": "pdf",
"doc": "doc", "docx": "doc",
"xls": "xls", "xlsx": "xls",
"ppt": "ppt", "pptx": "ppt",
}
return type_map.get(ext, "stream")
def resolve_outbound_media_kind(filename: str, content_type: str = "") -> dict:
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
image_exts = {"png", "jpg", "jpeg", "gif", "bmp", "webp", "tiff"}
opus_exts = {"opus", "ogg"}
video_exts = {"mp4", "mov", "avi", "mkv", "webm"}
if ext in image_exts or (content_type and content_type.startswith("image/")):
return {"msg_type": "image", "file_type": "image"}
if ext in opus_exts or content_type in ("audio/ogg", "audio/opus"):
return {"msg_type": "audio", "file_type": "opus"}
if ext in video_exts or (content_type and content_type.startswith("video/")):
return {"msg_type": "media", "file_type": "mp4"}
return {"msg_type": "file", "file_type": detect_file_type(filename)}
async def upload_image(
app_id: str,
app_secret: str,
domain: str,
file_path: str,
http_timeout_ms: int = 30000,
) -> dict:
try:
from yuxi.channel.extensions.feishu.client import get_client
client = get_client(app_id, app_secret, domain, http_timeout_ms)
import lark_oapi
with open(file_path, "rb") as f:
req = lark_oapi.im.v1.CreateImageRequest(
request_body=lark_oapi.im.v1.CreateImageRequestBody(
image_type="message",
image=f,
),
)
resp = await client.im.v1.image.create_async(req)
if resp.success():
return {"success": True, "image_key": getattr(resp.data, "image_key", "")}
else:
return {"success": False, "error": getattr(resp, "msg", "")}
except ImportError:
return {"success": False, "error": "lark-oapi SDK not installed"}
except Exception:
logger.exception("Feishu upload_image error")
return {"success": False, "error": "Internal error"}
async def upload_file(
app_id: str,
app_secret: str,
domain: str,
file_path: str,
file_type: str = "stream",
filename: str = "",
http_timeout_ms: int = 30000,
) -> dict:
try:
from yuxi.channel.extensions.feishu.client import get_client
client = get_client(app_id, app_secret, domain, http_timeout_ms)
import lark_oapi
with open(file_path, "rb") as f:
req = lark_oapi.im.v1.CreateFileRequest(
request_body=lark_oapi.im.v1.CreateFileRequestBody(
file_type=file_type,
file_name=filename or safe_filename(file_path),
file=f,
),
)
resp = await client.im.v1.file.create_async(req)
if resp.success():
return {"success": True, "file_key": getattr(resp.data, "file_key", "")}
else:
return {"success": False, "error": getattr(resp, "msg", "")}
except ImportError:
return {"success": False, "error": "lark-oapi SDK not installed"}
except Exception:
logger.exception("Feishu upload_file error")
return {"success": False, "error": "Internal error"}
async def download_image(
app_id: str,
app_secret: str,
domain: str,
image_key: str,
http_timeout_ms: int = 30000,
) -> dict:
try:
from yuxi.channel.extensions.feishu.client import get_client
client = get_client(app_id, app_secret, domain, http_timeout_ms)
import lark_oapi
req = lark_oapi.im.v1.GetImageRequest(image_key=image_key)
resp = await client.im.v1.image.get_async(req)
if resp.success():
return {
"success": True,
"content_type": resp.file.content_type if resp.file else "",
}
else:
return {"success": False, "error": getattr(resp, "msg", "")}
except ImportError:
return {"success": False, "error": "lark-oapi SDK not installed"}
except Exception:
logger.exception("Feishu download_image error")
return {"success": False, "error": "Internal error"}