From 73d4778dab730e6b641e2ed5a1edccac6fd7ec6b Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 5 Jul 2026 01:32:30 +0300 Subject: [PATCH] Update: 2026-07-05 01:32:30 --- backend/marketing_engine/cron_insert_task.php | 12 +++-- .../socialmedia_bot/data/SeenPostDatabase.kt | 51 +++++++++++++++++++ .../social/facebook/FacebookCommentReader.kt | 12 ++++- .../social/instagram/InstagramBotService.kt | 16 +++++- .../social/instagram/InstagramNavigator.kt | 50 ++++++++++++++++-- .../social/telegram/TelegramNavigator.kt | 11 +++- 6 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 socialBot/app/src/main/java/com/siro/socialmedia_bot/data/SeenPostDatabase.kt diff --git a/backend/marketing_engine/cron_insert_task.php b/backend/marketing_engine/cron_insert_task.php index 1ba9e9fb..c79e8bba 100644 --- a/backend/marketing_engine/cron_insert_task.php +++ b/backend/marketing_engine/cron_insert_task.php @@ -2,8 +2,12 @@ require_once __DIR__ . '/../core/bootstrap.php'; $con = Database::get('main'); -// Insert the autonomous_scroll_and_reply task -$stmt = $con->prepare("INSERT INTO marketing_tasks (platform, type, status) VALUES ('facebook', 'autonomous_scroll_and_reply', 'pending')"); -$stmt->execute(); +// Insert the autonomous_scroll_and_reply task for each platform +$platforms = ['facebook', 'instagram']; -echo "Task inserted successfully at " . date('Y-m-d H:i:s') . "\n"; +foreach ($platforms as $platform) { + $stmt = $con->prepare("INSERT INTO marketing_tasks (platform, type, status) VALUES (?, 'autonomous_scroll_and_reply', 'pending')"); + $stmt->execute([$platform]); +} + +echo "Tasks inserted successfully at " . date('Y-m-d H:i:s') . "\n"; diff --git a/socialBot/app/src/main/java/com/siro/socialmedia_bot/data/SeenPostDatabase.kt b/socialBot/app/src/main/java/com/siro/socialmedia_bot/data/SeenPostDatabase.kt new file mode 100644 index 00000000..54b02328 --- /dev/null +++ b/socialBot/app/src/main/java/com/siro/socialmedia_bot/data/SeenPostDatabase.kt @@ -0,0 +1,51 @@ +package com.siro.socialmedia_bot.data + +import android.content.ContentValues +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper +import java.security.MessageDigest + +class SeenPostDatabase(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { + + companion object { + private const val DATABASE_NAME = "SeenPostsDB" + private const val DATABASE_VERSION = 1 + private const val TABLE_SEEN_POSTS = "seen_posts" + private const val COLUMN_HASH = "hash" + } + + override fun onCreate(db: SQLiteDatabase) { + val createTable = "CREATE TABLE $TABLE_SEEN_POSTS ($COLUMN_HASH TEXT PRIMARY KEY)" + db.execSQL(createTable) + } + + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("DROP TABLE IF EXISTS $TABLE_SEEN_POSTS") + onCreate(db) + } + + fun isPostSeen(text: String): Boolean { + val hash = generateHash(text) + val db = this.readableDatabase + val cursor = db.rawQuery("SELECT 1 FROM $TABLE_SEEN_POSTS WHERE $COLUMN_HASH = ?", arrayOf(hash)) + val exists = cursor.count > 0 + cursor.close() + return exists + } + + fun markPostAsSeen(text: String) { + val hash = generateHash(text) + val db = this.writableDatabase + val values = ContentValues().apply { + put(COLUMN_HASH, hash) + } + db.insertWithOnConflict(TABLE_SEEN_POSTS, null, values, SQLiteDatabase.CONFLICT_IGNORE) + } + + private fun generateHash(input: String): String { + val md = MessageDigest.getInstance("SHA-256") + val digest = md.digest(input.toByteArray()) + return digest.fold("") { str, it -> str + "%02x".format(it) } + } +} diff --git a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/facebook/FacebookCommentReader.kt b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/facebook/FacebookCommentReader.kt index 4083c409..8a4edef9 100644 --- a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/facebook/FacebookCommentReader.kt +++ b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/facebook/FacebookCommentReader.kt @@ -3,8 +3,12 @@ package com.siro.socialmedia_bot.social.facebook import android.accessibilityservice.AccessibilityService import android.view.accessibility.AccessibilityNodeInfo +import com.siro.socialmedia_bot.data.SeenPostDatabase + class FacebookCommentReader(private val service: AccessibilityService) { + private val seenDb = SeenPostDatabase(service) + fun extractPostsAndComments(): List { val root = service.rootInActiveWindow ?: return emptyList() val extractedTexts = mutableListOf() @@ -32,8 +36,12 @@ class FacebookCommentReader(private val service: AccessibilityService) { val text = node.text?.toString() ?: node.contentDescription?.toString() if (!text.isNullOrBlank()) { - if (isValidPostText(text)) { - collectedTexts.add(text.trim()) + val trimmedText = text.trim() + if (isValidPostText(trimmedText)) { + if (!seenDb.isPostSeen(trimmedText)) { + collectedTexts.add(trimmedText) + seenDb.markPostAsSeen(trimmedText) + } } } diff --git a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramBotService.kt b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramBotService.kt index 6ea23e22..717289a3 100644 --- a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramBotService.kt +++ b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramBotService.kt @@ -110,13 +110,25 @@ class InstagramBotService : AccessibilityService() { // Add to our global collection (Set avoids duplicates) allCollectedTexts.addAll(posts) + + if (navigator.openComments()) { + Log.d(TAG, "Opened comments section...") + delay(3000) + val comments = navigator.extractPostsAndComments() + allCollectedTexts.addAll(comments) + + // Go back to the feed + performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) + delay(2000) + } + Log.d(TAG, "Collected ${posts.size} texts in this iteration. Total unique so far: ${allCollectedTexts.size}") if (allCollectedTexts.size == initialSize) { 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.") + if (consecutiveNoNewPosts >= 5) { + Log.d(TAG, "No new posts for 5 consecutive iterations. Reached bottom or stuck. Breaking early.") break } } else { diff --git a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramNavigator.kt b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramNavigator.kt index bff2ef21..76299cab 100644 --- a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramNavigator.kt +++ b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/instagram/InstagramNavigator.kt @@ -7,12 +7,16 @@ import android.os.Bundle import android.view.accessibility.AccessibilityNodeInfo import kotlinx.coroutines.delay +import com.siro.socialmedia_bot.data.SeenPostDatabase + /** * Handles navigation and interaction within the Instagram app. * Instagram's package name: com.instagram.android */ class InstagramNavigator(private val service: AccessibilityService) { + private val seenDb = SeenPostDatabase(service) + suspend fun openUrl(url: String) { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -110,9 +114,12 @@ class InstagramNavigator(private val service: AccessibilityService) { val validText = text ?: contentDesc // Filter out short UI elements, timestamps, likes, etc. - if (!validText.isNullOrEmpty() && validText.length > 20 && !validText.contains(Regex("^[0-9]+ likes$"))) { - android.util.Log.d("ScrapedText", "Extracted: $validText") - results.add(validText) + if (!validText.isNullOrEmpty() && validText.length >= 10 && !validText.contains(Regex("^[0-9]+ likes$"))) { + if (!seenDb.isPostSeen(validText)) { + android.util.Log.d("ScrapedText", "Extracted: $validText") + results.add(validText) + seenDb.markPostAsSeen(validText) + } } } @@ -133,6 +140,43 @@ class InstagramNavigator(private val service: AccessibilityService) { return null } + fun openComments(): Boolean { + val root = service.rootInActiveWindow ?: return false + val commentNodes = mutableListOf() + + findNodesByPartialText(root, "تعليق", commentNodes) + findNodesByPartialText(root, "Comment", commentNodes) + + for (node in commentNodes) { + if (node.isClickable && node.performAction(AccessibilityNodeInfo.ACTION_CLICK)) return true + + var parent = node.parent + var depth = 0 + while (parent != null && depth < 2) { + if (parent.isClickable && parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)) { + return true + } + parent = parent.parent + depth++ + } + } + return false + } + + private fun findNodesByPartialText(node: AccessibilityNodeInfo?, target: String, list: MutableList) { + if (node == null) return + if (!node.isVisibleToUser) return + + val textStr = node.text?.toString() ?: "" + val descStr = node.contentDescription?.toString() ?: "" + if (textStr.contains(target, ignoreCase = true) || descStr.contains(target, ignoreCase = true)) { + list.add(node) + } + for (i in 0 until node.childCount) { + findNodesByPartialText(node.getChild(i), target, list) + } + } + private fun findNodeByHint(node: AccessibilityNodeInfo?, hint: String): AccessibilityNodeInfo? { if (node == null) return null if (node.hintText?.toString()?.contains(hint, ignoreCase = true) == true) return node diff --git a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/telegram/TelegramNavigator.kt b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/telegram/TelegramNavigator.kt index 80825a2f..976a00ba 100644 --- a/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/telegram/TelegramNavigator.kt +++ b/socialBot/app/src/main/java/com/siro/socialmedia_bot/social/telegram/TelegramNavigator.kt @@ -7,12 +7,16 @@ import android.os.Bundle import android.view.accessibility.AccessibilityNodeInfo import kotlinx.coroutines.delay +import com.siro.socialmedia_bot.data.SeenPostDatabase + /** * Handles navigation and interaction within the Telegram app. * Telegram's package name: org.telegram.messenger */ class TelegramNavigator(private val service: AccessibilityService) { + private val seenDb = SeenPostDatabase(service) + suspend fun openApp() { val intent = service.packageManager.getLaunchIntentForPackage("org.telegram.messenger") if (intent != null) { @@ -82,8 +86,11 @@ class TelegramNavigator(private val service: AccessibilityService) { // Filter short words if (!validText.isNullOrEmpty() && validText.length > 20) { - android.util.Log.d("ScrapedText", "Extracted: $validText") - results.add(validText) + if (!seenDb.isPostSeen(validText)) { + android.util.Log.d("ScrapedText", "Extracted: $validText") + results.add(validText) + seenDb.markPostAsSeen(validText) + } } }