diff --git a/android/ozon/GetCardInfoScript.cpp b/android/ozon/GetCardInfoScript.cpp index 681f01f..0cbe17f 100644 --- a/android/ozon/GetCardInfoScript.cpp +++ b/android/ozon/GetCardInfoScript.cpp @@ -109,6 +109,25 @@ void GetCardInfoScript::doStart() { qDebug() << "[GetCardInfo] Profile info already in DB or username not found, skipping profile parse"; } + // Проверяем что на домашнем экране перед поиском карт + xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); + if (!xmlScreenParser.isHomeScreen()) { + qDebug() << "[GetCardInfo] Not on home screen before card search, restarting..."; + if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, + device.screenWidth, device.screenHeight)) { + m_error = "Cannot reach home screen before card search: " + m_deviceId; + qWarning() << m_error; + AppLogger::log("ozon/GetCardInfo", m_deviceId, m_error, + AdbUtils::captureScreenshotBytes(m_deviceId), + "FETCH_PROFILE"); + AdbUtils::tryToKillApplication(m_deviceId, app.package); + emit finishedWithResult(m_error); + return; + } + tryToCloseAllBannersOnHomePage(m_deviceId, device.screenWidth, device.screenHeight); + QThread::msleep(500); + } + // Ждём пока все карты подгрузятся — до 5 попыток QList cards; for (int attempt = 0; attempt < 5; ++attempt) { @@ -194,7 +213,17 @@ void GetCardInfoScript::doStart() { if (freshApp.bankProfileId.isEmpty()) { ns.addExtra("app_id", freshApp.id); ns.postBankProfile(); - qDebug() << "[GetCardInfo] postBankProfile done"; + // Проверяем что bankProfileId сохранился + const ApplicationInfo afterPost = ApplicationDAO::getApplication(m_deviceId, m_appCode); + if (afterPost.bankProfileId.isEmpty()) { + const QString err = "postBankProfile failed for " + m_deviceId; + qWarning() << "[GetCardInfo]" << err; + AppLogger::log("ozon/GetCardInfo", m_deviceId, err, + AdbUtils::captureScreenshotBytes(m_deviceId), + "FETCH_PROFILE"); + } else { + qDebug() << "[GetCardInfo] postBankProfile done"; + } } else { ns.addExtra("bank_profile_id", freshApp.bankProfileId); ns.patchBankProfile(); @@ -227,16 +256,24 @@ void GetCardInfoScript::doStart() { AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y()); - // Ждём экран карты — ищем "Реквизиты карты" в XML дампе + // Ждём экран карты — ищем кнопку "Показать" Node showBtn; for (int attempt = 0; attempt < 10; ++attempt) { QThread::msleep(2000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node reqNode = xmlScreenParser.findTextNode("Реквизиты карты"); - if (!reqNode.isEmpty()) { - showBtn = xmlScreenParser.findButtonNode("Показать", false); - if (!showBtn.isEmpty()) break; + + showBtn = xmlScreenParser.findButtonNode("Показать", false); + if (!showBtn.isEmpty()) break; + + // Если всё ещё на домашнем экране — тап не сработал, повторяем + if (xmlScreenParser.isHomeScreen()) { + cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers); + if (!cardBtn.isEmpty()) { + qDebug() << "[GetCardInfo] Still on home screen, re-tapping card" << card.lastNumbers; + AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y()); + } } + qDebug() << "[GetCardInfo] Card screen not loaded, attempt" << attempt + 1 << "/ 10"; } @@ -277,7 +314,26 @@ void GetCardInfoScript::doStart() { continue; } - qDebug() << "[GetCardInfo] Card number not visible, attempt" << attempt + 1 << "/ 10"; + // "Подождите" — загрузка в процессе, просто ждём + if (!xmlScreenParser.findButtonNode("Подождите", false).isEmpty()) { + qDebug() << "[GetCardInfo] 'Подождите' visible, waiting... attempt" << attempt + 1 << "/ 10"; + continue; + } + + // "Скрыть" видна — номер должен быть раскрыт, но парсер не нашёл + if (!xmlScreenParser.findButtonNode("Скрыть", false).isEmpty()) { + qDebug() << "[GetCardInfo] 'Скрыть' visible but card number not parsed, attempt" << attempt + 1 << "/ 10"; + continue; + } + + // Повторно нажимаем "Показать" если тап не сработал + Node retryShowBtn = xmlScreenParser.findButtonNode("Показать", false); + if (!retryShowBtn.isEmpty()) { + qDebug() << "[GetCardInfo] Re-tapping 'Показать', attempt" << attempt + 1 << "/ 10"; + AdbUtils::makeTap(m_deviceId, retryShowBtn.x(), retryShowBtn.y()); + } else { + qDebug() << "[GetCardInfo] Card number not visible, attempt" << attempt + 1 << "/ 10"; + } } if (!cardNumber.isEmpty()) { @@ -288,7 +344,16 @@ void GetCardInfoScript::doStart() { AccountDAO::updateCardNumber(saved.id, cardNumber); } } else { - qWarning() << "[GetCardInfo] Card number not found for" << card.lastNumbers; + const QString err = "Card number not found for " + card.lastNumbers; + qWarning() << "[GetCardInfo]" << err; + AccountInfo saved = AccountDAO::findAppAccount(m_deviceId, m_appCode, card.lastNumbers); + if (saved.id != -1) { + AccountDAO::updateAccountById(saved.id, accountStatusToString(AccountStatus::Off), + saved.description, saved.currency); + } + AppLogger::log("ozon/GetCardInfo", m_deviceId, err, + AdbUtils::captureScreenshotBytes(m_deviceId), + "FETCH_PROFILE"); } // Возвращаемся на домашний экран для следующей карты @@ -314,6 +379,7 @@ void GetCardInfoScript::doStart() { materialBody["bank_profile_id"] = updatedApp.bankProfileId; materialBody["card_number"] = dbCard.cardNumber; materialBody["name"] = updatedApp.fullName; + materialBody["state"] = accountStatusToString(dbCard.status) == "active" ? "enabled" : "disabled"; ns.setPayload(QJsonDocument(materialBody).toJson()); ns.addExtra("account_id", dbCard.id); ns.postMaterial(); diff --git a/android/ozon/GetLastTransactionsScript.cpp b/android/ozon/GetLastTransactionsScript.cpp index 9ab65e1..eedb281 100644 --- a/android/ozon/GetLastTransactionsScript.cpp +++ b/android/ozon/GetLastTransactionsScript.cpp @@ -239,6 +239,13 @@ void GetLastTransactionsScript::doStart() { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); detail = xmlScreenParser.parseTransactionDetail(); if (!detail.bankTime.isEmpty()) break; + + // Если всё ещё на экране операций — тап не сработал, повторяем + if (xmlScreenParser.isTransactionListScreen()) { + qDebug() << "[GetLastTransactions] Still on transaction list, re-tapping" << i + 1; + AdbUtils::makeTap(m_deviceId, visibleBtn.x(), visibleBtn.y()); + } + qDebug() << "[GetLastTransactions] Detail not loaded, attempt" << attempt + 1 << "/ 10"; QThread::msleep(1500); } diff --git a/android/ozon/GetProfileInfoScript.cpp b/android/ozon/GetProfileInfoScript.cpp index 7766ebc..21e116a 100644 --- a/android/ozon/GetProfileInfoScript.cpp +++ b/android/ozon/GetProfileInfoScript.cpp @@ -11,6 +11,7 @@ #include "dao/ApplicationDAO.h" #include "dao/DeviceDAO.h" #include "net/NetworkService.h" +#include "AppLogger.h" namespace Ozon { @@ -141,7 +142,16 @@ void GetProfileInfoScript::doStart() { if (freshApp.bankProfileId.isEmpty()) { ns.addExtra("app_id", freshApp.id); ns.postBankProfile(); - qDebug() << "[GetProfileInfo] postBankProfile done"; + const ApplicationInfo afterPost = ApplicationDAO::getApplication(m_deviceId, m_appCode); + if (afterPost.bankProfileId.isEmpty()) { + const QString err = "postBankProfile failed for " + m_deviceId; + qWarning() << "[GetProfileInfo]" << err; + AppLogger::log("ozon/GetProfileInfo", m_deviceId, err, + AdbUtils::captureScreenshotBytes(m_deviceId), + "FETCH_PROFILE"); + } else { + qDebug() << "[GetProfileInfo] postBankProfile done"; + } } else { ns.addExtra("bank_profile_id", freshApp.bankProfileId); ns.patchBankProfile(); diff --git a/android/ozon/PayByCardScript.cpp b/android/ozon/PayByCardScript.cpp index bbcf7b1..e118670 100644 --- a/android/ozon/PayByCardScript.cpp +++ b/android/ozon/PayByCardScript.cpp @@ -121,7 +121,7 @@ void PayByCardScript::doStart() { // Закрываем баннер "Заказать карту" — тапаем сверху экрана if (Node orderCardNode = xmlScreenParser.findTextNode("Заказать карту"); !orderCardNode.isEmpty()) { qDebug() << "[PayByCard] 'Заказать карту' banner detected, closing..."; - AdbUtils::makeTap(device.id, device.screenWidth / 2, 100); + AdbUtils::makeTap(device.id, device.screenWidth / 2, device.screenHeight / 20); QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); } diff --git a/android/ozon/PayByPhoneScript.cpp b/android/ozon/PayByPhoneScript.cpp index 1ff5c21..682d1d9 100644 --- a/android/ozon/PayByPhoneScript.cpp +++ b/android/ozon/PayByPhoneScript.cpp @@ -122,7 +122,7 @@ void PayByPhoneScript::doStart() { // Закрываем баннер "Заказать карту" — тапаем сверху экрана if (Node orderCardNode = xmlScreenParser.findTextNode("Заказать карту"); !orderCardNode.isEmpty()) { qDebug() << "[PayByPhone] 'Заказать карту' banner detected, closing..."; - AdbUtils::makeTap(device.id, device.screenWidth / 2, 100); + AdbUtils::makeTap(device.id, device.screenWidth / 2, device.screenHeight / 20); QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); } @@ -223,7 +223,7 @@ void PayByPhoneScript::makePayment( // Проверяем: уже на экране суммы? Node sumTitle = xmlScreenParser.findTextNode("По номеру телефона"); - if (!sumTitle.isEmpty() && sumTitle.y() < 300) { + if (!sumTitle.isEmpty() && sumTitle.y() < height / 7) { sumScreenLoaded = true; break; } diff --git a/android/ozon/ScreenXmlParser.cpp b/android/ozon/ScreenXmlParser.cpp index 9d91bc9..55d6025 100644 --- a/android/ozon/ScreenXmlParser.cpp +++ b/android/ozon/ScreenXmlParser.cpp @@ -272,8 +272,8 @@ Node ScreenXmlParser::findUserNameOnHomeScreen(const int screenHeight) { } QString ScreenXmlParser::parseProfileFullName() { - // Полное имя — TextView ниже зоны заголовка, выше строки с телефоном. - // В profile.xml это единственный короткий нейтральный TextView в диапазоне y ≈ 400–750. + // Полное имя — первый TextView ниже заголовка "Аккаунт", + // который не является известным элементом UI и не содержит спецсимволов. static const QSet uiLabels = { "Аккаунт", "Мои документы", "Паспорт и ИНН", "Уровень счёта Лимиты и тарифы", "Повысить", @@ -283,11 +283,34 @@ QString ScreenXmlParser::parseProfileFullName() { "QR-код для пополнений Принимайте чаевые и донаты", "Добавить Почта" }; - for (const Node &node: m_nodes) { + + // Находим y-координату заголовка "Аккаунт" + int titleY = -1; + for (const Node &node : m_nodes) { + if (node.className == "android.widget.TextView" + && node.text.trimmed() == "Аккаунт" + && node.width() > 50) { + titleY = node.y(); + break; + } + } + if (titleY < 0) return {}; + + // Находим y-координату блока с телефоном (ограничение снизу) + int phoneY = INT_MAX; + for (const Node &node : m_nodes) { + if (node.text.contains("Номер телефона")) { + phoneY = node.y(); + break; + } + } + + // Первый подходящий TextView между заголовком и телефоном + for (const Node &node : m_nodes) { if (node.className == "android.widget.TextView" && !node.text.trimmed().isEmpty() - && node.y() > 400 - && node.y() < 750 + && node.y() > titleY + && node.y() < phoneY && !uiLabels.contains(node.text.trimmed())) { return node.text.trimmed(); } @@ -1333,12 +1356,14 @@ QList > ScreenXmlParser::parseAccountsInfo() { } QString ScreenXmlParser::findFullCardNumber() { - static const QRegularExpression cardNumberRegex(R"(^\d{4}\s\d{4}\s\d{4}\s\d{4}$)"); + static const QRegularExpression cardNumberRegex(R"(^\d{4}[\s\x{00A0}\x{202F}]\d{4}[\s\x{00A0}\x{202F}]\d{4}[\s\x{00A0}\x{202F}]\d{4}$)"); for (const Node &node : m_nodes) { if (node.className == "android.widget.TextView") { const QString text = node.text.trimmed(); if (cardNumberRegex.match(text).hasMatch()) { - return QString(text).remove(QChar(' ')); + QString result = text; + result.remove(QChar(' ')).remove(QChar(0x00A0)).remove(QChar(0x202F)); + return result; } } }