Update: 2026-07-05 01:32:30
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -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<String> {
|
||||
val root = service.rootInActiveWindow ?: return emptyList()
|
||||
val extractedTexts = mutableListOf<String>()
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-2
@@ -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 {
|
||||
|
||||
+45
-1
@@ -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$"))) {
|
||||
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<AccessibilityNodeInfo>()
|
||||
|
||||
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<AccessibilityNodeInfo>) {
|
||||
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
|
||||
|
||||
+7
@@ -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) {
|
||||
if (!seenDb.isPostSeen(validText)) {
|
||||
android.util.Log.d("ScrapedText", "Extracted: $validText")
|
||||
results.add(validText)
|
||||
seenDb.markPostAsSeen(validText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user