Files
wevads-platform/scripts/api_multichannel-bridge.php
2026-02-26 04:53:11 +01:00

143 lines
7.8 KiB
PHP
Executable File

<?php
/**
* MULTICHANNEL BRIDGE — Extends unified-send to trigger YT + Ads in parallel
* Called by: unified-send UI, N8N, or direct API
* Endpoint: :5821/api/multichannel-bridge.php
*/
require_once("/opt/wevads/config/credentials.php");
header('Content-Type: application/json');
$db = pg_connect("host=localhost dbname=adx_system user=admin password=".WEVADS_DB_PASS);
pg_query($db, "SET search_path TO admin");
$action = $_GET['action'] ?? $_POST['action'] ?? 'launch';
switch($action) {
case 'launch':
// Launch coordinated multichannel campaign
$name = pg_escape_string($db, $_POST['name'] ?? 'MC-'.date('YmdHis'));
$offer_link = pg_escape_string($db, $_POST['offer_link'] ?? '');
$angle = pg_escape_string($db, $_POST['content_angle'] ?? '');
$budget = (float)($_POST['ads_budget'] ?? 50);
$channels = json_decode($_POST['channels'] ?? '{}', true);
$results = ['campaign_name'=>$name, 'channels_launched'=>[]];
// 1. YouTube video generation (async)
if (!empty($channels['youtube'])) {
// Find or create trend
$trend_id = null;
if ($angle) {
$k = pg_escape_string($db, $angle);
pg_query($db, "INSERT INTO yt_trends(keyword,trend_score,country_code,source) VALUES('$k',99,'FR','manual_campaign') ON CONFLICT DO NOTHING");
$t = pg_fetch_assoc(pg_query($db, "SELECT id FROM yt_trends WHERE keyword='$k' ORDER BY id DESC LIMIT 1"));
$trend_id = $t['id'] ?? null;
}
if ($trend_id) {
// Generate script via HAMID
$prompt = urlencode("YouTube script about: $angle. Style: educational. 180s. French.");
$hamid = json_decode(@file_get_contents("http://127.0.0.1:5821/api/hamid-brain.php?action=generate&prompt=$prompt"), true);
$script = pg_escape_string($db, $hamid['response'] ?? $hamid['text'] ?? "Script about $angle");
$job_id = pg_fetch_result(pg_query($db, "SELECT gen_random_uuid()"), 0);
pg_query($db, "INSERT INTO yt_jobs(id, trend_id, script_text, title, status) VALUES('$job_id', $trend_id, '$script', '$k', 'pending')");
pg_query($db, "UPDATE yt_trends SET processed=true WHERE id=$trend_id");
$results['channels_launched']['youtube'] = ['job_id'=>$job_id, 'status'=>'queued_for_render'];
}
}
// 2. Social Ads campaign
if (!empty($channels['ads'])) {
$platform = $channels['ads']['platform'] ?? 'all';
$camp_id = pg_fetch_result(pg_query($db, "SELECT gen_random_uuid()"), 0);
pg_query($db, "INSERT INTO xc_campaigns(id,campaign_name,platform_target,offer_link,budget_daily,roas_target,status) VALUES('$camp_id','$name','$platform','$offer_link',$budget,".($channels['ads']['roas_target']??2.0).",'draft')");
$results['channels_launched']['ads'] = ['campaign_id'=>$camp_id, 'platform'=>$platform, 'budget'=>$budget];
}
// 3. Email (trigger existing unified-send)
if (!empty($channels['email'])) {
$email_config = $channels['email'];
$results['channels_launched']['email'] = [
'status'=>'ready',
'config_id'=>$email_config['config_id'] ?? null,
'list_id'=>$email_config['list_id'] ?? null,
'note'=>'Call brain_unified_send.php with these params'
];
}
// 4. SMS
if (!empty($channels['sms'])) {
$results['channels_launched']['sms'] = [
'status'=>'ready',
'provider'=>$channels['sms']['provider'] ?? 'twilio',
'note'=>'Call sms-api.php with campaign params'
];
}
// 5. Create cross-channel sync record
$yt_jid = $results['channels_launched']['youtube']['job_id'] ?? null;
$ads_cid = $results['channels_launched']['ads']['campaign_id'] ?? null;
if ($yt_jid || $ads_cid) {
pg_query($db, "INSERT INTO xc_content_sync(yt_job_id, ads_campaign_id, content_angle, sync_status) VALUES(".($yt_jid?"'$yt_jid'":"NULL").", ".($ads_cid?"'$ads_cid'":"NULL").", '$angle', 'synced')");
}
echo json_encode(['status'=>'success', 'multichannel'=>$results]);
break;
case 'sync_yt_to_ads':
// After YT video renders, auto-create ad creative from it
$yt_job_id = pg_escape_string($db, $_POST['yt_job_id'] ?? '');
$job = pg_fetch_assoc(pg_query($db, "SELECT * FROM yt_jobs WHERE id='$yt_job_id' AND status='rendered'"));
if (!$job) { echo json_encode(['error'=>'No rendered job']); break; }
// Create creative from YT content
$cid = pg_fetch_result(pg_query($db, "SELECT gen_random_uuid()"), 0);
$titles = "'{\"".pg_escape_string($db,$job['title'])."\",\"Decouvrez ".pg_escape_string($db,substr($job['title'],0,80))."\"}'";
$descs = "'{\"".pg_escape_string($db,substr($job['script_text']??'',0,200))."\"}'";
$media = $job['youtube_url'] ? "'{\"".pg_escape_string($db,$job['youtube_url'])."\"}'":"'{}'";
pg_query($db, "INSERT INTO ads_creatives(id,title_variations,description_variations,media_urls,media_type,source_yt_job_id,source_type) VALUES('$cid',$titles,$descs,$media,'video','$yt_job_id','youtube_auto')");
// Link to any pending ads campaign
$sync = pg_fetch_assoc(pg_query($db, "SELECT ads_campaign_id FROM xc_content_sync WHERE yt_job_id='$yt_job_id' AND ads_campaign_id IS NOT NULL LIMIT 1"));
if ($sync) {
pg_query($db, "UPDATE xc_campaigns SET creative_id='$cid', youtube_url='".pg_escape_string($db,$job['youtube_url']??'')."' WHERE id='{$sync['ads_campaign_id']}'");
}
echo json_encode(['status'=>'success', 'creative_id'=>$cid]);
break;
case 'performance':
// Cross-channel performance aggregation
$days = (int)($_GET['days'] ?? 7);
$yt = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='published') as pub, COALESCE(SUM(views_count),0) as views FROM yt_jobs WHERE created_at > NOW()-interval '$days days'"));
$ads = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as total, COALESCE(SUM(budget_spent),0) as spent, COALESCE(SUM(revenue_generated),0) as rev, COALESCE(SUM(conversions),0) as conv FROM xc_campaigns WHERE created_at > NOW()-interval '$days days'"));
$email = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as sent FROM unified_send_log WHERE created_at > NOW()-interval '$days days'"));
$arb = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE decision_type='kill') as kills, COUNT(*) FILTER(WHERE decision_type='boost') as boosts FROM ads_arbitrage_log WHERE executed_at > NOW()-interval '$days days'"));
$total_spent = (float)($ads['spent']??0);
$total_rev = (float)($ads['rev']??0);
echo json_encode(['status'=>'success','period_days'=>$days,'channels'=>[
'youtube'=>$yt, 'ads'=>$ads, 'email'=>['sent'=>$email['sent']??0], 'arbitrage'=>$arb
],'totals'=>['spent'=>$total_spent,'revenue'=>$total_rev,'roas'=>$total_spent>0?round($total_rev/$total_spent,2):0]]);
break;
case 'health':
// System health check
$checks = [];
$checks['db'] = pg_ping($db) ? 'ok' : 'fail';
$checks['ffmpeg'] = trim(shell_exec('which ffmpeg 2>/dev/null')) ? 'ok' : 'missing';
$checks['hamid'] = @file_get_contents('http://127.0.0.1:5821/api/hamid-brain.php?action=status') ? 'ok' : 'fail';
$checks['yt_pending'] = (int)pg_fetch_result(pg_query($db, "SELECT COUNT(*) FROM yt_jobs WHERE status='pending'"),0);
$checks['ads_active'] = (int)pg_fetch_result(pg_query($db, "SELECT COUNT(*) FROM xc_campaigns WHERE status IN ('active','boosted')"),0);
$checks['trends_today'] = (int)pg_fetch_result(pg_query($db, "SELECT COUNT(*) FROM yt_trends WHERE scraped_at::date=CURRENT_DATE"),0);
$checks['disk_free_gb'] = round(disk_free_space('/') / 1073741824, 1);
$overall = ($checks['db']==='ok' && $checks['ffmpeg']==='ok') ? 'healthy' : 'degraded';
echo json_encode(['status'=>$overall, 'checks'=>$checks, 'timestamp'=>date('c')]);
break;
}
?>