#include "CommonScript.h" #include #include #include #include #include "adb/AdbUtils.h" #include "net/NetworkService.h" namespace Ozon { 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) { AdbUtils::makeTap(deviceId, node.x(), node.y()); } 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; bool findAndInputPincode = false; while (true) { counter++; // Если более 10 раз мы пытаемся перейти на домашнюю if (counter > 10) { AdbUtils::takeScreenshot(deviceId, "too_many_attempts"); return false; } 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; } // Проверяем на баннер Google Play "Доступно обновление" if (Node gpNode = xmlScreenParser.tryToFindGooglePlayBanner(); !gpNode.isEmpty()) { qDebug() << "[runAppAndGoToHomeScreen] Google Play update banner detected, closing..."; AdbUtils::makeTap(deviceId, gpNode.x(), gpNode.y()); QThread::msleep(1500); continue; } // Проверяем на рекламный bottom sheet (ипотека и т.п.) if (Node adNode = xmlScreenParser.tryToFindAdBanner(); !adNode.isEmpty()) { qDebug() << "[runAppAndGoToHomeScreen] Ad bottom sheet detected, closing..."; AdbUtils::makeTap(deviceId, adNode.x(), adNode.y()); QThread::msleep(1000); continue; } // Проверяем, что это экран ввода пин-кода if (!findAndInputPincode) { if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН"); pincode.size() == 4) { // чтобы пробудить экран if (Node textPinCodeNode = xmlScreenParser.findTextNode("Введите ПИН"); !textPinCodeNode.isEmpty()) { AdbUtils::makeTap(deviceId, textPinCodeNode.x(), textPinCodeNode.y()); } inputPinCode(deviceId, pincode); // После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить findAndInputPincode = true; QThread::msleep(3000); continue; } } QThread::msleep(1000); } } 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)); } // Ozon: домашний экран и есть "Финансы" — счета уже доступны if (xmlScreenParser.isHomeScreen()) { return true; } // Если по какой-то причине не на домашнем — пробуем тапнуть по вкладке "Финансы" for (int i = 0; i < 3; ++i) { if (Node financeTab = xmlScreenParser.findNodeByContentDesc("Финансы"); !financeTab.isEmpty()) { AdbUtils::makeTap(deviceId, financeTab.x(), financeTab.y()); QThread::msleep(1500); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); if (xmlScreenParser.isHomeScreen()) { return true; } } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } qDebug() << "goToAllProducts: 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.appCode; obj["lastNumbers"] = account.lastNumbers; obj["amount"] = account.amount; obj["description"] = account.description; obj["currency"] = account.currency; obj["status"] = accountStatusToString(account.status); list.append(obj); } // FIXME доставить десктоп ид из настроеек worker->setDeviceId(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->setDeviceId(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->setDeviceId(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; } } // namespace Ozon