230 lines
8.3 KiB
PHP
230 lines
8.3 KiB
PHP
<?php
|
|
/**
|
|
* Cron script to publish scheduled posts
|
|
* Run this every minute: * * * * * php /path/to/cron_publish.php
|
|
*/
|
|
|
|
// Set timezone to Moscow
|
|
date_default_timezone_set('Europe/Moscow');
|
|
|
|
// Prevent web access
|
|
if (php_sapi_name() !== 'cli' && !isset($_GET['cron_key'])) {
|
|
// Allow web access with secret key for hosts without cron
|
|
$configFile = __DIR__ . '/config.php';
|
|
if (file_exists($configFile)) {
|
|
$config = require $configFile;
|
|
$cronKey = $config['cron_key'] ?? '';
|
|
if (empty($cronKey) || $_GET['cron_key'] !== $cronKey) {
|
|
http_response_code(403);
|
|
die('Access denied');
|
|
}
|
|
} else {
|
|
http_response_code(403);
|
|
die('Access denied');
|
|
}
|
|
}
|
|
|
|
// Load config - must assign to $config variable
|
|
$config = require __DIR__ . '/config.php';
|
|
require_once __DIR__ . '/classes/VKAPI.php';
|
|
require_once __DIR__ . '/classes/TelegramBot.php';
|
|
|
|
// Log file for debugging
|
|
$logFile = __DIR__ . '/data/cron_log.txt';
|
|
function logMessage($msg) {
|
|
global $logFile;
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
file_put_contents($logFile, "[$timestamp] $msg\n", FILE_APPEND);
|
|
echo $msg . "\n";
|
|
}
|
|
|
|
$scheduledFile = __DIR__ . '/data/scheduled_posts.json';
|
|
|
|
if (!file_exists($scheduledFile)) {
|
|
logMessage("No scheduled posts file");
|
|
exit;
|
|
}
|
|
|
|
$posts = json_decode(file_get_contents($scheduledFile), true) ?: [];
|
|
$now = time();
|
|
$updated = false;
|
|
|
|
logMessage("Starting cron run. Now: " . date('Y-m-d H:i:s', $now) . " (" . $now . ")");
|
|
|
|
$pendingCount = count(array_filter($posts, fn($p) => $p['status'] === 'pending'));
|
|
logMessage("Found $pendingCount pending posts");
|
|
|
|
foreach ($posts as &$post) {
|
|
// Skip if not pending
|
|
if ($post['status'] !== 'pending') {
|
|
continue;
|
|
}
|
|
|
|
$scheduledTime = strtotime($post['scheduled_time']);
|
|
logMessage("Post {$post['id']}: scheduled for " . date('Y-m-d H:i:s', $scheduledTime) . " ($scheduledTime)");
|
|
|
|
if ($scheduledTime > $now) {
|
|
$diff = $scheduledTime - $now;
|
|
logMessage(" -> Not yet time (in $diff seconds)");
|
|
continue;
|
|
}
|
|
|
|
logMessage(" -> Time to publish!");
|
|
|
|
// Prepare text with tags
|
|
$baseText = $post['text'] ?? '';
|
|
$tags = $post['tags'] ?? [];
|
|
if (!empty($tags)) {
|
|
$tagsString = implode(' ', array_map(function($t) { return '#' . $t; }, $tags));
|
|
$baseText = $baseText ? $baseText . "\n\n" . $tagsString : $tagsString;
|
|
}
|
|
|
|
// Collect all photo URLs
|
|
$photoUrls = $post['photos'] ?? [];
|
|
$uploadedFiles = $post['uploaded_files'] ?? [];
|
|
foreach ($uploadedFiles as $file) {
|
|
if (!empty($file['url'])) {
|
|
$photoUrls[] = $file['url'];
|
|
}
|
|
}
|
|
|
|
logMessage(" Photos: " . count($photoUrls));
|
|
|
|
// Check if cross-promo was enabled for this post
|
|
$crossPromoEnabled = $post['cross_promo'] ?? false;
|
|
logMessage(" Cross-promo enabled: " . ($crossPromoEnabled ? 'yes' : 'no'));
|
|
|
|
// Load cross-promo settings if enabled
|
|
$crossPromoFile = __DIR__ . '/data/cross_promo.json';
|
|
$crossPromo = [];
|
|
if ($crossPromoEnabled && file_exists($crossPromoFile)) {
|
|
$crossPromo = json_decode(file_get_contents($crossPromoFile), true) ?: [];
|
|
}
|
|
$hasCrossPromo = $crossPromoEnabled && (!empty($crossPromo['telegramLink']) || !empty($crossPromo['vkLink']));
|
|
|
|
// Check which platforms we're posting to
|
|
$platforms = $post['platforms'] ?? [];
|
|
$postingToTelegram = false;
|
|
$postingToVk = false;
|
|
foreach ($platforms as $p) {
|
|
$pType = $p['type'] ?? $p;
|
|
if ($pType === 'telegram') $postingToTelegram = true;
|
|
if ($pType === 'vk') $postingToVk = true;
|
|
}
|
|
|
|
// Prepare platform-specific texts with cross-promo
|
|
$textForTelegram = $baseText;
|
|
$textForVk = $baseText;
|
|
|
|
if ($hasCrossPromo) {
|
|
// Add VK link to Telegram posts
|
|
if (!empty($crossPromo['vkLink']) && $postingToTelegram) {
|
|
$linkText = $crossPromo['textForTg'] ?? 'Мой канал ВКонтакте';
|
|
$textForTelegram .= "\n\n<a href=\"{$crossPromo['vkLink']}\">{$linkText}</a>";
|
|
logMessage(" Cross-promo: VK link added to TG text");
|
|
}
|
|
// Add Telegram link to VK posts
|
|
if (!empty($crossPromo['telegramLink']) && $postingToVk) {
|
|
$linkText = $crossPromo['textForVk'] ?? 'Мой канал в Telegram';
|
|
$textForVk .= "\n\n{$linkText}: {$crossPromo['telegramLink']}";
|
|
logMessage(" Cross-promo: TG link added to VK text");
|
|
}
|
|
}
|
|
|
|
$results = [];
|
|
|
|
logMessage(" Platforms: " . json_encode($platforms));
|
|
logMessage(" TG token set: " . (!empty($config['telegram']['bot_token']) ? 'yes' : 'NO'));
|
|
logMessage(" VK token set: " . (!empty($config['vk']['access_token']) ? 'yes' : 'NO'));
|
|
|
|
if (empty($platforms)) {
|
|
logMessage(" WARNING: No platforms specified!");
|
|
}
|
|
|
|
foreach ($platforms as $platform) {
|
|
$type = $platform['type'] ?? $platform;
|
|
$target = $platform['target'] ?? '';
|
|
|
|
logMessage(" Processing platform: $type, target: $target");
|
|
|
|
if ($type === 'telegram') {
|
|
if (empty($config['telegram']['bot_token'])) {
|
|
logMessage(" Telegram: SKIPPED - no bot token in config");
|
|
continue;
|
|
}
|
|
try {
|
|
$telegram = new TelegramBot($config['telegram']['bot_token']);
|
|
// Get first channel if no target specified
|
|
if (empty($target)) {
|
|
$channels = $telegram->getChannels();
|
|
logMessage(" Telegram channels: " . count($channels));
|
|
if (!empty($channels)) {
|
|
$target = $channels[0]['id'];
|
|
}
|
|
}
|
|
if ($target) {
|
|
logMessage(" Posting to Telegram channel: $target");
|
|
$result = $telegram->post($target, $photoUrls, $textForTelegram, 'HTML');
|
|
$results['telegram'] = ['success' => true, 'result' => $result];
|
|
logMessage(" Telegram: OK");
|
|
} else {
|
|
$results['telegram'] = ['success' => false, 'error' => 'No target channel'];
|
|
logMessage(" Telegram: No target channel");
|
|
}
|
|
} catch (Exception $e) {
|
|
$results['telegram'] = ['success' => false, 'error' => $e->getMessage()];
|
|
logMessage(" Telegram: ERROR - {$e->getMessage()}");
|
|
}
|
|
}
|
|
|
|
if ($type === 'vk') {
|
|
if (empty($config['vk']['access_token'])) {
|
|
logMessage(" VK: SKIPPED - no access token in config");
|
|
continue;
|
|
}
|
|
try {
|
|
$vk = new VKAPI($config['vk']['access_token']);
|
|
// Get first group if no target specified
|
|
if (empty($target)) {
|
|
$validation = $vk->validateToken();
|
|
if ($validation['valid'] && ($validation['type'] ?? '') === 'community') {
|
|
$target = $validation['user_id'];
|
|
} else {
|
|
$groups = $vk->getGroups();
|
|
if (!empty($groups)) {
|
|
$target = $groups[0]['id'];
|
|
}
|
|
}
|
|
}
|
|
if ($target) {
|
|
logMessage(" Posting to VK group: $target");
|
|
$result = $vk->post($target, $photoUrls, strip_tags($textForVk));
|
|
$results['vk'] = ['success' => true, 'result' => $result];
|
|
logMessage(" VK: OK");
|
|
} else {
|
|
$results['vk'] = ['success' => false, 'error' => 'No target group'];
|
|
logMessage(" VK: No target group");
|
|
}
|
|
} catch (Exception $e) {
|
|
$results['vk'] = ['success' => false, 'error' => $e->getMessage()];
|
|
logMessage(" VK: ERROR - {$e->getMessage()}");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update post status
|
|
$post['status'] = 'published';
|
|
$post['published_at'] = date('Y-m-d H:i:s');
|
|
$post['results'] = $results;
|
|
$updated = true;
|
|
logMessage("Post {$post['id']} marked as published");
|
|
}
|
|
|
|
// Save updated posts
|
|
if ($updated) {
|
|
file_put_contents($scheduledFile, json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
logMessage("Posts file updated");
|
|
}
|
|
|
|
logMessage("Done\n---");
|