netgescon-day0/app/Console/Commands/GoogleImportKeepNotesCommand.php

310 lines
12 KiB
PHP
Executable File

<?php
namespace App\Console\Commands;
use App\Models\Amministratore;
use App\Models\ChiamataPostIt;
use App\Models\Stabile;
use App\Models\Ticket;
use App\Support\PhoneNumber;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
class GoogleImportKeepNotesCommand extends Command
{
protected $signature = 'google:import-keep-notes
{--admin-id=* : Limita import a uno o piu ID amministratore}
{--limit=100 : Numero massimo note da importare per admin}
{--stabile-id= : Stabile di default per creazione ticket}
{--create-ticket : Se presente, crea ticket dalle note importate}';
protected $description = 'Importa note Google Keep in Post-it chiamate e opzionalmente le converte in ticket.';
public function handle(): int
{
$limit = max(1, (int) $this->option('limit'));
$defaultStabileId = (int) ($this->option('stabile-id') ?: 0);
$createTicket = (bool) $this->option('create-ticket');
$filterAdminIds = collect((array) $this->option('admin-id'))
->map(fn($v) => (int) $v)
->filter(fn(int $v) => $v > 0)
->values()
->all();
$query = Amministratore::query();
if ($filterAdminIds !== []) {
$query->whereIn('id', $filterAdminIds);
}
$amministratori = $query->get();
if ($amministratori->isEmpty()) {
$this->warn('Nessun amministratore trovato per i filtri richiesti.');
return self::SUCCESS;
}
foreach ($amministratori as $amministratore) {
$google = Arr::get($amministratore->impostazioni ?? [], 'google', []);
$oauth = Arr::get($google, 'oauth', []);
if (! (bool) Arr::get($oauth, 'connected', false)) {
$this->line("Admin {$amministratore->id}: Google non collegato, salto.");
continue;
}
$token = $this->resolveAccessToken($amministratore, $google, $oauth);
if ($token === null) {
$this->warn("Admin {$amministratore->id}: token non disponibile, salto.");
continue;
}
$imported = 0;
$skipped = 0;
$tickets = 0;
$nextPageToken = null;
$creatorUserId = (int) ($amministratore->user_id ?? 0) > 0 ? (int) $amministratore->user_id : null;
while ($imported < $limit) {
$response = Http::withToken($token)
->acceptJson()
->get('https://keep.googleapis.com/v1/notes', [
'pageSize' => min(50, $limit),
'pageToken' => $nextPageToken,
]);
if ($response->status() === 401) {
$token = $this->resolveAccessToken($amministratore, $google, $oauth, true) ?? $token;
$response = Http::withToken($token)
->acceptJson()
->get('https://keep.googleapis.com/v1/notes', [
'pageSize' => min(50, $limit),
'pageToken' => $nextPageToken,
]);
}
if (! $response->successful()) {
$err = $this->extractGoogleError($response);
$this->warn("Admin {$amministratore->id}: errore Keep API ({$response->status()}) {$err}");
break;
}
$notes = $response->json('notes');
if (! is_array($notes) || count($notes) === 0) {
break;
}
foreach ($notes as $note) {
if ($imported >= $limit) {
break;
}
$resourceId = (string) (Arr::get($note, 'name') ?: Arr::get($note, 'id') ?: Arr::get($note, 'resourceName') ?: '');
if ($resourceId === '') {
$skipped++;
continue;
}
$already = ChiamataPostIt::query()
->where('origine', 'google_keep')
->where('origine_id', $resourceId)
->exists();
if ($already) {
$skipped++;
continue;
}
$title = trim((string) (Arr::get($note, 'title') ?: Arr::get($note, 'textContent', '')));
$body = trim((string) (Arr::get($note, 'body.text') ?: Arr::get($note, 'textContent') ?: Arr::get($note, 'body') ?: ''));
if ($title === '' && $body === '') {
$skipped++;
continue;
}
$fullText = trim($title . "\n" . $body);
$telefono = $this->extractPhone($fullText);
$direzione = $this->extractDirection($fullText);
$priorita = $this->extractPriority($fullText);
$chiamataIl = Arr::get($note, 'createTime') ?: Arr::get($note, 'createdTime');
$chiamataIl = is_string($chiamataIl) ? Carbon::parse($chiamataIl) : now();
$postIt = ChiamataPostIt::query()->create([
'telefono' => $telefono,
'nome_chiamante' => null,
'direzione' => $direzione,
'esito' => 'importata da Google Keep',
'oggetto' => $title !== '' ? $title : 'Nota Keep',
'nota' => $body !== '' ? $body : $title,
'priorita' => $priorita,
'stato' => 'post_it',
'creato_da_user_id' => $creatorUserId,
'chiamata_il' => $chiamataIl,
'origine' => 'google_keep',
'origine_id' => $resourceId,
]);
if ($createTicket) {
$stabileId = $this->extractStabileId($fullText) ?: $defaultStabileId;
if ($stabileId > 0 && Stabile::query()->whereKey($stabileId)->exists()) {
$ticket = Ticket::query()->create([
'stabile_id' => $stabileId,
'aperto_da_user_id' => (int) ($amministratore->user_id ?: 1),
'titolo' => $title !== '' ? $title : 'Nota Keep importata',
'descrizione' => ($body !== '' ? $body : $title) . "\n\nOrigine: Google Keep ({$resourceId})",
'data_apertura' => now(),
'stato' => 'Aperto',
'priorita' => $priorita,
]);
$postIt->ticket_id = $ticket->id;
$postIt->stato = 'ticket';
$postIt->save();
$tickets++;
}
}
$imported++;
}
$nextPageToken = $response->json('nextPageToken');
if (! is_string($nextPageToken) || $nextPageToken === '') {
break;
}
}
$this->info("Admin {$amministratore->id}: importate {$imported}, saltate {$skipped}, ticket creati {$tickets}.");
}
return self::SUCCESS;
}
private function extractStabileId(string $text): int
{
if (preg_match('/stabile\s*[:#-]?\s*(\d+)/i', $text, $m) === 1) {
return (int) ($m[1] ?? 0);
}
return 0;
}
private function extractPhone(string $text): ?string
{
if (preg_match('/(\+?\d[\d\s\-\(\)]{7,}\d)/', $text, $m) !== 1) {
return null;
}
return PhoneNumber::toE164Italy((string) ($m[1] ?? ''));
}
private function extractDirection(string $text): string
{
$t = strtolower($text);
if (str_contains($t, '#uscita') || str_contains($t, 'in uscita')) {
return 'in_uscita';
}
if (str_contains($t, '#persa') || str_contains($t, 'chiamata persa')) {
return 'persa';
}
return 'in_arrivo';
}
private function extractPriority(string $text): string
{
$t = strtolower($text);
if (str_contains($t, '#urgente') || str_contains($t, 'urgente')) {
return 'Urgente';
}
if (str_contains($t, '#alta') || str_contains($t, 'alta priorita')) {
return 'Alta';
}
if (str_contains($t, '#bassa') || str_contains($t, 'bassa priorita')) {
return 'Bassa';
}
return 'Media';
}
private function extractGoogleError($response): string
{
$msg = (string) (Arr::get($response->json(), 'error.message') ?: Arr::get($response->json(), 'error.status') ?: '');
return trim($msg);
}
private function resolveAccessToken(Amministratore $amministratore, array $google, array $oauth, bool $forceRefresh = false): ?string
{
$accessToken = trim((string) Arr::get($oauth, 'access_token', ''));
$refreshToken = trim((string) Arr::get($oauth, 'refresh_token', ''));
if (! $forceRefresh && $accessToken !== '') {
return $accessToken;
}
if ($refreshToken === '') {
return null;
}
$settingsClientId = trim((string) Arr::get($google, 'client_id', ''));
$settingsClientSecret = trim((string) Arr::get($google, 'client_secret', ''));
$configClientId = trim((string) config('services.google.client_id'));
$configClientSecret = trim((string) config('services.google.client_secret'));
$credentialPairs = [];
if ($settingsClientId !== '' && $settingsClientSecret !== '') {
$credentialPairs[] = [$settingsClientId, $settingsClientSecret];
}
if (
$configClientId !== ''
&& $configClientSecret !== ''
&& ($configClientId !== $settingsClientId || $configClientSecret !== $settingsClientSecret)
) {
$credentialPairs[] = [$configClientId, $configClientSecret];
}
if ($credentialPairs === []) {
return null;
}
foreach ($credentialPairs as [$clientId, $clientSecret]) {
$response = Http::asForm()->post('https://oauth2.googleapis.com/token', [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token',
]);
if (! $response->successful()) {
continue;
}
$payload = $response->json();
$newAccessToken = trim((string) Arr::get($payload, 'access_token', ''));
if ($newAccessToken === '') {
continue;
}
$impostazioni = $amministratore->impostazioni ?? [];
$impostazioniGoogle = Arr::get($impostazioni, 'google', []);
$impostazioniOauth = Arr::get($impostazioniGoogle, 'oauth', []);
$impostazioniOauth['access_token'] = $newAccessToken;
$impostazioniOauth['expires_in'] = (int) Arr::get($payload, 'expires_in', 3600);
$impostazioniOauth['refreshed_at'] = Carbon::now()->toDateTimeString();
if (isset($payload['refresh_token']) && is_string($payload['refresh_token']) && $payload['refresh_token'] !== '') {
$impostazioniOauth['refresh_token'] = $payload['refresh_token'];
}
$impostazioniGoogle['oauth'] = $impostazioniOauth;
$impostazioni['google'] = $impostazioniGoogle;
$amministratore->impostazioni = $impostazioni;
$amministratore->save();
return $newAccessToken;
}
return null;
}
}