Files
crm-ms1/v4_ci4/app/Libraries/Radar/GmailReader.php

141 lines
4.3 KiB
PHP

<?php
namespace App\Libraries\Radar;
/**
* Lecture Gmail (readonly) pour une mailbox impersonnée.
*/
class GmailReader
{
public function __construct(
private GoogleWorkspaceToken $tokenClient,
private string $scopes,
) {
}
/**
* @return list<array{
* gmail_thread_id:string,
* mailbox:string,
* subject:string,
* from_email:string,
* from_name:string,
* snippet:string,
* gmail_url:string,
* received_at:?string
* }>
*/
public function listRecentThreads(string $mailbox, int $lookbackDays, int $maxMessages): array
{
$access = $this->tokenClient->getAccessToken($mailbox, $this->scopes);
$q = 'newer_than:' . max(1, $lookbackDays) . 'd';
$url = 'https://gmail.googleapis.com/gmail/v1/users/me/messages?'
. http_build_query([
'q' => $q,
'maxResults' => $maxMessages,
]);
[$code, $body] = $this->tokenClient->http(
'GET',
$url,
['Authorization: Bearer ' . $access]
);
if ($code !== 200) {
throw new \RuntimeException("Gmail messages.list ({$code}): {$body}");
}
$data = json_decode($body, true) ?: [];
$ids = $data['messages'] ?? [];
if ($ids === []) {
return [];
}
$byThread = [];
foreach ($ids as $m) {
$msgId = $m['id'] ?? null;
if (! $msgId) {
continue;
}
$detail = $this->getMessageMeta($access, $msgId);
if ($detail === null) {
continue;
}
$tid = $detail['gmail_thread_id'];
// Garder le message le plus récent du fil dans ce batch
if (! isset($byThread[$tid]) || ($detail['internal_ts'] ?? 0) > ($byThread[$tid]['internal_ts'] ?? 0)) {
$detail['mailbox'] = $mailbox;
$byThread[$tid] = $detail;
}
}
$out = array_values($byThread);
usort($out, static fn ($a, $b) => ($b['internal_ts'] ?? 0) <=> ($a['internal_ts'] ?? 0));
foreach ($out as &$row) {
unset($row['internal_ts']);
}
unset($row);
return $out;
}
/**
* @return ?array<string, mixed>
*/
private function getMessageMeta(string $access, string $messageId): ?array
{
$url = 'https://gmail.googleapis.com/gmail/v1/users/me/messages/' . rawurlencode($messageId)
. '?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date';
[$code, $body] = $this->tokenClient->http(
'GET',
$url,
['Authorization: Bearer ' . $access]
);
if ($code !== 200) {
return null;
}
$msg = json_decode($body, true);
if (! is_array($msg)) {
return null;
}
$headers = [];
foreach ($msg['payload']['headers'] ?? [] as $h) {
$headers[strtolower($h['name'] ?? '')] = $h['value'] ?? '';
}
$from = $this->parseFrom($headers['from'] ?? '');
$ts = isset($msg['internalDate']) ? (int) floor(((int) $msg['internalDate']) / 1000) : 0;
return [
'gmail_thread_id' => (string) ($msg['threadId'] ?? $messageId),
'subject' => (string) ($headers['subject'] ?? '(sans sujet)'),
'from_email' => $from['email'],
'from_name' => $from['name'],
'snippet' => (string) ($msg['snippet'] ?? ''),
'gmail_url' => 'https://mail.google.com/mail/u/0/#all/' . rawurlencode((string) ($msg['threadId'] ?? $messageId)),
'received_at' => $ts > 0 ? date('Y-m-d H:i:s', $ts) : null,
'internal_ts' => $ts,
];
}
/**
* @return array{email:string,name:string}
*/
private function parseFrom(string $from): array
{
if (preg_match('/^(.*)<([^>]+)>$/', trim($from), $m)) {
return [
'name' => trim($m[1], " \t\"'"),
'email' => strtolower(trim($m[2])),
];
}
$email = strtolower(trim($from));
return ['email' => $email, 'name' => ''];
}
}