Files
wevads-platform/scripts/graph-sender-lib.php.disabled

129 lines
6.1 KiB
Plaintext

<?php
/**
* GRAPH API SEND LIBRARY
* Replaces SMTP basic auth with OAuth2 client_credentials + Graph API sendMail
* Used by brain-pipeline.php
*/
class GraphSender {
private $pdo;
private $tokenCache = []; // tenant_id => ['token'=>..., 'expires'=>...]
public function __construct($pdo) { $this->pdo = $pdo; }
public function getToken($tenantId, $clientId, $clientSecret) {
$key = $tenantId;
if (isset($this->tokenCache[$key]) && $this->tokenCache[$key]['expires'] > time()) {
return $this->tokenCache[$key]['token'];
}
$ch = curl_init("https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token");
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>15,
CURLOPT_POSTFIELDS=>http_build_query([
'grant_type'=>'client_credentials','client_id'=>$clientId,
'client_secret'=>$clientSecret,'scope'=>'https://graph.microsoft.com/.default'
])]);
$r = json_decode(curl_exec($ch), true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if (!isset($r['access_token'])) return null;
$this->tokenCache[$key] = ['token'=>$r['access_token'], 'expires'=>time()+3500];
return $r['access_token'];
}
public function getRandomTenant() {
return $this->pdo->query("
SELECT * FROM admin.graph_tenants
WHERE status='active' AND sends_today < daily_limit
ORDER BY RANDOM() LIMIT 1
")->fetch(\PDO::FETCH_ASSOC);
}
public function getRandomUser($token, $tenantDomain) {
$ch = curl_init("https://graph.microsoft.com/v1.0/users?\$top=50&\$select=userPrincipalName,displayName");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_HTTPHEADER=>["Authorization: Bearer $token"]]);
$users = json_decode(curl_exec($ch), true);
curl_close($ch);
$valid = array_filter($users['value'] ?? [], function($u) {
return strpos($u['userPrincipalName'], '#') === false; // skip external users
});
return $valid ? $valid[array_rand($valid)]['userPrincipalName'] : null;
}
/**
* Send test email via Graph API
* Returns ['success'=>bool, 'sender'=>string, 'error'=>string, 'tenant'=>string]
*/
public function sendTest($toEmail, $subject, $body, $headers = []) {
$tenant = $this->getRandomTenant();
if (!$tenant) return ['success'=>false, 'error'=>'No available tenant', 'sender'=>'', 'tenant'=>''];
$token = $this->getToken($tenant['tenant_id'], $tenant['client_id'], $tenant['client_secret']);
if (!$token) return ['success'=>false, 'error'=>'Token failed', 'sender'=>'', 'tenant'=>$tenant['tenant_domain']];
$sender = $this->getRandomUser($token, $tenant['tenant_domain']);
if (!$sender) return ['success'=>false, 'error'=>'No valid user', 'sender'=>'', 'tenant'=>$tenant['tenant_domain']];
$msg = [
'message' => [
'subject' => $subject,
'body' => ['contentType' => 'HTML', 'content' => $body],
'toRecipients' => [['emailAddress' => ['address' => $toEmail]]],
],
'saveToSentItems' => false
];
// Add custom headers via internetMessageHeaders
if (!empty($headers)) {
$msg['message']['internetMessageHeaders'] = [];
foreach ($headers as $k => $v) {
if ($v) $msg['message']['internetMessageHeaders'][] = ['name' => $k, 'value' => $v];
}
}
$url = "https://graph.microsoft.com/v1.0/users/" . urlencode($sender) . "/sendMail";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST=>true, CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>15,
CURLOPT_HTTPHEADER=>["Authorization: Bearer $token","Content-Type: application/json"],
CURLOPT_POSTFIELDS=>json_encode($msg)
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code == 202) {
$this->pdo->exec("UPDATE admin.graph_tenants SET sends_today=sends_today+1, last_send_at=NOW() WHERE tenant_domain='{$tenant['tenant_domain']}'");
return ['success'=>true, 'sender'=>$sender, 'error'=>'', 'tenant'=>$tenant['tenant_domain']];
}
$err = json_decode($resp, true);
$errMsg = $err['error']['message'] ?? $resp;
// If mailbox stale, try another user
if (strpos($errMsg, 'Stale') !== false || strpos($errMsg, 'NotFound') !== false) {
// Retry with different user
for ($retry = 0; $retry < 3; $retry++) {
$sender2 = $this->getRandomUser($token, $tenant['tenant_domain']);
if (!$sender2 || $sender2 == $sender) continue;
$ch = curl_init("https://graph.microsoft.com/v1.0/users/" . urlencode($sender2) . "/sendMail");
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>15,
CURLOPT_HTTPHEADER=>["Authorization: Bearer $token","Content-Type: application/json"],
CURLOPT_POSTFIELDS=>json_encode($msg)]);
$resp2 = curl_exec($ch); $code2 = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($code2 == 202) {
$this->pdo->exec("UPDATE admin.graph_tenants SET sends_today=sends_today+1, last_send_at=NOW() WHERE tenant_domain='{$tenant['tenant_domain']}'");
return ['success'=>true, 'sender'=>$sender2, 'error'=>'', 'tenant'=>$tenant['tenant_domain']];
}
}
}
return ['success'=>false, 'sender'=>$sender, 'error'=>substr($errMsg, 0, 200), 'tenant'=>$tenant['tenant_domain']];
}
/** Reset daily counters (call at midnight) */
public function resetDailyCounters() {
$this->pdo->exec("UPDATE admin.graph_tenants SET sends_today=0 WHERE status='active'");
}
}