auto-sync-0340

This commit is contained in:
opus
2026-04-20 03:40:02 +02:00
parent 910c76bd7c
commit d6a443a245
4 changed files with 299 additions and 42 deletions

View File

@@ -1,10 +1,9 @@
<?php
// WEVIA APPLE INGEST v3 — full iPhone ingestion + AI analysis
// Accepts: photos (OCR), messages, contacts, calendar, notes, health, calls
// WEVIA APPLE INGEST v3.1 — full iPhone ingestion + AI analysis + task/alert management
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-WEVIA-Token');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
$action = $_GET['action'] ?? $_POST['action'] ?? 'status';
@@ -41,52 +40,52 @@ function append_event($ev) {
function extract_entities($text) {
$e = ['people'=>[], 'orgs'=>[], 'money'=>[], 'deadlines'=>[], 'locations'=>[], 'emails'=>[], 'phones'=>[], 'urls'=>[], 'apps'=>[], 'keywords'=>[], 'sentiment'=>'neutral', 'urgency'=>'low', 'oss'=>[]];
preg_match_all('/[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/', $text, $m);
$e['emails'] = array_values(array_unique($m[0]));
preg_match_all('/(?:\+\d{1,3}[\s\-]?)?(?:\(?\d{2,4}\)?[\s\-]?){2,5}\d{2,4}/', $text, $m);
$e['phones'] = array_values(array_unique(array_filter($m[0], function($p) {
$d = preg_replace('/[^\d]/', '', $p);
return strlen($d) >= 8 && strlen($d) <= 15;
})));
preg_match_all('#https?://[^\s<>"\']+#i', $text, $m);
$e['urls'] = array_values(array_unique($m[0]));
preg_match_all('/(?:[\$€£¥]|MAD|DZD|TND|EUR|USD|DH)\s*[\d\s,.]+|\d+(?:[\s,.]\d+)*\s*(?:€|\$|£|MAD|DZD|TND|DH|EUR|USD|k|K|M)\b/u', $text, $m);
$e['money'] = array_values(array_unique(array_map('trim', $m[0])));
preg_match_all('/\b(?:\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}|\d{4}\-\d{2}\-\d{2}|(?:aujourd\'hui|demain|hier|tomorrow|today|yesterday|this week|next week|la semaine prochaine))/iu', $text, $m);
$e['deadlines'] = array_values(array_unique(array_map('trim', $m[0])));
preg_match_all('/\b(?:Dr|Mr|Mrs|Ms|Mme|M\.|Pr|Prof|CEO|CTO|CFO|COO|VP|Dir|Directeur|Director|Manager)\.?\s+([A-ZÀÂÉÈÊÎÔÛÇ][a-zàâéèêîôûç]+(?:\s+[A-ZÀÂÉÈÊÎÔÛÇ][a-zàâéèêîôûç]+)*)/u', $text, $m);
$e['people'] = array_values(array_unique($m[0]));
preg_match_all('/\b[A-Z][a-zA-Z0-9]+(?:\s+[A-Z][a-zA-Z0-9]+)*\s+(?:Inc\.?|LLC|Ltd|Corp\.?|SA|SARL|SAS|GmbH|AG|BV|plc)\b/', $text, $m);
$e['orgs'] = array_values(array_unique($m[0]));
$oss_list = ['langchain','langgraph','crewai','n8n','rasa','ollama','vllm','openrouter','langfuse','dify','flowise','qdrant','chromadb','weaviate','pinecone','milvus','postgres','mongodb','redis','kafka','nginx','kubernetes','docker','grafana','prometheus','stripe','claude','gpt-4','gpt-4o','llama','mistral','gemma','whisper','sap','salesforce','hubspot','notion','obsidian','vercel','cloudflare','github','gitea','supabase','firebase','airtable','zapier','openai','anthropic','gemini','wevads','weval','wevia','ethica','paperclip','arsenal','resend','sendgrid','twilio'];
$tl = ' ' . strtolower($text) . ' ';
foreach ($oss_list as $o) {
if (preg_match('/[^a-z0-9]' . preg_quote($o, '/') . '[^a-z0-9]/i', $tl)) $e['oss'][] = $o;
}
$e['oss'] = array_values(array_unique($e['oss']));
if (preg_match('/\b(urgent|asap|immediatly|critical|critique|deadline|echeance|today|aujourd)/i', $text)) $e['urgency'] = 'high';
elseif (preg_match('/\b(important|priority|priorite|week|semaine|soon|bientot)/i', $text)) $e['urgency'] = 'medium';
$pos_w = preg_match_all('/\b(great|excellent|parfait|super|merci|thanks|good|ok|approved|accepted|yes|oui)\b/i', $text);
$neg_w = preg_match_all('/\b(problem|probleme|error|erreur|refused|rejected|no|non|urgent|complaint|issue|bug|broken)\b/i', $text);
if ($pos_w > $neg_w + 1) $e['sentiment'] = 'positive';
elseif ($neg_w > $pos_w + 1) $e['sentiment'] = 'negative';
$apps = ['whatsapp','telegram','instagram','tiktok','linkedin','twitter','x.com','facebook','messenger','slack','discord','teams','zoom','gmail','outlook','calendly','stripe','paypal','revolut','airbnb','uber'];
foreach ($apps as $a) {
if (stripos($text, $a) !== false) $e['apps'][] = $a;
}
$e['apps'] = array_values(array_unique($e['apps']));
return $e;
}
@@ -95,7 +94,7 @@ function generate_recommendations($item) {
$e = $item['entities'] ?? [];
$type = $item['type'] ?? 'unknown';
$text = $item['text_sample'] ?? '';
if (!empty($e['deadlines'])) {
foreach ($e['deadlines'] as $d) {
$reco[] = ['kind'=>'task_create', 'priority'=>($e['urgency']==='high'?'P0':($e['urgency']==='medium'?'P1':'P2')),
@@ -166,7 +165,7 @@ function ocr_image($path) {
elseif (isset($d['response'])) $ocr = $d['response'];
elseif (isset($d['result'])) $ocr = $d['result'];
} catch (Exception $e) {}
if (!$ocr && shell_exec('which tesseract 2>/dev/null')) {
$tmp = tempnam('/tmp', 'ocr_');
exec("tesseract " . escapeshellarg($path) . " $tmp 2>/dev/null");
@@ -178,18 +177,37 @@ function ocr_image($path) {
return trim($ocr);
}
// FIX v3.1: tasks and alerts can co-exist — task_create P0 goes to BOTH tasks[] AND alerts[]
function apply_reco_to_index(&$idx, $reco, $item_id) {
foreach ($reco as $r) {
$r['id'] = 'reco_' . uniqid('', true);
$r['source_item'] = $item_id;
$r['created_at'] = date('c');
$r['status'] = 'open';
// ALL P0 → alerts
if ($r['priority'] === 'P0') $idx['alerts'][] = $r;
// ALL task_create → tasks (regardless of priority)
if ($r['kind'] === 'task_create') $idx['tasks'][] = $r;
// tech_research → opportunities
if ($r['kind'] === 'tech_research') $idx['opportunities'][] = $r;
}
}
// ===== ACTIONS =====
if ($action === 'status') {
$idx = load_index();
echo json_encode([
'ok' => true, 'v' => 'v3-full-ingestion', 'ts' => date('c'),
'ok' => true, 'v' => 'v3.1-full-ingestion', 'ts' => date('c'),
'total_items' => $idx['total_items'] ?? 0,
'by_type' => $idx['by_type'] ?? [],
'entities_count' => array_map(function($v) { return is_array($v) ? count($v) : 0; }, $idx['entities'] ?? []),
'tasks_pending' => count(array_filter($idx['tasks'] ?? [], function($t) { return ($t['status'] ?? 'open') === 'open'; })),
'tasks_total' => count($idx['tasks'] ?? []),
'opportunities' => count($idx['opportunities'] ?? []),
'alerts' => count($idx['alerts'] ?? []),
'alerts_pending' => count(array_filter($idx['alerts'] ?? [], function($a) { return ($a['status'] ?? 'open') === 'open'; })),
'alerts_total' => count($idx['alerts'] ?? []),
'last_update' => $idx['last_update'] ?? null,
'drill_count' => count($idx['drill_index'] ?? [])
], JSON_PRETTY_PRINT);
@@ -203,7 +221,7 @@ if ($action === 'ingest_photo') {
$safe = preg_replace('/[^a-zA-Z0-9._\-]/', '_', $f['name']);
$dest = "$DATA_DIR/photos/$id.$safe";
move_uploaded_file($f['tmp_name'], $dest);
$ocr = ocr_image($dest);
$entities = extract_entities($ocr);
$item = ['id'=>$id, 'type'=>'photo', 'filename'=>$f['name'], 'size'=>filesize($dest), 'path'=>$dest,
@@ -211,7 +229,7 @@ if ($action === 'ingest_photo') {
'ocr'=>$ocr, 'ocr_len'=>strlen($ocr), 'text_sample'=>substr($ocr, 0, 500),
'entities'=>$entities, 'ingested_at'=>date('c')];
$item['recommendations'] = generate_recommendations($item);
$idx = load_index();
$idx['total_items']++;
$idx['by_type']['photo'] = ($idx['by_type']['photo'] ?? 0) + 1;
@@ -221,11 +239,7 @@ if ($action === 'ingest_photo') {
foreach ($v as $val) $idx['entities'][$k][] = ['val'=>$val, 'source'=>$id];
}
}
foreach ($item['recommendations'] as $r) {
if ($r['priority'] === 'P0') $idx['alerts'][] = $r;
elseif ($r['kind'] === 'task_create') $idx['tasks'][] = array_merge(['status'=>'open'], $r);
elseif ($r['kind'] === 'tech_research') $idx['opportunities'][] = $r;
}
apply_reco_to_index($idx, $item['recommendations'], $id);
save_index($idx);
append_event(['type'=>'ingest', 'item_id'=>$id, 'ts'=>date('c')]);
echo json_encode(['ok'=>true, 'id'=>$id, 'item'=>$item], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
@@ -237,7 +251,7 @@ if ($action === 'ingest_structured') {
$type = $body['type'] ?? $_POST['type'] ?? 'note';
$items = $body['items'] ?? ($body['item'] ? [$body['item']] : []);
if (empty($items)) { echo json_encode(['ok'=>false,'error'=>'no items']); exit; }
$idx = load_index();
$processed = [];
foreach ($items as $it) {
@@ -249,23 +263,19 @@ if ($action === 'ingest_structured') {
elseif ($type === 'health') $text = json_encode($it, JSON_UNESCAPED_UNICODE);
elseif ($type === 'call') $text = trim(($it['name'] ?? 'Unknown') . ' - ' . ($it['number'] ?? '') . ' - ' . ($it['duration'] ?? '') . 's - ' . ($it['direction'] ?? ''));
else $text = json_encode($it, JSON_UNESCAPED_UNICODE);
$entities = extract_entities($text);
$item = ['id'=>$id, 'type'=>$type, 'raw'=>$it, 'text_sample'=>substr($text, 0, 500),
'entities'=>$entities, 'ingested_at'=>date('c')];
$item['recommendations'] = generate_recommendations($item);
$idx['total_items']++;
$idx['by_type'][$type] = ($idx['by_type'][$type] ?? 0) + 1;
$idx['drill_index'][$id] = $item;
foreach ($entities as $k => $v) {
if (is_array($v)) foreach ($v as $val) $idx['entities'][$k][] = ['val'=>$val, 'source'=>$id];
}
foreach ($item['recommendations'] as $r) {
if ($r['priority'] === 'P0') $idx['alerts'][] = $r;
elseif ($r['kind'] === 'task_create') $idx['tasks'][] = array_merge(['status'=>'open'], $r);
elseif ($r['kind'] === 'tech_research') $idx['opportunities'][] = $r;
}
apply_reco_to_index($idx, $item['recommendations'], $id);
$processed[] = $id;
append_event(['type'=>'ingest', 'item_id'=>$id, 'data_type'=>$type, 'ts'=>date('c')]);
}
@@ -279,6 +289,9 @@ if ($action === 'drill') {
$idx = load_index();
$item = $idx['drill_index'][$id] ?? null;
if (!$item) { echo json_encode(['ok'=>false,'error'=>'not found']); exit; }
// Also return related reco + tasks + alerts for this item
$item['related_tasks'] = array_values(array_filter($idx['tasks'] ?? [], function($t) use ($id) { return ($t['source_item'] ?? '') === $id; }));
$item['related_alerts'] = array_values(array_filter($idx['alerts'] ?? [], function($a) use ($id) { return ($a['source_item'] ?? '') === $id; }));
echo json_encode(['ok'=>true, 'item'=>$item], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;
}
@@ -333,13 +346,137 @@ if ($action === 'entities') {
exit;
}
if ($action === 'tasks') { $idx = load_index(); echo json_encode(['ok'=>true, 'tasks'=>$idx['tasks'] ?? []]); exit; }
if ($action === 'alerts') { $idx = load_index(); echo json_encode(['ok'=>true, 'alerts'=>$idx['alerts'] ?? []]); exit; }
if ($action === 'tasks') {
$idx = load_index();
$filter = $_GET['status'] ?? null;
$tasks = $idx['tasks'] ?? [];
if ($filter) $tasks = array_values(array_filter($tasks, function($t) use ($filter) { return ($t['status'] ?? 'open') === $filter; }));
echo json_encode(['ok'=>true, 'tasks'=>$tasks, 'total'=>count($tasks)]);
exit;
}
if ($action === 'alerts') {
$idx = load_index();
$filter = $_GET['status'] ?? null;
$alerts = $idx['alerts'] ?? [];
if ($filter) $alerts = array_values(array_filter($alerts, function($a) use ($filter) { return ($a['status'] ?? 'open') === $filter; }));
echo json_encode(['ok'=>true, 'alerts'=>$alerts, 'total'=>count($alerts)]);
exit;
}
// NEW v3.1: mark_done / resolve_alert
if ($action === 'mark_done') {
$id = $_GET['id'] ?? $_POST['id'] ?? '';
$idx = load_index();
$found = false;
foreach ($idx['tasks'] ?? [] as &$t) {
if (($t['id'] ?? '') === $id) {
$t['status'] = 'done';
$t['done_at'] = date('c');
$found = true;
break;
}
}
if ($found) { save_index($idx); append_event(['type'=>'task_done', 'task_id'=>$id, 'ts'=>date('c')]); }
echo json_encode(['ok'=>$found, 'id'=>$id]);
exit;
}
if ($action === 'resolve_alert') {
$id = $_GET['id'] ?? $_POST['id'] ?? '';
$idx = load_index();
$found = false;
foreach ($idx['alerts'] ?? [] as &$a) {
if (($a['id'] ?? '') === $id) {
$a['status'] = 'resolved';
$a['resolved_at'] = date('c');
$found = true;
break;
}
}
if ($found) { save_index($idx); append_event(['type'=>'alert_resolved', 'alert_id'=>$id, 'ts'=>date('c')]); }
echo json_encode(['ok'=>$found, 'id'=>$id]);
exit;
}
// NEW v3.1: stats_timeline — daily aggregation for charts
if ($action === 'stats_timeline') {
global $EVENTS_FILE;
if (!file_exists($EVENTS_FILE)) { echo json_encode(['ok'=>true, 'timeline'=>[]]); exit; }
$lines = file($EVENTS_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$byDay = [];
foreach ($lines as $l) {
$ev = @json_decode($l, true);
if (!$ev || empty($ev['ts'])) continue;
$day = substr($ev['ts'], 0, 10);
if (!isset($byDay[$day])) $byDay[$day] = ['date'=>$day, 'ingests'=>0, 'tasks_done'=>0, 'alerts_resolved'=>0];
if (($ev['type'] ?? '') === 'ingest') $byDay[$day]['ingests']++;
elseif (($ev['type'] ?? '') === 'task_done') $byDay[$day]['tasks_done']++;
elseif (($ev['type'] ?? '') === 'alert_resolved') $byDay[$day]['alerts_resolved']++;
}
ksort($byDay);
echo json_encode(['ok'=>true, 'timeline'=>array_values($byDay)]);
exit;
}
// NEW v3.1: delete item (with cascade to tasks/alerts/reco)
if ($action === 'delete_item') {
$id = $_GET['id'] ?? $_POST['id'] ?? '';
$idx = load_index();
if (!isset($idx['drill_index'][$id])) { echo json_encode(['ok'=>false,'error'=>'not found']); exit; }
$removed = $idx['drill_index'][$id];
unset($idx['drill_index'][$id]);
$type = $removed['type'] ?? 'unknown';
if (isset($idx['by_type'][$type]) && $idx['by_type'][$type] > 0) $idx['by_type'][$type]--;
$idx['total_items']--;
// Cascade: remove related tasks, alerts, opportunities
$idx['tasks'] = array_values(array_filter($idx['tasks'] ?? [], function($t) use ($id) { return ($t['source_item'] ?? '') !== $id; }));
$idx['alerts'] = array_values(array_filter($idx['alerts'] ?? [], function($a) use ($id) { return ($a['source_item'] ?? '') !== $id; }));
$idx['opportunities'] = array_values(array_filter($idx['opportunities'] ?? [], function($o) use ($id) { return ($o['source_item'] ?? '') !== $id; }));
// Also remove from entities
foreach ($idx['entities'] ?? [] as $cat => &$list) {
if (is_array($list)) {
$list = array_values(array_filter($list, function($e) use ($id) { return !(is_array($e) && ($e['source'] ?? '') === $id); }));
}
}
// Delete physical file if photo
if ($type === 'photo' && !empty($removed['path']) && file_exists($removed['path'])) @unlink($removed['path']);
save_index($idx);
append_event(['type'=>'delete', 'item_id'=>$id, 'ts'=>date('c')]);
echo json_encode(['ok'=>true, 'deleted'=>$id]);
exit;
}
// NEW v3.1: search across all items
if ($action === 'search') {
$q = trim($_GET['q'] ?? $_POST['q'] ?? '');
if (strlen($q) < 2) { echo json_encode(['ok'=>false, 'error'=>'query too short']); exit; }
$idx = load_index();
$matches = [];
foreach ($idx['drill_index'] ?? [] as $item) {
$hay = strtolower(($item['text_sample'] ?? '') . ' ' . ($item['ocr'] ?? '') . ' ' . json_encode($item['entities'] ?? []));
if (strpos($hay, strtolower($q)) !== false) {
$matches[] = ['id'=>$item['id'], 'type'=>$item['type'], 'ingested_at'=>$item['ingested_at'],
'preview'=>substr($item['text_sample'] ?? '', 0, 200)];
}
}
echo json_encode(['ok'=>true, 'query'=>$q, 'matches'=>$matches, 'total'=>count($matches)]);
exit;
}
if ($action === 'shortcut_manifest') {
echo json_encode([
'ok' => true, 'version' => '3.0',
'ok' => true, 'version' => '3.1',
'endpoint' => 'https://weval-consulting.com/api/wevia-apple-ingest.php',
'downloads' => [
'photos' => 'https://weval-consulting.com/downloads/wevia-shortcut-photos.json',
'messages' => 'https://weval-consulting.com/downloads/wevia-shortcut-messages.json',
'contacts' => 'https://weval-consulting.com/downloads/wevia-shortcut-contacts.json',
'calendar' => 'https://weval-consulting.com/downloads/wevia-shortcut-calendar.json',
'notes' => 'https://weval-consulting.com/downloads/wevia-shortcut-notes.json',
'calls' => 'https://weval-consulting.com/downloads/wevia-shortcut-calls.json',
'health' => 'https://weval-consulting.com/downloads/wevia-shortcut-health.json'
],
'actions' => [
'photos' => ['endpoint_action' => 'ingest_photo', 'method' => 'POST multipart', 'field' => 'file'],
'messages' => ['endpoint_action' => 'ingest_structured', 'body' => ['type'=>'message', 'items'=>[['from'=>'', 'to'=>'', 'body'=>'', 'date'=>'']]]],
@@ -353,4 +490,4 @@ if ($action === 'shortcut_manifest') {
exit;
}
echo json_encode(['ok'=>false, 'error'=>'unknown action', 'available'=>['status','ingest_photo','ingest_structured','drill','list','recommendations','entities','tasks','alerts','shortcut_manifest']]);
echo json_encode(['ok'=>false, 'error'=>'unknown action', 'available'=>['status','ingest_photo','ingest_structured','drill','list','recommendations','entities','tasks','alerts','mark_done','resolve_alert','stats_timeline','delete_item','search','shortcut_manifest']]);

View File

@@ -1,3 +1,4 @@
{"type":"ingest","item_id":"message_69e5824ba35bd6.09795740","data_type":"message","ts":"2026-04-20T01:32:59+00:00"}
{"type":"ingest","item_id":"contact_69e5824bdbea77.22631491","data_type":"contact","ts":"2026-04-20T01:32:59+00:00"}
{"type":"ingest","item_id":"calendar_69e5824c2569b2.34050670","data_type":"calendar","ts":"2026-04-20T01:33:00+00:00"}
{"type":"ingest","item_id":"message_69e583ec4a8c63.43728183","data_type":"message","ts":"2026-04-20T01:39:56+00:00"}

View File

@@ -1,8 +1,8 @@
{
"total_items": 3,
"total_items": 4,
"by_type": {
"photo": 0,
"message": 1,
"message": 2,
"contact": 1,
"calendar": 1,
"note": 0,
@@ -30,6 +30,10 @@
{
"val": "25000 USD",
"source": "calendar_69e5824c2569b2.34050670"
},
{
"val": "32000 EUR",
"source": "message_69e583ec4a8c63.43728183"
}
],
"deadlines": [
@@ -40,6 +44,10 @@
{
"val": "2026-04-22",
"source": "calendar_69e5824c2569b2.34050670"
},
{
"val": "demain",
"source": "message_69e583ec4a8c63.43728183"
}
],
"locations": [],
@@ -55,6 +63,10 @@
{
"val": "olga.vanurina@vistex.com",
"source": "contact_69e5824bdbea77.22631491"
},
{
"val": "yacineutt@gmail.com",
"source": "message_69e583ec4a8c63.43728183"
}
],
"phones": [
@@ -89,6 +101,14 @@
{
"val": "x.com",
"source": "contact_69e5824bdbea77.22631491"
},
{
"val": "teams",
"source": "message_69e583ec4a8c63.43728183"
},
{
"val": "gmail",
"source": "message_69e583ec4a8c63.43728183"
}
],
"oss": [
@@ -106,7 +126,19 @@
}
]
},
"tasks": [],
"tasks": [
{
"kind": "task_create",
"priority": "P0",
"label": "Créer tâche pour échéance: demain",
"action": "Ajouter à Calendar\/Reminders avec contexte: Olga Vanurina -> yacineutt@gmail.com\nURGENT: Vistex addendum signing deadline demain. Budget 32000 EUR confirmé. Appel ",
"source": "message_69e583ec4a8c63.43728183",
"id": "reco_69e583ec4b0830.98454851",
"source_item": "message_69e583ec4a8c63.43728183",
"created_at": "2026-04-20T01:39:56+00:00",
"status": "open"
}
],
"opportunities": [
{
"kind": "tech_research",
@@ -137,9 +169,31 @@
"label": "Créer tâche pour échéance: 2026-04-22",
"action": "Ajouter à Calendar\/Reminders avec contexte: CRITICAL: Board meeting with Dr. Ray Wu\nHuawei Cloud HQ\nLitige billing review - 25000 USD at stake - urgent\n2026-04-22T1",
"source": "calendar_69e5824c2569b2.34050670"
},
{
"kind": "task_create",
"priority": "P0",
"label": "Créer tâche pour échéance: demain",
"action": "Ajouter à Calendar\/Reminders avec contexte: Olga Vanurina -> yacineutt@gmail.com\nURGENT: Vistex addendum signing deadline demain. Budget 32000 EUR confirmé. Appel ",
"source": "message_69e583ec4a8c63.43728183",
"id": "reco_69e583ec4b0830.98454851",
"source_item": "message_69e583ec4a8c63.43728183",
"created_at": "2026-04-20T01:39:56+00:00",
"status": "open"
},
{
"kind": "urgent_alert",
"priority": "P0",
"label": "Item urgent — traitement immédiat",
"action": "Telegram @wevia_cyber_bot",
"source": "message_69e583ec4a8c63.43728183",
"id": "reco_69e583ec4b0a35.50995876",
"source_item": "message_69e583ec4a8c63.43728183",
"created_at": "2026-04-20T01:39:56+00:00",
"status": "open"
}
],
"last_update": "2026-04-20T01:33:00+00:00",
"last_update": "2026-04-20T01:39:56+00:00",
"drill_index": {
"message_69e5824ba35bd6.09795740": {
"id": "message_69e5824ba35bd6.09795740",
@@ -346,6 +400,71 @@
"source": "calendar_69e5824c2569b2.34050670"
}
]
},
"message_69e583ec4a8c63.43728183": {
"id": "message_69e583ec4a8c63.43728183",
"type": "message",
"raw": {
"from": "Olga Vanurina",
"to": "yacineutt@gmail.com",
"body": "URGENT: Vistex addendum signing deadline demain. Budget 32000 EUR confirmé. Appel 15h via Teams."
},
"text_sample": "Olga Vanurina -> yacineutt@gmail.com\nURGENT: Vistex addendum signing deadline demain. Budget 32000 EUR confirmé. Appel 15h via Teams.",
"entities": {
"people": [],
"orgs": [],
"money": [
"32000 EUR"
],
"deadlines": [
"demain"
],
"locations": [],
"emails": [
"yacineutt@gmail.com"
],
"phones": [],
"urls": [],
"apps": [
"teams",
"gmail"
],
"keywords": [],
"sentiment": "neutral",
"urgency": "high",
"oss": []
},
"ingested_at": "2026-04-20T01:39:56+00:00",
"recommendations": [
{
"kind": "task_create",
"priority": "P0",
"label": "Créer tâche pour échéance: demain",
"action": "Ajouter à Calendar\/Reminders avec contexte: Olga Vanurina -> yacineutt@gmail.com\nURGENT: Vistex addendum signing deadline demain. Budget 32000 EUR confirmé. Appel ",
"source": "message_69e583ec4a8c63.43728183"
},
{
"kind": "finance_track",
"priority": "P1",
"label": "Montant(s) détecté(s): 32000 EUR",
"action": "Vérifier facture\/devis et lier CRM WEVAL",
"source": "message_69e583ec4a8c63.43728183"
},
{
"kind": "contact_capture",
"priority": "P2",
"label": "1 email(s), 0 phone(s)",
"action": "Sync iPhone Contacts + CRM",
"source": "message_69e583ec4a8c63.43728183"
},
{
"kind": "urgent_alert",
"priority": "P0",
"label": "Item urgent — traitement immédiat",
"action": "Telegram @wevia_cyber_bot",
"source": "message_69e583ec4a8c63.43728183"
}
]
}
}
}