#include "GetCardInfoScript.h" #include #include #include #include "adb/AdbUtils.h" #include "dao/MaterialDAO.h" #include "dao/BankProfileDAO.h" #include "dao/DeviceDAO.h" #include "dump/TaskDumpRecorder.h" #include "net/NetworkService.h" namespace Dushanbe { GetCardInfoScript::GetCardInfoScript( QString deviceId, QString appCode, QObject *parent ) : CommonScript(parent), m_deviceId(std::move(deviceId)), m_appCode(std::move(appCode)) { } GetCardInfoScript::~GetCardInfoScript() = default; void GetCardInfoScript::doStart() { DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); if (device.id.isEmpty()) device = DeviceDAO::findByAdbSerial(m_deviceId); if (!device.adbSerial.isEmpty()) m_deviceId = device.adbSerial; const BankProfileInfo app = BankProfileDAO::getApplication(device.id, m_appCode); if (device.id.isEmpty() || app.id == -1) { m_error = "Device or app not found: " + m_deviceId + " " + m_appCode; qCritical() << m_error; emit finishedWithResult(m_error); return; } const TaskDumpMeta dumpMeta{}; TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight); if (!xmlScreenParser.isHomeScreen()) { qDebug() << "[Dushanbe::GetCardInfo] Not on home screen, restarting app..."; if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { m_error = "Cannot reach home screen: " + device.name + " (" + m_deviceId + ") " + m_appCode; qWarning() << m_error; AdbUtils::tryToKillApplication(m_deviceId, app.package); emit finishedWithResult(m_error); return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight); } // Парсим баланс с домашнего экрана const double balance = xmlScreenParser.parseBalanceFromHomeScreen(); qDebug() << "[Dushanbe::GetCardInfo] balance:" << balance; if (!BankProfileDAO::updateAmount(app.id, balance)) { qWarning() << "[Dushanbe::GetCardInfo] Failed to update balance for app" << app.id; } // Тапаем "Карты" на домашнем экране Node cardsButton = xmlScreenParser.findNodeByContentDesc(QString::fromUtf8("Карты")); if (cardsButton.x() == 0 && cardsButton.y() == 0) { m_error = "Cannot find Cards button: " + m_deviceId; qWarning() << m_error; AdbUtils::tryToKillApplication(m_deviceId, app.package); emit finishedWithResult(m_error); return; } AdbUtils::makeTap(m_deviceId, cardsButton.x(), cardsButton.y()); QThread::msleep(3000); // Ждём экран "Мои карты" bool cardsScreenFound = false; for (int attempt = 0; attempt < 5; ++attempt) { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight); if (xmlScreenParser.isCardsScreen()) { cardsScreenFound = true; break; } QThread::msleep(1000); } if (!cardsScreenFound) { m_error = "Cards screen not detected: " + m_deviceId; qWarning() << m_error; AdbUtils::tryToKillApplication(m_deviceId, app.package); emit finishedWithResult(m_error); return; } // Парсим карты из списка (lastNumbers, баланс) const QList cards = xmlScreenParser.parseCards(); qDebug() << "[Dushanbe::GetCardInfo] Found" << cards.size() << "cards in list"; if (cards.isEmpty()) { m_error = "No cards found: " + m_deviceId; qWarning() << m_error; AdbUtils::tryToKillApplication(m_deviceId, app.package); emit finishedWithResult(m_error); return; } // Находим все clickable ImageView с "****" — это карточки в списке // Пропускаем кредитные карты QList cardNodes; for (const Node &node : xmlScreenParser.findAllNodes()) { if (node.className == "android.widget.ImageView" && node.clickable && node.contentDesc.contains("****") && !node.contentDesc.contains(QString::fromUtf8("Кредитная карта"))) { cardNodes.append(node); } } // Кликаем по каждой карте для получения полного номера for (int i = 0; i < cardNodes.size(); ++i) { if (i >= cards.size()) break; const Node &cardNode = cardNodes[i]; AdbUtils::makeTap(m_deviceId, cardNode.x(), cardNode.y()); QThread::msleep(2000); // Ждём экран детали карты bool detailFound = false; for (int attempt = 0; attempt < 5; ++attempt) { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight); if (xmlScreenParser.isCardDetailScreen()) { detailFound = true; break; } QThread::msleep(1000); } if (!detailFound) { qWarning() << "[Dushanbe::GetCardInfo] Card detail screen not found for card" << i; continue; } // Ждём загрузки баланса (может быть 0 пока данные подтягиваются) QThread::msleep(1500); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight); QString fullNumber = xmlScreenParser.parseFullCardNumber(); const double cardBalance = xmlScreenParser.parseCardDetailBalance(); const QString holder = xmlScreenParser.parseCardDetailHolder(); const QString fullNumberClean = QString(fullNumber).remove(' '); const QString lastNumbers = (fullNumberClean.length() >= 4) ? fullNumberClean.right(4) : (cards[i].lastNumbers.isEmpty() ? QString() : cards[i].lastNumbers); qDebug() << "[Dushanbe::GetCardInfo] Card" << i << ":" << fullNumber << "balance:" << cardBalance << "holder:" << holder; if (lastNumbers.isEmpty()) { qWarning() << "[Dushanbe::GetCardInfo] No card number for card" << i; AdbUtils::goBack(m_deviceId); QThread::msleep(1500); continue; } // Достаём номер счёта: Настройки карты → Реквизиты карты → таб "Реквизиты счета". // ВРЕМЕННО ОТКЛЮЧЕНО QString accountNumber; // const Node settingsBtn = xmlScreenParser.findCardSettingsButton(); // if (settingsBtn.x() > 0) { // AdbUtils::makeTap(m_deviceId, settingsBtn.x(), settingsBtn.y()); // QThread::msleep(1500); // // bool sheetFound = false; // for (int attempt = 0; attempt < 5; ++attempt) { // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // if (xmlScreenParser.isCardSettingsBottomSheet()) { // sheetFound = true; // break; // } // QThread::msleep(1000); // } // // if (sheetFound) { // const Node requisitesItem = xmlScreenParser.findCardRequisitesMenuItem(); // if (requisitesItem.x() > 0) { // AdbUtils::makeTap(m_deviceId, requisitesItem.x(), requisitesItem.y()); // QThread::msleep(1500); // // bool reqScreenFound = false; // for (int attempt = 0; attempt < 5; ++attempt) { // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // if (xmlScreenParser.isCardRequisitesScreen()) { // reqScreenFound = true; // break; // } // QThread::msleep(1000); // } // // if (reqScreenFound) { // const Node accountTab = xmlScreenParser.findAccountRequisitesTab(); // if (accountTab.x() > 0) { // AdbUtils::makeTap(m_deviceId, accountTab.x(), accountTab.y()); // QThread::msleep(1500); // // // Несколько попыток — данные могут подгружаться // for (int attempt = 0; attempt < 5; ++attempt) { // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // accountNumber = xmlScreenParser.parseAccountNumber(); // if (!accountNumber.isEmpty()) break; // QThread::msleep(1000); // } // qDebug() << "[Dushanbe::GetCardInfo] accountNumber for card" << i // << ":" << accountNumber; // } else { // qWarning() << "[Dushanbe::GetCardInfo] Account tab not found for card" << i; // } // } else { // qWarning() << "[Dushanbe::GetCardInfo] Requisites screen not detected for card" << i; // } // } else { // qWarning() << "[Dushanbe::GetCardInfo] 'Реквизиты карты' menu item not found for card" << i; // } // } else { // qWarning() << "[Dushanbe::GetCardInfo] Card settings bottom sheet not detected for card" << i; // } // // // Возврат на экран детали карты — bottom sheet и/или экран Реквизитов // for (int attempt = 0; attempt < 4; ++attempt) { // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // if (xmlScreenParser.isCardDetailScreen()) break; // AdbUtils::goBack(m_deviceId); // QThread::msleep(1200); // } // } else { // qWarning() << "[Dushanbe::GetCardInfo] 'Настройки карты' button not found for card" << i; // } MaterialInfo account; account.deviceId = device.id; account.appCode = m_appCode; account.lastNumbers = lastNumbers; account.cardNumber = QString(fullNumber).remove(' '); account.accountNumber = accountNumber; account.amount = cardBalance; account.currency = "TJS"; account.description = cards[i].description.isEmpty() ? holder : cards[i].description; account.status = MaterialStatus::Off; account.updateTime = QDateTime::currentDateTimeUtc(); qDebug() << "[Dushanbe::GetCardInfo] Saving card:" << "lastNumbers=" << lastNumbers << "cardNumber=" << account.cardNumber << "amount=" << cardBalance; if (!MaterialDAO::upsertAccount(account)) { qWarning() << "[Dushanbe::GetCardInfo] Failed to upsert account" << lastNumbers; } // Отправляем на сервер если нет material_id const BankProfileInfo updatedApp = BankProfileDAO::getApplication(device.id, m_appCode); if (!updatedApp.bankProfileId.isEmpty()) { const MaterialInfo savedAccount = MaterialDAO::findAppAccount(device.id, m_appCode, lastNumbers); if (savedAccount.id != -1 && savedAccount.materialId.isEmpty()) { NetworkService ns; ns.setDeviceId(m_deviceId); const QMap currencyCodes = { {"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32}, {"TJS", 972} }; QJsonObject materialBody; materialBody["bank_profile_id"] = updatedApp.bankProfileId; materialBody["card_number"] = QString(fullNumber).remove(' '); materialBody["account_number"] = accountNumber; materialBody["name"] = updatedApp.fullName; materialBody["state"] = materialStatusToString(savedAccount.status) == "active" ? "enabled" : "disabled"; materialBody["currency"] = currencyCodes.value(savedAccount.currency.toUpper(), 972); ns.setPayload(QJsonDocument(materialBody).toJson()); ns.addExtra("account_id", savedAccount.id); ns.postMaterial(); qDebug() << "[Dushanbe::GetCardInfo] postMaterial done for" << lastNumbers; } } // Назад к списку карт AdbUtils::goBack(m_deviceId); QThread::msleep(1500); // Ждём возврата на экран "Мои карты" for (int attempt = 0; attempt < 5; ++attempt) { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight); if (xmlScreenParser.isCardsScreen()) break; QThread::msleep(1000); } } AdbUtils::tryToKillApplication(m_deviceId, app.package); emit finishedWithResult(m_error); } } // namespace Dushanbe