273 lines
13 KiB
PHP
273 lines
13 KiB
PHP
<?php
|
|
// ============================================================
|
|
// social_worker.php
|
|
// Endpoint for the Android Social Media Bot
|
|
// ============================================================
|
|
|
|
require_once __DIR__ . '/../core/bootstrap.php';
|
|
require_once __DIR__ . '/../functions.php';
|
|
|
|
// مصادقة البوت.
|
|
// كان جسم الشرط معطّلاً بالتعليق، فكان الفحص بلا أثر والنقطة مفتوحة على
|
|
// الإنترنت: أي أحد يستطيع سحب المهام والكتابة في social_logs وقراءة الحسابات.
|
|
// القيمة الافتراضية هي نفسها المبنية حالياً في APK (BuildConfig.BOT_TOKEN)
|
|
// حتى لا ينكسر البوت الميداني؛ غيّرها في الطرفين معاً عبر SOCIAL_BOT_TOKEN.
|
|
$headers = getallheaders();
|
|
$botToken = $headers['X-Bot-Token'] ?? '';
|
|
$expectedToken = getenv('SOCIAL_BOT_TOKEN') ?: 'YOUR_SECRET_BOT_TOKEN';
|
|
if (!hash_equals($expectedToken, $botToken)) {
|
|
http_response_code(401);
|
|
exit(json_encode(['status' => 'error', 'message' => 'Unauthorized']));
|
|
}
|
|
|
|
$action = $_GET['action'] ?? '';
|
|
|
|
try {
|
|
$con = Database::get('main');
|
|
|
|
switch ($action) {
|
|
case 'get_task':
|
|
// The bot asks for a task to do
|
|
$platform = $_GET['platform'] ?? 'facebook';
|
|
$requestedAccountId = $_GET['account_id'] ?? null;
|
|
$deviceId = $_GET['device_id'] ?? null;
|
|
|
|
// Automatic Plug & Play Device Binding
|
|
if ($deviceId) {
|
|
$stmtDev = $con->prepare("SELECT id FROM social_accounts WHERE device_id = ?");
|
|
$stmtDev->execute([$deviceId]);
|
|
$acc = $stmtDev->fetch(PDO::FETCH_ASSOC);
|
|
if ($acc) {
|
|
$requestedAccountId = $acc['id'];
|
|
} else {
|
|
require_once __DIR__ . '/account_manager.php';
|
|
$am = new AccountManager();
|
|
// Find an account that doesn't have a device_id yet
|
|
$stmtFind = $con->prepare("SELECT id FROM social_accounts WHERE platform = ? AND device_id IS NULL AND status = 'active' LIMIT 1");
|
|
$stmtFind->execute([$platform]);
|
|
$newAcc = $stmtFind->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($newAcc) {
|
|
$requestedAccountId = $newAcc['id'];
|
|
$updateDev = $con->prepare("UPDATE social_accounts SET device_id = ? WHERE id = ?");
|
|
$updateDev->execute([$deviceId, $requestedAccountId]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'No available unassigned accounts for this new device']);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find a pending task that is scheduled for now or earlier
|
|
if ($requestedAccountId) {
|
|
// If the phone is strictly tied to one account, fetch a task specifically for it
|
|
// Or fetch an unassigned task and assign it to this phone's account
|
|
$stmt = $con->prepare("
|
|
SELECT id, account_id, type, target_url, prompt_context, generated_comment
|
|
FROM social_tasks
|
|
WHERE status = 'pending'
|
|
AND platform = ?
|
|
AND (account_id = ? OR account_id IS NULL)
|
|
AND (scheduled_at IS NULL OR scheduled_at <= NOW())
|
|
ORDER BY created_at ASC LIMIT 1
|
|
");
|
|
$stmt->execute([$platform, $requestedAccountId]);
|
|
} else {
|
|
$stmt = $con->prepare("
|
|
SELECT id, account_id, type, target_url, prompt_context, generated_comment
|
|
FROM social_tasks
|
|
WHERE status = 'pending' AND platform = ? AND (scheduled_at IS NULL OR scheduled_at <= NOW())
|
|
ORDER BY created_at ASC LIMIT 1
|
|
");
|
|
$stmt->execute([$platform]);
|
|
}
|
|
$task = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($task) {
|
|
require_once __DIR__ . '/account_manager.php';
|
|
$am = new AccountManager();
|
|
|
|
$accountId = $task['account_id'] ?: $requestedAccountId;
|
|
$account = null;
|
|
|
|
if ($accountId) {
|
|
// Fetch this specific account details
|
|
$stmtAcc = $con->prepare("
|
|
SELECT id, username, proxy_ip, proxy_port, proxy_username, proxy_password
|
|
FROM social_accounts WHERE id = ?
|
|
");
|
|
$stmtAcc->execute([$accountId]);
|
|
$account = $stmtAcc->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($account && !$task['account_id']) {
|
|
$updateTask = $con->prepare("UPDATE social_tasks SET account_id = ? WHERE id = ?");
|
|
$updateTask->execute([$accountId, $task['id']]);
|
|
$task['account_id'] = $accountId;
|
|
}
|
|
} else {
|
|
// Dynamically get an available account and assign it
|
|
$account = $am->getAvailableAccount($platform, 15); // 15 min cooldown
|
|
if ($account) {
|
|
$accountId = $account['id'];
|
|
$updateTask = $con->prepare("UPDATE social_tasks SET account_id = ? WHERE id = ?");
|
|
$updateTask->execute([$accountId, $task['id']]);
|
|
$task['account_id'] = $accountId;
|
|
}
|
|
}
|
|
|
|
if (!$account) {
|
|
echo json_encode(['status' => 'error', 'message' => 'No active social account available or in cooldown']);
|
|
break;
|
|
}
|
|
|
|
if ($account) {
|
|
$task['bot_username'] = $account['username'];
|
|
$task['proxy_ip'] = $account['proxy_ip'];
|
|
$task['proxy_port'] = $account['proxy_port'];
|
|
$task['proxy_username'] = $account['proxy_username'];
|
|
$task['proxy_password'] = $account['proxy_password'];
|
|
}
|
|
|
|
// Mark as in progress and update last active
|
|
$update = $con->prepare("UPDATE social_tasks SET status = 'in_progress' WHERE id = ?");
|
|
$update->execute([$task['id']]);
|
|
|
|
$am->markAccountActive($accountId);
|
|
|
|
// Return task directly in 'data' to preserve compatibility with existing Android bots
|
|
echo json_encode([
|
|
'status' => 'success',
|
|
'data' => $task
|
|
]);
|
|
} else {
|
|
echo json_encode(['status' => 'success', 'message' => 'No tasks available', 'data' => null]);
|
|
}
|
|
case 'process_organic_post':
|
|
// The bot found a post organically via Accessibility, copied its link, and needs a comment NOW
|
|
$platform = $_POST['platform'] ?? 'facebook';
|
|
$deviceId = $_POST['device_id'] ?? null;
|
|
$targetUrl = $_POST['target_url'] ?? null;
|
|
$postText = $_POST['post_text'] ?? '';
|
|
|
|
if (!$deviceId || !$targetUrl || !$postText) {
|
|
echo json_encode(['status' => 'error', 'message' => 'device_id, target_url, and post_text are required']);
|
|
break;
|
|
}
|
|
|
|
// 0. Duplicate Check (Prevent commenting on the same post twice)
|
|
$stmtCheck = $con->prepare("SELECT id FROM social_tasks WHERE target_url = ?");
|
|
$stmtCheck->execute([$targetUrl]);
|
|
if ($stmtCheck->fetch()) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Post already processed by another device']);
|
|
break;
|
|
}
|
|
|
|
// 1. Resolve Device ID
|
|
$stmtDev = $con->prepare("SELECT id FROM social_accounts WHERE device_id = ?");
|
|
$stmtDev->execute([$deviceId]);
|
|
$acc = $stmtDev->fetch(PDO::FETCH_ASSOC);
|
|
$accountId = $acc ? $acc['id'] : null;
|
|
|
|
if (!$accountId) {
|
|
// Not registered yet, auto-bind
|
|
require_once __DIR__ . '/account_manager.php';
|
|
$am = new AccountManager();
|
|
$stmtFind = $con->prepare("SELECT id FROM social_accounts WHERE platform = ? AND device_id IS NULL AND status = 'active' LIMIT 1");
|
|
$stmtFind->execute([$platform]);
|
|
$newAcc = $stmtFind->fetch(PDO::FETCH_ASSOC);
|
|
if ($newAcc) {
|
|
$accountId = $newAcc['id'];
|
|
$updateDev = $con->prepare("UPDATE social_accounts SET device_id = ? WHERE id = ?");
|
|
$updateDev->execute([$deviceId, $accountId]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'No accounts available for new device']);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 2. Generate Immediate Comment
|
|
require_once __DIR__ . '/gemini_comment_generator.php';
|
|
// Use soft promotion since the app isn't fully launched
|
|
$generatedComment = generateCommentWithGemini($postText, 'soft_promote_siro');
|
|
|
|
// 3. Save this initial organic action to database as completed
|
|
$stmt = $con->prepare("
|
|
INSERT INTO social_tasks (account_id, platform, type, target_url, prompt_context, generated_comment, status, completed_at)
|
|
VALUES (?, ?, 'post_comment', ?, ?, ?, 'completed', NOW())
|
|
");
|
|
$stmt->execute([$accountId, $platform, $targetUrl, $postText, $generatedComment]);
|
|
|
|
// 4. Schedule Drama (Supporting Accounts)
|
|
require_once __DIR__ . '/schedule_manager.php';
|
|
$sm = new ScheduleManager();
|
|
$sm->scheduleDialogueDrama($platform, $targetUrl, $postText, $accountId, 5); // 5 mins delay
|
|
|
|
// 5. Return the generated comment immediately to the Android Bot
|
|
echo json_encode([
|
|
'status' => 'success',
|
|
'data' => [
|
|
'generated_comment' => $generatedComment,
|
|
'target_url' => $targetUrl
|
|
]
|
|
]);
|
|
break;
|
|
|
|
case 'complete_task':
|
|
// The bot reports task completion
|
|
$taskId = $_POST['task_id'] ?? null;
|
|
$result = $_POST['result'] ?? ''; // e.g. success or error details
|
|
|
|
if ($taskId) {
|
|
$stmt = $con->prepare("UPDATE social_tasks SET status = 'completed', completed_at = NOW() WHERE id = ?");
|
|
$stmt->execute([$taskId]);
|
|
|
|
// Log it
|
|
$log = $con->prepare("INSERT INTO social_logs (task_id, log_level, message) VALUES (?, 'info', ?)");
|
|
$log->execute([$taskId, "Task completed: " . substr($result, 0, 200)]);
|
|
|
|
echo json_encode(['status' => 'success']);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'task_id required']);
|
|
}
|
|
break;
|
|
|
|
case 'fail_task':
|
|
// The bot reports task failure
|
|
$taskId = $_POST['task_id'] ?? null;
|
|
$errorMsg = $_POST['error_message'] ?? 'Unknown error';
|
|
|
|
if ($taskId) {
|
|
$stmt = $con->prepare("UPDATE social_tasks SET status = 'failed', error_message = ? WHERE id = ?");
|
|
$stmt->execute([$errorMsg, $taskId]);
|
|
|
|
// Log it
|
|
$log = $con->prepare("INSERT INTO social_logs (task_id, log_level, message) VALUES (?, 'error', ?)");
|
|
$log->execute([$taskId, "Task failed: " . substr($errorMsg, 0, 200)]);
|
|
|
|
echo json_encode(['status' => 'success']);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'task_id required']);
|
|
}
|
|
break;
|
|
|
|
case 'log':
|
|
// The bot sends a general log
|
|
$accountId = $_POST['account_id'] ?? null;
|
|
$level = $_POST['level'] ?? 'info';
|
|
$message = $_POST['message'] ?? '';
|
|
|
|
$log = $con->prepare("INSERT INTO social_logs (account_id, log_level, message) VALUES (?, ?, ?)");
|
|
$log->execute([$accountId, $level, $message]);
|
|
|
|
echo json_encode(['status' => 'success']);
|
|
break;
|
|
|
|
default:
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid action']);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
echo json_encode(['status' => 'error', 'message' => 'Database error: ' . $e->getMessage()]);
|
|
}
|