通过webhook实现ai自动回复需要输入邮箱和目标
This commit is contained in:
parent
0bcd8822dc
commit
22e8a672ed
@ -17,6 +17,9 @@ from rest_framework.permissions import IsAuthenticated
|
||||
from apps.gmail.models import GmailCredential, GmailConversation, AutoReplyConfig
|
||||
from apps.gmail.services.gmail_service import GmailService
|
||||
from apps.gmail.serializers import AutoReplyConfigSerializer
|
||||
from apps.gmail.services.goal_service import get_or_create_goal, get_conversation_summary
|
||||
from apps.chat.models import ChatHistory
|
||||
from apps.knowledge_base.models import KnowledgeBase
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -295,7 +298,7 @@ class AutoGmailConversationView(APIView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
创建自动对话,发送第一条打招呼消息
|
||||
创建自动对话,根据需要发送第一条打招呼消息
|
||||
|
||||
请求参数:
|
||||
- user_email: 用户的Gmail邮箱(已授权)
|
||||
@ -317,42 +320,140 @@ class AutoGmailConversationView(APIView):
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 获取或创建活跃对话
|
||||
# 先查找现有对话
|
||||
# 验证Gmail凭证
|
||||
credential = GmailCredential.objects.filter(user=request.user, email=user_email).first()
|
||||
if not credential:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': f"未找到{user_email}的Gmail授权",
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
logger.info(f"开始创建/更新Gmail自动对话: 用户={request.user.username}, 邮箱={user_email}, 达人={influencer_email}")
|
||||
|
||||
# 查找现有对话
|
||||
existing_conversation = GmailConversation.objects.filter(
|
||||
user=request.user,
|
||||
user_email=user_email,
|
||||
influencer_email=influencer_email
|
||||
).first()
|
||||
|
||||
if existing_conversation:
|
||||
# 激活现有对话
|
||||
existing_conversation.is_active = True
|
||||
existing_conversation.save()
|
||||
logger.info(f"找到并激活现有对话: {existing_conversation.conversation_id}")
|
||||
conversation_id = None
|
||||
is_new_conversation = False
|
||||
|
||||
# 调用服务创建自动对话
|
||||
success, result, goal = AutoGmailConversationService.create_auto_conversation(
|
||||
if existing_conversation:
|
||||
# 使用现有对话
|
||||
conversation = existing_conversation
|
||||
conversation_id = conversation.conversation_id
|
||||
# 激活对话
|
||||
conversation.is_active = True
|
||||
|
||||
conversation.save()
|
||||
logger.info(f"找到并激活现有对话: {conversation_id}, has_sent_greeting={conversation.has_sent_greeting}")
|
||||
else:
|
||||
# 创建新对话
|
||||
conversation_id = f"gmail_{request.user.id}_{str(uuid.uuid4())[:8]}"
|
||||
conversation = GmailConversation.objects.create(
|
||||
user=request.user,
|
||||
user_email=user_email,
|
||||
influencer_email=influencer_email,
|
||||
greeting_message="", # 使用空字符串,实际消息在服务中已固定
|
||||
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()
|
||||
}
|
||||
)
|
||||
is_new_conversation = True
|
||||
logger.info(f"创建新的自动对话: {conversation_id}")
|
||||
|
||||
# 使用goal_service创建或更新目标
|
||||
goal, is_new_goal = get_or_create_goal(
|
||||
user=request.user,
|
||||
conversation_id=conversation_id,
|
||||
goal_description=goal_description
|
||||
)
|
||||
|
||||
if not success:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': result,
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
logger.info(f"目标{'创建' if is_new_goal else '更新'}成功: {goal.id if goal else 'None'}")
|
||||
|
||||
# 确保对话是活跃的
|
||||
conversation = GmailConversation.objects.filter(conversation_id=result).first()
|
||||
if conversation and not conversation.is_active:
|
||||
conversation.is_active = True
|
||||
conversation.save()
|
||||
logger.info(f"已将对话 {conversation.conversation_id} 设置为活跃")
|
||||
# 检查是否需要发送打招呼消息
|
||||
if not conversation.has_sent_greeting:
|
||||
logger.info(f"准备发送打招呼消息: {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} 发送打招呼消息")
|
||||
|
||||
# 使用GmailService发送邮件
|
||||
success, message_id = GmailService.send_email(
|
||||
user=request.user,
|
||||
user_email=user_email,
|
||||
to_email=influencer_email,
|
||||
subject=subject,
|
||||
body=greeting_message
|
||||
)
|
||||
|
||||
if success:
|
||||
# 将打招呼消息保存到聊天历史 - 使用更完整的方式
|
||||
try:
|
||||
# 查找或创建默认知识库
|
||||
knowledge_base = KnowledgeBase.objects.filter(user_id=request.user.id, type='private').first()
|
||||
if knowledge_base:
|
||||
# 创建聊天消息
|
||||
ChatHistory.objects.create(
|
||||
user=request.user,
|
||||
knowledge_base=knowledge_base,
|
||||
conversation_id=conversation_id,
|
||||
message_id=f"greeting_{message_id}",
|
||||
title=conversation.title,
|
||||
role="user", # 用户发出的消息
|
||||
content=greeting_message,
|
||||
metadata={
|
||||
'gmail_message_id': message_id,
|
||||
'from': user_email,
|
||||
'to': influencer_email,
|
||||
'date': timezone.now().isoformat(),
|
||||
'subject': subject,
|
||||
'greeting': True,
|
||||
'source': 'gmail'
|
||||
}
|
||||
)
|
||||
logger.info(f"打招呼消息已保存到聊天历史: {message_id}")
|
||||
else:
|
||||
logger.warning("未找到默认知识库,打招呼消息未保存到聊天历史")
|
||||
except Exception as chat_error:
|
||||
logger.error(f"保存打招呼消息到聊天历史失败: {str(chat_error)}")
|
||||
# 这里我们继续执行,不影响主流程
|
||||
|
||||
# 更新对话的has_sent_greeting字段
|
||||
conversation.has_sent_greeting = True
|
||||
conversation.save(update_fields=['has_sent_greeting', 'updated_at'])
|
||||
logger.info(f"对话 {conversation_id} 已发送打招呼消息,并更新了has_sent_greeting=True")
|
||||
else:
|
||||
logger.error(f"发送打招呼消息失败: {message_id}")
|
||||
return Response({
|
||||
'code': 500,
|
||||
'message': f"发送打招呼消息失败: {message_id}",
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
else:
|
||||
logger.info(f"对话 {conversation_id} 已经发送过打招呼消息,不再重复发送")
|
||||
|
||||
# 设置Gmail推送通知
|
||||
notification_result, notification_error = GmailService.setup_gmail_push_notification(
|
||||
@ -361,29 +462,34 @@ class AutoGmailConversationView(APIView):
|
||||
)
|
||||
|
||||
if not notification_result and notification_error:
|
||||
logger.warning(f"设置Gmail推送通知失败: {notification_error},但对话创建成功")
|
||||
logger.warning(f"设置Gmail推送通知失败: {notification_error},但对话创建/更新成功")
|
||||
|
||||
# 生成对话摘要(如果有足够的消息)
|
||||
summary = get_conversation_summary(conversation_id)
|
||||
|
||||
# 返回结果
|
||||
return Response({
|
||||
'code': 201,
|
||||
'message': '自动对话创建成功,已发送打招呼消息',
|
||||
'code': 201 if is_new_conversation else 200,
|
||||
'message': '自动对话创建成功' if is_new_conversation else '自动对话更新成功',
|
||||
'data': {
|
||||
'conversation_id': result,
|
||||
'conversation_id': conversation_id,
|
||||
'goal_id': str(goal.id) if goal else None,
|
||||
'user_email': user_email,
|
||||
'influencer_email': influencer_email,
|
||||
'is_active': True,
|
||||
'has_sent_greeting': conversation.has_sent_greeting,
|
||||
'goal_description': goal_description,
|
||||
'push_notification': notification_result
|
||||
'push_notification': notification_result,
|
||||
'summary': summary
|
||||
}
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
}, status=status.HTTP_201_CREATED if is_new_conversation else status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建自动对话失败: {str(e)}")
|
||||
logger.error(f"创建/更新自动对话失败: {str(e)}")
|
||||
error_details = traceback.format_exc()
|
||||
return Response({
|
||||
'code': 500,
|
||||
'message': f'创建自动对话失败: {str(e)}',
|
||||
'message': f'创建/更新自动对话失败: {str(e)}',
|
||||
'data': {
|
||||
'details': error_details[:500]
|
||||
}
|
||||
|
@ -40,8 +40,12 @@ class GmailAuthInitiateView(APIView):
|
||||
"""
|
||||
处理 POST 请求,启动 Gmail OAuth2 认证并返回授权 URL。
|
||||
|
||||
支持两种方式提供客户端密钥:
|
||||
1. 在请求体中提供client_secret_json字段
|
||||
2. 上传名为client_secret_file的JSON文件
|
||||
|
||||
Args:
|
||||
request: Django REST Framework 请求对象,包含客户端密钥 JSON 数据。
|
||||
request: Django REST Framework 请求对象,包含客户端密钥 JSON 数据或文件。
|
||||
|
||||
Returns:
|
||||
Response: 包含授权 URL 的 JSON 响应(成功时),或错误信息(失败时)。
|
||||
@ -52,11 +56,62 @@ class GmailAuthInitiateView(APIView):
|
||||
500: 服务器内部错误(如认证服务失败)。
|
||||
"""
|
||||
logger.debug(f"Received auth initiate request: {request.data}")
|
||||
|
||||
# 检查是否是文件上传方式
|
||||
client_secret_json = None
|
||||
if 'client_secret_file' in request.FILES:
|
||||
try:
|
||||
# 读取上传的JSON文件内容
|
||||
client_secret_file = request.FILES['client_secret_file']
|
||||
client_secret_json = json.loads(client_secret_file.read().decode('utf-8'))
|
||||
logger.info(f"从上传文件读取到客户端密钥JSON")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"解析客户端密钥JSON文件失败: {str(e)}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': f'无效的JSON文件格式: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
logger.error(f"处理上传文件失败: {str(e)}")
|
||||
return Response({
|
||||
'code': 500,
|
||||
'message': f'处理上传文件失败: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
# 如果不是文件上传,则尝试从请求数据中提取JSON
|
||||
if not client_secret_json:
|
||||
serializer = GmailCredentialSerializer(data=request.data, context={'request': request})
|
||||
if serializer.is_valid():
|
||||
try:
|
||||
# 从请求数据中提取客户端密钥 JSON
|
||||
client_secret_json = serializer.validated_data['client_secret_json']
|
||||
except Exception as e:
|
||||
logger.error(f"未提供客户端密钥JSON: {str(e)}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请提供client_secret_json字段或上传client_secret_file文件',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
# 记录无效请求数据并返回错误响应
|
||||
logger.warning(f"Invalid request data: {serializer.errors}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请求数据无效,请提供client_secret_json字段或上传client_secret_file文件',
|
||||
'data': serializer.errors
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 如果此时仍然没有client_secret_json,返回错误
|
||||
if not client_secret_json:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请提供client_secret_json字段或上传client_secret_file文件',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
# 调用 GmailService 生成授权 URL
|
||||
auth_url = GmailService.initiate_authentication(request.user, client_secret_json)
|
||||
logger.info(f"Generated auth URL for user {request.user.id}")
|
||||
@ -73,13 +128,6 @@ class GmailAuthInitiateView(APIView):
|
||||
'message': f'认证初始化错误:{str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
# 记录无效请求数据并返回错误响应
|
||||
logger.warning(f"Invalid request data: {serializer.errors}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请求数据无效',
|
||||
'data': serializer.errors
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class GmailAuthCompleteView(APIView):
|
||||
@ -92,8 +140,12 @@ class GmailAuthCompleteView(APIView):
|
||||
"""
|
||||
处理 POST 请求,使用授权代码完成 Gmail OAuth2 认证并保存凭证。
|
||||
|
||||
支持两种方式提供客户端密钥:
|
||||
1. 在请求体中提供client_secret_json字段
|
||||
2. 上传名为client_secret_file的JSON文件
|
||||
|
||||
Args:
|
||||
request: Django REST Framework 请求对象,包含授权代码和客户端密钥 JSON。
|
||||
request: Django REST Framework 请求对象,包含授权代码和客户端密钥 JSON 或文件。
|
||||
|
||||
Returns:
|
||||
Response: 包含已保存凭证数据的 JSON 响应(成功时),或错误信息(失败时)。
|
||||
@ -104,21 +156,80 @@ class GmailAuthCompleteView(APIView):
|
||||
500: 服务器内部错误(如认证失败)。
|
||||
"""
|
||||
logger.debug(f"Received auth complete request: {request.data}")
|
||||
|
||||
# 检查是否是文件上传方式
|
||||
client_secret_json = None
|
||||
if 'client_secret_file' in request.FILES:
|
||||
try:
|
||||
# 读取上传的JSON文件内容
|
||||
client_secret_file = request.FILES['client_secret_file']
|
||||
client_secret_json = json.loads(client_secret_file.read().decode('utf-8'))
|
||||
logger.info(f"从上传文件读取到客户端密钥JSON")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"解析客户端密钥JSON文件失败: {str(e)}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': f'无效的JSON文件格式: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
logger.error(f"处理上传文件失败: {str(e)}")
|
||||
return Response({
|
||||
'code': 500,
|
||||
'message': f'处理上传文件失败: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
# 获取授权码,无论是哪种方式都需要
|
||||
auth_code = request.data.get('auth_code')
|
||||
if not auth_code:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '必须提供授权码',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 如果不是文件上传,则尝试从请求数据中提取JSON
|
||||
if not client_secret_json:
|
||||
serializer = GmailCredentialSerializer(data=request.data, context={'request': request})
|
||||
if serializer.is_valid():
|
||||
try:
|
||||
# 提取授权代码和客户端密钥 JSON
|
||||
auth_code = serializer.validated_data['auth_code']
|
||||
# 从请求数据中提取客户端密钥 JSON
|
||||
client_secret_json = serializer.validated_data['client_secret_json']
|
||||
except Exception as e:
|
||||
logger.error(f"未提供客户端密钥JSON: {str(e)}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请提供client_secret_json字段或上传client_secret_file文件',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
# 记录无效请求数据并返回错误响应
|
||||
logger.warning(f"Invalid request data: {serializer.errors}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请求数据无效,请提供client_secret_json字段或上传client_secret_file文件',
|
||||
'data': serializer.errors
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 如果此时仍然没有client_secret_json,返回错误
|
||||
if not client_secret_json:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请提供client_secret_json字段或上传client_secret_file文件',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
# 完成认证并保存凭证
|
||||
credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json)
|
||||
# 序列化凭证数据以返回
|
||||
serializer = GmailCredentialSerializer(credential, context={'request': request})
|
||||
return_serializer = GmailCredentialSerializer(credential, context={'request': request})
|
||||
logger.info(f"Authentication completed for user {request.user.id}, email: {credential.email}")
|
||||
return Response({
|
||||
'code': 201,
|
||||
'message': '认证完成并成功保存凭证',
|
||||
'data': serializer.data
|
||||
'data': return_serializer.data
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
except Exception as e:
|
||||
# 记录错误并返回服务器错误响应
|
||||
@ -128,13 +239,6 @@ class GmailAuthCompleteView(APIView):
|
||||
'message': f'完成认证时发生错误:{str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
# 记录无效请求数据并返回错误响应
|
||||
logger.warning(f"Invalid request data: {serializer.errors}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '请求数据无效',
|
||||
'data': serializer.errors
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class GmailCredentialViewSet(viewsets.ModelViewSet):
|
||||
@ -212,36 +316,21 @@ class GmailCredentialViewSet(viewsets.ModelViewSet):
|
||||
client_secret_json = serializer.validated_data.get('client_secret_json')
|
||||
|
||||
if not auth_code: # 初始化OAuth
|
||||
auth_url = GmailService.start_oauth(client_secret_json)
|
||||
auth_url = GmailService.initiate_authentication(request.user, client_secret_json)
|
||||
return Response({
|
||||
'code': 200,
|
||||
'message': '授权URL生成成功',
|
||||
'data': {'auth_url': auth_url}
|
||||
})
|
||||
else: # 完成OAuth
|
||||
email, credentials = GmailService.complete_oauth(client_secret_json, auth_code)
|
||||
if not email:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': f'授权失败: {credentials}',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 保存凭证
|
||||
serializer.save(
|
||||
user=request.user,
|
||||
email=email,
|
||||
credentials=credentials,
|
||||
is_valid=True
|
||||
)
|
||||
|
||||
credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json)
|
||||
return Response({
|
||||
'code': 201,
|
||||
'message': '授权成功',
|
||||
'data': {
|
||||
'id': serializer.instance.id,
|
||||
'email': email,
|
||||
'is_valid': True
|
||||
'id': credential.id,
|
||||
'email': credential.email,
|
||||
'is_valid': credential.is_valid
|
||||
}
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
||||
@ -365,6 +454,68 @@ class GmailCredentialViewSet(viewsets.ModelViewSet):
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
@action(detail=False, methods=['post'])
|
||||
def upload_client_secret(self, request):
|
||||
"""
|
||||
通过上传客户端密钥JSON文件来创建Gmail凭证
|
||||
|
||||
请求参数:
|
||||
- client_secret_file: Google Cloud项目的客户端密钥JSON文件
|
||||
- auth_code: [可选] 授权码,用于完成OAuth流程
|
||||
"""
|
||||
try:
|
||||
# 检查是否上传了文件
|
||||
if 'client_secret_file' not in request.FILES:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '未找到客户端密钥文件,请使用client_secret_file字段上传',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 读取上传的JSON文件内容
|
||||
client_secret_file = request.FILES['client_secret_file']
|
||||
client_secret_json = json.loads(client_secret_file.read().decode('utf-8'))
|
||||
|
||||
# 获取可选的授权码
|
||||
auth_code = request.data.get('auth_code', '')
|
||||
|
||||
if not auth_code: # 初始化OAuth
|
||||
# 调用GmailService生成授权URL
|
||||
auth_url = GmailService.initiate_authentication(request.user, client_secret_json)
|
||||
return Response({
|
||||
'code': 200,
|
||||
'message': '授权URL生成成功',
|
||||
'data': {'auth_url': auth_url}
|
||||
})
|
||||
else: # 完成OAuth
|
||||
# 完成认证并保存凭证
|
||||
credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json)
|
||||
return Response({
|
||||
'code': 201,
|
||||
'message': '授权成功',
|
||||
'data': {
|
||||
'id': credential.id,
|
||||
'email': credential.email,
|
||||
'is_valid': credential.is_valid
|
||||
}
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"解析客户端密钥JSON文件失败: {str(e)}")
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': f'无效的JSON文件格式: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
logger.error(f"上传客户端密钥文件失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return Response({
|
||||
'code': 500,
|
||||
'message': f'处理客户端密钥文件失败: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
class GmailConversationView(APIView):
|
||||
"""
|
||||
@ -846,12 +997,120 @@ class GmailWebhookView(APIView):
|
||||
# 获取并保存最新邮件
|
||||
print("[Gmail Webhook] 开始获取最新邮件...")
|
||||
|
||||
# 处理新邮件,使用静态方法而不是实例化类
|
||||
try:
|
||||
# 使用GmailService的静态方法处理新邮件
|
||||
processed_emails = GmailService.process_new_emails(user, credential, history_id)
|
||||
print(f"[Gmail Webhook] 新邮件处理完成,共 {len(processed_emails)} 封")
|
||||
|
||||
# 处理活跃对话的自动回复
|
||||
if processed_emails:
|
||||
print(f"[Gmail Webhook] 开始处理自动回复...")
|
||||
|
||||
# 获取刚处理的邮件的对应的对话信息
|
||||
from apps.gmail.models import GmailConversation, UserGoal
|
||||
from apps.chat.models import ChatHistory
|
||||
|
||||
# 遍历每个处理过的邮件ID
|
||||
for email_id in processed_emails:
|
||||
try:
|
||||
# 通过邮件ID查找对应的聊天记录
|
||||
chat_msg = ChatHistory.objects.filter(
|
||||
metadata__gmail_message_id=email_id
|
||||
).order_by('-created_at').first()
|
||||
|
||||
if not chat_msg:
|
||||
print(f"[Gmail Webhook] 邮件 {email_id} 未找到对应的聊天记录,跳过自动回复")
|
||||
continue
|
||||
|
||||
conversation_id = chat_msg.conversation_id
|
||||
|
||||
# 检查对话是否是活跃的
|
||||
conversation = GmailConversation.objects.filter(
|
||||
conversation_id=conversation_id,
|
||||
is_active=True
|
||||
).first()
|
||||
|
||||
if not conversation:
|
||||
print(f"[Gmail Webhook] 对话 {conversation_id} 不是活跃的,跳过自动回复")
|
||||
continue
|
||||
|
||||
print(f"[Gmail Webhook] 发现活跃对话 {conversation_id},准备自动回复")
|
||||
|
||||
# 检查邮件角色,只有达人发送的邮件才自动回复
|
||||
if chat_msg.role != 'assistant':
|
||||
print(f"[Gmail Webhook] 邮件 {email_id} 不是达人发送的,跳过自动回复")
|
||||
continue
|
||||
|
||||
# 查找对话的目标
|
||||
goal = UserGoal.objects.filter(
|
||||
conversation=conversation,
|
||||
is_active=True
|
||||
).first()
|
||||
|
||||
if not goal:
|
||||
print(f"[Gmail Webhook] 对话 {conversation_id} 没有活跃目标,跳过自动回复")
|
||||
continue
|
||||
|
||||
# 获取对话摘要
|
||||
from apps.gmail.services.goal_service import get_conversation_summary, get_last_message, generate_recommended_reply
|
||||
|
||||
conversation_summary = get_conversation_summary(conversation_id)
|
||||
if not conversation_summary:
|
||||
conversation_summary = "无对话摘要"
|
||||
|
||||
# 获取最后一条达人消息
|
||||
last_message = get_last_message(conversation_id)
|
||||
if not last_message:
|
||||
print(f"[Gmail Webhook] 对话 {conversation_id} 没有达人消息,跳过自动回复")
|
||||
continue
|
||||
|
||||
# 生成推荐回复
|
||||
reply_content, error = generate_recommended_reply(
|
||||
user=user,
|
||||
goal_description=goal.description,
|
||||
conversation_summary=conversation_summary,
|
||||
last_message=last_message
|
||||
)
|
||||
|
||||
if error:
|
||||
print(f"[Gmail Webhook] 生成推荐回复失败: {error}")
|
||||
continue
|
||||
|
||||
if not reply_content:
|
||||
print(f"[Gmail Webhook] 生成的推荐回复为空")
|
||||
continue
|
||||
|
||||
# 构建回复的主题
|
||||
subject = chat_msg.metadata.get('subject', '')
|
||||
reply_subject = f"回复: {subject}" if not subject.startswith('回复:') else subject
|
||||
|
||||
# 准备发送自动回复
|
||||
print(f"[Gmail Webhook] 准备发送自动回复: 从{conversation.user_email}到{conversation.influencer_email}")
|
||||
|
||||
success, reply_message_id = GmailService.send_email(
|
||||
user=user,
|
||||
user_email=conversation.user_email,
|
||||
to_email=conversation.influencer_email,
|
||||
subject=reply_subject,
|
||||
body=reply_content
|
||||
)
|
||||
|
||||
if success:
|
||||
print(f"[Gmail Webhook] 已成功发送自动回复: {reply_message_id}")
|
||||
|
||||
# 更新目标状态
|
||||
goal.last_activity_time = timezone.now()
|
||||
if goal.status == 'pending':
|
||||
goal.status = 'in_progress'
|
||||
goal.save(update_fields=['last_activity_time', 'status', 'updated_at'])
|
||||
else:
|
||||
print(f"[Gmail Webhook] 发送自动回复失败: {reply_message_id}")
|
||||
|
||||
except Exception as reply_error:
|
||||
print(f"[Gmail Webhook] 处理自动回复过程中出错: {str(reply_error)}")
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
# 更新凭证的历史ID(移到处理完成后再更新)
|
||||
if history_id:
|
||||
credential.last_history_id = history_id
|
||||
@ -861,6 +1120,8 @@ class GmailWebhookView(APIView):
|
||||
except Exception as e:
|
||||
print(f"[Gmail Webhook] 获取最新邮件失败: {str(e)}")
|
||||
logger.error(f"获取最新邮件失败: {str(e)}")
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
else:
|
||||
print(f"[Gmail Webhook] 警告: 未找到对应的Gmail凭证: {email_address}")
|
||||
logger.warning(f"收到推送通知,但未找到对应的Gmail凭证: {email_address}")
|
||||
|
Loading…
Reference in New Issue
Block a user