|
import os |
|
import logging |
|
import aiohttp |
|
import pytz |
|
import base64 |
|
import asyncio |
|
|
|
from fastapi import APIRouter, HTTPException, Header, Body |
|
from pydantic import BaseModel |
|
from dateutil import parser |
|
from datetime import datetime |
|
from typing import Optional |
|
|
|
logger = logging.getLogger("uvicorn.error") |
|
|
|
router = APIRouter() |
|
|
|
|
|
|
|
SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co" |
|
SUPABASE_KEY = os.getenv("SUPA_KEY") |
|
SUPABASE_ROLE_KEY = os.getenv("SUPA_SERVICE_KEY") |
|
|
|
if not SUPABASE_KEY or not SUPABASE_ROLE_KEY: |
|
raise ValueError("❌ SUPA_KEY or SUPA_SERVICE_KEY not set in environment!") |
|
|
|
SUPABASE_HEADERS = { |
|
"apikey": SUPABASE_KEY, |
|
"Authorization": f"Bearer {SUPABASE_KEY}", |
|
"Content-Type": "application/json" |
|
} |
|
|
|
SUPABASE_ROLE_HEADERS = { |
|
"apikey": SUPABASE_ROLE_KEY, |
|
"Authorization": f"Bearer {SUPABASE_ROLE_KEY}", |
|
"Content-Type": "application/json", |
|
"Prefer": "return=representation" |
|
} |
|
|
|
|
|
|
|
async def verify_user_token(user_token: str) -> str: |
|
headers = { |
|
"Authorization": f"Bearer {user_token}", |
|
"apikey": SUPABASE_KEY, |
|
"Content-Type": "application/json" |
|
} |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers) as response: |
|
if response.status != 200: |
|
raise HTTPException(status_code=401, detail="Token inválido ou expirado") |
|
|
|
user_data = await response.json() |
|
user_id = user_data.get("id") |
|
if not user_id: |
|
raise HTTPException(status_code=400, detail="ID do usuário não encontrado") |
|
|
|
return user_id |
|
|
|
|
|
|
|
class CreateTicketRequest(BaseModel): |
|
message: str |
|
subject: str |
|
priority: str |
|
|
|
def format_datetime(dt_str): |
|
dt = parser.isoparse(dt_str) |
|
return dt.strftime("%m/%d/%Y, %I:%M %p") |
|
|
|
@router.get("/ticket/user") |
|
async def get_user_tickets( |
|
user_token: str = Header(None, alias="User-key") |
|
): |
|
user_id = await verify_user_token(user_token) |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?user_id=eq.{user_id}&order=created_at.desc&limit=50", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as ticket_resp: |
|
if ticket_resp.status != 200: |
|
error_detail = await ticket_resp.text() |
|
raise HTTPException(status_code=500, detail=f"Error fetching tickets: {error_detail}") |
|
|
|
tickets = await ticket_resp.json() |
|
|
|
ticket_results = [] |
|
|
|
for ticket in tickets: |
|
ticket_id = ticket["id"] |
|
ticket_created = parser.isoparse(ticket["created_at"]) |
|
formatted_ticket_date = ticket_created.strftime("%m/%d/%Y") |
|
|
|
|
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/messages_tickets?ticket_id=eq.{ticket_id}&order=created_at.asc&limit=1", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as first_msg_resp: |
|
first_message = None |
|
if first_msg_resp.status == 200: |
|
messages = await first_msg_resp.json() |
|
first_message = messages[0] if messages else None |
|
|
|
|
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/messages_tickets?ticket_id=eq.{ticket_id}&order=created_at.asc", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as all_msg_resp: |
|
all_messages = await all_msg_resp.json() if all_msg_resp.status == 200 else [] |
|
|
|
history = [f"Ticket created on {ticket_created.strftime('%m/%d/%Y, %I:%M %p')}"] |
|
|
|
|
|
assigned_support_ids = [] |
|
last_support_id = None |
|
for msg in all_messages: |
|
msg_user = msg["user"] |
|
if msg_user != user_id and msg_user != last_support_id: |
|
last_support_id = msg_user |
|
assigned_support_ids.append((msg_user, msg["created_at"])) |
|
|
|
for support_id, assigned_time in assigned_support_ids: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{support_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as user_resp: |
|
support_name = "Support" |
|
if user_resp.status == 200: |
|
user_data = await user_resp.json() |
|
if user_data: |
|
support_name = user_data[0].get("name", "Support") |
|
history.append(f"Assigned to {support_name} on {format_datetime(assigned_time)}") |
|
|
|
|
|
status = "Open" |
|
if ticket.get("finished"): |
|
status = "Closed" |
|
if ticket.get("finished_date"): |
|
closed_dt = parser.isoparse(ticket["finished_date"]) |
|
history.append(f"Ticket closed on {closed_dt.strftime('%m/%d/%Y, %I:%M %p')}") |
|
elif all_messages: |
|
last_msg = all_messages[-1] |
|
if last_msg["user"] != user_id: |
|
status = "Answered" |
|
else: |
|
status = "Open" |
|
|
|
if len(all_messages) > 1: |
|
|
|
last_msg_time = format_datetime(last_msg["created_at"]) |
|
history.append(f"Last updated on {last_msg_time}") |
|
|
|
|
|
ticket_data = dict(ticket) |
|
ticket_data["formatted_date"] = formatted_ticket_date |
|
ticket_data["status"] = status |
|
|
|
if ticket.get("support_id"): |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{ticket['support_id']}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as support_resp: |
|
if support_resp.status == 200: |
|
support_data = await support_resp.json() |
|
if support_data: |
|
support_info = support_data[0] |
|
ticket_data["support_name"] = support_info.get("name") |
|
ticket_data["support_email"] = support_info.get("email") |
|
|
|
ticket_results.append({ |
|
"ticket": ticket_data, |
|
"first_message": first_message, |
|
"history": history |
|
}) |
|
|
|
return ticket_results |
|
|
|
@router.get("/ticket/detail") |
|
async def get_ticket_details(ticket_id: int): |
|
async with aiohttp.ClientSession() as session: |
|
|
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{ticket_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as ticket_resp: |
|
if ticket_resp.status != 200: |
|
raise HTTPException(status_code=404, detail="Ticket não encontrado") |
|
|
|
ticket_data = await ticket_resp.json() |
|
if not ticket_data: |
|
raise HTTPException(status_code=404, detail="Ticket inexistente") |
|
|
|
ticket = ticket_data[0] |
|
|
|
|
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/messages_tickets?ticket_id=eq.{ticket_id}&order=created_at.desc&limit=50", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as msg_resp: |
|
if msg_resp.status != 200: |
|
raise HTTPException(status_code=500, detail="Erro ao buscar mensagens") |
|
|
|
messages_raw = await msg_resp.json() |
|
|
|
|
|
user_cache = {} |
|
|
|
for msg in messages_raw: |
|
user_id = msg["user"] |
|
if user_id not in user_cache: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as user_resp: |
|
if user_resp.status == 200: |
|
user_data = await user_resp.json() |
|
if user_data: |
|
u = user_data[0] |
|
user_cache[user_id] = { |
|
"id": u["id"], |
|
"name": u.get("name", "Desconhecido"), |
|
"avatar": u.get("avatar", None), |
|
"type": "support" if u.get("role") == "admin" else "customer" |
|
} |
|
else: |
|
user_cache[user_id] = { |
|
"id": user_id, |
|
"name": "Desconhecido", |
|
"avatar": None, |
|
"type": "unknown" |
|
} |
|
|
|
|
|
messages = [] |
|
for msg in messages_raw: |
|
uid = msg["user"] |
|
user_info = user_cache.get(uid, { |
|
"id": uid, |
|
"name": "Desconhecido", |
|
"avatar": None, |
|
}) |
|
|
|
|
|
if uid == ticket["user_id"]: |
|
user_info["type"] = "customer" |
|
else: |
|
user_info["type"] = "support" |
|
|
|
messages.append({ |
|
**msg, |
|
"user": user_info |
|
}) |
|
|
|
return { |
|
"ticket": ticket, |
|
"messages": messages |
|
} |
|
|
|
class RespondTicketRequest(BaseModel): |
|
ticket_id: int |
|
content: str |
|
|
|
@router.post("/ticket/respond") |
|
async def respond_ticket( |
|
body: RespondTicketRequest, |
|
user_token: str = Header(None, alias="User-key") |
|
): |
|
user_id = await verify_user_token(user_token) |
|
created_at = datetime.utcnow().isoformat() |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as ticket_resp: |
|
if ticket_resp.status != 200: |
|
raise HTTPException(status_code=404, detail="Ticket não encontrado") |
|
|
|
ticket_data = await ticket_resp.json() |
|
if not ticket_data: |
|
raise HTTPException(status_code=404, detail="Ticket inexistente") |
|
|
|
ticket = ticket_data[0] |
|
|
|
support_id = ticket.get("support_id") |
|
ticket_creator_id = ticket.get("user_id") |
|
|
|
|
|
if support_id is None: |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as user_resp: |
|
if user_resp.status != 200: |
|
raise HTTPException(status_code=403, detail="Usuário inválido") |
|
|
|
user_data = await user_resp.json() |
|
if not user_data or not user_data[0].get("is_admin", False): |
|
raise HTTPException(status_code=403, detail="Apenas administradores podem assumir tickets") |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.patch( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}", |
|
headers={**SUPABASE_ROLE_HEADERS, "Prefer": "return=representation"}, |
|
json={"support_id": user_id} |
|
) as patch_resp: |
|
if patch_resp.status != 200: |
|
raise HTTPException(status_code=500, detail="Erro ao atribuir o suporte ao ticket") |
|
else: |
|
|
|
if support_id != user_id and user_id != ticket_creator_id: |
|
raise HTTPException(status_code=403, detail="Ticket já está atribuído a outro suporte") |
|
|
|
|
|
await asyncio.sleep(1) |
|
|
|
|
|
if ticket.get("finished", False) is True: |
|
async with aiohttp.ClientSession() as session: |
|
async with session.patch( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}", |
|
headers={**SUPABASE_ROLE_HEADERS, "Prefer": "return=representation"}, |
|
json={"finished": False, "finished_date": None} |
|
) as reopen_resp: |
|
if reopen_resp.status != 200: |
|
raise HTTPException(status_code=500, detail="Erro ao reabrir o ticket") |
|
|
|
|
|
message_payload = { |
|
"user": user_id, |
|
"content": body.content, |
|
"created_at": created_at, |
|
"ticket_id": body.ticket_id |
|
} |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.post( |
|
f"{SUPABASE_URL}/rest/v1/messages_tickets", |
|
headers=SUPABASE_ROLE_HEADERS, |
|
json=message_payload |
|
) as message_resp: |
|
if message_resp.status != 201: |
|
error_detail = await message_resp.text() |
|
raise HTTPException(status_code=500, detail=f"Erro ao salvar a mensagem: {error_detail}") |
|
|
|
message_data = await message_resp.json() |
|
|
|
return { |
|
"status": "response sent successfully", |
|
"ticket_id": body.ticket_id, |
|
"message_id": message_data[0]["id"], |
|
"message_content": body.content |
|
} |
|
|
|
@router.post("/ticket/create") |
|
async def create_ticket( |
|
body: CreateTicketRequest, |
|
user_token: str = Header(None, alias="User-key") |
|
): |
|
user_id = await verify_user_token(user_token) |
|
created_at = datetime.utcnow().isoformat() |
|
|
|
ticket_payload = { |
|
"user_id": user_id, |
|
"support_id": None, |
|
"created_at": created_at, |
|
"subject": body.subject, |
|
"priority": body.priority |
|
} |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.post( |
|
f"{SUPABASE_URL}/rest/v1/Tickets", |
|
headers=SUPABASE_ROLE_HEADERS, |
|
json=ticket_payload |
|
) as ticket_resp: |
|
|
|
if ticket_resp.status != 201: |
|
error_detail = await ticket_resp.text() |
|
raise HTTPException(status_code=500, detail=f"Erro ao criar ticket: {error_detail}") |
|
|
|
ticket_data = await ticket_resp.json() |
|
ticket_id = ticket_data[0]["id"] |
|
|
|
message_payload = { |
|
"user": user_id, |
|
"content": body.message, |
|
"created_at": created_at, |
|
"ticket_id": ticket_id |
|
} |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.post( |
|
f"{SUPABASE_URL}/rest/v1/messages_tickets", |
|
headers=SUPABASE_ROLE_HEADERS, |
|
json=message_payload |
|
) as message_resp: |
|
|
|
if message_resp.status != 201: |
|
error_detail = await message_resp.text() |
|
raise HTTPException(status_code=500, detail=f"Erro ao criar mensagem: {error_detail}") |
|
|
|
return {"ticket_id": ticket_id} |
|
|
|
class CloseTicketRequest(BaseModel): |
|
ticket_id: int |
|
|
|
@router.post("/ticket/close") |
|
async def close_ticket( |
|
body: CloseTicketRequest, |
|
user_token: str = Header(None, alias="User-key") |
|
): |
|
user_id = await verify_user_token(user_token) |
|
logger.info(f"User ID verified: {user_id}") |
|
|
|
finished_date = datetime.utcnow().isoformat() |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as get_resp: |
|
if get_resp.status != 200: |
|
error_detail = await get_resp.text() |
|
logger.error(f"Error fetching ticket: {error_detail}") |
|
raise HTTPException(status_code=500, detail=f"Error fetching ticket: {error_detail}") |
|
|
|
ticket_data = await get_resp.json() |
|
if not ticket_data: |
|
raise HTTPException(status_code=404, detail="Ticket not found") |
|
|
|
ticket = ticket_data[0] |
|
if ticket.get("finished", False): |
|
logger.info(f"Ticket {body.ticket_id} is already closed.") |
|
return {"message": "Ticket is already closed", "ticket_id": body.ticket_id} |
|
|
|
|
|
update_payload = { |
|
"finished": True, |
|
"finished_date": finished_date |
|
} |
|
logger.info(f"Updating ticket with payload: {update_payload}") |
|
|
|
try: |
|
async with aiohttp.ClientSession() as session: |
|
async with session.patch( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}", |
|
headers={ |
|
**SUPABASE_ROLE_HEADERS, |
|
"Content-Type": "application/json" |
|
}, |
|
json=update_payload |
|
) as update_resp: |
|
logger.info(f"Response status: {update_resp.status}") |
|
response_data = await update_resp.json() |
|
logger.info(f"Response data: {response_data}") |
|
|
|
if update_resp.status == 200: |
|
|
|
updated_ticket = response_data[0] |
|
if updated_ticket["finished"] and updated_ticket["finished_date"]: |
|
logger.info(f"Ticket {body.ticket_id} closed successfully.") |
|
return {"message": "Ticket closed successfully", "ticket_id": body.ticket_id} |
|
else: |
|
logger.error(f"Ticket update failed: {updated_ticket}") |
|
raise HTTPException(status_code=500, detail="Ticket update failed.") |
|
else: |
|
logger.error(f"Error updating ticket: {response_data}") |
|
raise HTTPException(status_code=500, detail=f"Error updating ticket: {response_data}") |
|
except aiohttp.ClientError as e: |
|
logger.error(f"AIOHTTP client error: {str(e)}") |
|
raise HTTPException(status_code=500, detail=f"Client error: {str(e)}") |
|
except Exception as e: |
|
logger.error(f"Unexpected error: {str(e)}") |
|
raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}") |
|
|
|
|
|
GMAIL_CLIENT_ID = "784687789817-3genmmvps11ip3a6fkbkkd8dm3bstgdc.apps.googleusercontent.com" |
|
GMAIL_CLIENT_SECRET = "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" |
|
GMAIL_REFRESH_TOKEN = "1//04ZOO_chVwlYiCgYIARAAGAQSNwF-L9IrhQO1ij79thk-DTjiMudl_XQshuU5CDTDYtt8rrOTMbz_rL8ECGjNfEN9da6W-mnjhZA" |
|
|
|
class TicketResponseRequest(BaseModel): |
|
ticket_id: int |
|
content: str |
|
|
|
|
|
async def get_gmail_access_token() -> str: |
|
url = "https://oauth2.googleapis.com/token" |
|
data = { |
|
"client_id": GMAIL_CLIENT_ID, |
|
"client_secret": GMAIL_CLIENT_SECRET, |
|
"refresh_token": GMAIL_REFRESH_TOKEN, |
|
"grant_type": "refresh_token" |
|
} |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.post(url, data=data) as response: |
|
if response.status != 200: |
|
raise HTTPException(status_code=500, detail="Erro ao obter access_token do Gmail") |
|
token_data = await response.json() |
|
return token_data["access_token"] |
|
|
|
|
|
def encode_message(raw_message: str) -> str: |
|
return base64.urlsafe_b64encode(raw_message.encode("utf-8")).decode("utf-8").replace("=", "") |
|
|
|
|
|
@router.post("/ticket/support/respond") |
|
async def respond_ticket( |
|
payload: TicketResponseRequest, |
|
user_token: str = Header(None, alias="User-key") |
|
): |
|
|
|
support_id = await verify_user_token(user_token) |
|
created_at = datetime.utcnow().isoformat() |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{support_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as support_user_resp: |
|
if support_user_resp.status != 200: |
|
raise HTTPException(status_code=404, detail="Support agent not found") |
|
|
|
support_user_data = await support_user_resp.json() |
|
if not support_user_data: |
|
raise HTTPException(status_code=404, detail="Support agent does not exist") |
|
|
|
support_name = support_user_data[0].get("name", "Support Team").strip() |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{payload.ticket_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as ticket_resp: |
|
if ticket_resp.status != 200: |
|
raise HTTPException(status_code=404, detail="Ticket not found") |
|
|
|
ticket_data = await ticket_resp.json() |
|
if not ticket_data: |
|
raise HTTPException(status_code=404, detail="Ticket does not exist") |
|
|
|
ticket = ticket_data[0] |
|
user_id = ticket["user_id"] |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}", |
|
headers=SUPABASE_ROLE_HEADERS |
|
) as user_resp: |
|
if user_resp.status != 200: |
|
raise HTTPException(status_code=404, detail="User not found") |
|
|
|
user_data = await user_resp.json() |
|
if not user_data: |
|
raise HTTPException(status_code=404, detail="User does not exist") |
|
|
|
user_email = user_data[0]["email"] |
|
user_name = user_data[0].get("name", "user").strip() |
|
first_name = user_name.split(" ")[0] if user_name else "user" |
|
|
|
|
|
access_token = await get_gmail_access_token() |
|
subject = f"Customer Support - Case {payload.ticket_id}" |
|
|
|
email_html = f""" |
|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
</head> |
|
<body style="margin:0;padding:0;background-color:#f9f9f9;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"> |
|
<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f9f9f9"> |
|
<tr> |
|
<td align="center" style="padding:40px 10px;"> |
|
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background:#ffffff;box-shadow:0 2px 4px rgba(0,0,0,0.1);"> |
|
<tr> |
|
<td style="height:5px;background:linear-gradient(to right, #6E2FC6, #9662D9);"></td> |
|
</tr> |
|
<tr> |
|
<td style="padding:30px 40px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:14px;line-height:1.6;color:#333333;"> |
|
<p style="margin:0 0 20px;font-size:15px;color:#333333;">Hello {first_name},</p> |
|
<div style="margin:0 0 20px;color:#333333;"> |
|
{payload.content} |
|
</div> |
|
<p style="margin:20px 0 0;font-size:15px;color:#333333;">Best regards,</p> |
|
<p style="margin:2px 0 0;font-size:15px;color:#333333;">{support_name}</p> |
|
<p style="margin:2px 0 0;font-size:14px;color:#666666;">Customer Support</p> |
|
</td> |
|
</tr> |
|
<tr> |
|
<td style="height:1px;background:#f0f0f0;"></td> |
|
</tr> |
|
<tr> |
|
<td style="padding:20px;text-align:center;font-size:12px;color:#999999;"> |
|
ClosetCoach © 2025 |
|
</td> |
|
</tr> |
|
</table> |
|
</td> |
|
</tr> |
|
</table> |
|
</body> |
|
</html> |
|
""" |
|
|
|
raw_message = f"""To: {user_email} |
|
From: "ClosetCoach" <[email protected]> |
|
Subject: {subject} |
|
Content-Type: text/html; charset="UTF-8" |
|
|
|
{email_html} |
|
""" |
|
|
|
encoded_message = encode_message(raw_message) |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.post( |
|
"https://gmail.googleapis.com/gmail/v1/users/me/messages/send", |
|
headers={ |
|
"Authorization": f"Bearer {access_token}", |
|
"Content-Type": "application/json" |
|
}, |
|
json={"raw": encoded_message} |
|
) as gmail_resp: |
|
if gmail_resp.status != 200: |
|
error_detail = await gmail_resp.text() |
|
raise HTTPException(status_code=500, detail=f"Error sending email: {error_detail}") |
|
gmail_data = await gmail_resp.json() |
|
|
|
|
|
message_payload = { |
|
"user": support_id, |
|
"content": payload.content, |
|
"created_at": created_at, |
|
"ticket_id": payload.ticket_id |
|
} |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.post( |
|
f"{SUPABASE_URL}/rest/v1/messages_tickets", |
|
headers=SUPABASE_ROLE_HEADERS, |
|
json=message_payload |
|
) as msg_resp: |
|
if msg_resp.status != 201: |
|
error_detail = await msg_resp.text() |
|
raise HTTPException(status_code=500, detail=f"Error recording response: {error_detail}") |
|
|
|
return { |
|
"status": "response sent successfully", |
|
"ticket_id": payload.ticket_id, |
|
"email_to": user_email, |
|
"message_id": gmail_data.get("id") |
|
} |