π Source: chat_handler.php
<?php
set_time_limit(90);
ini_set('max_execution_time', 90);
date_default_timezone_set('Europe/Bucharest');
session_start();
require_once 'vendor/autoload.php';
require_once 'database.php';
header('Content-Type: application/json');
if (!isset($_SESSION['access_token'])) {
http_response_code(401);
echo json_encode(['reply' => 'Please login first']);
exit();
}
$input = json_decode(file_get_contents('php://input'), true);
$query = $input['query'] ?? '';
$lang = $input['lang'] ?? 'en';
$requestedChatId = $input['conversation_id'] ?? null;
if (empty($query)) {
echo json_encode(['reply' => 'What would you like to know about your emails?']);
exit();
}
// Get user email
if (!isset($_SESSION['user_email']) && isset($_SESSION['access_token'])) {
try {
$tempClient = new Google\Client();
$tempClient->setAccessToken($_SESSION['access_token']);
$oauth2 = new Google\Service\Oauth2($tempClient);
$userInfo = $oauth2->userinfo->get();
$_SESSION['user_email'] = $userInfo->email;
} catch (Exception $e) {
$_SESSION['user_email'] = 'user_' . session_id();
}
}
$userEmail = $_SESSION['user_email'] ?? 'guest_' . session_id();
$db = new ChatDatabase();
// ============================================
// ENSURE THERE'S ALWAYS A CURRENT CONVERSATION
// ============================================
if (!isset($_SESSION['current_conversation_id'])) {
$currentChatId = $db->createConversation($userEmail);
$_SESSION['current_conversation_id'] = $currentChatId;
}
// Handle chat ID from request
if ($requestedChatId) {
$currentChatId = intval($requestedChatId);
$_SESSION['current_conversation_id'] = $currentChatId;
} elseif (isset($_SESSION['current_conversation_id'])) {
$currentChatId = $_SESSION['current_conversation_id'];
} else {
$currentChatId = $db->createConversation($userEmail);
$_SESSION['current_conversation_id'] = $currentChatId;
}
// Load ALL previous messages from database for context
$previousMessages = $db->getMessages($currentChatId);
// Get email body function
function getEmailBodyContent($service, $messageId) {
try {
$message = $service->users_messages->get('me', $messageId, ['format' => 'full']);
$payload = $message->getPayload();
$decode = function($data) {
return base64_decode(strtr($data, '-_', '+/'));
};
$body = "";
// VerificΔ direct body
if ($payload->getBody() && $payload->getBody()->getData()) {
$body = $decode($payload->getBody()->getData());
}
// VerificΔ pΔrΘile componente
if (empty($body) && $payload->getParts()) {
foreach ($payload->getParts() as $part) {
if ($part->getBody() && $part->getBody()->getData()) {
$body = $decode($part->getBody()->getData());
if (!empty(trim($body))) break;
}
if ($part->getParts()) {
foreach ($part->getParts() as $subPart) {
if ($subPart->getBody() && $subPart->getBody()->getData()) {
$body = $decode($subPart->getBody()->getData());
if (!empty(trim($body))) break 2;
}
}
}
}
}
if (empty($body)) return "";
// CurΔΘare HTML mai agresivΔ
// EliminΔ tag-urile style Θi script
$body = preg_replace('/<style[^>]*>.*?<\/style>/is', '', $body);
$body = preg_replace('/<script[^>]*>.*?<\/script>/is', '', $body);
// EliminΔ tag-urile de head, meta, link
$body = preg_replace('/<head[^>]*>.*?<\/head>/is', '', $body);
$body = preg_replace('/<meta[^>]*>/i', '', $body);
$body = preg_replace('/<link[^>]*>/i', '', $body);
// Extrage doar textul dintre tag-uri
$body = strip_tags($body);
// DecodeazΔ entitΔΘile HTML
$body = html_entity_decode($body, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// NormalizeazΔ spaΘiile Θi liniile noi
$body = preg_replace('/\s+/', ' ', $body);
$body = trim($body);
// EliminΔ disclaimer-ul Θi footer-ul (opΘional)
$disclaimerMarkers = ['disclaimer', '***', 'informare', 'confidenΘialΔ', 'penal'];
foreach ($disclaimerMarkers as $marker) {
$pos = stripos($body, $marker);
if ($pos !== false) {
$body = substr($body, 0, $pos);
}
}
// Limitare la 2000 caractere
return substr($body, 0, 2000);
} catch (Exception $e) {
error_log("Error getting email body: " . $e->getMessage());
return "";
}
}
// Reminder check
$isReminder = false;
$reminderMinutes = 0;
$reminderText = '';
if (preg_match('/(remind me|set a reminder|set a timer) in (\d+)\s*(minute|minutes|min|m)/i', $query, $matches)) {
$reminderMinutes = intval($matches[2]);
$reminderText = "Check your email";
$isReminder = true;
}
if ($isReminder) {
$reply = "π I'll send a notification in $reminderMinutes minute(s): \"$reminderText\"";
$db->addMessage($currentChatId, 'user', $query);
$db->addMessage($currentChatId, 'assistant', $reply);
echo json_encode([
'reply' => $reply,
'reminder' => ['text' => $reminderText, 'minutes' => $reminderMinutes],
'emails' => [],
'total_emails' => 0,
'conversation_id' => $currentChatId
]);
exit();
}
// Fetch emails - NEWEST FIRST
$emailSummary = "";
$emailListForDisplay = [];
$totalEmails = 0;
$fullContent = "";
try {
$client = new Google\Client();
$client->setAccessToken($_SESSION['access_token']);
$service = new Google\Service\Gmail($client);
$optParams = ['maxResults' => 15, 'labelIds' => ['INBOX']];
$messages = $service->users_messages->listUsersMessages('me', $optParams);
$allEmails = [];
if ($messages->getMessages()) {
foreach ($messages->getMessages() as $msg) {
$message = $service->users_messages->get('me', $msg->getId(), [
'format' => 'metadata',
'metadataHeaders' => ['From', 'Subject', 'Date']
]);
$headers = $message->getPayload()->getHeaders();
$from = 'Unknown';
$subject = '(no subject)';
$date = '';
$threadId = $message->getThreadId();
foreach ($headers as $header) {
if ($header->getName() == 'From') $from = $header->getValue();
if ($header->getName() == 'Subject') $subject = $header->getValue();
if ($header->getName() == 'Date') $date = $header->getValue();
}
try {
$dateObj = new DateTime($date);
$dateObj->setTimezone(new DateTimeZone('Europe/Bucharest'));
$dateFormatted = $dateObj->format('d M Y, H:i');
$timestamp = $dateObj->getTimestamp();
} catch (Exception $e) {
$dateFormatted = 'unknown date';
$timestamp = 0;
}
$allEmails[] = [
'from' => $from,
'subject' => $subject,
'date' => $dateFormatted,
'timestamp' => $timestamp,
'threadId' => $threadId,
'id' => $msg->getId()
];
}
}
// Sort by timestamp DESC (NEWEST FIRST)
usort($allEmails, function($a, $b) {
return $b['timestamp'] - $a['timestamp'];
});
$totalEmails = count($allEmails);
foreach ($allEmails as $newIndex => $email) {
$displayNumber = $newIndex + 1;
$emailSummary .= "$displayNumber. {$email['from']} | {$email['subject']} [{$email['date']}]\n";
$emailListForDisplay[] = [
'from' => htmlspecialchars($email['from']),
'subject' => htmlspecialchars($email['subject']),
'date' => $email['date'],
'id' => $email['id'],
'threadId' => $email['threadId']
];
}
// If asking for content, get the specific email's body
if (preg_match('/(contents? of|what.*say|what.*in the email)/i', $query)) {
$targetEmail = null;
if (stripos($query, 'twitch') !== false) {
foreach ($allEmails as $email) {
if (stripos($email['from'], 'twitch') !== false) {
$targetEmail = $email;
break;
}
}
} elseif (stripos($query, 'iconscout') !== false) {
foreach ($allEmails as $email) {
if (stripos($email['from'], 'iconscout') !== false) {
$targetEmail = $email;
break;
}
}
} elseif (stripos($query, 'latest') !== false || stripos($query, 'most recent') !== false) {
$targetEmail = $allEmails[0] ?? null;
}
if ($targetEmail && !empty($targetEmail['id'])) {
$fullContent = getEmailBodyContent($service, $targetEmail['id']);
if (!empty($fullContent)) {
$emailSummary .= "\n\n[FULL CONTENT of {$targetEmail['from']} email]:\n$fullContent";
}
}
}
} catch (Exception $e) {
echo json_encode(['reply' => 'Error: ' . $e->getMessage(), 'emails' => []]);
exit();
}
// Build conversation context from ALL previous messages
$convContext = "";
if (!empty($previousMessages)) {
$convContext = "\n\nPrevious conversation history (IMPORTANT - remember this):\n";
foreach ($previousMessages as $msg) {
$convContext .= ($msg['role'] == 'user' ? "User: " : "Assistant: ") . $msg['content'] . "\n";
}
}
// Save current user message
$db->addMessage($currentChatId, 'user', $query);
// Build prompt with full context
$prompt = "Emails (1=NEWEST, $totalEmails total):\n$emailSummary\n$convContext\n\nCurrent question: $query\n\nAnswer based on the emails and remember the conversation history above.";
$url = 'http://127.0.0.1:11434/api/generate';
$data = [
'model' => 'llama3.2:3b',
'prompt' => $prompt,
'stream' => false,
'options' => [
'num_predict' => 800,
'temperature' => 0.3
]
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
$result = json_decode($response, true);
$reply = trim($result['response'] ?? "No response");
} else {
$reply = "AI processing... Please try again.";
}
// Save assistant response
$db->addMessage($currentChatId, 'assistant', $reply);
// Auto-create chat title if needed
$conversations = $db->getConversations($userEmail);
foreach ($conversations as $conv) {
if ($conv['id'] == $currentChatId && $conv['title'] == 'New Chat') {
$newTitle = substr($query, 0, 30) . (strlen($query) > 30 ? '...' : '');
$db->renameConversation($currentChatId, $newTitle);
break;
}
}
echo json_encode([
'reply' => $reply,
'emails' => $emailListForDisplay,
'total_emails' => $totalEmails,
'conversation_id' => $currentChatId
]);
?>
β Back