Enhance error handling, improve UI parsing logic, and refine tap retry workflows across Ozon scripts
This commit is contained in:
parent
01f8801a73
commit
b42fb5eea9
@ -109,6 +109,25 @@ void GetCardInfoScript::doStart() {
|
|||||||
qDebug() << "[GetCardInfo] Profile info already in DB or username not found, skipping profile parse";
|
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 попыток
|
// Ждём пока все карты подгрузятся — до 5 попыток
|
||||||
QList<AccountInfo> cards;
|
QList<AccountInfo> cards;
|
||||||
for (int attempt = 0; attempt < 5; ++attempt) {
|
for (int attempt = 0; attempt < 5; ++attempt) {
|
||||||
@ -194,7 +213,17 @@ void GetCardInfoScript::doStart() {
|
|||||||
if (freshApp.bankProfileId.isEmpty()) {
|
if (freshApp.bankProfileId.isEmpty()) {
|
||||||
ns.addExtra("app_id", freshApp.id);
|
ns.addExtra("app_id", freshApp.id);
|
||||||
ns.postBankProfile();
|
ns.postBankProfile();
|
||||||
|
// Проверяем что 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";
|
qDebug() << "[GetCardInfo] postBankProfile done";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ns.addExtra("bank_profile_id", freshApp.bankProfileId);
|
ns.addExtra("bank_profile_id", freshApp.bankProfileId);
|
||||||
ns.patchBankProfile();
|
ns.patchBankProfile();
|
||||||
@ -227,16 +256,24 @@ void GetCardInfoScript::doStart() {
|
|||||||
|
|
||||||
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
|
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
|
||||||
|
|
||||||
// Ждём экран карты — ищем "Реквизиты карты" в XML дампе
|
// Ждём экран карты — ищем кнопку "Показать"
|
||||||
Node showBtn;
|
Node showBtn;
|
||||||
for (int attempt = 0; attempt < 10; ++attempt) {
|
for (int attempt = 0; attempt < 10; ++attempt) {
|
||||||
QThread::msleep(2000);
|
QThread::msleep(2000);
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
Node reqNode = xmlScreenParser.findTextNode("Реквизиты карты");
|
|
||||||
if (!reqNode.isEmpty()) {
|
|
||||||
showBtn = xmlScreenParser.findButtonNode("Показать", false);
|
showBtn = xmlScreenParser.findButtonNode("Показать", false);
|
||||||
if (!showBtn.isEmpty()) break;
|
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";
|
qDebug() << "[GetCardInfo] Card screen not loaded, attempt" << attempt + 1 << "/ 10";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,8 +314,27 @@ void GetCardInfoScript::doStart() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "Подождите" — загрузка в процессе, просто ждём
|
||||||
|
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";
|
qDebug() << "[GetCardInfo] Card number not visible, attempt" << attempt + 1 << "/ 10";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!cardNumber.isEmpty()) {
|
if (!cardNumber.isEmpty()) {
|
||||||
qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "full number:" << cardNumber;
|
qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "full number:" << cardNumber;
|
||||||
@ -288,7 +344,16 @@ void GetCardInfoScript::doStart() {
|
|||||||
AccountDAO::updateCardNumber(saved.id, cardNumber);
|
AccountDAO::updateCardNumber(saved.id, cardNumber);
|
||||||
}
|
}
|
||||||
} else {
|
} 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["bank_profile_id"] = updatedApp.bankProfileId;
|
||||||
materialBody["card_number"] = dbCard.cardNumber;
|
materialBody["card_number"] = dbCard.cardNumber;
|
||||||
materialBody["name"] = updatedApp.fullName;
|
materialBody["name"] = updatedApp.fullName;
|
||||||
|
materialBody["state"] = accountStatusToString(dbCard.status) == "active" ? "enabled" : "disabled";
|
||||||
ns.setPayload(QJsonDocument(materialBody).toJson());
|
ns.setPayload(QJsonDocument(materialBody).toJson());
|
||||||
ns.addExtra("account_id", dbCard.id);
|
ns.addExtra("account_id", dbCard.id);
|
||||||
ns.postMaterial();
|
ns.postMaterial();
|
||||||
|
|||||||
@ -239,6 +239,13 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
detail = xmlScreenParser.parseTransactionDetail();
|
detail = xmlScreenParser.parseTransactionDetail();
|
||||||
if (!detail.bankTime.isEmpty()) break;
|
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";
|
qDebug() << "[GetLastTransactions] Detail not loaded, attempt" << attempt + 1 << "/ 10";
|
||||||
QThread::msleep(1500);
|
QThread::msleep(1500);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "dao/ApplicationDAO.h"
|
#include "dao/ApplicationDAO.h"
|
||||||
#include "dao/DeviceDAO.h"
|
#include "dao/DeviceDAO.h"
|
||||||
#include "net/NetworkService.h"
|
#include "net/NetworkService.h"
|
||||||
|
#include "AppLogger.h"
|
||||||
|
|
||||||
namespace Ozon {
|
namespace Ozon {
|
||||||
|
|
||||||
@ -141,7 +142,16 @@ void GetProfileInfoScript::doStart() {
|
|||||||
if (freshApp.bankProfileId.isEmpty()) {
|
if (freshApp.bankProfileId.isEmpty()) {
|
||||||
ns.addExtra("app_id", freshApp.id);
|
ns.addExtra("app_id", freshApp.id);
|
||||||
ns.postBankProfile();
|
ns.postBankProfile();
|
||||||
|
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";
|
qDebug() << "[GetProfileInfo] postBankProfile done";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ns.addExtra("bank_profile_id", freshApp.bankProfileId);
|
ns.addExtra("bank_profile_id", freshApp.bankProfileId);
|
||||||
ns.patchBankProfile();
|
ns.patchBankProfile();
|
||||||
|
|||||||
@ -121,7 +121,7 @@ void PayByCardScript::doStart() {
|
|||||||
// Закрываем баннер "Заказать карту" — тапаем сверху экрана
|
// Закрываем баннер "Заказать карту" — тапаем сверху экрана
|
||||||
if (Node orderCardNode = xmlScreenParser.findTextNode("Заказать карту"); !orderCardNode.isEmpty()) {
|
if (Node orderCardNode = xmlScreenParser.findTextNode("Заказать карту"); !orderCardNode.isEmpty()) {
|
||||||
qDebug() << "[PayByCard] 'Заказать карту' banner detected, closing...";
|
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);
|
QThread::msleep(1000);
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,7 +122,7 @@ void PayByPhoneScript::doStart() {
|
|||||||
// Закрываем баннер "Заказать карту" — тапаем сверху экрана
|
// Закрываем баннер "Заказать карту" — тапаем сверху экрана
|
||||||
if (Node orderCardNode = xmlScreenParser.findTextNode("Заказать карту"); !orderCardNode.isEmpty()) {
|
if (Node orderCardNode = xmlScreenParser.findTextNode("Заказать карту"); !orderCardNode.isEmpty()) {
|
||||||
qDebug() << "[PayByPhone] 'Заказать карту' banner detected, closing...";
|
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);
|
QThread::msleep(1000);
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
||||||
}
|
}
|
||||||
@ -223,7 +223,7 @@ void PayByPhoneScript::makePayment(
|
|||||||
|
|
||||||
// Проверяем: уже на экране суммы?
|
// Проверяем: уже на экране суммы?
|
||||||
Node sumTitle = xmlScreenParser.findTextNode("По номеру телефона");
|
Node sumTitle = xmlScreenParser.findTextNode("По номеру телефона");
|
||||||
if (!sumTitle.isEmpty() && sumTitle.y() < 300) {
|
if (!sumTitle.isEmpty() && sumTitle.y() < height / 7) {
|
||||||
sumScreenLoaded = true;
|
sumScreenLoaded = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,8 +272,8 @@ Node ScreenXmlParser::findUserNameOnHomeScreen(const int screenHeight) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString ScreenXmlParser::parseProfileFullName() {
|
QString ScreenXmlParser::parseProfileFullName() {
|
||||||
// Полное имя — TextView ниже зоны заголовка, выше строки с телефоном.
|
// Полное имя — первый TextView ниже заголовка "Аккаунт",
|
||||||
// В profile.xml это единственный короткий нейтральный TextView в диапазоне y ≈ 400–750.
|
// который не является известным элементом UI и не содержит спецсимволов.
|
||||||
static const QSet<QString> uiLabels = {
|
static const QSet<QString> uiLabels = {
|
||||||
"Аккаунт", "Мои документы", "Паспорт и ИНН",
|
"Аккаунт", "Мои документы", "Паспорт и ИНН",
|
||||||
"Уровень счёта Лимиты и тарифы", "Повысить",
|
"Уровень счёта Лимиты и тарифы", "Повысить",
|
||||||
@ -283,11 +283,34 @@ QString ScreenXmlParser::parseProfileFullName() {
|
|||||||
"QR-код для пополнений Принимайте чаевые и донаты",
|
"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"
|
if (node.className == "android.widget.TextView"
|
||||||
&& !node.text.trimmed().isEmpty()
|
&& !node.text.trimmed().isEmpty()
|
||||||
&& node.y() > 400
|
&& node.y() > titleY
|
||||||
&& node.y() < 750
|
&& node.y() < phoneY
|
||||||
&& !uiLabels.contains(node.text.trimmed())) {
|
&& !uiLabels.contains(node.text.trimmed())) {
|
||||||
return node.text.trimmed();
|
return node.text.trimmed();
|
||||||
}
|
}
|
||||||
@ -1333,12 +1356,14 @@ QList<QPair<AccountInfo, Node> > ScreenXmlParser::parseAccountsInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString ScreenXmlParser::findFullCardNumber() {
|
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) {
|
for (const Node &node : m_nodes) {
|
||||||
if (node.className == "android.widget.TextView") {
|
if (node.className == "android.widget.TextView") {
|
||||||
const QString text = node.text.trimmed();
|
const QString text = node.text.trimmed();
|
||||||
if (cardNumberRegex.match(text).hasMatch()) {
|
if (cardNumberRegex.match(text).hasMatch()) {
|
||||||
return QString(text).remove(QChar(' '));
|
QString result = text;
|
||||||
|
result.remove(QChar(' ')).remove(QChar(0x00A0)).remove(QChar(0x202F));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user