mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-03-04 07:43:46 +08:00
180 lines
8.4 KiB
Python
180 lines
8.4 KiB
Python
from bridge.context import ContextType
|
||
from channel.chat_message import ChatMessage
|
||
import json
|
||
import os
|
||
import requests
|
||
from common.log import logger
|
||
from common.tmp_dir import TmpDir
|
||
from common import utils
|
||
from common.utils import expand_path
|
||
from config import conf
|
||
|
||
|
||
class FeishuMessage(ChatMessage):
|
||
def __init__(self, event: dict, is_group=False, access_token=None):
|
||
super().__init__(event)
|
||
msg = event.get("message")
|
||
sender = event.get("sender")
|
||
self.access_token = access_token
|
||
self.msg_id = msg.get("message_id")
|
||
self.create_time = msg.get("create_time")
|
||
self.is_group = is_group
|
||
msg_type = msg.get("message_type")
|
||
|
||
if msg_type == "text":
|
||
self.ctype = ContextType.TEXT
|
||
content = json.loads(msg.get('content'))
|
||
self.content = content.get("text").strip()
|
||
elif msg_type == "image":
|
||
# 单张图片消息:下载并缓存,等待用户提问时一起发送
|
||
self.ctype = ContextType.IMAGE
|
||
content = json.loads(msg.get("content"))
|
||
image_key = content.get("image_key")
|
||
|
||
# 下载图片到工作空间临时目录
|
||
workspace_root = expand_path(conf().get("agent_workspace", "~/cow"))
|
||
tmp_dir = os.path.join(workspace_root, "tmp")
|
||
os.makedirs(tmp_dir, exist_ok=True)
|
||
image_path = os.path.join(tmp_dir, f"{image_key}.png")
|
||
|
||
# 下载图片
|
||
url = f"https://open.feishu.cn/open-apis/im/v1/messages/{msg.get('message_id')}/resources/{image_key}"
|
||
headers = {"Authorization": "Bearer " + access_token}
|
||
params = {"type": "image"}
|
||
response = requests.get(url=url, headers=headers, params=params)
|
||
|
||
if response.status_code == 200:
|
||
with open(image_path, "wb") as f:
|
||
f.write(response.content)
|
||
logger.info(f"[FeiShu] Downloaded single image, key={image_key}, path={image_path}")
|
||
self.content = image_path
|
||
self.image_path = image_path # 保存图片路径
|
||
else:
|
||
logger.error(f"[FeiShu] Failed to download single image, key={image_key}, status={response.status_code}")
|
||
self.content = f"[图片下载失败: {image_key}]"
|
||
self.image_path = None
|
||
elif msg_type == "post":
|
||
# 富文本消息,可能包含图片、文本等多种元素
|
||
content = json.loads(msg.get("content"))
|
||
|
||
# 飞书富文本消息结构:content 直接包含 title 和 content 数组
|
||
# 不是嵌套在 post 字段下
|
||
title = content.get("title", "")
|
||
content_list = content.get("content", [])
|
||
|
||
logger.info(f"[FeiShu] Post message - title: '{title}', content_list length: {len(content_list)}")
|
||
|
||
# 收集所有图片和文本
|
||
image_keys = []
|
||
text_parts = []
|
||
|
||
if title:
|
||
text_parts.append(title)
|
||
|
||
for block in content_list:
|
||
logger.debug(f"[FeiShu] Processing block: {block}")
|
||
# block 本身就是元素列表
|
||
if not isinstance(block, list):
|
||
continue
|
||
|
||
for element in block:
|
||
element_tag = element.get("tag")
|
||
logger.debug(f"[FeiShu] Element tag: {element_tag}, element: {element}")
|
||
if element_tag == "img":
|
||
# 找到图片元素
|
||
image_key = element.get("image_key")
|
||
if image_key:
|
||
image_keys.append(image_key)
|
||
elif element_tag == "text":
|
||
# 文本元素
|
||
text_content = element.get("text", "")
|
||
if text_content:
|
||
text_parts.append(text_content)
|
||
|
||
logger.info(f"[FeiShu] Parsed - images: {len(image_keys)}, text_parts: {text_parts}")
|
||
|
||
# 富文本消息统一作为文本消息处理
|
||
self.ctype = ContextType.TEXT
|
||
|
||
if image_keys:
|
||
# 如果包含图片,下载并在文本中引用本地路径
|
||
workspace_root = expand_path(conf().get("agent_workspace", "~/cow"))
|
||
tmp_dir = os.path.join(workspace_root, "tmp")
|
||
os.makedirs(tmp_dir, exist_ok=True)
|
||
|
||
# 保存图片路径映射
|
||
self.image_paths = {}
|
||
for image_key in image_keys:
|
||
image_path = os.path.join(tmp_dir, f"{image_key}.png")
|
||
self.image_paths[image_key] = image_path
|
||
|
||
def _download_images():
|
||
for image_key, image_path in self.image_paths.items():
|
||
url = f"https://open.feishu.cn/open-apis/im/v1/messages/{self.msg_id}/resources/{image_key}"
|
||
headers = {"Authorization": "Bearer " + access_token}
|
||
params = {"type": "image"}
|
||
response = requests.get(url=url, headers=headers, params=params)
|
||
if response.status_code == 200:
|
||
with open(image_path, "wb") as f:
|
||
f.write(response.content)
|
||
logger.info(f"[FeiShu] Image downloaded from post message, key={image_key}, path={image_path}")
|
||
else:
|
||
logger.error(f"[FeiShu] Failed to download image from post, key={image_key}, status={response.status_code}")
|
||
|
||
# 立即下载图片,不使用延迟下载
|
||
# 因为 TEXT 类型消息不会调用 prepare()
|
||
_download_images()
|
||
|
||
# 构建消息内容:文本 + 图片路径
|
||
content_parts = []
|
||
if text_parts:
|
||
content_parts.append("\n".join(text_parts).strip())
|
||
for image_key, image_path in self.image_paths.items():
|
||
content_parts.append(f"[图片: {image_path}]")
|
||
|
||
self.content = "\n".join(content_parts)
|
||
logger.info(f"[FeiShu] Received post message with {len(image_keys)} image(s) and text: {self.content}")
|
||
else:
|
||
# 纯文本富文本消息
|
||
self.content = "\n".join(text_parts).strip() if text_parts else "[富文本消息]"
|
||
logger.info(f"[FeiShu] Received post message (text only): {self.content}")
|
||
elif msg_type == "file":
|
||
self.ctype = ContextType.FILE
|
||
content = json.loads(msg.get("content"))
|
||
file_key = content.get("file_key")
|
||
file_name = content.get("file_name")
|
||
|
||
self.content = TmpDir().path() + file_key + "." + utils.get_path_suffix(file_name)
|
||
|
||
def _download_file():
|
||
# 如果响应状态码是200,则将响应内容写入本地文件
|
||
url = f"https://open.feishu.cn/open-apis/im/v1/messages/{self.msg_id}/resources/{file_key}"
|
||
headers = {
|
||
"Authorization": "Bearer " + access_token,
|
||
}
|
||
params = {
|
||
"type": "file"
|
||
}
|
||
response = requests.get(url=url, headers=headers, params=params)
|
||
if response.status_code == 200:
|
||
with open(self.content, "wb") as f:
|
||
f.write(response.content)
|
||
else:
|
||
logger.info(f"[FeiShu] Failed to download file, key={file_key}, res={response.text}")
|
||
self._prepare_fn = _download_file
|
||
else:
|
||
raise NotImplementedError("Unsupported message type: Type:{} ".format(msg_type))
|
||
|
||
self.from_user_id = sender.get("sender_id").get("open_id")
|
||
self.to_user_id = event.get("app_id")
|
||
if is_group:
|
||
# 群聊
|
||
self.other_user_id = msg.get("chat_id")
|
||
self.actual_user_id = self.from_user_id
|
||
self.content = self.content.replace("@_user_1", "").strip()
|
||
self.actual_user_nickname = ""
|
||
else:
|
||
# 私聊
|
||
self.other_user_id = self.from_user_id
|
||
self.actual_user_id = self.from_user_id
|