Files
html/api/v96-linkedin-automation.php
2026-04-20 14:37:26 +02:00

140 lines
6.2 KiB
PHP

<?php
// V96 LinkedIn Automation Hub — auto-generate posts + stats tracking + queue
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$action = $_GET['action'] ?? 'overview';
$queue_file = '/opt/weval-l99/linkedin-post-queue.jsonl';
$published_file = '/opt/weval-l99/linkedin-published.jsonl';
$stats_file = '/opt/weval-l99/linkedin-page-stats.json';
// Ensure files exist
if (!file_exists($queue_file)) @file_put_contents($queue_file, '');
if (!file_exists($published_file)) @file_put_contents($published_file, '');
if (!file_exists($stats_file)) @file_put_contents($stats_file, json_encode([
'page' => 'Weval',
'followers' => 921,
'last_7d_new_followers' => 5,
'last_7d_search_appearances' => 176,
'last_7d_post_impressions' => 762,
'last_7d_page_visitors' => 15,
'pct_growth_search' => 87.2,
'pct_growth_followers' => 66.7,
'pct_growth_impressions' => 172.1,
'pct_growth_visitors' => 25.0,
'updated_at' => date('c'),
'source' => 'linkedin.com/company/69533182/admin/dashboard Yacine screenshot 20-avr'
]));
switch ($action) {
case 'overview':
$stats = json_decode(@file_get_contents($stats_file), true) ?: [];
$queue = [];
foreach (@file($queue_file) ?: [] as $l) { $e=@json_decode(trim($l),true); if($e)$queue[]=$e; }
$published = [];
foreach (@file($published_file) ?: [] as $l) { $e=@json_decode(trim($l),true); if($e)$published[]=$e; }
$pixel_tracker = @json_decode(@file_get_contents('http://localhost/api/v85-demo-tracker.php'), true) ?: [];
echo json_encode([
'v' => 'V96-linkedin-automation',
'ts' => date('c'),
'page_stats' => $stats,
'queue_pending' => count($queue),
'queue_preview' => array_slice($queue, -5),
'published_count' => count($published),
'recent_published' => array_slice($published, -3),
'pixel_hits_month' => $pixel_tracker['month_hits_total'] ?? 0,
'pixel_linkedin_referrer' => $pixel_tracker['linkedin_referrer_month'] ?? 0,
'kpis_to_push_score_10' => [
'posts_with_metric' => '71% -> 90% (add concrete numbers to 5 more posts)',
'avg_reach_30d' => '517 -> 800 (3-5 posts/week native video)',
'unique_proofs_cited' => '11 -> 15 (add 4 more client names: Abbott, Servier, OCP, Marjane)',
'linkedin_to_demo' => sprintf('%d -> 30 (pixel live)', $pixel_tracker['month_hits_total'] ?? 0),
],
], JSON_PRETTY_PRINT);
break;
case 'generate_post':
$theme = $_GET['theme'] ?? 'wevia_sovereign_ai';
// Generate post from sovereign Ollama llama3.2
$themes = [
'wevia_sovereign_ai' => 'Generate a LinkedIn post (150-200 words, FR, professional tone) about WEVIA sovereign AI. Include: 157K HCPs, 626 tools, 153/153 NR, 9.1/10 LinkedIn alignment score. Add hashtags #WEVAL #AI #SAP. Include at least 3 concrete numbers. End with CTA: demo request.',
'ethica_hcp' => 'Generate a LinkedIn post (150-200 words, FR) about WEVAL Ethica HCP platform. Include: 126K medecins, 109K emails verified 87%, 121K phones, 18 marques pharma, 34 specialites. Add #PharmaMarketing #HCP. Concrete numbers + CTA.',
'vistex_sap' => 'Generate LinkedIn post (150 words, FR) about WEVAL Vistex SAP partnership. Include: SAP Ecosystem Partner, Vistex lead protection, B2B enterprise. Hashtags #SAP #Vistex #Enterprise. Numbers + CTA.',
'case_study' => 'Generate LinkedIn case study post (200 words, FR) showcasing a WEVAL client success. Mention: 1.2M EUR waste eliminated, -40% delays, ROI 4 months. Hashtag #Lean6Sigma #CaseStudy. Include client name placeholder [CLIENT] + CTA demo.',
];
$prompt = $themes[$theme] ?? $themes['wevia_sovereign_ai'];
$ch = curl_init('http://127.0.0.1:11434/api/generate');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => json_encode([
'model' => 'llama3.2:latest',
'prompt' => $prompt,
'stream' => false,
'options' => ['num_predict' => 300, 'temperature' => 0.6],
]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_TIMEOUT => 60,
]);
$t0 = microtime(true);
$resp = curl_exec($ch);
$ms = round((microtime(true) - $t0) * 1000);
curl_close($ch);
$data = @json_decode($resp, true);
$post = trim($data['response'] ?? '');
// Metrics check
$metrics_found = preg_match_all('/\d+[K%]?|\d+\.\d+/', $post, $m);
$hashtags_found = preg_match_all('/#\w+/', $post, $h);
// Add to queue
$entry = [
'id' => 'v96-' . uniqid(),
'ts' => date('c'),
'theme' => $theme,
'post' => $post,
'chars' => strlen($post),
'metrics_count' => $metrics_found,
'hashtags_count' => $hashtags_found,
'status' => 'draft_queued',
'provider' => 'ollama-llama3.2-sovereign',
'cost_eur' => 0,
'latency_ms' => $ms,
];
@file_put_contents($queue_file, json_encode($entry) . "\n", FILE_APPEND);
echo json_encode($entry, JSON_PRETTY_PRINT);
break;
case 'queue':
$queue = [];
foreach (@file($queue_file) ?: [] as $l) { $e=@json_decode(trim($l),true); if($e)$queue[]=$e; }
echo json_encode(['ok' => true, 'count' => count($queue), 'queue' => $queue], JSON_PRETTY_PRINT);
break;
case 'clear_queue':
@file_put_contents($queue_file, '');
echo json_encode(['ok' => true, 'action' => 'queue_cleared']);
break;
case 'update_stats':
// Update LinkedIn page stats (manual feed from Yacine screenshot)
$new = [
'followers' => intval($_POST['followers'] ?? $_GET['followers'] ?? 921),
'last_7d_new_followers' => intval($_POST['new_followers'] ?? $_GET['new_followers'] ?? 5),
'last_7d_search_appearances' => intval($_POST['search'] ?? $_GET['search'] ?? 176),
'last_7d_post_impressions' => intval($_POST['impressions'] ?? $_GET['impressions'] ?? 762),
'last_7d_page_visitors' => intval($_POST['visitors'] ?? $_GET['visitors'] ?? 15),
'updated_at' => date('c'),
'source' => 'manual_update',
];
@file_put_contents($stats_file, json_encode($new, JSON_PRETTY_PRINT));
echo json_encode(['ok'=>true,'stats'=>$new]);
break;
default:
echo json_encode(['err'=>'unknown_action','available'=>['overview','generate_post','queue','clear_queue','update_stats']]);
}