diff --git a/android/dushanbe/PayByCardScript.cpp b/android/dushanbe/PayByCardScript.cpp index f53706c..6bc73cb 100644 --- a/android/dushanbe/PayByCardScript.cpp +++ b/android/dushanbe/PayByCardScript.cpp @@ -382,10 +382,14 @@ void PayByCardScript::makePayment( // отдельного узла "Подтверждаю" в дампе нет — координаты берём OCR-ом // по скриншоту. Если OCR не нашёл — fallback на геометрию (центр // нижней четверти sheet, выше системного nav-бара). + // Compose отдаёт sheet как одну ноду на весь экран ([0,0][720,1640]), + // поэтому confirmNode.y1/y2 не сужают поиск — OCR сканирует весь + // скриншот и ловит ложные совпадения. Ограничиваем нижней половиной. + const int ocrYMin = (confirmNode.y1() + confirmNode.y2()) / 2; const QByteArray screen = AdbUtils::captureScreenshotBytes(deviceId); QPoint tap = OcrUtils::findTextCenter(screen, QString::fromUtf8("Подтверждаю"), - confirmNode.y1(), confirmNode.y2()); + ocrYMin, confirmNode.y2()); if (tap.x() < 0) { const int tapY = confirmNode.y2() - qMax(150, height / 14); tap = QPoint(confirmNode.x(), tapY); @@ -478,10 +482,11 @@ void PayByCardScript::makePayment( ++confirmRetries; qDebug() << "[Dushanbe::PayByCard] Confirm sheet still visible, re-tap" << confirmRetries << "/" << maxConfirmRetries; + const int ocrYMin = (retryConfirm.y1() + retryConfirm.y2()) / 2; const QByteArray screen = AdbUtils::captureScreenshotBytes(deviceId); QPoint tap = OcrUtils::findTextCenter(screen, QString::fromUtf8("Подтверждаю"), - retryConfirm.y1(), retryConfirm.y2()); + ocrYMin, retryConfirm.y2()); if (tap.x() < 0) { // Варьируем Y: первый retry — выше fallback на 100px, // второй — ещё на 100px выше. Compose-кнопка действия diff --git a/android/dushanbe/PayByPhoneScript.cpp b/android/dushanbe/PayByPhoneScript.cpp index 8422423..8f3c1b5 100644 --- a/android/dushanbe/PayByPhoneScript.cpp +++ b/android/dushanbe/PayByPhoneScript.cpp @@ -378,10 +378,15 @@ void PayByPhoneScript::makePayment( // отдельного узла "Подтверждаю" в дампе нет — координаты берём OCR-ом // по скриншоту. Если OCR не нашёл — fallback на геометрию (центр // нижней четверти sheet, выше системного nav-бара). + // Compose отдаёт sheet как одну ноду на весь экран ([0,0][720,1640]), + // поэтому confirmNode.y1/y2 не сужают поиск — OCR сканирует весь + // скриншот и ловит ложные совпадения (буква "в" в шапке). + // Ограничиваем нижней половиной — действие-кнопка всегда внизу sheet'a. + const int ocrYMin = (confirmNode.y1() + confirmNode.y2()) / 2; const QByteArray screen = AdbUtils::captureScreenshotBytes(deviceId); QPoint tap = OcrUtils::findTextCenter(screen, QString::fromUtf8("Подтверждаю"), - confirmNode.y1(), confirmNode.y2()); + ocrYMin, confirmNode.y2()); if (tap.x() < 0) { const int tapY = confirmNode.y2() - qMax(150, height / 14); tap = QPoint(confirmNode.x(), tapY); @@ -470,10 +475,11 @@ void PayByPhoneScript::makePayment( ++confirmRetries; qDebug() << "[Dushanbe::PayByPhone] Confirm sheet still visible, re-tap" << confirmRetries << "/" << maxConfirmRetries; + const int ocrYMin = (retryConfirm.y1() + retryConfirm.y2()) / 2; const QByteArray screen = AdbUtils::captureScreenshotBytes(deviceId); QPoint tap = OcrUtils::findTextCenter(screen, QString::fromUtf8("Подтверждаю"), - retryConfirm.y1(), retryConfirm.y2()); + ocrYMin, retryConfirm.y2()); if (tap.x() < 0) { // Варьируем Y: первый retry — выше fallback на 100px, // второй — ещё на 100px выше. Compose-кнопка действия diff --git a/android/ocr/OcrUtils.cpp b/android/ocr/OcrUtils.cpp index eac82eb..2819b3a 100644 --- a/android/ocr/OcrUtils.cpp +++ b/android/ocr/OcrUtils.cpp @@ -70,7 +70,12 @@ bool fuzzyMatch(const QString &word, const QString &needle) { if (word.isEmpty() || needle.isEmpty()) return false; const QString w = word.toLower(); const QString n = needle.toLower(); - if (w.contains(n) || n.contains(w)) return true; + // Substring-ветка работает только если word достаточно длинный. + // Иначе любая одиночная буква, входящая в needle (для «подтверждаю»: + // п/о/д/т/в/е/р/ж/а/ю), даст ложный матч на случайный текст экрана — + // ровно так бот тапал в (620,302) по букве «в» где-то в шапке sheet'a. + const int minLen = qMax(qMin(n.size(), 3), n.size() * 3 / 5); + if (w.size() >= minLen && (w.contains(n) || n.contains(w))) return true; // Допускаем до 20% ошибок (но не меньше 1) — Tesseract на «Подтверждаю» иногда // даёт «Подтверждак»/«Подтверждак)» из-за оранжевого фона и сглаживания. const int tolerance = qMax(1, n.size() / 5);