operations_project/apps/feishu/services/auto_gmail_conversation_service.py
2025-05-20 15:57:10 +08:00

923 lines
38 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
import json
import time
from datetime import datetime, timedelta
import uuid
from django.db import transaction
from django.db.models import Q
from django.utils import timezone
from apps.gmail.models import GmailConversation, GmailCredential, UserGoal, AutoReplyConfig
from apps.chat.models import ChatHistory
from apps.gmail.services.gmail_service import GmailService
from apps.gmail.services.goal_service import get_conversation_summary, get_last_message, generate_recommended_reply
from apps.common.services.ai_service import AIService
logger = logging.getLogger(__name__)
class AutoGmailConversationService:
"""
自动化Gmail对话服务基于用户目标自动与多个达人进行沟通
"""
@staticmethod
def create_auto_conversation(user, user_email, influencer_email, greeting_message, goal_description):
"""
创建自动对话并发送初始消息
Args:
user: 用户对象
user_email: 用户邮箱(已授权)
influencer_email: 达人邮箱
greeting_message: 初始打招呼消息
goal_description: 对话目标
Returns:
tuple: (是否成功, 对话ID或错误信息, 目标对象或None)
"""
try:
# 验证用户Gmail凭证
credential = GmailCredential.objects.filter(user=user, email=user_email).first()
if not credential:
return False, f"未找到{user_email}的Gmail授权", None
# 检查是否已存在与该达人的对话
existing_conversation = GmailConversation.objects.filter(
user=user,
user_email=user_email,
influencer_email=influencer_email,
is_active=True
).first()
conversation = None
if existing_conversation:
conversation = existing_conversation
logger.info(f"找到与达人 {influencer_email} 的现有对话: {conversation.conversation_id}")
# 打印对话的has_sent_greeting字段值以便追踪
logger.info(f"对话 {conversation.conversation_id} 的has_sent_greeting值: {conversation.has_sent_greeting}")
else:
# 创建新的对话
conversation_id = f"gmail_{user.id}_{str(uuid.uuid4())[:8]}"
conversation = GmailConversation.objects.create(
user=user,
user_email=user_email,
influencer_email=influencer_email,
conversation_id=conversation_id,
title=f"{influencer_email} 的Gmail对话",
is_active=True,
has_sent_greeting=False, # 新创建的对话尚未发送打招呼消息
metadata={
'auto_conversation': True,
'created_at': timezone.now().isoformat()
}
)
logger.info(f"创建新的自动对话: {conversation.conversation_id}, has_sent_greeting={conversation.has_sent_greeting}")
# 只有当对话尚未发送打招呼消息时才发送
logger.info(f"检查是否需要发送打招呼消息: conversation_id={conversation.conversation_id}, has_sent_greeting={conversation.has_sent_greeting}")
if not conversation.has_sent_greeting:
logger.info(f"对话 {conversation.conversation_id} 尚未发送打招呼消息,准备发送")
# 设置固定的问候消息
greeting_message = """Paid Collaboration Opportunity with TikTok's #1 Fragrance Brand 🌸
Hi,
I'm Vira from OOIN Media, and I'm reaching out on behalf of a top-performing fragrance brand Sttes on TikTok Shop—currently ranked #1 in the perfume category.
This brand has already launched several viral products and is now looking to partner with select creators like you through paid collaborations to continue driving awareness and sales.
We'd love to explore a partnership and would appreciate it if you could share:
Your rate for a single TikTok video
Whether you offer bundle pricing for multiple videos
Any additional details or formats you offer (e.g. story integration, livestream add-ons, etc.)
The product has strong market traction, proven conversions, and a competitive commission structure if you're also open to affiliate partnerships.
Looking forward to the opportunity to work together and hearing your rates!
Warm regards,
Vira
OOIN Media"""
# 发送打招呼消息
subject = "Paid Collaboration Opportunity with TikTok's #1 Fragrance Brand"
logger.info(f"开始向 {influencer_email} 发送打招呼消息")
success, message_id = GmailService.send_email(
user=user,
user_email=user_email,
to_email=influencer_email,
subject=subject,
body=greeting_message
)
if not success:
logger.error(f"发送打招呼消息失败: {message_id}")
return False, f"发送打招呼消息失败: {message_id}", None
# 更新对话的has_sent_greeting字段
conversation.has_sent_greeting = True
conversation.save(update_fields=['has_sent_greeting', 'updated_at'])
logger.info(f"对话 {conversation.conversation_id} 已发送打招呼消息并更新了has_sent_greeting={conversation.has_sent_greeting}")
else:
logger.info(f"对话 {conversation.conversation_id} 已经发送过打招呼消息,不再重复发送")
# 创建目标
goal, error = AutoGmailConversationService.create_user_goal(
user=user,
conversation_id=conversation.conversation_id,
goal_description=goal_description
)
if error:
return False, f"创建对话目标失败: {error}", None
# 设置或刷新Gmail推送通知
notification_result, notification_error = GmailService.setup_gmail_push_notification(
user=user,
user_email=user_email
)
if not notification_result and notification_error:
logger.warning(f"设置Gmail推送通知失败: {notification_error},但对话创建成功")
# 查询最新的conversation对象以验证字段值
refreshed_conversation = GmailConversation.objects.get(id=conversation.id)
logger.info(f"返回结果前检查对话状态: conversation_id={refreshed_conversation.conversation_id}, has_sent_greeting={refreshed_conversation.has_sent_greeting}")
return True, conversation.conversation_id, goal
except Exception as e:
logger.error(f"创建自动对话失败: {str(e)}")
import traceback
logger.error(traceback.format_exc())
return False, f"创建自动对话失败: {str(e)}", None
@staticmethod
def create_user_goal(user, conversation_id, goal_description):
"""
为用户创建对话目标
Args:
user: 用户对象
conversation_id: 对话ID
goal_description: 目标描述
Returns:
tuple: (目标对象, 错误信息)
"""
try:
# 检查对话是否存在
conversation = GmailConversation.objects.filter(conversation_id=conversation_id).first()
if not conversation:
return None, "对话不存在"
# 检查权限
if conversation.user.id != user.id:
return None, "无权限访问此对话"
# 停用该用户针对这个对话的之前的目标
UserGoal.objects.filter(
user=user,
conversation=conversation,
is_active=True
).update(is_active=False)
# 创建新目标
goal = UserGoal.objects.create(
user=user,
conversation=conversation,
description=goal_description,
is_active=True,
status='pending',
metadata={
'created_at': timezone.now().isoformat(),
'influencer_email': conversation.influencer_email,
'user_email': conversation.user_email
}
)
logger.info(f"用户 {user.username} 为对话 {conversation_id} 创建了新目标")
return goal, None
except Exception as e:
logger.error(f"创建用户目标失败: {str(e)}")
return None, f"创建用户目标失败: {str(e)}"
@staticmethod
def check_goal_achieved(goal_description, conversation_history):
"""
检查目标是否已经达成
Args:
goal_description: 目标描述
conversation_history: 对话历史
Returns:
tuple: (是否达成, 置信度, 错误信息)
"""
try:
# 格式化对话历史
dialog_text = "\n\n".join([
f"{'用户' if msg.get('role') == 'user' else '达人'}: {msg.get('content', '')}"
for msg in conversation_history
])
# 构建提示词
prompt = f"""
分析以下对话历史,判断用户的目标是否已经达成:
用户目标: {goal_description}
对话历史:
{dialog_text}
请分析并判断此目标是否已经达成。仅回答""""以及一个0到1之间的数字表示达成目标的置信度格式为: "判断结果|置信度"
例如: "是|0.85""否|0.32"
"""
messages = [
{
"role": "system",
"content": "你是一个专业的目标达成分析师,你的任务是判断对话中用户的目标是否已经达成。"
},
{
"role": "user",
"content": prompt
}
]
# 调用AI服务
result, error = AIService.call_silicon_cloud_api(
messages,
model="Pro/deepseek-ai/DeepSeek-R1",
max_tokens=100,
temperature=0.1
)
if error:
return False, 0, error
# 解析结果
result = result.strip().lower()
# 尝试提取判断结果和置信度
if '|' in result:
judgment, confidence_str = result.split('|', 1)
is_achieved = judgment.strip() == ''
try:
confidence = float(confidence_str.strip())
confidence = max(0, min(1, confidence)) # 确保在0-1之间
except ValueError:
confidence = 0.5 # 默认置信度
return is_achieved, confidence, None
else:
# 兜底处理
is_achieved = '' in result
return is_achieved, 0.5, "AI返回格式异常"
except Exception as e:
logger.error(f"检查目标达成状态失败: {str(e)}")
return False, 0, f"检查目标达成状态失败: {str(e)}"
@staticmethod
def generate_reply_and_send(user, goal_id, conversation_id=None):
"""
生成回复并发送邮件
Args:
user: 用户对象
goal_id: 目标ID
conversation_id: 对话ID (可选,如果提供则验证目标与对话的关联)
Returns:
tuple: (成功标志, 错误信息)
"""
try:
# 获取目标信息
goal = UserGoal.objects.filter(id=goal_id, user=user).first()
if not goal:
return False, "目标不存在或不属于当前用户"
# 获取关联的对话ID
goal_conversation_id = goal.get_conversation_id()
# 如果提供了conversation_id验证与目标关联的对话一致
if conversation_id and goal_conversation_id and conversation_id != goal_conversation_id:
return False, "目标与对话不匹配"
# 确定最终使用的对话ID
final_conversation_id = conversation_id or goal_conversation_id
if not final_conversation_id:
return False, "无法确定对话ID目标未关联对话"
# 获取对话信息
conversation = GmailConversation.objects.filter(conversation_id=final_conversation_id).first()
if not conversation:
return False, "对话不存在"
# 检查权限
if conversation.user.id != user.id:
return False, "无权限访问此对话"
# 获取Gmail凭证
credential = GmailCredential.objects.filter(user=user, email=conversation.user_email).first()
if not credential:
return False, f"未找到{conversation.user_email}的Gmail凭证"
# 获取对话摘要
conversation_summary = get_conversation_summary(final_conversation_id)
if not conversation_summary:
conversation_summary = "无对话摘要"
# 获取最后一条达人消息
last_message = get_last_message(final_conversation_id)
if not last_message:
return False, "对话中没有达人消息,无法生成回复"
# 生成回复内容
reply_content, error = generate_recommended_reply(
user=user,
goal_description=goal.description,
conversation_summary=conversation_summary,
last_message=last_message
)
if error:
return False, f"生成回复内容失败: {error}"
if not reply_content:
return False, "生成的回复内容为空"
# 从最后一条达人消息中提取主题
subject = "回复: "
if last_message and "主题:" in last_message:
subject_line = last_message.split("\n")[0]
if "主题:" in subject_line:
original_subject = subject_line.split("主题:", 1)[1].strip()
subject = f"回复: {original_subject}"
# 发送邮件
success, message_id = GmailService.send_email(
user=user,
user_email=conversation.user_email,
to_email=conversation.influencer_email,
subject=subject,
body=reply_content
)
if not success:
return False, f"发送邮件失败: {message_id}"
# 更新目标状态
goal.last_activity_time = timezone.now()
goal.status = 'in_progress'
goal.metadata = goal.metadata or {}
goal.metadata['last_reply_time'] = timezone.now().isoformat()
goal.metadata['last_reply_id'] = message_id
goal.save()
logger.info(f"成功为用户 {user.username} 生成并发送回复邮件")
return True, message_id
except Exception as e:
logger.error(f"生成回复并发送邮件失败: {str(e)}")
return False, f"生成回复并发送邮件失败: {str(e)}"
@staticmethod
def get_active_goals_for_conversation(user, conversation_id):
"""
获取指定对话的活跃目标
Args:
user: 用户对象
conversation_id: 对话ID
Returns:
UserGoal: 活跃目标对象或None
"""
# 先查找对话对象
conversation = GmailConversation.objects.filter(conversation_id=conversation_id).first()
if not conversation:
return None
# 查找与对话关联的活跃目标
return UserGoal.objects.filter(
user=user,
conversation=conversation,
is_active=True
).order_by('-created_at').first()
@staticmethod
def get_recommended_reply(user, conversation_id):
"""
获取推荐回复
Args:
user: 用户对象
conversation_id: 对话ID
Returns:
tuple: (推荐回复内容, 错误信息)
"""
try:
# 获取对话信息
conversation = GmailConversation.objects.filter(conversation_id=conversation_id).first()
if not conversation:
return None, "对话不存在"
# 检查权限
if conversation.user.id != user.id:
return None, "无权限访问此对话"
# 获取该对话的目标
goal = AutoGmailConversationService.get_active_goals_for_conversation(user, conversation_id)
if not goal:
return None, "未找到该对话的活跃目标"
# 获取对话摘要
conversation_summary = get_conversation_summary(conversation_id)
if not conversation_summary:
conversation_summary = "无对话摘要"
# 获取最后一条达人消息
last_message = get_last_message(conversation_id)
if not last_message:
return None, "对话中没有达人消息,无法生成推荐回复"
# 生成推荐回复
reply_content, error = generate_recommended_reply(
user=user,
goal_description=goal.description,
conversation_summary=conversation_summary,
last_message=last_message
)
if error:
return None, f"生成推荐回复失败: {error}"
return reply_content, None
except Exception as e:
logger.error(f"获取推荐回复失败: {str(e)}")
return None, f"获取推荐回复失败: {str(e)}"
@staticmethod
@transaction.atomic
def process_active_goals():
"""
处理所有活跃的目标,检查新消息并自动回复
"""
try:
# 获取所有活跃目标
active_goals = UserGoal.objects.filter(
is_active=True,
status__in=['pending', 'in_progress']
).select_related('user', 'conversation')
logger.info(f"发现 {active_goals.count()} 个活跃目标需要处理")
for goal in active_goals:
try:
# 获取对话ID
if not goal.conversation:
logger.error(f"目标 {goal.id} 没有关联对话")
continue
conversation_id = goal.conversation.conversation_id
# 获取用户
user = goal.user
# 获取最近的消息记录 - 修改查询方式避免"Cannot filter a query once a slice has been taken"错误
# 分别获取最新的用户消息和助手消息
latest_assistant_msg = ChatHistory.objects.filter(
conversation_id=conversation_id,
role='assistant'
).order_by('-created_at').first()
latest_user_msg = ChatHistory.objects.filter(
conversation_id=conversation_id,
role='user'
).order_by('-created_at').first()
# 获取用于分析目标达成情况的最近消息记录
recent_messages = ChatHistory.objects.filter(
conversation_id=conversation_id
).order_by('-created_at')[:20]
# 生成对话历史,用于判断目标是否达成
conversation_history = []
for msg in reversed(list(recent_messages)): # 按时间顺序排列
conversation_history.append({
'role': msg.role,
'content': msg.content,
'created_at': msg.created_at.isoformat()
})
# 检查目标是否已达成
is_achieved, confidence, error = AutoGmailConversationService.check_goal_achieved(
goal_description=goal.description,
conversation_history=conversation_history
)
# 更新目标状态
goal.metadata = goal.metadata or {}
goal.metadata['last_check_time'] = timezone.now().isoformat()
if is_achieved and confidence >= 0.7: # 高置信度认为目标已达成
goal.status = 'completed'
goal.completion_time = timezone.now()
goal.metadata['completion_confidence'] = confidence
goal.save()
logger.info(f"目标 {goal.id} 已达成,置信度: {confidence}")
continue
# 获取最近的一条消息,不区分角色
latest_msg = ChatHistory.objects.filter(
conversation_id=conversation_id
).order_by('-created_at').first()
# 检查是否有新的达人消息需要回复
# 输出详细的消息角色和ID信息以便调试
if latest_assistant_msg:
logger.info(f"最新达人消息: ID={latest_assistant_msg.id}, 时间={latest_assistant_msg.created_at}, 内容前20字符={latest_assistant_msg.content[:20]}")
if latest_user_msg:
logger.info(f"最新用户消息: ID={latest_user_msg.id}, 时间={latest_user_msg.created_at}, 内容前20字符={latest_user_msg.content[:20]}")
if latest_msg:
logger.info(f"最新消息(任意角色): ID={latest_msg.id}, 角色={latest_msg.role}, 时间={latest_msg.created_at}")
# 如果最后一条消息是达人发的,则需要回复
needs_reply = latest_msg and latest_msg.role == 'assistant'
if not needs_reply:
# 更新目标状态
goal.status = 'in_progress'
goal.save()
logger.info(f"目标 {goal.id} 无需回复,最后消息角色为: {latest_msg.role if latest_msg else '无消息'}")
continue
# 需要回复,直接生成回复并发送
logger.info(f"目标 {goal.id} 需要回复,最后消息来自达人")
success, msg = AutoGmailConversationService.generate_reply_and_send(
user=user,
goal_id=goal.id,
conversation_id=conversation_id
)
if success:
# 更新目标状态
goal.status = 'in_progress'
goal.save()
logger.info(f"目标 {goal.id} 自动回复成功")
else:
logger.error(f"目标 {goal.id} 自动回复失败: {msg}")
except Exception as e:
logger.error(f"处理目标 {goal.id} 时出错: {str(e)}")
return True
except Exception as e:
logger.error(f"处理活跃目标失败: {str(e)}")
return False
@staticmethod
def get_all_user_conversations_with_goals(user):
"""
获取用户所有对话和对应的目标
Args:
user: 用户对象
Returns:
list: 对话和目标信息列表
"""
try:
# 获取用户所有对话
conversations = GmailConversation.objects.filter(user=user).order_by('-updated_at')
result = []
for conversation in conversations:
# 查询该对话的活跃目标
goal = UserGoal.objects.filter(
user=user,
conversation=conversation,
is_active=True
).order_by('-created_at').first()
# 准备对话和目标信息
conv_data = {
'conversation_id': conversation.conversation_id,
'influencer_email': conversation.influencer_email,
'user_email': conversation.user_email,
'title': conversation.title,
'last_message_time': conversation.updated_at,
'has_active_goal': goal is not None
}
# 如果有活跃目标,添加目标信息
if goal:
conv_data['goal'] = {
'id': str(goal.id),
'description': goal.description,
'status': goal.status,
'created_at': goal.created_at,
'updated_at': goal.updated_at
}
result.append(conv_data)
return result
except Exception as e:
logger.error(f"获取用户对话和目标失败: {str(e)}")
return []
@staticmethod
def find_auto_reply_config(user_email, influencer_email):
"""
查找匹配的自动回复配置
Args:
user_email: 用户Gmail邮箱
influencer_email: 达人Gmail邮箱
Returns:
AutoReplyConfig: 匹配的自动回复配置或None
"""
try:
config = AutoReplyConfig.objects.filter(
user_email=user_email,
influencer_email=influencer_email,
is_enabled=True
).first()
return config
except Exception as e:
logger.error(f"查找自动回复配置失败: {str(e)}")
return None
@staticmethod
def get_or_create_conversation(user, user_email, influencer_email):
"""
获取或创建Gmail对话
Args:
user: 用户对象
user_email: 用户Gmail邮箱
influencer_email: 达人Gmail邮箱
Returns:
tuple: (GmailConversation, bool) - 对话对象和是否新创建
"""
try:
# 查找现有对话
conversation = GmailConversation.objects.filter(
user=user,
user_email=user_email,
influencer_email=influencer_email,
is_active=True
).first()
if conversation:
return conversation, False
# 创建新对话
conversation_id = f"feishu_gmail_{user.id}_{influencer_email.split('@')[0]}_{timezone.now().strftime('%m%d%H%M')}"
conversation = GmailConversation.objects.create(
user=user,
user_email=user_email,
influencer_email=influencer_email,
conversation_id=conversation_id,
title=f"飞书与 {influencer_email} 的自动对话",
is_active=True,
metadata={
'auto_reply': True,
'feishu_auto': True,
'created_at': timezone.now().isoformat()
}
)
return conversation, True
except Exception as e:
logger.error(f"获取或创建Gmail对话失败: {str(e)}")
return None, False
@staticmethod
def should_auto_reply(config, message_data):
"""
判断是否应该自动回复
Args:
config: 自动回复配置
message_data: 消息数据,包含发件人、收件人等信息
Returns:
bool: 是否应该自动回复
"""
if not config or not config.can_reply():
return False
# 判断消息发送时间,避免回复太老的消息
message_time = message_data.get('timestamp')
if message_time:
try:
message_time = timezone.datetime.fromisoformat(message_time.replace('Z', '+00:00'))
current_time = timezone.now()
# 只回复24小时内的消息
if (current_time - message_time) > timedelta(hours=24):
logger.info(f"消息太旧,不自动回复: {message_time}")
return False
except Exception as e:
logger.warning(f"解析消息时间失败: {str(e)}")
# 检查上次回复时间,避免频繁回复
if config.last_reply_time:
time_since_last_reply = timezone.now() - config.last_reply_time
# 至少间隔30分钟
if time_since_last_reply < timedelta(minutes=30):
logger.info(f"距离上次回复时间太短,不自动回复: {time_since_last_reply}")
return False
return True
@staticmethod
def process_webhook_message(message_data):
"""
处理来自webhook的消息推送自动回复活跃对话
Args:
message_data: 邮件数据
Returns:
bool: 是否成功处理
"""
try:
# 提取发件人邮箱
from_email = message_data.get('from', {}).get('emailAddress', {}).get('address')
if not from_email:
logger.warning(f"无法提取发件人邮箱: {message_data}")
return False
# 提取收件人邮箱
to_emails = []
for recipient in message_data.get('toRecipients', []):
email = recipient.get('emailAddress', {}).get('address')
if email:
to_emails.append(email)
if not to_emails:
logger.warning(f"无法提取收件人邮箱: {message_data}")
return False
# 收件人邮箱是我们系统用户的邮箱,发件人是达人邮箱
user_email = to_emails[0] # 假设第一个收件人是我们系统的用户
influencer_email = from_email
logger.info(f"处理webhook消息: 发件人(达人)={influencer_email}, 收件人(用户)={user_email}")
# 查找符合条件的活跃对话
active_conversations = GmailConversation.objects.filter(
user_email=user_email,
influencer_email=influencer_email,
is_active=True
).select_related('user')
if not active_conversations.exists():
logger.info(f"未找到与达人 {influencer_email} 的活跃对话")
return False
# 获取最近的一个活跃对话
conversation = active_conversations.order_by('-updated_at').first()
user = conversation.user
logger.info(f"找到活跃对话: {conversation.conversation_id}, 用户: {user.username}")
# 验证Gmail凭证
credential = GmailCredential.objects.filter(user=user, email=user_email).first()
if not credential:
logger.error(f"未找到用户 {user.username} 的Gmail凭证: {user_email}")
return False
# 记录邮件内容到对话历史
message_id = message_data.get('id')
subject = message_data.get('subject', '无主题')
body = message_data.get('body', {}).get('content', '')
# 创建达人消息记录
chat_message = ChatHistory.objects.create(
conversation_id=conversation.conversation_id,
message_id=f"email_{message_id}",
role='assistant', # 达人消息用assistant角色
content=f"主题: {subject}\n\n{body}",
metadata={
'email_id': message_id,
'from': influencer_email,
'to': user_email,
'subject': subject,
'timestamp': message_data.get('timestamp'),
'webhook_auto': True
}
)
# 获取活跃目标
goal = AutoGmailConversationService.get_active_goals_for_conversation(user, conversation.conversation_id)
# 获取对话摘要
conversation_summary = get_conversation_summary(conversation.conversation_id)
if not conversation_summary:
conversation_summary = "无对话摘要"
# 获取最后一条消息
last_message = get_last_message(conversation.conversation_id)
if not last_message:
last_message = f"主题: {subject}\n\n{body}"
# 生成推荐回复
goal_description = goal.description if goal else "礼貌回应邮件并获取更多信息"
reply_content, error = generate_recommended_reply(
user=user,
goal_description=goal_description,
conversation_summary=conversation_summary,
last_message=last_message
)
if error:
logger.error(f"生成推荐回复失败: {error}")
return False
if not reply_content:
logger.error("生成的推荐回复内容为空")
return False
# 构建回复的主题
reply_subject = f"回复: {subject}" if not subject.startswith('回复:') else subject
# 发送回复邮件
success, reply_message_id = GmailService.send_email(
user=user,
user_email=user_email,
to_email=influencer_email,
subject=reply_subject,
body=reply_content
)
if not success:
logger.error(f"发送自动回复邮件失败: {reply_message_id}")
return False
# 记录自动回复到对话历史
ChatHistory.objects.create(
conversation_id=conversation.conversation_id,
message_id=f"email_reply_{reply_message_id}",
role='user', # 用户发出的消息用user角色
content=reply_content,
metadata={
'email_id': reply_message_id,
'from': user_email,
'to': influencer_email,
'subject': reply_subject,
'auto_reply': True,
'webhook_auto': True,
'timestamp': timezone.now().isoformat()
}
)
# 更新目标状态(如果有)
if goal:
goal.last_activity_time = timezone.now()
goal.status = 'in_progress'
goal.metadata = goal.metadata or {}
goal.metadata['last_reply_time'] = timezone.now().isoformat()
goal.metadata['last_reply_id'] = reply_message_id
goal.save()
# 检查目标是否达成
latest_messages = ChatHistory.objects.filter(
conversation_id=conversation.conversation_id
).order_by('-created_at')[:20]
conversation_history = []
for msg in reversed(list(latest_messages)):
conversation_history.append({
'role': msg.role,
'content': msg.content,
'created_at': msg.created_at.isoformat()
})
is_achieved, confidence, error = AutoGmailConversationService.check_goal_achieved(
goal_description=goal.description,
conversation_history=conversation_history
)
if is_achieved and confidence >= 0.7:
goal.status = 'completed'
goal.completion_time = timezone.now()
goal.metadata['completion_confidence'] = confidence
goal.save()
logger.info(f"目标 {goal.id} 已达成,置信度: {confidence}")
logger.info(f"成功回复webhook消息: {user_email} -> {influencer_email}")
return True
except Exception as e:
logger.error(f"处理webhook消息失败: {str(e)}")
import traceback
logger.error(traceback.format_exc())
return False