#include "CommonScript.h" #include #include #include #include #include "adb/AdbUtils.h" #include "net/NetworkService.h" CommonScript::CommonScript(QObject *parent) : QObject(parent) { } CommonScript::~CommonScript() = default; void CommonScript::start() { doStart(); emit finished(); } Node CommonScript::swipeToButton( const QString &deviceId, const QString &text, int &counter, const int width, const int height ) { for (int i = 0; i < 10; ++i) { if (Node button = xmlScreenParser.findButtonNode(text, true); button.x() > 0 && button.y() > 0) { return button; } const int x = width / 2; const int offset = height / 4; AdbUtils::makeSwipe(deviceId, x, height - offset, x, offset); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); } return {}; } bool CommonScript::inputPinCode(const QString &deviceId, const QList &pincode) { for (const Node &node: pincode) { const bool completePincode = AdbUtils::makeTap(deviceId, node.x(), node.y()); if (!completePincode) { return false; } QThread::msleep(QRandomGenerator::global()->bounded(100, 901)); } return true; } bool CommonScript::runAppAndGoToHomeScreen( const QString &deviceId, const QString &packageName, const QString &pinCode, const int width, const int height ) { // Запустить устройство if (const QString pid = AdbUtils::tryToGetRunningAppPid(deviceId, packageName); !pid.isEmpty()) { qDebug() << "Process already running, pid: " << pid; AdbUtils::getScreenDumpAsXml(deviceId, 1000); AdbUtils::tryToKillApplication(deviceId, packageName); } if (!AdbUtils::tryToStartApplication(deviceId, packageName)) { AdbUtils::takeScreenshot(deviceId, "run_app_error"); qWarning() << "Process has not started"; return false; } // Даем минимальное время на запуск QThread::msleep(4500); int counter = 0; int restarted = 0; int totalCounter = 0; bool findAndInputPincode = false; while (true) { counter++; totalCounter++; // 3 раза уже перезапускали приложение, завершаем попытки if (restarted > 2) { AdbUtils::takeScreenshot(deviceId, "3_times_try_start"); return false; } // Если более 30 секунд мы пытаемся перейти на домашнюю, перезапускаем if (counter > 30) { AdbUtils::tryToKillApplication(deviceId, packageName); QThread::msleep(4500); ++restarted; counter = 0; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); if (xmlScreenParser.isHomeScreen()) { return true; } // Проверяем на банеры после пин-кодом Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(width, height); if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) { AdbUtils::makeTap(deviceId, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y()); QThread::msleep(1000); continue; } // Проверяем на банеры перед пин-кодом Node closeBannerNode = xmlScreenParser.tryToFindSecurityBanner(); if (!closeBannerNode.isEmpty()) { AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); QThread::msleep(1000); continue; } // Проверяем, что это экран ввода пин-кода if (!findAndInputPincode) { if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН"); pincode.size() == 4) { inputPinCode(deviceId, pincode); // После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить findAndInputPincode = true; QThread::msleep(3000); continue; } } QThread::msleep(1000); // Если что-то пойдет не по плану... if (totalCounter > 100) { AdbUtils::takeScreenshot(deviceId, "too_many_attempts"); return false; } } } bool CommonScript::tryToCloseAllBannersOnHomePage(const QString &deviceId, const int width, const int height) { Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(width, height); if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) { AdbUtils::makeTap(deviceId, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y()); return true; } // TODO тут все другие банеры попробовать закрыть return false; } bool CommonScript::goToAllProducts( const QString &deviceId, const int width, const int height ) { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter)); if (tryToCloseAllBannersOnHomePage(deviceId, width, height)) { QThread::msleep(500); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } if (xmlScreenParser.isHomeScreen()) { // идем на экран "Все продукты" if (Node allCardTitle = xmlScreenParser.findTextNode("Счета и карты"); !allCardTitle.isEmpty()) { // 1. Если нет кнопки видимой, свайпаем. Кликаем на "Все продукты" for (int i = 0; i < 5; ++i) { Node allProductsBtn = xmlScreenParser.findButtonNode("Все продукты", false); if (allProductsBtn.x() > 0 && allProductsBtn.y() > 0) { AdbUtils::makeTap(deviceId, allProductsBtn.x(), allProductsBtn.y()); QThread::msleep(1000); break; } AdbUtils::makeSwipe(deviceId, allCardTitle.x(), allCardTitle.y(), allCardTitle.x(), allCardTitle.y() - 200); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } for (int i = 0; i < 5; ++i) { Node allProductsTitle = xmlScreenParser.findTextNode("Все продукты"); // Проверяем что вверху заголовок if (!allProductsTitle.isEmpty() && allProductsTitle.y() < 300) { return true; } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } } else { // FIXME пишем в БД, что не дошли до домашней страницы, и завершаемся // сохраняем скрин приложения // дделаем алерт } } else { // FIXME qDebug() << "Not home screen..."; } return false; } bool CommonScript::findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers) { QList > accounts = xmlScreenParser.parseAccountsInfo(); for (auto &[account, node]: accounts) { // FIXME будем считать что числа последние везде уникальные if (accountNumbers == account.lastNumbers) { qDebug() << "findAndTapOnAccount: " << accountNumbers; AdbUtils::makeTap(deviceId, node.x(), node.y()); QThread::msleep(1000); return true; } } return false; } void CommonScript::postAccountsData(const QList > &accounts) { if (accounts.isEmpty()) { return; } auto *thread = new QThread; auto *worker = new NetworkService; QJsonArray list; for (const QPair &pair: accounts) { QJsonObject obj; AccountInfo account = pair.first; obj["appName"] = account.appName; obj["lastNumbers"] = account.lastNumbers; obj["amount"] = account.amount; obj["description"] = account.description; list.append(obj); } // FIXME доставить десктоп ид из настроеек worker->setDeviceData("DT88bokcwQ", accounts.first().first.deviceId); worker->setPayload(QJsonDocument(list).toJson()); worker->moveToThread(thread); connect(thread, &QThread::started, worker, &NetworkService::postAccountsData); connect(worker, &NetworkService::finished, thread, &QThread::quit); connect(worker, &NetworkService::finished, worker, &QObject::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void CommonScript::postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction) { auto *thread = new QThread; auto *worker = new NetworkService; QJsonObject obj; obj["id"] = transaction.id; obj["amount"] = transaction.amount; obj["name"] = transaction.name; obj["fee"] = transaction.fee; obj["status"] = transactionStatusToString(transaction.status); obj["type"] = transactionTypeToString(transaction.type); obj["phone"] = transaction.phone; obj["description"] = transaction.description; obj["updatedDescription"] = transaction.updatedDescription; obj["bankName"] = transaction.bankName; obj["bankTime"] = transaction.bankTime; obj["bankTransactionId"] = transaction.bankTrExternalId; // FIXME доставить десктоп ид из настроеек worker->setDeviceData("DT88bokcwQ", account.deviceId); worker->addExtra("lastNumbers", account.lastNumbers); worker->addExtra("transactionId", transaction.id); worker->setPayload(QJsonDocument(obj).toJson()); worker->moveToThread(thread); connect(thread, &QThread::started, worker, &NetworkService::insertTransactionData); connect(worker, &NetworkService::finished, thread, &QThread::quit); connect(worker, &NetworkService::finished, worker, &QObject::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void CommonScript::updateTransactionData( const AccountInfo &account, const int transactionId, const TransactionStatus status, const QString &oldDesc, const QString &newDesc ) { auto *thread = new QThread; auto *worker = new NetworkService; QJsonObject obj; obj["id"] = transactionId; obj["status"] = transactionStatusToString(status); obj["description"] = newDesc; obj["updatedDescription"] = oldDesc; // FIXME доставить десктоп ид из настроеек worker->setDeviceData("DT88bokcwQ", account.deviceId); worker->addExtra("lastNumbers", account.lastNumbers); worker->setPayload(QJsonDocument(obj).toJson()); worker->moveToThread(thread); connect(thread, &QThread::started, worker, &NetworkService::updateTransactionData); connect(worker, &NetworkService::finished, thread, &QThread::quit); connect(worker, &NetworkService::finished, worker, &QObject::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void CommonScript::stop() { m_running = false; }