From ecfe756849768f919f0dd9d0418d896008796c11 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Mon, 27 Jul 2026 05:06:08 +0300 Subject: [PATCH] =?UTF-8?q?fix(bots):=20=D8=AA=D9=88=D8=AC=D9=8A=D9=87=20?= =?UTF-8?q?=D8=A7=D9=84=D8=B3=D9=88=D8=B4=D8=A7=D9=84=20=D8=A8=D9=88=D8=AA?= =?UTF-8?q?=20=D9=84=D9=86=D9=82=D8=B7=D8=A9=20=D8=A7=D9=84=D9=86=D9=87?= =?UTF-8?q?=D8=A7=D9=8A=D8=A9=20=D8=A7=D9=84=D8=B5=D8=AD=D9=8A=D8=AD=D8=A9?= =?UTF-8?q?=20=E2=80=94=20=D8=A7=D9=84=D8=AA=D8=B9=D9=84=D9=8A=D9=82=20?= =?UTF-8?q?=D8=A7=D9=84=D8=B9=D8=B6=D9=88=D9=8A=20=D9=83=D8=A7=D9=86=20?= =?UTF-8?q?=D9=85=D9=8A=D8=AA=D8=A7=D9=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit السبب الجذري: BASE_URL واحد يشير إلى marketing_engine/index.php، بينما `process_organic_post` و`log` موجودان في social_worker.php وحده. فكانا يقعان على `default` فيعود {"status":"error"} بكود HTTP 200 — بلا استثناء ولا سجل. أثره: processOrganicPost يعود null فيفشل شرط generated_comment في FacebookBotService:137 → لا تعليق عضوي يُنشر أبداً، وكل logMessage يُبتلع صامتاً فلم يظهر العطل في أي سجل. (تعليق العميل سطر 10 كان يذكر social_worker.php أصلاً — الرابط هو ما خالف النية.) - توجيه لكل أمر حسب موضعه: index.php لـ get_task/complete_task/fail_task/ evaluate_posts · social_worker.php لـ process_organic_post/log - evaluate_posts: index.php يرد بلا حقل `data`، والعميل كان يشترطه فيعود null في كل نجاح. النجاح صار يُقاس بـ status - ترميز URL لكل المعاملات: رسالة خطأ فيها & أو # كانت تشقّ جسم x-www-form-urlencoded وتفسد صفّ المهمة - كل رد غير 200 أو status != success يُسجَّل في Logcat بدل الصمت - المضيف إلى BuildConfig.BACKEND_HOST في البوتين (gradle) — لا نطاق مشفّر في الكود، فتبديله لأي علامة أخرى سطر واحد - val لا const val لقيم BuildConfig (ليست ثوابت وقت ترجمة في كوتلن) لم يُبنَ بعد. لم يُمسّ أي PHP: الباك إند حيّ. ما زال مفتوحاً بقرار المالك: (1) فحص X-Bot-Token معطَّل بتعليق في social_worker.php:14 و index.php:17 والتوكن placeholder → النقطتان مفتوحتان؛ (2) طابور social_tasks الذي يغذّيه schedule_manager بلا مستهلك. Co-Authored-By: Claude Opus 5 --- android_bot/app/build.gradle.kts | 3 + .../siro/android_bot/network/WorkerClient.kt | 8 +- socialBot/app/build.gradle.kts | 8 ++ .../network/SocialBotClient.kt | 86 +++++++++++++------ 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/android_bot/app/build.gradle.kts b/android_bot/app/build.gradle.kts index 6a283103..af210dfe 100644 --- a/android_bot/app/build.gradle.kts +++ b/android_bot/app/build.gradle.kts @@ -17,6 +17,9 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "BOT_SECRET_KEY", "\"SIRO_BOT_SUPER_SECRET_123\"") + // مضيف الباك إند في مكان واحد: تبديله عند نسخ البوت لعلامة أخرى + // (مثل انطلق على api.intaleqapp.com) يصير سطراً واحداً لا بحثاً في الكود. + buildConfigField("String", "BACKEND_HOST", "\"https://jordan-siro.intaleqapp.com\"") } buildTypes { diff --git a/android_bot/app/src/main/java/com/siro/android_bot/network/WorkerClient.kt b/android_bot/app/src/main/java/com/siro/android_bot/network/WorkerClient.kt index 852ac5d2..7c060ef8 100644 --- a/android_bot/app/src/main/java/com/siro/android_bot/network/WorkerClient.kt +++ b/android_bot/app/src/main/java/com/siro/android_bot/network/WorkerClient.kt @@ -25,9 +25,11 @@ class WorkerClient(private val context: Context) { Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) ?: "UNKNOWN_DEVICE" } - // Change this to your actual server domain - private val BASE_URL = "https://jordan-siro.intaleqapp.com/backend/bot/standalone_worker.php" - // For local testing use: "http://10.0.2.2:8000/standalone_worker.php" + // المضيف من BuildConfig.BACKEND_HOST (app/build.gradle.kts) — لا يُشفَّر هنا، + // حتى يكون تبديله عند نسخ البوت لعلامة أخرى سطراً واحداً في gradle. + // للتجريب المحلي: اضبط BACKEND_HOST على "http://10.0.2.2:8000" مع تقديم + // نفس المسار /backend/bot/ من جذر السيرفر المحلي. + private val BASE_URL = "${BuildConfig.BACKEND_HOST}/backend/bot/standalone_worker.php" private fun generateSignature(deviceId: String, ts: Long): String { val message = "$deviceId$ts" diff --git a/socialBot/app/build.gradle.kts b/socialBot/app/build.gradle.kts index b59ff402..83af3717 100644 --- a/socialBot/app/build.gradle.kts +++ b/socialBot/app/build.gradle.kts @@ -15,6 +15,13 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + // مضيف الباك إند في مكان واحد: تبديله عند نسخ البوت لعلامة أخرى + // (مثل انطلق على api.intaleqapp.com) يصير سطراً واحداً لا بحثاً في الكود. + buildConfigField("String", "BACKEND_HOST", "\"https://jordan-siro.intaleqapp.com\"") + // ⚠️ فحص هذا التوكن معطَّل على السيرفر حالياً (social_worker.php سطر 14 + // و marketing_engine/index.php سطر 17) — نقطتا النهاية مفتوحتان فعلياً. + buildConfigField("String", "BOT_TOKEN", "\"YOUR_SECRET_BOT_TOKEN\"") } buildTypes { @@ -35,6 +42,7 @@ android { } buildFeatures { viewBinding = true + buildConfig = true } } diff --git a/socialBot/app/src/main/java/com/siro/socialmedia_bot/network/SocialBotClient.kt b/socialBot/app/src/main/java/com/siro/socialmedia_bot/network/SocialBotClient.kt index 23d2f4af..390c593b 100644 --- a/socialBot/app/src/main/java/com/siro/socialmedia_bot/network/SocialBotClient.kt +++ b/socialBot/app/src/main/java/com/siro/socialmedia_bot/network/SocialBotClient.kt @@ -1,38 +1,58 @@ package com.siro.socialmedia_bot.network +import com.siro.socialmedia_bot.BuildConfig import org.json.JSONObject import java.io.OutputStreamWriter import java.net.HttpURLConnection import java.net.URL +import java.net.URLEncoder import java.util.Scanner /** - * Client to connect to the social_worker.php endpoint + * عميل الباك إند للبوت الاجتماعي. + * + * ⚠️ نقطتا نهاية لا واحدة — وخلط الاثنتين كان سبب تعطّل التعليق العضوي: + * + * - [ENGINE_URL] (`marketing_engine/index.php`) يخدم طابور `marketing_tasks` + * الذي تغذّيه `cron_insert_task.php` و`insert_autonomous_task.php` + * و`ContentWorkflow.php` بمهام `autonomous_scroll_and_reply`، + * ويملك `evaluate_posts` و`get_reports`. + * - [WORKER_URL] (`marketing_engine/social_worker.php`) يملك وحده + * `process_organic_post` (توليد التعليق عبر Gemini) و`log`. + * + * كان BASE_URL واحداً يشير إلى index.php، فكان `process_organic_post` + * و`log` يقعان على `default` فيعودان `{"status":"error"}` بكود 200 — + * أي **لا تعليق عضوي يُنشر أبداً وكل سجلات البوت تُبتلع صامتة**. */ object SocialBotClient { - private const val BASE_URL = "https://jordan-siro.intaleqapp.com/backend/marketing_engine/index.php" - private const val BOT_TOKEN = "YOUR_SECRET_BOT_TOKEN" + // `val` لا `const val`: قيم BuildConfig ليست ثوابت وقت ترجمة بنظر كوتلن، + // و`const` عليها لا يترجم. + private val ENGINE_URL = "${BuildConfig.BACKEND_HOST}/backend/marketing_engine/index.php" + private val WORKER_URL = "${BuildConfig.BACKEND_HOST}/backend/marketing_engine/social_worker.php" + private val BOT_TOKEN = BuildConfig.BOT_TOKEN + + private fun enc(value: String): String = URLEncoder.encode(value, "UTF-8") fun getTask(platform: String): JSONObject? { try { - val urlString = "$BASE_URL?action=get_task&platform=$platform" + val urlString = "$ENGINE_URL?action=get_task&platform=${enc(platform)}" android.util.Log.d("SocialBotClient", "Requesting URL: $urlString") - + val url = URL(urlString) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" connection.setRequestProperty("X-Bot-Token", BOT_TOKEN) - + val responseCode = connection.responseCode android.util.Log.d("SocialBotClient", "Response Code: $responseCode") - + if (responseCode == 200) { val scanner = Scanner(connection.inputStream) val response = if (scanner.hasNext()) scanner.useDelimiter("\\A").next() else "" scanner.close() - + android.util.Log.d("SocialBotClient", "Response Body: $response") - + val json = JSONObject(response) if (json.getString("status") == "success" && !json.isNull("data")) { return json.getJSONObject("data") @@ -51,12 +71,17 @@ object SocialBotClient { } fun completeTask(taskId: Int, result: String) { - postData("action=complete_task", "task_id=$taskId&result=$result") + postData(ENGINE_URL, "action=complete_task", "task_id=$taskId&result=${enc(result)}") } + /** + * ملاحظة عقد: `index.php` يرد على النجاح بـ`status`+`message` **بلا حقل + * `data`** (يحفظ التقرير في `marketing_reports` ويُشعر الأدمن عبر FCM). + * لذا النجاح يُقاس بـ`status` لا بوجود `data`، وإلا بدا كل تقرير ناجح فشلاً. + */ fun evaluatePosts(posts: List, platform: String = "facebook"): JSONObject? { try { - val urlString = "$BASE_URL?action=evaluate_posts&platform=$platform" + val urlString = "$ENGINE_URL?action=evaluate_posts&platform=${enc(platform)}" val url = URL(urlString) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "POST" @@ -80,9 +105,12 @@ object SocialBotClient { scanner.close() val json = JSONObject(response) - if (json.getString("status") == "success" && !json.isNull("data")) { - return json.getJSONObject("data") + if (json.getString("status") == "success") { + return if (!json.isNull("data")) json.getJSONObject("data") else json } + android.util.Log.e("SocialBotClient", "evaluatePosts rejected: $response") + } else { + android.util.Log.e("SocialBotClient", "evaluatePosts HTTP $responseCode") } } catch (e: Exception) { android.util.Log.e("SocialBotClient", "Exception in evaluatePosts: ${e.message}") @@ -91,17 +119,20 @@ object SocialBotClient { return null } + /** يخاطب [WORKER_URL] — `index.php` لا يعرف هذا الأمر إطلاقاً. */ fun processOrganicPost(deviceId: String, platform: String, postText: String, targetUrl: String): JSONObject? { try { - val urlString = "$BASE_URL?action=process_organic_post" + val urlString = "$WORKER_URL?action=process_organic_post" val url = URL(urlString) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.setRequestProperty("X-Bot-Token", BOT_TOKEN) - - val params = "device_id=$deviceId&platform=$platform&post_text=${java.net.URLEncoder.encode(postText, "UTF-8")}&target_url=${java.net.URLEncoder.encode(targetUrl, "UTF-8")}" + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded") + + val params = "device_id=${enc(deviceId)}&platform=${enc(platform)}" + + "&post_text=${enc(postText)}&target_url=${enc(targetUrl)}" connection.doOutput = true - + val writer = OutputStreamWriter(connection.outputStream) writer.write(params) writer.flush() @@ -117,6 +148,9 @@ object SocialBotClient { if (json.getString("status") == "success" && !json.isNull("data")) { return json.getJSONObject("data") } + android.util.Log.e("SocialBotClient", "processOrganicPost rejected: $response") + } else { + android.util.Log.e("SocialBotClient", "processOrganicPost HTTP $responseCode") } } catch (e: Exception) { android.util.Log.e("SocialBotClient", "Exception in processOrganicPost: ${e.message}") @@ -126,29 +160,33 @@ object SocialBotClient { } fun failTask(taskId: Int, errorMessage: String) { - postData("action=fail_task", "task_id=$taskId&error_message=$errorMessage") + postData(ENGINE_URL, "action=fail_task", "task_id=$taskId&error_message=${enc(errorMessage)}") } + /** يخاطب [WORKER_URL] — يكتب في `social_logs`؛ `index.php` لا يعرف `log`. */ fun logMessage(accountId: Int?, level: String, message: String) { val accIdParam = accountId?.let { "&account_id=$it" } ?: "" - postData("action=log", "level=$level&message=$message$accIdParam") + postData(WORKER_URL, "action=log", "level=${enc(level)}&message=${enc(message)}$accIdParam") } - private fun postData(actionParam: String, postBody: String) { + private fun postData(endpoint: String, actionParam: String, postBody: String) { try { - val url = URL("$BASE_URL?$actionParam") + val url = URL("$endpoint?$actionParam") val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.setRequestProperty("X-Bot-Token", BOT_TOKEN) connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded") connection.doOutput = true - + val writer = OutputStreamWriter(connection.outputStream) writer.write(postBody) writer.flush() writer.close() - - connection.responseCode // execute + + val responseCode = connection.responseCode + if (responseCode != 200) { + android.util.Log.e("SocialBotClient", "$actionParam HTTP $responseCode") + } } catch (e: Exception) { e.printStackTrace() }