Update: 2026-07-04 20:53:30
This commit is contained in:
@@ -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
|
||||
|
||||
+16
-2
@@ -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}")
|
||||
|
||||
+55
-13
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user