diff --git a/android_bot/.gradle/8.13/executionHistory/executionHistory.bin b/android_bot/.gradle/8.13/executionHistory/executionHistory.bin index 66eeb691..8292756c 100644 Binary files a/android_bot/.gradle/8.13/executionHistory/executionHistory.bin and b/android_bot/.gradle/8.13/executionHistory/executionHistory.bin differ diff --git a/android_bot/.gradle/8.13/executionHistory/executionHistory.lock b/android_bot/.gradle/8.13/executionHistory/executionHistory.lock index 1b3f1fd9..ccbc9fe7 100644 Binary files a/android_bot/.gradle/8.13/executionHistory/executionHistory.lock and b/android_bot/.gradle/8.13/executionHistory/executionHistory.lock differ diff --git a/android_bot/.gradle/8.13/fileHashes/fileHashes.bin b/android_bot/.gradle/8.13/fileHashes/fileHashes.bin index 80921cf5..9dc707e5 100644 Binary files a/android_bot/.gradle/8.13/fileHashes/fileHashes.bin and b/android_bot/.gradle/8.13/fileHashes/fileHashes.bin differ diff --git a/android_bot/.gradle/8.13/fileHashes/fileHashes.lock b/android_bot/.gradle/8.13/fileHashes/fileHashes.lock index 926142d7..42c78729 100644 Binary files a/android_bot/.gradle/8.13/fileHashes/fileHashes.lock and b/android_bot/.gradle/8.13/fileHashes/fileHashes.lock differ diff --git a/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin b/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin index e4f3e863..edff224e 100644 Binary files a/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin and b/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin differ diff --git a/android_bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android_bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 1df63993..b3ed1532 100644 Binary files a/android_bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/android_bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt b/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt index 18cdc66f..2403efa1 100644 --- a/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt +++ b/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt @@ -42,11 +42,6 @@ class ScraperAccessibilityService : AccessibilityService() { private val taxiFCollectedPrices = mutableListOf>() private var taxiFPriceScrollAttempts = 0 - // Multi-trip batch fields - private val taxiFMultiTripTrips = mutableListOf() - private var taxiFMultiTripIndex = 0 - private var taxiFPriceSubmitTime = 0L - companion object { private val TAXIF_EXCLUDED_TEXTS = setOf( "JOD", "ECO", "TaxiF", "SUV", "EV", "Mini", "Female", "Van", @@ -90,25 +85,11 @@ class ScraperAccessibilityService : AccessibilityService() { currentTask = task currentAppName = task.optString("app") val taskId = task.optString("task_id") - val taskType = task.optString("type", "price_check") - Log.i(TAG, "Received Task: $taskId for App: $currentAppName, Type: $taskType") + Log.i(TAG, "Received Task: $taskId for App: $currentAppName") taxiFPickupDone = false taxiFDestinationDone = false - taxiFMultiTripTrips.clear() - taxiFMultiTripIndex = 0 - taxiFPriceSubmitTime = 0L - - if (taskType == "batch_multi_trip") { - val tripsJson = task.optJSONArray("trips") - if (tripsJson != null) { - for (i in 0 until tripsJson.length()) { - taxiFMultiTripTrips.add(tripsJson.getJSONObject(i)) - } - } - Log.i(TAG, "TaxiF: Batch multi-trip loaded with ${taxiFMultiTripTrips.size} trips") - } currentState = BotState.LAUNCHING_APP // Launch the App @@ -384,26 +365,17 @@ class ScraperAccessibilityService : AccessibilityService() { val task = currentTask ?: return val taskId = task.optString("task_id") - val startLat: Double - val startLng: Double - val endLat: Double - val endLng: Double - - val tripCoords = getCurrentTripLatLng() - if (tripCoords != null) { - startLat = tripCoords.first[0] - startLng = tripCoords.first[1] - endLat = tripCoords.second[0] - endLng = tripCoords.second[1] - } else { - val payload = task.optJSONObject("payload") - startLat = payload?.optDouble("start_lat", 0.0) ?: 0.0 - startLng = payload?.optDouble("start_lng", 0.0) ?: 0.0 - endLat = payload?.optDouble("end_lat", 0.0) ?: 0.0 - endLng = payload?.optDouble("end_lng", 0.0) ?: 0.0 - } + // Extract nested payload object where coordinates reside + val payload = task.optJSONObject("payload") + val startLat = payload?.optDouble("start_lat", 0.0) ?: 0.0 + val startLng = payload?.optDouble("start_lng", 0.0) ?: 0.0 + val endLat = payload?.optDouble("end_lat", 0.0) ?: 0.0 + val endLng = payload?.optDouble("end_lng", 0.0) ?: 0.0 + // Calculate distance val distanceKm = calculateDistanceInKm(startLat, startLng, endLat, endLng) + + // Extract numeric digits from price val numericPrice = rawPrice.replace(Regex("[^0-9.]"), "").toDoubleOrNull() ?: 0.0 serviceScope.launch { @@ -424,6 +396,10 @@ class ScraperAccessibilityService : AccessibilityService() { } else { Log.e(TAG, "Failed to submit price.") } + + // Go back to IDLE + currentState = BotState.IDLE + currentTask = null } } @@ -510,10 +486,7 @@ class ScraperAccessibilityService : AccessibilityService() { private fun handleTaxiFAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) { val task = currentTask ?: return - // 3-second cool-down between multi-trips - if ((System.currentTimeMillis() - taxiFPriceSubmitTime) < 3000) return - Log.d(TAG, "TaxiF Automation event. State: $currentState" + - if (taxiFMultiTripTrips.isNotEmpty()) " Trip ${taxiFMultiTripIndex + 1}/${taxiFMultiTripTrips.size}" else "") + Log.d(TAG, "TaxiF Automation event. State: $currentState") when (currentState) { BotState.NAVIGATING_HOME -> { if (hasJustClickedHome()) return @@ -539,7 +512,7 @@ class ScraperAccessibilityService : AccessibilityService() { BotState.SEARCHING_START -> { if (detectTaxiFPriceScreen(rootNode)) { if (hasJustClickedLocation() || hasJustClickedSuggestion()) return - if (!taxiFPickupDone && shouldEditLocation(rootNode, isPickup = true)) { + if (!taxiFPickupDone && shouldEditLocation(rootNode, task, isPickup = true)) { clickLocationRow(rootNode, isPickup = true) taxiFLocationClickTime = System.currentTimeMillis() return @@ -548,7 +521,7 @@ class ScraperAccessibilityService : AccessibilityService() { currentState = BotState.SEARCHING_END return } - if (!taxiFPickupDone && handleTaxiFSearchScreen(rootNode, getCurrentStartLocation())) { + if (!taxiFPickupDone && handleTaxiFSearchScreen(rootNode, task.optString("start_location", "Amman"))) { taxiFPickupDone = true Log.i(TAG, "TaxiF: Handled pickup search, waiting for price screen") } @@ -556,7 +529,7 @@ class ScraperAccessibilityService : AccessibilityService() { BotState.SEARCHING_END -> { if (detectTaxiFPriceScreen(rootNode)) { if (hasJustClickedLocation() || hasJustClickedSuggestion()) return - if (!taxiFDestinationDone && shouldEditLocation(rootNode, isPickup = false)) { + if (!taxiFDestinationDone && shouldEditLocation(rootNode, task, isPickup = false)) { clickLocationRow(rootNode, isPickup = false) taxiFLocationClickTime = System.currentTimeMillis() return @@ -567,7 +540,7 @@ class ScraperAccessibilityService : AccessibilityService() { taxiFPriceScrollAttempts = 0 return } - if (!taxiFDestinationDone && handleTaxiFSearchScreen(rootNode, getCurrentEndLocation())) { + if (!taxiFDestinationDone && handleTaxiFSearchScreen(rootNode, task.optString("end_location", "Airport"))) { taxiFDestinationDone = true Log.i(TAG, "TaxiF: Handled dest search, waiting for price screen") } @@ -593,30 +566,6 @@ class ScraperAccessibilityService : AccessibilityService() { return (System.currentTimeMillis() - taxiFSuggestionClickTime) < 1500 } - private fun getCurrentStartLocation(): String { - return if (taxiFMultiTripTrips.isNotEmpty() && taxiFMultiTripIndex < taxiFMultiTripTrips.size) - taxiFMultiTripTrips[taxiFMultiTripIndex].optString("start_location", "Amman") - else - currentTask?.optString("start_location", "Amman") ?: "Amman" - } - - private fun getCurrentEndLocation(): String { - return if (taxiFMultiTripTrips.isNotEmpty() && taxiFMultiTripIndex < taxiFMultiTripTrips.size) - taxiFMultiTripTrips[taxiFMultiTripIndex].optString("end_location", "Airport") - else - currentTask?.optString("end_location", "Airport") ?: "Airport" - } - - private fun getCurrentTripLatLng(): Pair? { - return if (taxiFMultiTripTrips.isNotEmpty() && taxiFMultiTripIndex < taxiFMultiTripTrips.size) { - val trip = taxiFMultiTripTrips[taxiFMultiTripIndex] - Pair( - doubleArrayOf(trip.optDouble("start_lat", 0.0), trip.optDouble("start_lng", 0.0)), - doubleArrayOf(trip.optDouble("end_lat", 0.0), trip.optDouble("end_lng", 0.0)) - ) - } else null - } - private fun getLocationTexts(node: android.view.accessibility.AccessibilityNodeInfo?): List { val texts = mutableListOf() collectLocationTexts(node, texts) @@ -636,9 +585,9 @@ class ScraperAccessibilityService : AccessibilityService() { } } - private fun shouldEditLocation(rootNode: android.view.accessibility.AccessibilityNodeInfo, isPickup: Boolean): Boolean { + private fun shouldEditLocation(rootNode: android.view.accessibility.AccessibilityNodeInfo, task: JSONObject, isPickup: Boolean): Boolean { val locationTexts = getLocationTexts(rootNode) - val taskLoc = if (isPickup) getCurrentStartLocation() else getCurrentEndLocation() + val taskLoc = if (isPickup) task.optString("start_location", "") else task.optString("end_location", "") if (taskLoc.isEmpty()) return false val matchFound = locationTexts.any { text -> text.contains(taskLoc, ignoreCase = true) || taskLoc.contains(text, ignoreCase = true) @@ -776,40 +725,13 @@ class ScraperAccessibilityService : AccessibilityService() { Log.i(TAG, "TaxiF: Found price option: ${label.take(50)} = $price JOD") } val cheapest = prices.minByOrNull { it.first } ?: prices.first() - val tripLabel = if (taxiFMultiTripTrips.isNotEmpty()) - "Trip ${taxiFMultiTripIndex + 1}/${taxiFMultiTripTrips.size}: " else "" - Log.i(TAG, "TaxiF: ${tripLabel}Cheapest price: ${cheapest.first} JOD") + Log.i(TAG, "TaxiF: Submitting cheapest price: ${cheapest.second.take(50)} = ${cheapest.first} JOD") submitPriceToServer("${cheapest.first} JOD") - advanceToNextTaxiFTrip() } else { - Log.w(TAG, "TaxiF: No JOD prices found${if (taxiFMultiTripTrips.isNotEmpty()) " for trip ${taxiFMultiTripIndex + 1}" else ""}") - if (taxiFMultiTripTrips.isEmpty()) { - searchPriceByCurrency(rootNode) - currentState = BotState.IDLE - } - } - } - - private fun advanceToNextTaxiFTrip() { - taxiFPriceSubmitTime = System.currentTimeMillis() - if (taxiFMultiTripTrips.isNotEmpty()) { - taxiFMultiTripIndex++ - taxiFPickupDone = false - taxiFDestinationDone = false - - if (taxiFMultiTripIndex < taxiFMultiTripTrips.size) { - Log.i(TAG, "TaxiF: 3s cool-down then trip ${taxiFMultiTripIndex + 1}/${taxiFMultiTripTrips.size}") - currentState = BotState.SEARCHING_START - } else { - Log.i(TAG, "TaxiF: All ${taxiFMultiTripTrips.size} trips completed!") - currentState = BotState.IDLE - currentTask = null - taxiFMultiTripTrips.clear() - taxiFMultiTripIndex = 0 - } - } else { - currentState = BotState.IDLE + Log.w(TAG, "TaxiF: No JOD prices found, falling back to generic search") + searchPriceByCurrency(rootNode) } + currentState = BotState.IDLE } private fun findHorizontalRecyclerView(node: android.view.accessibility.AccessibilityNodeInfo?): android.view.accessibility.AccessibilityNodeInfo? { diff --git a/backend/bot/standalone_worker.php b/backend/bot/standalone_worker.php index 50a2227d..1c9fbc34 100644 --- a/backend/bot/standalone_worker.php +++ b/backend/bot/standalone_worker.php @@ -198,33 +198,31 @@ if (isset($_POST['action'])) { [5, 11], // Tla Al-Ali → Hashmi (~5km) ]; - $trips = []; + $tasks = json_decode(file_get_contents(TASKS_FILE), true); + $count = 0; foreach ($tripPairs as $i => $pair) { $start = $ammanLocations[$pair[0]]; $end = $ammanLocations[$pair[1]]; - $trips[] = [ - 'trip_index' => $i, + $taskId = "prc_" . uniqid(); + + $newTask = [ + 'task_id' => $taskId, + 'type' => 'price_check', + 'app' => $app, 'start_location' => $start['name'], 'end_location' => $end['name'], - 'start_lat' => $start['lat'], - 'start_lng' => $start['lng'], - 'end_lat' => $end['lat'], - 'end_lng' => $end['lng'], + 'payload' => [ + 'start_lat' => $start['lat'], + 'start_lng' => $start['lng'], + 'end_lat' => $end['lat'], + 'end_lng' => $end['lng'], + ], ]; + $tasks[] = $newTask; + $count++; } - - $taskId = "batch_" . uniqid(); - $batchTask = [ - 'task_id' => $taskId, - 'type' => 'batch_multi_trip', - 'app' => $app, - 'trips' => $trips, - ]; - - $tasks = json_decode(file_get_contents(TASKS_FILE), true); - $tasks[] = $batchTask; file_put_contents(TASKS_FILE, json_encode($tasks, JSON_PRETTY_PRINT)); - $message = "Batch task with 10 trips generated! Task ID: $taskId"; + $message = "Generated $count standard tasks (one per trip) for TaxiF!"; } elseif ($_POST['action'] === 'clear_tasks') { file_put_contents(TASKS_FILE, json_encode([])); $message = "Task queue cleared successfully."; @@ -712,10 +710,10 @@ $scrapedResults = json_decode(file_get_contents(RESULTS_FILE), true);
-

Batch Multi-Trip Generator

+

Generate 10 Amman Trips

- Generates 1 batch task containing 10 varied Amman trips (2–17 km) for TaxiF. - The bot processes all 10 trips without reopening the app. + Queues 10 separate standard tasks (one per trip) with varied distances (2–17 km) for TaxiF. + The bot processes each trip individually through normal polling.