#include "GetCardInfoScript.h" #include #include #include #include "adb/AdbUtils.h" #include "dao/MaterialDAO.h" #include "dao/BankProfileDAO.h" #include "dao/DeviceDAO.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; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); 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)); } // Парсим баланс с домашнего экрана const double balance = xmlScreenParser.parseBalanceFromHomeScreen(); qDebug() << "[Dushanbe::GetCardInfo] balance:" << balance; BankProfileDAO::updateAmount(app.id, balance); // Тапаем "Карты" на домашнем экране 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)); 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)); 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)); 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; } MaterialInfo account; account.deviceId = device.id; account.appCode = m_appCode; account.lastNumbers = lastNumbers; account.cardNumber = QString(fullNumber).remove(' '); account.amount = cardBalance; account.currency = "TJS"; account.description = cards[i].description.isEmpty() ? holder : cards[i].description; account.status = MaterialStatus::Active; 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); QJsonObject materialBody; materialBody["bank_profile_id"] = updatedApp.bankProfileId; materialBody["card_number"] = QString(fullNumber).remove(' '); materialBody["name"] = updatedApp.fullName; materialBody["state"] = materialStatusToString(savedAccount.status) == "active" ? "enabled" : "disabled"; 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)); if (xmlScreenParser.isCardsScreen()) break; QThread::msleep(1000); } } AdbUtils::tryToKillApplication(m_deviceId, app.package); emit finishedWithResult(m_error); } } // namespace Dushanbe