Update: 2026-06-29 23:45:06

This commit is contained in:
Hamza-Ayed
2026-06-29 23:45:06 +03:00
parent 3506b07bc7
commit 299132b505
13 changed files with 278 additions and 53 deletions

View File

@@ -15,7 +15,15 @@ object AppLauncher {
"zaken" to "com.zakinn.app",
"com.zakinn.app" to "com.zakinn.app",
"tufaddal" to "com.bis.taxi",
"com.bis.taxi" to "com.bis.taxi"
"com.bis.taxi" to "com.bis.taxi",
"careem" to "com.careem.acma",
"com.careem.acma" to "com.careem.acma",
"uber" to "com.ubercab",
"com.ubercab" to "com.ubercab",
"taxif" to "com.taxif.passenger",
"com.taxif.passenger" to "com.taxif.passenger",
"jeeny" to "me.com.easytaxi",
"me.com.easytaxi" to "me.com.easytaxi"
)
fun launchApp(context: Context, appName: String): Boolean {

View File

@@ -98,6 +98,10 @@ class ScraperAccessibilityService : AccessibilityService() {
"ae.com.yalla.go.dubai.client" -> handleYallaGoAutomation(rootNode)
"com.zakinn.app" -> handleZakinnAutomation(rootNode)
"com.bis.taxi" -> handleTfadalAutomation(rootNode)
"com.careem.acma" -> handleCareemAutomation(rootNode)
"com.ubercab" -> handleUberAutomation(rootNode)
"com.taxif.passenger" -> handleTaxiFAutomation(rootNode)
"me.com.easytaxi" -> handleJeenyAutomation(rootNode)
}
}
@@ -322,7 +326,7 @@ class ScraperAccessibilityService : AccessibilityService() {
private fun searchPriceByCurrency(node: android.view.accessibility.AccessibilityNodeInfo?) {
if (node == null) return
val text = node.text?.toString() ?: ""
if (text.contains("ل.س") || text.contains("SYP") || text.contains("AED") || text.contains("SP") || text.contains("SP.")) {
if (text.contains("ل.س") || text.contains("SYP") || text.contains("AED") || text.contains("SP") || text.contains("SP.") || text.contains("JOD") || text.contains("د.أ") || text.contains("JD")) {
Log.i(TAG, "Found price pattern dynamically: $text")
submitPriceToServer(text)
return
@@ -385,6 +389,146 @@ class ScraperAccessibilityService : AccessibilityService() {
return r * c
}
private fun handleCareemAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
val task = currentTask ?: return
Log.d(TAG, "Careem Automation event. State: $currentState")
when (currentState) {
BotState.SEARCHING_START -> {
val pickupEdit = findEditableNode(rootNode)
if (pickupEdit != null) {
val startLoc = task.optString("start_location", "Amman")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc)
}
pickupEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "Careem: Entered start: $startLoc")
currentState = BotState.SEARCHING_END
}
}
BotState.SEARCHING_END -> {
val destEdit = findEditableNode(rootNode)
if (destEdit != null) {
val endLoc = task.optString("end_location", "Airport")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc)
}
destEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "Careem: Entered end: $endLoc")
currentState = BotState.READING_PRICE
}
}
BotState.READING_PRICE -> {
searchPriceByCurrency(rootNode)
}
else -> {}
}
}
private fun handleUberAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
val task = currentTask ?: return
Log.d(TAG, "Uber Automation event. State: $currentState")
when (currentState) {
BotState.SEARCHING_START -> {
val pickupEdit = findEditableNode(rootNode)
if (pickupEdit != null) {
val startLoc = task.optString("start_location", "Amman")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc)
}
pickupEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "Uber: Entered start: $startLoc")
currentState = BotState.SEARCHING_END
}
}
BotState.SEARCHING_END -> {
val destEdit = findEditableNode(rootNode)
if (destEdit != null) {
val endLoc = task.optString("end_location", "Airport")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc)
}
destEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "Uber: Entered end: $endLoc")
currentState = BotState.READING_PRICE
}
}
BotState.READING_PRICE -> {
searchPriceByCurrency(rootNode)
}
else -> {}
}
}
private fun handleTaxiFAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
val task = currentTask ?: return
Log.d(TAG, "TaxiF Automation event. State: $currentState")
when (currentState) {
BotState.SEARCHING_START -> {
val pickupEdit = findEditableNode(rootNode)
if (pickupEdit != null) {
val startLoc = task.optString("start_location", "Amman")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc)
}
pickupEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "TaxiF: Entered start: $startLoc")
currentState = BotState.SEARCHING_END
}
}
BotState.SEARCHING_END -> {
val destEdit = findEditableNode(rootNode)
if (destEdit != null) {
val endLoc = task.optString("end_location", "Airport")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc)
}
destEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "TaxiF: Entered end: $endLoc")
currentState = BotState.READING_PRICE
}
}
BotState.READING_PRICE -> {
searchPriceByCurrency(rootNode)
}
else -> {}
}
}
private fun handleJeenyAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
val task = currentTask ?: return
Log.d(TAG, "Jeeny Automation event. State: $currentState")
when (currentState) {
BotState.SEARCHING_START -> {
val pickupEdit = findEditableNode(rootNode)
if (pickupEdit != null) {
val startLoc = task.optString("start_location", "Amman")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc)
}
pickupEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "Jeeny: Entered start: $startLoc")
currentState = BotState.SEARCHING_END
}
}
BotState.SEARCHING_END -> {
val destEdit = findEditableNode(rootNode)
if (destEdit != null) {
val endLoc = task.optString("end_location", "Airport")
val arguments = android.os.Bundle().apply {
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc)
}
destEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
Log.i(TAG, "Jeeny: Entered end: $endLoc")
currentState = BotState.READING_PRICE
}
}
BotState.READING_PRICE -> {
searchPriceByCurrency(rootNode)
}
else -> {}
}
}
override fun onInterrupt() {
Log.w(TAG, "Accessibility Service Interrupted")
}

