fix(bots): توجيه السوشال بوت لنقطة النهاية الصحيحة — التعليق العضوي كان ميتاً
السبب الجذري: 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4d2beaae5e
commit
ecfe756849
@@ -17,6 +17,9 @@ android {
|
|||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
buildConfigField("String", "BOT_SECRET_KEY", "\"SIRO_BOT_SUPER_SECRET_123\"")
|
buildConfigField("String", "BOT_SECRET_KEY", "\"SIRO_BOT_SUPER_SECRET_123\"")
|
||||||
|
// مضيف الباك إند في مكان واحد: تبديله عند نسخ البوت لعلامة أخرى
|
||||||
|
// (مثل انطلق على api.intaleqapp.com) يصير سطراً واحداً لا بحثاً في الكود.
|
||||||
|
buildConfigField("String", "BACKEND_HOST", "\"https://jordan-siro.intaleqapp.com\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ class WorkerClient(private val context: Context) {
|
|||||||
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) ?: "UNKNOWN_DEVICE"
|
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) ?: "UNKNOWN_DEVICE"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change this to your actual server domain
|
// المضيف من BuildConfig.BACKEND_HOST (app/build.gradle.kts) — لا يُشفَّر هنا،
|
||||||
private val BASE_URL = "https://jordan-siro.intaleqapp.com/backend/bot/standalone_worker.php"
|
// حتى يكون تبديله عند نسخ البوت لعلامة أخرى سطراً واحداً في gradle.
|
||||||
// For local testing use: "http://10.0.2.2:8000/standalone_worker.php"
|
// للتجريب المحلي: اضبط 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 {
|
private fun generateSignature(deviceId: String, ts: Long): String {
|
||||||
val message = "$deviceId$ts"
|
val message = "$deviceId$ts"
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ android {
|
|||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
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 {
|
buildTypes {
|
||||||
@@ -35,6 +42,7 @@ android {
|
|||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
|
buildConfig = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,58 @@
|
|||||||
package com.siro.socialmedia_bot.network
|
package com.siro.socialmedia_bot.network
|
||||||
|
|
||||||
|
import com.siro.socialmedia_bot.BuildConfig
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.net.URLEncoder
|
||||||
import java.util.Scanner
|
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 {
|
object SocialBotClient {
|
||||||
private const val BASE_URL = "https://jordan-siro.intaleqapp.com/backend/marketing_engine/index.php"
|
// `val` لا `const val`: قيم BuildConfig ليست ثوابت وقت ترجمة بنظر كوتلن،
|
||||||
private const val BOT_TOKEN = "YOUR_SECRET_BOT_TOKEN"
|
// و`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? {
|
fun getTask(platform: String): JSONObject? {
|
||||||
try {
|
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")
|
android.util.Log.d("SocialBotClient", "Requesting URL: $urlString")
|
||||||
|
|
||||||
val url = URL(urlString)
|
val url = URL(urlString)
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "GET"
|
connection.requestMethod = "GET"
|
||||||
connection.setRequestProperty("X-Bot-Token", BOT_TOKEN)
|
connection.setRequestProperty("X-Bot-Token", BOT_TOKEN)
|
||||||
|
|
||||||
val responseCode = connection.responseCode
|
val responseCode = connection.responseCode
|
||||||
android.util.Log.d("SocialBotClient", "Response Code: $responseCode")
|
android.util.Log.d("SocialBotClient", "Response Code: $responseCode")
|
||||||
|
|
||||||
if (responseCode == 200) {
|
if (responseCode == 200) {
|
||||||
val scanner = Scanner(connection.inputStream)
|
val scanner = Scanner(connection.inputStream)
|
||||||
val response = if (scanner.hasNext()) scanner.useDelimiter("\\A").next() else ""
|
val response = if (scanner.hasNext()) scanner.useDelimiter("\\A").next() else ""
|
||||||
scanner.close()
|
scanner.close()
|
||||||
|
|
||||||
android.util.Log.d("SocialBotClient", "Response Body: $response")
|
android.util.Log.d("SocialBotClient", "Response Body: $response")
|
||||||
|
|
||||||
val json = JSONObject(response)
|
val json = JSONObject(response)
|
||||||
if (json.getString("status") == "success" && !json.isNull("data")) {
|
if (json.getString("status") == "success" && !json.isNull("data")) {
|
||||||
return json.getJSONObject("data")
|
return json.getJSONObject("data")
|
||||||
@@ -51,12 +71,17 @@ object SocialBotClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun completeTask(taskId: Int, result: String) {
|
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<String>, platform: String = "facebook"): JSONObject? {
|
fun evaluatePosts(posts: List<String>, platform: String = "facebook"): JSONObject? {
|
||||||
try {
|
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 url = URL(urlString)
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "POST"
|
connection.requestMethod = "POST"
|
||||||
@@ -80,9 +105,12 @@ object SocialBotClient {
|
|||||||
scanner.close()
|
scanner.close()
|
||||||
|
|
||||||
val json = JSONObject(response)
|
val json = JSONObject(response)
|
||||||
if (json.getString("status") == "success" && !json.isNull("data")) {
|
if (json.getString("status") == "success") {
|
||||||
return json.getJSONObject("data")
|
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) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("SocialBotClient", "Exception in evaluatePosts: ${e.message}")
|
android.util.Log.e("SocialBotClient", "Exception in evaluatePosts: ${e.message}")
|
||||||
@@ -91,17 +119,20 @@ object SocialBotClient {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** يخاطب [WORKER_URL] — `index.php` لا يعرف هذا الأمر إطلاقاً. */
|
||||||
fun processOrganicPost(deviceId: String, platform: String, postText: String, targetUrl: String): JSONObject? {
|
fun processOrganicPost(deviceId: String, platform: String, postText: String, targetUrl: String): JSONObject? {
|
||||||
try {
|
try {
|
||||||
val urlString = "$BASE_URL?action=process_organic_post"
|
val urlString = "$WORKER_URL?action=process_organic_post"
|
||||||
val url = URL(urlString)
|
val url = URL(urlString)
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "POST"
|
connection.requestMethod = "POST"
|
||||||
connection.setRequestProperty("X-Bot-Token", BOT_TOKEN)
|
connection.setRequestProperty("X-Bot-Token", BOT_TOKEN)
|
||||||
|
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
||||||
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")}"
|
|
||||||
|
val params = "device_id=${enc(deviceId)}&platform=${enc(platform)}" +
|
||||||
|
"&post_text=${enc(postText)}&target_url=${enc(targetUrl)}"
|
||||||
connection.doOutput = true
|
connection.doOutput = true
|
||||||
|
|
||||||
val writer = OutputStreamWriter(connection.outputStream)
|
val writer = OutputStreamWriter(connection.outputStream)
|
||||||
writer.write(params)
|
writer.write(params)
|
||||||
writer.flush()
|
writer.flush()
|
||||||
@@ -117,6 +148,9 @@ object SocialBotClient {
|
|||||||
if (json.getString("status") == "success" && !json.isNull("data")) {
|
if (json.getString("status") == "success" && !json.isNull("data")) {
|
||||||
return json.getJSONObject("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) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("SocialBotClient", "Exception in processOrganicPost: ${e.message}")
|
android.util.Log.e("SocialBotClient", "Exception in processOrganicPost: ${e.message}")
|
||||||
@@ -126,29 +160,33 @@ object SocialBotClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun failTask(taskId: Int, errorMessage: String) {
|
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) {
|
fun logMessage(accountId: Int?, level: String, message: String) {
|
||||||
val accIdParam = accountId?.let { "&account_id=$it" } ?: ""
|
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 {
|
try {
|
||||||
val url = URL("$BASE_URL?$actionParam")
|
val url = URL("$endpoint?$actionParam")
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "POST"
|
connection.requestMethod = "POST"
|
||||||
connection.setRequestProperty("X-Bot-Token", BOT_TOKEN)
|
connection.setRequestProperty("X-Bot-Token", BOT_TOKEN)
|
||||||
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
||||||
connection.doOutput = true
|
connection.doOutput = true
|
||||||
|
|
||||||
val writer = OutputStreamWriter(connection.outputStream)
|
val writer = OutputStreamWriter(connection.outputStream)
|
||||||
writer.write(postBody)
|
writer.write(postBody)
|
||||||
writer.flush()
|
writer.flush()
|
||||||
writer.close()
|
writer.close()
|
||||||
|
|
||||||
connection.responseCode // execute
|
val responseCode = connection.responseCode
|
||||||
|
if (responseCode != 200) {
|
||||||
|
android.util.Log.e("SocialBotClient", "$actionParam HTTP $responseCode")
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user