Update: 2026-07-06 00:18:03
This commit is contained in:
@@ -91,6 +91,40 @@ object SocialBotClient {
|
||||
return null
|
||||
}
|
||||
|
||||
fun processOrganicPost(deviceId: String, platform: String, postText: String, targetUrl: String): JSONObject? {
|
||||
try {
|
||||
val urlString = "$BASE_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.doOutput = true
|
||||
|
||||
val writer = OutputStreamWriter(connection.outputStream)
|
||||
writer.write(params)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == 200) {
|
||||
val scanner = Scanner(connection.inputStream)
|
||||
val response = if (scanner.hasNext()) scanner.useDelimiter("\\A").next() else ""
|
||||
scanner.close()
|
||||
|
||||
val json = JSONObject(response)
|
||||
if (json.getString("status") == "success" && !json.isNull("data")) {
|
||||
return json.getJSONObject("data")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("SocialBotClient", "Exception in processOrganicPost: ${e.message}")
|
||||
e.printStackTrace()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun failTask(taskId: Int, errorMessage: String) {
|
||||
postData("action=fail_task", "task_id=$taskId&error_message=$errorMessage")
|
||||
}
|
||||
|
||||
+48
-36
@@ -99,54 +99,72 @@ class FacebookBotService : AccessibilityService() {
|
||||
navigator.openApp()
|
||||
delay(3000)
|
||||
|
||||
val allCollectedTexts = mutableSetOf<String>()
|
||||
val totalIterations = 20 // 20 iterations * ~15 sec = ~5 minutes of scrolling
|
||||
val deviceId = android.provider.Settings.Secure.getString(contentResolver, android.provider.Settings.Secure.ANDROID_ID)
|
||||
val totalIterations = 20
|
||||
var iterations = 0
|
||||
var consecutiveNoNewPosts = 0
|
||||
var lastProcessedPostText = ""
|
||||
|
||||
while (iterations < totalIterations) {
|
||||
Log.d(TAG, "Autonomous Iteration: ${iterations + 1} of $totalIterations")
|
||||
val initialSize = allCollectedTexts.size
|
||||
|
||||
// 1. Expand texts
|
||||
commentReader.expandPostText()
|
||||
delay(1000)
|
||||
|
||||
// 2. Read feed posts
|
||||
val posts = commentReader.extractPostsAndComments().map { "[FEED]: $it" }.toMutableList()
|
||||
val posts = commentReader.extractPostsAndComments()
|
||||
val currentPost = posts.firstOrNull { it.length > 20 && it != lastProcessedPostText }
|
||||
|
||||
// 3. Open comments if available
|
||||
if (commentReader.openComments()) {
|
||||
Log.d(TAG, "Opened comments section. Reading...")
|
||||
delay(4000) // wait for bottom sheet to load
|
||||
if (currentPost != null) {
|
||||
Log.d(TAG, "Found valid post: \n$currentPost")
|
||||
lastProcessedPostText = currentPost
|
||||
consecutiveNoNewPosts = 0
|
||||
|
||||
// 4. Expand long comments
|
||||
commentReader.expandPostText()
|
||||
delay(1000)
|
||||
// 3. Attempt to copy link
|
||||
val linkCopied = navigator.copyPostLink()
|
||||
|
||||
// 5. Read comments
|
||||
val comments = commentReader.extractPostsAndComments().map { "[COMMENT]: $it" }
|
||||
posts.addAll(comments)
|
||||
|
||||
// 6. Go back to feed
|
||||
Log.d(TAG, "Going back to main feed...")
|
||||
navigator.goBack()
|
||||
delay(3000)
|
||||
}
|
||||
|
||||
// Add to our global collection (Set avoids duplicates)
|
||||
allCollectedTexts.addAll(posts)
|
||||
Log.d(TAG, "Collected ${posts.size} texts in this iteration. Total unique so far: ${allCollectedTexts.size}")
|
||||
|
||||
if (allCollectedTexts.size == initialSize) {
|
||||
if (linkCopied) {
|
||||
// 4. Get copied link from clipboard
|
||||
val clipboardManager = getSystemService(android.content.Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||
val clipboardText = clipboardManager.primaryClip?.getItemAt(0)?.text?.toString() ?: ""
|
||||
|
||||
if (clipboardText.contains("http", ignoreCase = true)) {
|
||||
Log.d(TAG, "Copied Link: $clipboardText")
|
||||
|
||||
// 5. Send to Server for organic processing
|
||||
Log.d(TAG, "Sending to server for organic processing...")
|
||||
val responseData = SocialBotClient.processOrganicPost(deviceId, "facebook", currentPost, clipboardText)
|
||||
|
||||
if (responseData != null && responseData.has("generated_comment")) {
|
||||
val comment = responseData.getString("generated_comment")
|
||||
Log.d(TAG, "Server returned comment: $comment")
|
||||
|
||||
// 6. Post the comment
|
||||
if (commentReader.openComments()) {
|
||||
delay(3000)
|
||||
val posted = commentPoster.postComment(comment)
|
||||
if (posted) {
|
||||
Log.d(TAG, "Successfully posted organic comment!")
|
||||
SocialBotClient.logMessage(null, "info", "Successfully posted organic comment on: $clipboardText")
|
||||
}
|
||||
navigator.goBack() // Close comments
|
||||
delay(2000)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Copied text was not a URL: $clipboardText")
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Failed to copy link for this post.")
|
||||
}
|
||||
} else {
|
||||
consecutiveNoNewPosts++
|
||||
Log.d(TAG, "No new posts found in this iteration. (Streak: $consecutiveNoNewPosts)")
|
||||
if (consecutiveNoNewPosts >= 3) {
|
||||
Log.d(TAG, "No new posts for 3 consecutive iterations. Reached bottom or stuck. Breaking early.")
|
||||
Log.d(TAG, "No new posts for 3 consecutive iterations. Breaking early.")
|
||||
break
|
||||
}
|
||||
} else {
|
||||
consecutiveNoNewPosts = 0
|
||||
}
|
||||
|
||||
Log.d(TAG, "Scrolling down for more posts...")
|
||||
@@ -155,13 +173,7 @@ class FacebookBotService : AccessibilityService() {
|
||||
iterations++
|
||||
}
|
||||
|
||||
// Send everything to Gemini AT ONCE to save API costs and generate a comprehensive report
|
||||
if (allCollectedTexts.isNotEmpty()) {
|
||||
Log.d(TAG, "Finished scrolling. Sending ${allCollectedTexts.size} total texts to Gemini for one comprehensive report...")
|
||||
SocialBotClient.evaluatePosts(allCollectedTexts.toList())
|
||||
}
|
||||
|
||||
SocialBotClient.completeTask(task.id, "Autonomous session completed. Scraped ${allCollectedTexts.size} items.")
|
||||
SocialBotClient.completeTask(task.id, "Autonomous session completed.")
|
||||
}
|
||||
"read_posts" -> {
|
||||
// Logic to read posts and send to backend
|
||||
|
||||
+49
@@ -207,4 +207,53 @@ class FacebookNavigator(private val service: AccessibilityService) {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun copyPostLink(): Boolean {
|
||||
val root = service.rootInActiveWindow ?: return false
|
||||
|
||||
// 1. Click "Share" (مشاركة)
|
||||
val shareNodes = mutableListOf<android.view.accessibility.AccessibilityNodeInfo>()
|
||||
findNodesByText(root, "مشاركة", shareNodes)
|
||||
findNodesByText(root, "Share", shareNodes)
|
||||
|
||||
if (shareNodes.isEmpty()) {
|
||||
android.util.Log.d("FacebookNavigator", "Share button not found")
|
||||
return false
|
||||
}
|
||||
|
||||
// Click the first valid Share button
|
||||
val shareBtn = shareNodes.first()
|
||||
shareBtn.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK)
|
||||
kotlinx.coroutines.delay(2000) // Wait for bottom sheet
|
||||
|
||||
// 2. Look for "Copy Link" (نسخ الرابط) in the bottom sheet
|
||||
val newRoot = service.rootInActiveWindow ?: return false
|
||||
val copyNodes = mutableListOf<android.view.accessibility.AccessibilityNodeInfo>()
|
||||
findNodesByText(newRoot, "نسخ الرابط", copyNodes)
|
||||
findNodesByText(newRoot, "Copy link", copyNodes)
|
||||
|
||||
if (copyNodes.isEmpty()) {
|
||||
android.util.Log.d("FacebookNavigator", "Copy Link button not found in sheet")
|
||||
goBack() // close sheet
|
||||
return false
|
||||
}
|
||||
|
||||
// Click Copy Link
|
||||
val copyBtn = copyNodes.first()
|
||||
copyBtn.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK)
|
||||
kotlinx.coroutines.delay(1000)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun findNodesByText(node: android.view.accessibility.AccessibilityNodeInfo?, text: String, list: MutableList<android.view.accessibility.AccessibilityNodeInfo>) {
|
||||
if (node == null) return
|
||||
val nodeText = (node.text?.toString() ?: "") + " " + (node.contentDescription?.toString() ?: "")
|
||||
if (nodeText.contains(text, ignoreCase = true)) {
|
||||
list.add(node)
|
||||
}
|
||||
for (i in 0 until node.childCount) {
|
||||
findNodesByText(node.getChild(i), text, list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user