?

áEÓê¤ÎïLwebshll2019

Current Path : /home/w/e/b/webyoo/www/nathan-ai/site/
Upload File :
Current File : /home/w/e/b/webyoo/www/nathan-ai/site/publish.php

<?php
/**
 * publish.php — publie LE prochain post de la file sur LinkedIn.
 * Appelé par le cron (3x/jour). Publie 1 post par exécution.
 *
 * Usage manuel de test :  php publish.php
 */
require __DIR__ . '/lib.php';
$c = cfg();

// --- Sécurités de base ---
if (empty($c['access_token']) || empty($c['author_urn'])) {
    logline("ABANDON: access_token ou author_urn manquant dans config.php.");
    exit(1);
}

// --- Charger la file de posts ---
if (!file_exists($c['queue_file'])) { logline("ABANDON: posts.json introuvable."); exit(1); }
$posts = json_decode(file_get_contents($c['queue_file']), true);
if (!is_array($posts) || count($posts) === 0) { logline("ABANDON: posts.json vide ou invalide."); exit(1); }

// --- Charger l'état (index du prochain post) ---
$state = ['next' => 0];
if (file_exists($c['state_file'])) {
    $s = json_decode(file_get_contents($c['state_file']), true);
    if (is_array($s) && isset($s['next'])) $state = $s;
}

$idx = (int)$state['next'];

// Fin de file ?
if ($idx >= count($posts)) {
    if (!empty($c['loop_queue'])) {
        $idx = 0; // on recommence
    } else {
        logline("File terminée ($idx/" . count($posts) . "). Réalimente posts.json puis remets state.json à {\"next\":0}.");
        exit(0);
    }
}

// --- Le texte à publier ---
$post = $posts[$idx];
$commentary = is_array($post) ? ($post['text'] ?? '') : (string)$post;
$commentary = trim($commentary);
if ($commentary === '') { logline("Post #$idx vide, on saute."); $state['next'] = $idx + 1; file_put_contents($c['state_file'], json_encode($state)); exit(0); }

// --- Appel API LinkedIn (Posts API versionnée) ---
$payload = [
    'author'      => $c['author_urn'],
    'commentary'  => $commentary,
    'visibility'  => 'PUBLIC',
    'distribution'=> [
        'feedDistribution'             => 'MAIN_FEED',
        'targetEntities'               => [],
        'thirdPartyDistributionChannels'=> [],
    ],
    'lifecycleState'          => 'PUBLISHED',
    'isReshareDisabledByAuthor'=> false,
];

$headers = [
    'Authorization: Bearer ' . $c['access_token'],
    'Content-Type: application/json',
    'X-Restli-Protocol-Version: 2.0.0',
    'LinkedIn-Version: ' . $c['linkedin_version'],
];

list($code, $resp) = http_request('POST', 'https://api.linkedin.com/rest/posts', $headers, json_encode($payload));

if ($code >= 200 && $code < 300) {
    // LinkedIn renvoie l'ID du post dans l'en-tête x-restli-id, ici on log le succès simple.
    $preview = mb_substr(str_replace("\n", ' ', $commentary), 0, 70);
    logline("OK (#$idx) publié [$code] : \"$preview...\"");
    $state['next'] = $idx + 1;
    file_put_contents($c['state_file'], json_encode($state));
    exit(0);
} else {
    logline("ECHEC (#$idx) [$code] : $resp");
    // On n'incrémente PAS : le prochain cron réessaiera le même post.
    // Si erreur 401 -> jeton expiré (voir guide, renouvellement).
    exit(1);
}



web shell, Coded By 2019