View File

@@ -34,8 +34,11 @@ CREATE TABLE IF NOT EXISTS competitor_prices (
";
$con->exec($sql);
// 2. Ten Key Regions in Damascus
$regions = [
// 2. Ten Key Regions in Damascus (Syria) and Amman (Jordan)
$countriesConfig = [
'SY' => [
'competitors' => ['yallago', 'zaken', 'tufaddal'],
'regions' => [
['name' => 'Umayyad Square', 'lat' => 33.5138, 'lng' => 36.2765],
['name' => 'Mezzeh', 'lat' => 33.5074, 'lng' => 36.2530],
['name' => 'Malki', 'lat' => 33.5220, 'lng' => 36.2840],
@@ -46,10 +49,25 @@ $regions = [
['name' => 'Dummar', 'lat' => 33.5385, 'lng' => 36.2250],
['name' => 'Baramkeh', 'lat' => 33.5100, 'lng' => 36.2885],
['name' => 'Muhajireen', 'lat' => 33.5320, 'lng' => 36.2720],
]
],
'JO' => [
'competitors' => ['com.careem.acma', 'com.ubercab', 'com.taxif.passenger', 'me.com.easytaxi'],
'regions' => [
['name' => 'Abdoun', 'lat' => 31.9392, 'lng' => 35.8942],
['name' => 'Jabal Amman', 'lat' => 31.9511, 'lng' => 35.9189],
['name' => 'Sweileh', 'lat' => 32.0167, 'lng' => 35.8333],
['name' => 'Khalda', 'lat' => 31.9861, 'lng' => 35.8450],
['name' => 'Al-Jubaiha', 'lat' => 32.0194, 'lng' => 35.8753],
['name' => 'Tla Al-Ali', 'lat' => 31.9961, 'lng' => 35.8647],
['name' => 'Shmeisani', 'lat' => 31.9680, 'lng' => 35.9020],
['name' => 'Um Uthaina', 'lat' => 31.9610, 'lng' => 35.8770],
['name' => 'Jabal Al-Weibdeh', 'lat' => 31.9560, 'lng' => 35.9220],
['name' => 'Marj Al-Hamam', 'lat' => 31.9000, 'lng' => 35.8500],
]
]
];
$competitors = ['yallago', 'zaken', 'tufaddal'];
// Helper to generate a random point within a radius (in km)
function generateRandomPoint($lat, $lng, $radius) {
$radiusInDegrees = $radius / 111.0; // 1 degree is ~111km
@@ -72,6 +90,10 @@ function generateRandomPoint($lat, $lng, $radius) {
$tasksCreated = 0;
foreach ($countriesConfig as $countryCode => $config) {
$competitors = $config['competitors'];
$regions = $config['regions'];
foreach ($regions as $region) {
// A. Generate Start Point (within 2km of region center)
$start = generateRandomPoint($region['lat'], $region['lng'], 2);
@@ -108,5 +130,6 @@ foreach ($regions as $region) {
}
}
}
}
echo "Successfully generated and queued $tasksCreated pricing tasks.\n";

View File

@@ -506,6 +506,10 @@ $scrapedResults = json_decode(file_get_contents(RESULTS_FILE), true);
.app-yallago { background: rgba(59, 130, 246, 0.15); color: #60a5fa; border: 1px solid rgba(59, 130, 246, 0.3); }
.app-zaken { background: rgba(16, 185, 129, 0.15); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.3); }
.app-tufaddal { background: rgba(245, 158, 11, 0.15); color: #fbbf24; border: 1px solid rgba(245, 158, 11, 0.3); }
.app-careem { background: rgba(16, 185, 129, 0.15); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.3); }
.app-uber { background: rgba(255, 255, 255, 0.1); color: #f3f4f6; border: 1px solid rgba(255, 255, 255, 0.2); }
.app-taxif { background: rgba(239, 68, 68, 0.15); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.3); }
.app-jeeny { background: rgba(139, 92, 246, 0.15); color: #a78bfa; border: 1px solid rgba(139, 92, 246, 0.3); }
.time-badge {
font-size: 0.80rem;
@@ -601,6 +605,10 @@ $scrapedResults = json_decode(file_get_contents(RESULTS_FILE), true);
<option value="ae.com.yalla.go.dubai.client">YallaGo (ae.com.yalla.go.dubai.client)</option>
<option value="com.zakinn.app">Zaken / Zakinn (com.zakinn.app)</option>
<option value="com.bis.taxi">Tfadal (com.bis.taxi)</option>
<option value="com.careem.acma">Careem (com.careem.acma)</option>
<option value="com.ubercab">Uber (com.ubercab)</option>
<option value="com.taxif.passenger">TaxiF (com.taxif.passenger)</option>
<option value="me.com.easytaxi">Jeeny (me.com.easytaxi)</option>
</select>
</div>
@@ -664,7 +672,24 @@ $scrapedResults = json_decode(file_get_contents(RESULTS_FILE), true);
<div class="empty-state">No pending tasks. Queue some tasks using the form on the left!</div>
<?php else: ?>
<?php foreach ($currentTasks as $task):
$appLabel = ($task['app'] === 'ae.com.yalla.go.dubai.client') ? 'yallago' : (($task['app'] === 'com.zakinn.app') ? 'zaken' : 'tufaddal');
$app = $task['app'] ?? '';
if ($app === 'ae.com.yalla.go.dubai.client') {
$appLabel = 'yallago';
} elseif ($app === 'com.zakinn.app') {
$appLabel = 'zaken';
} elseif ($app === 'com.bis.taxi') {
$appLabel = 'tufaddal';
} elseif ($app === 'com.careem.acma') {
$appLabel = 'careem';
} elseif ($app === 'com.ubercab') {
$appLabel = 'uber';
} elseif ($app === 'com.taxif.passenger') {
$appLabel = 'taxif';
} elseif ($app === 'me.com.easytaxi') {
$appLabel = 'jeeny';
} else {
$appLabel = 'unknown';
}
?>
<div class="list-item">
<div class="task-info">
@@ -702,7 +727,23 @@ $scrapedResults = json_decode(file_get_contents(RESULTS_FILE), true);
<?php foreach ($scrapedResults as $res):
$data = $res['result_data'];
$app = $data['app'] ?? 'unknown';
$appLabel = (strpos($app, 'yalla') !== false) ? 'yallago' : ((strpos($app, 'zakinn') !== false || strpos($app, 'zaken') !== false) ? 'zaken' : 'tufaddal');
if (strpos($app, 'yalla') !== false) {
$appLabel = 'yallago';
} elseif (strpos($app, 'zakinn') !== false || strpos($app, 'zaken') !== false) {
$appLabel = 'zaken';
} elseif (strpos($app, 'bis.taxi') !== false || strpos($app, 'tufaddal') !== false) {
$appLabel = 'tufaddal';
} elseif (strpos($app, 'careem') !== false) {
$appLabel = 'careem';
} elseif (strpos($app, 'ubercab') !== false || strpos($app, 'uber') !== false) {
$appLabel = 'uber';
} elseif (strpos($app, 'taxif') !== false) {
$appLabel = 'taxif';
} elseif (strpos($app, 'easytaxi') !== false || strpos($app, 'jeeny') !== false) {
$appLabel = 'jeeny';
} else {
$appLabel = 'unknown';
}
$isSuccess = ($res['status'] === 'success');
$price = $data['price'] ?? 0;
$dist = $data['distance_km'] ?? 0;
@@ -718,7 +759,11 @@ $scrapedResults = json_decode(file_get_contents(RESULTS_FILE), true);
</div>
<div class="price-text">
<?php if ($isSuccess): ?>
<?php echo number_format($price, 0); ?> SYP
<?php
$currency = (in_array($appLabel, ['careem', 'uber', 'taxif', 'jeeny'])) ? 'JOD' : 'SYP';
$decimals = ($currency === 'JOD') ? 2 : 0;
echo number_format($price, $decimals) . ' ' . $currency;
?>
<?php else: ?>
<span style="color:var(--error-color); font-size:0.9rem;">Scrape Failed</span>
<?php endif; ?>

View File

@@ -220,7 +220,9 @@ ALTER TABLE `car_tracks`
-- Indexes for table `driver_behavior`
--
ALTER TABLE `driver_behavior`
ADD PRIMARY KEY (`id`);
ADD PRIMARY KEY (`id`),
ADD KEY `idx_driver_id` (`driver_id`),
ADD KEY `idx_trip_id` (`trip_id`);
--
-- Indexes for table `driver_daily_summary`
@@ -233,26 +235,29 @@ ALTER TABLE `driver_daily_summary`
-- Indexes for table `driver_daily_work`
--
ALTER TABLE `driver_daily_work`
ADD PRIMARY KEY (`driver_id`,`work_date`),
ADD KEY `idx_driver_date` (`driver_id`,`work_date`);
ADD PRIMARY KEY (`driver_id`,`work_date`);
--
-- Indexes for table `driver_orders`
--
ALTER TABLE `driver_orders`
ADD PRIMARY KEY (`id`);
ADD PRIMARY KEY (`id`),
ADD KEY `idx_driver_id` (`driver_id`),
ADD KEY `idx_order_id` (`order_id`);
--
-- Indexes for table `login_attempts`
--
ALTER TABLE `login_attempts`
ADD PRIMARY KEY (`id`);
ADD PRIMARY KEY (`id`),
ADD KEY `idx_ip_time` (`ip_address`,`attempt_time`);
--
-- Indexes for table `login_attempts_drivers`
--
ALTER TABLE `login_attempts_drivers`
ADD PRIMARY KEY (`id`);
ADD PRIMARY KEY (`id`),
ADD KEY `idx_ip_time` (`ip_address`,`attempt_time`);
--
-- Indexes for table `places`