diff --git a/CMakeLists.txt b/CMakeLists.txt index 16901bd..2aa3cc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,10 @@ add_executable(ARCDesktopProject WIN32 ${ANDROID_SOURCES} views/bank/BankSettingsWindow.cpp views/bank/BankSettingsWindow.h + views/log/FileLogWindow.cpp + views/log/FileLogWindow.h + database/dao/GeneralLogDAO.cpp + database/dao/GeneralLogDAO.h ) elseif (APPLE) add_executable(ARCDesktopProject @@ -103,6 +107,10 @@ elseif (APPLE) ${ANDROID_SOURCES} views/bank/BankSettingsWindow.cpp views/bank/BankSettingsWindow.h + views/log/FileLogWindow.cpp + views/log/FileLogWindow.h + database/dao/GeneralLogDAO.cpp + database/dao/GeneralLogDAO.h ) endif () diff --git a/android/adb/AdbUtils.cpp b/android/adb/AdbUtils.cpp index 8ad1e85..8b86efb 100644 --- a/android/adb/AdbUtils.cpp +++ b/android/adb/AdbUtils.cpp @@ -4,6 +4,7 @@ #include #include #include +#include AdbUtils::AdbUtils(QObject *parent) : QObject(parent) { } @@ -198,9 +199,18 @@ QString AdbUtils::getScreenDumpAsXml(const QString &deviceId, int idx, bool scre const QString adb = adbPath(); QString output; - // 1. Dump UI hierarchy - if (!startProcess(adb, {"-s", deviceId, "shell", "uiautomator", "dump", "/sdcard/uidump.xml"})) { - qWarning() << "Failed to dump UI hierarchy."; + // 1. Dump UI hierarchy (retry up to 3 times — uiautomator may be killed by Android) + bool dumpOk = false; + for (int attempt = 0; attempt < 3; ++attempt) { + if (startProcess(adb, {"-s", deviceId, "shell", "uiautomator", "dump", "/sdcard/uidump.xml"})) { + dumpOk = true; + break; + } + qWarning() << "[AdbUtils] uiautomator dump failed, attempt" << (attempt + 1) << "of 3"; + QThread::msleep(1000); + } + if (!dumpOk) { + qWarning() << "Failed to dump UI hierarchy after 3 attempts."; return {}; } // 2. Optionally pull and save the XML file diff --git a/android/birbank/CommonBirbankScript.cpp b/android/birbank/CommonBirbankScript.cpp deleted file mode 100644 index 3c2dba3..0000000 --- a/android/birbank/CommonBirbankScript.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "CommonBirbankScript.h" - -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "net/NetworkService.h" -#include "time/DateUtils.h" - -CommonBirbankScript::CommonBirbankScript(QObject *parent) - : QObject(parent) { -} - -CommonBirbankScript::~CommonBirbankScript() = default; - -void CommonBirbankScript::start() { - doStart(); - emit finished(); -} - -Node CommonBirbankScript::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 CommonBirbankScript::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(2500); - 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)); - - Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - continue; - } - - if (xmlScreenParser.isHomeScreen()) { - return true; - } - - // Проверяем, что это экран ввода пин-кода - if (!findAndInputPincode) { - if (const QList pincode = xmlScreenParser.findPinCodeNodes(pinCode); pincode.size() == 4) { - for (const Node &node: pincode) { - AdbUtils::makeTap(deviceId, node.x(), node.y()); - QThread::msleep(QRandomGenerator::global()->bounded(200, 901)); - } - // После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить - findAndInputPincode = true; - QThread::msleep(3000); - continue; - } - } - - QThread::msleep(1000); - } -} - -bool CommonBirbankScript::goToAllProducts( - const QString &deviceId, - const int width, - const int height -) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter)); - Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(500); - } - - if (xmlScreenParser.isHomeScreen()) { - Node allMyAccountBtn = xmlScreenParser.findButtonNode("All My Accounts", false); - if (!allMyAccountBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, allMyAccountBtn.x(), allMyAccountBtn.y()); - QThread::msleep(500); - return true; - } - } - return false; -} - -void CommonBirbankScript::postAccountsData(const QList &accounts) { - if (accounts.isEmpty()) { - return; - } - - auto *thread = new QThread; - auto *worker = new NetworkService; - - QJsonArray list; - for (const AccountInfo &account: accounts) { - QJsonObject obj; - obj["appName"] = account.appCode; - obj["lastNumbers"] = account.lastNumbers; - obj["amount"] = account.amount; - obj["currency"] = account.currency; - obj["description"] = account.description; - obj["status"] = accountStatusToString(account.status); - - list.append(obj); - } - // FIXME доставить десктоп ид из настроеек - worker->setDeviceId(accounts.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 CommonBirbankScript::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 + " | " + transaction.bankTime; - obj["updatedDescription"] = transaction.updatedDescription; - obj["bankName"] = transaction.bankName; - obj["bankTime"] = DateUtils::toUtcString(transaction.completeTime); - 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 CommonBirbankScript::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 CommonBirbankScript::stop() { - m_running = false; -} diff --git a/android/birbank/CommonBirbankScript.h b/android/birbank/CommonBirbankScript.h deleted file mode 100644 index 37b2ecb..0000000 --- a/android/birbank/CommonBirbankScript.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include "ScreenXmlBirbankParser.h" -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" - -class CommonBirbankScript : public QObject { - Q_OBJECT - -public: - explicit CommonBirbankScript(QObject *parent = nullptr); - - ~CommonBirbankScript() override; - -protected: - virtual void doStart() = 0; - - bool goToAllProducts( - const QString &deviceId, - int width, - int height - ); - - void postAccountsData(const QList &accounts); - - void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction); - - void updateTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); - - Node swipeToButton( - const QString &deviceId, - const QString &text, - int &counter, int width, int height - ); - - bool runAppAndGoToHomeScreen( - const QString &deviceId, - const QString &packageName, - const QString &pinCode, - int width, - int height - ); - - bool m_running = true; - ScreenXmlBirbankParser xmlScreenParser; - int m_counter = 0; - -signals: - void finished(); - - void finishedWithResult(const QString &result); - -public slots: - virtual void start() final; - - virtual void stop() final; - -private: - Q_DISABLE_COPY(CommonBirbankScript); -}; diff --git a/android/birbank/GetLastDaysHistoryBirbankScript.cpp b/android/birbank/GetLastDaysHistoryBirbankScript.cpp deleted file mode 100644 index 5966518..0000000 --- a/android/birbank/GetLastDaysHistoryBirbankScript.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "GetLastDaysHistoryBirbankScript.h" - -#include -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/DeviceInfo.h" -#include "net/NetworkService.h" -#include "time/DateUtils.h" - -GetLastDaysHistoryBirbankScript::GetLastDaysHistoryBirbankScript( - AccountInfo account, - QObject *parent -) : CommonBirbankScript(parent), - m_account(std::move(account)) { -} - -GetLastDaysHistoryBirbankScript::~GetLastDaysHistoryBirbankScript() = default; - -bool GetLastDaysHistoryBirbankScript::saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status -) { - TransactionInfo tr; - tr.accountId = account.id; - tr.description = transaction.description; - tr.bankTime = transaction.bankTime; - tr.bankName = transaction.bankName; - tr.amount = transaction.amount; - tr.phone = transaction.phone; - tr.updateTime = DateUtils::getUtcNow(); - tr.fee = transaction.fee; - tr.name = transaction.name; - tr.bankTrExternalId = transaction.bankTrExternalId; - tr.status = status; - tr.type = TransactionType::Card; - tr.completeTime = transaction.completeTime; - tr.id = TransactionDAO::insertTransaction(tr); - - if (tr.id == -1) { - return false; - } - postNewTransactionData(account, tr); - return true; -} - -bool GetLastDaysHistoryBirbankScript::updateAndPostTransactionData( - const AccountInfo &account, - const int transactionId, - const TransactionStatus status, - const QString &oldDesc, - const QString &newDesc -) { - if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) { - updateTransactionData(account, transactionId, status, oldDesc, newDesc); - return true; - } - return false; -} - -void GetLastDaysHistoryBirbankScript::doStart() { - // берем все транзакции из БД - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(device.id, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - } - - if (xmlScreenParser.isHomeScreen()) { - // QList localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); - - QList oldAccounts = AccountDAO::getAccounts(m_account.deviceId, m_account.appCode); - QMap accountMap; - for (AccountInfo &oldAccount: oldAccounts) { - if (oldAccount.status == AccountStatus::Active) { - accountMap.insert(oldAccount.lastNumbers, oldAccount); - } - } - QList accounts = xmlScreenParser.parseAccountsInfo(); - for (auto account: accounts) { - if (accountMap.contains(account.lastNumbers)) { - account.deviceId = m_account.deviceId; - account.appCode = m_account.appCode; - account.currency = accountMap[account.lastNumbers].currency; - account.description = accountMap[account.lastNumbers].description; - account.status = AccountStatus::Active; - if (!AccountDAO::upsertAccount(account)) { - m_error = "Not updated: " + account.lastNumbers; - qCritical() << m_error; - } - } - } - - QList savedAccounts = AccountDAO::getAccounts(m_account.deviceId, m_account.appCode); - postAccountsData(savedAccounts); - - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node cardText = xmlScreenParser.findTextNode(m_account.lastNumbers); - if (!cardText.isEmpty()) { - AdbUtils::makeTap(device.id, cardText.x(), cardText.y()); - QThread::msleep(2000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node cardTitleText = xmlScreenParser.findTextNode(m_account.lastNumbers); - Node cardHistory = xmlScreenParser.findTextNode("История платежей"); - if (!cardTitleText.isEmpty() && !cardHistory.isEmpty()) { - QList transactions = xmlScreenParser.parsePositiveTransactions(); - QList savedTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); - for (TransactionInfo &transaction: transactions) { - bool isSaved = false; - for (TransactionInfo &savedTransaction: savedTransactions) { - if (savedTransaction.amount == transaction.amount - && savedTransaction.bankTime == transaction.bankTime) { - isSaved = true; - break; - } - } - if (!isSaved) { - if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Complete)) { - qDebug() << "--- saveAndPostNewTransactionData failed"; - } - } - } - } else { - m_error = "Card page not found: " + m_account.deviceId + " " + m_account.lastNumbers; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Card not found: " + m_account.deviceId + " " + m_account.lastNumbers; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - } - AdbUtils::goBack(m_account.deviceId); - emit finishedWithResult(m_error); -} diff --git a/android/birbank/GetLastDaysHistoryBirbankScript.h b/android/birbank/GetLastDaysHistoryBirbankScript.h deleted file mode 100644 index 1895378..0000000 --- a/android/birbank/GetLastDaysHistoryBirbankScript.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "CommonBirbankScript.h" - -class GetLastDaysHistoryBirbankScript final : public CommonBirbankScript { - Q_OBJECT - -public: - ~GetLastDaysHistoryBirbankScript() override; - - explicit GetLastDaysHistoryBirbankScript( - AccountInfo account, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - QString m_error; - - bool saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status - ); - - bool updateAndPostTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); -}; diff --git a/android/birbank/LoginAndCheckAccountsBirbankScript.cpp b/android/birbank/LoginAndCheckAccountsBirbankScript.cpp deleted file mode 100644 index 340f842..0000000 --- a/android/birbank/LoginAndCheckAccountsBirbankScript.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "LoginAndCheckAccountsBirbankScript.h" - -#include - -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "time/DateUtils.h" - -LoginAndCheckAccountsBirbankScript::LoginAndCheckAccountsBirbankScript( - QString deviceId, - QString appCode, - QObject *parent -) : CommonBirbankScript(parent), - m_deviceId(std::move(deviceId)), m_appCode(std::move(appCode)) { -} - -LoginAndCheckAccountsBirbankScript::~LoginAndCheckAccountsBirbankScript() = default; - - -void LoginAndCheckAccountsBirbankScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, m_appCode); - - - if (true) { - // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - // if (!xmlScreenParser.isHomeScreen()) { - // if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - // m_error = "Не смогли дойти до домашней страницы"; - // qCritical() << m_error; - // emit finishedWithResult(m_error); - // return; - // } - // } - // - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - - Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(m_deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - } - - if (xmlScreenParser.isHomeScreen()) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node cardText = xmlScreenParser.findTextNode("2480"); - if (!cardText.isEmpty()) { - AdbUtils::makeTap(m_deviceId, cardText.x(), cardText.y()); - QThread::msleep(2000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node payBtn = xmlScreenParser.findTextNode("Перевести"); - if (!payBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, payBtn.x(), payBtn.y()); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node payByCardBtn = xmlScreenParser.findTextNode("На карту любого банка"); - if (!payByCardBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, payByCardBtn.x(), payByCardBtn.y()); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node cardEditText = xmlScreenParser.findEditText("Номер карты"); - Node amountEditText = xmlScreenParser.findEditText("Сумма"); - if (!cardEditText.isEmpty() && !amountEditText.isEmpty()) { - AdbUtils::makeTap(m_deviceId, cardEditText.x(), cardEditText.y()); - QThread::msleep(200); - AdbUtils::inputText(m_deviceId, "1111222233334444"); - - AdbUtils::hideKeyboard(m_deviceId); - - AdbUtils::makeTap(m_deviceId, amountEditText.x(), amountEditText.y()); - QThread::msleep(200); - AdbUtils::inputText(m_deviceId, "10"); - - AdbUtils::hideKeyboard(m_deviceId); - - - // дальше оплата, но хз как - } - - } else { - m_error = "Pay by card button not found: " + m_deviceId + " " + m_appCode; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Pay button not found: " + m_deviceId + " " + m_appCode; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - - } - } - - } else { - // "Maraqlı deyil" - не интересно - 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 (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Cant open the home screen: " + m_deviceId + " " + m_appCode; - qWarning() << m_error; - if (!ApplicationDAO::updatePinCodeStatus(app.id, "no", "Не прошла проверка [дата]")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finishedWithResult(m_error); - return; - } else { - if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - QThread::msleep(2000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - QList oldAccounts = AccountDAO::getAccounts(m_deviceId, m_appCode); - QMap accountMap; - for (AccountInfo &oldAccount: oldAccounts) { - if (oldAccount.status == AccountStatus::Active) { - accountMap.insert(oldAccount.lastNumbers, oldAccount); - } - } - - QList accounts = xmlScreenParser.parseAccountsInfo(); - for (auto account: accounts) { - if (accountMap.contains(account.lastNumbers)) { - account.deviceId = m_deviceId; - account.appCode = m_appCode; - account.currency = accountMap[account.lastNumbers].currency; - account.description = accountMap[account.lastNumbers].description; - account.status = AccountStatus::Active; - if (!AccountDAO::upsertAccount(account)) { - m_error = "Not updated: " + account.lastNumbers; - qCritical() << m_error; - } - } - } - - QList savedAccounts = AccountDAO::getAccounts(m_deviceId, m_appCode); - postAccountsData(savedAccounts); - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - } - emit finishedWithResult(m_error); -} diff --git a/android/birbank/LoginAndCheckAccountsBirbankScript.h b/android/birbank/LoginAndCheckAccountsBirbankScript.h deleted file mode 100644 index fd3581d..0000000 --- a/android/birbank/LoginAndCheckAccountsBirbankScript.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "CommonBirbankScript.h" - -class LoginAndCheckAccountsBirbankScript final : public CommonBirbankScript { - Q_OBJECT - -public: - ~LoginAndCheckAccountsBirbankScript() override; - - explicit LoginAndCheckAccountsBirbankScript( - QString deviceId, - QString appCode, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const QString m_deviceId; - const QString m_appCode; - QString m_error; -}; diff --git a/android/birbank/PayByCardBirbankScript.cpp b/android/birbank/PayByCardBirbankScript.cpp deleted file mode 100644 index b5d371f..0000000 --- a/android/birbank/PayByCardBirbankScript.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "PayByCardBirbankScript.h" - -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/ApplicationInfo.h" -#include "db/DeviceInfo.h" -#include "time/DateUtils.h" - -PayByCardBirbankScript::PayByCardBirbankScript( - AccountInfo account, - QString card, - QString fullName, - const double amount, - QObject *parent -) : CommonBirbankScript(parent), - m_account(std::move(account)), m_card(std::move(card)), - m_fullName(std::move(fullName)), m_amount(amount) { -} - -PayByCardBirbankScript::~PayByCardBirbankScript() = default; - - -void PayByCardBirbankScript::makePayment(const DeviceInfo &device) { - // Оплата со страницы карты - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node cardEditText = xmlScreenParser.findEditText("Номер карты"); - Node amountEditText = xmlScreenParser.findEditText("Сумма"); - if (!cardEditText.isEmpty() && !amountEditText.isEmpty()) { - AdbUtils::makeTap(device.id, cardEditText.x(), cardEditText.y()); - QThread::msleep(200); - AdbUtils::inputText(device.id, m_card); - - AdbUtils::hideKeyboard(device.id); - - AdbUtils::makeTap(device.id, amountEditText.x(), amountEditText.y()); - QThread::msleep(200); - AdbUtils::inputText(device.id, QString::number(m_amount)); - - AdbUtils::hideKeyboard(device.id); - - // дальше оплата, но хз как - - - } else { - m_error = "Не нашли поля ввода 'Номер карты' или 'Сумма'"; - qDebug() << m_error; - } -} - -void PayByCardBirbankScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - if (device.id.isEmpty() || app.id == -1) { - m_error = "Device or app not found."; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - } - - if (xmlScreenParser.isHomeScreen()) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node cardText = xmlScreenParser.findTextNode("2480"); - if (!cardText.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, cardText.x(), cardText.y()); - QThread::msleep(2000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node payBtn = xmlScreenParser.findTextNode("Перевести"); - if (!payBtn.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, payBtn.x(), payBtn.y()); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node payByCardBtn = xmlScreenParser.findTextNode("На карту любого банка"); - if (!payByCardBtn.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, payByCardBtn.x(), payByCardBtn.y()); - QThread::msleep(1000); - - makePayment(device); - } else { - m_error = "Pay by card button not found: " + m_account.deviceId + " " + m_account.appCode; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Pay button not found: " + m_account.deviceId + " " + m_account.appCode; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - } - } - - if (!m_error.isEmpty()) { - AdbUtils::takeXmlDump(device.id, "pay_by_Card_Birbank_fail_transactions_" + QString::number(m_account.id)); - } - - emit finishedWithResult(m_error); -} diff --git a/android/birbank/PayByCardBirbankScript.h b/android/birbank/PayByCardBirbankScript.h deleted file mode 100644 index 0d73ff3..0000000 --- a/android/birbank/PayByCardBirbankScript.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "CommonBirbankScript.h" -#include "db/DeviceInfo.h" - -class PayByCardBirbankScript final : public CommonBirbankScript { - Q_OBJECT - -public: - ~PayByCardBirbankScript() override; - - PayByCardBirbankScript( - AccountInfo account, - QString card, - QString fullName, - double amount, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - const QString m_card; - const QString m_fullName; - const double m_amount; - QString m_error; - - void makePayment(const DeviceInfo &device); -}; diff --git a/android/birbank/ScreenXmlBirbankParser.cpp b/android/birbank/ScreenXmlBirbankParser.cpp deleted file mode 100644 index ba2f8f9..0000000 --- a/android/birbank/ScreenXmlBirbankParser.cpp +++ /dev/null @@ -1,747 +0,0 @@ -#include "ScreenXmlBirbankParser.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" -#include "time/DateUtils.h" - -ScreenXmlBirbankParser::ScreenXmlBirbankParser(QObject *parent) : QObject(parent) { -} - -ScreenXmlBirbankParser::~ScreenXmlBirbankParser() = default; - -static const QRegularExpression boundsRegex(R"(\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\])"); -static const QRegularExpression onlyDigitsRegex(R"([^0-9])"); -static const QRegularExpression onlyDigitsAndDotRegex(R"([^0-9.])"); - -static const QRegularExpression rePhone(R"raw((\+7\(?\d{3}\)?[- ]?\d{3}[- ]?\d{2}[- ]?\d{2}))raw"); -static const QRegularExpression reFromBank(R"raw(из (.+?) на)raw"); -static const QRegularExpression reToBank(R"raw(в (.+?) через)raw"); -static const QRegularExpression reSbpId(R"(СБП(?: С2С)? ([A-Z0-9]{30,}))"); -static const QRegularExpression reIdAlt(R"(ID перевода (\d+))"); -static const QRegularExpression reDate(R"((?:Дата операции|дата) (\d{2}\.\d{2}\.\d{4}(?: \d{2}:\d{2}:\d{2})?))"); -static const QRegularExpression reSender(R"(от ([^\(]+))"); -static const QRegularExpression reNameAlt(R"(Перевод ([А-Яа-яЁё\s\*]+) в)"); - -static const QRegularExpression reCardData(R"(цифрами\s+(\d\s\d\s\d\s\d).*?Сумма:\s([\d\s]+)\sруб)"); - -Node ScreenXmlBirbankParser::tryToFindBannerOrDialog(const int width, const int height) { - // 1. Ищем push banner - он перекрывает весь экран - Node first = m_nodes[0]; - for (const Node &node: m_nodes) { - // Если это однозначно, что пуш-баннер - if (node.resourceId == "aft-popup-pushNotification-iconClose" && node.className == "android.widget.Button") { - return node; - } - // Если есть кнопка "Позже" - if (node.text.trimmed() == "Позже" && node.className == "android.widget.Button") { - return node; - } - // Если нашли пустую кнпоку - if (node.className == "android.widget.Button" && node.clickable - && node.text.isEmpty() && node.contentDesc.isEmpty()) { - // - if (node.x() > width / 2 && node.y() < width / 2) { - return node; - } - } - } - return {}; -} - -Node ScreenXmlBirbankParser::findAccountButton(const QString &numbers) { - // TODO возможно это не так, надо уточнять - return findTextNode(QString("LEVENT - %1").arg(numbers)); -} - -/** - * Смотрим что есть текс "Мы заботимся о вашей безопасности" и - * кнопка "Отложить" - * @return координаты кнопки для закрытия - */ -Node ScreenXmlBirbankParser::tryToFindSecurityBanner() { - Node text; - Node closeBtn; - for (const Node &node: m_nodes) { - if (node.text.trimmed() == "Мы заботимся о вашей безопасности" && node.className == "android.app.Dialog") { - text = node; - } - if (node.text.trimmed() == "Отложить" && node.className == "android.widget.Button") { - closeBtn = node; - } - } - - // Не нужно проверять все, если хотя бы одна координата есть - if (!text.isEmpty() && !closeBtn.isEmpty()) { - return closeBtn; - } - return {}; -} - -Node ScreenXmlBirbankParser::tryToFindSelectBottomSheet() { - Node closeBtn; - for (const Node &node: m_nodes) { - if ((node.text.trimmed() == "Отмена" || node.text.trimmed() == "Отложить") && node.className == - "android.widget.Button") { - closeBtn = node; - } - } - // Не нужно проверять все, если хотя бы одна координата есть - if (!closeBtn.isEmpty()) { - return closeBtn; - } - return {}; -} - -Node ScreenXmlBirbankParser::findTextNode(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlBirbankParser::findTextNode(const QString &text, const int x1, const int y1, const int x2, const int y2) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text - && node.x() >= x1 && node.x() <= x2 && node.y() >= y1 && node.y() <= y2) { - return node; - } - } - return {}; -} - -Node ScreenXmlBirbankParser::findEditText(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.text == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlBirbankParser::findEditTextByResourceId(const QString &resourceId) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.resourceId == resourceId) { - return node; - } - } - return {}; -} - -Node ScreenXmlBirbankParser::findButtonNode(const QString &text, const bool contains) { - for (const Node &node: m_nodes) { - if (contains) { - if (node.className == "android.widget.Button" && node.text.contains(text)) { - return node; - } - } else { - if (node.className == "android.widget.Button" && node.text.trimmed() == text) { - return node; - } - } - } - return {}; -} - -bool ScreenXmlBirbankParser::isHomeScreen() { - Node activitiesBtn; - for (const Node &node: m_nodes) { - if (node.text.trimmed() == "Карты и счета") { - activitiesBtn = node; - } - } - return !activitiesBtn.isEmpty(); -} - -QList ScreenXmlBirbankParser::findPinCodeNodes(const QString &pinCode) { - QMap buttons; - const QSet numberSet = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - - for (const Node &node: m_nodes) { - const QString &text = node.text.trimmed(); - if (node.className == "android.widget.TextView" && numberSet.contains(text)) { - buttons[text] = node; - } - } - - QList nodes; - for (int i = 0; i < pinCode.length(); ++i) { - nodes.append(buttons[QString(pinCode.at(i))]); - } - return nodes; -} - -QList ScreenXmlBirbankParser::getLast2DaysTransactions() { - QList nodes; - for (const Node &node: m_nodes) { - if (node.className == "android.widget.Button" && node.text.contains("рубл") - && (node.text.contains("Минус") || node.text.contains("Плюс"))) { - nodes.append(node); - } - } - return nodes; -} - -/** -* - */ -void ScreenXmlBirbankParser::parseAndSaveXml(const QString &xml) { - QDomDocument doc; - doc.setContent(xml); - m_xml = doc.documentElement(); - m_nodes.clear(); - - QXmlStreamReader reader(xml); - while (!reader.atEnd()) { - reader.readNext(); - if (reader.isStartElement() && reader.name() == "node") { - Node node = Node(); - if (reader.attributes().hasAttribute("bounds")) { - QString bounds = reader.attributes().value("bounds").toString(); - if (QRegularExpressionMatch match = boundsRegex.match(bounds); match.hasMatch()) { - node.setBounds( - match.captured(1).toInt(), - match.captured(2).toInt(), - match.captured(3).toInt(), - match.captured(4).toInt() - ); - } - } - if (reader.attributes().hasAttribute("text")) { - node.text = reader.attributes().value("text").toString(); - } - - if (reader.attributes().hasAttribute("content-desc")) { - node.contentDesc = reader.attributes().value("content-desc").toString(); - } - - if (reader.attributes().hasAttribute("class")) { - node.className = reader.attributes().value("class").toString(); - } - - if (reader.attributes().hasAttribute("clickable")) { - node.clickable = reader.attributes().value("clickable").toString() == "true"; - } - if (reader.attributes().hasAttribute("focusable")) { - node.focusable = reader.attributes().value("focusable").toString() == "true"; - } - if (reader.attributes().hasAttribute("resource-id")) { - node.resourceId = reader.attributes().value("resource-id").toString(); - } - - m_nodes.append(node); - } - } -} - -QDomElement findSubElementBirbank(const QDomElement &elem, const QString &index) { - if (elem.hasChildNodes()) { - QDomNodeList subInfoList = elem.childNodes(); - for (QDomNode subInfoNode: subInfoList) { - if (!subInfoNode.isElement()) continue; - QDomElement subInfo = subInfoNode.toElement(); - if (subInfo.attribute("index") == index) { - return subInfo; - } - } - } - return {}; -} - -QString findSubInfoBirbank(const QDomElement &elem, const QString &index) { - QDomElement subInfo = findSubElementBirbank(elem, index); - if (!subInfo.isNull()) { - return subInfo.attribute("text"); - } - return ""; -} - -bool hasTimeComponentBirbank(const QString &datetimeStr) { - QDateTime dt = QDateTime::fromString(datetimeStr, "dd.MM.yyyy HH:mm:ss"); - if (!dt.isValid()) { - return false; // Строка не соответствует формату даты и времени - } - return dt.time() != QTime(0, 0, 0); // Проверка, что время не равно 00:00:00 -} - -QString transformBankDateTimeBirbank(const QString &date) { - QDateTime dt; - if (date.contains(",")) { - // 10.05.2025, 15:03 - dt = QDateTime::fromString(date, "dd.MM.yyyy, HH:mm"); - } else { - dt = QDateTime::fromString(date, "dd.MM.yyyy HH:mm:ss"); - } - if (!dt.isValid()) { - dt = QDateTime::fromString(date, "dd.MM.yyyy HH:mm"); - } - if (!dt.isValid()) { - return date; // не распарсили - } - return dt.toString("dd.MM.yyyy HH:mm:ss"); -} - -void parseTransferInfoBirbank(const QString &text, TransactionInfo &info) { - if (const auto m = reSender.match(text); m.hasMatch()) { - info.name = m.captured(1).trimmed(); - } else if (const auto m1 = reNameAlt.match(text); m1.hasMatch()) { - info.name = m1.captured(1).trimmed(); - } - - if (const auto m = rePhone.match(text); m.hasMatch()) { - info.phone = m.captured(1).trimmed().remove(onlyDigitsRegex); - } - - if (const auto m = reFromBank.match(text); m.hasMatch()) { - if (const QString bank = m.captured(1).trimmed(); !bank.isEmpty()) { - info.bankName = bank; - } - } else if (const auto m1 = reToBank.match(text); m1.hasMatch()) { - if (const QString bank = m1.captured(1).trimmed(); !bank.isEmpty()) { - info.bankName = bank; - } - } - - if (const auto m = reSbpId.match(text); m.hasMatch()) { - info.bankTrExternalId = m.captured(1); - } else if (const auto m1 = reIdAlt.match(text); m1.hasMatch()) { - info.bankTrExternalId = m1.captured(1); - } - - if (const auto m = reDate.match(text); m.hasMatch()) { - const QString date = m.captured(1); - // если не пустая и дата у нас с 00:00 - // if (hasTimeComponent2(date)) { - info.bankTime = transformBankDateTimeBirbank(date); - // } - } -} - -void traverseNodesBirbank(const QDomNode &node) { - if (node.isElement()) { - QDomElement elem = node.toElement(); - - if (node.isElement()) { - QDomElement elem = node.toElement(); - - if (elem.tagName() == "node" && - elem.attribute("class") == "android.widget.Button" && - elem.attribute("text") == "Детали операции") { - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - - for (int j = 0; j < detailInfo.count(); ++j) { - QDomNode info = detailInfo.at(j); - if (!info.isElement()) continue; - QDomElement infoElement = info.toElement(); - QString index = infoElement.attribute("index"); - switch (index.toInt()) { - case 2: - qDebug() << "Дата операции:" << findSubInfoBirbank(infoElement, "1"); - break; - case 5: - qDebug() << "Название:" << findSubInfoBirbank(infoElement, "1"); - qDebug() << "-------------------------------------"; - // parseTransfer(findSubInfo2(infoElement)); - break; - case 7: - qDebug() << "Комиссия:" << findSubInfoBirbank(infoElement, "1"); - break; - default: - qDebug() << "Прочее:" << findSubInfoBirbank(infoElement, "1"); - break; - } - } - } - } - } - - // Рекурсивно обходим всех детей - QDomNode child = node.firstChild(); - while (!child.isNull()) { - traverseNodesBirbank(child); - child = child.nextSibling(); - } -} - -QDomNode findNodeBirbank(const QDomNode &node, const QString &text) { - if (node.isElement()) { - QString nodeText = node.toElement().attribute("text"); - if (nodeText.toLower().startsWith(text)) { - return node; - } - } - - - QDomNode child = node.firstChild(); - while (!child.isNull()) { - QDomNode result = findNodeBirbank(child, text); - if (!result.isNull()) { - return result; - } - child = child.nextSibling(); - } - return {}; -} - -float extractAmountsBirbank(const QString &text) { - // Разрешаем пробелы внутри чисел, захватываем "рубль", "рубля", "рублей" и т.д. - // QRegularExpression re(R"((Плюс|Минус)\s+([\d\s]+)\s+рубл\w*\s+(\d+)\s+копе\w*)"); - QRegularExpression re( - R"((Плюс|Минус)\s+([\d\s]+)\s+рубл\w*\s+(\d+)\s+копе\w*)", - QRegularExpression::UseUnicodePropertiesOption - ); - - QRegularExpressionMatch match = re.match(text); - if (match.hasMatch()) { - QString signStr = match.captured(1); // "Плюс" или "Минус" - QString rublesStr = match.captured(2).remove(' '); // "1 000" → "1000" - int rubles = rublesStr.toInt(); - int kopecks = match.captured(3).toInt(); - - int totalKopecks = rubles * 100 + kopecks; - if (signStr == "Минус") { - totalKopecks *= -1; - } - - return totalKopecks / 100.0f; - } - return 0; -} - -QString beforeAmountBirbank(const QString &text) { - int index = text.indexOf(QRegularExpression("Плюс|Минус")); - if (index != -1) { - return text.left(index).trimmed(); - } - return text; // если не найдено — вернуть всю строку -} - -double parseAmountBirbank(const QString &amountAsString) { - QString numberString = amountAsString; - numberString.remove(QRegularExpression(R"([^\dточка])")); - numberString.replace("точка", "."); - double amount = numberString.toDouble(); - - if (amountAsString.startsWith("-")) { - amount *= -1; - } - - return amount; -} - -QDomElement findElementWithPaymentsBirbank(const QDomElement &parent, const QString &resourceId) { - QDomElement element = parent.firstChildElement(); - while (!element.isNull()) { - QDomElement child = element.firstChildElement(); - while (!child.isNull()) { - if (child.hasAttribute("resource-id") && child.attribute("resource-id") == resourceId) { - return child; - } - child = child.nextSiblingElement(); - } - - QDomElement found = findElementWithPaymentsBirbank(element, resourceId); - if (!found.isNull()) { - return found; - } - element = element.nextSiblingElement(); - } - return {}; -} - -QDateTime getUtcFromBakuDate(const QString &date, const QString &time) { - QDate baseDate; - QTime parsedTime = QTime::fromString(time, "H:mm"); - if (!parsedTime.isValid()) { - qWarning() << "Невалидное время:" << time; - return {}; - } - - QString dateLower = date.trimmed().toLower(); - - if (dateLower == "сегодня") { - baseDate = QDate::currentDate(); - } else if (dateLower == "вчера") { - baseDate = QDate::currentDate().addDays(-1); - } else if (dateLower == "позавчера") { - baseDate = QDate::currentDate().addDays(-2); - } else { - // Пробуем разобрать обычную дату, например "24 июля" - QLocale ruLocale(QLocale::Russian, QLocale::Russia); - int year = QDate::currentDate().year(); - QString combined = QString("%1 %2 %3").arg(date).arg(year).arg(time); // "24 июля 2025 0:31" - QDateTime dt = ruLocale.toDateTime(combined, "d MMMM yyyy H:mm"); - if (!dt.isValid()) { - qWarning() << "Ошибка разбора даты:" << combined; - return {}; - } - dt.setTimeZone(QTimeZone("Asia/Baku")); - return dt.toTimeZone(QTimeZone::utc()); - } - - // Собираем и переводим в UTC, если дата относительная - QDateTime bakuDateTime(baseDate, parsedTime, QTimeZone("Asia/Baku")); - return bakuDateTime.toTimeZone(QTimeZone::utc()); -} - -QList ScreenXmlBirbankParser::parsePositiveTransactions() { - QList transactions; - - QDomElement child = findElementWithPaymentsBirbank(m_xml, "az.kapitalbank.mbanking:id/rv_pfm_operations"); - // QDomElement child = findElementWithPaymentsBirbank(m_xml, "az.kapitalbank.mbanking:id/rv_operations"); - QDomNodeList listView = child.childNodes(); - qDebug() << listView.count(); - if (!listView.isEmpty()) { - QString date = ""; - - for (QDomNode node: listView) { - const QDomNodeList trBoxGroup = node.childNodes(); - if (trBoxGroup.count() > 0) { - TransactionInfo transaction; - for (QDomNode item: trBoxGroup) { - QDomElement groupItem = item.toElement(); - if ("az.kapitalbank.mbanking:id/tv_date" == groupItem.attribute("resource-id")) { - date = groupItem.attribute("text"); - } else if ("az.kapitalbank.mbanking:id/transaction_title" == groupItem.attribute("resource-id")) { - transaction.description = groupItem.attribute("text"); - } else if ("az.kapitalbank.mbanking:id/transaction_time" == groupItem.attribute("resource-id")) { - transaction.bankTime = date + " " + groupItem.attribute("text"); - transaction.completeTime = getUtcFromBakuDate(date, groupItem.attribute("text")); - } else if ("az.kapitalbank.mbanking:id/transaction_amount" == groupItem.attribute("resource-id")) { - auto amount = groupItem.attribute("text"); - if (amount.startsWith("+")) { - transaction.amount = amount.remove("+").remove("₼").replace(",", ".").trimmed().toFloat(); - } - } - } - if (transaction.amount > 0) { - transactions.append(transaction); - qDebug().noquote().nospace() << convertTransactionToString(transaction); - } - } - } - } else { - qDebug() << "ListView is empty"; - } - return transactions; -} - - -TransactionInfo parseExpandedInfoHistoryBirbank(const QDomElement &root) { - if (root.isElement()) { - QDomElement elem = root.toElement(); - if (elem.tagName() == "node" - && elem.attribute("class") == "android.widget.Button" - && elem.attribute("text") == "Детали операции" - ) { - TransactionInfo transaction; - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - for (int i = 0; i < detailInfo.count(); ++i) { - QDomNode info = detailInfo.at(i); - if (!info.isElement()) continue; - - switch (QDomElement e = info.toElement(); e.attribute("index").toInt()) { - case 2: - // Дата операции - transaction.bankTime = transformBankDateTimeBirbank(findSubInfoBirbank(e, "1")); - break; - case 5: - transaction.description = findSubInfoBirbank(e, "1"); - parseTransferInfoBirbank(transaction.description, transaction); - break; - case 7: - // Комиссия - transaction.fee = findSubInfoBirbank(e, "1").remove(onlyDigitsRegex).toFloat(); - break; - default: - // qDebug() << "Прочее:" << findSubInfo2(e); - break; - } - } - return transaction; - } - } - - // Рекурсивно обходим всех детей - QDomNode child = root.firstChild(); - while (!child.isNull()) { - if (TransactionInfo info = parseExpandedInfoHistoryBirbank(child.toElement()); !info.bankTime.isEmpty()) { - return info; - } - child = child.nextSibling(); - } - - return {}; -} - -TransactionInfo ScreenXmlBirbankParser::parseTransactionInfoHistory(int screenWidth, int screenHeight) { - // 1. Распарсить описание транзакции - TransactionInfo transaction = parseExpandedInfoHistoryBirbank(m_xml); - // 1. Найти статус: Исполнен | Операция исполнена - transaction.status = TransactionStatus::Wait; - Node status1 = findTextNode("Исполнен", 0, 0, screenWidth, screenHeight / 2); - Node status2 = findTextNode("Операция исполнена", 0, 0, screenWidth, screenHeight / 2); - if (!status1.isEmpty() || !status2.isEmpty()) { - transaction.status = TransactionStatus::Complete; - } - return transaction; -} - -TransactionInfo parseExpandedInfoBirbank(const QDomElement &root) { - if (root.isElement()) { - QDomElement elem = root.toElement(); - if (elem.tagName() == "node" - && elem.attribute("class") == "android.widget.Button" - && elem.attribute("text") == "Детали операции" - ) { - TransactionInfo transaction; - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - for (int i = 0; i < detailInfo.count(); ++i) { - QDomNode info = detailInfo.at(i); - if (!info.isElement()) continue; - - QDomElement infoElement = info.toElement(); - QString index = infoElement.attribute("index"); - QDomElement ee; - switch (index.toInt()) { - case 3: - transaction.bankTime = transformBankDateTimeBirbank(findSubInfoBirbank(infoElement, "1")); - break; - case 4: - ee = findSubElementBirbank(infoElement, "0"); - ee = findSubElementBirbank(ee, "1"); - transaction.phone = findSubInfoBirbank(ee, "0").remove(onlyDigitsRegex); - break; - case 5: - transaction.name = findSubInfoBirbank(infoElement, "1"); - break; - case 6: - transaction.bankName = findSubInfoBirbank(infoElement, "1"); - break; - case 9: - transaction.fee = findSubInfoBirbank(infoElement, "1").remove(onlyDigitsAndDotRegex).toFloat(); - break; - case 10: - ee = findSubElementBirbank(infoElement, "1"); - transaction.bankTrExternalId = findSubInfoBirbank(ee, "1"); - break; - default: - break; - } - } - return transaction; - } - } - - // Рекурсивно обходим всех детей - QDomNode child = root.firstChild(); - while (!child.isNull()) { - if (TransactionInfo info = parseExpandedInfoBirbank(child.toElement()); !info.bankTime.isEmpty()) { - return info; - } - child = child.nextSibling(); - } - - return {}; -} - -TransactionInfo ScreenXmlBirbankParser::parseTransactionInfo(int screenWidth, int screenHeight) { - // 1. Распарсить описание транзакции - TransactionInfo transaction = parseExpandedInfoBirbank(m_xml); - // 1. Найти статус: Исполнен | Операция исполнена - transaction.status = TransactionStatus::Wait; - Node status1 = findTextNode("Исполнен", 0, 0, screenWidth, screenHeight / 2); - Node status2 = findTextNode("Операция исполнена", 0, 0, screenWidth, screenHeight / 2); - if (!status1.isEmpty() || !status2.isEmpty()) { - transaction.status = TransactionStatus::Complete; - } - return transaction; -} - - -QList ScreenXmlBirbankParser::parseAccountsInfo() { - const QDomNodeList nodes = m_xml.elementsByTagName("node"); - QList accounts; - - for (int i = 0; i < nodes.count(); ++i) { - const QDomElement el = nodes.at(i).toElement(); - if (el.attribute("text").contains("₼")) { - // Нашли сумму - // el.attribute("text") - ₼ и трим - auto amount = el.attribute("text").replace("₼", "").trimmed(); - const QDomElement parent = el.parentNode().parentNode().toElement(); - // Перебираем потомков - QDomNode n = parent.firstChild().firstChild().firstChild().firstChild(); - while (!n.isNull()) { - const QDomElement e = n.toElement(); - if (e.attribute("resource-id") == "az.kapitalbank.mbanking:id/tvNumber") { - const QString account = e.attribute("text"); - - AccountInfo info; - info.lastNumbers = account; - info.amount = amount.toFloat(); - info.description = account; - info.updateTime = DateUtils::getUtcNow(); - - accounts.append(info); - break; - } - n = n.nextSibling(); - } - } - } - return accounts; -} - -void ScreenXmlBirbankParser::test() { - // TransactionInfo info2 = parseExpandedInfo(m_xml); - // qDebug().noquote().nospace() << convertTransactionToString(info2); - // return; - - QFile file("assets/rshb/all_products.xml"); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qDebug() << "Cannot open file"; - } - - QDomDocument doc; - if (!doc.setContent(&file)) { - qDebug() << "Failed to parse XML"; - } - file.close(); - - qDebug() << "Start parsing XML:"; - - // Номер карты с последними цифрами - QDomElement root = doc.documentElement(); - parseAndSaveXml(doc.toString()); - // for (const AccountInfo &info: parseAccountsInfo()) { - // qDebug().noquote().nospace() << convertAccountToString(info); - // } - // TransactionInfo info = parseExpandedInfo(root); - // qDebug().noquote().nospace() << convertTransactionToString(info); - // ЕСПП -} diff --git a/android/birbank/ScreenXmlBirbankParser.h b/android/birbank/ScreenXmlBirbankParser.h deleted file mode 100644 index f812aa8..0000000 --- a/android/birbank/ScreenXmlBirbankParser.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" - -class ScreenXmlBirbankParser final : public QObject { - Q_OBJECT - -public: - explicit ScreenXmlBirbankParser(QObject *parent = nullptr); - - ~ScreenXmlBirbankParser() override; - - Node tryToFindBannerOrDialog(int width, int height); - - Node tryToFindSecurityBanner(); - - Node tryToFindSelectBottomSheet(); - - Node findAccountButton(const QString &numbers); - - Node findTextNode(const QString &text); - - Node findTextNode(const QString &text, int x1, int y1, int x2, int y2); - - Node findEditText(const QString &text); - - Node findEditTextByResourceId(const QString &resourceId); - - Node findButtonNode(const QString &text, bool contains); - - bool isHomeScreen(); - - QList getLast2DaysTransactions(); - - void parseAndSaveXml(const QString &xml); - - TransactionInfo parseTransactionInfoHistory(int screenWidth, int screenHeight); - - TransactionInfo parseTransactionInfo(int screenWidth, int screenHeight); - - QList parsePositiveTransactions(); - - QList parseAccountsInfo(); - - QList findPinCodeNodes(const QString &pinCode); - - void test(); - -private: - QList m_nodes; - QDomElement m_xml; - -}; diff --git a/android/black/CommonScript.cpp b/android/black/CommonScript.cpp index 4224cb0..c7a93ef 100644 --- a/android/black/CommonScript.cpp +++ b/android/black/CommonScript.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "adb/AdbUtils.h" @@ -197,18 +199,37 @@ void CommonScript::updateTransactionData( void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode) { const ApplicationInfo app = ApplicationDAO::getApplication(deviceId, appCode); + if (app.fullName.trimmed().isEmpty() || app.phone.trimmed().isEmpty()) { + qWarning() << "[Black] createBankProfileIfNeeded: skipping — fullName or phone is empty"; + return; + } + const QMap currencyCodes = { {"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32} }; const int currencyCode = currencyCodes.value(app.currency.toUpper(), 840); const QStringList parts = app.fullName.split(' ', Qt::SkipEmptyParts); + // Нормализуем телефон: оставляем только цифры, добавляем код страны и "+" + const QSettings settings("config.ini", QSettings::IniFormat); + const QString phoneCode = settings.value(appCode + "/phone_code").toString(); + QString phone = app.phone; + phone.remove(QRegularExpression("[^0-9]")); + if (!phoneCode.isEmpty() && !phone.startsWith(phoneCode)) { + if (phone.startsWith('8') && phoneCode != "8") { + phone.replace(0, 1, phoneCode); + } else { + phone.prepend(phoneCode); + } + } + phone.prepend('+'); + QJsonObject profileBody; profileBody["first_name"] = parts.value(0); profileBody["middle_name"] = parts.size() >= 3 ? parts.value(1) : QString(""); profileBody["last_name"] = parts.size() >= 3 ? parts.value(2) : parts.value(1); profileBody["bank_name"] = appCode; - profileBody["phone_number"] = app.phone.simplified().remove(' '); + profileBody["phone_number"] = phone; profileBody["balance"] = QString::number(qRound64(app.amount * 100)); profileBody["state"] = (app.status == "active") ? "enabled" : "disabled"; profileBody["currency"] = currencyCode; diff --git a/android/ozon/CommonScript.cpp b/android/ozon/CommonScript.cpp index 45668a1..dc899cd 100644 --- a/android/ozon/CommonScript.cpp +++ b/android/ozon/CommonScript.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "adb/AdbUtils.h" @@ -293,18 +295,37 @@ void CommonScript::updateTransactionData( void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode) { const ApplicationInfo app = ApplicationDAO::getApplication(deviceId, appCode); + if (app.fullName.trimmed().isEmpty() || app.phone.trimmed().isEmpty()) { + qWarning() << "[Ozon] createBankProfileIfNeeded: skipping — fullName or phone is empty"; + return; + } + const QMap currencyCodes = { {"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32} }; const int currencyCode = currencyCodes.value(app.currency.toUpper(), 643); const QStringList parts = app.fullName.split(' ', Qt::SkipEmptyParts); + // Нормализуем телефон: оставляем только цифры, добавляем код страны и "+" + const QSettings settings("config.ini", QSettings::IniFormat); + const QString phoneCode = settings.value(appCode + "/phone_code").toString(); + QString phone = app.phone; + phone.remove(QRegularExpression("[^0-9]")); + if (!phoneCode.isEmpty() && !phone.startsWith(phoneCode)) { + if (phone.startsWith('8') && phoneCode != "8") { + phone.replace(0, 1, phoneCode); + } else { + phone.prepend(phoneCode); + } + } + phone.prepend('+'); + QJsonObject profileBody; profileBody["first_name"] = parts.value(0); profileBody["middle_name"] = parts.size() >= 3 ? parts.value(1) : QString(""); profileBody["last_name"] = parts.size() >= 3 ? parts.value(2) : parts.value(1); profileBody["bank_name"] = appCode; - profileBody["phone_number"] = app.phone.simplified().remove(' '); + profileBody["phone_number"] = phone; profileBody["balance"] = QString::number(qRound64(app.amount * 100)); profileBody["state"] = (app.status == "active") ? "enabled" : "disabled"; profileBody["currency"] = currencyCode; diff --git a/android/rshb/CommonScript.cpp b/android/rshb/CommonScript.cpp deleted file mode 100644 index 840532e..0000000 --- a/android/rshb/CommonScript.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include "CommonScript.h" - -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "net/NetworkService.h" - -namespace Rshb { - -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; - } - - - // Проверяем, что это экран ввода пин-кода - 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)); - } - - 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 && allProductsBtn.y() < height - 200) { - 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.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 Rshb \ No newline at end of file diff --git a/android/rshb/CommonScript.h b/android/rshb/CommonScript.h deleted file mode 100644 index 5ec8338..0000000 --- a/android/rshb/CommonScript.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include -#include "ScreenXmlParser.h" - -namespace Rshb { - -class CommonScript : public QObject { - Q_OBJECT - -public: - explicit CommonScript(QObject *parent = nullptr); - - ~CommonScript() override; - -protected: - virtual void doStart() = 0; - - // static method - static bool inputPinCode(const QString &deviceId, const QList &pincode); - - bool goToAllProducts( - const QString &deviceId, - int width, - int height - ); - - bool findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers); - - void postAccountsData(const QList > &accounts); - - void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction); - - void updateTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); - - Node swipeToButton( - const QString &deviceId, - const QString &text, - int &counter, int width, int height - ); - - bool runAppAndGoToHomeScreen( - const QString &deviceId, - const QString &packageName, - const QString &pinCode, - int width, - int height - ); - - bool tryToCloseAllBannersOnHomePage( - const QString &deviceId, int width, int height - ); - - bool m_running = true; - ScreenXmlParser xmlScreenParser; - int m_counter = 0; - -signals: - void finished(); - - void finishedWithResult(const QString &result); - -public slots: - virtual void start() final; - - virtual void stop() final; - -private: - Q_DISABLE_COPY(CommonScript); -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/GetLastDaysHistoryScript.cpp b/android/rshb/GetLastDaysHistoryScript.cpp deleted file mode 100644 index e83bfdb..0000000 --- a/android/rshb/GetLastDaysHistoryScript.cpp +++ /dev/null @@ -1,399 +0,0 @@ -#include "GetLastDaysHistoryScript.h" - -#include -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/DeviceInfo.h" -#include "net/NetworkService.h" -#include "time/DateUtils.h" - -namespace Rshb { - -GetLastDaysHistoryScript::GetLastDaysHistoryScript( - AccountInfo account, - QObject *parent -) : CommonScript(parent), - m_account(std::move(account)) { -} - -GetLastDaysHistoryScript::~GetLastDaysHistoryScript() = default; - -QList findSavedTransaction( - const QList &transactions, - const TransactionInfo &parsed -) { - QList txs; - const QDateTime dateTime = QDateTime::fromString(parsed.bankTime, "dd.MM.yyyy"); // Московское время - - for (const TransactionInfo &info: transactions) { - QDateTime timestamp = info.timestamp; - QString description = parsed.description; - // если тот же день - if (dateTime.date() == timestamp.date() && parsed.amount == info.amount) { - // Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815... - if (description.contains("Перевод")) { - if (description.contains(info.name)) { - txs.append(info); - } - } else { - // Перевод по карте - if (description == info.description) { - txs.append(info); - } - } - } - } - - return txs; -} - -int countSimilar(const TransactionInfo &transaction, const QList &transactions) { - int count = 0; - for (const TransactionInfo &info: transactions) { - if (info.amount == transaction.amount - && info.description == transaction.description - && info.bankTime == transaction.bankTime) { - ++count; - } - } - return count; -} - -QString formatPrice(const double value) { - QString sign = value < 0 ? "Минус" : "Плюс"; - const auto intValue = static_cast(std::abs(value)); // округление до целого - - QString number = QString::number(intValue); - QString formatted; - - int count = 0; - for (int i = number.length() - 1; i >= 0; --i) { - formatted.prepend(number[i]); - ++count; - if (count % 3 == 0 && i != 0) { - formatted.prepend(' '); - } - } - - return QString("%1 %2").arg(sign, formatted); -} - -bool GetLastDaysHistoryScript::saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status -) { - TransactionInfo tr; - tr.accountId = account.id; - tr.description = transaction.description; - tr.bankTime = transaction.bankTime; - tr.bankName = transaction.bankName; - tr.amount = transaction.amount; - tr.phone = transaction.phone; - tr.updateTime = DateUtils::getUtcNow(); - tr.fee = transaction.fee; - tr.name = transaction.name; - tr.bankTrExternalId = transaction.bankTrExternalId; - tr.status = status; - tr.type = transaction.description.startsWith("Перевод") ? TransactionType::Phone : TransactionType::Card; - tr.completeTime = DateUtils::getUtcNow(); - tr.id = TransactionDAO::insertTransaction(tr); - - if (tr.id == -1) { - return false; - } - postNewTransactionData(account, tr); - return true; -} - -bool GetLastDaysHistoryScript::updateAndPostTransactionData( - const AccountInfo &account, - const int transactionId, - const TransactionStatus status, - const QString &oldDesc, - const QString &newDesc -) { - if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) { - updateTransactionData(account, transactionId, status, oldDesc, newDesc); - return true; - } - return false; -} - -void GetLastDaysHistoryScript::doStart() { - // берем все транзакции из БД - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(device.screenWidth, device.screenHeight); - if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) { - AdbUtils::makeTap(device.id, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y()); - QThread::msleep(500); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - if (xmlScreenParser.isHomeScreen()) { - if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { - QList localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); - - QList > accounts = xmlScreenParser.parseAccountsInfo(); - for (auto &[account, node]: accounts) { - account.deviceId = m_account.deviceId; - account.appCode = m_account.appCode; - if (!AccountDAO::upsertAccount(account)) { - qDebug() << "Not updated: " << account.lastNumbers; - } - } - postAccountsData(accounts); - - findAndTapOnAccount(device.id, m_account.lastNumbers); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node actionBtn = xmlScreenParser.findButtonNode("Действия", false); - if (!actionBtn.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y()); - QThread::msleep(200); - } else { - m_error = "All action btn not found"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - Node reportButton = swipeToButton( - m_account.deviceId, - "Выписка", - m_counter, device.screenWidth, - device.screenHeight - ); - - if (!reportButton.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, reportButton.x(), reportButton.y()); - QThread::msleep(1000); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - Node reportTitle = xmlScreenParser.findTextNode("Выписка"); - if (!reportTitle.isEmpty() && reportTitle.y() < 300) { - QDate beforeYesterday = DateUtils::getMoscowNow().date().addDays(-2); - QString beforeYesterdayTitle = beforeYesterday.toString("dd.MM.yyyy"); - - for (int i = 0; i < 5; ++i) { - Node byDayTitle = xmlScreenParser.findTextNode(beforeYesterdayTitle); - if (!byDayTitle.isEmpty()) { - qWarning() << "--- beforeYesterdayTitle found!"; - break; - } - AdbUtils::makeSwipe( - m_account.deviceId, - device.screenWidth / 2, device.screenHeight / 4 * 3, - device.screenWidth / 2, device.screenHeight / 4 - ); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - } - } else { - m_error = "All action - report title not found"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - - // все поддвержденные - QList parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory(); - QSet parsedTransactionIds; - for (const TransactionInfo &transaction: parsedTransactions) { - bool updated = false; - - for (const TransactionInfo &saved: localTransactions) { - if (parsedTransactionIds.contains(saved.id)) { - continue; - } - QDate sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy HH:mm:ss").date(); - if (!sDate.isValid()) { - sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy").date(); - } - QDate pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy HH:mm:ss").date(); - if (!pDate.isValid()) { - pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy").date(); - } - // Если сумма и дата совпадают - if (saved.amount == transaction.amount && sDate == pDate) { - if (transaction.description.startsWith("Перевод")) { - // Перевод по номеру - if (saved.bankName == transaction.bankName && saved.phone == transaction.phone) { - parsedTransactionIds.insert(saved.id); - updated = true; - if (saved.status != TransactionStatus::Complete || saved.description.isEmpty()) { - if (!TransactionDAO::updateTransaction( - saved.id, - TransactionStatus::Complete, - saved.description, - transaction.description - )) { - qDebug().noquote().nospace() << "--- updated+phone failed" << - convertTransactionToString(transaction);; - } - } - break; - } - } else { - // перевод по карте - // перезаписать saved.description -> updatedDescription если не равны - if (!saved.description.isEmpty()) { - if (saved.description == transaction.description) { - // описания совпадают - if (saved.status != TransactionStatus::Complete) { - if (!TransactionDAO::updateTransaction( - saved.id, - TransactionStatus::Complete, - saved.description, - transaction.description - )) { - qDebug().noquote().nospace() << "--- updated+card[description same] failed" - << - convertTransactionToString(transaction);; - } - } - parsedTransactionIds.insert(saved.id); - updated = true; - break; - } else { - // описания не совпадают, то пробуем найти такое же, если не находим то это новая - bool findWithSameName = false; - for (const TransactionInfo &saved2: localTransactions) { - if (saved2.description == transaction.description && !parsedTransactionIds. - contains( - saved2.id)) { - // мы нашли с одинаковым описанием и еще не обработанную - if (saved2.status != TransactionStatus::Complete) { - if (!TransactionDAO::updateTransaction( - saved2.id, - TransactionStatus::Complete, - saved2.description, - transaction.description - )) { - qDebug().noquote().nospace() << - "--- updated+card2[description same] failed" - << - convertTransactionToString(transaction);; - } - } - parsedTransactionIds.insert(saved2.id); - updated = true; - findWithSameName = true; - break; - } - } - if (!findWithSameName) { - // обновляем описание - if (!TransactionDAO::updateTransaction( - saved.id, - TransactionStatus::Complete, - saved.description, - transaction.description - )) { - qDebug().noquote().nospace() << - "--- updated+card2+new desc[description same] failed" << - convertTransactionToString(transaction);; - } - } - parsedTransactionIds.insert(saved.id); - updated = true; - break; - } - } else { - // пустой описание - if (!TransactionDAO::updateTransaction( - saved.id, - TransactionStatus::Complete, - saved.description, - transaction.description - )) { - qDebug().noquote().nospace() << - "--- updated+card[empty description]" << - convertTransactionToString(transaction);; - } - parsedTransactionIds.insert(saved.id); - updated = true; - break; - } - } - } - } - if (!updated) { - if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Complete)) { - qDebug() << "--- saveAndPostNewTransactionData failed"; - } - } - - // Если это перевод - // Если совпадает по сумме + телефон + банк дата (без времени) + банк - - // Если по карте, мы знаем ток дату + сумма - } - Node waitTab = xmlScreenParser.findButtonNode("Ожидающие обработки", true); - if (!waitTab.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, waitTab.x(), waitTab.y()); - QThread::msleep(1000); - - QList parsedWaitTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory(); - for (const TransactionInfo &transaction: parsedWaitTransactions) { - QList localActualTransactions = TransactionDAO::getTransactionsWithinHours( - m_account.id, 48 - ); - for (const TransactionInfo &saved: localActualTransactions) { - if (saved.status != TransactionStatus::Wait) { - continue; - } - - QDate sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy HH:mm:ss").date(); - if (!sDate.isValid()) { - sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy").date(); - } - QDate pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy HH:mm:ss").date(); - if (!pDate.isValid()) { - pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy").date(); - } - if (saved.amount == transaction.amount && sDate == pDate) { - // одно и тоже - } else { - if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Wait)) { - qDebug() << "--- saveAndPostNewTransactionData - Wait failed"; - } - } - } - } - } - } - } else { - m_error = "GetLastDaysHistoryScript - home screen not found"; - qWarning() << m_error; - emit finishedWithResult(m_error); - } - AdbUtils::goBack(m_account.deviceId); - AdbUtils::goBack(m_account.deviceId); - AdbUtils::goBack(m_account.deviceId); - emit finishedWithResult(m_error); -} - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/GetLastDaysHistoryScript.h b/android/rshb/GetLastDaysHistoryScript.h deleted file mode 100644 index bd49505..0000000 --- a/android/rshb/GetLastDaysHistoryScript.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "CommonScript.h" - -namespace Rshb { - -class GetLastDaysHistoryScript final : public CommonScript { - Q_OBJECT - -public: - ~GetLastDaysHistoryScript() override; - - explicit GetLastDaysHistoryScript( - AccountInfo account, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - QString m_error; - - bool saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status - ); - - bool updateAndPostTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/HomeScreenScript.cpp b/android/rshb/HomeScreenScript.cpp deleted file mode 100644 index 9a72cc2..0000000 --- a/android/rshb/HomeScreenScript.cpp +++ /dev/null @@ -1,513 +0,0 @@ -#include "HomeScreenScript.h" - -#include -#include -#include -#include - -#include "ScreenXmlParser.h" -#include "adb/AdbUtils.h" - -namespace Rshb { - - -HomeScreenScript::HomeScreenScript(QObject *parent) - : QObject(parent) { -} - -HomeScreenScript::~HomeScreenScript() = default; - -bool HomeScreenScript::goToHomeScreen( - 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)) { - qWarning() << "Process has not started"; - return false; - } - - xmlScreenParser.parseAndSaveXml( - AdbUtils::getScreenDumpAsXml(deviceId, 0) - ); - - // Даем минимальное время на запуск - QThread::msleep(4500); - int counter = 0; - int restarted = 0; - int totalCounter = 0; - bool findAndInputPincode = false; - while (true) { - counter++; - totalCounter++; - - // 3 раза уже перезапускали приложение, завершаем попытки - if (restarted > 2) { - return false; - } - - // Если более 30 секунд мы пытаемся перейти на домашнюю, перезапускаем - if (counter > 30) { - AdbUtils::tryToKillApplication(deviceId, packageName); - QThread::msleep(4500); - ++restarted; - counter = 0; - } - - const QString xml = AdbUtils::getScreenDumpAsXml(deviceId, totalCounter + 1); - xmlScreenParser.parseAndSaveXml(xml); - qDebug() << "Created xml+screenshot, idx: " << (totalCounter + 1); - - if (xmlScreenParser.isHomeScreen()) { - qDebug() << "isHomeScreen!!!!!!!"; - return true; - } - - // Проверяем на банеры после пин-кодом - Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(width, height); - if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) { - qDebug() << "--- tryToFindBannerOrDialog"; - AdbUtils::makeTap(deviceId, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y()); - QThread::msleep(1000); - continue; - } - - // Проверяем на банеры перед пин-кодом - Node closeBannerNode = xmlScreenParser.tryToFindSecurityBanner(); - if (!closeBannerNode.isEmpty()) { - qDebug() << "--- tryToFindSecurityBanner"; - AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - continue; - } - - // Проверяем, что это экран ввода пин-кода - if (!findAndInputPincode) { - if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН"); - pincode.size() == 4) { - qDebug() << "--- tryToFindPinCode"; - // делаем 4 тапа и ждем секунду и продолжаем - for (const Node &node: pincode) { - AdbUtils::makeTap(deviceId, node.x(), node.y()); - QThread::msleep(QRandomGenerator::global()->bounded(200, 901)); - } - // После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить - findAndInputPincode = true; - QThread::msleep(3000); - continue; - } - } - - QThread::msleep(1000); - - // Если что-то пойдет не по плану... - if (totalCounter > 100) { - return false; - } - } -} - -QString getPriceAsString(const double value) { - QString sign = value < 0 ? "Минус" : "Плюс"; - const auto intValue = static_cast(std::abs(value)); // округление до целого - - QString number = QString::number(intValue); - QString formatted; - - int count = 0; - for (int i = number.length() - 1; i >= 0; --i) { - formatted.prepend(number[i]); - ++count; - if (count % 3 == 0 && i != 0) { - formatted.prepend(' '); - } - } - - return QString("%1 %2").arg(sign, formatted); -} - -Node swipeToButton( - ScreenXmlParser &xmlScreenParser, 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 {}; -} - -void HomeScreenScript::start() { - // Достаем данные из БД по ИД транзакции, перед тем как запустить мы кладем в БД все данные - const QString deviceId = "6edc4a47"; - const QString packageName = "ru.rshb.dbo"; - const QString pinCode = "1102"; - const int width = 720; - const int height = 1600; - - const QString cardNumber = "7 1 8 3"; - // В том числе и данные на каком устройстве это делать - // TODO тут код работы с БД - - // +7 (964) 181-51-46 - const QString phoneNumber = "+79641815146"; - const QString bankName = "Альфа-Банк"; - const QString paySum = "10"; - - int index = 0; - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++index)); - - // xmlScreenParser.test(); - // тут просто получаем инфу которую спарсил - // == 1. Входящий (TransactionType::Phone) - // Перевод через СБП от Егор Иванович Ж (+7(000)-000-00-87) из Озон Банк (Ozon) на 40817810328000022077 . - // Идентификатор операции в СБП С2С B5121111443333300170011491301 , Дата операции 01.05.2025 14:14:41 Статус перевода Исполнен - // == 2. Исходящий (TransactionType::Phone) - // Перевод Надежда Иванкина К** в Сбербанк через СБП, по номеру телефона +79000810046, ID перевода 445900141, дата 03.05.2025 - - // (TransactionType::Card) -> T-BANK CARD2CARD - // TransactionInfo trInfo = xmlScreenParser.parseTransactionInfoHistory(width, height); - // qDebug().noquote().nospace() << convertTransactionToString(trInfo); - - // Если начинается с ЕСПП - то не кликаем еще, ждет обработку - // А все переводы по карте - они не начинаются со слова "Перевод" -> - // .. пробуем кликать, но если не кликается ??? - QList transactions = xmlScreenParser.parseTodayAndYesterdayHistory(); - for (const TransactionInfo &info: transactions) { - QString text = QString("%1 %2").arg(info.description, getPriceAsString(info.amount)); - qDebug().noquote().nospace() << convertTransactionToString(info); - - // Node trButton = xmlScreenParser.findButtonNode(text, true); - Node trButton = swipeToButton(xmlScreenParser, deviceId, text, index, width, height); - if (!trButton.isEmpty()) { - AdbUtils::makeTap(deviceId, trButton.x(), trButton.y()); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++index)); - - Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false); - if (!moreDetailBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, moreDetailBtn.x(), moreDetailBtn.y()); - QThread::msleep(200); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++index)); - TransactionInfo info = xmlScreenParser.parseTransactionInfoHistory(width, height); - qDebug().noquote().nospace() << convertTransactionToString(info); - } - - AdbUtils::goBack(deviceId); - } else { - qWarning() << "--- trButton not found.........."; - } - // Перевод ВЛАДИСЛАВ НИКОЛАЕВИЧ К** в Т-Банк через СБП, по номеру телефона +7985553... Минус 10 рублей 0 копеек - } - - return; - - int payCounter = 0; - - if (false) { - // Оплата со страницы карты - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - Node payBtn = xmlScreenParser.findButtonNode("Оплатить", false); - if (!payBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, payBtn.x(), payBtn.y()); - QThread::msleep(1000); - } else { - qWarning() << "--- payBtn not found"; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - // TODO - По номеру карты В РСХБ и другие банки - Node payTitle = xmlScreenParser.findTextNode("Платежи и переводы"); - if (!payTitle.isEmpty() && payTitle.y() < 300) { - Node payByPhoneBtn = xmlScreenParser.findButtonNode("По номеру телефона В РСХБ и через", false); - if (!payByPhoneBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, payByPhoneBtn.x(), payByPhoneBtn.y()); - QThread::msleep(1000); - } else { - qWarning() << "--- payByPhoneBtn not found"; - } - } else { - qWarning() << "--- payTitle not found"; - } - - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - Node payByPhoneTitle = xmlScreenParser.findTextNode("Перевод по телефону"); - Node continueBtn = xmlScreenParser.findButtonNode("Продолжить", false); - if (!payByPhoneTitle.isEmpty() && payByPhoneTitle.y() < 300 && continueBtn.isEmpty()) { - Node inputPhone = xmlScreenParser.findFirstEditText(); - AdbUtils::makeTap(deviceId, inputPhone.x(), inputPhone.y()); - QThread::msleep(500); - AdbUtils::inputText(deviceId, phoneNumber); - QThread::msleep(1000); - } else { - qWarning() << "--- payByPhoneTitle not found (no continueBtn)"; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - QString payBankName; - payBankName = bankName == "Россельхозбанк" ? "Россельхозбанк" : "Другой банк через СБП"; - Node payOtherBankBtn = xmlScreenParser.findButtonNode(payBankName, false); - if (!payOtherBankBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, payOtherBankBtn.x(), payOtherBankBtn.y()); - QThread::msleep(1000); - } else { - qWarning() << "--- payOtherBankBtn not found"; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - for (int i = 0; i < 5; ++i) { - Node allBanksTitle = xmlScreenParser.findTextNode("Банки участники"); - if (!allBanksTitle.isEmpty() && allBanksTitle.y() < 300) { - Node inputBankName = xmlScreenParser.findFirstEditText(); - AdbUtils::makeTap(deviceId, inputBankName.x(), inputBankName.y()); - QThread::msleep(500); - AdbUtils::enableAdbIMEKeyboard(deviceId); - AdbUtils::inputAdbIMEText(deviceId, bankName); - AdbUtils::disableAdbIMEKeyboard(deviceId); - QThread::msleep(1000); - break; - } else { - qWarning() << "--- allBanksTitle not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - for (int i = 0; i < 5; ++i) { - Node bankNameListItem = xmlScreenParser.findTextNode(bankName); - if (!bankNameListItem.isEmpty()) { - AdbUtils::makeTap(deviceId, bankNameListItem.x(), bankNameListItem.y()); - QThread::msleep(1000); - break; - } else { - qWarning() << "--- bankNameListItem not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - - Node payByPhoneScreenTitle = xmlScreenParser.findTextNode("Перевод по телефону"); - Node continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false); - if (!payByPhoneScreenTitle.isEmpty() && payByPhoneScreenTitle.y() < 300 && !continuePayBtn.isEmpty()) { - Node sumBtn = xmlScreenParser.findButtonNode("0", false); - if (!sumBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, sumBtn.x(), sumBtn.y()); - QThread::msleep(500); - AdbUtils::inputText(deviceId, paySum); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - Node readyBtn = xmlScreenParser.findTextNode("Готово"); - if (!readyBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, readyBtn.x(), readyBtn.y()); - QThread::msleep(500); - AdbUtils::makeTap(deviceId, continuePayBtn.x(), continuePayBtn.y()); - QThread::msleep(1000); - } else { - qWarning() << "--- readyBtn not found"; - } - } else { - qWarning() << "--- sumBtn not found"; - } - } else { - qWarning() << "--- payByPhoneScreenTitle not found"; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - for (int i = 0; i < 5; ++i) { - // TODO кнопка содержит "Перевести 10 ₽" если будет от 1000 то скорее всвего будет "Перевести 1 000 ₽", - // TODO не ясно как форматирование будет, поэтому пока на начало зашьем - Node makePayBtn = xmlScreenParser.findButtonNode("Перевести ", true); - if (!makePayBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, makePayBtn.x(), makePayBtn.y()); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите код доступа"); - pincode.size() == 4) { - // FIXME вынести в отдельный метод - // делаем 4 тапа и ждем секунду и продолжаем - for (const Node &node: pincode) { - AdbUtils::makeTap(deviceId, node.x(), node.y()); - QThread::msleep(QRandomGenerator::global()->bounded(200, 901)); - } - - // После ввода пин-кода, ждем 3с - QThread::msleep(3000); - } - break; - } else { - qWarning() << "--- makePayBtn not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - - for (int i = 0; i < 5; ++i) { - Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false); - if (!moreDetailBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, moreDetailBtn.x(), moreDetailBtn.y()); - QThread::msleep(200); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - - Node paymentCompleted = xmlScreenParser.findTextNode("Исполнен", 0, 0, width, height / 2); - Node paymentInProgress = xmlScreenParser.findTextNode("Выполняется", 0, 0, width, height / 2); - - if (!paymentCompleted.isEmpty()) { - // Если вдруг исполнен - } else if (!paymentInProgress.isEmpty()) { - // Если все еще в ожидании - } else { - qWarning() << "--- paymentCompleted or paymentInProgress not found..."; - } - break; - } else { - qWarning() << "--- moreDetailBtn not found, i: " << i; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++payCounter)); - } - - - return; - - int counter = 0; - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, counter)); - Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(width, height); - if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) { - qDebug() << "--- tryToFindBannerOrDialog"; - AdbUtils::makeTap(deviceId, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y()); - QThread::msleep(1000); - } - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); - if (xmlScreenParser.isHomeScreen()) { - qDebug() << "--- isHomeScreen"; - if (Node allCardTitle = xmlScreenParser.findTextNode("Счета и карты"); !allCardTitle.isEmpty()) { - // 1. Если нет кнопки видимой, свайпаем. Кликаем на "Все продукты" - for (int i = 0; i < 5; ++i) { - Node allProductsBtn = xmlScreenParser.findButtonNode("Все продукты", false); - // Node allProductsBtn = xmlScreenParser.findTextNode("Курсы валют и металлов"); для теста - if (allProductsBtn.x() > 0 && allProductsBtn.y() > 0) { - qDebug() << "--- All Product in focus"; - 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, ++counter)); - } - - for (int i = 0; i < 5; ++i) { - Node allProductsTitle = xmlScreenParser.findTextNode("Все продукты"); - // Проверяем что вверху заголовок - if (!allProductsTitle.isEmpty() && allProductsTitle.y() < 300) { - qDebug() << "--- All Product screen"; - - // TODO тут делаем синхронизацию данных все карт - - Node cardNumberBtn = xmlScreenParser.findButtonNode(cardNumber, true); - AdbUtils::makeTap(deviceId, cardNumberBtn.x(), cardNumberBtn.y()); - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); - Node filterBtn = xmlScreenParser.findButtonNode("РСХБ Онлайн", false); - if (!filterBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, filterBtn.x(), filterBtn.y()); - QThread::msleep(1000); - } else { - qWarning() << "--- filterBtn not found"; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); - Node allTransactionFilerBtn = xmlScreenParser.findButtonNode("Все операции", false); - if (!allTransactionFilerBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, allTransactionFilerBtn.x(), allTransactionFilerBtn.y()); - QThread::msleep(1000); - } else { - qWarning() << "--- allTransactionFilerBtn not found"; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); - for (int i = 0; i < 5; ++i) { - QList transactions = xmlScreenParser.getLast2DaysTransactions(); - if (!transactions.empty()) { - for (const Node &transaction: transactions) { - qDebug() << transaction.text; - // TODO вот мы дошли до списка транзакций - } - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter)); - } - - // - } else { - // пишем в БД, что не дошли до домашней страницы, и завершаемся - // сохраняем скрин приложения - // дделаем алерт - } - } else { - // пишем в БД, что не дошли до домашней страницы, и завершаемся - // сохраняем скрин приложения - // дделаем алерт - } - - return; - - // 1. Идем на домашнюю страницу - if (goToHomeScreen(deviceId, packageName, pinCode, width, height)) { - // мы на главном - // перед тем как юзать снова проверить на банеры - // Проверяем на банеры после пин-кодом - - - // Это уже для шага дальше - // если нулевые координаты, свайпаем вверх, чтобы текст "Счета и карты" был у верхнего края - } else { - // пишем в БД, что не дошли до домашней страницы, и завершаемся - // сохраняем скрин приложения - // дделаем алерт - } - - // 2. Идем дальше - - emit finished(); -} - - -void HomeScreenScript::stop() { - m_running = false; -} - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/HomeScreenScript.h b/android/rshb/HomeScreenScript.h deleted file mode 100644 index 59b84cb..0000000 --- a/android/rshb/HomeScreenScript.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -#include "ScreenXmlParser.h" - -namespace Rshb { - -class HomeScreenScript final : public QObject { - Q_OBJECT - -public: - explicit HomeScreenScript(QObject *parent = nullptr); - - ~HomeScreenScript() override; - -public slots: - void start(); - - void stop(); - -signals: - void finished(); - -private: - bool m_running = true; - ScreenXmlParser xmlScreenParser; - - bool goToHomeScreen( - const QString &deviceId, - const QString &packageName, - const QString &pinCode, - int width, - int height - ); -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/LoginAndCheckAccountsScript.cpp b/android/rshb/LoginAndCheckAccountsScript.cpp deleted file mode 100644 index 9241024..0000000 --- a/android/rshb/LoginAndCheckAccountsScript.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "LoginAndCheckAccountsScript.h" - -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" - -namespace Rshb { - -LoginAndCheckAccountsScript::LoginAndCheckAccountsScript( - QString deviceId, - QString appCode, - QObject *parent -) : CommonScript(parent), - m_deviceId(std::move(deviceId)), m_appCode(std::move(appCode)) { -} - -LoginAndCheckAccountsScript::~LoginAndCheckAccountsScript() = default; - - -void LoginAndCheckAccountsScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, 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 (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Cant open the home screen: " + m_deviceId + " " + m_appCode; - qWarning() << m_error; - if (!ApplicationDAO::updatePinCodeStatus(app.id, "no", "Не прошла проверка [дата]")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finishedWithResult(m_error); - return; - } else { - if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { - QList > accounts = xmlScreenParser.parseAccountsInfo(); - for (auto &[account, node]: accounts) { - account.deviceId = m_deviceId; - account.appCode = m_appCode; - if (!AccountDAO::upsertAccount(account)) { - m_error = "Not updated: " + account.lastNumbers; - qCritical() << m_error; - } - } - - postAccountsData(accounts); - } else { - qWarning() << "Cant go to all products: " << m_deviceId << " " << m_appCode; - } - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finishedWithResult(m_error); -} - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/LoginAndCheckAccountsScript.h b/android/rshb/LoginAndCheckAccountsScript.h deleted file mode 100644 index 8d08cfa..0000000 --- a/android/rshb/LoginAndCheckAccountsScript.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "CommonScript.h" - -namespace Rshb { - -class LoginAndCheckAccountsScript final : public CommonScript { - Q_OBJECT - -public: - ~LoginAndCheckAccountsScript() override; - - explicit LoginAndCheckAccountsScript( - QString deviceId, - QString appCode, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const QString m_deviceId; - const QString m_appCode; - QString m_error; -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/PayByPhoneScript.cpp b/android/rshb/PayByPhoneScript.cpp deleted file mode 100644 index 86d6853..0000000 --- a/android/rshb/PayByPhoneScript.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#include "PayByPhoneScript.h" - -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/ApplicationInfo.h" -#include "db/DeviceInfo.h" - -namespace Rshb { - -PayByPhoneScript::PayByPhoneScript( - AccountInfo account, - QString phone, - QString bankName, - const double amount, - QObject *parent -) : CommonScript(parent), - m_account(std::move(account)), m_phone(std::move(phone)), - m_bankName(std::move(bankName)), m_amount(amount) { -} - -PayByPhoneScript::~PayByPhoneScript() = default; - - -void PayByPhoneScript::makePayment( - const QString &deviceId, - const QString &pinCode, - int width, - int height -) { - // Оплата со страницы карты - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - Node payBtn = xmlScreenParser.findButtonNode("Оплатить", false); - if (!payBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, payBtn.x(), payBtn.y()); - QThread::msleep(1000); - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_pay_btn_not_found"); - m_error = "Кнопка 'Оплатить' не нашлась"; - qDebug() << m_error; - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - // TODO - По номеру карты В РСХБ и другие банки - Node payTitle = xmlScreenParser.findTextNode("Платежи и переводы"); - if (!payTitle.isEmpty() && payTitle.y() < 300) { - Node payByPhoneBtn = xmlScreenParser.findButtonNode("По номеру телефона В РСХБ и через", false); - if (!payByPhoneBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, payByPhoneBtn.x(), payByPhoneBtn.y()); - QThread::msleep(1000); - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_not_found_pay_by_phone"); - m_error = "Не смогли найти пункт 'По номеру телефона В РСХБ и через'"; - qDebug() << m_error; - return; - } - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_go_to_payments"); - m_error = "Не смогли перейти на экран 'Платежи и переводы'"; - qDebug() << m_error; - return; - } - - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - Node payByPhoneTitle = xmlScreenParser.findTextNode("Перевод по телефону"); - Node continueBtn = xmlScreenParser.findButtonNode("Продолжить", false); - if (!payByPhoneTitle.isEmpty() && payByPhoneTitle.y() < 300 && continueBtn.isEmpty()) { - Node inputPhone = xmlScreenParser.findFirstEditText(); - AdbUtils::makeTap(deviceId, inputPhone.x(), inputPhone.y()); - QThread::msleep(500); - AdbUtils::inputText(deviceId, m_phone); - QThread::msleep(1000); - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_find_pay_by_phone"); - m_error = "Не смогли найти пункт 'Перевод по телефону'"; - qDebug() << m_error; - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - QString payBankName = m_bankName == QStringLiteral("Россельхозбанк") ? "Россельхозбанк" : "Другой банк через СБП"; - Node payOtherBankBtn = xmlScreenParser.findButtonNode(payBankName, false); - if (!payOtherBankBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, payOtherBankBtn.x(), payOtherBankBtn.y()); - QThread::msleep(1000); - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_tap_on_bank"); - m_error = "Не смогли нажать на первичный выбор банка"; - qDebug() << m_error; - return; - } - - if (m_bankName != QStringLiteral("Россельхозбанк")) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - for (int i = 0; i < 5; ++i) { - Node allBanksTitle = xmlScreenParser.findTextNode("Банки участники"); - if (!allBanksTitle.isEmpty() && allBanksTitle.y() < 300) { - Node inputBankName = xmlScreenParser.findFirstEditText(); - AdbUtils::makeTap(deviceId, inputBankName.x(), inputBankName.y()); - QThread::msleep(500); - AdbUtils::enableAdbIMEKeyboard(deviceId); - AdbUtils::inputAdbIMEText(deviceId, m_bankName); - AdbUtils::disableAdbIMEKeyboard(deviceId); - QThread::msleep(1000); - break; - } else { - qDebug() << "--- allBanksTitle not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - for (int i = 0; i < 5; ++i) { - Node bankNameListItem = xmlScreenParser.findTextNode(m_bankName); - if (!bankNameListItem.isEmpty()) { - AdbUtils::makeTap(deviceId, bankNameListItem.x(), bankNameListItem.y()); - QThread::msleep(1000); - break; - } else { - qDebug() << "--- bankNameListItem not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - - const bool isRshbBank = m_bankName == QStringLiteral("Россельхозбанк"); - QString payByPhoneScreenTitleText = isRshbBank ? "Перевод клиенту РСХБ" : "Перевод по телефону"; - Node payByPhoneScreenTitle = xmlScreenParser.findTextNode(payByPhoneScreenTitleText); - Node continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false); - - if (isRshbBank || - !payByPhoneScreenTitle.isEmpty() && payByPhoneScreenTitle.y() < 300 && !continuePayBtn.isEmpty()) { - Node sumBtn = xmlScreenParser.findButtonNode("0", false); - if (isRshbBank || !sumBtn.isEmpty()) { - if (!isRshbBank) { - AdbUtils::makeTap(deviceId, sumBtn.x(), sumBtn.y()); - QThread::msleep(500); - } - AdbUtils::inputText(deviceId, QString::number(m_amount)); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - Node readyBtn = xmlScreenParser.findTextNode("Готово"); - if (!readyBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, readyBtn.x(), readyBtn.y()); - QThread::msleep(500); - if (isRshbBank) { - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false); - } - AdbUtils::makeTap(deviceId, continuePayBtn.x(), continuePayBtn.y()); - QThread::msleep(1000); - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_tap_on_ready"); - m_error = "Не смогли нажать 'Готово'"; - qDebug() << m_error; - return; - } - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_tap_on_continue"); - m_error = "Не смогли нажать 'Продолжить'"; - qDebug() << m_error; - return; - } - } else { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_find_bank_title"); - m_error = "Не смогли найти заголовок: " + payByPhoneScreenTitleText; - qDebug() << m_error; - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - bool inputedPinCode = false; - for (int i = 0; i < 5; ++i) { - // TODO кнопка содержит "Перевести 10 ₽" если будет от 1000 то скорее всвего будет "Перевести 1 000 ₽", - // TODO не ясно как форматирование будет, поэтому пока на начало зашьем - Node makePayBtn = xmlScreenParser.findButtonNode("Перевести ", true); - if (!makePayBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, makePayBtn.x(), makePayBtn.y()); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите код доступа"); - pincode.size() == 4) { - inputPinCode(deviceId, pincode); - inputedPinCode = true; - // После ввода пин-кода, ждем 3с - QThread::msleep(3000); - } - break; - } else { - qWarning() << "--- makePayBtn not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - } - - if (!isRshbBank && !inputedPinCode) { - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_input_pin_code"); - m_error = "Не смогли ввести пин-код!'"; - qDebug() << m_error; - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - - for (int i = 0; i < 10; ++i) { - Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false); - if (!moreDetailBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, moreDetailBtn.x(), moreDetailBtn.y()); - QThread::msleep(200); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - - // TODO считываем все данные по переводу - TransactionInfo transaction = xmlScreenParser.parseTransactionInfo(width, height); - if (isRshbBank) { - transaction.bankName = m_bankName; - } - transaction.accountId = m_account.id; - transaction.amount = -m_amount; - transaction.type = TransactionType::Phone; - transaction.id = TransactionDAO::insertTransaction(transaction); - if (transaction.id == -1) { - // FIXME alert!! - qDebug() << "Transaction has not inserted..."; - } - - // ждем 10 секунд смену статуса - for (int k = 0; k < 10; ++k) { - Node paymentCompleted = xmlScreenParser.findTextNode("Исполнен", 0, 0, width, height / 2); - if (!paymentCompleted.isEmpty()) { - transaction.status = TransactionStatus::Complete; - if (!TransactionDAO::updateTransactionStatus(transaction.id, TransactionStatus::Complete)) { - qDebug() << "Not updated: " << transaction.id; - } - // TODO обновляем данные - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - } - - postNewTransactionData(m_account, transaction); - - AdbUtils::takeScreenshot(deviceId, "pay_by_phone_finish_transactions_" + transaction.id); - AdbUtils::takeXmlDump(deviceId, "pay_by_phone_finish_transactions_" + transaction.id); - break; - } else { - qWarning() << "--- moreDetailBtn not found, i: " << i; - } - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - } - - Node homeBtn = xmlScreenParser.findButtonNode("Вернуться на главную", false); - AdbUtils::makeTap(deviceId, homeBtn.x(), homeBtn.y()); - QThread::msleep(1000); -} - -void PayByPhoneScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - if (device.id.isEmpty() || app.id == -1) { - m_error = "Device or app not found...."; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { - QList > accounts = xmlScreenParser.parseAccountsInfo(); - for (auto &[account, node]: accounts) { - account.deviceId = m_account.deviceId; - account.appCode = m_account.appCode; - account.status = account.amount > 0 ? AccountStatus::Active : AccountStatus::Off; - if (!AccountDAO::upsertAccount(account)) { - qDebug() << "Not updated: " << account.lastNumbers; - } - } - - postAccountsData(accounts); - - for (auto &[account, node]: accounts) { - // FIXME будем считать что числа последние везде уникальные - if (m_account.lastNumbers == account.lastNumbers) { - if (account.amount >= m_amount) { - AdbUtils::makeTap(device.id, node.x(), node.y()); - QThread::msleep(1000); - } else { - AdbUtils::takeScreenshot(device.id, "pay_by_phone_not_enough_money_" + QString::number(m_account.id)); - m_error = "Not enough money, id: " + QString::number(m_account.id) - + ", amount: " + QString::number(account.amount) - + ", trx amount: " + QString::number(m_amount); - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - break; - } - } - - makePayment(device.id, app.pinCode, device.screenWidth, device.screenHeight); - // AdbUtils::tryToKillApplication(device.id, app.package); TODO - надо ли это??? - } else { - AdbUtils::takeScreenshot(device.id, "pay_by_phone_cant_open_all_products_" + QString::number(m_account.id)); - AdbUtils::tryToKillApplication(device.id, app.package); - m_error = "Не смогли открыть 'Все продукты'"; - qDebug() << m_error; - } - - emit finishedWithResult(m_error); -} - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/PayByPhoneScript.h b/android/rshb/PayByPhoneScript.h deleted file mode 100644 index 09b80a4..0000000 --- a/android/rshb/PayByPhoneScript.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include "CommonScript.h" - -namespace Rshb { - -class PayByPhoneScript final : public CommonScript { - Q_OBJECT - -public: - ~PayByPhoneScript() override; - - PayByPhoneScript( - AccountInfo account, - QString phone, - QString bankName, - double amount, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - const QString m_phone; - const QString m_bankName; - const double m_amount; - QString m_error; - - void makePayment( - const QString &deviceId, - const QString &pinCode, - int width, - int height - ); -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/ScreenXmlParser.cpp b/android/rshb/ScreenXmlParser.cpp deleted file mode 100644 index 16a3919..0000000 --- a/android/rshb/ScreenXmlParser.cpp +++ /dev/null @@ -1,980 +0,0 @@ -#include "ScreenXmlParser.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" -#include "time/DateUtils.h" - -namespace Rshb { - -ScreenXmlParser::ScreenXmlParser(QObject *parent) : QObject(parent) { -} - -ScreenXmlParser::~ScreenXmlParser() = default; - -static const QRegularExpression boundsRegex(R"(\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\])"); -static const QRegularExpression onlyDigitsRegex(R"([^0-9])"); -static const QRegularExpression onlyDigitsAndDotRegex(R"([^0-9.])"); - -static const QRegularExpression rePhone(R"raw((\+7\(?\d{3}\)?[- ]?\d{3}[- ]?\d{2}[- ]?\d{2}))raw"); -static const QRegularExpression reFromBank(R"raw(из (.+?) на)raw"); -static const QRegularExpression reToBank(R"raw(в (.+?) через)raw"); -static const QRegularExpression reSbpId(R"(СБП(?: С2С)? ([A-Z0-9]{30,}))"); -static const QRegularExpression reIdAlt(R"(ID перевода (\d+))"); -static const QRegularExpression reDate(R"((?:Дата операции|дата) (\d{2}\.\d{2}\.\d{4}(?: \d{2}:\d{2}:\d{2})?))"); -static const QRegularExpression reSender(R"(от ([^\(]+))"); -static const QRegularExpression reNameAlt(R"(Перевод ([А-Яа-яЁё\s\*]+) в)"); - -static const QRegularExpression reCardData(R"(цифрами\s+(\d\s\d\s\d\s\d).*?Сумма:\s([\d\s]+)\sруб)"); - -Node ScreenXmlParser::tryToFindBannerOrDialog(const int width, const int height) { - // 1. Ищем push banner - он перекрывает весь экран - Node first = m_nodes[0]; - for (const Node &node: m_nodes) { - // Если это однозначно, что пуш-баннер - if (node.resourceId == "aft-popup-pushNotification-iconClose" && node.className == "android.widget.Button") { - return node; - } - // Если есть кнопка "Позже" - if (node.text.trimmed() == "Позже" && node.className == "android.widget.Button") { - return node; - } - // Если нашли пустую кнпоку - if (node.className == "android.widget.Button" && node.clickable - && node.text.isEmpty() && node.contentDesc.isEmpty()) { - // - if (node.x() > width / 2 && node.y() < width / 2) { - return node; - } - } - } - return {}; -} - -/** - * Смотрим что есть текс "Мы заботимся о вашей безопасности" и - * кнопка "Отложить" - * @return координаты кнопки для закрытия - */ -Node ScreenXmlParser::tryToFindSecurityBanner() { - Node text; - Node closeBtn; - for (const Node &node: m_nodes) { - if (node.text.trimmed() == "Мы заботимся о вашей безопасности" && node.className == "android.app.Dialog") { - text = node; - } - if (node.text.trimmed() == "Отложить" && node.className == "android.widget.Button") { - closeBtn = node; - } - } - - // Не нужно проверять все, если хотя бы одна координата есть - if (!text.isEmpty() && !closeBtn.isEmpty()) { - return closeBtn; - } - return {}; -} - -Node ScreenXmlParser::tryToFindSelectBottomSheet() { - Node closeBtn; - for (const Node &node: m_nodes) { - if ((node.text.trimmed() == "Отмена" || node.text.trimmed() == "Отложить") && node.className == - "android.widget.Button") { - closeBtn = node; - } - } - // Не нужно проверять все, если хотя бы одна координата есть - if (!closeBtn.isEmpty()) { - return closeBtn; - } - return {}; -} - -Node ScreenXmlParser::findTextNode(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlParser::findTextNode(const QString &text, const int x1, const int y1, const int x2, const int y2) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text - && node.x() >= x1 && node.x() <= x2 && node.y() >= y1 && node.y() <= y2) { - return node; - } - } - return {}; -} - -Node ScreenXmlParser::findFirstEditText() { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.focusable) { - return node; - } - } - return {}; -} - -Node ScreenXmlParser::findButtonNode(const QString &text, const bool contains) { - for (const Node &node: m_nodes) { - if (contains) { - if (node.className == "android.widget.Button" && node.text.contains(text)) { - return node; - } - } else { - if (node.className == "android.widget.Button" && node.text.trimmed() == text) { - return node; - } - } - } - return {}; -} - -bool ScreenXmlParser::isHomeScreen() { - // Есть заголовок "Счета и карты" и кнопка "Все продукты" - Node text; - Node allProductBtn; - for (const Node &node: m_nodes) { - if (node.text.trimmed() == "Счета и карты" && node.className == "android.widget.TextView") { - text = node; - } - if (node.text.trimmed() == "Все продукты" && node.className == "android.widget.Button") { - allProductBtn = node; - } - } - return !text.isEmpty() && !allProductBtn.isEmpty(); -} - -QList ScreenXmlParser::findPinCodeNodes(const QString &pinCode) { - QMap buttons; - const QSet numberSet = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - - for (const Node &node: m_nodes) { - const QString &text = node.text.trimmed(); - if (node.className == "android.widget.Button" && numberSet.contains(text)) { - buttons[text] = node; - } - } - - QList nodes; - for (int i = 0; i < pinCode.length(); ++i) { - nodes.append(buttons[QString(pinCode.at(i))]); - } - return nodes; -} - -QList ScreenXmlParser::tryToFindPinCode(const QString &pinCode, const QString &title) { - for (const Node &node: m_nodes) { - // если там есть такая кнопка значит банер есть, и пин-код не прожать - if (node.text.trimmed() == "Отложить" && node.className == "android.widget.Button") { - return {}; - } - } - for (const Node &node: m_nodes) { - // FIXME определение пин-кода лучше сделать - if (node.text.trimmed() == title) { - return findPinCodeNodes(pinCode); - } - } - return {}; -} - -QList ScreenXmlParser::getLast2DaysTransactions() { - QList nodes; - for (const Node &node: m_nodes) { - if (node.className == "android.widget.Button" && node.text.contains("рубл") - && (node.text.contains("Минус") || node.text.contains("Плюс"))) { - nodes.append(node); - } - } - return nodes; -} - -/** -* - */ -void ScreenXmlParser::parseAndSaveXml(const QString &xml) { - QDomDocument doc; - doc.setContent(xml); - m_xml = doc.documentElement(); - m_nodes.clear(); - - QXmlStreamReader reader(xml); - while (!reader.atEnd()) { - reader.readNext(); - if (reader.isStartElement() && reader.name() == "node") { - Node node = Node(); - if (reader.attributes().hasAttribute("bounds")) { - QString bounds = reader.attributes().value("bounds").toString(); - if (QRegularExpressionMatch match = boundsRegex.match(bounds); match.hasMatch()) { - node.setBounds( - match.captured(1).toInt(), - match.captured(2).toInt(), - match.captured(3).toInt(), - match.captured(4).toInt() - ); - } - } - if (reader.attributes().hasAttribute("text")) { - node.text = reader.attributes().value("text").toString(); - } - - if (reader.attributes().hasAttribute("content-desc")) { - node.contentDesc = reader.attributes().value("content-desc").toString(); - } - - if (reader.attributes().hasAttribute("class")) { - node.className = reader.attributes().value("class").toString(); - } - - if (reader.attributes().hasAttribute("clickable")) { - node.clickable = reader.attributes().value("clickable").toString() == "true"; - } - if (reader.attributes().hasAttribute("focusable")) { - node.focusable = reader.attributes().value("focusable").toString() == "true"; - } - if (reader.attributes().hasAttribute("resource-id")) { - node.resourceId = reader.attributes().value("resource-id").toString(); - } - - m_nodes.append(node); - } - } -} - -QString getDayOfWeekAsString(const int day_of_week) { - switch (day_of_week) { - case 1: return "понедельник"; - case 2: return "вторник"; - case 3: return "среда"; - case 4: return "четверг"; - case 5: return "пятница"; - case 6: return "суббота"; - case 7: return "воскресенье"; - default: return ""; - } -} - -QString getMonthAsString(const int month) { - switch (month) { - case 1: return "января"; - case 2: return "февраля"; - case 3: return "марта"; - case 4: return "апреля"; - case 5: return "мая"; - case 6: return "июня"; - case 7: return "июля"; - case 8: return "августа"; - case 9: return "сентября"; - case 10: return "октября"; - case 11: return "ноября"; - case 12: return "декабря"; - default: return ""; - } -} - -QDomElement findSubElement(const QDomElement &elem, const QString &index) { - if (elem.hasChildNodes()) { - QDomNodeList subInfoList = elem.childNodes(); - for (QDomNode subInfoNode: subInfoList) { - if (!subInfoNode.isElement()) continue; - QDomElement subInfo = subInfoNode.toElement(); - if (subInfo.attribute("index") == index) { - return subInfo; - } - } - } - return {}; -} - -QString findSubInfo(const QDomElement &elem, const QString &index) { - QDomElement subInfo = findSubElement(elem, index); - if (!subInfo.isNull()) { - return subInfo.attribute("text"); - } - return ""; -} - -bool hasTimeComponent(const QString &datetimeStr) { - QDateTime dt = QDateTime::fromString(datetimeStr, "dd.MM.yyyy HH:mm:ss"); - if (!dt.isValid()) { - return false; // Строка не соответствует формату даты и времени - } - return dt.time() != QTime(0, 0, 0); // Проверка, что время не равно 00:00:00 -} - -QString transformBankDateTime(const QString &date) { - QDateTime dt; - if (date.contains(",")) { - // 10.05.2025, 15:03 - dt = QDateTime::fromString(date, "dd.MM.yyyy, HH:mm"); - } else { - dt = QDateTime::fromString(date, "dd.MM.yyyy HH:mm:ss"); - } - if (!dt.isValid()) { - dt = QDateTime::fromString(date, "dd.MM.yyyy HH:mm"); - } - if (!dt.isValid()) { - return date; // не распарсили - } - return dt.toString("dd.MM.yyyy HH:mm:ss"); -} - -void parseTransferInfo(const QString &text, TransactionInfo &info) { - if (const auto m = reSender.match(text); m.hasMatch()) { - info.name = m.captured(1).trimmed(); - } else if (const auto m1 = reNameAlt.match(text); m1.hasMatch()) { - info.name = m1.captured(1).trimmed(); - } - - if (const auto m = rePhone.match(text); m.hasMatch()) { - info.phone = m.captured(1).trimmed().remove(onlyDigitsRegex); - } - - if (const auto m = reFromBank.match(text); m.hasMatch()) { - if (const QString bank = m.captured(1).trimmed(); !bank.isEmpty()) { - info.bankName = bank; - } - } else if (const auto m1 = reToBank.match(text); m1.hasMatch()) { - if (const QString bank = m1.captured(1).trimmed(); !bank.isEmpty()) { - info.bankName = bank; - } - } - - if (const auto m = reSbpId.match(text); m.hasMatch()) { - info.bankTrExternalId = m.captured(1); - } else if (const auto m1 = reIdAlt.match(text); m1.hasMatch()) { - info.bankTrExternalId = m1.captured(1); - } - - if (const auto m = reDate.match(text); m.hasMatch()) { - const QString date = m.captured(1); - // если не пустая и дата у нас с 00:00 - // if (hasTimeComponent(date)) { - info.bankTime = transformBankDateTime(date); - // } - } -} - -void traverseNodes(const QDomNode &node) { - if (node.isElement()) { - QDomElement elem = node.toElement(); - - if (node.isElement()) { - QDomElement elem = node.toElement(); - - if (elem.tagName() == "node" && - elem.attribute("class") == "android.widget.Button" && - elem.attribute("text") == "Детали операции") { - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - - for (int j = 0; j < detailInfo.count(); ++j) { - QDomNode info = detailInfo.at(j); - if (!info.isElement()) continue; - QDomElement infoElement = info.toElement(); - QString index = infoElement.attribute("index"); - switch (index.toInt()) { - case 2: - qDebug() << "Дата операции:" << findSubInfo(infoElement, "1"); - break; - case 5: - qDebug() << "Название:" << findSubInfo(infoElement, "1"); - qDebug() << "-------------------------------------"; - // parseTransfer(findSubInfo(infoElement)); - break; - case 7: - qDebug() << "Комиссия:" << findSubInfo(infoElement, "1"); - break; - default: - qDebug() << "Прочее:" << findSubInfo(infoElement, "1"); - break; - } - - /** - Node: "2" "" - ... Node: "0" "Дата операции" - ... Node: "1" "30.04.2025 17:46:29" - Node: "5" "" - ... Node: "0" "Название" - ... Node: "1" "DBO TRANSFER RSHB INTERNET-BANK" - Node: "7" "" - ... Node: "0" "Комиссия" - ... Node: "1" "50 ₽" - */ - - // if (sibElem.hasChildNodes()) { - // QDomNodeList sibChildren = sibElem.childNodes(); - // for (int k = 0; k < sibChildren.count(); ++k) { - // QDomNode sibChild = sibChildren.at(k); - // if (!sibChild.isElement()) continue; - // QDomElement sibChildElem = sibChild.toElement(); - // qDebug() << "... Node:" - // << sibChildElem.attribute("index") - // << sibChildElem.attribute("text"); - // // << sibChildElem.attribute("class"); - // - // - // if (sibChildElem.hasChildNodes()) { - // QDomNodeList sibChildren2 = sibChildElem.childNodes(); - // for (int k = 0; k < sibChildren2.count(); ++k) { - // QDomNode sibChild2 = sibChildren2.at(k); - // if (!sibChild2.isElement()) continue; - // QDomElement sibChildElem2 = sibChild2.toElement(); - // - // qDebug() << "...... Node:" - // << sibChildElem2.attribute("index") - // << sibChildElem2.attribute("text"); - // // << sibChildElem2.attribute("class"); - // - // if (sibChildElem2.hasChildNodes()) { - // QDomNodeList sibChildren3 = sibChildElem2.childNodes(); - // for (int k = 0; k < sibChildren3.count(); ++k) { - // QDomNode sibChild3 = sibChildren3.at(k); - // if (!sibChild3.isElement()) continue; - // QDomElement sibChildElem3 = sibChild3.toElement(); - // - // qDebug() << "......... Node:" - // << sibChildElem3.attribute("index") - // << sibChildElem3.attribute("text"); - // // << sibChildElem2.attribute("class"); - // } - // } - // } - // } - // } - // } - } - } - } - } - - // Рекурсивно обходим всех детей - QDomNode child = node.firstChild(); - while (!child.isNull()) { - traverseNodes(child); - child = child.nextSibling(); - } -} - -QDomNode findNode(const QDomNode &node, const QString &text) { - if (node.isElement()) { - QString nodeText = node.toElement().attribute("text"); - if (nodeText.toLower().startsWith(text)) { - return node; - } - } - - - QDomNode child = node.firstChild(); - while (!child.isNull()) { - QDomNode result = findNode(child, text); - if (!result.isNull()) { - return result; - } - child = child.nextSibling(); - } - return {}; -} - -float extractAmounts(const QString &text) { - // Разрешаем пробелы внутри чисел, захватываем "рубль", "рубля", "рублей" и т.д. - // QRegularExpression re(R"((Плюс|Минус)\s+([\d\s]+)\s+рубл\w*\s+(\d+)\s+копе\w*)"); - QRegularExpression re( - R"((Плюс|Минус)\s+([\d\s]+)\s+рубл\w*\s+(\d+)\s+копе\w*)", - QRegularExpression::UseUnicodePropertiesOption - ); - - QRegularExpressionMatch match = re.match(text); - if (match.hasMatch()) { - QString signStr = match.captured(1); // "Плюс" или "Минус" - QString rublesStr = match.captured(2).remove(' '); // "1 000" → "1000" - int rubles = rublesStr.toInt(); - int kopecks = match.captured(3).toInt(); - - int totalKopecks = rubles * 100 + kopecks; - if (signStr == "Минус") { - totalKopecks *= -1; - } - - return totalKopecks / 100.0f; - } - return 0; -} - -QString beforeAmount(const QString &text) { - int index = text.indexOf(QRegularExpression("Плюс|Минус")); - if (index != -1) { - return text.left(index).trimmed(); - } - return text; // если не найдено — вернуть всю строку -} - -double parseAmount(const QString &amountAsString) { - QString numberString = amountAsString; - numberString.remove(QRegularExpression(R"([^\dточка])")); - numberString.replace("точка", "."); - double amount = numberString.toDouble(); - - if (amountAsString.startsWith("-")) { - amount *= -1; - } - - return amount; -} - -QDomElement findElementWithListView(const QDomElement &parent) { - QDomElement element = parent.firstChildElement(); - while (!element.isNull()) { - QDomElement child = element.firstChildElement(); - while (!child.isNull()) { - if (child.hasAttribute("class") && child.attribute("class") == "android.widget.ListView") { - return child; - } - child = child.nextSiblingElement(); - } - - QDomElement found = findElementWithListView(element); - if (!found.isNull()) { - return found; - } - element = element.nextSiblingElement(); - } - return {}; -} - -QList ScreenXmlParser::parseTodayAndYesterdayReportHistory() { - QList transactions; - - QDomElement child = findElementWithListView(m_xml); - QDomNodeList listView = findElementWithListView(m_xml).childNodes(); - qDebug() << listView.count(); - if (!listView.isEmpty()) { - QString date; - for (QDomNode node: listView) { - if (node.hasChildNodes()) { - QString text; - if (node.childNodes().count() > 1) { - date = node.firstChildElement().attribute("text"); - text = node.lastChildElement().attribute("text"); - } else if (node.childNodes().count() == 1) { - text = node.firstChildElement().attribute("text"); - } - - TransactionInfo transaction; - if (text.startsWith("Перевод")) { - QRegularExpression re(R"([+-] \d{1,3}(?: \d{3})* точка \d{2} Рубль$)"); - QRegularExpressionMatch match = re.match(text); - if (match.hasMatch()) { - QString amount = match.captured(0); - transaction.amount = parseAmount(amount.trimmed()); - text.chop(amount.length()); - } - transaction.description = text.trimmed(); - parseTransferInfo(text, transaction); - if (transaction.bankTime.isEmpty()) { - transaction.bankTime = transformBankDateTime(date); - } - } else { - // карта - if (text.contains("без учета комиссии")) { - QRegularExpression re(R"( без учета комиссии -\d{1,3}(?: \d{3})* точка \d{2} Рубль$)"); - QRegularExpressionMatch match = re.match(text); - if (match.hasMatch()) { - QString fee = match.captured(0); - text.chop(fee.length()); - transaction.fee = parseAmount(fee.replace("без учета комиссии", "").trimmed()); - } - } - QRegularExpression re(R"([+-] \d{1,3}(?: \d{3})* точка \d{2} Рубль$)"); - QRegularExpressionMatch match = re.match(text); - if (match.hasMatch()) { - QString amount = match.captured(0); - transaction.amount = parseAmount(amount.trimmed()); - text.chop(amount.length()); - } - transaction.description = text.trimmed(); - if (transaction.bankTime.isEmpty()) { - transaction.bankTime = transformBankDateTime(date); - } - } - transactions.append(transaction); - } - } - } else { - qDebug() << "ListView is empty"; - } - return transactions; -} - -QList ScreenXmlParser::parseTodayAndYesterdayHistory() { - QDate today = DateUtils::getMoscowNow().date(); - QDate yesterday = today.addDays(-1); - QDate beforeYesterday = today.addDays(-2); - - - QString todayTitle = QString("сегодня, %1").arg(getDayOfWeekAsString(today.dayOfWeek())); - QString yesterdayTitle = QString("вчера, %1").arg(getDayOfWeekAsString(yesterday.dayOfWeek())); - QString beforeYesterdayTitle = QString("%1 %2, %3").arg( - QString::number(beforeYesterday.day()), - getMonthAsString(beforeYesterday.month()), - getDayOfWeekAsString(beforeYesterday.dayOfWeek()) - ); - - QDomNode todayNode = findNode(m_xml, todayTitle); - QDomNode yesterdayNode = findNode(m_xml, yesterdayTitle); - - QDomNode parentNode; - if (!yesterdayNode.isNull()) { - parentNode = yesterdayNode.parentNode(); - } else if (!todayNode.isNull()) { - parentNode = todayNode.parentNode(); - } - - - QList transactions; - if (!parentNode.isNull()) { - QDate date = {}; - for (QDomNode transactionNode: parentNode.childNodes()) { - QString transactionShortText = transactionNode.toElement().attribute("text"); - - const QString titleLowerCase = transactionShortText.toLower(); - if (titleLowerCase.startsWith(beforeYesterdayTitle)) { - // только сегодня и вчера парсим, позавчера не трогаем - break; - } - if (titleLowerCase.startsWith(todayTitle)) { - date = today; - transactionShortText = transactionShortText.mid(todayTitle.length() + 1); - } - if (titleLowerCase.startsWith(yesterdayTitle)) { - date = yesterday; - transactionShortText = transactionShortText.mid(yesterdayTitle.length() + 1); - } - - if (transactionShortText.contains("ЕСПП")) { - continue; - } - - float amount = extractAmounts(transactionShortText); - QString shortDesc = beforeAmount(transactionShortText); - - TransactionInfo info; - info.amount = amount; - info.bankTime = date.toString("dd.MM.yyyy"); - info.description = shortDesc; - - transactions.append(info); - } - } - return transactions; -} - -void parse2daysHistory(const QDomNode &node) { - QDate today = DateUtils::getMoscowNow().date(); - QDate tomorrow = today.addDays(-1); - QDate after2Days = today.addDays(-2); - - QString todayTitle = QString("сегодня, %1").arg(getDayOfWeekAsString(today.dayOfWeek())); - QDomNode todayNode = findNode(node, todayTitle); - if (todayNode.isNull()) { - } else { - qDebug() << "todayNode: " << todayNode.toElement().attribute("text"); - } - - QString tomorrowTitle = QString("вчера, %1").arg(getDayOfWeekAsString(tomorrow.dayOfWeek())); - QDomNode tomorrowNode = findNode(node, tomorrowTitle); - if (tomorrowNode.isNull()) { - } else { - qDebug() << "tomorrowNode: " << tomorrowNode.toElement().attribute("text"); - } - - // 30 апреля, Среда - QString after2DaysTitle = QString("%1 %2, %3").arg( - QString::number(after2Days.day()), - getMonthAsString(after2Days.month()), - getDayOfWeekAsString(after2Days.dayOfWeek())); - QDomNode after2DaysNode = findNode(node, after2DaysTitle); - if (after2DaysNode.isNull()) { - qDebug() << "No after2DaysNode"; - } else { - qDebug() << "after2DaysNode: " << after2DaysNode.toElement().attribute("text"); - } - - QDomNode parentNode; - if (!todayNode.isNull() || !tomorrowNode.isNull() || !after2DaysNode.isNull()) { - if (!todayNode.isNull()) { - parentNode = todayNode.parentNode(); - } - if (!tomorrowNode.isNull()) { - parentNode = tomorrowNode.parentNode(); - } - if (!after2DaysNode.isNull()) { - parentNode = after2DaysNode.parentNode(); - } - } - - - if (!parentNode.isNull()) { - for (QDomNode child_node: parentNode.childNodes()) { - QString child_text = child_node.toElement().attribute("text"); - if (child_text.toLower().startsWith(todayTitle)) { - qDebug() << "=== Today: " << todayTitle; - qDebug() << "node: " << beforeAmount(child_text.mid(todayTitle.length() + 1)); - qDebug() << "--> sum: " << QString::number(extractAmounts(child_text), 'f', 2); - // qDebug() << ""; - continue; - } - if (child_text.toLower().startsWith(tomorrowTitle)) { - qDebug() << "=== Yesterday: " << todayTitle; - qDebug() << "node: " << beforeAmount(child_text.mid(tomorrowTitle.length() + 1)); - qDebug() << "--> sum: " << QString::number(extractAmounts(child_text), 'f', 2); - // qDebug() << ""; - continue; - } - if (child_text.toLower().startsWith(after2DaysTitle)) { - qDebug() << "=== Before yesterday: " << todayTitle; - qDebug() << "node: " << beforeAmount(child_text.mid(after2DaysTitle.length() + 1)); - qDebug() << "--> sum: " << QString::number(extractAmounts(child_text), 'f', 2); - // qDebug() << ""; - continue; - } - - qDebug() << "node: " << beforeAmount(child_node.toElement().attribute("text")); - qDebug() << "--> sum: " << - QString::number(extractAmounts(child_node.toElement().attribute("text")), 'f', 2); - // qDebug() << ""; - } - } - - - // QString d = today.day() > 9 ? QString::number(today.day()) : "0" + QString::number(today.day()); - // QString m = today.month() > 9 ? QString::number(today.month()) : "0" + QString::number(today.month()); - // QString y = QString::number(today.year()); - // QString w = getDayOfWeekAsString(today.dayOfWeek()); - // QString mm = getMonthAsString(today.month()); - // qDebug() << "Date:" << d << m << y << w << mm; - - - // QString d2 = after2Days.day() > 9 ? QString::number(after2Days.day()) : "0" + QString::number(after2Days.day()); - // QString m2 = after2Days.month() > 9 - // ? QString::number(after2Days.month()) - // : "0" + QString::number(after2Days.month()); - // QString y2 = QString::number(after2Days.year()); - // QString w2 = getDayOfWeekAsString(after2Days.dayOfWeek()); - // QString mm2 = getMonthAsString(after2Days.month()); - // qDebug() << "Date:" << d2 << m2 << y2 << w2 << mm2; -} - -TransactionInfo parseExpandedInfoHistory(const QDomElement &root) { - if (root.isElement()) { - QDomElement elem = root.toElement(); - if (elem.tagName() == "node" - && elem.attribute("class") == "android.widget.Button" - && elem.attribute("text") == "Детали операции" - ) { - TransactionInfo transaction; - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - for (int i = 0; i < detailInfo.count(); ++i) { - QDomNode info = detailInfo.at(i); - if (!info.isElement()) continue; - - switch (QDomElement e = info.toElement(); e.attribute("index").toInt()) { - case 2: - // Дата операции - transaction.bankTime = transformBankDateTime(findSubInfo(e, "1")); - break; - case 5: - transaction.description = findSubInfo(e, "1"); - parseTransferInfo(transaction.description, transaction); - break; - case 7: - // Комиссия - transaction.fee = findSubInfo(e, "1").remove(onlyDigitsRegex).toFloat(); - break; - default: - // qDebug() << "Прочее:" << findSubInfo(e); - break; - } - } - return transaction; - } - } - - // Рекурсивно обходим всех детей - QDomNode child = root.firstChild(); - while (!child.isNull()) { - if (TransactionInfo info = parseExpandedInfoHistory(child.toElement()); !info.bankTime.isEmpty()) { - return info; - } - child = child.nextSibling(); - } - - return {}; -} - -TransactionInfo ScreenXmlParser::parseTransactionInfoHistory(int screenWidth, int screenHeight) { - // 1. Распарсить описание транзакции - TransactionInfo transaction = parseExpandedInfoHistory(m_xml); - // 1. Найти статус: Исполнен | Операция исполнена - transaction.status = TransactionStatus::Wait; - Node status1 = findTextNode("Исполнен", 0, 0, screenWidth, screenHeight / 2); - Node status2 = findTextNode("Операция исполнена", 0, 0, screenWidth, screenHeight / 2); - if (!status1.isEmpty() || !status2.isEmpty()) { - transaction.status = TransactionStatus::Complete; - } - return transaction; -} - -TransactionInfo parseExpandedInfo(const QDomElement &root) { - if (root.isElement()) { - QDomElement elem = root.toElement(); - if (elem.tagName() == "node" - && elem.attribute("class") == "android.widget.Button" - && elem.attribute("text") == "Детали операции" - ) { - TransactionInfo transaction; - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - for (int i = 0; i < detailInfo.count(); ++i) { - QDomNode info = detailInfo.at(i); - if (!info.isElement()) continue; - - QDomElement infoElement = info.toElement(); - QString index = infoElement.attribute("index"); - QDomElement ee; - switch (index.toInt()) { - case 3: - transaction.bankTime = transformBankDateTime(findSubInfo(infoElement, "1")); - break; - case 4: - ee = findSubElement(infoElement, "0"); - ee = findSubElement(ee, "1"); - transaction.phone = findSubInfo(ee, "0").remove(onlyDigitsRegex); - break; - case 5: - transaction.name = findSubInfo(infoElement, "1"); - break; - case 6: - transaction.bankName = findSubInfo(infoElement, "1"); - break; - case 9: - transaction.fee = findSubInfo(infoElement, "1").remove(onlyDigitsAndDotRegex).toFloat(); - break; - case 10: - ee = findSubElement(infoElement, "1"); - transaction.bankTrExternalId = findSubInfo(ee, "1"); - break; - default: - break; - } - } - return transaction; - } - } - - // Рекурсивно обходим всех детей - QDomNode child = root.firstChild(); - while (!child.isNull()) { - if (TransactionInfo info = parseExpandedInfo(child.toElement()); !info.bankTime.isEmpty()) { - return info; - } - child = child.nextSibling(); - } - - return {}; -} - -TransactionInfo ScreenXmlParser::parseTransactionInfo(int screenWidth, int screenHeight) { - // 1. Распарсить описание транзакции - TransactionInfo transaction = parseExpandedInfo(m_xml); - // 1. Найти статус: Исполнен | Операция исполнена - transaction.status = TransactionStatus::Wait; - Node status1 = findTextNode("Исполнен", 0, 0, screenWidth, screenHeight / 2); - Node status2 = findTextNode("Операция исполнена", 0, 0, screenWidth, screenHeight / 2); - if (!status1.isEmpty() || !status2.isEmpty()) { - transaction.status = TransactionStatus::Complete; - } - return transaction; -} - - -QList > ScreenXmlParser::parseAccountsInfo() { - QList > accounts; - for (const Node &node: m_nodes) { - const QString text = node.text; - - // Проверяем только нужные строки - if (!text.contains("Номер карты с последними цифрами")) { - continue; - } - QRegularExpressionMatch match = reCardData.match(text); - if (!match.hasMatch()) { - continue; - } - - AccountInfo info; - info.lastNumbers = match.captured(1).remove(" "); - info.amount = match.captured(2).remove(" ").toFloat(); - info.description = text.mid(0, text.indexOf("Номер карты")); - info.updateTime = DateUtils::getUtcNow(); - - accounts.append(qMakePair(info, node)); - } - - return accounts; -} - -void ScreenXmlParser::test() { - // TransactionInfo info2 = parseExpandedInfo(m_xml); - // qDebug().noquote().nospace() << convertTransactionToString(info2); - // return; - - QFile file("assets/rshb/all_products.xml"); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qDebug() << "Cannot open file"; - } - - QDomDocument doc; - if (!doc.setContent(&file)) { - qDebug() << "Failed to parse XML"; - } - file.close(); - - qDebug() << "Start parsing XML:"; - - // Номер карты с последними цифрами - QDomElement root = doc.documentElement(); - parseAndSaveXml(doc.toString()); - // for (const AccountInfo &info: parseAccountsInfo()) { - // qDebug().noquote().nospace() << convertAccountToString(info); - // } - // TransactionInfo info = parseExpandedInfo(root); - // qDebug().noquote().nospace() << convertTransactionToString(info); - // ЕСПП -} - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/ScreenXmlParser.h b/android/rshb/ScreenXmlParser.h deleted file mode 100644 index 288a566..0000000 --- a/android/rshb/ScreenXmlParser.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include -#include -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" - -namespace Rshb { - -class ScreenXmlParser final : public QObject { - Q_OBJECT - -public: - explicit ScreenXmlParser(QObject *parent = nullptr); - - ~ScreenXmlParser() override; - - Node tryToFindBannerOrDialog(int width, int height); - - Node tryToFindSecurityBanner(); - - Node tryToFindSelectBottomSheet(); - - Node findTextNode(const QString &text); - - Node findTextNode(const QString &text, int x1, int y1, int x2, int y2); - - Node findFirstEditText(); - - Node findButtonNode(const QString &text, bool contains); - - bool isHomeScreen(); - - - QList tryToFindPinCode(const QString &pinCode, const QString &title); - - QList getLast2DaysTransactions(); - - void parseAndSaveXml(const QString &xml); - - TransactionInfo parseTransactionInfoHistory(int screenWidth, int screenHeight); - TransactionInfo parseTransactionInfo(int screenWidth, int screenHeight); - - QList parseTodayAndYesterdayReportHistory(); - - QList parseTodayAndYesterdayHistory(); - - - QList > parseAccountsInfo(); - - void test(); - -private: - QList m_nodes; - QDomElement m_xml; - - QList findPinCodeNodes(const QString &pinCode); -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/SyncAccountDataScript.cpp b/android/rshb/SyncAccountDataScript.cpp deleted file mode 100644 index d0078bc..0000000 --- a/android/rshb/SyncAccountDataScript.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "SyncAccountDataScript.h" - -namespace Rshb { - -SyncAccountDataScript::SyncAccountDataScript( - QString deviceId, - QObject *parent -) : CommonScript(parent), - m_deviceId(std::move(deviceId)) { -} - -SyncAccountDataScript::~SyncAccountDataScript() = default; - -void SyncAccountDataScript::doStart() { - - // 1. -} - -} // namespace Rshb \ No newline at end of file diff --git a/android/rshb/SyncAccountDataScript.h b/android/rshb/SyncAccountDataScript.h deleted file mode 100644 index a853c5f..0000000 --- a/android/rshb/SyncAccountDataScript.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "CommonScript.h" - -namespace Rshb { - -class SyncAccountDataScript final : public CommonScript { - Q_OBJECT - -public: - ~SyncAccountDataScript() override; - - explicit SyncAccountDataScript( - QString deviceId, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const QString m_deviceId; -}; - -} // namespace Rshb \ No newline at end of file diff --git a/android/toss/CommonTossScript.cpp b/android/toss/CommonTossScript.cpp deleted file mode 100644 index f911976..0000000 --- a/android/toss/CommonTossScript.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "CommonTossScript.h" - -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "net/NetworkService.h" -#include "time/DateUtils.h" - -CommonTossScript::CommonTossScript(QObject *parent) - : QObject(parent) { -} - -CommonTossScript::~CommonTossScript() = default; - -void CommonTossScript::start() { - doStart(); - emit finished(); -} - -Node CommonTossScript::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 CommonTossScript::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(2500); - 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)); - - // Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - // if (!closeBannerNode.isEmpty()) { - // AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - // QThread::msleep(1000); - // continue; - // } - - if (xmlScreenParser.isHomeScreen()) { - return true; - } - - // Проверяем, что это экран ввода пин-кода - if (!findAndInputPincode) { - if (const QList pincode = xmlScreenParser.findPinCodeNodes(pinCode); pincode.size() == 6) { - for (const Node &node: pincode) { - AdbUtils::makeTap(deviceId, node.x(), node.y()); - QThread::msleep(QRandomGenerator::global()->bounded(200, 901)); - } - // После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить - findAndInputPincode = true; - QThread::msleep(3000); - continue; - } - } - - QThread::msleep(1000); - } -} - -bool CommonTossScript::goToAllProducts( - const QString &deviceId, - const int width, - const int height -) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter)); - Node closeBannerNode = xmlScreenParser.findButtonNode("Maraqlı deyil", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(500); - } - - if (xmlScreenParser.isHomeScreen()) { - Node allMyAccountBtn = xmlScreenParser.findButtonNode("All My Accounts", false); - if (!allMyAccountBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, allMyAccountBtn.x(), allMyAccountBtn.y()); - QThread::msleep(500); - return true; - } - } - return false; -} - -void CommonTossScript::postAccountsData(const QList &accounts) { - if (accounts.isEmpty()) { - return; - } - - auto *thread = new QThread; - auto *worker = new NetworkService; - - QJsonArray list; - for (const AccountInfo &account: accounts) { - QJsonObject obj; - obj["appName"] = account.appCode; - obj["lastNumbers"] = account.lastNumbers; - obj["amount"] = account.amount; - obj["currency"] = account.currency; - obj["description"] = account.description; - obj["status"] = accountStatusToString(account.status); - - list.append(obj); - } - // FIXME доставить десктоп ид из настроеек - worker->setDeviceId(accounts.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 CommonTossScript::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 + " | " + transaction.bankTime; - obj["updatedDescription"] = transaction.updatedDescription; - obj["bankName"] = transaction.bankName; - obj["bankTime"] = DateUtils::toUtcString(transaction.completeTime); - 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 CommonTossScript::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 CommonTossScript::stop() { - m_running = false; -} diff --git a/android/toss/CommonTossScript.h b/android/toss/CommonTossScript.h deleted file mode 100644 index cd5b0bc..0000000 --- a/android/toss/CommonTossScript.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include "ScreenXmlTossParser.h" -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" - -class CommonTossScript : public QObject { - Q_OBJECT - -public: - explicit CommonTossScript(QObject *parent = nullptr); - - ~CommonTossScript() override; - -protected: - virtual void doStart() = 0; - - bool goToAllProducts( - const QString &deviceId, - int width, - int height - ); - - void postAccountsData(const QList &accounts); - - void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction); - - void updateTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); - - Node swipeToButton( - const QString &deviceId, - const QString &text, - int &counter, int width, int height - ); - - bool runAppAndGoToHomeScreen( - const QString &deviceId, - const QString &packageName, - const QString &pinCode, - int width, - int height - ); - - bool m_running = true; - ScreenXmlTossParser xmlScreenParser; - int m_counter = 0; - -signals: - void finished(); - - void finishedWithResult(const QString &result); - -public slots: - virtual void start() final; - - virtual void stop() final; - -private: - Q_DISABLE_COPY(CommonTossScript); -}; diff --git a/android/toss/GetLastDaysHistoryTossScript.cpp b/android/toss/GetLastDaysHistoryTossScript.cpp deleted file mode 100644 index 7499743..0000000 --- a/android/toss/GetLastDaysHistoryTossScript.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "GetLastDaysHistoryTossScript.h" - -#include -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/DeviceInfo.h" -#include "net/NetworkService.h" -#include "time/DateUtils.h" - -GetLastDaysHistoryTossScript::GetLastDaysHistoryTossScript( - AccountInfo account, - QObject *parent -) : CommonTossScript(parent), - m_account(std::move(account)) { -} - -GetLastDaysHistoryTossScript::~GetLastDaysHistoryTossScript() = default; - -bool GetLastDaysHistoryTossScript::saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status -) { - TransactionInfo tr; - tr.accountId = account.id; - tr.description = transaction.description; - tr.bankTime = transaction.bankTime; - tr.bankName = transaction.bankName; - tr.amount = transaction.amount; - tr.phone = transaction.phone; - tr.updateTime = DateUtils::getUtcNow(); - tr.fee = transaction.fee; - tr.name = transaction.name; - tr.bankTrExternalId = transaction.bankTrExternalId; - tr.status = status; - tr.type = TransactionType::Card; - tr.completeTime = transaction.completeTime; - tr.id = TransactionDAO::insertTransaction(tr); - - if (tr.id == -1) { - return false; - } - postNewTransactionData(account, tr); - return true; -} - -bool GetLastDaysHistoryTossScript::updateAndPostTransactionData( - const AccountInfo &account, - const int transactionId, - const TransactionStatus status, - const QString &oldDesc, - const QString &newDesc -) { - if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) { - updateTransactionData(account, transactionId, status, oldDesc, newDesc); - return true; - } - return false; -} - -void GetLastDaysHistoryTossScript::doStart() { - // берем все транзакции из БД - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - if (xmlScreenParser.isHomeScreen()) { - QList oldAccounts = AccountDAO::getAccounts(m_account.deviceId, m_account.appCode); - QMap accountMap; - for (AccountInfo &oldAccount: oldAccounts) { - if (oldAccount.status == AccountStatus::Active) { - accountMap.insert(oldAccount.lastNumbers, oldAccount); - } - } - - QList accounts = xmlScreenParser.parseAccountsInfo(); - if (accounts.length() == 1 && accountMap.size() == 1) { - AccountInfo account = accounts[0]; - account.deviceId = m_account.deviceId; - account.appCode = m_account.appCode; - account.lastNumbers = accountMap.first().lastNumbers; - account.description = accountMap.first().description; - account.currency = accountMap.first().currency; - account.description = accountMap.first().description; - account.status = AccountStatus::Active; - if (!AccountDAO::upsertAccount(account)) { - m_error = "Not updated: " + account.lastNumbers; - qCritical() << m_error; - } - } - - QList savedAccounts = AccountDAO::getAccounts(m_account.deviceId, m_account.appCode); - postAccountsData(savedAccounts); - - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node transferText = xmlScreenParser.findFirstAccountLine(); - if (!transferText.isEmpty()) { - AdbUtils::makeTap(device.id, transferText.x(), transferText.y()); - QThread::msleep(3000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - for (int i = 0; i < 5; ++i) { - Node myBankText = xmlScreenParser.findTextNode("My Toss Bank"); - Node accountText = xmlScreenParser.findTextNode("Account"); - - if (!myBankText.isEmpty() && !accountText.isEmpty()) { - QList transactions = xmlScreenParser.parsePositiveTransactions(); - QList savedTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); - for (TransactionInfo &transaction: transactions) { - bool isSaved = false; - for (TransactionInfo &savedTransaction: savedTransactions) { - if (savedTransaction.amount == transaction.amount - && savedTransaction.bankTime == transaction.bankTime) { - isSaved = true; - break; - } - } - if (!isSaved) { - if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Complete)) { - qDebug() << "--- saveAndPostNewTransactionData failed"; - } - } - } - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId , ++m_counter)); - } - } else { - m_error = "Card not found: " + m_account.deviceId + " " + m_account.lastNumbers; - qWarning() << m_error; - emit finishedWithResult(m_error); - return; - } - } - AdbUtils::goBack(m_account.deviceId); - emit finishedWithResult(m_error); -} diff --git a/android/toss/GetLastDaysHistoryTossScript.h b/android/toss/GetLastDaysHistoryTossScript.h deleted file mode 100644 index 99b8636..0000000 --- a/android/toss/GetLastDaysHistoryTossScript.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "CommonTossScript.h" - -class GetLastDaysHistoryTossScript final : public CommonTossScript { - Q_OBJECT - -public: - ~GetLastDaysHistoryTossScript() override; - - explicit GetLastDaysHistoryTossScript( - AccountInfo account, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - QString m_error; - - bool saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status - ); - - bool updateAndPostTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); -}; diff --git a/android/toss/LoginAndCheckAccountsTossScript.cpp b/android/toss/LoginAndCheckAccountsTossScript.cpp deleted file mode 100644 index ca7feac..0000000 --- a/android/toss/LoginAndCheckAccountsTossScript.cpp +++ /dev/null @@ -1,379 +0,0 @@ -#include "LoginAndCheckAccountsTossScript.h" - -#include -#include - -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "time/DateUtils.h" - -LoginAndCheckAccountsTossScript::LoginAndCheckAccountsTossScript( - QString deviceId, - QString appCode, - QObject *parent -) : CommonTossScript(parent), - m_deviceId(std::move(deviceId)), m_appCode(std::move(appCode)) { -} - -LoginAndCheckAccountsTossScript::~LoginAndCheckAccountsTossScript() = default; - -/** -NongHyup -KakaoBank -Kookmin -Toss Bank -Shinhan -Woori -IBK -Hana -MG -Busan Bank -iM Bank(Daegu) -Kbank -Credit Union -Postal Sav. -SC -Kyongnam -Kwangju -Suhyup -Jeonbuk -Savings Banks -Jeju Bank -Jeonbuk -Savings Banks -Jeju Bank -Citibank -KDB -NFCF -SBI Sav. -BOA -Bank of China -HSBC -ICBC -Deutsche -JPMorgan -BNP Paribas -CCB -*/ -void LoginAndCheckAccountsTossScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, m_appCode); - - - if (true) { - - if (true) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - auto accounts = xmlScreenParser.parseAccountsInfo(); - if (!accounts.empty()) { - qDebug() << "--- Finish, amount: " << QString::number(accounts[0].amount); - } - return; - } - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - // находим первую кнопку Transfer - Node transferText = xmlScreenParser.findButtonNode("Transfer", false); - if (!transferText.isEmpty()) { - AdbUtils::makeTap(m_deviceId, transferText.x(), transferText.y()); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node enterAmount = xmlScreenParser.findEditText("Enter account number"); - if (!enterAmount.isEmpty()) { - AdbUtils::makeTap(m_deviceId, enterAmount.x(), enterAmount.y()); - QThread::msleep(200); - - AdbUtils::inputText(m_deviceId, "51291082442507"); - QThread::msleep(200); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node inputBank = xmlScreenParser.findNodeByClassName("android.widget.Spinner"); - if (!inputBank.isEmpty()) { - AdbUtils::makeTap(m_deviceId, inputBank.x(), inputBank.y()); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - - bool foundBank = false; - for (int i = 0; i < 5; ++i) { - Node bank = xmlScreenParser.findTextNode("Hana"); - if (!bank.isEmpty()) { - AdbUtils::makeTap(m_deviceId, bank.x(), bank.y()); - QThread::msleep(200); - foundBank = true; - break; - } else { - AdbUtils::makeSwipe( - m_deviceId, - device.screenWidth / 2, device.screenHeight / 4 * 3, - device.screenWidth / 2, device.screenHeight / 2 - ); - QThread::msleep(500); - } - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - } - - if (!foundBank) { - m_error = "Bank not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node confirmTransfer = xmlScreenParser.findButtonNode("Confirm", false); - if (!confirmTransfer.isEmpty()) { - AdbUtils::makeTap(m_deviceId, confirmTransfer.x(), confirmTransfer.y()); - QThread::msleep(3000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - - bool enterAmountFound = false; - for (int i = 0; i < 10; ++i) { - Node enterAmountText = xmlScreenParser.findTextNode("Enter amount"); - if (!enterAmountText.isEmpty()) { - // AdbUtils::makeTap(m_deviceId, enterAmountText.x(), enterAmountText.y()); - // QThread::msleep(500); - enterAmountFound = true; - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - } - - if (!enterAmountFound) { - m_error = "Enter amount not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node closeBtn = xmlScreenParser.findButtonNode("확인", false); - if (!closeBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, closeBtn.x(), closeBtn.y()); - QThread::msleep(200); - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - for (Node &number: xmlScreenParser.getNodeNumbers(4)) { - AdbUtils::makeTap(m_deviceId, number.x(), number.y()); - QThread::msleep(QRandomGenerator::global()->bounded(200, 901)); - } - - // AdbUtils::inputText(m_deviceId, "2"); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node nextBtn = xmlScreenParser.findButtonNode("Next", false); - if (!nextBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, nextBtn.x(), nextBtn.y()); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node sendBtn = xmlScreenParser.findButtonNode("Send", false); - if (!sendBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, sendBtn.x(), sendBtn.y()); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml( - AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - Node sendAgainBtn = xmlScreenParser.findButtonNode("Send", false); - if (!sendAgainBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, sendAgainBtn.x(), sendAgainBtn.y()); - QThread::msleep(1000); - } - - bool findAndInputPincode = false; - xmlScreenParser.parseAndSaveXml( - AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - if (const QList pincode = xmlScreenParser.findPinCodeNodes(app.pinCode); - pincode.size() == 6) { - for (const Node &node: pincode) { - AdbUtils::makeTap(m_deviceId, node.x(), node.y()); - QThread::msleep(QRandomGenerator::global()->bounded(200, 901)); - } - findAndInputPincode = true; - QThread::msleep(2000); - } - if (!findAndInputPincode) { - m_error = "Pincode not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml( - AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - bool foundConfirmBtn = false; - for (int i = 0; i < 5; ++i) { - Node confirmBtn = xmlScreenParser.findButtonNode("Confirm", false); - if (!confirmBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, confirmBtn.x(), confirmBtn.y()); - foundConfirmBtn = true; - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - } - - if (!foundConfirmBtn) { - m_error = "Last Confirm button not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - for (int i = 0; i < 10; ++i) { - Node lastBtn = xmlScreenParser.findTextNode("닫기"); - Node lastBtn2 = xmlScreenParser.findTextNode("Close"); - if (!lastBtn.isEmpty()) { - AdbUtils::makeTap(m_deviceId, lastBtn.x(), lastBtn.y()); - break; - } - if (!lastBtn2.isEmpty()) { - AdbUtils::makeTap(m_deviceId, lastBtn2.x(), lastBtn2.y()); - break; - } - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - } - - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - qDebug() << "--- Finish: " << xmlScreenParser.parseAccountAmount(); - } - } else { - m_error = "Next button not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Confirm transfer button not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Input bank not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Enter account number not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - m_error = "Transfer button not found: " + m_deviceId + " " + m_appCode; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } else { - // "Maraqlı deyil" - не интересно - 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 (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Cant open the home screen: " + m_deviceId + " " + m_appCode; - qWarning() << m_error; - if (!ApplicationDAO::updatePinCodeStatus(app.id, "no", "Не прошла проверка [дата]")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finishedWithResult(m_error); - return; - } else { - if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - Node mainAccount = xmlScreenParser.findTextContainsNode("Main account"); - if (!mainAccount.isEmpty()) { - // AdbUtils::makeTap(device.id, mainAccount.x(), mainAccount.y()); - // QThread::msleep(1000); - // for (int i = 0; i < 5; ++i) { - // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - // Node mainAccount = xmlScreenParser.findTextContainsNode("Main account"); - // if (!byDayTitle.isEmpty()) { - // qWarning() << "--- beforeYesterdayTitle found!"; - // break; - // } - // AdbUtils::makeSwipe( - // m_account.deviceId, - // device.screenWidth / 2, device.screenHeight / 4 * 3, - // device.screenWidth / 2, device.screenHeight / 4 - // ); - // QThread::msleep(1000); - // - // } - } - - QThread::msleep(2000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - - QList oldAccounts = AccountDAO::getAccounts(m_deviceId, m_appCode); - QMap accountMap; - for (AccountInfo &oldAccount: oldAccounts) { - if (oldAccount.status == AccountStatus::Active) { - accountMap.insert(oldAccount.lastNumbers, oldAccount); - } - } - - QList accounts = xmlScreenParser.parseAccountsInfo(); - if (accounts.length() == 1 && accountMap.size() == 1) { - AccountInfo account = accounts[0]; - account.deviceId = m_deviceId; - account.appCode = m_appCode; - account.lastNumbers = accountMap.first().lastNumbers; - account.description = accountMap.first().description; - account.currency = accountMap.first().currency; - account.description = accountMap.first().description; - account.status = AccountStatus::Active; - if (!AccountDAO::upsertAccount(account)) { - m_error = "Not updated: " + account.lastNumbers; - qCritical() << m_error; - } - } - // for (auto account: accounts) { - // if (accountMap.contains(account.lastNumbers)) { - // account.deviceId = m_deviceId; - // account.appCode = m_appCode; - // account.currency = accountMap[account.lastNumbers].currency; - // account.description = accountMap[account.lastNumbers].description; - // account.status = AccountStatus::Active; - // if (!AccountDAO::upsertAccount(account)) { - // m_error = "Not updated: " + account.lastNumbers; - // qCritical() << m_error; - // } - // } - // } - - QList savedAccounts = AccountDAO::getAccounts(m_deviceId, m_appCode); - postAccountsData(savedAccounts); - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - } - emit finishedWithResult(m_error); -} diff --git a/android/toss/LoginAndCheckAccountsTossScript.h b/android/toss/LoginAndCheckAccountsTossScript.h deleted file mode 100644 index 7ff1922..0000000 --- a/android/toss/LoginAndCheckAccountsTossScript.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "CommonTossScript.h" - -class LoginAndCheckAccountsTossScript final : public CommonTossScript { - Q_OBJECT - -public: - ~LoginAndCheckAccountsTossScript() override; - - explicit LoginAndCheckAccountsTossScript( - QString deviceId, - QString appCode, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const QString m_deviceId; - const QString m_appCode; - QString m_error; -}; diff --git a/android/toss/PayByCardTossScript.cpp b/android/toss/PayByCardTossScript.cpp deleted file mode 100644 index 4fc08ab..0000000 --- a/android/toss/PayByCardTossScript.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include "PayByCardTossScript.h" - -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/ApplicationInfo.h" -#include "db/DeviceInfo.h" -#include "time/DateUtils.h" - -PayByCardTossScript::PayByCardTossScript( - AccountInfo account, - QString iban, - QString fullName, - const double amount, - QObject *parent -) : CommonTossScript(parent), - m_account(std::move(account)), - m_fullName(std::move(fullName)), m_amount(amount) { -} - -PayByCardTossScript::~PayByCardTossScript() = default; - - -void PayByCardTossScript::makePayment(const DeviceInfo &device) { - // Оплата со страницы карты - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node editIbanEditText = xmlScreenParser.findEditTextByResourceId("com.Toss.Tossmobil:id/edit_iban"); - Node fullNameEditText = xmlScreenParser.findEditTextByResourceId("com.Toss.Tossmobil:id/edit_full_name"); - if (!editIbanEditText.isEmpty() && !fullNameEditText.isEmpty()) { - AdbUtils::makeTap(device.id, editIbanEditText.x(), editIbanEditText.y()); - QThread::msleep(500); - // AdbUtils::inputText(device.id, m_iban); - - AdbUtils::makeTap(device.id, editIbanEditText.x(), editIbanEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, m_fullName); - - Node nextBtn = xmlScreenParser.findButtonNode("NEXT", false); - AdbUtils::makeTap(device.id, nextBtn.x(), nextBtn.y()); - - bool findSenderAccount = false; - for (int i = 0; i < 5; ++i) { - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node senderAccount = xmlScreenParser.findTextNode("Sender Account"); - if (!senderAccount.isEmpty()) { - // Node inputBankName = xmlScreenParser.findAccountButton(m_account.lastNumbers); - // if (!inputBankName.isEmpty()) { - // AdbUtils::makeTap(device.id, inputBankName.x(), inputBankName.y()); - // QThread::msleep(500); - - - // // FXIME скорее всего придется нажать кнопку NEXT - // findSenderAccount = true; - // } - break; - } - } - if (!findSenderAccount) { - m_error = "Не нашли Sender Account"; - qDebug() << m_error; - return; - } - - bool findAmountDetermination = false; - for (int i = 0; i < 5; ++i) { - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node amountDetermination = xmlScreenParser.findTextNode("Amount Determination"); - if (!amountDetermination.isEmpty()) { - Node integerEditText = xmlScreenParser.findEditTextByResourceId( - "com.Toss.Tossmobil:id/edit_text_integer"); - Node decimalEditText = xmlScreenParser.findEditTextByResourceId( - "com.Toss.Tossmobil:id/edit_text_decimal"); - if (!integerEditText.isEmpty() && !decimalEditText.isEmpty()) { - int integerPart = static_cast(m_amount); - AdbUtils::makeTap(device.id, integerEditText.x(), integerEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, QString::number(integerPart)); - - int decimalPart = static_cast((m_amount - integerPart) * 100); - AdbUtils::makeTap(device.id, decimalEditText.x(), decimalEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, QString::number(decimalPart)); - - findAmountDetermination = true; - break; - } else { - m_error = "Не нашли поля ввода для ввода суммы перевода"; - qDebug() << m_error; - return; - } - } - } - if (!findAmountDetermination) { - m_error = "Не нашли Amount Determination"; - qDebug() << m_error; - return; - } - - bool findDetailDefinition = false; - for (int i = 0; i < 5; ++i) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node fastTransferBtn = xmlScreenParser.findButtonNode("FAST TRANSFER", false); - if (!fastTransferBtn.isEmpty()) { - AdbUtils::makeTap(device.id, fastTransferBtn.x(), fastTransferBtn.y()); - QThread::msleep(500); - continue; - } - - Node detailDefinitionTitle = xmlScreenParser.findTextNode("Detail Definition"); - Node pleaseChooseBtn = xmlScreenParser.findButtonNode("Please choose", false); - if (!detailDefinitionTitle.isEmpty() && !pleaseChooseBtn.isEmpty()) { - AdbUtils::makeTap(device.id, pleaseChooseBtn.x(), pleaseChooseBtn.y()); - QThread::msleep(500); - - Node searchEditText = xmlScreenParser. - findEditTextByResourceId("com.Toss.Tossmobil:id/input_search"); - if (!searchEditText.isEmpty()) { - AdbUtils::makeTap(device.id, searchEditText.x(), searchEditText.y()); - QThread::msleep(500); - // FIXME "Individual Payment" или "Other Payments" если нет одного, то надо другой - AdbUtils::inputText(device.id, "Other Payments"); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node otherPaymentsText = xmlScreenParser.findTextNode("Other Payments"); - if (!otherPaymentsText.isEmpty()) { - AdbUtils::makeTap(device.id, otherPaymentsText.x(), otherPaymentsText.y()); - QThread::msleep(500); - findDetailDefinition = true; - break; - } - } else { - for (int i = 0; i < 5; ++i) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node otherPaymentsText = xmlScreenParser.findTextNode("Other Payments"); - if (otherPaymentsText.isEmpty()) { - AdbUtils::makeSwipe( - m_account.deviceId, - device.screenWidth / 2, device.screenHeight / 4 * 3, - device.screenWidth / 2, device.screenHeight / 4 - ); - } else { - AdbUtils::makeTap(device.id, otherPaymentsText.x(), otherPaymentsText.y()); - QThread::msleep(500); - findDetailDefinition = true; - break; - } - QThread::msleep(500); - } - } - } - } - - if (!findDetailDefinition) { - m_error = "Не нашли Detail Definition"; - qDebug() << m_error; - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node nextBtn4 = xmlScreenParser.findButtonNode("NEXT", false); - AdbUtils::makeTap(device.id, nextBtn4.x(), nextBtn4.y()); - QThread::msleep(500); - - Node checkInfo = xmlScreenParser.findTextNode( - "Check the details of the transaction you shall realize and approve your transaction."); - if (!checkInfo.isEmpty()) { - for (int i = 0; i < 5; ++i) { - Node confirmBtn = xmlScreenParser.findButtonNode("CONFIRM", false); - if (confirmBtn.isEmpty()) { - AdbUtils::makeSwipe( - m_account.deviceId, - device.screenWidth / 2, device.screenHeight / 4 * 3, - device.screenWidth / 2, device.screenHeight / 4 - ); - } else { - AdbUtils::makeTap(device.id, confirmBtn.x(), confirmBtn.y()); - QThread::msleep(500); - break; - } - - QThread::msleep(500); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - } - - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node successText = xmlScreenParser.findTextNode("Your transaction is successfully realized."); - if (!successText.isEmpty()) { - Node receiptBtn = xmlScreenParser.findButtonNode("RECEIPT", false); - if (!receiptBtn.isEmpty()) { - AdbUtils::makeTap(device.id, receiptBtn.x(), receiptBtn.y()); - QThread::msleep(500); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node sendBtn = xmlScreenParser.findButtonNode("SEND E-MAIL", false); - if (!sendBtn.isEmpty()) { - AdbUtils::takeXmlDump( - device.id, "pay_by_iban_finish_transactions_" + QString::number(m_account.id) - ); - // Успешная транзакция - // TODO считываем все данные по переводу - TransactionInfo transaction; - transaction.name = m_fullName; - transaction.accountId = m_account.id; - transaction.amount = -m_amount; - transaction.fee = 0; - transaction.status = TransactionStatus::Complete; - transaction.type = TransactionType::Iban; - transaction.updateTime = DateUtils::getUtcNow(); - transaction.completeTime = DateUtils::getUtcNow(); - transaction.timestamp = DateUtils::getUtcNow(); - transaction.id = TransactionDAO::insertTransaction(transaction); - if (transaction.id == -1) { - // FIXME alert!! - qDebug() << "Transaction has not inserted..."; - } - - postNewTransactionData(m_account, transaction); - - Node backBtn = xmlScreenParser.findButtonNode("BACK", false); - if (!backBtn.isEmpty()) { - AdbUtils::makeTap(device.id, backBtn.x(), backBtn.y()); - QThread::msleep(500); - - Node returnBtn = xmlScreenParser.findButtonNode("RETURN TO HOME PAGE", false); - if (!returnBtn.isEmpty()) { - AdbUtils::makeTap(device.id, returnBtn.x(), returnBtn.y()); - } - } - } else { - //FIXME не успешная транзакция - AdbUtils::takeXmlDump( - device.id, "pay_by_iban_fail_transactions_receipt_" + QString::number(m_account.id) - ); - m_error = "Транзакция не успешна"; - qDebug() << m_error; - return; - } - } - } else { - m_error = "Транзакция не успешна"; - qDebug() << m_error; - return; - } - } else { - m_error = "Не нашли Check the details title info"; - qDebug() << m_error; - return; - } - } else { - m_error = "Не нашли поля ввода для IBAN"; - qDebug() << m_error; - return; - } -} - -void PayByCardTossScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - if (device.id.isEmpty() || app.id == -1) { - m_error = "Device or app not found."; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node allTransactionsNav = xmlScreenParser.findTextNode("Transactions"); - if (!allTransactionsNav.isEmpty()) { - AdbUtils::makeTap(device.id, allTransactionsNav.x(), allTransactionsNav.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - // перешли на страницу транзакций - Node allTransactionsTitle = xmlScreenParser.findTextNode( - "Transactions", 0, 0, device.screenWidth, device.screenHeight / 2 - ); - Node moneyTransfersBtn = xmlScreenParser.findTextNode("Money Transfers"); - if (!allTransactionsTitle.isEmpty() && !moneyTransfersBtn.isEmpty()) { - AdbUtils::makeTap(device.id, moneyTransfersBtn.x(), moneyTransfersBtn.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - // перешли на страницу переводов - Node moneyTransfersTitle = xmlScreenParser.findTextNode( - "Money Transfers", 0, 0, device.screenWidth, device.screenHeight / 2 - ); - Node transferToOtherAccountBtn = xmlScreenParser.findTextNode("Transfer to Other Account"); - if (!moneyTransfersTitle.isEmpty() && !transferToOtherAccountBtn.isEmpty()) { - AdbUtils::makeTap(device.id, transferToOtherAccountBtn.x(), transferToOtherAccountBtn.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - Node transferToOtherAccountTitle = xmlScreenParser.findTextNode( - "Transfer to Other Account", 0, 0, device.screenWidth, device.screenHeight / 2 - ); - Node aNewRecipientBtn = xmlScreenParser.findTextNode("A New Recipient"); - if (!transferToOtherAccountTitle.isEmpty() && !aNewRecipientBtn.isEmpty()) { - AdbUtils::makeTap(device.id, aNewRecipientBtn.x(), aNewRecipientBtn.y()); - QThread::msleep(1000); - - makePayment(device); - } else { - m_error = "Не нашли кнопку 'A New Recipient'"; - qDebug() << m_error; - } - } else { - m_error = "Не нашли кнопку 'Transfer to Other Account'"; - qDebug() << m_error; - } - } else { - m_error = "Не нашли кнопку 'Money Transfers'"; - qDebug() << m_error; - } - } else { - m_error = "Не нашли пункт меню 'Transactions'"; - qDebug() << m_error; - } - - if (!m_error.isEmpty()) { - AdbUtils::takeXmlDump(device.id, "pay_by_iban_fail_transactions_" + QString::number(m_account.id)); - } - - emit finishedWithResult(m_error); -} diff --git a/android/toss/PayByCardTossScript.h b/android/toss/PayByCardTossScript.h deleted file mode 100644 index 1c13a71..0000000 --- a/android/toss/PayByCardTossScript.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "CommonTossScript.h" -#include "db/DeviceInfo.h" - -class PayByCardTossScript final : public CommonTossScript { - Q_OBJECT - -public: - ~PayByCardTossScript() override; - - PayByCardTossScript( - AccountInfo account, - QString CardToss, - QString fullName, - double amount, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - const QString m_CardToss; - const QString m_fullName; - const double m_amount; - QString m_error; - - void makePayment(const DeviceInfo &device); -}; diff --git a/android/toss/ScreenXmlTossParser.cpp b/android/toss/ScreenXmlTossParser.cpp deleted file mode 100644 index d0d5e4a..0000000 --- a/android/toss/ScreenXmlTossParser.cpp +++ /dev/null @@ -1,537 +0,0 @@ -#include "ScreenXmlTossParser.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" -#include "time/DateUtils.h" - -ScreenXmlTossParser::ScreenXmlTossParser(QObject *parent) : QObject(parent) { -} - -ScreenXmlTossParser::~ScreenXmlTossParser() = default; - -static const QRegularExpression boundsRegex(R"(\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\])"); - -Node ScreenXmlTossParser::findTextNode(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlTossParser::findTextContainsNode(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.contains(text)) { - return node; - } - } - return {}; -} - -Node ScreenXmlTossParser::findTextNode(const QString &text, const int x1, const int y1, const int x2, const int y2) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text - && node.x() >= x1 && node.x() <= x2 && node.y() >= y1 && node.y() <= y2) { - return node; - } - } - return {}; -} - -Node ScreenXmlTossParser::findEditText(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.text == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlTossParser::findEditTextByResourceId(const QString &resourceId) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.resourceId == resourceId) { - return node; - } - } - return {}; -} - - -QList ScreenXmlTossParser::getNodeNumbers(double value) { - // Формат 'f' с precision = 0 — дробная часть отбрасывается - QString str = QString::number(value, 'f', 0); - // Разбиваем на отдельные символы - QList nodes; - for (QChar ch: str) { - QString num = QString(ch); - for (const Node &node: m_nodes) { - if (node.contentDesc == num && node.className == "android.widget.Button") { - nodes.append(node); - } - } - } - - return nodes; -} - -Node ScreenXmlTossParser::findNodeByClassName(const QString &className) { - for (const Node &node: m_nodes) { - if (node.className == className) { - return node; - } - } - return {}; -} - -Node ScreenXmlTossParser::findButtonNode(const QString &text, const bool contains) { - for (const Node &node: m_nodes) { - if (contains) { - if (node.className == "android.widget.Button" && node.text.contains(text)) { - return node; - } - } else { - if (node.className == "android.widget.Button" && node.text.trimmed() == text) { - return node; - } - } - } - return {}; -} - -bool ScreenXmlTossParser::isHomeScreen() { - Node activitiesBtn; - for (const Node &node: m_nodes) { - if (node.text.contains("Transfer") && node.resourceId == "viva.republica.toss:id/rightButton") { - activitiesBtn = node; - } - } - return !activitiesBtn.isEmpty(); -} - -QList ScreenXmlTossParser::findPinCodeNodes(const QString &pinCode) { - QMap buttons; - const QSet numberSet = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - - for (const Node &node: m_nodes) { - const QString &text = node.text.trimmed(); - if (node.className == "android.widget.Button" && numberSet.contains(text)) { - buttons[text] = node; - } - } - - QList nodes; - for (int i = 0; i < pinCode.length(); ++i) { - nodes.append(buttons[QString(pinCode.at(i))]); - } - return nodes; -} - -Node convertTossDomElementToNode(const QDomElement &element) { - auto node = Node(); - if (element.hasAttribute("bounds")) { - const QString bounds = element.attribute("bounds"); - if (const QRegularExpressionMatch match = boundsRegex.match(bounds); match.hasMatch()) { - node.setBounds( - match.captured(1).toInt(), - match.captured(2).toInt(), - match.captured(3).toInt(), - match.captured(4).toInt() - ); - } - } - if (element.hasAttribute("text")) { - node.text = element.attribute("text"); - } - if (element.hasAttribute("content-desc")) { - node.contentDesc = element.attribute("content-desc"); - } - if (element.hasAttribute("class")) { - node.className = element.attribute("class"); - } - if (element.hasAttribute("clickable")) { - node.clickable = element.attribute("clickable") == "true"; - } - if (element.hasAttribute("focusable")) { - node.focusable = element.attribute("focusable") == "true"; - } - if (element.hasAttribute("resource-id")) { - node.resourceId = element.attribute("resource-id"); - } - - return node; -} - -void ScreenXmlTossParser::parseAndSaveXml(const QString &xml) { - QDomDocument doc; - doc.setContent(xml); - m_xml = doc.documentElement(); - m_nodes.clear(); - - QXmlStreamReader reader(xml); - while (!reader.atEnd()) { - reader.readNext(); - if (reader.isStartElement() && reader.name() == "node") { - Node node = Node(); - if (reader.attributes().hasAttribute("bounds")) { - QString bounds = reader.attributes().value("bounds").toString(); - if (QRegularExpressionMatch match = boundsRegex.match(bounds); match.hasMatch()) { - node.setBounds( - match.captured(1).toInt(), - match.captured(2).toInt(), - match.captured(3).toInt(), - match.captured(4).toInt() - ); - } - } - if (reader.attributes().hasAttribute("text")) { - node.text = reader.attributes().value("text").toString(); - } - - if (reader.attributes().hasAttribute("content-desc")) { - node.contentDesc = reader.attributes().value("content-desc").toString(); - } - - if (reader.attributes().hasAttribute("class")) { - node.className = reader.attributes().value("class").toString(); - } - - if (reader.attributes().hasAttribute("clickable")) { - node.clickable = reader.attributes().value("clickable").toString() == "true"; - } - if (reader.attributes().hasAttribute("focusable")) { - node.focusable = reader.attributes().value("focusable").toString() == "true"; - } - if (reader.attributes().hasAttribute("resource-id")) { - node.resourceId = reader.attributes().value("resource-id").toString(); - } - - m_nodes.append(node); - } - } -} - -QDomElement findSubElementToss(const QDomElement &elem, const QString &index) { - if (elem.hasChildNodes()) { - QDomNodeList subInfoList = elem.childNodes(); - for (QDomNode subInfoNode: subInfoList) { - if (!subInfoNode.isElement()) continue; - QDomElement subInfo = subInfoNode.toElement(); - if (subInfo.attribute("index") == index) { - return subInfo; - } - } - } - return {}; -} - -QString findSubInfoToss(const QDomElement &elem, const QString &index) { - QDomElement subInfo = findSubElementToss(elem, index); - if (!subInfo.isNull()) { - return subInfo.attribute("text"); - } - return ""; -} - -QDomNode findNodeToss(const QDomNode &node, const QString &text) { - if (node.isElement()) { - QString nodeText = node.toElement().attribute("text"); - if (nodeText.toLower().startsWith(text)) { - return node; - } - } - - QDomNode child = node.firstChild(); - while (!child.isNull()) { - QDomNode result = findNodeToss(child, text); - if (!result.isNull()) { - return result; - } - child = child.nextSibling(); - } - return {}; -} - -QList findElementsWithPaymentsToss(const QDomElement &parent, const QString &attribute, - const QString &value) { - QList result; - - QDomElement element = parent.firstChildElement(); - while (!element.isNull()) { - // Проверка самого element - // if (element.hasAttribute(attribute) && element.attribute(attribute) == value) { - // result.append(element); - // } - - // Проверка его дочерних элементов - QDomElement child = element.firstChildElement(); - while (!child.isNull()) { - if (child.hasAttribute(attribute) && child.attribute(attribute) == value) { - result.append(child); - } - child = child.nextSiblingElement(); - } - - // Рекурсивный обход - result.append(findElementsWithPaymentsToss(element, attribute, value)); - - element = element.nextSiblingElement(); - } - - return result; -} - -QDomElement findElementWithPaymentsToss(const QDomElement &parent, const QString &attribute, const QString &value) { - QDomElement element = parent.firstChildElement(); - while (!element.isNull()) { - QDomElement child = element.firstChildElement(); - while (!child.isNull()) { - if (child.hasAttribute(attribute) && child.attribute(attribute) == value) { - return child; - } - child = child.nextSiblingElement(); - } - - QDomElement found = findElementWithPaymentsToss(element, attribute, value); - if (!found.isNull()) { - return found; - } - element = element.nextSiblingElement(); - } - return {}; -} - -QDateTime getUtcFromSeoulDate(const QString &date, const QString &time) { - int year = QDate::currentDate().year(); // или укажи вручную нужный год - QString withYear = QString("%1년 %2 %3").arg(year).arg(date).arg(time); // "2025년 7월 28일 17:54" - - QLocale korean(QLocale::Korean, QLocale::SouthKorea); - QString format = "yyyy년 M월 d일 H:mm"; - - QDateTime dt = korean.toDateTime(withYear, format); - if (dt.isValid()) { - dt.setTimeZone(QTimeZone("Asia/Seoul")); - return dt.toTimeZone(QTimeZone::utc()); - } else { - qWarning() << "Ошибка разбора даты:" << withYear; - return {}; - } -} - -Node ScreenXmlTossParser::findFirstAccountLine() { - Node node; - auto elements = findElementsWithPaymentsToss(m_xml, "text", "Transfer"); - if (elements.length() == 2) { - for (QDomElement &element: elements) { - qDebug() << "Found account: " << element.attribute("content-desc"); - } - } - if (!elements.isEmpty()) { - node = convertTossDomElementToNode(elements[0].parentNode().toElement()); - } - return node; -} - - -QList ScreenXmlTossParser::parsePositiveTransactions() { - QList transactions; - - QDomElement child = findElementWithPaymentsToss(m_xml, "class", "android.widget.ListView"); - QDomNodeList listView = child.childNodes(); - qDebug() << listView.count(); - if (!listView.isEmpty()) { - QString date = ""; - - for (QDomNode node: listView) { - QString title = node.toElement().attribute("text"); - if (title == "전체" || title.startsWith("AD") || title.endsWith("AD")) { - continue; - } - if (!title.isEmpty()) { - date = node.toElement().attribute("text"); - } - - if (date.isEmpty()) { - continue; - } - - const QDomNodeList trBoxGroup = node.childNodes(); - if (trBoxGroup.count() > 0) { - TransactionInfo transaction; - for (QDomNode item: trBoxGroup) { - QDomElement groupItem = item.toElement(); - QString line = groupItem.attribute("text"); - if (line.contains("잔액") && line.contains("입금")) { - QString beforeBalance = line.split("잔액").first().trimmed(); - - QString time = beforeBalance.split("입금").first().trimmed().last(5); - QString desc = beforeBalance.split("입금").first().replace(time, "").trimmed(); - QString amount = beforeBalance.split("입금").last().replace("원", "").replace(",", "").trimmed(); - - transaction.amount = amount.toDouble(); - transaction.description = desc; - transaction.bankTime = date + " " + time; - transaction.completeTime = getUtcFromSeoulDate(date, time); - } - } - if (transaction.amount > 0) { - transactions.append(transaction); - } - } - } - } else { - qDebug() << "ListView is empty"; - } - return transactions; -} - -float extractKoreanWonFromText(const QString &input) { - QRegularExpression re(R"(₩([\d,]+))"); - QRegularExpressionMatch match = re.match(input); - if (!match.hasMatch()) { - qWarning() << "Не удалось найти сумму в строке:" << input; - return 0.0f; - } - - QString numberStr = match.captured(1); - numberStr.remove(','); // удаляем запятые - - return numberStr.toFloat(); -} - -double parseKoreanNumber(const QString &input) { - // Карта корейских единиц в числовые множители - static const QMap unitMap = { - {QStringLiteral("조"), 1e12}, - {QStringLiteral("억"), 1e8}, - {QStringLiteral("만"), 1e4} - }; - // Статические регулярки (Clazy: don't create temporaries) - static const QRegularExpression cleanRe(QStringLiteral("[^0-9.,만억조]")); - static const QRegularExpression segmentRe(QStringLiteral(R"(([\d,]+(?:\.\d+)?)(조|억|만))")); - - QString s = input; - s.remove(cleanRe); - - double total = 0.0; - auto it = segmentRe.globalMatch(s); - qsizetype pos = 0; - - while (it.hasNext()) { - auto match = it.next(); - QString numStr = match.captured(1); - QString unitStr = match.captured(2); - - numStr.remove(','); - bool ok = false; - const double num = numStr.toDouble(&ok); - if (ok) - total += num * unitMap.value(unitStr); - - pos = match.capturedEnd(); - } - - // Обработка остатка (обычное число, возможно с дробной частью) - QString rem = s.mid(static_cast(pos)); - rem.remove(','); - if (!rem.isEmpty()) { - bool ok = false; - const double num = rem.toDouble(&ok); - if (ok) total += num; - } - - - return total; -} - -double ScreenXmlTossParser::parseAccountAmount() { - const QDomNodeList nodes = m_xml.elementsByTagName("node"); - - for (int i = 0; i < nodes.count(); ++i) { - const QDomElement el = nodes.at(i).toElement(); - - // Аккаунтов может быть несколько, вот берем первый по списку - if (el.attribute("text") == "Top up" && el.attribute("class") == "android.widget.Button") { - for (QDomNode node: el.parentNode().childNodes()) { - const QDomElement child = node.toElement(); - if (child.attribute("text").contains("만") || child.attribute("text").contains("원")) { - return parseKoreanNumber(child.attribute("text")); - } - } - } - } - return 0; -} - -QList ScreenXmlTossParser::parseAccountsInfo() { - const QDomNodeList nodes = m_xml.elementsByTagName("node"); - QList accounts; - - for (int i = 0; i < nodes.count(); ++i) { - const QDomElement el = nodes.at(i).toElement(); - - - // Аккаунтов может быть несколько, вот берем первый по списку - if (el.attribute("text") == "Transfer") { - for (QDomNode node: el.parentNode().parentNode().parentNode().childNodes()) { - const QDomElement child = node.toElement(); - if (child.attribute("content-desc").contains("₩")) { - AccountInfo info; - info.amount = extractKoreanWonFromText(child.attribute("content-desc")); - info.updateTime = DateUtils::getUtcNow(); - accounts.append(info); - return accounts; - } - } - } - - // if (el.attribute("content-desc").contains("토스뱅크 통장") && el.attribute("content-desc").contains("₩")) { - // AccountInfo info; - // info.amount = extractKoreanWonFromText(el.attribute("content-desc")); - // info.updateTime = DateUtils::getUtcNow(); - // accounts.append(info); - // break; - // } - } - return accounts; -} - -void ScreenXmlTossParser::test() { - // TransactionInfo info2 = parseExpandedInfo(m_xml); - // qDebug().noquote().nospace() << convertTransactionToString(info2); - // return; - - QFile file("assets/rshb/all_products.xml"); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qDebug() << "Cannot open file"; - } - - QDomDocument doc; - if (!doc.setContent(&file)) { - qDebug() << "Failed to parse XML"; - } - file.close(); - - qDebug() << "Start parsing XML:"; - - // Номер карты с последними цифрами - QDomElement root = doc.documentElement(); - parseAndSaveXml(doc.toString()); - // for (const AccountInfo &info: parseAccountsInfo()) { - // qDebug().noquote().nospace() << convertAccountToString(info); - // } - // TransactionInfo info = parseExpandedInfo(root); - // qDebug().noquote().nospace() << convertTransactionToString(info); - // ЕСПП -} diff --git a/android/toss/ScreenXmlTossParser.h b/android/toss/ScreenXmlTossParser.h deleted file mode 100644 index 9a97b7a..0000000 --- a/android/toss/ScreenXmlTossParser.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" - -class ScreenXmlTossParser final : public QObject { - Q_OBJECT - -public: - explicit ScreenXmlTossParser(QObject *parent = nullptr); - - ~ScreenXmlTossParser() override; - - Node findTextNode(const QString &text); - - Node findTextContainsNode(const QString &text); - - Node findFirstAccountLine(); - - Node findTextNode(const QString &text, int x1, int y1, int x2, int y2); - - Node findEditText(const QString &text); - - Node findEditTextByResourceId(const QString &resourceId); - - Node findNodeByClassName(const QString &className); - - Node findButtonNode(const QString &text, bool contains); - - bool isHomeScreen(); - - void parseAndSaveXml(const QString &xml); - - QList parsePositiveTransactions(); - - QList parseAccountsInfo(); - - double parseAccountAmount(); - - QList findPinCodeNodes(const QString &pinCode); - - QList getNodeNumbers(double value); - - void test(); - -private: - QList m_nodes; - QDomElement m_xml; -}; diff --git a/android/ziraat/CommonZiraatScript.cpp b/android/ziraat/CommonZiraatScript.cpp deleted file mode 100644 index 13807be..0000000 --- a/android/ziraat/CommonZiraatScript.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "CommonZiraatScript.h" - -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "net/NetworkService.h" - -CommonZiraatScript::CommonZiraatScript(QObject *parent) - : QObject(parent) { -} - -CommonZiraatScript::~CommonZiraatScript() = default; - -void CommonZiraatScript::start() { - doStart(); - emit finished(); -} - -Node CommonZiraatScript::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 CommonZiraatScript::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 findAndInputPassword = false; - - while (true) { - counter++; - - // Если более 10 раз мы пытаемся перейти на домашнюю - if (counter > 10) { - AdbUtils::takeScreenshot(deviceId, "too_many_attempts"); - return false; - } - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - - Node closeBannerNode = xmlScreenParser.findButtonNode("HAYIR", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - continue; - } - - if (xmlScreenParser.isHomeScreen()) { - return true; - } - - // Проверяем, что это экран ввода пин-кода - if (!findAndInputPassword) { - Node loginNode = xmlScreenParser.findButtonNode("Login", false); - if (!loginNode.isEmpty()) { - AdbUtils::makeTap(deviceId, loginNode.x(), loginNode.y()); - QThread::msleep(500); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); - - Node passwordEditText = xmlScreenParser.findEditText("Password"); - if (!passwordEditText.isEmpty()) { - AdbUtils::makeTap(deviceId, passwordEditText.x(), passwordEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(deviceId, pinCode); - - findAndInputPassword = true; - QThread::msleep(3000); - continue; - } - } - } - - QThread::msleep(1000); - } -} - -bool CommonZiraatScript::goToAllProducts( - const QString &deviceId, - const int width, - const int height -) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter)); - Node closeBannerNode = xmlScreenParser.findButtonNode("HAYIR", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(500); - } - - if (xmlScreenParser.isHomeScreen()) { - Node allMyAccountBtn = xmlScreenParser.findButtonNode("All My Accounts", false); - if (!allMyAccountBtn.isEmpty()) { - AdbUtils::makeTap(deviceId, allMyAccountBtn.x(), allMyAccountBtn.y()); - QThread::msleep(500); - return true; - } - } - return false; -} - -void CommonZiraatScript::postAccountsData(const QList &accounts) { - if (accounts.isEmpty()) { - return; - } - - auto *thread = new QThread; - auto *worker = new NetworkService; - - QJsonArray list; - for (const AccountInfo &account: accounts) { - QJsonObject obj; - obj["appName"] = account.appCode; - obj["lastNumbers"] = account.lastNumbers; - obj["amount"] = account.amount; - obj["currency"] = account.currency; - obj["description"] = account.description; - obj["status"] = accountStatusToString(account.status); - - list.append(obj); - } - // FIXME доставить десктоп ид из настроеек - worker->setDeviceId(accounts.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 CommonZiraatScript::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 CommonZiraatScript::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 CommonZiraatScript::stop() { - m_running = false; -} diff --git a/android/ziraat/CommonZiraatScript.h b/android/ziraat/CommonZiraatScript.h deleted file mode 100644 index 65d0c54..0000000 --- a/android/ziraat/CommonZiraatScript.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include "ScreenXmlZiraatParser.h" - -class CommonZiraatScript : public QObject { - Q_OBJECT - -public: - explicit CommonZiraatScript(QObject *parent = nullptr); - - ~CommonZiraatScript() override; - -protected: - virtual void doStart() = 0; - - bool goToAllProducts( - const QString &deviceId, - int width, - int height - ); - - void postAccountsData(const QList &accounts); - - void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction); - - void updateTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); - - Node swipeToButton( - const QString &deviceId, - const QString &text, - int &counter, int width, int height - ); - - bool runAppAndGoToHomeScreen( - const QString &deviceId, - const QString &packageName, - const QString &pinCode, - int width, - int height - ); - - bool m_running = true; - ScreenXmlZiraatParser xmlScreenParser; - int m_counter = 0; - -signals: - void finished(); - - void finishedWithResult(const QString &result); - -public slots: - virtual void start() final; - - virtual void stop() final; - -private: - Q_DISABLE_COPY(CommonZiraatScript); -}; diff --git a/android/ziraat/GetLastDaysHistoryZiraatScript.cpp b/android/ziraat/GetLastDaysHistoryZiraatScript.cpp deleted file mode 100644 index 7d87d5b..0000000 --- a/android/ziraat/GetLastDaysHistoryZiraatScript.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "GetLastDaysHistoryZiraatScript.h" - -#include -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/DeviceInfo.h" -#include "net/NetworkService.h" -#include "time/DateUtils.h" - -GetLastDaysHistoryZiraatScript::GetLastDaysHistoryZiraatScript( - AccountInfo account, - QObject *parent -) : CommonZiraatScript(parent), - m_account(std::move(account)) { -} - -GetLastDaysHistoryZiraatScript::~GetLastDaysHistoryZiraatScript() = default; - -bool GetLastDaysHistoryZiraatScript::saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status -) { - TransactionInfo tr; - tr.accountId = account.id; - tr.description = transaction.description; - tr.bankTime = transaction.bankTime; - tr.bankName = transaction.bankName; - tr.amount = transaction.amount; - tr.phone = transaction.phone; - tr.updateTime = DateUtils::getUtcNow(); - tr.fee = transaction.fee; - tr.name = transaction.name; - tr.bankTrExternalId = transaction.bankTrExternalId; - tr.status = status; - tr.type = transaction.description.startsWith("Перевод") ? TransactionType::Phone : TransactionType::Card; - tr.completeTime = DateUtils::getUtcNow(); - tr.id = TransactionDAO::insertTransaction(tr); - - if (tr.id == -1) { - return false; - } - postNewTransactionData(account, tr); - return true; -} - -bool GetLastDaysHistoryZiraatScript::updateAndPostTransactionData( - const AccountInfo &account, - const int transactionId, - const TransactionStatus status, - const QString &oldDesc, - const QString &newDesc -) { - if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) { - updateTransactionData(account, transactionId, status, oldDesc, newDesc); - return true; - } - return false; -} - -void GetLastDaysHistoryZiraatScript::doStart() { - // берем все транзакции из БД - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - Node closeBannerNode = xmlScreenParser.findButtonNode("HAYIR", false); - if (!closeBannerNode.isEmpty()) { - AdbUtils::makeTap(device.id, closeBannerNode.x(), closeBannerNode.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - } - - if (xmlScreenParser.isHomeScreen()) { - if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { - QList localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); - - QList accounts = xmlScreenParser.parseAccountsInfo(); - for (auto &account: accounts) { - account.deviceId = m_account.deviceId; - account.appCode = m_account.appCode; - if (!AccountDAO::upsertAccount(account)) { - qDebug() << "Not updated: " << account.lastNumbers; - } - } - postAccountsData(accounts); - AdbUtils::goBack(device.id); - QThread::msleep(500); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - Node actionBtn = xmlScreenParser.findButtonNode("Account Activities", false); - if (!actionBtn.isEmpty()) { - AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y()); - QThread::msleep(1000); - } else { - m_error = "Account Activities btn not found"; - qCritical() << m_error; - emit finishedWithResult(m_error); - return; - } - - // localTransactions - // FIXME доделать так, чтобы свайпать и брать больше чем 5 транзакций - for (int i = 0; i < 5; ++i) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - QList transactions = xmlScreenParser.parseTodayAndYesterdayReportHistory(); - if (!transactions.isEmpty()) { - break; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - // все подтвержденные - QList parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory(); - - QSet parsedTransactionIds; - for (const TransactionInfo &transaction: parsedTransactions) { - for (const TransactionInfo &saved: localTransactions) { - if (parsedTransactionIds.contains(saved.id)) { - continue; - } - - if (transaction.bankTime == saved.bankTime && transaction.amount == saved.amount) { - parsedTransactionIds.insert(saved.id); - continue; - } - - if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Complete)) { - qDebug() << "--- saveAndPostNewTransactionData failed"; - } - } - } - } - } else { - m_error = "GetLastDaysHistoryZiraatScript - home screen not found"; - qWarning() << m_error; - emit finishedWithResult(m_error); - } - AdbUtils::goBack(m_account.deviceId); - emit finishedWithResult(m_error); -} diff --git a/android/ziraat/GetLastDaysHistoryZiraatScript.h b/android/ziraat/GetLastDaysHistoryZiraatScript.h deleted file mode 100644 index 97eff06..0000000 --- a/android/ziraat/GetLastDaysHistoryZiraatScript.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "CommonZiraatScript.h" - -class GetLastDaysHistoryZiraatScript final : public CommonZiraatScript { - Q_OBJECT - -public: - ~GetLastDaysHistoryZiraatScript() override; - - explicit GetLastDaysHistoryZiraatScript( - AccountInfo account, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - QString m_error; - - bool saveAndPostNewTransactionData( - const AccountInfo &account, - const TransactionInfo &transaction, - const TransactionStatus &status - ); - - bool updateAndPostTransactionData( - const AccountInfo &account, - int transactionId, - TransactionStatus status, - const QString &oldDesc, - const QString &newDesc - ); -}; diff --git a/android/ziraat/LoginAndCheckAccountsZiraatScript.cpp b/android/ziraat/LoginAndCheckAccountsZiraatScript.cpp deleted file mode 100644 index fceb424..0000000 --- a/android/ziraat/LoginAndCheckAccountsZiraatScript.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "LoginAndCheckAccountsZiraatScript.h" - -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" - -LoginAndCheckAccountsZiraatScript::LoginAndCheckAccountsZiraatScript( - QString deviceId, - QString appCode, - QObject *parent -) : CommonZiraatScript(parent), - m_deviceId(std::move(deviceId)), m_appCode(std::move(appCode)) { -} - -LoginAndCheckAccountsZiraatScript::~LoginAndCheckAccountsZiraatScript() = default; - - -void LoginAndCheckAccountsZiraatScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, 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 (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Cant open the home screen: " + m_deviceId + " " + m_appCode; - qWarning() << m_error; - if (!ApplicationDAO::updatePinCodeStatus(app.id, "no", "Не прошла проверка [дата]")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finishedWithResult(m_error); - return; - } else { - if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) { - qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; - } - - if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); - - QList accounts = xmlScreenParser.parseAccountsInfo(); - for (auto account: accounts) { - account.deviceId = m_deviceId; - account.appCode = m_appCode; - account.status = AccountStatus::Active; - if (!AccountDAO::upsertAccount(account)) { - m_error = "Not updated: " + account.lastNumbers; - qCritical() << m_error; - } - } - - postAccountsData(accounts); - } else { - qWarning() << "Cant go to all products: " << m_deviceId << " " << m_appCode; - } - } - - AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finishedWithResult(m_error); -} diff --git a/android/ziraat/LoginAndCheckAccountsZiraatScript.h b/android/ziraat/LoginAndCheckAccountsZiraatScript.h deleted file mode 100644 index 6b7a638..0000000 --- a/android/ziraat/LoginAndCheckAccountsZiraatScript.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "CommonZiraatScript.h" - -class LoginAndCheckAccountsZiraatScript final : public CommonZiraatScript { - Q_OBJECT - -public: - ~LoginAndCheckAccountsZiraatScript() override; - - explicit LoginAndCheckAccountsZiraatScript( - QString deviceId, - QString appCode, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const QString m_deviceId; - const QString m_appCode; - QString m_error; -}; \ No newline at end of file diff --git a/android/ziraat/PayByIbanScript.cpp b/android/ziraat/PayByIbanScript.cpp deleted file mode 100644 index b898c15..0000000 --- a/android/ziraat/PayByIbanScript.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include "PayByIbanScript.h" - -#include -#include -#include - -#include "DatabaseManager.h" -#include "adb/AdbUtils.h" -#include "dao/AccountDAO.h" -#include "dao/ApplicationDAO.h" -#include "dao/DeviceDAO.h" -#include "dao/TransactionDAO.h" -#include "db/ApplicationInfo.h" -#include "db/DeviceInfo.h" -#include "time/DateUtils.h" - -PayByIbanScript::PayByIbanScript( - AccountInfo account, - QString iban, - QString fullName, - const double amount, - QObject *parent -) : CommonZiraatScript(parent), - m_account(std::move(account)), m_iban(std::move(iban)), - m_fullName(std::move(fullName)), m_amount(amount) { -} - -PayByIbanScript::~PayByIbanScript() = default; - - -void PayByIbanScript::makePayment(const DeviceInfo &device) { - // Оплата со страницы карты - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node editIbanEditText = xmlScreenParser.findEditTextByResourceId("com.ziraat.ziraatmobil:id/edit_iban"); - Node fullNameEditText = xmlScreenParser.findEditTextByResourceId("com.ziraat.ziraatmobil:id/edit_full_name"); - if (!editIbanEditText.isEmpty() && !fullNameEditText.isEmpty()) { - AdbUtils::makeTap(device.id, editIbanEditText.x(), editIbanEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, m_iban); - - AdbUtils::makeTap(device.id, editIbanEditText.x(), editIbanEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, m_fullName); - - Node nextBtn = xmlScreenParser.findButtonNode("NEXT", false); - AdbUtils::makeTap(device.id, nextBtn.x(), nextBtn.y()); - - bool findSenderAccount = false; - for (int i = 0; i < 5; ++i) { - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node senderAccount = xmlScreenParser.findTextNode("Sender Account"); - if (!senderAccount.isEmpty()) { - Node inputBankName = xmlScreenParser.findAccountButton(m_account.lastNumbers); - if (!inputBankName.isEmpty()) { - AdbUtils::makeTap(device.id, inputBankName.x(), inputBankName.y()); - QThread::msleep(500); - - - // FXIME скорее всего придется нажать кнопку NEXT - findSenderAccount = true; - } - break; - } - } - if (!findSenderAccount) { - m_error = "Не нашли Sender Account"; - qDebug() << m_error; - return; - } - - bool findAmountDetermination = false; - for (int i = 0; i < 5; ++i) { - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node amountDetermination = xmlScreenParser.findTextNode("Amount Determination"); - if (!amountDetermination.isEmpty()) { - Node integerEditText = xmlScreenParser.findEditTextByResourceId( - "com.ziraat.ziraatmobil:id/edit_text_integer"); - Node decimalEditText = xmlScreenParser.findEditTextByResourceId( - "com.ziraat.ziraatmobil:id/edit_text_decimal"); - if (!integerEditText.isEmpty() && !decimalEditText.isEmpty()) { - int integerPart = static_cast(m_amount); - AdbUtils::makeTap(device.id, integerEditText.x(), integerEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, QString::number(integerPart)); - - int decimalPart = static_cast((m_amount - integerPart) * 100); - AdbUtils::makeTap(device.id, decimalEditText.x(), decimalEditText.y()); - QThread::msleep(500); - AdbUtils::inputText(device.id, QString::number(decimalPart)); - - findAmountDetermination = true; - break; - } else { - m_error = "Не нашли поля ввода для ввода суммы перевода"; - qDebug() << m_error; - return; - } - } - } - if (!findAmountDetermination) { - m_error = "Не нашли Amount Determination"; - qDebug() << m_error; - return; - } - - bool findDetailDefinition = false; - for (int i = 0; i < 5; ++i) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node fastTransferBtn = xmlScreenParser.findButtonNode("FAST TRANSFER", false); - if (!fastTransferBtn.isEmpty()) { - AdbUtils::makeTap(device.id, fastTransferBtn.x(), fastTransferBtn.y()); - QThread::msleep(500); - continue; - } - - Node detailDefinitionTitle = xmlScreenParser.findTextNode("Detail Definition"); - Node pleaseChooseBtn = xmlScreenParser.findButtonNode("Please choose", false); - if (!detailDefinitionTitle.isEmpty() && !pleaseChooseBtn.isEmpty()) { - AdbUtils::makeTap(device.id, pleaseChooseBtn.x(), pleaseChooseBtn.y()); - QThread::msleep(500); - - Node searchEditText = xmlScreenParser. - findEditTextByResourceId("com.ziraat.ziraatmobil:id/input_search"); - if (!searchEditText.isEmpty()) { - AdbUtils::makeTap(device.id, searchEditText.x(), searchEditText.y()); - QThread::msleep(500); - // FIXME "Individual Payment" или "Other Payments" если нет одного, то надо другой - AdbUtils::inputText(device.id, "Other Payments"); - QThread::msleep(1000); - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node otherPaymentsText = xmlScreenParser.findTextNode("Other Payments"); - if (!otherPaymentsText.isEmpty()) { - AdbUtils::makeTap(device.id, otherPaymentsText.x(), otherPaymentsText.y()); - QThread::msleep(500); - findDetailDefinition = true; - break; - } - } else { - for (int i = 0; i < 5; ++i) { - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node otherPaymentsText = xmlScreenParser.findTextNode("Other Payments"); - if (otherPaymentsText.isEmpty()) { - AdbUtils::makeSwipe( - m_account.deviceId, - device.screenWidth / 2, device.screenHeight / 4 * 3, - device.screenWidth / 2, device.screenHeight / 4 - ); - } else { - AdbUtils::makeTap(device.id, otherPaymentsText.x(), otherPaymentsText.y()); - QThread::msleep(500); - findDetailDefinition = true; - break; - } - QThread::msleep(500); - } - } - } - } - - if (!findDetailDefinition) { - m_error = "Не нашли Detail Definition"; - qDebug() << m_error; - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node nextBtn4 = xmlScreenParser.findButtonNode("NEXT", false); - AdbUtils::makeTap(device.id, nextBtn4.x(), nextBtn4.y()); - QThread::msleep(500); - - Node checkInfo = xmlScreenParser.findTextNode( - "Check the details of the transaction you shall realize and approve your transaction."); - if (!checkInfo.isEmpty()) { - for (int i = 0; i < 5; ++i) { - Node confirmBtn = xmlScreenParser.findButtonNode("CONFIRM", false); - if (confirmBtn.isEmpty()) { - AdbUtils::makeSwipe( - m_account.deviceId, - device.screenWidth / 2, device.screenHeight / 4 * 3, - device.screenWidth / 2, device.screenHeight / 4 - ); - } else { - AdbUtils::makeTap(device.id, confirmBtn.x(), confirmBtn.y()); - QThread::msleep(500); - break; - } - - QThread::msleep(500); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - } - - - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - Node successText = xmlScreenParser.findTextNode("Your transaction is successfully realized."); - if (!successText.isEmpty()) { - Node receiptBtn = xmlScreenParser.findButtonNode("RECEIPT", false); - if (!receiptBtn.isEmpty()) { - AdbUtils::makeTap(device.id, receiptBtn.x(), receiptBtn.y()); - QThread::msleep(500); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter)); - - Node sendBtn = xmlScreenParser.findButtonNode("SEND E-MAIL", false); - if (!sendBtn.isEmpty()) { - AdbUtils::takeXmlDump( - device.id, "pay_by_iban_finish_transactions_" + QString::number(m_account.id) - ); - // Успешная транзакция - // TODO считываем все данные по переводу - TransactionInfo transaction; - transaction.name = m_fullName; - transaction.accountId = m_account.id; - transaction.amount = -m_amount; - transaction.fee = 0; - transaction.status = TransactionStatus::Complete; - transaction.type = TransactionType::Iban; - transaction.updateTime = DateUtils::getUtcNow(); - transaction.completeTime = DateUtils::getUtcNow(); - transaction.timestamp = DateUtils::getUtcNow(); - transaction.id = TransactionDAO::insertTransaction(transaction); - if (transaction.id == -1) { - // FIXME alert!! - qDebug() << "Transaction has not inserted..."; - } - - postNewTransactionData(m_account, transaction); - - Node backBtn = xmlScreenParser.findButtonNode("BACK", false); - if (!backBtn.isEmpty()) { - AdbUtils::makeTap(device.id, backBtn.x(), backBtn.y()); - QThread::msleep(500); - - Node returnBtn = xmlScreenParser.findButtonNode("RETURN TO HOME PAGE", false); - if (!returnBtn.isEmpty()) { - AdbUtils::makeTap(device.id, returnBtn.x(), returnBtn.y()); - } - } - } else { - //FIXME не успешная транзакция - AdbUtils::takeXmlDump( - device.id, "pay_by_iban_fail_transactions_receipt_" + QString::number(m_account.id) - ); - m_error = "Транзакция не успешна"; - qDebug() << m_error; - return; - } - } - } else { - m_error = "Транзакция не успешна"; - qDebug() << m_error; - return; - } - } else { - m_error = "Не нашли Check the details title info"; - qDebug() << m_error; - return; - } - } else { - m_error = "Не нашли поля ввода для IBAN"; - qDebug() << m_error; - return; - } -} - -void PayByIbanScript::doStart() { - const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); - const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); - - if (device.id.isEmpty() || app.id == -1) { - m_error = "Device or app not found."; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - if (!xmlScreenParser.isHomeScreen()) { - if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - m_error = "Не смогли дойти до домашней страницы"; - qDebug() << m_error; - emit finishedWithResult(m_error); - return; - } - } - - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - Node allTransactionsNav = xmlScreenParser.findTextNode("Transactions"); - if (!allTransactionsNav.isEmpty()) { - AdbUtils::makeTap(device.id, allTransactionsNav.x(), allTransactionsNav.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - // перешли на страницу транзакций - Node allTransactionsTitle = xmlScreenParser.findTextNode( - "Transactions", 0, 0, device.screenWidth, device.screenHeight / 2 - ); - Node moneyTransfersBtn = xmlScreenParser.findTextNode("Money Transfers"); - if (!allTransactionsTitle.isEmpty() && !moneyTransfersBtn.isEmpty()) { - AdbUtils::makeTap(device.id, moneyTransfersBtn.x(), moneyTransfersBtn.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - // перешли на страницу переводов - Node moneyTransfersTitle = xmlScreenParser.findTextNode( - "Money Transfers", 0, 0, device.screenWidth, device.screenHeight / 2 - ); - Node transferToOtherAccountBtn = xmlScreenParser.findTextNode("Transfer to Other Account"); - if (!moneyTransfersTitle.isEmpty() && !transferToOtherAccountBtn.isEmpty()) { - AdbUtils::makeTap(device.id, transferToOtherAccountBtn.x(), transferToOtherAccountBtn.y()); - QThread::msleep(1000); - xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); - - Node transferToOtherAccountTitle = xmlScreenParser.findTextNode( - "Transfer to Other Account", 0, 0, device.screenWidth, device.screenHeight / 2 - ); - Node aNewRecipientBtn = xmlScreenParser.findTextNode("A New Recipient"); - if (!transferToOtherAccountTitle.isEmpty() && !aNewRecipientBtn.isEmpty()) { - AdbUtils::makeTap(device.id, aNewRecipientBtn.x(), aNewRecipientBtn.y()); - QThread::msleep(1000); - - makePayment(device); - } else { - m_error = "Не нашли кнопку 'A New Recipient'"; - qDebug() << m_error; - } - } else { - m_error = "Не нашли кнопку 'Transfer to Other Account'"; - qDebug() << m_error; - } - } else { - m_error = "Не нашли кнопку 'Money Transfers'"; - qDebug() << m_error; - } - } else { - m_error = "Не нашли пункт меню 'Transactions'"; - qDebug() << m_error; - } - - if (!m_error.isEmpty()) { - AdbUtils::takeXmlDump(device.id, "pay_by_iban_fail_transactions_" + QString::number(m_account.id)); - } - - emit finishedWithResult(m_error); -} diff --git a/android/ziraat/PayByIbanScript.h b/android/ziraat/PayByIbanScript.h deleted file mode 100644 index 717e3c9..0000000 --- a/android/ziraat/PayByIbanScript.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "CommonZiraatScript.h" -#include "db/DeviceInfo.h" - -class PayByIbanScript final : public CommonZiraatScript { - Q_OBJECT - -public: - ~PayByIbanScript() override; - - PayByIbanScript( - AccountInfo account, - QString iban, - QString fullName, - double amount, - QObject *parent = nullptr - ); - -protected: - void doStart() override; - -private: - const AccountInfo m_account; - const QString m_iban; - const QString m_fullName; - const double m_amount; - QString m_error; - - void makePayment(const DeviceInfo &device); -}; diff --git a/android/ziraat/ScreenXmlZiraatParser.cpp b/android/ziraat/ScreenXmlZiraatParser.cpp deleted file mode 100644 index b9e7d0f..0000000 --- a/android/ziraat/ScreenXmlZiraatParser.cpp +++ /dev/null @@ -1,797 +0,0 @@ -#include "ScreenXmlZiraatParser.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "adb/AdbUtils.h" -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" -#include "time/DateUtils.h" - -ScreenXmlZiraatParser::ScreenXmlZiraatParser(QObject *parent) : QObject(parent) { -} - -ScreenXmlZiraatParser::~ScreenXmlZiraatParser() = default; - -static const QRegularExpression boundsRegex(R"(\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\])"); -static const QRegularExpression onlyDigitsRegex(R"([^0-9])"); -static const QRegularExpression onlyDigitsAndDotRegex(R"([^0-9.])"); - -static const QRegularExpression rePhone(R"raw((\+7\(?\d{3}\)?[- ]?\d{3}[- ]?\d{2}[- ]?\d{2}))raw"); -static const QRegularExpression reFromBank(R"raw(из (.+?) на)raw"); -static const QRegularExpression reToBank(R"raw(в (.+?) через)raw"); -static const QRegularExpression reSbpId(R"(СБП(?: С2С)? ([A-Z0-9]{30,}))"); -static const QRegularExpression reIdAlt(R"(ID перевода (\d+))"); -static const QRegularExpression reDate(R"((?:Дата операции|дата) (\d{2}\.\d{2}\.\d{4}(?: \d{2}:\d{2}:\d{2})?))"); -static const QRegularExpression reSender(R"(от ([^\(]+))"); -static const QRegularExpression reNameAlt(R"(Перевод ([А-Яа-яЁё\s\*]+) в)"); - -static const QRegularExpression reCardData(R"(цифрами\s+(\d\s\d\s\d\s\d).*?Сумма:\s([\d\s]+)\sруб)"); - -Node ScreenXmlZiraatParser::tryToFindBannerOrDialog(const int width, const int height) { - // 1. Ищем push banner - он перекрывает весь экран - Node first = m_nodes[0]; - for (const Node &node: m_nodes) { - // Если это однозначно, что пуш-баннер - if (node.resourceId == "aft-popup-pushNotification-iconClose" && node.className == "android.widget.Button") { - return node; - } - // Если есть кнопка "Позже" - if (node.text.trimmed() == "Позже" && node.className == "android.widget.Button") { - return node; - } - // Если нашли пустую кнпоку - if (node.className == "android.widget.Button" && node.clickable - && node.text.isEmpty() && node.contentDesc.isEmpty()) { - // - if (node.x() > width / 2 && node.y() < width / 2) { - return node; - } - } - } - return {}; -} - -Node ScreenXmlZiraatParser::findAccountButton(const QString &numbers) { - // TODO возможно это не так, надо уточнять - return findTextNode(QString("LEVENT - %1").arg(numbers)); -} - -/** - * Смотрим что есть текс "Мы заботимся о вашей безопасности" и - * кнопка "Отложить" - * @return координаты кнопки для закрытия - */ -Node ScreenXmlZiraatParser::tryToFindSecurityBanner() { - Node text; - Node closeBtn; - for (const Node &node: m_nodes) { - if (node.text.trimmed() == "Мы заботимся о вашей безопасности" && node.className == "android.app.Dialog") { - text = node; - } - if (node.text.trimmed() == "Отложить" && node.className == "android.widget.Button") { - closeBtn = node; - } - } - - // Не нужно проверять все, если хотя бы одна координата есть - if (!text.isEmpty() && !closeBtn.isEmpty()) { - return closeBtn; - } - return {}; -} - -Node ScreenXmlZiraatParser::tryToFindSelectBottomSheet() { - Node closeBtn; - for (const Node &node: m_nodes) { - if ((node.text.trimmed() == "Отмена" || node.text.trimmed() == "Отложить") && node.className == - "android.widget.Button") { - closeBtn = node; - } - } - // Не нужно проверять все, если хотя бы одна координата есть - if (!closeBtn.isEmpty()) { - return closeBtn; - } - return {}; -} - -Node ScreenXmlZiraatParser::findTextNode(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlZiraatParser::findTextNode(const QString &text, const int x1, const int y1, const int x2, const int y2) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.TextView" && node.text.trimmed() == text - && node.x() >= x1 && node.x() <= x2 && node.y() >= y1 && node.y() <= y2) { - return node; - } - } - return {}; -} - -Node ScreenXmlZiraatParser::findEditText(const QString &text) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.text == text) { - return node; - } - } - return {}; -} - -Node ScreenXmlZiraatParser::findEditTextByResourceId(const QString &resourceId) { - for (const Node &node: m_nodes) { - if (node.className == "android.widget.EditText" && node.resourceId == resourceId) { - return node; - } - } - return {}; -} - -Node ScreenXmlZiraatParser::findButtonNode(const QString &text, const bool contains) { - for (const Node &node: m_nodes) { - if (contains) { - if (node.className == "android.widget.Button" && node.text.contains(text)) { - return node; - } - } else { - if (node.className == "android.widget.Button" && node.text.trimmed() == text) { - return node; - } - } - } - return {}; -} - -bool ScreenXmlZiraatParser::isHomeScreen() { - Node activitiesBtn; - Node allProductBtn; - for (const Node &node: m_nodes) { - if (node.text.trimmed() == "Account Activities" && node.className == "android.widget.Button") { - activitiesBtn = node; - } - if (node.text.trimmed() == "All My Accounts" && node.className == "android.widget.Button") { - allProductBtn = node; - } - } - return !activitiesBtn.isEmpty() && !allProductBtn.isEmpty(); -} - -QList ScreenXmlZiraatParser::findPinCodeNodes(const QString &pinCode) { - QMap buttons; - const QSet numberSet = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - - for (const Node &node: m_nodes) { - const QString &text = node.text.trimmed(); - if (node.className == "android.widget.Button" && numberSet.contains(text)) { - buttons[text] = node; - } - } - - QList nodes; - for (int i = 0; i < pinCode.length(); ++i) { - nodes.append(buttons[QString(pinCode.at(i))]); - } - return nodes; -} - -QList ScreenXmlZiraatParser::getLast2DaysTransactions() { - QList nodes; - for (const Node &node: m_nodes) { - if (node.className == "android.widget.Button" && node.text.contains("рубл") - && (node.text.contains("Минус") || node.text.contains("Плюс"))) { - nodes.append(node); - } - } - return nodes; -} - -/** -* - */ -void ScreenXmlZiraatParser::parseAndSaveXml(const QString &xml) { - QDomDocument doc; - doc.setContent(xml); - m_xml = doc.documentElement(); - m_nodes.clear(); - - QXmlStreamReader reader(xml); - while (!reader.atEnd()) { - reader.readNext(); - if (reader.isStartElement() && reader.name() == "node") { - Node node = Node(); - if (reader.attributes().hasAttribute("bounds")) { - QString bounds = reader.attributes().value("bounds").toString(); - if (QRegularExpressionMatch match = boundsRegex.match(bounds); match.hasMatch()) { - node.setBounds( - match.captured(1).toInt(), - match.captured(2).toInt(), - match.captured(3).toInt(), - match.captured(4).toInt() - ); - } - } - if (reader.attributes().hasAttribute("text")) { - node.text = reader.attributes().value("text").toString(); - } - - if (reader.attributes().hasAttribute("content-desc")) { - node.contentDesc = reader.attributes().value("content-desc").toString(); - } - - if (reader.attributes().hasAttribute("class")) { - node.className = reader.attributes().value("class").toString(); - } - - if (reader.attributes().hasAttribute("clickable")) { - node.clickable = reader.attributes().value("clickable").toString() == "true"; - } - if (reader.attributes().hasAttribute("focusable")) { - node.focusable = reader.attributes().value("focusable").toString() == "true"; - } - if (reader.attributes().hasAttribute("resource-id")) { - node.resourceId = reader.attributes().value("resource-id").toString(); - } - - m_nodes.append(node); - } - } -} - -QDomElement findSubElement2(const QDomElement &elem, const QString &index) { - if (elem.hasChildNodes()) { - QDomNodeList subInfoList = elem.childNodes(); - for (QDomNode subInfoNode: subInfoList) { - if (!subInfoNode.isElement()) continue; - QDomElement subInfo = subInfoNode.toElement(); - if (subInfo.attribute("index") == index) { - return subInfo; - } - } - } - return {}; -} - -QString findSubInfo2(const QDomElement &elem, const QString &index) { - QDomElement subInfo = findSubElement2(elem, index); - if (!subInfo.isNull()) { - return subInfo.attribute("text"); - } - return ""; -} - -bool hasTimeComponent2(const QString &datetimeStr) { - QDateTime dt = QDateTime::fromString(datetimeStr, "dd.MM.yyyy HH:mm:ss"); - if (!dt.isValid()) { - return false; // Строка не соответствует формату даты и времени - } - return dt.time() != QTime(0, 0, 0); // Проверка, что время не равно 00:00:00 -} - -QString transformBankDateTime2(const QString &date) { - QDateTime dt; - if (date.contains(",")) { - // 10.05.2025, 15:03 - dt = QDateTime::fromString(date, "dd.MM.yyyy, HH:mm"); - } else { - dt = QDateTime::fromString(date, "dd.MM.yyyy HH:mm:ss"); - } - if (!dt.isValid()) { - dt = QDateTime::fromString(date, "dd.MM.yyyy HH:mm"); - } - if (!dt.isValid()) { - return date; // не распарсили - } - return dt.toString("dd.MM.yyyy HH:mm:ss"); -} - -void parseTransferInfo2(const QString &text, TransactionInfo &info) { - if (const auto m = reSender.match(text); m.hasMatch()) { - info.name = m.captured(1).trimmed(); - } else if (const auto m1 = reNameAlt.match(text); m1.hasMatch()) { - info.name = m1.captured(1).trimmed(); - } - - if (const auto m = rePhone.match(text); m.hasMatch()) { - info.phone = m.captured(1).trimmed().remove(onlyDigitsRegex); - } - - if (const auto m = reFromBank.match(text); m.hasMatch()) { - if (const QString bank = m.captured(1).trimmed(); !bank.isEmpty()) { - info.bankName = bank; - } - } else if (const auto m1 = reToBank.match(text); m1.hasMatch()) { - if (const QString bank = m1.captured(1).trimmed(); !bank.isEmpty()) { - info.bankName = bank; - } - } - - if (const auto m = reSbpId.match(text); m.hasMatch()) { - info.bankTrExternalId = m.captured(1); - } else if (const auto m1 = reIdAlt.match(text); m1.hasMatch()) { - info.bankTrExternalId = m1.captured(1); - } - - if (const auto m = reDate.match(text); m.hasMatch()) { - const QString date = m.captured(1); - // если не пустая и дата у нас с 00:00 - // if (hasTimeComponent2(date)) { - info.bankTime = transformBankDateTime2(date); - // } - } -} - -void traverseNodes2(const QDomNode &node) { - if (node.isElement()) { - QDomElement elem = node.toElement(); - - if (node.isElement()) { - QDomElement elem = node.toElement(); - - if (elem.tagName() == "node" && - elem.attribute("class") == "android.widget.Button" && - elem.attribute("text") == "Детали операции") { - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - - for (int j = 0; j < detailInfo.count(); ++j) { - QDomNode info = detailInfo.at(j); - if (!info.isElement()) continue; - QDomElement infoElement = info.toElement(); - QString index = infoElement.attribute("index"); - switch (index.toInt()) { - case 2: - qDebug() << "Дата операции:" << findSubInfo2(infoElement, "1"); - break; - case 5: - qDebug() << "Название:" << findSubInfo2(infoElement, "1"); - qDebug() << "-------------------------------------"; - // parseTransfer(findSubInfo2(infoElement)); - break; - case 7: - qDebug() << "Комиссия:" << findSubInfo2(infoElement, "1"); - break; - default: - qDebug() << "Прочее:" << findSubInfo2(infoElement, "1"); - break; - } - - /** - Node: "2" "" - ... Node: "0" "Дата операции" - ... Node: "1" "30.04.2025 17:46:29" - Node: "5" "" - ... Node: "0" "Название" - ... Node: "1" "DBO TRANSFER RSHB INTERNET-BANK" - Node: "7" "" - ... Node: "0" "Комиссия" - ... Node: "1" "50 ₽" - */ - - // if (sibElem.hasChildNodes()) { - // QDomNodeList sibChildren = sibElem.childNodes(); - // for (int k = 0; k < sibChildren.count(); ++k) { - // QDomNode sibChild = sibChildren.at(k); - // if (!sibChild.isElement()) continue; - // QDomElement sibChildElem = sibChild.toElement(); - // qDebug() << "... Node:" - // << sibChildElem.attribute("index") - // << sibChildElem.attribute("text"); - // // << sibChildElem.attribute("class"); - // - // - // if (sibChildElem.hasChildNodes()) { - // QDomNodeList sibChildren2 = sibChildElem.childNodes(); - // for (int k = 0; k < sibChildren2.count(); ++k) { - // QDomNode sibChild2 = sibChildren2.at(k); - // if (!sibChild2.isElement()) continue; - // QDomElement sibChildElem2 = sibChild2.toElement(); - // - // qDebug() << "...... Node:" - // << sibChildElem2.attribute("index") - // << sibChildElem2.attribute("text"); - // // << sibChildElem2.attribute("class"); - // - // if (sibChildElem2.hasChildNodes()) { - // QDomNodeList sibChildren3 = sibChildElem2.childNodes(); - // for (int k = 0; k < sibChildren3.count(); ++k) { - // QDomNode sibChild3 = sibChildren3.at(k); - // if (!sibChild3.isElement()) continue; - // QDomElement sibChildElem3 = sibChild3.toElement(); - // - // qDebug() << "......... Node:" - // << sibChildElem3.attribute("index") - // << sibChildElem3.attribute("text"); - // // << sibChildElem2.attribute("class"); - // } - // } - // } - // } - // } - // } - } - } - } - } - - // Рекурсивно обходим всех детей - QDomNode child = node.firstChild(); - while (!child.isNull()) { - traverseNodes2(child); - child = child.nextSibling(); - } -} - -QDomNode findNode2(const QDomNode &node, const QString &text) { - if (node.isElement()) { - QString nodeText = node.toElement().attribute("text"); - if (nodeText.toLower().startsWith(text)) { - return node; - } - } - - - QDomNode child = node.firstChild(); - while (!child.isNull()) { - QDomNode result = findNode2(child, text); - if (!result.isNull()) { - return result; - } - child = child.nextSibling(); - } - return {}; -} - -float extractAmounts2(const QString &text) { - // Разрешаем пробелы внутри чисел, захватываем "рубль", "рубля", "рублей" и т.д. - // QRegularExpression re(R"((Плюс|Минус)\s+([\d\s]+)\s+рубл\w*\s+(\d+)\s+копе\w*)"); - QRegularExpression re( - R"((Плюс|Минус)\s+([\d\s]+)\s+рубл\w*\s+(\d+)\s+копе\w*)", - QRegularExpression::UseUnicodePropertiesOption - ); - - QRegularExpressionMatch match = re.match(text); - if (match.hasMatch()) { - QString signStr = match.captured(1); // "Плюс" или "Минус" - QString rublesStr = match.captured(2).remove(' '); // "1 000" → "1000" - int rubles = rublesStr.toInt(); - int kopecks = match.captured(3).toInt(); - - int totalKopecks = rubles * 100 + kopecks; - if (signStr == "Минус") { - totalKopecks *= -1; - } - - return totalKopecks / 100.0f; - } - return 0; -} - -QString beforeAmount2(const QString &text) { - int index = text.indexOf(QRegularExpression("Плюс|Минус")); - if (index != -1) { - return text.left(index).trimmed(); - } - return text; // если не найдено — вернуть всю строку -} - -double parseAmount2(const QString &amountAsString) { - QString numberString = amountAsString; - numberString.remove(QRegularExpression(R"([^\dточка])")); - numberString.replace("точка", "."); - double amount = numberString.toDouble(); - - if (amountAsString.startsWith("-")) { - amount *= -1; - } - - return amount; -} - -QDomElement findElementWithListView2(const QDomElement &parent) { - QDomElement element = parent.firstChildElement(); - while (!element.isNull()) { - QDomElement child = element.firstChildElement(); - while (!child.isNull()) { - if (child.hasAttribute("class") && child.attribute("class") == - "androidx.recyclerview.widget.RecyclerView") { - return child; - } - child = child.nextSiblingElement(); - } - - QDomElement found = findElementWithListView2(element); - if (!found.isNull()) { - return found; - } - element = element.nextSiblingElement(); - } - return {}; -} - - -QDateTime getUtcFromTurkey(const QString &d, const QString &m, const QString &t) { - const QMap monthMap = { - {"JAN", 1}, {"FEB", 2}, {"MAR", 3}, {"APR", 4}, - {"MAY", 5}, {"JUN", 6}, {"JUL", 7}, {"AUG", 8}, - {"SEP", 9}, {"OCT", 10}, {"NOV", 11}, {"DEC", 12} - }; - - const int day = d.toInt(); - const int month = monthMap.value(m.toUpper(), 0); - const int year = QDate::currentDate().year(); - - const QDate date(year, month, day); - const QTime time = QTime::fromString(t, "HH:mm"); - - const QTimeZone tz("Europe/Istanbul"); // TRT (UTC+3, без перехода на зимнее время) - const QDateTime dtLocal(date, time, tz); - - const QDateTime dtUtc = dtLocal.toTimeZone(QTimeZone::utc()); - return dtUtc; -} - - -QList ScreenXmlZiraatParser::parseTodayAndYesterdayReportHistory() { - QList transactions; - - QDomElement child = findElementWithListView2(m_xml); - QDomNodeList listView = child.childNodes(); - qDebug() << listView.count(); - if (!listView.isEmpty()) { - for (QDomNode node: listView) { - const QDomNodeList trBoxGroup = node.childNodes(); - if (trBoxGroup.count() > 0) { - for (QDomNode item: trBoxGroup) { - TransactionInfo transaction; - QDomElement groupItem = item.toElement(); - if (groupItem.attribute("index") == "0") { - for (QDomNode subDate: groupItem.childNodes()) { - QDomElement dateItem = subDate.toElement(); - QString date; - QString month; - QString time; - if (dateItem.attribute("index") == "0") { - date = dateItem.attribute("text"); - } else if (groupItem.attribute("index") == "1") { - month = groupItem.attribute("text"); - } else if (groupItem.attribute("index") == "2") { - time = groupItem.attribute("text"); - } - transaction.bankTime = date + " " + month + " " + time; - transaction.completeTime = getUtcFromTurkey(date, month, time); - transaction.timestamp = DateUtils::getUtcNow(); - } - } else if (groupItem.attribute("index") == "2") { - transaction.description = groupItem.attribute("text"); - } else if (groupItem.attribute("index") == "3") { - transaction.amount = groupItem.attribute("text").replace("TL", "").trimmed().toFloat(); - } - transactions.append(transaction); - } - } - } - } else { - qDebug() << "ListView is empty"; - } - return transactions; -} - - -TransactionInfo parseExpandedInfoHistory2(const QDomElement &root) { - if (root.isElement()) { - QDomElement elem = root.toElement(); - if (elem.tagName() == "node" - && elem.attribute("class") == "android.widget.Button" - && elem.attribute("text") == "Детали операции" - ) { - TransactionInfo transaction; - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - for (int i = 0; i < detailInfo.count(); ++i) { - QDomNode info = detailInfo.at(i); - if (!info.isElement()) continue; - - switch (QDomElement e = info.toElement(); e.attribute("index").toInt()) { - case 2: - // Дата операции - transaction.bankTime = transformBankDateTime2(findSubInfo2(e, "1")); - break; - case 5: - transaction.description = findSubInfo2(e, "1"); - parseTransferInfo2(transaction.description, transaction); - break; - case 7: - // Комиссия - transaction.fee = findSubInfo2(e, "1").remove(onlyDigitsRegex).toFloat(); - break; - default: - // qDebug() << "Прочее:" << findSubInfo2(e); - break; - } - } - return transaction; - } - } - - // Рекурсивно обходим всех детей - QDomNode child = root.firstChild(); - while (!child.isNull()) { - if (TransactionInfo info = parseExpandedInfoHistory2(child.toElement()); !info.bankTime.isEmpty()) { - return info; - } - child = child.nextSibling(); - } - - return {}; -} - -TransactionInfo ScreenXmlZiraatParser::parseTransactionInfoHistory(int screenWidth, int screenHeight) { - // 1. Распарсить описание транзакции - TransactionInfo transaction = parseExpandedInfoHistory2(m_xml); - // 1. Найти статус: Исполнен | Операция исполнена - transaction.status = TransactionStatus::Wait; - Node status1 = findTextNode("Исполнен", 0, 0, screenWidth, screenHeight / 2); - Node status2 = findTextNode("Операция исполнена", 0, 0, screenWidth, screenHeight / 2); - if (!status1.isEmpty() || !status2.isEmpty()) { - transaction.status = TransactionStatus::Complete; - } - return transaction; -} - -TransactionInfo parseExpandedInfo2(const QDomElement &root) { - if (root.isElement()) { - QDomElement elem = root.toElement(); - if (elem.tagName() == "node" - && elem.attribute("class") == "android.widget.Button" - && elem.attribute("text") == "Детали операции" - ) { - TransactionInfo transaction; - // Все что ниже деталей операции - QDomNodeList detailInfo = elem.parentNode().childNodes(); - - for (int i = 0; i < detailInfo.count(); ++i) { - QDomNode info = detailInfo.at(i); - if (!info.isElement()) continue; - - QDomElement infoElement = info.toElement(); - QString index = infoElement.attribute("index"); - QDomElement ee; - switch (index.toInt()) { - case 3: - transaction.bankTime = transformBankDateTime2(findSubInfo2(infoElement, "1")); - break; - case 4: - ee = findSubElement2(infoElement, "0"); - ee = findSubElement2(ee, "1"); - transaction.phone = findSubInfo2(ee, "0").remove(onlyDigitsRegex); - break; - case 5: - transaction.name = findSubInfo2(infoElement, "1"); - break; - case 6: - transaction.bankName = findSubInfo2(infoElement, "1"); - break; - case 9: - transaction.fee = findSubInfo2(infoElement, "1").remove(onlyDigitsAndDotRegex).toFloat(); - break; - case 10: - ee = findSubElement2(infoElement, "1"); - transaction.bankTrExternalId = findSubInfo2(ee, "1"); - break; - default: - break; - } - } - return transaction; - } - } - - // Рекурсивно обходим всех детей - QDomNode child = root.firstChild(); - while (!child.isNull()) { - if (TransactionInfo info = parseExpandedInfo2(child.toElement()); !info.bankTime.isEmpty()) { - return info; - } - child = child.nextSibling(); - } - - return {}; -} - -TransactionInfo ScreenXmlZiraatParser::parseTransactionInfo(int screenWidth, int screenHeight) { - // 1. Распарсить описание транзакции - TransactionInfo transaction = parseExpandedInfo2(m_xml); - // 1. Найти статус: Исполнен | Операция исполнена - transaction.status = TransactionStatus::Wait; - Node status1 = findTextNode("Исполнен", 0, 0, screenWidth, screenHeight / 2); - Node status2 = findTextNode("Операция исполнена", 0, 0, screenWidth, screenHeight / 2); - if (!status1.isEmpty() || !status2.isEmpty()) { - transaction.status = TransactionStatus::Complete; - } - return transaction; -} - - -QList ScreenXmlZiraatParser::parseAccountsInfo() { - const QDomNodeList nodes = m_xml.elementsByTagName("node"); - QList accounts; - - for (int i = 0; i < nodes.count(); ++i) { - const QDomElement el = nodes.at(i).toElement(); - if (el.attribute("resource-id") == "com.ziraat.ziraatmobil:id/text_account_or_bank_name") { - // Нашли нужный аккаунт - const QDomElement parent = el.parentNode().toElement(); // account_name_layout - const QDomElement balanceFrame = parent.parentNode().parentNode().lastChildElement("node"); - // Перебираем потомков в поисках суммы - QDomNode n = balanceFrame; - while (!n.isNull()) { - const QDomElement e = n.toElement(); - if (e.attribute("resource-id") == "com.ziraat.ziraatmobil:id/text_value") { - QString amount = e.attribute("text"); - QString account = el.attribute("text"); - - AccountInfo info; - info.lastNumbers = account; - info.amount = amount.toFloat(); - info.description = account; - info.updateTime = DateUtils::getUtcNow(); - - accounts.append(info); - break; - } - n = n.nextSibling(); - } - } - } - return accounts; -} - -void ScreenXmlZiraatParser::test() { - // TransactionInfo info2 = parseExpandedInfo(m_xml); - // qDebug().noquote().nospace() << convertTransactionToString(info2); - // return; - - QFile file("assets/rshb/all_products.xml"); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qDebug() << "Cannot open file"; - } - - QDomDocument doc; - if (!doc.setContent(&file)) { - qDebug() << "Failed to parse XML"; - } - file.close(); - - qDebug() << "Start parsing XML:"; - - // Номер карты с последними цифрами - QDomElement root = doc.documentElement(); - parseAndSaveXml(doc.toString()); - // for (const AccountInfo &info: parseAccountsInfo()) { - // qDebug().noquote().nospace() << convertAccountToString(info); - // } - // TransactionInfo info = parseExpandedInfo(root); - // qDebug().noquote().nospace() << convertTransactionToString(info); - // ЕСПП -} diff --git a/android/ziraat/ScreenXmlZiraatParser.h b/android/ziraat/ScreenXmlZiraatParser.h deleted file mode 100644 index 36669d9..0000000 --- a/android/ziraat/ScreenXmlZiraatParser.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include -#include -#include "android/xml/Node.h" -#include "db/AccountInfo.h" -#include "db/TransactionInfo.h" - -class ScreenXmlZiraatParser final : public QObject { - Q_OBJECT - -public: - explicit ScreenXmlZiraatParser(QObject *parent = nullptr); - - ~ScreenXmlZiraatParser() override; - - Node tryToFindBannerOrDialog(int width, int height); - - Node tryToFindSecurityBanner(); - - Node tryToFindSelectBottomSheet(); - - Node findAccountButton(const QString &numbers); - - Node findTextNode(const QString &text); - - Node findTextNode(const QString &text, int x1, int y1, int x2, int y2); - - Node findEditText(const QString &text); - - Node findEditTextByResourceId(const QString &resourceId); - - Node findButtonNode(const QString &text, bool contains); - - bool isHomeScreen(); - - QList getLast2DaysTransactions(); - - void parseAndSaveXml(const QString &xml); - - TransactionInfo parseTransactionInfoHistory(int screenWidth, int screenHeight); - - TransactionInfo parseTransactionInfo(int screenWidth, int screenHeight); - - QList parseTodayAndYesterdayReportHistory(); - - QList parseAccountsInfo(); - - void test(); - -private: - QList m_nodes; - QDomElement m_xml; - - QList findPinCodeNodes(const QString &pinCode); -}; diff --git a/assets/birbank/banner/block_banner.png b/assets/birbank/banner/block_banner.png deleted file mode 100644 index 715970a..0000000 Binary files a/assets/birbank/banner/block_banner.png and /dev/null differ diff --git a/assets/birbank/banner/block_banner.xml b/assets/birbank/banner/block_banner.xml deleted file mode 100644 index 1d77326..0000000 --- a/assets/birbank/banner/block_banner.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/birbank/banner/home_banner.png b/assets/birbank/banner/home_banner.png deleted file mode 100644 index 1ead70c..0000000 Binary files a/assets/birbank/banner/home_banner.png and /dev/null differ diff --git a/assets/birbank/banner/home_banner.xml b/assets/birbank/banner/home_banner.xml deleted file mode 100644 index f9180b9..0000000 --- a/assets/birbank/banner/home_banner.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/birbank/home_page.png b/assets/birbank/home_page.png deleted file mode 100644 index 27c6daa..0000000 Binary files a/assets/birbank/home_page.png and /dev/null differ diff --git a/assets/birbank/home_page.xml b/assets/birbank/home_page.xml deleted file mode 100644 index e0db3e3..0000000 --- a/assets/birbank/home_page.xml +++ /dev/null @@ -1,1950 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/birbank/payments_card.png b/assets/birbank/payments_card.png deleted file mode 100644 index 21a8730..0000000 Binary files a/assets/birbank/payments_card.png and /dev/null differ diff --git a/assets/birbank/payments_card.xml b/assets/birbank/payments_card.xml deleted file mode 100644 index 7689417..0000000 --- a/assets/birbank/payments_card.xml +++ /dev/null @@ -1,798 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/birbank/payments_history.png b/assets/birbank/payments_history.png deleted file mode 100644 index ba7c934..0000000 Binary files a/assets/birbank/payments_history.png and /dev/null differ diff --git a/assets/birbank/payments_history.xml b/assets/birbank/payments_history.xml deleted file mode 100644 index a353dd4..0000000 --- a/assets/birbank/payments_history.xml +++ /dev/null @@ -1,1122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/birbank/pin_code.png b/assets/birbank/pin_code.png deleted file mode 100644 index efffc1c..0000000 Binary files a/assets/birbank/pin_code.png and /dev/null differ diff --git a/assets/birbank/pin_code.xml b/assets/birbank/pin_code.xml deleted file mode 100644 index 2393a92..0000000 --- a/assets/birbank/pin_code.xml +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/birbank/transaction_by_card.png b/assets/birbank/transaction_by_card.png deleted file mode 100644 index 35733ba..0000000 Binary files a/assets/birbank/transaction_by_card.png and /dev/null differ diff --git a/assets/birbank/transaction_by_card.xml b/assets/birbank/transaction_by_card.xml deleted file mode 100644 index 0a81ce1..0000000 --- a/assets/birbank/transaction_by_card.xml +++ /dev/null @@ -1,800 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/birbank/transactions.png b/assets/birbank/transactions.png deleted file mode 100644 index 944522e..0000000 Binary files a/assets/birbank/transactions.png and /dev/null differ diff --git a/assets/birbank/transactions.xml b/assets/birbank/transactions.xml deleted file mode 100644 index ffe15f9..0000000 --- a/assets/birbank/transactions.xml +++ /dev/null @@ -1,719 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/all_products.png b/assets/rshb/all_products.png deleted file mode 100644 index 3520293..0000000 Binary files a/assets/rshb/all_products.png and /dev/null differ diff --git a/assets/rshb/all_products.xml b/assets/rshb/all_products.xml deleted file mode 100644 index c43f03f..0000000 --- a/assets/rshb/all_products.xml +++ /dev/null @@ -1,297 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/banks.txt b/assets/rshb/banks.txt deleted file mode 100644 index 9f8fd6b..0000000 --- a/assets/rshb/banks.txt +++ /dev/null @@ -1,205 +0,0 @@ -Сбербанк -Т-Банк -ВТБ -Альфа-Банк -Филиал Открытие БМ-Банк -Почта Банк -Райффайзен Банк -Газпромбанк -Совкомбанк -ПСБ -Ак Барс Банк -МКБ (Московский кредитный банк) -Банк Синара -МТС-Банк -Золотая Корона – Korona (РНКО Платежный Центр) -Банк Санкт-Петербург -Банк Русский Стандарт -VK Pay - ВК Платёжные решения -Wallet One (РНКО Единая касса) -Wildberries (Вайлдберриз Банк) -АБ РОССИЯ -АИКБ Енисейский объединенный банк -АКБ Держава -АКБ ЕВРОФИНАНС МОСНАРБАНК -АКБ Ланта Банк -АКБ НРБанк -АКБ СЛАВИЯ -АКБ Тендер Банк -АКИБАНК -Абсолют Банк -Авито Кошелек (Платежный конструктор) -Авто Финанс Банк -Автоторгбанк -Азиатско-Тихоокеанский Банк -Алеф-Банк -Алмазэргиэнбанк -БАНК МОСКВА СИТИ -БАНК ОРЕНБУРГ -БАНК СГБ -БАНК СНГБ -БАНК УРАЛСИБ -ББР Банк -БКС Банк -Банк АВАНГАРД -Банк АЛЕКСАНДРОВСКИЙ -Банк Аверс -Банк Агророс -Банк Акцепт -Банк БЖФ -Банк ВБРР -Банк Венец -Банк Вологжанин -Банк ДОМ.РФ -Банк ЗЕНИТ -Банк Заречье -Банк ИПБ -Банк ИТУРУП -Банк Интеза -Банк Йошкар-Ола -Банк Кремлевский -Банк Кузнецкий -Банк Левобережный -Банк МБА МОСКВА -Банк НОВИКОМ (НОВИКОМБАНК) -Банк Национальный стандарт -Банк Объединенный капитал -Банк Оранжевый -Банк ПСКБ -Банк Приморье -Банк РЕСО Кредит -Банк РСИ -Банк Развитие-Столица -Банк Саратов -Банк Снежинский -Банк Уралфинанс -Банк ФИНАМ -Банк Финсервис -Банк ЦентроКредит -Банк ЧБРР -Бланк банк -Братский АНКБ -БыстроБанк -ВЛАДБИЗНЕСБАНК -ВНЕШФИНБАНК -ВУЗ-банк -ГЕНБАНК -ГОРБАНК -ГУТА-БАНК -Газтрансбанк -Газэнергобанк -Далена Банк -Дальневосточный банк -Датабанк -Драйв Клик Банк -ЖИВАГО БАНК -Земский банк -ИК Банк -ИС Банк -ИШБАНК -Инбанк -Ингосстрах Банк -КБ РостФинанс -КБ АГРОПРОМКРЕДИТ -КБ АРЕСБАНК -КБ Долинск -КБ Крокус Банк -КБ ЛОКО-Банк -КБ Модульбанк -КБ Москоммерцбанк -КБ Пойдём -КБ Солидарность -КБ Стройлесбанк -КБ Хлынов -КБ ЭНЕРГОТРАНСБАНК -КБЭР Банк Казани -КОШЕЛЕВ-БАНК -Контур.Банк -Кошелек ЦУПИС (Мобильная карта) -Кредит Европа Банк (Россия) -Кредит Урал Банк -Кубань Кредит -Кубаньторгбанк -Кузнецкбизнесбанк -МБ Банк -МЕЖДУНАРОДНЫЙ ФИНАНСОВЫЙ КЛУБ -МЕТКОМБАНК -МОНЕТА -МОРСКОЙ БАНК -МОСКОМБАНК -МС Банк Рус -МТС Деньги (ЭКСИ Банк) -Металлинвестбанк -Мир Привилегий (МП Банк) -НБД-Банк -НИКО-БАНК -НК Банк -НКО МОБИ.Деньги -НКО Расчетные Решения -НОКССБАНК -НС Банк -Нацинвестпромбанк -Новобанк -Новый век -Норвик Банк -ОТП Банк -Озон Банк (Ozon) -ПЕРВОУРАЛЬСКБАНК -Первый Дортрансбанк -Первый Инвестиционный Банк -Прио-Внешторгбанк -ПроБанк -РНКБ Банк -РУСНАРБАНК -Реалист Банк -Ренессанс Банк (Ренессанс Кредит) -Рокетбанк -РосДорБанк -Роял Кредит Банк -Русьуниверсалбанк -СДМ-Банк -СИБСОЦБАНК -СИНКО-БАНК -СКБ Примсоцбанк -СОЦИУМ БАНК -Свой Банк -Северный Народный Банк -Солид Банк -Ставропольпромстройбанк -ТАТСОЦБАНК -ТРАНСКАПИТАЛБАНК -Таврический Банк -Тимер Банк -Тойота Банк -Тольяттихимбанк -Томскпромстройбанк -Точка Банк -Трансстройбанк -УБРиР -УКБ Белгородсоцбанк -УРАЛПРОМБАНК -Углеметбанк -Урал ФД -ФИНСТАР БАНК -ФОРА-БАНК -Форштадт -Хайс -Хакасский муниципальный банк -ЦМРБанк -Центр-инвест -Цифра банк -ЧЕЛИНДБАНК -ЧЕЛЯБИНВЕСТБАНК -ЭЛПЛАТ -Экономбанк -Экспобанк -Энергобанк -Эс-Би-Ай Банк -ЮГ-Инвестбанк -ЮМани -ЮНИСТРИМ БАНК -ЮниКредит Банк -ЯРИНТЕРБАНК -Яндекс -банк Раунд -банк Элита \ No newline at end of file diff --git a/assets/rshb/card_report_complete.png b/assets/rshb/card_report_complete.png deleted file mode 100644 index 3317f6e..0000000 Binary files a/assets/rshb/card_report_complete.png and /dev/null differ diff --git a/assets/rshb/card_report_complete.xml b/assets/rshb/card_report_complete.xml deleted file mode 100644 index b0732ea..0000000 --- a/assets/rshb/card_report_complete.xml +++ /dev/null @@ -1,430 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/card_report_complete_0.png b/assets/rshb/card_report_complete_0.png deleted file mode 100644 index 6d5a527..0000000 Binary files a/assets/rshb/card_report_complete_0.png and /dev/null differ diff --git a/assets/rshb/card_report_complete_0.xml b/assets/rshb/card_report_complete_0.xml deleted file mode 100644 index 248e542..0000000 --- a/assets/rshb/card_report_complete_0.xml +++ /dev/null @@ -1,1068 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/card_screen.png b/assets/rshb/card_screen.png deleted file mode 100644 index 76e973a..0000000 Binary files a/assets/rshb/card_screen.png and /dev/null differ diff --git a/assets/rshb/card_screen.xml b/assets/rshb/card_screen.xml deleted file mode 100644 index 9fffac4..0000000 --- a/assets/rshb/card_screen.xml +++ /dev/null @@ -1,1034 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/card_screen_actions.png b/assets/rshb/card_screen_actions.png deleted file mode 100644 index c6069a4..0000000 Binary files a/assets/rshb/card_screen_actions.png and /dev/null differ diff --git a/assets/rshb/card_screen_actions.xml b/assets/rshb/card_screen_actions.xml deleted file mode 100644 index 298c077..0000000 --- a/assets/rshb/card_screen_actions.xml +++ /dev/null @@ -1,869 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/card_screen_all_transactions.png b/assets/rshb/card_screen_all_transactions.png deleted file mode 100644 index 0984d96..0000000 Binary files a/assets/rshb/card_screen_all_transactions.png and /dev/null differ diff --git a/assets/rshb/card_screen_all_transactions.xml b/assets/rshb/card_screen_all_transactions.xml deleted file mode 100644 index 8e55d70..0000000 --- a/assets/rshb/card_screen_all_transactions.xml +++ /dev/null @@ -1,898 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/card_screen_open_filer.png b/assets/rshb/card_screen_open_filer.png deleted file mode 100644 index 9ac5b1a..0000000 Binary files a/assets/rshb/card_screen_open_filer.png and /dev/null differ diff --git a/assets/rshb/card_screen_open_filer.xml b/assets/rshb/card_screen_open_filer.xml deleted file mode 100644 index 19b64c3..0000000 --- a/assets/rshb/card_screen_open_filer.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/error/not_found_number+bank.png b/assets/rshb/error/not_found_number+bank.png deleted file mode 100644 index 84dbf0a..0000000 Binary files a/assets/rshb/error/not_found_number+bank.png and /dev/null differ diff --git a/assets/rshb/error/not_found_number+bank.xml b/assets/rshb/error/not_found_number+bank.xml deleted file mode 100644 index 1ec7ef7..0000000 --- a/assets/rshb/error/not_found_number+bank.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/error/pay_by_phone_unknown_error.png b/assets/rshb/error/pay_by_phone_unknown_error.png deleted file mode 100644 index 8a13eea..0000000 Binary files a/assets/rshb/error/pay_by_phone_unknown_error.png and /dev/null differ diff --git a/assets/rshb/error/pay_by_phone_unknown_error.xml b/assets/rshb/error/pay_by_phone_unknown_error.xml deleted file mode 100644 index af79288..0000000 --- a/assets/rshb/error/pay_by_phone_unknown_error.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/rshb/error/session_expired.png b/assets/rshb/error/session_expired.png deleted file mode 100644 index c52c6ed..0000000 Binary files a/assets/rshb/error/session_expired.png and /dev/null differ diff --git a/assets/rshb/error/session_expired.xml b/assets/rshb/error/session_expired.xml deleted file mode 100644 index 490111d..0000000 --- a/assets/rshb/error/session_expired.xml +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/history_wait_complite.png b/assets/rshb/history_wait_complite.png deleted file mode 100644 index 1985154..0000000 Binary files a/assets/rshb/history_wait_complite.png and /dev/null differ diff --git a/assets/rshb/history_wait_complite.xml b/assets/rshb/history_wait_complite.xml deleted file mode 100644 index 481efae..0000000 --- a/assets/rshb/history_wait_complite.xml +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/home_page_push_banner.png b/assets/rshb/home_page_push_banner.png deleted file mode 100644 index 40f1000..0000000 Binary files a/assets/rshb/home_page_push_banner.png and /dev/null differ diff --git a/assets/rshb/home_page_push_banner.xml b/assets/rshb/home_page_push_banner.xml deleted file mode 100644 index 6d58a8d..0000000 --- a/assets/rshb/home_page_push_banner.xml +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/home_screen.png b/assets/rshb/home_screen.png deleted file mode 100644 index e35c012..0000000 Binary files a/assets/rshb/home_screen.png and /dev/null differ diff --git a/assets/rshb/home_screen.xml b/assets/rshb/home_screen.xml deleted file mode 100644 index ec705e9..0000000 --- a/assets/rshb/home_screen.xml +++ /dev/null @@ -1,1721 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/bank_list.png b/assets/rshb/pay/bank_list.png deleted file mode 100644 index ab3b6d8..0000000 Binary files a/assets/rshb/pay/bank_list.png and /dev/null differ diff --git a/assets/rshb/pay/bank_list.xml b/assets/rshb/pay/bank_list.xml deleted file mode 100644 index 4c07cce..0000000 --- a/assets/rshb/pay/bank_list.xml +++ /dev/null @@ -1,1326 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone.png b/assets/rshb/pay/pay_by_phone.png deleted file mode 100644 index 70d1b22..0000000 Binary files a/assets/rshb/pay/pay_by_phone.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone.xml b/assets/rshb/pay/pay_by_phone.xml deleted file mode 100644 index 9611f52..0000000 --- a/assets/rshb/pay/pay_by_phone.xml +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_after_input.png b/assets/rshb/pay/pay_by_phone_after_input.png deleted file mode 100644 index c2b0a88..0000000 Binary files a/assets/rshb/pay/pay_by_phone_after_input.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_after_input.xml b/assets/rshb/pay/pay_by_phone_after_input.xml deleted file mode 100644 index c3008ac..0000000 --- a/assets/rshb/pay/pay_by_phone_after_input.xml +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_complete.png b/assets/rshb/pay/pay_by_phone_complete.png deleted file mode 100644 index d6fe31a..0000000 Binary files a/assets/rshb/pay/pay_by_phone_complete.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_complete.xml b/assets/rshb/pay/pay_by_phone_complete.xml deleted file mode 100644 index ac38770..0000000 --- a/assets/rshb/pay/pay_by_phone_complete.xml +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_complete_expanded.png b/assets/rshb/pay/pay_by_phone_complete_expanded.png deleted file mode 100644 index bc445a7..0000000 Binary files a/assets/rshb/pay/pay_by_phone_complete_expanded.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_complete_expanded.xml b/assets/rshb/pay/pay_by_phone_complete_expanded.xml deleted file mode 100644 index bab4eae..0000000 --- a/assets/rshb/pay/pay_by_phone_complete_expanded.xml +++ /dev/null @@ -1,534 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_complete_wait.png b/assets/rshb/pay/pay_by_phone_complete_wait.png deleted file mode 100644 index 771b867..0000000 Binary files a/assets/rshb/pay/pay_by_phone_complete_wait.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_complete_wait.xml b/assets/rshb/pay/pay_by_phone_complete_wait.xml deleted file mode 100644 index 5d20cbc..0000000 --- a/assets/rshb/pay/pay_by_phone_complete_wait.xml +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_complete_wait_expanded.png b/assets/rshb/pay/pay_by_phone_complete_wait_expanded.png deleted file mode 100644 index 727accf..0000000 Binary files a/assets/rshb/pay/pay_by_phone_complete_wait_expanded.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_complete_wait_expanded.xml b/assets/rshb/pay/pay_by_phone_complete_wait_expanded.xml deleted file mode 100644 index 942b0a3..0000000 --- a/assets/rshb/pay/pay_by_phone_complete_wait_expanded.xml +++ /dev/null @@ -1,534 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_history_expanded.png b/assets/rshb/pay/pay_by_phone_history_expanded.png deleted file mode 100644 index b34f930..0000000 Binary files a/assets/rshb/pay/pay_by_phone_history_expanded.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_history_expanded.xml b/assets/rshb/pay/pay_by_phone_history_expanded.xml deleted file mode 100644 index 96ddc16..0000000 --- a/assets/rshb/pay/pay_by_phone_history_expanded.xml +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_input_pincode.png b/assets/rshb/pay/pay_by_phone_input_pincode.png deleted file mode 100644 index 01c29e0..0000000 Binary files a/assets/rshb/pay/pay_by_phone_input_pincode.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_input_pincode.xml b/assets/rshb/pay/pay_by_phone_input_pincode.xml deleted file mode 100644 index 95580cb..0000000 --- a/assets/rshb/pay/pay_by_phone_input_pincode.xml +++ /dev/null @@ -1,233 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_input_sum.png b/assets/rshb/pay/pay_by_phone_input_sum.png deleted file mode 100644 index 4afdace..0000000 Binary files a/assets/rshb/pay/pay_by_phone_input_sum.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_input_sum.xml b/assets/rshb/pay/pay_by_phone_input_sum.xml deleted file mode 100644 index eb99841..0000000 --- a/assets/rshb/pay/pay_by_phone_input_sum.xml +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_ready_to_transfer.png b/assets/rshb/pay/pay_by_phone_ready_to_transfer.png deleted file mode 100644 index c08dd44..0000000 Binary files a/assets/rshb/pay/pay_by_phone_ready_to_transfer.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_ready_to_transfer.xml b/assets/rshb/pay/pay_by_phone_ready_to_transfer.xml deleted file mode 100644 index 2462221..0000000 --- a/assets/rshb/pay/pay_by_phone_ready_to_transfer.xml +++ /dev/null @@ -1,437 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_by_phone_screen.png b/assets/rshb/pay/pay_by_phone_screen.png deleted file mode 100644 index 166bc31..0000000 Binary files a/assets/rshb/pay/pay_by_phone_screen.png and /dev/null differ diff --git a/assets/rshb/pay/pay_by_phone_screen.xml b/assets/rshb/pay/pay_by_phone_screen.xml deleted file mode 100644 index c3ced14..0000000 --- a/assets/rshb/pay/pay_by_phone_screen.xml +++ /dev/null @@ -1,430 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pay/pay_variants.png b/assets/rshb/pay/pay_variants.png deleted file mode 100644 index 72664c8..0000000 Binary files a/assets/rshb/pay/pay_variants.png and /dev/null differ diff --git a/assets/rshb/pay/pay_variants.xml b/assets/rshb/pay/pay_variants.xml deleted file mode 100644 index 503b351..0000000 --- a/assets/rshb/pay/pay_variants.xml +++ /dev/null @@ -1,635 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/pin_code.png b/assets/rshb/pin_code.png deleted file mode 100644 index 4e88831..0000000 Binary files a/assets/rshb/pin_code.png and /dev/null differ diff --git a/assets/rshb/pin_code.xml b/assets/rshb/pin_code.xml deleted file mode 100644 index 1233ccf..0000000 --- a/assets/rshb/pin_code.xml +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/start_security_banner.png b/assets/rshb/start_security_banner.png deleted file mode 100644 index 9fce31b..0000000 Binary files a/assets/rshb/start_security_banner.png and /dev/null differ diff --git a/assets/rshb/start_security_banner.xml b/assets/rshb/start_security_banner.xml deleted file mode 100644 index 45e6b56..0000000 --- a/assets/rshb/start_security_banner.xml +++ /dev/null @@ -1,310 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/story_banner.png b/assets/rshb/story_banner.png deleted file mode 100644 index 55588f3..0000000 Binary files a/assets/rshb/story_banner.png and /dev/null differ diff --git a/assets/rshb/story_banner.xml b/assets/rshb/story_banner.xml deleted file mode 100644 index ff41760..0000000 --- a/assets/rshb/story_banner.xml +++ /dev/null @@ -1,1312 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/test/card_test_recive_history.png b/assets/rshb/test/card_test_recive_history.png deleted file mode 100644 index ac4b880..0000000 Binary files a/assets/rshb/test/card_test_recive_history.png and /dev/null differ diff --git a/assets/rshb/test/card_test_recive_history.xml b/assets/rshb/test/card_test_recive_history.xml deleted file mode 100644 index 8d9fb41..0000000 --- a/assets/rshb/test/card_test_recive_history.xml +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/test/card_test_send_history.png b/assets/rshb/test/card_test_send_history.png deleted file mode 100644 index a886096..0000000 Binary files a/assets/rshb/test/card_test_send_history.png and /dev/null differ diff --git a/assets/rshb/test/card_test_send_history.xml b/assets/rshb/test/card_test_send_history.xml deleted file mode 100644 index 573822f..0000000 --- a/assets/rshb/test/card_test_send_history.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/rshb/test/spb_test_recive_history.png b/assets/rshb/test/spb_test_recive_history.png deleted file mode 100644 index 39b20af..0000000 Binary files a/assets/rshb/test/spb_test_recive_history.png and /dev/null differ diff --git a/assets/rshb/test/spb_test_recive_history.xml b/assets/rshb/test/spb_test_recive_history.xml deleted file mode 100644 index 2b79195..0000000 --- a/assets/rshb/test/spb_test_recive_history.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/rshb/test/spb_test_send.xml b/assets/rshb/test/spb_test_send.xml deleted file mode 100644 index 5678ca4..0000000 --- a/assets/rshb/test/spb_test_send.xml +++ /dev/null @@ -1,533 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/rshb/test/spb_test_send_history.png b/assets/rshb/test/spb_test_send_history.png deleted file mode 100644 index 08aec1c..0000000 Binary files a/assets/rshb/test/spb_test_send_history.png and /dev/null differ diff --git a/assets/rshb/test/spb_test_send_history.xml b/assets/rshb/test/spb_test_send_history.xml deleted file mode 100644 index fb6eaff..0000000 --- a/assets/rshb/test/spb_test_send_history.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/rshb/test/test_test.xml b/assets/rshb/test/test_test.xml deleted file mode 100644 index 22b6fad..0000000 --- a/assets/rshb/test/test_test.xml +++ /dev/null @@ -1,1000 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/bank_main_screen.png b/assets/toss/bank_main_screen.png deleted file mode 100644 index 30ee17b..0000000 Binary files a/assets/toss/bank_main_screen.png and /dev/null differ diff --git a/assets/toss/bank_main_screen.xml b/assets/toss/bank_main_screen.xml deleted file mode 100644 index 5af0d02..0000000 --- a/assets/toss/bank_main_screen.xml +++ /dev/null @@ -1,738 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/confirmation_oper.png b/assets/toss/confirmation_oper.png deleted file mode 100644 index e05d0d4..0000000 Binary files a/assets/toss/confirmation_oper.png and /dev/null differ diff --git a/assets/toss/confirmation_oper.xml b/assets/toss/confirmation_oper.xml deleted file mode 100644 index bf71ecc..0000000 --- a/assets/toss/confirmation_oper.xml +++ /dev/null @@ -1,716 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/enter_amount.png b/assets/toss/enter_amount.png deleted file mode 100644 index c1029e6..0000000 Binary files a/assets/toss/enter_amount.png and /dev/null differ diff --git a/assets/toss/enter_amount.xml b/assets/toss/enter_amount.xml deleted file mode 100644 index 26710ad..0000000 --- a/assets/toss/enter_amount.xml +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/enter_amount_error.png b/assets/toss/enter_amount_error.png deleted file mode 100644 index e21e41f..0000000 Binary files a/assets/toss/enter_amount_error.png and /dev/null differ diff --git a/assets/toss/enter_amount_error.xml b/assets/toss/enter_amount_error.xml deleted file mode 100644 index e64258c..0000000 --- a/assets/toss/enter_amount_error.xml +++ /dev/null @@ -1,442 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/entered_amount.png b/assets/toss/entered_amount.png deleted file mode 100644 index 791a96d..0000000 Binary files a/assets/toss/entered_amount.png and /dev/null differ diff --git a/assets/toss/entered_amount.xml b/assets/toss/entered_amount.xml deleted file mode 100644 index e59b297..0000000 --- a/assets/toss/entered_amount.xml +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/home_screen.png b/assets/toss/home_screen.png deleted file mode 100644 index 650a0be..0000000 Binary files a/assets/toss/home_screen.png and /dev/null differ diff --git a/assets/toss/home_screen.xml b/assets/toss/home_screen.xml deleted file mode 100644 index 4c77e04..0000000 --- a/assets/toss/home_screen.xml +++ /dev/null @@ -1,1401 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/inputed_account_Number.png b/assets/toss/inputed_account_Number.png deleted file mode 100644 index 0d04157..0000000 Binary files a/assets/toss/inputed_account_Number.png and /dev/null differ diff --git a/assets/toss/inputed_account_Number.xml b/assets/toss/inputed_account_Number.xml deleted file mode 100644 index 5f79f7a..0000000 --- a/assets/toss/inputed_account_Number.xml +++ /dev/null @@ -1,209 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/last_step.png b/assets/toss/last_step.png deleted file mode 100644 index 4c9899d..0000000 Binary files a/assets/toss/last_step.png and /dev/null differ diff --git a/assets/toss/last_step.xml b/assets/toss/last_step.xml deleted file mode 100644 index 23ec943..0000000 --- a/assets/toss/last_step.xml +++ /dev/null @@ -1,353 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/pin_code.xml b/assets/toss/pin_code.xml deleted file mode 100644 index c452fa7..0000000 --- a/assets/toss/pin_code.xml +++ /dev/null @@ -1,350 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/pop_up_win.png b/assets/toss/pop_up_win.png deleted file mode 100644 index c668895..0000000 Binary files a/assets/toss/pop_up_win.png and /dev/null differ diff --git a/assets/toss/pop_up_win.xml b/assets/toss/pop_up_win.xml deleted file mode 100644 index 36f455f..0000000 --- a/assets/toss/pop_up_win.xml +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/select_bank.png b/assets/toss/select_bank.png deleted file mode 100644 index e2f3e61..0000000 Binary files a/assets/toss/select_bank.png and /dev/null differ diff --git a/assets/toss/select_bank.xml b/assets/toss/select_bank.xml deleted file mode 100644 index 9443bb9..0000000 --- a/assets/toss/select_bank.xml +++ /dev/null @@ -1,814 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/select_recipient.png b/assets/toss/select_recipient.png deleted file mode 100644 index 0a8d8ff..0000000 Binary files a/assets/toss/select_recipient.png and /dev/null differ diff --git a/assets/toss/select_recipient.xml b/assets/toss/select_recipient.xml deleted file mode 100644 index f1abf8b..0000000 --- a/assets/toss/select_recipient.xml +++ /dev/null @@ -1,689 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/selected_acc_bank.png b/assets/toss/selected_acc_bank.png deleted file mode 100644 index e8f32f9..0000000 Binary files a/assets/toss/selected_acc_bank.png and /dev/null differ diff --git a/assets/toss/selected_acc_bank.xml b/assets/toss/selected_acc_bank.xml deleted file mode 100644 index bcb2659..0000000 --- a/assets/toss/selected_acc_bank.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/send_operation.png b/assets/toss/send_operation.png deleted file mode 100644 index a2dff27..0000000 Binary files a/assets/toss/send_operation.png and /dev/null differ diff --git a/assets/toss/send_operation.xml b/assets/toss/send_operation.xml deleted file mode 100644 index 781e5dd..0000000 --- a/assets/toss/send_operation.xml +++ /dev/null @@ -1,594 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/send_operation_1.png b/assets/toss/send_operation_1.png deleted file mode 100644 index 4a3d9dd..0000000 Binary files a/assets/toss/send_operation_1.png and /dev/null differ diff --git a/assets/toss/send_operation_1.xml b/assets/toss/send_operation_1.xml deleted file mode 100644 index b456ba8..0000000 --- a/assets/toss/send_operation_1.xml +++ /dev/null @@ -1,685 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/toss_banks.png b/assets/toss/toss_banks.png deleted file mode 100644 index 21fe6dc..0000000 Binary files a/assets/toss/toss_banks.png and /dev/null differ diff --git a/assets/toss/toss_banks.xml b/assets/toss/toss_banks.xml deleted file mode 100644 index 843b677..0000000 --- a/assets/toss/toss_banks.xml +++ /dev/null @@ -1,945 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/toss_banks_2.xml b/assets/toss/toss_banks_2.xml deleted file mode 100644 index ab72765..0000000 --- a/assets/toss/toss_banks_2.xml +++ /dev/null @@ -1,962 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/toss/win_after_pass.png b/assets/toss/win_after_pass.png deleted file mode 100644 index 89a65f8..0000000 Binary files a/assets/toss/win_after_pass.png and /dev/null differ diff --git a/assets/toss/win_after_pass.xml b/assets/toss/win_after_pass.xml deleted file mode 100644 index 52fccc1..0000000 --- a/assets/toss/win_after_pass.xml +++ /dev/null @@ -1,427 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/10_trans_detail.jpg b/assets/ziraat/10_trans_detail.jpg deleted file mode 100644 index 1d4bfe9..0000000 Binary files a/assets/ziraat/10_trans_detail.jpg and /dev/null differ diff --git a/assets/ziraat/10_trans_detail.xml b/assets/ziraat/10_trans_detail.xml deleted file mode 100644 index 5a464d2..0000000 --- a/assets/ziraat/10_trans_detail.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/11_trans_receipt.jpg b/assets/ziraat/11_trans_receipt.jpg deleted file mode 100644 index f5f9c9d..0000000 Binary files a/assets/ziraat/11_trans_receipt.jpg and /dev/null differ diff --git a/assets/ziraat/11_trans_receipt.xml b/assets/ziraat/11_trans_receipt.xml deleted file mode 100644 index 10d99c1..0000000 --- a/assets/ziraat/11_trans_receipt.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/12_trans_receipt1.xml b/assets/ziraat/12_trans_receipt1.xml deleted file mode 100644 index 3966f94..0000000 --- a/assets/ziraat/12_trans_receipt1.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/26_outgoing_transfer_IBAN.jpg b/assets/ziraat/26_outgoing_transfer_IBAN.jpg deleted file mode 100644 index f761251..0000000 Binary files a/assets/ziraat/26_outgoing_transfer_IBAN.jpg and /dev/null differ diff --git a/assets/ziraat/27_outgoing_transfer_acc_num.jpg b/assets/ziraat/27_outgoing_transfer_acc_num.jpg deleted file mode 100644 index 0f56656..0000000 Binary files a/assets/ziraat/27_outgoing_transfer_acc_num.jpg and /dev/null differ diff --git a/assets/ziraat/27_outgoing_transfer_acc_num.xml b/assets/ziraat/27_outgoing_transfer_acc_num.xml deleted file mode 100644 index f76e981..0000000 --- a/assets/ziraat/27_outgoing_transfer_acc_num.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/31_trans_warning_after_step_3.jpg b/assets/ziraat/31_trans_warning_after_step_3.jpg deleted file mode 100644 index 91c2fd4..0000000 Binary files a/assets/ziraat/31_trans_warning_after_step_3.jpg and /dev/null differ diff --git a/assets/ziraat/32_trans_error.jpg b/assets/ziraat/32_trans_error.jpg deleted file mode 100644 index b9111bb..0000000 Binary files a/assets/ziraat/32_trans_error.jpg and /dev/null differ diff --git a/assets/ziraat/32_trans_error.xml b/assets/ziraat/32_trans_error.xml deleted file mode 100644 index 7dd2411..0000000 --- a/assets/ziraat/32_trans_error.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/33_trans_fast_transfer.jpg b/assets/ziraat/33_trans_fast_transfer.jpg deleted file mode 100644 index 7ede9f7..0000000 Binary files a/assets/ziraat/33_trans_fast_transfer.jpg and /dev/null differ diff --git a/assets/ziraat/34_trans_with_IBAN_step_4.jpg b/assets/ziraat/34_trans_with_IBAN_step_4.jpg deleted file mode 100644 index 1bf6273..0000000 Binary files a/assets/ziraat/34_trans_with_IBAN_step_4.jpg and /dev/null differ diff --git a/assets/ziraat/35_trans_type.jpg b/assets/ziraat/35_trans_type.jpg deleted file mode 100644 index 88b2b7a..0000000 Binary files a/assets/ziraat/35_trans_type.jpg and /dev/null differ diff --git a/assets/ziraat/35_trans_type.xml b/assets/ziraat/35_trans_type.xml deleted file mode 100644 index 6c458bf..0000000 --- a/assets/ziraat/35_trans_type.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/36_trans_recipient.jpg b/assets/ziraat/36_trans_recipient.jpg deleted file mode 100644 index f5a976d..0000000 Binary files a/assets/ziraat/36_trans_recipient.jpg and /dev/null differ diff --git a/assets/ziraat/37_trans_recipient1.jpg b/assets/ziraat/37_trans_recipient1.jpg deleted file mode 100644 index 8b55354..0000000 Binary files a/assets/ziraat/37_trans_recipient1.jpg and /dev/null differ diff --git a/assets/ziraat/4_all_my_accounts.jpg b/assets/ziraat/4_all_my_accounts.jpg deleted file mode 100644 index 6de147d..0000000 Binary files a/assets/ziraat/4_all_my_accounts.jpg and /dev/null differ diff --git a/assets/ziraat/5_account_detail.jpg b/assets/ziraat/5_account_detail.jpg deleted file mode 100644 index 578f22f..0000000 Binary files a/assets/ziraat/5_account_detail.jpg and /dev/null differ diff --git a/assets/ziraat/6_account_activities.jpg b/assets/ziraat/6_account_activities.jpg deleted file mode 100644 index 6582ed4..0000000 Binary files a/assets/ziraat/6_account_activities.jpg and /dev/null differ diff --git a/assets/ziraat/7_acc_activ_period.jpg b/assets/ziraat/7_acc_activ_period.jpg deleted file mode 100644 index 78828e7..0000000 Binary files a/assets/ziraat/7_acc_activ_period.jpg and /dev/null differ diff --git a/assets/ziraat/9_acc_activ_send.jpg b/assets/ziraat/9_acc_activ_send.jpg deleted file mode 100644 index 8753299..0000000 Binary files a/assets/ziraat/9_acc_activ_send.jpg and /dev/null differ diff --git a/assets/ziraat/9_acc_activ_send.xml b/assets/ziraat/9_acc_activ_send.xml deleted file mode 100644 index 5e4ccb5..0000000 --- a/assets/ziraat/9_acc_activ_send.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/acc_activ_filter.jpg b/assets/ziraat/acc_activ_filter.jpg deleted file mode 100644 index 0221498..0000000 Binary files a/assets/ziraat/acc_activ_filter.jpg and /dev/null differ diff --git a/assets/ziraat/acc_activ_filter.xml b/assets/ziraat/acc_activ_filter.xml deleted file mode 100644 index 5fba7e0..0000000 --- a/assets/ziraat/acc_activ_filter.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/acc_activ_period.xml b/assets/ziraat/acc_activ_period.xml deleted file mode 100644 index 06f6195..0000000 --- a/assets/ziraat/acc_activ_period.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/account_activities.xml b/assets/ziraat/account_activities.xml deleted file mode 100644 index 19a3183..0000000 --- a/assets/ziraat/account_activities.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/account_detail.xml b/assets/ziraat/account_detail.xml deleted file mode 100644 index c9ece24..0000000 --- a/assets/ziraat/account_detail.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/account_detail2.jpg b/assets/ziraat/account_detail2.jpg deleted file mode 100644 index c143ae1..0000000 Binary files a/assets/ziraat/account_detail2.jpg and /dev/null differ diff --git a/assets/ziraat/account_detail2.xml b/assets/ziraat/account_detail2.xml deleted file mode 100644 index f33d72b..0000000 --- a/assets/ziraat/account_detail2.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/all_my_accounts.xml b/assets/ziraat/all_my_accounts.xml deleted file mode 100644 index 03a8fec..0000000 --- a/assets/ziraat/all_my_accounts.xml +++ /dev/null @@ -1,816 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/details_trans_1.xml b/assets/ziraat/details_trans_1.xml deleted file mode 100644 index 1d07d5f..0000000 --- a/assets/ziraat/details_trans_1.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/details_trans_2.xml b/assets/ziraat/details_trans_2.xml deleted file mode 100644 index ee3ccd4..0000000 --- a/assets/ziraat/details_trans_2.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/enter_password.jpg b/assets/ziraat/enter_password.jpg deleted file mode 100644 index a40e615..0000000 Binary files a/assets/ziraat/enter_password.jpg and /dev/null differ diff --git a/assets/ziraat/enter_password.xml b/assets/ziraat/enter_password.xml deleted file mode 100644 index 6815646..0000000 --- a/assets/ziraat/enter_password.xml +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/home_window.jpg b/assets/ziraat/home_window.jpg deleted file mode 100644 index 392a6e2..0000000 Binary files a/assets/ziraat/home_window.jpg and /dev/null differ diff --git a/assets/ziraat/home_window.xml b/assets/ziraat/home_window.xml deleted file mode 100644 index 0f846f6..0000000 --- a/assets/ziraat/home_window.xml +++ /dev/null @@ -1,1156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/login_window.jpg b/assets/ziraat/login_window.jpg deleted file mode 100644 index 835d314..0000000 Binary files a/assets/ziraat/login_window.jpg and /dev/null differ diff --git a/assets/ziraat/login_window.xml b/assets/ziraat/login_window.xml deleted file mode 100644 index 3e9293f..0000000 --- a/assets/ziraat/login_window.xml +++ /dev/null @@ -1,860 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/money_transfers.jpg b/assets/ziraat/money_transfers.jpg deleted file mode 100644 index 9f4a216..0000000 Binary files a/assets/ziraat/money_transfers.jpg and /dev/null differ diff --git a/assets/ziraat/money_transfers.xml b/assets/ziraat/money_transfers.xml deleted file mode 100644 index d8a290c..0000000 --- a/assets/ziraat/money_transfers.xml +++ /dev/null @@ -1,1372 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/photo_5359685578850956204_y.jpg b/assets/ziraat/photo_5359685578850956204_y.jpg deleted file mode 100644 index ba3db41..0000000 Binary files a/assets/ziraat/photo_5359685578850956204_y.jpg and /dev/null differ diff --git a/assets/ziraat/status1.xml b/assets/ziraat/status1.xml deleted file mode 100644 index 198e23e..0000000 --- a/assets/ziraat/status1.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/test2.xml b/assets/ziraat/test2.xml deleted file mode 100644 index 46d2510..0000000 --- a/assets/ziraat/test2.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/trans_after_outgoing.xml b/assets/ziraat/trans_after_outgoing.xml deleted file mode 100644 index 9d95389..0000000 --- a/assets/ziraat/trans_after_outgoing.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/trans_drop_down_list_1.jpg b/assets/ziraat/trans_drop_down_list_1.jpg deleted file mode 100644 index f40b490..0000000 Binary files a/assets/ziraat/trans_drop_down_list_1.jpg and /dev/null differ diff --git a/assets/ziraat/trans_drop_down_list_1.xml b/assets/ziraat/trans_drop_down_list_1.xml deleted file mode 100644 index 02c35a1..0000000 --- a/assets/ziraat/trans_drop_down_list_1.xml +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/trans_drop_down_list_2.jpg b/assets/ziraat/trans_drop_down_list_2.jpg deleted file mode 100644 index a4fa85d..0000000 Binary files a/assets/ziraat/trans_drop_down_list_2.jpg and /dev/null differ diff --git a/assets/ziraat/trans_drop_down_list_2.xml b/assets/ziraat/trans_drop_down_list_2.xml deleted file mode 100644 index 27e3484..0000000 --- a/assets/ziraat/trans_drop_down_list_2.xml +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/trans_fast_transfer.xml b/assets/ziraat/trans_fast_transfer.xml deleted file mode 100644 index fa99b0c..0000000 --- a/assets/ziraat/trans_fast_transfer.xml +++ /dev/null @@ -1,2436 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/trans_recipient.xml b/assets/ziraat/trans_recipient.xml deleted file mode 100644 index 29983b7..0000000 --- a/assets/ziraat/trans_recipient.xml +++ /dev/null @@ -1,3474 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/trans_recipient1.xml b/assets/ziraat/trans_recipient1.xml deleted file mode 100644 index d755744..0000000 --- a/assets/ziraat/trans_recipient1.xml +++ /dev/null @@ -1,3339 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/trans_succesful.jpg b/assets/ziraat/trans_succesful.jpg deleted file mode 100644 index 9ac3f47..0000000 Binary files a/assets/ziraat/trans_succesful.jpg and /dev/null differ diff --git a/assets/ziraat/trans_succesful.xml b/assets/ziraat/trans_succesful.xml deleted file mode 100644 index 1279294..0000000 --- a/assets/ziraat/trans_succesful.xml +++ /dev/null @@ -1,3563 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/trans_warning_after_step_3.xml b/assets/ziraat/trans_warning_after_step_3.xml deleted file mode 100644 index c545afa..0000000 --- a/assets/ziraat/trans_warning_after_step_3.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/ziraat/transactions.jpg b/assets/ziraat/transactions.jpg deleted file mode 100644 index c17c771..0000000 Binary files a/assets/ziraat/transactions.jpg and /dev/null differ diff --git a/assets/ziraat/transactions.xml b/assets/ziraat/transactions.xml deleted file mode 100644 index ef1e8d5..0000000 --- a/assets/ziraat/transactions.xml +++ /dev/null @@ -1,1162 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transactions_2.jpg b/assets/ziraat/transactions_2.jpg deleted file mode 100644 index 47d035e..0000000 Binary files a/assets/ziraat/transactions_2.jpg and /dev/null differ diff --git a/assets/ziraat/transactions_2.xml b/assets/ziraat/transactions_2.xml deleted file mode 100644 index 3eb23ef..0000000 --- a/assets/ziraat/transactions_2.xml +++ /dev/null @@ -1,624 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transfer_by_IBAN_step_1.jpg b/assets/ziraat/transfer_by_IBAN_step_1.jpg deleted file mode 100644 index 0cd08dc..0000000 Binary files a/assets/ziraat/transfer_by_IBAN_step_1.jpg and /dev/null differ diff --git a/assets/ziraat/transfer_by_IBAN_step_1.xml b/assets/ziraat/transfer_by_IBAN_step_1.xml deleted file mode 100644 index ac87659..0000000 --- a/assets/ziraat/transfer_by_IBAN_step_1.xml +++ /dev/null @@ -1,699 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transfer_by_IBAN_step_1_empty_iban.xml b/assets/ziraat/transfer_by_IBAN_step_1_empty_iban.xml deleted file mode 100644 index 82336b3..0000000 --- a/assets/ziraat/transfer_by_IBAN_step_1_empty_iban.xml +++ /dev/null @@ -1,592 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transfer_by_IBAN_step_2.jpg b/assets/ziraat/transfer_by_IBAN_step_2.jpg deleted file mode 100644 index 9909c50..0000000 Binary files a/assets/ziraat/transfer_by_IBAN_step_2.jpg and /dev/null differ diff --git a/assets/ziraat/transfer_by_IBAN_step_2.xml b/assets/ziraat/transfer_by_IBAN_step_2.xml deleted file mode 100644 index 5c78e97..0000000 --- a/assets/ziraat/transfer_by_IBAN_step_2.xml +++ /dev/null @@ -1,1390 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transfer_by_IBAN_step_3.jpg b/assets/ziraat/transfer_by_IBAN_step_3.jpg deleted file mode 100644 index 9273331..0000000 Binary files a/assets/ziraat/transfer_by_IBAN_step_3.jpg and /dev/null differ diff --git a/assets/ziraat/transfer_by_IBAN_step_3.xml b/assets/ziraat/transfer_by_IBAN_step_3.xml deleted file mode 100644 index 0bd1a9f..0000000 --- a/assets/ziraat/transfer_by_IBAN_step_3.xml +++ /dev/null @@ -1,2059 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transfer_by_IBAN_step_4.xml b/assets/ziraat/transfer_by_IBAN_step_4.xml deleted file mode 100644 index e354110..0000000 --- a/assets/ziraat/transfer_by_IBAN_step_4.xml +++ /dev/null @@ -1,2807 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/transfer_to_other_acc.jpg b/assets/ziraat/transfer_to_other_acc.jpg deleted file mode 100644 index b5f9c03..0000000 Binary files a/assets/ziraat/transfer_to_other_acc.jpg and /dev/null differ diff --git a/assets/ziraat/transfer_to_other_acc.xml b/assets/ziraat/transfer_to_other_acc.xml deleted file mode 100644 index cdbe73c..0000000 --- a/assets/ziraat/transfer_to_other_acc.xml +++ /dev/null @@ -1,974 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/ziraat/window_after_autho.xml b/assets/ziraat/window_after_autho.xml deleted file mode 100644 index 1676cf4..0000000 --- a/assets/ziraat/window_after_autho.xml +++ /dev/null @@ -1,174 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config.ini b/config.ini index 542f5d6..2d9cad1 100644 --- a/config.ini +++ b/config.ini @@ -29,13 +29,11 @@ transactionUpdate=http://localhost:9999/api/v1/transaction/update android_package_name=app.blackwallet.ar currency=ARS name=Black +phone_code=54 [ozon] android_package_name=ru.ozon.fintech.finance currency=RUB name=Ozon +phone_code=7 -[rshb] -android_package_name=ru.rshb.dbo -currency=RUB -name=Росcельхозбанк diff --git a/database/DatabaseManager.cpp b/database/DatabaseManager.cpp index a86a70d..97d118e 100644 --- a/database/DatabaseManager.cpp +++ b/database/DatabaseManager.cpp @@ -12,7 +12,7 @@ DatabaseManager::DatabaseManager() = default; DatabaseManager::~DatabaseManager() { - QMutexLocker locker(&mutex); + QMutexLocker locker(&mutex); // QSqlDatabase требует живого QCoreApplication — при завершении // статический синглтон может разрушаться после QApplication @@ -34,7 +34,7 @@ DatabaseManager &DatabaseManager::instance() { } QSqlDatabase DatabaseManager::database() { - QMutexLocker locker(&mutex); + QMutexLocker locker(&mutex); QString name = connectionName(); // Создаем новое соединение, если его ещё нет @@ -56,11 +56,15 @@ QSqlDatabase DatabaseManager::database() { qWarning() << "Ошибка открытия базы данных:" << db.lastError().text(); } + // Включаем каскадное удаление (FK) + QSqlQuery pragmaQuery(db); + pragmaQuery.exec("PRAGMA foreign_keys = ON"); + connectionPool[name] = db; // Автоматическое удаление соединения при завершении потока QObject::connect(QThread::currentThread(), &QThread::finished, [this, name]() { - QMutexLocker cLocker(&mutex); + QMutexLocker cLocker(&mutex); if (connectionPool.contains(name)) { connectionPool.remove(name); QSqlDatabase::removeDatabase(name); @@ -233,10 +237,26 @@ void DatabaseManager::initSchema() { q.exec("PRAGMA user_version = 2"); qDebug() << "[DB] Migration 2 applied"; } + + // ── Версия 3: таблица general_logs для всех Qt-логов ──────────────────── + if (version < 3) { + db.transaction(); + q.exec(R"(CREATE TABLE IF NOT EXISTS general_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + level TEXT NOT NULL DEFAULT '', + source TEXT NOT NULL DEFAULT '', + message TEXT NOT NULL, + timestamp TEXT NOT NULL + ))"); + q.exec("CREATE INDEX IF NOT EXISTS idx_general_logs_id ON general_logs (id DESC)"); + db.commit(); + q.exec("PRAGMA user_version = 3"); + qDebug() << "[DB] Migration 3 applied"; + } } void DatabaseManager::closeConnection() { - QMutexLocker locker(&mutex); + QMutexLocker locker(&mutex); if (const QString name = connectionName(); connectionPool.contains(name)) { connectionPool.remove(name); QSqlDatabase::removeDatabase(name); diff --git a/database/DatabaseManager.h b/database/DatabaseManager.h index 8094b8a..bdeea3d 100644 --- a/database/DatabaseManager.h +++ b/database/DatabaseManager.h @@ -20,7 +20,7 @@ private: static QString connectionName(); - QMutex mutex; + QRecursiveMutex mutex; QSet activeConnections; QMap connectionPool; }; \ No newline at end of file diff --git a/database/dao/AccountDAO.cpp b/database/dao/AccountDAO.cpp index 09bd502..45acfad 100644 --- a/database/dao/AccountDAO.cpp +++ b/database/dao/AccountDAO.cpp @@ -243,6 +243,20 @@ QStringList AccountDAO::getActiveMaterialIds() { return ids; } +QStringList AccountDAO::getActiveMaterialIdsExcludingDevices(const QSet &excludeDevices) { + const QStringList all = getActiveMaterialIds(); + if (excludeDevices.isEmpty()) return all; + + QStringList filtered; + for (const QString &mid : all) { + const AccountInfo acc = findByMaterialId(mid); + if (!excludeDevices.contains(acc.deviceId)) { + filtered << mid; + } + } + return filtered; +} + AccountInfo AccountDAO::findByMaterialId(const QString &materialId) { QSqlQuery query(DatabaseManager::instance().database()); diff --git a/database/dao/AccountDAO.h b/database/dao/AccountDAO.h index 30d05ad..9927a98 100644 --- a/database/dao/AccountDAO.h +++ b/database/dao/AccountDAO.h @@ -1,4 +1,5 @@ #pragma once +#include #include "db/AccountInfo.h" class AccountDAO { @@ -33,4 +34,6 @@ public: [[nodiscard]] static AccountInfo findByMaterialId(const QString &materialId); [[nodiscard]] static QStringList getActiveMaterialIds(); + + [[nodiscard]] static QStringList getActiveMaterialIdsExcludingDevices(const QSet &excludeDevices); }; diff --git a/database/dao/ApplicationDAO.cpp b/database/dao/ApplicationDAO.cpp index d0ff368..156d72e 100644 --- a/database/dao/ApplicationDAO.cpp +++ b/database/dao/ApplicationDAO.cpp @@ -233,6 +233,25 @@ bool ApplicationDAO::updateBankProfileId(const int appId, const QString &bankPro return true; } +QList ApplicationDAO::findAllByBankProfileId(const QString &bankProfileId) { + QList list; + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare(R"( + SELECT id, device_id, pin_code, code, name, package, status, comment, install, pin_code_checked, phone, email, full_name, bank_profile_id, currency, amount + FROM applications + WHERE bank_profile_id = :bank_profile_id + )"); + query.bindValue(":bank_profile_id", bankProfileId); + if (!query.exec()) { + qWarning() << "Ошибка при поиске applications по bank_profile_id:" << query.lastError().text(); + return list; + } + while (query.next()) { + list.append(parseApplication(query)); + } + return list; +} + bool ApplicationDAO::activateAppsForDevice(const QString &deviceId) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( diff --git a/database/dao/ApplicationDAO.h b/database/dao/ApplicationDAO.h index dc9677f..06553de 100644 --- a/database/dao/ApplicationDAO.h +++ b/database/dao/ApplicationDAO.h @@ -30,6 +30,8 @@ public: [[nodiscard]] static bool updateBankProfileId(int appId, const QString &bankProfileId); + [[nodiscard]] static QList findAllByBankProfileId(const QString &bankProfileId); + [[nodiscard]] static bool updateAmount(int appId, double amount); [[nodiscard]] static ApplicationInfo getApplication(const QString &deviceId, const QString &appCode); diff --git a/database/dao/DeviceDAO.cpp b/database/dao/DeviceDAO.cpp index 3150154..29aed15 100644 --- a/database/dao/DeviceDAO.cpp +++ b/database/dao/DeviceDAO.cpp @@ -192,6 +192,18 @@ bool DeviceDAO::updateApiId(const QString &deviceId, const QString &apiId) { return true; } +bool DeviceDAO::deleteDevice(const QString &deviceId) { + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare("DELETE FROM devices WHERE id = :id"); + query.bindValue(":id", deviceId); + + if (!query.exec()) { + qWarning() << "Ошибка при удалении устройства:" << query.lastError().text(); + return false; + } + return true; +} + bool DeviceDAO::updateImage(const QString &deviceId, const QByteArray &imageData) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare("UPDATE devices SET image = :image WHERE id = :id"); diff --git a/database/dao/DeviceDAO.h b/database/dao/DeviceDAO.h index ca68093..fdadb52 100644 --- a/database/dao/DeviceDAO.h +++ b/database/dao/DeviceDAO.h @@ -17,4 +17,6 @@ public: [[nodiscard]] static QList getAllDevices(bool online); [[nodiscard]] static QList getOnlineDeviceCodes(); + + [[nodiscard]] static bool deleteDevice(const QString &deviceId); }; diff --git a/database/dao/EventDAO.cpp b/database/dao/EventDAO.cpp index d6989bc..bd8c279 100644 --- a/database/dao/EventDAO.cpp +++ b/database/dao/EventDAO.cpp @@ -211,6 +211,37 @@ int EventDAO::getTotalCount() { return 0; } +bool EventDAO::hasPendingEvents() { + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare(R"( + SELECT 1 FROM events + WHERE status IN ('wait', 'iPprogress') + LIMIT 1 + )"); + if (!query.exec()) { + qCritical() << "Ошибка при hasPendingEvents:" << query.lastError().text(); + return false; + } + return query.next(); +} + +QSet EventDAO::getDevicesWithPendingEvents() { + QSet devices; + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare(R"( + SELECT DISTINCT device_id FROM events + WHERE status IN ('wait', 'iPprogress') + )"); + if (!query.exec()) { + qCritical() << "Ошибка при getDevicesWithPendingEvents:" << query.lastError().text(); + return devices; + } + while (query.next()) { + devices.insert(query.value(0).toString()); + } + return devices; +} + bool EventDAO::existsActiveByExternalId(const QString &externalId) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( diff --git a/database/dao/EventDAO.h b/database/dao/EventDAO.h index 45b3fdc..467d2e8 100644 --- a/database/dao/EventDAO.h +++ b/database/dao/EventDAO.h @@ -1,5 +1,6 @@ #pragma once +#include #include class EventDAO { @@ -25,4 +26,8 @@ public: [[nodiscard]] static QList getEvents(int page, int pageSize); [[nodiscard]] static int getTotalCount(); + + [[nodiscard]] static bool hasPendingEvents(); + + [[nodiscard]] static QSet getDevicesWithPendingEvents(); }; diff --git a/database/dao/GeneralLogDAO.cpp b/database/dao/GeneralLogDAO.cpp new file mode 100644 index 0000000..9700993 --- /dev/null +++ b/database/dao/GeneralLogDAO.cpp @@ -0,0 +1,65 @@ +#include "GeneralLogDAO.h" + +#include +#include + +#include "DatabaseManager.h" +#include "time/DateUtils.h" + +bool GeneralLogDAO::insert(const QString &level, const QString &source, const QString &message) { + QSqlQuery q(DatabaseManager::instance().database()); + q.prepare(R"( + INSERT INTO general_logs (level, source, message, timestamp) + VALUES (:level, :source, :message, :timestamp) + )"); + q.bindValue(":level", level); + q.bindValue(":source", source); + q.bindValue(":message", message); + q.bindValue(":timestamp", DateUtils::toUtcString(QDateTime::currentDateTimeUtc())); + return q.exec(); +} + +static QString buildWhereClause(const QStringList &levels) { + if (levels.isEmpty()) return {}; + QStringList placeholders; + for (int i = 0; i < levels.size(); ++i) + placeholders << QString(":lvl%1").arg(i); + return " WHERE level IN (" + placeholders.join(',') + ")"; +} + +static void bindLevels(QSqlQuery &q, const QStringList &levels) { + for (int i = 0; i < levels.size(); ++i) + q.bindValue(QString(":lvl%1").arg(i), levels[i]); +} + +QList GeneralLogDAO::getLogs(int page, int pageSize, const QStringList &levels) { + QList result; + QSqlQuery q(DatabaseManager::instance().database()); + q.prepare( + "SELECT id, level, source, message, timestamp FROM general_logs" + + buildWhereClause(levels) + + " ORDER BY id DESC LIMIT :limit OFFSET :offset" + ); + bindLevels(q, levels); + q.bindValue(":limit", pageSize); + q.bindValue(":offset", page * pageSize); + if (!q.exec()) return result; + while (q.next()) { + GeneralLogEntry e; + e.id = q.value("id").toInt(); + e.level = q.value("level").toString(); + e.source = q.value("source").toString(); + e.message = q.value("message").toString(); + e.timestamp = DateUtils::fromUtcString(q.value("timestamp").toString()); + result.append(e); + } + return result; +} + +int GeneralLogDAO::getTotalCount(const QStringList &levels) { + QSqlQuery q(DatabaseManager::instance().database()); + q.prepare("SELECT COUNT(*) FROM general_logs" + buildWhereClause(levels)); + bindLevels(q, levels); + if (!q.exec() || !q.next()) return 0; + return q.value(0).toInt(); +} diff --git a/database/dao/GeneralLogDAO.h b/database/dao/GeneralLogDAO.h new file mode 100644 index 0000000..fb90ac8 --- /dev/null +++ b/database/dao/GeneralLogDAO.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include + +struct GeneralLogEntry { + int id = -1; + QString level; + QString source; + QString message; + QDateTime timestamp; +}; + +class GeneralLogDAO { +public: + static bool insert(const QString &level, const QString &source, const QString &message); + + [[nodiscard]] static QList getLogs(int page, int pageSize, + const QStringList &levels = {}); + [[nodiscard]] static int getTotalCount(const QStringList &levels = {}); +}; diff --git a/images/delete.png b/images/delete.png new file mode 100644 index 0000000..d93174d Binary files /dev/null and b/images/delete.png differ diff --git a/main.cpp b/main.cpp index ffd23b4..d84866f 100644 --- a/main.cpp +++ b/main.cpp @@ -9,6 +9,7 @@ #include "database/dao/DeviceDAO.h" #include "services/DeviceScreener.h" +#include #include #include #include @@ -18,6 +19,7 @@ #include #include "AppLogger.h" +#include "dao/GeneralLogDAO.h" #include "dao/SettingsDAO.h" #include "EventHandler.h" #include "net/NetworkService.h" @@ -27,6 +29,8 @@ #include "widget/loader/LoaderWindow.h" #include "widget/loader/RegistrationWindow.h" #include +#include +#include void startDeviceScreener(const QCoreApplication &app) { auto *thread = new QThread(); @@ -160,14 +164,14 @@ void startNetworkService(const QCoreApplication &app) { static QFile logFile; static QMutex logMutex; +static std::atomic dbReady{false}; void messageHandler( const QtMsgType type, const QMessageLogContext &context, const QString &msg ) { - Q_UNUSED(context) // можно раскомментировать, если нужны file/line/function - // Формируем строку с типом и временем + Q_UNUSED(context) const QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); QString level; switch (type) { @@ -183,6 +187,7 @@ void messageHandler( break; } + const QString source = context.function ? QString("%1:%2").arg(context.function).arg(context.line) : QString(); const QString line = QString("%1 [%2] [%3:%4] %5\n") .arg(timestamp, level, context.function, QString::number(context.line), msg); @@ -195,6 +200,16 @@ void messageHandler( } } + // Дублируем в БД (с защитой от рекурсии) + if (dbReady.load()) { + static thread_local bool writing = false; + if (!writing) { + writing = true; + GeneralLogDAO::insert(level, source, msg); + writing = false; + } + } + // По желанию — дублировать в консоль const QByteArray localMsg = line.toLocal8Bit(); fprintf(stderr, "%s", localMsg.constData()); @@ -234,8 +249,45 @@ int main(int argc, char *argv[]) { qInstallMessageHandler(messageHandler); + // Проверяем наличие файла БД, если нет — предлагаем загрузить + { + QSettings settings("config.ini", QSettings::IniFormat); + QString dbPath = settings.value("db/dbPath").toString(); + if (dbPath.startsWith("~")) + dbPath.replace(0, 1, QDir::homePath()); + + if (!QFile::exists(dbPath)) { + QMessageBox msgBox; + msgBox.setWindowTitle("База данных не найдена"); + msgBox.setText("Файл базы данных не найден:\n" + dbPath); + QPushButton *selectBtn = msgBox.addButton("Выбрать файл", QMessageBox::AcceptRole); + QPushButton *createBtn = msgBox.addButton("Создать новую", QMessageBox::ActionRole); + QPushButton *exitBtn = msgBox.addButton("Выход", QMessageBox::RejectRole); + msgBox.setDefaultButton(selectBtn); + msgBox.exec(); + + if (msgBox.clickedButton() == selectBtn) { + QString selected = QFileDialog::getOpenFileName( + nullptr, "Выберите файл базы данных", QDir::homePath(), "SQLite (*.db *.sqlite);;Все файлы (*)"); + if (selected.isEmpty()) { + return 0; + } + QDir().mkpath(QFileInfo(dbPath).absolutePath()); + if (!QFile::copy(selected, dbPath)) { + QMessageBox::critical(nullptr, "Ошибка", "Не удалось скопировать файл базы данных."); + return 1; + } + } else if (msgBox.clickedButton() == createBtn) { + // Просто продолжаем — initSchema создаст новую БД + } else { + return 0; + } + } + } + // Миграции БД DatabaseManager::instance().initSchema(); + dbReady.store(true); // 1. Если есть api_key — логинимся и проверяем/создаём desktop // - ошибка сети или сервера [code -1] diff --git a/migrations.sql b/migrations.sql index ab0cc29..c649137 100644 --- a/migrations.sql +++ b/migrations.sql @@ -33,7 +33,7 @@ CREATE TABLE IF NOT EXISTS applications UNIQUE (device_id, code) ); --- app_code: код банка ("ozon", "birbank", ...) — не путать с человекочитаемым name +-- app_code: код банка ("ozon", "black") — не путать с человекочитаемым name CREATE TABLE IF NOT EXISTS accounts ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/services/DeviceScreener.cpp b/services/DeviceScreener.cpp index 63fa5ff..4850d89 100644 --- a/services/DeviceScreener.cpp +++ b/services/DeviceScreener.cpp @@ -260,6 +260,42 @@ void DeviceScreener::toggleBankProfilesOnApi(const QString &deviceId, bool enabl } } +QJsonValue DeviceScreener::apiGet(const QString &path) { + if (!m_manager) m_manager = new QNetworkAccessManager(this); + + QNetworkRequest request(QUrl(m_apiBase + path)); + request.setRawHeader("Authorization", + ("Bearer " + SettingsDAO::get("access_token")).toUtf8()); + + QNetworkReply *reply = m_manager->get(request); + QEventLoop loop; + QTimer timeout; + timeout.setSingleShot(true); + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); }); + connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit); + timeout.start(30000); + loop.exec(); + + QJsonValue result; + if (!timeout.isActive()) { + qCritical() << "DeviceScreener apiGet timeout:" << path; + reply->abort(); + } else if (reply->error() == QNetworkReply::NoError) { + const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); + if (doc.isObject()) { + const QJsonObject root = doc.object(); + if (root["success"].toBool()) result = root["data"]; + else qCritical() << "DeviceScreener apiGet error:" << root; + } + } else { + qCritical() << "DeviceScreener apiGet network error:" << reply->errorString(); + } + reply->deleteLater(); + return result; +} + + void DeviceScreener::start() { while (m_running) { // Устройства, которые были онлайн в БД до текущего скана @@ -283,7 +319,7 @@ void DeviceScreener::start() { } } if (!isChecked) { - // Новое устройство — включаем на сервере только active профили + // Новое устройство — включаем active профили toggleBankProfilesOnApi(id, true); } } @@ -302,7 +338,9 @@ void DeviceScreener::start() { // Устройство ещё не зарегистрировано — создаём const QString apiId = createDeviceOnApi(device); if (!apiId.isEmpty()) { - DeviceDAO::updateApiId(device.id, apiId); + if (!DeviceDAO::updateApiId(device.id, apiId)) { + qWarning() << "Failed to save api_id for device:" << device.id; + } qDebug() << "Device registered on API:" << device.id << "->" << apiId; } } else if (status == "device") { @@ -330,6 +368,9 @@ void DeviceScreener::start() { } } + if (m_firstScan) { + m_firstScan = false; + } QThread::msleep(1000); } emit finished(); diff --git a/services/DeviceScreener.h b/services/DeviceScreener.h index 12c1524..6a23248 100644 --- a/services/DeviceScreener.h +++ b/services/DeviceScreener.h @@ -22,6 +22,7 @@ signals: private: bool m_running = true; + bool m_firstScan = true; QString m_urlAppUpdate; QString m_token; QString m_apiBase; @@ -36,6 +37,7 @@ private: void postDevicesData(const QList &devices); + QJsonValue apiGet(const QString &path); QJsonValue apiPost(const QString &path, const QJsonObject &body); void apiPatch(const QString &path, const QJsonObject &body); diff --git a/services/EventHandler.cpp b/services/EventHandler.cpp index 5c72734..fc333c6 100644 --- a/services/EventHandler.cpp +++ b/services/EventHandler.cpp @@ -19,22 +19,13 @@ #include "db/ApplicationInfo.h" #include "adb/AdbUtils.h" #include "net/NetworkService.h" -#include "birbank/GetLastDaysHistoryBirbankScript.h" -#include "birbank/LoginAndCheckAccountsBirbankScript.h" #include "black/GetLastTransactionsScript.h" #include "black/LoginAndCheckAccountsScript.h" #include "ozon/GetLastTransactionsScript.h" #include "ozon/LoginAndCheckAccountsScript.h" -#include "rshb/GetLastDaysHistoryScript.h" -#include "rshb/LoginAndCheckAccountsScript.h" -#include "toss/GetLastDaysHistoryTossScript.h" -#include "toss/LoginAndCheckAccountsTossScript.h" -#include "ziraat/GetLastDaysHistoryZiraatScript.h" -#include "ziraat/LoginAndCheckAccountsZiraatScript.h" #include "black/PayByCardScript.h" #include "ozon/PayByCardScript.h" #include "ozon/PayByPhoneScript.h" -#include "rshb/PayByPhoneScript.h" static void sendTaskResult(const QString &type, const QString &materialId, const QString &bankName, const QJsonValue &data); static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName, const QString &description); @@ -50,7 +41,20 @@ void EventHandler::start() { m_pollTimer = new QTimer(this); m_pollTimer->setInterval(5000); - connect(m_pollTimer, &QTimer::timeout, m_network, &NetworkService::getTasks); + connect(m_pollTimer, &QTimer::timeout, this, [this]() { + // Пробуем запустить задачи из очереди для свободных устройств + const QStringList onlineDevices = DeviceDAO::getOnlineDeviceCodes(); + for (const QString &deviceId : onlineDevices) { + processNextTask(deviceId); + } + + // Запрашиваем новые задачи только для устройств без pending events + const QSet busyDevices = EventDAO::getDevicesWithPendingEvents(); + const QStringList materialIds = AccountDAO::getActiveMaterialIdsExcludingDevices(busyDevices); + if (!materialIds.isEmpty()) { + m_network->getTasks(materialIds); + } + }); m_pollTimer->start(); // Закрываем зависшие события с прошлого запуска (только не отправленные на сервер) @@ -141,30 +145,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) { // 2. Для каждого онлайн устройства без активного потока — запускаем следующую задачу for (const QString &deviceId : onlineDevices) { - if (const QThread *thr = m_threads.value(deviceId, nullptr); thr && thr->isRunning()) continue; - - const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(deviceId); - if (event.id == -1) continue; - - // Если задача ждёт больше 5 минут — отклоняем - const qint64 waitSecs = event.timestamp.secsTo(QDateTime::currentDateTimeUtc()); - if (waitSecs > 5 * 60) { - qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id; - EventDAO::updateEvent(event.id, EventStatus::Error, "Task expired: waited " + QString::number(waitSecs) + "s"); - sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Task expired: not started within 5 minutes"); - continue; - } - - const AccountInfo account = AccountDAO::getAccountById(event.accountId); - const ApplicationInfo app = ApplicationDAO::getApplication(deviceId, account.appCode); - if (app.status != "active") continue; - - if (!EventDAO::updateEvent(event.id, EventStatus::InProgress, "")) { - qCritical() << "[EventHandler] Failed to set InProgress, event id:" << event.id; - continue; - } - - startThreadForTask(account, event); + processNextTask(deviceId); } } @@ -367,6 +348,9 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event, tm->deleteLater(); } m_threads.remove(key); + + // Сразу пробуем запустить следующую задачу для этого устройства + processNextTask(key); } void EventHandler::startThreadForTask(const AccountInfo &account, const EventInfo &event) { @@ -393,20 +377,12 @@ void EventHandler::startThreadForTask(const AccountInfo &account, const EventInf if (event.type == EventType::FetchProfile) { if (appCode == "ozon") setup(new Ozon::LoginAndCheckAccountsScript(deviceId, appCode)); - else if (appCode == "birbank") setup(new LoginAndCheckAccountsBirbankScript(deviceId, appCode)); - else if (appCode == "rshb") setup(new Rshb::LoginAndCheckAccountsScript(deviceId, appCode)); - else if (appCode == "toss") setup(new LoginAndCheckAccountsTossScript(deviceId, appCode)); - else if (appCode == "ziraat") setup(new LoginAndCheckAccountsZiraatScript(deviceId, appCode)); else if (appCode == "black") setup(new Black::LoginAndCheckAccountsScript(deviceId, appCode)); else qWarning() << "[EventHandler] FETCH_PROFILE: unknown appCode:" << appCode; } if (event.type == EventType::FetchTransactions) { - if (appCode == "birbank") setup(new GetLastDaysHistoryBirbankScript(account)); - else if (appCode == "ozon") setup(new Ozon::GetLastTransactionsScript(deviceId, appCode)); - else if (appCode == "rshb") setup(new Rshb::GetLastDaysHistoryScript(account)); - else if (appCode == "toss") setup(new GetLastDaysHistoryTossScript(account)); - else if (appCode == "ziraat") setup(new GetLastDaysHistoryZiraatScript(account)); + if (appCode == "ozon") setup(new Ozon::GetLastTransactionsScript(deviceId, appCode)); else if (appCode == "black") setup(new Black::GetLastTransactionsScript(deviceId, appCode)); else qWarning() << "[EventHandler] FETCH_TRANSACTIONS: unknown appCode:" << appCode; } @@ -426,7 +402,6 @@ void EventHandler::startThreadForTask(const AccountInfo &account, const EventInf } else { // Перевод по номеру телефона (top-up) if (appCode == "ozon") setup(new Ozon::PayByPhoneScript(account, event.phone, recipientBank, event.amount)); - else if (appCode == "rshb") setup(new Rshb::PayByPhoneScript(account, event.phone, recipientBank, event.amount)); else qWarning() << "[EventHandler] CREATE_TRANSACTION: unknown appCode:" << appCode; } } @@ -457,6 +432,36 @@ void EventHandler::startThreadForTask(const AccountInfo &account, const EventInf thr->start(); } +void EventHandler::processNextTask(const QString &deviceId) { + if (!m_running) return; + if (const QThread *thr = m_threads.value(deviceId, nullptr); thr && thr->isRunning()) return; + + while (true) { + const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(deviceId); + if (event.id == -1) break; + + const qint64 waitSecs = event.timestamp.secsTo(QDateTime::currentDateTimeUtc()); + if (waitSecs > 5 * 60) { + qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id; + EventDAO::updateEvent(event.id, EventStatus::Error, "Task expired: waited " + QString::number(waitSecs) + "s"); + sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Task expired: not started within 5 minutes"); + continue; + } + + const AccountInfo account = AccountDAO::getAccountById(event.accountId); + const ApplicationInfo app = ApplicationDAO::getApplication(deviceId, account.appCode); + if (app.status != "active") break; + + if (!EventDAO::updateEvent(event.id, EventStatus::InProgress, "")) { + qCritical() << "[EventHandler] Failed to set InProgress, event id:" << event.id; + break; + } + + startThreadForTask(account, event); + break; + } +} + void EventHandler::clearAll() { for (QThread *thread : m_threads) { if (thread->isRunning()) { diff --git a/services/EventHandler.h b/services/EventHandler.h index 6f8f855..ad958a5 100644 --- a/services/EventHandler.h +++ b/services/EventHandler.h @@ -40,4 +40,6 @@ private: void updateEventThread(const QString &key, const EventInfo &event, const QString &result); void startThreadForTask(const AccountInfo &account, const EventInfo &event); + + void processNextTask(const QString &deviceId); }; diff --git a/services/net/NetworkService.cpp b/services/net/NetworkService.cpp index 97e38cd..f19dfa5 100644 --- a/services/net/NetworkService.cpp +++ b/services/net/NetworkService.cpp @@ -38,6 +38,7 @@ NetworkService::NetworkService(QObject *parent) : QObject(parent) { m_pathGetTasks = settings.value("net/pathGetTasks", "/api/v1/task/get_tasks").toString(); m_pathTaskResult = settings.value("net/pathTaskResult", "/api/v1/task/task_result").toString(); m_pathMonitoringLog = settings.value("net/pathMonitoringLog", "/monitoring/log").toString(); + m_pathCurrentProfile = settings.value("net/pathCurrentProfile", "/api/v1/profile/current_profile").toString(); m_manager = new QNetworkAccessManager(this); m_token = settings.value("common/token").toString(); m_accessToken = SettingsDAO::get("access_token"); @@ -292,30 +293,29 @@ void NetworkService::loginAndSetup() { // Шаг 2: проверяем есть ли desktop_id const QString desktopId = SettingsDAO::get("desktop_id"); - if (!desktopId.isEmpty()) { - emit finishedWithResult(1); - emit finished(); - return; + if (desktopId.isEmpty()) { + // Шаг 3: создаём desktop + QJsonObject desktopBody; + desktopBody["blue_token"] = QJsonValue::Null; + desktopBody["name"] = QJsonValue::Null; + desktopBody["contact"] = QJsonValue::Null; + const QSettings settings("config.ini", QSettings::IniFormat); + desktopBody["app_version"] = settings.value("common/version").toString(); + const QJsonValue desktopResult = postJson(m_apiBase + m_pathDesktopCreate, desktopBody, true); + + if (desktopResult.isUndefined() || desktopResult.isNull()) { + emit finishedWithResult(-1); + emit finished(); + return; + } + + const QJsonObject desktopData = desktopResult.toObject(); + SettingsDAO::set("desktop_id", desktopData["id"].toString()); + SettingsDAO::set("profile_id", desktopData["profile_id"].toString()); } - // Шаг 3: создаём desktop - QJsonObject desktopBody; - desktopBody["blue_token"] = QJsonValue::Null; - desktopBody["name"] = QJsonValue::Null; - desktopBody["contact"] = QJsonValue::Null; - const QSettings settings("config.ini", QSettings::IniFormat); - desktopBody["app_version"] = settings.value("common/version").toString(); - const QJsonValue desktopResult = postJson(m_apiBase + m_pathDesktopCreate, desktopBody, true); - - if (desktopResult.isUndefined() || desktopResult.isNull()) { - emit finishedWithResult(-1); - emit finished(); - return; - } - - const QJsonObject desktopData = desktopResult.toObject(); - SettingsDAO::set("desktop_id", desktopData["id"].toString()); - SettingsDAO::set("profile_id", desktopData["profile_id"].toString()); + // Шаг 4: синхронизация устройств и профилей + syncCurrentProfile(); emit finishedWithResult(1); emit finished(); @@ -412,6 +412,140 @@ QJsonValue NetworkService::patchJson(const QString &path, const QJsonObject &bod return result; } +QJsonValue NetworkService::getJson(const QString &path, bool withAuth) { + const QUrl url(path); + QNetworkRequest request(url); + if (withAuth && !m_accessToken.isEmpty()) { + request.setRawHeader("Authorization", ("Bearer " + m_accessToken).toUtf8()); + } + + QNetworkReply *reply = m_manager->get(request); + QEventLoop loop; + QTimer timeout; + timeout.setSingleShot(true); + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { + loop.quit(); + }); + connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit); + timeout.start(30000); + loop.exec(); + + QJsonValue result; + if (!timeout.isActive()) { + qCritical() << "Таймаут запроса (getJson):" << path; + reply->abort(); + result = QJsonValue::Undefined; + } else if (reply->error() != QNetworkReply::NoError) { + qCritical() << "Network error (GET):" << reply->errorString() << path; + result = QJsonValue::Undefined; + } else { + const QByteArray raw = reply->readAll(); + QJsonParseError err; + const QJsonDocument doc = QJsonDocument::fromJson(raw, &err); + if (err.error == QJsonParseError::NoError && doc.isObject()) { + const QJsonObject root = doc.object(); + if (root["success"].toBool()) { + result = root["data"]; + } else { + qCritical() << "API error (GET):" << root; + result = QJsonValue::Null; + } + } + } + reply->deleteLater(); + return result; +} + +void NetworkService::syncCurrentProfile() { + // 1. Определяем онлайн-устройства по БД (DeviceScreener обновит позже) + const QList allDevices = DeviceDAO::getAllDevices(false); + QSet onlineDeviceIds; + for (const DeviceInfo &dev : allDevices) { + if (dev.status == "device" && !dev.image.isEmpty()) { + onlineDeviceIds.insert(dev.id); + } + } + + qDebug() << "[syncCurrentProfile] online devices:" << onlineDeviceIds; + + // 3. GET current_profile + const QJsonValue profileResult = getJson(m_apiBase + m_pathCurrentProfile, true); + if (profileResult.isUndefined() || profileResult.isNull()) { + qWarning() << "[syncCurrentProfile] failed to get current_profile"; + return; + } + + const QJsonObject profileData = profileResult.toObject(); + + // 4. Сохраняем profile_id + const QString profileId = profileData["id"].toString(); + if (!profileId.isEmpty()) { + SettingsDAO::set("profile_id", profileId); + } + + // 5. Синхронизируем bank_profiles и materials + const QJsonArray bankProfiles = profileData["bank_profiles"].toArray(); + for (const QJsonValue &bpVal : bankProfiles) { + const QJsonObject bp = bpVal.toObject(); + const QString bpId = bp["id"].toString(); + const QString serverBpState = bp["state"].toString(); // "enabled" / "disabled" + + // Находим applications с этим bank_profile_id + const QList apps = ApplicationDAO::findAllByBankProfileId(bpId); + for (const ApplicationInfo &app : apps) { + const bool deviceOnline = onlineDeviceIds.contains(app.deviceId); + const QString localStatus = app.status; // "active" / "off" + const QString serverStatus = (serverBpState == "disabled") ? "off" : "active"; + + if (!deviceOnline) { + // Офлайн: если на сервере enabled → PATCH disable + if (serverBpState != "disabled") { + qDebug() << "[syncCurrentProfile] device" << app.deviceId << "offline, disabling bank_profile" << bpId; + patchJson(m_apiBase + m_pathBankProfile + "/" + bpId + "/false", QJsonObject(), true); + } + } else { + // Онлайн: если локальный status != серверный → PATCH локальное значение на сервер + if (localStatus != serverStatus) { + const QString enableStr = (localStatus == "active") ? "true" : "false"; + qDebug() << "[syncCurrentProfile] device" << app.deviceId << "online, syncing bank_profile" << bpId << "to" << enableStr; + patchJson(m_apiBase + m_pathBankProfile + "/" + bpId + "/" + enableStr, QJsonObject(), true); + } + } + } + + // 6. Синхронизируем materials + const QJsonArray materials = bp["materials"].toArray(); + for (const QJsonValue &matVal : materials) { + const QJsonObject mat = matVal.toObject(); + const QString matId = mat["id"].toString(); + const QString serverMatState = mat["state"].toString(); + + const AccountInfo account = AccountDAO::findByMaterialId(matId); + if (account.id < 0) continue; // не найден в локальной БД + + const bool deviceOnline = onlineDeviceIds.contains(account.deviceId); + const QString localStatus = accountStatusToString(account.status); + const QString serverStatus = (serverMatState == "disabled") ? "off" : "active"; + + if (!deviceOnline) { + if (serverMatState != "disabled") { + qDebug() << "[syncCurrentProfile] device" << account.deviceId << "offline, disabling material" << matId; + patchJson(m_apiBase + m_pathMaterial + "/" + matId + "/false", QJsonObject(), true); + } + } else { + if (localStatus != serverStatus) { + const QString enableStr = (localStatus == "active") ? "true" : "false"; + qDebug() << "[syncCurrentProfile] device" << account.deviceId << "online, syncing material" << matId << "to" << enableStr; + patchJson(m_apiBase + m_pathMaterial + "/" + matId + "/" + enableStr, QJsonObject(), true); + } + } + } + } + + qDebug() << "[syncCurrentProfile] done"; +} + void NetworkService::postBankProfile() { if (!ensureValidToken()) { emit finished(); return; } @@ -655,7 +789,7 @@ void NetworkService::scheduleNextRefresh() { }); } -void NetworkService::getTasks() { +void NetworkService::getTasks(const QStringList &materialIds) { if (m_fetchingTasks) return; m_fetchingTasks = true; @@ -668,12 +802,12 @@ void NetworkService::getTasks() { QUrl url(m_apiBase + m_pathGetTasks); QUrlQuery urlQuery; - const QStringList materialIds = AccountDAO::getActiveMaterialIds(); - if (materialIds.isEmpty()) { + const QStringList ids = materialIds.isEmpty() ? AccountDAO::getActiveMaterialIds() : materialIds; + if (ids.isEmpty()) { m_fetchingTasks = false; return; } - for (const QString &mid : materialIds) { + for (const QString &mid : ids) { urlQuery.addQueryItem("materials_id", mid); } url.setQuery(urlQuery); diff --git a/services/net/NetworkService.h b/services/net/NetworkService.h index 26a732a..bbd9a47 100644 --- a/services/net/NetworkService.h +++ b/services/net/NetworkService.h @@ -45,12 +45,13 @@ public slots: void updateTransactionData(); void loginAndSetup(); + void syncCurrentProfile(); void createDevice(); void updateDevice(); - void getTasks(); + void getTasks(const QStringList &materialIds = {}); void postTaskResult(); @@ -92,12 +93,14 @@ private: QString m_pathGetTasks; QString m_pathTaskResult; QString m_pathMonitoringLog; + QString m_pathCurrentProfile; bool m_fetchingTasks = false; QNetworkAccessManager *m_manager; QJsonValue post(QHttpMultiPart *multiPart, const QString &path); QJsonArray get(const QString &path, const QMap ¶ms); + QJsonValue getJson(const QString &path, bool withAuth = false); QJsonValue postJson(const QString &path, const QJsonObject &body, bool withAuth = false); QJsonValue patchJson(const QString &path, const QJsonObject &body, bool withAuth = false); diff --git a/views/MainWindow.cpp b/views/MainWindow.cpp index 0917433..1419f5e 100644 --- a/views/MainWindow.cpp +++ b/views/MainWindow.cpp @@ -10,6 +10,7 @@ #include "bank/BankListWindow.h" #include "event/EventWindow.h" #include "log/LogWindow.h" +#include "log/FileLogWindow.h" #include "db/DeviceInfo.h" #include "device/DeviceWidget.h" #include "dao/DeviceDAO.h" @@ -41,13 +42,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { auto *bankListAction = new QAction("Банки", this); auto *tutorialPageAction = new QAction("Инструкция", this); auto *eventAction = new QAction("События", this); - auto *logAction = new QAction("Логи", this); + auto *errorLogAction = new QAction("Ошибки", this); + auto *fileLogAction = new QAction("Логи", this); menu->addAction(devicesPageAction); // menu->addAction(accountPageAction); menu->addAction(bankListAction); menu->addAction(eventAction); menu->addAction(tutorialPageAction); - menu->addAction(logAction); + menu->addAction(errorLogAction); + menu->addAction(fileLogAction); connect(devicesPageAction, &QAction::triggered, this, [=]() { if (!m_devicesWindow) { @@ -88,7 +91,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_eventWindow->activateWindow(); }); - connect(logAction, &QAction::triggered, this, [=]() { + connect(errorLogAction, &QAction::triggered, this, [=]() { if (!m_logWindow) { m_logWindow = new LogWindow(this); } @@ -97,6 +100,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_logWindow->activateWindow(); }); + connect(fileLogAction, &QAction::triggered, this, [=]() { + if (!m_fileLogWindow) { + m_fileLogWindow = new FileLogWindow(this); + } + m_fileLogWindow->show(); + m_fileLogWindow->raise(); + m_fileLogWindow->activateWindow(); + }); + // m_tutorialWindow = new TutorialWindow(this); // m_tutorialWindow->show(); // QTimer::singleShot(0, m_tutorialWindow, [w = m_tutorialWindow]() { @@ -157,9 +169,9 @@ void MainWindow::loadDevices() { QLayoutItem *item; while ((item = flowLayout->takeAt(0)) != nullptr) { if (item->widget()) { - delete item->widget(); // Удаляем виджет + item->widget()->deleteLater(); } - delete item; // Удаляем сам элемент из layout + delete item; } // Добавляем новые виджеты для каждого устройства for (const auto &device: devices) { diff --git a/views/MainWindow.h b/views/MainWindow.h index 777544c..93b5029 100644 --- a/views/MainWindow.h +++ b/views/MainWindow.h @@ -28,6 +28,7 @@ private: QWidget *m_tutorialWindow = nullptr; QWidget *m_bankListWindow = nullptr; QWidget *m_logWindow = nullptr; + QWidget *m_fileLogWindow = nullptr; QWidget *m_eventWindow = nullptr; void createDevicePage(); diff --git a/views/bank/AccountSettingsWindow.cpp b/views/bank/AccountSettingsWindow.cpp index db8bea8..0612889 100644 --- a/views/bank/AccountSettingsWindow.cpp +++ b/views/bank/AccountSettingsWindow.cpp @@ -11,13 +11,11 @@ #include #include -#include "birbank/LoginAndCheckAccountsBirbankScript.h" #include "ozon/LoginAndCheckAccountsScript.h" #include "ozon/GetProfileInfoScript.h" #include "ozon/GetCardInfoScript.h" #include "black/GetLastTransactionsScript.h" #include "ozon/GetLastTransactionsScript.h" -#include "toss/LoginAndCheckAccountsTossScript.h" #include "black/LoginAndCheckAccountsScript.h" #include "black/GetProfileInfoScript.h" #include "black/GetCardInfoScript.h" @@ -368,21 +366,7 @@ AccountSettingsWindow::AccountSettingsWindow( void AccountSettingsWindow::startCheckPinCode(const QString &appCode) { QThread *thread = new QThread(nullptr); - if (appCode == "birbank") { - auto *worker = new LoginAndCheckAccountsBirbankScript(m_deviceId, appCode, nullptr); - worker->moveToThread(thread); - connect(thread, &QThread::started, worker, &LoginAndCheckAccountsBirbankScript::start); - connect(worker, &LoginAndCheckAccountsBirbankScript::finished, thread, &QThread::quit); - connect(worker, &LoginAndCheckAccountsBirbankScript::finished, worker, &QObject::deleteLater); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); - } else if (appCode == "toss") { - auto *worker = new LoginAndCheckAccountsTossScript(m_deviceId, appCode, nullptr); - worker->moveToThread(thread); - connect(thread, &QThread::started, worker, &LoginAndCheckAccountsTossScript::start); - connect(worker, &LoginAndCheckAccountsTossScript::finished, thread, &QThread::quit); - connect(worker, &LoginAndCheckAccountsTossScript::finished, worker, &QObject::deleteLater); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); - } else if (appCode == "ozon") { + if (appCode == "ozon") { auto *worker = new Ozon::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr); worker->moveToThread(thread); connect(thread, &QThread::started, worker, &Ozon::LoginAndCheckAccountsScript::start); diff --git a/views/bank/AccountWindow.cpp b/views/bank/AccountWindow.cpp index 00b5049..391f998 100644 --- a/views/bank/AccountWindow.cpp +++ b/views/bank/AccountWindow.cpp @@ -30,7 +30,7 @@ AccountWindow::AccountWindow(QWidget *parent): QWidget(parent) { QList devices = DeviceDAO::getAllDevices(false); for (int i = 0; i < devices.size(); ++i) { DeviceInfo device = devices[i]; - QList accounts = AccountDAO::getAccounts(device.id, "rshb"); + QList accounts = AccountDAO::getAccounts(device.id, "ozon"); // Текст перед первой таблицей QString title = QString("%1 [%2] | %3").arg(device.name, device.id, device.status); QLabel *label = new QLabel(title, this); diff --git a/views/bank/BankListWindow.cpp b/views/bank/BankListWindow.cpp index 89bf674..630a337 100644 --- a/views/bank/BankListWindow.cpp +++ b/views/bank/BankListWindow.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -32,10 +33,16 @@ BankListWindow::BankListWindow(QWidget *parent) syncBtn->setStyleSheet("background-color: #1565C0; color: white;"); syncBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); topRow->addWidget(syncBtn); + auto *searchEdit = new QLineEdit(this); + searchEdit->setPlaceholderText("Поиск..."); + searchEdit->setClearButtonEnabled(true); + topRow->addWidget(searchEdit); + topRow->addStretch(); layout->addLayout(topRow); connect(syncBtn, &QPushButton::clicked, this, &BankListWindow::syncBanks); + connect(searchEdit, &QLineEdit::textChanged, this, &BankListWindow::filterTable); m_tableWidget = new QTableWidget(this); m_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); @@ -125,3 +132,17 @@ void BankListWindow::syncBanks() { qDebug() << "[BankListWindow] Synced" << banks.size() << "banks"; }); } + +void BankListWindow::filterTable(const QString &text) { + for (int i = 0; i < m_tableWidget->rowCount(); ++i) { + bool match = false; + for (int col = 0; col < m_tableWidget->columnCount(); ++col) { + QTableWidgetItem *item = m_tableWidget->item(i, col); + if (item && item->text().contains(text, Qt::CaseInsensitive)) { + match = true; + break; + } + } + m_tableWidget->setRowHidden(i, !match); + } +} diff --git a/views/bank/BankListWindow.h b/views/bank/BankListWindow.h index 2264503..5647950 100644 --- a/views/bank/BankListWindow.h +++ b/views/bank/BankListWindow.h @@ -14,4 +14,5 @@ private: void reloadTable(); void syncBanks(); + void filterTable(const QString &text); }; diff --git a/views/bank/BankSettingsWindow.cpp b/views/bank/BankSettingsWindow.cpp index 2af39b4..38f1c3f 100644 --- a/views/bank/BankSettingsWindow.cpp +++ b/views/bank/BankSettingsWindow.cpp @@ -20,12 +20,7 @@ #include "net/NetworkService.h" #include "common/PaddedItemDelegate.h" #include "device/EditBankDialog.h" -#include "birbank/LoginAndCheckAccountsBirbankScript.h" #include "ozon/LoginAndCheckAccountsScript.h" -#include "rshb/LoginAndCheckAccountsScript.h" -#include "toss/LoginAndCheckAccountsTossScript.h" -#include "widget/common/CollapsibleSection.h" -#include "ziraat/LoginAndCheckAccountsZiraatScript.h" #include "black/LoginAndCheckAccountsScript.h" QTableWidgetItem *BankSettingsWindow::createTableWidget(const QString &text) { @@ -53,41 +48,13 @@ QString parseStatus(const ApplicationInfo &app) { void BankSettingsWindow::startCheckPinCode(const QString &appCode) { QThread *thread = new QThread(nullptr); - if (appCode == "rshb") { - auto *worker = new Rshb::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr); - worker->moveToThread(thread); - connect(thread, &QThread::started, worker, &Rshb::LoginAndCheckAccountsScript::start); - connect(worker, &Rshb::LoginAndCheckAccountsScript::finished, thread, &QThread::quit); - connect(worker, &Rshb::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); - } else if (appCode == "ziraat") { - auto *worker = new LoginAndCheckAccountsZiraatScript(m_deviceId, appCode, nullptr); - worker->moveToThread(thread); - connect(thread, &QThread::started, worker, &LoginAndCheckAccountsZiraatScript::start); - connect(worker, &LoginAndCheckAccountsZiraatScript::finished, thread, &QThread::quit); - connect(worker, &LoginAndCheckAccountsZiraatScript::finished, worker, &QObject::deleteLater); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); - } else if (appCode == "ozon") { + if (appCode == "ozon") { auto *worker = new Ozon::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr); worker->moveToThread(thread); connect(thread, &QThread::started, worker, &Ozon::LoginAndCheckAccountsScript::start); connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, thread, &QThread::quit); connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater); connect(thread, &QThread::finished, thread, &QObject::deleteLater); - } else if (appCode == "birbank") { - auto *worker = new LoginAndCheckAccountsBirbankScript(m_deviceId, appCode, nullptr); - worker->moveToThread(thread); - connect(thread, &QThread::started, worker, &LoginAndCheckAccountsBirbankScript::start); - connect(worker, &LoginAndCheckAccountsBirbankScript::finished, thread, &QThread::quit); - connect(worker, &LoginAndCheckAccountsBirbankScript::finished, worker, &QObject::deleteLater); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); - } else if (appCode == "toss") { - auto *worker = new LoginAndCheckAccountsTossScript(m_deviceId, appCode, nullptr); - worker->moveToThread(thread); - connect(thread, &QThread::started, worker, &LoginAndCheckAccountsTossScript::start); - connect(worker, &LoginAndCheckAccountsTossScript::finished, thread, &QThread::quit); - connect(worker, &LoginAndCheckAccountsTossScript::finished, worker, &QObject::deleteLater); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); } else if (appCode == "black") { auto *worker = new Black::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr); worker->moveToThread(thread); diff --git a/views/device/DeviceSettingsWindow.cpp b/views/device/DeviceSettingsWindow.cpp new file mode 100644 index 0000000..15753a3 --- /dev/null +++ b/views/device/DeviceSettingsWindow.cpp @@ -0,0 +1,132 @@ +#include "DeviceSettingsWindow.h" + +#include +#include +#include +#include +#include +#include + +#include "bank/AccountSettingsWindow.h" +#include "dao/ApplicationDAO.h" +#include "db/ApplicationInfo.h" + +static QString formatStatus(const ApplicationInfo &app) { + if (!app.install) return "не установлен"; + if (app.status == "active") { + if (!app.pinCodeChecked) + return app.pinCode.isEmpty() ? "нет пин-код" : "пин-код не проверен"; + return "работает"; + } + if (app.status == "off") return "выключено"; + return app.status; +} + +DeviceSettingsWindow::DeviceSettingsWindow(QWidget *parent, const QString &deviceId, const QString &deviceName) + : QWidget(parent, + Qt::Window + | Qt::WindowTitleHint + | Qt::WindowSystemMenuHint + | Qt::WindowCloseButtonHint + | Qt::WindowMinMaxButtonsHint), + m_deviceId(deviceId), + m_deviceName(deviceName) { + setWindowTitle("Настройки устройства [" + m_deviceName + "]"); + setMinimumSize(600, 300); + resize(700, 400); + + auto *mainLayout = new QVBoxLayout(this); + + auto *scrollArea = new QScrollArea(this); + scrollArea->setWidgetResizable(true); + + auto *contentWidget = new QWidget; + auto *layout = new QVBoxLayout(contentWidget); + layout->setContentsMargins(4, 4, 4, 4); + layout->setAlignment(Qt::AlignTop); + + auto *tableWidget = new QTableWidget(this); + tableWidget->setSelectionMode(QAbstractItemView::NoSelection); + tableWidget->setFocusPolicy(Qt::NoFocus); + tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + + reloadContent(tableWidget); + tableWidget->resizeColumnsToContents(); + tableWidget->resizeRowsToContents(); + + layout->addWidget(tableWidget); + + contentWidget->setLayout(layout); + scrollArea->setWidget(contentWidget); + mainLayout->addWidget(scrollArea); + setLayout(mainLayout); +} + +void DeviceSettingsWindow::reloadContent(QTableWidget *tableWidget) { + QList apps = ApplicationDAO::getApplicationsByDeviceId(m_deviceId); + + tableWidget->clearContents(); + tableWidget->setRowCount(apps.size()); + tableWidget->setColumnCount(7); + tableWidget->setHorizontalHeaderLabels({"", "Приложение", "Пакет", "Валюта", "Статус", "Комментарий", ""}); + + tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); + for (int i = 0; i < 7; ++i) + tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents); + tableWidget->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Stretch); + + for (int k = 0; k < apps.size(); ++k) { + const ApplicationInfo &app = apps[k]; + + // Иконка + auto *icon = new QLabel(tableWidget); + QPixmap pixmap(QCoreApplication::applicationDirPath() + "/images/" + app.code + "_icon.png"); + icon->setPixmap(pixmap.scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + icon->setStyleSheet("background-color: transparent; padding: 0 4px 0 0;"); + icon->setMinimumSize(24, 20); + tableWidget->setCellWidget(k, 0, icon); + + // Название + auto *nameItem = new QTableWidgetItem(app.name); + nameItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + tableWidget->setItem(k, 1, nameItem); + + // Пакет + auto *packageItem = new QTableWidgetItem(app.package); + packageItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + tableWidget->setItem(k, 2, packageItem); + + // Валюта + auto *currencyItem = new QTableWidgetItem(app.currency); + currencyItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + tableWidget->setItem(k, 3, currencyItem); + + // Статус + auto *statusItem = new QTableWidgetItem(formatStatus(app)); + statusItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + if (app.status != "active" || !app.pinCodeChecked) + statusItem->setForeground(QBrush(Qt::red)); + else + statusItem->setForeground(QBrush(Qt::darkGreen)); + tableWidget->setItem(k, 4, statusItem); + + // Комментарий + auto *commentItem = new QTableWidgetItem(app.comment); + commentItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + tableWidget->setItem(k, 5, commentItem); + + // Кнопка "Настроить" + auto *settingsBtn = new QPushButton("Настроить", tableWidget); + settingsBtn->setStyleSheet("background-color: green; color: white; border-radius: 4px; height: 26px;"); + tableWidget->setCellWidget(k, 6, settingsBtn); + + connect(settingsBtn, &QPushButton::clicked, this, [this, app]() { + auto *window = new AccountSettingsWindow(nullptr, m_deviceId, m_deviceName, app); + window->setWindowFlags(window->windowFlags() | Qt::Window); + window->setAttribute(Qt::WA_DeleteOnClose); + window->show(); + window->raise(); + window->activateWindow(); + }); + } +} diff --git a/views/device/DeviceSettingsWindow.h b/views/device/DeviceSettingsWindow.h new file mode 100644 index 0000000..aa6a7c4 --- /dev/null +++ b/views/device/DeviceSettingsWindow.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class DeviceSettingsWindow final : public QWidget { + Q_OBJECT + +public: + DeviceSettingsWindow(QWidget *parent, const QString &deviceId, const QString &deviceName); + +private: + QString m_deviceId; + QString m_deviceName; + + void reloadContent(QTableWidget *tableWidget); +}; diff --git a/views/device/DeviceWidget.cpp b/views/device/DeviceWidget.cpp index b99d51f..f212692 100644 --- a/views/device/DeviceWidget.cpp +++ b/views/device/DeviceWidget.cpp @@ -12,7 +12,9 @@ #include "bank/AccountSettingsWindow.h" #include "bank/BankSettingsWindow.h" +#include "DeviceSettingsWindow.h" #include "dao/ApplicationDAO.h" +#include "dao/DeviceDAO.h" #include "db/ApplicationInfo.h" @@ -94,30 +96,27 @@ void DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget, btn1->setEnabled(false); } connect(btn1, &QPushButton::clicked, this, [app]() { - QString newStatus = app.status == "active" ? "off" : "active"; + const QString newStatus = app.status == "active" ? "off" : "active"; QMessageBox msgBox(nullptr); - msgBox.setWindowTitle(tr("Подтверждение")); + msgBox.setWindowTitle(newStatus == "off" ? "Отключить банк?" : "Включить банк?"); msgBox.setText( "Вы уверены, что хотите " + QString(newStatus == "off" ? "отключить от работы" : "включить") + " данный банк: " + app.name + "?"); - - QPushButton *deleteBtn = msgBox.addButton(tr(newStatus == "off" ? "Отключить" : "Включить"), - QMessageBox::AcceptRole); - QPushButton *closeBtn = msgBox.addButton(tr("Отмена"), QMessageBox::RejectRole); - msgBox.setDefaultButton(closeBtn); + QPushButton *confirmBtn = msgBox.addButton(newStatus == "off" ? "Отключить" : "Включить", + QMessageBox::AcceptRole); + QPushButton *cancelBtn = msgBox.addButton("Отмена", QMessageBox::RejectRole); + msgBox.setDefaultButton(cancelBtn); msgBox.setModal(true); msgBox.exec(); - if (msgBox.clickedButton() == deleteBtn) { + if (msgBox.clickedButton() == confirmBtn) { if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, newStatus)) { qDebug() << "Cant update bank data"; } else { sendBankStatus(app.deviceId, app.code, newStatus); } - } else { - // просто закрыли — ничего не делаем } }); hbox->addWidget(btn1); @@ -197,25 +196,74 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p imageLabel->setAlignment(Qt::AlignCenter); layout->addWidget(imageLabel, 0, 0); - auto *statusLabel = new QLabel(device.name, this); + auto *statusLabel = new QLabel(QString("%1 %2%").arg(device.name).arg(device.battery), this); statusLabel->setWordWrap(true); statusLabel->setMaximumWidth(300); statusLabel->setStyleSheet( "background-color: rgba(0, 0, 0, 0.5);" "color: white;" ); - // статус — в ту же ячейку, но поверх картинки и по углу layout->addWidget(statusLabel, 0, 0, Qt::AlignTop | Qt::AlignLeft); - auto *batteryLabel = new QLabel(QString("%1%").arg(device.battery), this); - batteryLabel->setWordWrap(true); - batteryLabel->setMaximumWidth(300); - batteryLabel->setStyleSheet( + // Кнопки настроек и удаления (справа сверху) + auto *topRightWidget = new QWidget(this); + topRightWidget->setStyleSheet("background: transparent;"); + auto *topRightLayout = new QHBoxLayout(topRightWidget); + topRightLayout->setContentsMargins(0, 0, 0, 0); + topRightLayout->setSpacing(2); + + auto *settingsBtn = new QPushButton(this); + settingsBtn->setIcon(QIcon(QCoreApplication::applicationDirPath() + "/images/cogwheel.png")); + settingsBtn->setIconSize(QSize(20, 20)); + settingsBtn->setFixedSize(28, 28); + settingsBtn->setFlat(true); + settingsBtn->setStyleSheet( "background-color: rgba(0, 0, 0, 0.5);" - "color: white;" + "border: none;" + "border-radius: 4px;" ); - // статус — в ту же ячейку, но поверх картинки и по углу - layout->addWidget(batteryLabel, 0, 0, Qt::AlignTop | Qt::AlignRight); + connect(settingsBtn, &QPushButton::clicked, this, [this]() { + auto *window = new DeviceSettingsWindow(nullptr, m_device.id, m_device.name); + window->setAttribute(Qt::WA_DeleteOnClose); + window->show(); + window->raise(); + window->activateWindow(); + }); + + auto *deleteBtn = new QPushButton(this); + deleteBtn->setIcon(QIcon(QCoreApplication::applicationDirPath() + "/images/delete.png")); + deleteBtn->setIconSize(QSize(20, 20)); + deleteBtn->setFixedSize(28, 28); + deleteBtn->setFlat(true); + deleteBtn->setStyleSheet( + "background-color: rgba(0, 0, 0, 0.5);" + "border: none;" + "border-radius: 4px;" + ); + connect(deleteBtn, &QPushButton::clicked, this, [this]() { + const QString deviceId = m_device.id; + const QString deviceName = m_device.name; + QPointer guard(this); + + QMessageBox msgBox(nullptr); + msgBox.setWindowTitle(tr("Удалить устройство?")); + msgBox.setText( + "Будет удалено устройство " + deviceName + + " и все связанные данные: банки, аккаунты, транзакции и события."); + QPushButton *confirmBtn = msgBox.addButton(tr("Удалить"), QMessageBox::AcceptRole); + QPushButton *cancelBtn = msgBox.addButton(tr("Отмена"), QMessageBox::RejectRole); + msgBox.setDefaultButton(cancelBtn); + msgBox.setModal(true); + msgBox.exec(); + + if (msgBox.clickedButton() == confirmBtn) { + DeviceDAO::deleteDevice(deviceId); + } + }); + + topRightLayout->addWidget(settingsBtn); + topRightLayout->addWidget(deleteBtn); + layout->addWidget(topRightWidget, 0, 0, Qt::AlignTop | Qt::AlignRight); if (device.status != "device") { auto *offlineLabel = new QLabel("Офлайн", this); diff --git a/views/device/DeviceWidget.h b/views/device/DeviceWidget.h index 709d616..34985f7 100644 --- a/views/device/DeviceWidget.h +++ b/views/device/DeviceWidget.h @@ -18,4 +18,8 @@ private: void buildBankRow(const ApplicationInfo &app, QWidget *rowWidget, QWidget *parent, bool isOffline = false); void setOpenBanksSettings(QPushButton *button, const ApplicationInfo &app); + +signals: + void deleteRequested(const QString &deviceId); + void settingsRequested(const QString &deviceId); }; diff --git a/views/log/FileLogWindow.cpp b/views/log/FileLogWindow.cpp new file mode 100644 index 0000000..336bec5 --- /dev/null +++ b/views/log/FileLogWindow.cpp @@ -0,0 +1,104 @@ +#include "FileLogWindow.h" + +#include +#include +#include + +#include "dao/GeneralLogDAO.h" + +static constexpr int COL_ID = 0; +static constexpr int COL_TIME = 1; +static constexpr int COL_LEVEL = 2; +static constexpr int COL_SOURCE = 3; +static constexpr int COL_MESSAGE = 4; + +FileLogWindow::FileLogWindow(QWidget *parent) : QDialog(parent) { + setWindowTitle("Логи"); + resize(1100, 600); + + m_table = new QTableWidget(this); + m_table->setColumnCount(5); + m_table->setHorizontalHeaderLabels({"#", "Время", "Уровень", "Источник", "Сообщение"}); + m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->setSelectionMode(QAbstractItemView::SingleSelection); + m_table->verticalHeader()->hide(); + m_table->horizontalHeader()->setStretchLastSection(true); + m_table->setColumnWidth(COL_ID, 45); + m_table->setColumnWidth(COL_TIME, 145); + m_table->setColumnWidth(COL_LEVEL, 80); + m_table->setColumnWidth(COL_SOURCE, 250); + + m_showAllCheck = new QCheckBox("Показать все (включая DEBUG/INFO)", this); + + m_prevBtn = new QPushButton("←", this); + m_nextBtn = new QPushButton("→", this); + m_pageLabel = new QLabel(this); + m_pageLabel->setAlignment(Qt::AlignCenter); + m_pageLabel->setMinimumWidth(140); + + auto *refreshBtn = new QPushButton("Обновить", this); + + auto *paginationLayout = new QHBoxLayout; + paginationLayout->addWidget(m_prevBtn); + paginationLayout->addWidget(m_pageLabel); + paginationLayout->addWidget(m_nextBtn); + paginationLayout->addStretch(); + paginationLayout->addWidget(m_showAllCheck); + paginationLayout->addWidget(refreshBtn); + + auto *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(m_table, 1); + mainLayout->addLayout(paginationLayout); + + connect(m_prevBtn, &QPushButton::clicked, this, [this]() { + if (m_page > 0) { --m_page; loadPage(); } + }); + connect(m_nextBtn, &QPushButton::clicked, this, [this]() { + if (m_page < m_totalPages - 1) { ++m_page; loadPage(); } + }); + connect(refreshBtn, &QPushButton::clicked, this, [this]() { + m_page = 0; + loadPage(); + }); + connect(m_showAllCheck, &QCheckBox::toggled, this, [this]() { + m_page = 0; + loadPage(); + }); + + loadPage(); +} + +QStringList FileLogWindow::currentLevelFilter() const { + if (m_showAllCheck->isChecked()) + return {}; + return {"WARNING", "CRITICAL", "FATAL"}; +} + +void FileLogWindow::loadPage() { + const QStringList levels = currentLevelFilter(); + const int total = GeneralLogDAO::getTotalCount(levels); + m_totalPages = qMax(1, (total + m_pageSize - 1) / m_pageSize); + m_page = qBound(0, m_page, m_totalPages - 1); + + const QList entries = GeneralLogDAO::getLogs(m_page, m_pageSize, levels); + + m_table->setRowCount(static_cast(entries.size())); + for (int i = 0; i < entries.size(); ++i) { + const auto &e = entries[i]; + m_table->setItem(i, COL_ID, new QTableWidgetItem(QString::number(e.id))); + m_table->setItem(i, COL_TIME, new QTableWidgetItem( + e.timestamp.toLocalTime().toString("dd.MM.yy HH:mm:ss"))); + m_table->setItem(i, COL_LEVEL, new QTableWidgetItem(e.level)); + m_table->setItem(i, COL_SOURCE, new QTableWidgetItem(e.source)); + m_table->setItem(i, COL_MESSAGE, new QTableWidgetItem(e.message)); + } + + updatePagination(); +} + +void FileLogWindow::updatePagination() { + m_pageLabel->setText(QString("Страница %1 из %2").arg(m_page + 1).arg(m_totalPages)); + m_prevBtn->setEnabled(m_page > 0); + m_nextBtn->setEnabled(m_page < m_totalPages - 1); +} diff --git a/views/log/FileLogWindow.h b/views/log/FileLogWindow.h new file mode 100644 index 0000000..9a7adeb --- /dev/null +++ b/views/log/FileLogWindow.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include +#include +#include + +class FileLogWindow final : public QDialog { + Q_OBJECT + +public: + explicit FileLogWindow(QWidget *parent = nullptr); + +private: + QTableWidget *m_table; + QLabel *m_pageLabel; + QPushButton *m_prevBtn; + QPushButton *m_nextBtn; + QCheckBox *m_showAllCheck; + + int m_page = 0; + int m_pageSize = 20; + int m_totalPages = 1; + + QStringList currentLevelFilter() const; + void loadPage(); + void updatePagination(); +}; diff --git a/views/widget/loader/LoaderWindow.cpp b/views/widget/loader/LoaderWindow.cpp index 2ba0a75..9dacc2e 100644 --- a/views/widget/loader/LoaderWindow.cpp +++ b/views/widget/loader/LoaderWindow.cpp @@ -50,12 +50,16 @@ LoaderWindow::LoaderWindow(QWidget *parent) : QWidget(parent) { } void LoaderWindow::updateProgress() { - if (const int value = progressBar->value(); value < 100) { + const int value = progressBar->value(); + if (value < 99) { progressBar->setValue(value + 1); - } else { + } else if (m_checkResult != 1000) { + // Результат получен — завершаем + progressBar->setValue(100); if (timer) { timer->stop(); } emit loadingFinished(m_checkResult); } + // Иначе ждём на 99 пока сеть не ответит }