Update: 2026-07-04 20:53:30

This commit is contained in:
Hamza-Ayed
2026-07-04 20:53:30 +03:00
parent 3c06b3624b
commit df4af1ac2b
15 changed files with 493 additions and 64 deletions
@@ -10,28 +10,41 @@ import java.util.Scanner
* Client to connect to the social_worker.php endpoint
*/
object SocialBotClient {
private const val BASE_URL = "https://your-domain.com/backend/bot/social_media_bot/social_worker.php"
private const val BASE_URL = "https://jordan-siro.intaleqapp.com/backend/marketing_engine/index.php"
private const val BOT_TOKEN = "YOUR_SECRET_BOT_TOKEN"
fun getTask(platform: String): JSONObject? {
try {
val url = URL("$BASE_URL?action=get_task&platform=$platform")
val urlString = "$BASE_URL?action=get_task&platform=$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 = scanner.useDelimiter("\\A").next()
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")
}
} else {
val scanner = Scanner(connection.errorStream)
val errorResponse = if (scanner.hasNext()) scanner.useDelimiter("\\A").next() else ""
scanner.close()
android.util.Log.e("SocialBotClient", "Error Response Body: $errorResponse")
}
} catch (e: Exception) {
android.util.Log.e("SocialBotClient", "Exception in getTask: ${e.message}")
e.printStackTrace()
}
return null
@@ -96,8 +96,22 @@ class FacebookBotService : AccessibilityService() {
}
"read_posts" -> {
// Logic to read posts and send to backend
// ...
SocialBotClient.completeTask(task.id, "Posts read successfully")
Log.d(TAG, "Starting to scan screen for posts/comments...")
val posts = commentReader.extractPostsAndComments()
if (posts.isNotEmpty()) {
Log.d(TAG, "===== FOUND ${posts.size} POSTS/COMMENTS =====")
for ((index, post) in posts.withIndex()) {
Log.d(TAG, "Post [${index + 1}]: $post")
}
Log.d(TAG, "===============================================")
// TODO: Send extracted posts to backend
SocialBotClient.completeTask(task.id, "Read ${posts.size} posts successfully")
} else {
Log.d(TAG, "No valid posts found on the screen.")
SocialBotClient.failTask(task.id, "No posts found")
}
}
else -> {
Log.w(TAG, "Unknown task type: ${task.type}")
@@ -5,30 +5,72 @@ import android.view.accessibility.AccessibilityNodeInfo
class FacebookCommentReader(private val service: AccessibilityService) {
fun extractComments(): List<String> {
fun extractPostsAndComments(): List<String> {
val root = service.rootInActiveWindow ?: return emptyList()
val comments = mutableListOf<String>()
val extractedTexts = mutableListOf<String>()
// Facebook UI is complex and changes often.
// This is a simplified logic. We'd usually look for nodes with text content inside a RecyclerView
findTextNodes(root, comments)
// Use a set to avoid duplicates as AccessibilityNodeInfo can be nested deeply
val uniqueTexts = mutableSetOf<String>()
findTextNodes(root, uniqueTexts)
return comments
extractedTexts.addAll(uniqueTexts)
return extractedTexts
}
private fun findTextNodes(node: AccessibilityNodeInfo?, comments: MutableList<String>) {
private fun findTextNodes(node: AccessibilityNodeInfo?, collectedTexts: MutableSet<String>) {
if (node == null) return
if (node.text != null && node.text.isNotEmpty()) {
// Add filtering logic to ensure it's a comment and not just UI text
val text = node.text.toString()
if (text.length > 10) { // arbitrary filter
comments.add(text)
val text = node.text?.toString() ?: node.contentDescription?.toString()
if (!text.isNullOrBlank()) {
if (isValidPostText(text)) {
collectedTexts.add(text.trim())
}
}
for (i in 0 until node.childCount) {
findTextNodes(node.getChild(i), comments)
findTextNodes(node.getChild(i), collectedTexts)
}
}
/**
* Filter out common Facebook UI elements and very short texts
* so we only send actual posts/comments to the backend.
*/
private fun isValidPostText(text: String): Boolean {
val trimmed = text.trim()
// 1. Ignore very short texts (less than 15 characters usually aren't full posts)
if (trimmed.length < 15) return false
// 2. Ignore common Facebook UI strings (English and Arabic)
val ignoreList = listOf(
"like", "comment", "share", "send", "write a comment",
"reply", "view more comments", "most relevant", "home",
"watch", "marketplace", "groups", "notifications", "menu",
"إعجاب", "تعليق", "مشاركة", "إرسال", "اكتب تعليقاً",
"رد", "عرض المزيد من التعليقات", "الأكثر صلة", "الصفحة الرئيسية",
"المجموعات", "الإشعارات", "القائمة", "Sponsored", "مُموَّل"
)
for (ignore in ignoreList) {
if (trimmed.equals(ignore, ignoreCase = true)) {
return false
}
}
// 3. Ignore texts that look like timestamps or metrics (e.g. "2 hrs", "10K Likes")
// Simplistic check: if it's short and ends with common time units
if (trimmed.length < 25) {
val timePatterns = listOf("hrs", "mins", "ساعات", "دقيقة", "دقائق", "أمس", "yesterday")
for (pattern in timePatterns) {
if (trimmed.contains(pattern, ignoreCase = true)) {
// It's likely a timestamp, not a post
return false
}
}
}
return true
}
}
@@ -58,7 +58,7 @@ class InstagramNavigator(private val service: AccessibilityService) {
private fun findNodeByHint(node: AccessibilityNodeInfo?, hint: String): AccessibilityNodeInfo? {
if (node == null) return null
if (node.hintText?.contains(hint, ignoreCase = true) == true) return node
if (node.hintText?.toString()?.contains(hint, ignoreCase = true) == true) return node
for (i in 0 until node.childCount) {
val found = findNodeByHint(node.getChild(i), hint)
if (found != null) return found
@@ -68,8 +68,8 @@ class InstagramNavigator(private val service: AccessibilityService) {
private fun findNodeByText(node: AccessibilityNodeInfo?, text: String): AccessibilityNodeInfo? {
if (node == null) return null
if (node.text?.equals(text, ignoreCase = true) == true ||
node.contentDescription?.equals(text, ignoreCase = true) == true) return node
if (node.text?.toString()?.equals(text, ignoreCase = true) == true ||
node.contentDescription?.toString()?.equals(text, ignoreCase = true) == true) return node
for (i in 0 until node.childCount) {
val found = findNodeByText(node.getChild(i), text)
if (found != null) return found