Add Ziraat Bank integration and support for IBAN payments
This update introduces support for Ziraat Bank, including scripts for retrieving transaction history, parsing account information, and performing IBAN payments. Enhancements include improved screen parsing logic, additional transaction types, and better handling of amounts and IBAN-specific operations.
This commit is contained in:
parent
ab16b8962f
commit
2552ca49be
@ -35,17 +35,6 @@ Node CommonZiraatScript::swipeToButton(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommonZiraatScript::inputPinCode(const QString &deviceId, const QList<Node> &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 CommonZiraatScript::runAppAndGoToHomeScreen(
|
bool CommonZiraatScript::runAppAndGoToHomeScreen(
|
||||||
const QString &deviceId,
|
const QString &deviceId,
|
||||||
const QString &packageName,
|
const QString &packageName,
|
||||||
@ -68,7 +57,7 @@ bool CommonZiraatScript::runAppAndGoToHomeScreen(
|
|||||||
// Даем минимальное время на запуск
|
// Даем минимальное время на запуск
|
||||||
QThread::msleep(4500);
|
QThread::msleep(4500);
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
bool findAndInputPincode = false;
|
bool findAndInputPassword = false;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
counter++;
|
counter++;
|
||||||
@ -80,40 +69,35 @@ bool CommonZiraatScript::runAppAndGoToHomeScreen(
|
|||||||
}
|
}
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||||||
|
|
||||||
if (xmlScreenParser.isHomeScreen()) {
|
Node closeBannerNode = xmlScreenParser.findButtonNode("HAYIR", false);
|
||||||
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()) {
|
if (!closeBannerNode.isEmpty()) {
|
||||||
AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y());
|
AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y());
|
||||||
QThread::msleep(1000);
|
QThread::msleep(1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xmlScreenParser.isHomeScreen()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем, что это экран ввода пин-кода
|
// Проверяем, что это экран ввода пин-кода
|
||||||
if (!findAndInputPincode) {
|
if (!findAndInputPassword) {
|
||||||
if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН");
|
Node loginNode = xmlScreenParser.findButtonNode("Login", false);
|
||||||
pincode.size() == 4) {
|
if (!loginNode.isEmpty()) {
|
||||||
// чтобы пробудить экран
|
AdbUtils::makeTap(deviceId, loginNode.x(), loginNode.y());
|
||||||
if (Node textPinCodeNode = xmlScreenParser.findTextNode("Введите ПИН"); !textPinCodeNode.isEmpty()) {
|
QThread::msleep(500);
|
||||||
AdbUtils::makeTap(deviceId, textPinCodeNode.x(), textPinCodeNode.y());
|
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;
|
||||||
}
|
}
|
||||||
inputPinCode(deviceId, pincode);
|
|
||||||
// После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить
|
|
||||||
findAndInputPincode = true;
|
|
||||||
QThread::msleep(3000);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,83 +105,30 @@ bool CommonZiraatScript::runAppAndGoToHomeScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommonZiraatScript::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 CommonZiraatScript::goToAllProducts(
|
bool CommonZiraatScript::goToAllProducts(
|
||||||
const QString &deviceId,
|
const QString &deviceId,
|
||||||
const int width,
|
const int width,
|
||||||
const int height
|
const int height
|
||||||
) {
|
) {
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter));
|
||||||
if (tryToCloseAllBannersOnHomePage(deviceId, width, height)) {
|
Node closeBannerNode = xmlScreenParser.findButtonNode("HAYIR", false);
|
||||||
|
if (!closeBannerNode.isEmpty()) {
|
||||||
|
AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y());
|
||||||
QThread::msleep(500);
|
QThread::msleep(500);
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xmlScreenParser.isHomeScreen()) {
|
if (xmlScreenParser.isHomeScreen()) {
|
||||||
// идем на экран "Все продукты"
|
Node allMyAccountBtn = xmlScreenParser.findButtonNode("All My Accounts", false);
|
||||||
if (Node allCardTitle = xmlScreenParser.findTextNode("Счета и карты"); !allCardTitle.isEmpty()) {
|
if (!allMyAccountBtn.isEmpty()) {
|
||||||
// 1. Если нет кнопки видимой, свайпаем. Кликаем на "Все продукты"
|
AdbUtils::makeTap(deviceId, allMyAccountBtn.x(), allMyAccountBtn.y());
|
||||||
for (int i = 0; i < 5; ++i) {
|
QThread::msleep(500);
|
||||||
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 CommonZiraatScript::findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers) {
|
|
||||||
QList<QPair<AccountInfo, Node> > 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 true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommonZiraatScript::postAccountsData(const QList<AccountInfo> &accounts) {
|
||||||
void CommonZiraatScript::postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts) {
|
|
||||||
if (accounts.isEmpty()) {
|
if (accounts.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -206,9 +137,8 @@ void CommonZiraatScript::postAccountsData(const QList<QPair<AccountInfo, Node> >
|
|||||||
auto *worker = new NetworkService;
|
auto *worker = new NetworkService;
|
||||||
|
|
||||||
QJsonArray list;
|
QJsonArray list;
|
||||||
for (const QPair<AccountInfo, Node> &pair: accounts) {
|
for (const AccountInfo &account: accounts) {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
AccountInfo account = pair.first;
|
|
||||||
obj["appName"] = account.appCode;
|
obj["appName"] = account.appCode;
|
||||||
obj["lastNumbers"] = account.lastNumbers;
|
obj["lastNumbers"] = account.lastNumbers;
|
||||||
obj["amount"] = account.amount;
|
obj["amount"] = account.amount;
|
||||||
@ -218,7 +148,7 @@ void CommonZiraatScript::postAccountsData(const QList<QPair<AccountInfo, Node> >
|
|||||||
list.append(obj);
|
list.append(obj);
|
||||||
}
|
}
|
||||||
// FIXME доставить десктоп ид из настроеек
|
// FIXME доставить десктоп ид из настроеек
|
||||||
worker->setDeviceId(accounts.first().first.deviceId);
|
worker->setDeviceId(accounts.first().deviceId);
|
||||||
worker->setPayload(QJsonDocument(list).toJson());
|
worker->setPayload(QJsonDocument(list).toJson());
|
||||||
worker->moveToThread(thread);
|
worker->moveToThread(thread);
|
||||||
connect(thread, &QThread::started, worker, &NetworkService::postAccountsData);
|
connect(thread, &QThread::started, worker, &NetworkService::postAccountsData);
|
||||||
|
|||||||
@ -14,17 +14,13 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void doStart() = 0;
|
virtual void doStart() = 0;
|
||||||
|
|
||||||
static bool inputPinCode(const QString &deviceId, const QList<Node> &pincode);
|
|
||||||
|
|
||||||
bool goToAllProducts(
|
bool goToAllProducts(
|
||||||
const QString &deviceId,
|
const QString &deviceId,
|
||||||
int width,
|
int width,
|
||||||
int height
|
int height
|
||||||
);
|
);
|
||||||
|
|
||||||
bool findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers);
|
void postAccountsData(const QList<AccountInfo> &accounts);
|
||||||
|
|
||||||
void postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts);
|
|
||||||
|
|
||||||
void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction);
|
void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction);
|
||||||
|
|
||||||
@ -50,10 +46,6 @@ protected:
|
|||||||
int height
|
int height
|
||||||
);
|
);
|
||||||
|
|
||||||
bool tryToCloseAllBannersOnHomePage(
|
|
||||||
const QString &deviceId, int width, int height
|
|
||||||
);
|
|
||||||
|
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
ScreenXmlZiraatParser xmlScreenParser;
|
ScreenXmlZiraatParser xmlScreenParser;
|
||||||
int m_counter = 0;
|
int m_counter = 0;
|
||||||
|
|||||||
160
android/ziraat/GetLastDaysHistoryZiraatScript.cpp
Normal file
160
android/ziraat/GetLastDaysHistoryZiraatScript.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include "GetLastDaysHistoryZiraatScript.h"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QThread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#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<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
|
||||||
|
|
||||||
|
QList<AccountInfo> 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);
|
||||||
|
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<TransactionInfo> transactions = xmlScreenParser.parseTodayAndYesterdayReportHistory();
|
||||||
|
if (!transactions.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||||||
|
// все подтвержденные
|
||||||
|
QList<TransactionInfo> parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory();
|
||||||
|
|
||||||
|
QSet<int> 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);
|
||||||
|
}
|
||||||
35
android/ziraat/GetLastDaysHistoryZiraatScript.h
Normal file
35
android/ziraat/GetLastDaysHistoryZiraatScript.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#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
|
||||||
|
);
|
||||||
|
};
|
||||||
69
android/ziraat/LoginAndCheckAccountsZiraatScript.cpp
Normal file
69
android/ziraat/LoginAndCheckAccountsZiraatScript.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#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<AccountInfo> 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);
|
||||||
|
}
|
||||||
23
android/ziraat/LoginAndCheckAccountsZiraatScript.h
Normal file
23
android/ziraat/LoginAndCheckAccountsZiraatScript.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#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;
|
||||||
|
};
|
||||||
@ -12,262 +12,259 @@
|
|||||||
#include "dao/TransactionDAO.h"
|
#include "dao/TransactionDAO.h"
|
||||||
#include "db/ApplicationInfo.h"
|
#include "db/ApplicationInfo.h"
|
||||||
#include "db/DeviceInfo.h"
|
#include "db/DeviceInfo.h"
|
||||||
|
#include "time/DateUtils.h"
|
||||||
|
|
||||||
PayByIbanScript::PayByIbanScript(
|
PayByIbanScript::PayByIbanScript(
|
||||||
AccountInfo account,
|
AccountInfo account,
|
||||||
QString phone,
|
QString iban,
|
||||||
QString bankName,
|
QString fullName,
|
||||||
const double amount,
|
const double amount,
|
||||||
QObject *parent
|
QObject *parent
|
||||||
) : CommonZiraatScript(parent),
|
) : CommonZiraatScript(parent),
|
||||||
m_account(std::move(account)), m_phone(std::move(phone)),
|
m_account(std::move(account)), m_iban(std::move(iban)),
|
||||||
m_bankName(std::move(bankName)), m_amount(amount) {
|
m_fullName(std::move(fullName)), m_amount(amount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PayByIbanScript::~PayByIbanScript() = default;
|
PayByIbanScript::~PayByIbanScript() = default;
|
||||||
|
|
||||||
|
|
||||||
void PayByIbanScript::makePayment(
|
void PayByIbanScript::makePayment(const DeviceInfo &device) {
|
||||||
const QString &deviceId,
|
|
||||||
const QString &pinCode,
|
|
||||||
int width,
|
|
||||||
int height
|
|
||||||
) {
|
|
||||||
// Оплата со страницы карты
|
// Оплата со страницы карты
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
||||||
Node payBtn = xmlScreenParser.findButtonNode("Оплатить", false);
|
Node editIbanEditText = xmlScreenParser.findEditTextByResourceId("com.ziraat.ziraatmobil:id/edit_iban");
|
||||||
if (!payBtn.isEmpty()) {
|
Node fullNameEditText = xmlScreenParser.findEditTextByResourceId("com.ziraat.ziraatmobil:id/edit_full_name");
|
||||||
AdbUtils::makeTap(deviceId, payBtn.x(), payBtn.y());
|
if (!editIbanEditText.isEmpty() && !fullNameEditText.isEmpty()) {
|
||||||
QThread::msleep(1000);
|
AdbUtils::makeTap(device.id, editIbanEditText.x(), editIbanEditText.y());
|
||||||
} else {
|
QThread::msleep(500);
|
||||||
AdbUtils::takeScreenshot(deviceId, "pay_by_phone_pay_btn_not_found");
|
AdbUtils::inputText(device.id, m_iban);
|
||||||
m_error = "Кнопка 'Оплатить' не нашлась";
|
|
||||||
qDebug() << m_error;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
AdbUtils::makeTap(device.id, editIbanEditText.x(), editIbanEditText.y());
|
||||||
// TODO - По номеру карты В РСХБ и другие банки
|
QThread::msleep(500);
|
||||||
Node payTitle = xmlScreenParser.findTextNode("Платежи и переводы");
|
AdbUtils::inputText(device.id, m_fullName);
|
||||||
if (!payTitle.isEmpty() && payTitle.y() < 300) {
|
|
||||||
Node payByPhoneBtn = xmlScreenParser.findButtonNode("По номеру телефона В РСХБ и через", false);
|
Node nextBtn = xmlScreenParser.findButtonNode("NEXT", false);
|
||||||
if (!payByPhoneBtn.isEmpty()) {
|
AdbUtils::makeTap(device.id, nextBtn.x(), nextBtn.y());
|
||||||
AdbUtils::makeTap(deviceId, payByPhoneBtn.x(), payByPhoneBtn.y());
|
|
||||||
|
bool findSenderAccount = false;
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
QThread::msleep(1000);
|
QThread::msleep(1000);
|
||||||
} else {
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
||||||
AdbUtils::takeScreenshot(deviceId, "pay_by_phone_not_found_pay_by_phone");
|
|
||||||
m_error = "Не смогли найти пункт 'По номеру телефона В РСХБ и через'";
|
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;
|
qDebug() << m_error;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_go_to_payments");
|
|
||||||
m_error = "Не смогли перейти на экран 'Платежи и переводы'";
|
|
||||||
qDebug() << m_error;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bool findAmountDetermination = false;
|
||||||
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) {
|
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);
|
QThread::msleep(1000);
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
||||||
}
|
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
Node amountDetermination = xmlScreenParser.findTextNode("Amount Determination");
|
||||||
for (int i = 0; i < 5; ++i) {
|
if (!amountDetermination.isEmpty()) {
|
||||||
Node bankNameListItem = xmlScreenParser.findTextNode(m_bankName);
|
Node integerEditText = xmlScreenParser.findEditTextByResourceId(
|
||||||
if (!bankNameListItem.isEmpty()) {
|
"com.ziraat.ziraatmobil:id/edit_text_integer");
|
||||||
AdbUtils::makeTap(deviceId, bankNameListItem.x(), bankNameListItem.y());
|
Node decimalEditText = xmlScreenParser.findEditTextByResourceId(
|
||||||
QThread::msleep(1000);
|
"com.ziraat.ziraatmobil:id/edit_text_decimal");
|
||||||
break;
|
if (!integerEditText.isEmpty() && !decimalEditText.isEmpty()) {
|
||||||
} else {
|
int integerPart = static_cast<int>(m_amount);
|
||||||
qDebug() << "--- bankNameListItem not found, i: " << i;
|
AdbUtils::makeTap(device.id, integerEditText.x(), integerEditText.y());
|
||||||
}
|
QThread::msleep(500);
|
||||||
QThread::msleep(1000);
|
AdbUtils::inputText(device.id, QString::number(integerPart));
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
int decimalPart = static_cast<int>((m_amount - integerPart) * 100);
|
||||||
|
AdbUtils::makeTap(device.id, decimalEditText.x(), decimalEditText.y());
|
||||||
|
QThread::msleep(500);
|
||||||
|
AdbUtils::inputText(device.id, QString::number(decimalPart));
|
||||||
|
|
||||||
const bool isRshbBank = m_bankName == QStringLiteral("Россельхозбанк");
|
findAmountDetermination = true;
|
||||||
QString payByPhoneScreenTitleText = isRshbBank ? "Перевод клиенту РСХБ" : "Перевод по телефону";
|
break;
|
||||||
Node payByPhoneScreenTitle = xmlScreenParser.findTextNode(payByPhoneScreenTitleText);
|
} else {
|
||||||
Node continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false);
|
m_error = "Не нашли поля ввода для ввода суммы перевода";
|
||||||
|
qDebug() << m_error;
|
||||||
if (isRshbBank ||
|
return;
|
||||||
!payByPhoneScreenTitle.isEmpty() && payByPhoneScreenTitle.y() < 300 && !continuePayBtn.isEmpty()) {
|
}
|
||||||
Node sumBtn = xmlScreenParser.findButtonNode("0", false);
|
}
|
||||||
if (isRshbBank || !sumBtn.isEmpty()) {
|
}
|
||||||
if (!isRshbBank) {
|
if (!findAmountDetermination) {
|
||||||
AdbUtils::makeTap(deviceId, sumBtn.x(), sumBtn.y());
|
m_error = "Не нашли Amount Determination";
|
||||||
QThread::msleep(500);
|
qDebug() << m_error;
|
||||||
}
|
return;
|
||||||
AdbUtils::inputText(deviceId, QString::number(m_amount));
|
}
|
||||||
QThread::msleep(500);
|
|
||||||
|
bool findDetailDefinition = false;
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
for (int i = 0; i < 5; ++i) {
|
||||||
Node readyBtn = xmlScreenParser.findTextNode("Готово");
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.id, ++m_counter));
|
||||||
if (!readyBtn.isEmpty()) {
|
Node fastTransferBtn = xmlScreenParser.findButtonNode("FAST TRANSFER", false);
|
||||||
AdbUtils::makeTap(deviceId, readyBtn.x(), readyBtn.y());
|
if (!fastTransferBtn.isEmpty()) {
|
||||||
QThread::msleep(500);
|
AdbUtils::makeTap(device.id, fastTransferBtn.x(), fastTransferBtn.y());
|
||||||
if (isRshbBank) {
|
QThread::msleep(500);
|
||||||
QThread::msleep(1000);
|
continue;
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
}
|
||||||
continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false);
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AdbUtils::makeTap(deviceId, continuePayBtn.x(), continuePayBtn.y());
|
|
||||||
QThread::msleep(1000);
|
|
||||||
} else {
|
} else {
|
||||||
AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_tap_on_ready");
|
m_error = "Транзакция не успешна";
|
||||||
m_error = "Не смогли нажать 'Готово'";
|
|
||||||
qDebug() << m_error;
|
qDebug() << m_error;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_tap_on_continue");
|
m_error = "Не нашли Check the details title info";
|
||||||
m_error = "Не смогли нажать 'Продолжить'";
|
|
||||||
qDebug() << m_error;
|
qDebug() << m_error;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AdbUtils::takeScreenshot(deviceId, "pay_by_phone_cant_find_bank_title");
|
m_error = "Не нашли поля ввода для IBAN";
|
||||||
m_error = "Не смогли найти заголовок: " + payByPhoneScreenTitleText;
|
|
||||||
qDebug() << m_error;
|
qDebug() << m_error;
|
||||||
return;
|
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<Node> 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 PayByIbanScript::doStart() {
|
void PayByIbanScript::doStart() {
|
||||||
@ -291,46 +288,62 @@ void PayByIbanScript::doStart() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||||||
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
|
Node allTransactionsNav = xmlScreenParser.findTextNode("Transactions");
|
||||||
for (auto &[account, node]: accounts) {
|
if (!allTransactionsNav.isEmpty()) {
|
||||||
account.deviceId = m_account.deviceId;
|
AdbUtils::makeTap(device.id, allTransactionsNav.x(), allTransactionsNav.y());
|
||||||
account.appCode = m_account.appCode;
|
QThread::msleep(1000);
|
||||||
account.status = account.amount > 0 ? AccountStatus::Active : AccountStatus::Blocked;
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||||||
if (!AccountDAO::upsertAccount(account)) {
|
|
||||||
qDebug() << "Not updated: " << account.lastNumbers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
postAccountsData(accounts);
|
// перешли на страницу транзакций
|
||||||
|
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));
|
||||||
|
|
||||||
for (auto &[account, node]: accounts) {
|
// перешли на страницу переводов
|
||||||
// FIXME будем считать что числа последние везде уникальные
|
Node moneyTransfersTitle = xmlScreenParser.findTextNode(
|
||||||
if (m_account.lastNumbers == account.lastNumbers) {
|
"Money Transfers", 0, 0, device.screenWidth, device.screenHeight / 2
|
||||||
if (account.amount >= m_amount) {
|
);
|
||||||
AdbUtils::makeTap(device.id, node.x(), node.y());
|
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);
|
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);
|
makePayment(device);
|
||||||
// AdbUtils::tryToKillApplication(device.id, app.package); TODO - надо ли это???
|
} 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 {
|
} else {
|
||||||
AdbUtils::takeScreenshot(device.id, "pay_by_phone_cant_open_all_products_" + QString::number(m_account.id));
|
m_error = "Не нашли пункт меню 'Transactions'";
|
||||||
AdbUtils::tryToKillApplication(device.id, app.package);
|
|
||||||
m_error = "Не смогли открыть 'Все продукты'";
|
|
||||||
qDebug() << m_error;
|
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);
|
emit finishedWithResult(m_error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "CommonZiraatScript.h"
|
#include "CommonZiraatScript.h"
|
||||||
|
#include "db/DeviceInfo.h"
|
||||||
|
|
||||||
class PayByIbanScript final : public CommonZiraatScript {
|
class PayByIbanScript final : public CommonZiraatScript {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -9,8 +10,8 @@ public:
|
|||||||
|
|
||||||
PayByIbanScript(
|
PayByIbanScript(
|
||||||
AccountInfo account,
|
AccountInfo account,
|
||||||
QString phone,
|
QString iban,
|
||||||
QString bankName,
|
QString fullName,
|
||||||
double amount,
|
double amount,
|
||||||
QObject *parent = nullptr
|
QObject *parent = nullptr
|
||||||
);
|
);
|
||||||
@ -20,15 +21,10 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const AccountInfo m_account;
|
const AccountInfo m_account;
|
||||||
const QString m_phone;
|
const QString m_iban;
|
||||||
const QString m_bankName;
|
const QString m_fullName;
|
||||||
const double m_amount;
|
const double m_amount;
|
||||||
QString m_error;
|
QString m_error;
|
||||||
|
|
||||||
void makePayment(
|
void makePayment(const DeviceInfo &device);
|
||||||
const QString &deviceId,
|
|
||||||
const QString &pinCode,
|
|
||||||
int width,
|
|
||||||
int height
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -59,6 +59,11 @@ Node ScreenXmlZiraatParser::tryToFindBannerOrDialog(const int width, const int h
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node ScreenXmlZiraatParser::findAccountButton(const QString &numbers) {
|
||||||
|
// TODO возможно это не так, надо уточнять
|
||||||
|
return findTextNode(QString("LEVENT - %1").arg(numbers));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Смотрим что есть текс "Мы заботимся о вашей безопасности" и
|
* Смотрим что есть текс "Мы заботимся о вашей безопасности" и
|
||||||
* кнопка "Отложить"
|
* кнопка "Отложить"
|
||||||
@ -117,9 +122,18 @@ Node ScreenXmlZiraatParser::findTextNode(const QString &text, const int x1, cons
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Node ScreenXmlZiraatParser::findFirstEditText() {
|
Node ScreenXmlZiraatParser::findEditText(const QString &text) {
|
||||||
for (const Node &node: m_nodes) {
|
for (const Node &node: m_nodes) {
|
||||||
if (node.className == "android.widget.EditText" && node.focusable) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,18 +156,17 @@ Node ScreenXmlZiraatParser::findButtonNode(const QString &text, const bool conta
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenXmlZiraatParser::isHomeScreen() {
|
bool ScreenXmlZiraatParser::isHomeScreen() {
|
||||||
// Есть заголовок "Счета и карты" и кнопка "Все продукты"
|
Node activitiesBtn;
|
||||||
Node text;
|
|
||||||
Node allProductBtn;
|
Node allProductBtn;
|
||||||
for (const Node &node: m_nodes) {
|
for (const Node &node: m_nodes) {
|
||||||
if (node.text.trimmed() == "Счета и карты" && node.className == "android.widget.TextView") {
|
if (node.text.trimmed() == "Account Activities" && node.className == "android.widget.Button") {
|
||||||
text = node;
|
activitiesBtn = node;
|
||||||
}
|
}
|
||||||
if (node.text.trimmed() == "Все продукты" && node.className == "android.widget.Button") {
|
if (node.text.trimmed() == "All My Accounts" && node.className == "android.widget.Button") {
|
||||||
allProductBtn = node;
|
allProductBtn = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !text.isEmpty() && !allProductBtn.isEmpty();
|
return !activitiesBtn.isEmpty() && !allProductBtn.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Node> ScreenXmlZiraatParser::findPinCodeNodes(const QString &pinCode) {
|
QList<Node> ScreenXmlZiraatParser::findPinCodeNodes(const QString &pinCode) {
|
||||||
@ -174,22 +187,6 @@ QList<Node> ScreenXmlZiraatParser::findPinCodeNodes(const QString &pinCode) {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Node> ScreenXmlZiraatParser::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<Node> ScreenXmlZiraatParser::getLast2DaysTransactions() {
|
QList<Node> ScreenXmlZiraatParser::getLast2DaysTransactions() {
|
||||||
QList<Node> nodes;
|
QList<Node> nodes;
|
||||||
for (const Node &node: m_nodes) {
|
for (const Node &node: m_nodes) {
|
||||||
@ -546,7 +543,8 @@ QDomElement findElementWithListView(const QDomElement &parent) {
|
|||||||
while (!element.isNull()) {
|
while (!element.isNull()) {
|
||||||
QDomElement child = element.firstChildElement();
|
QDomElement child = element.firstChildElement();
|
||||||
while (!child.isNull()) {
|
while (!child.isNull()) {
|
||||||
if (child.hasAttribute("class") && child.attribute("class") == "android.widget.ListView") {
|
if (child.hasAttribute("class") && child.attribute("class") ==
|
||||||
|
"androidx.recyclerview.widget.RecyclerView") {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
child = child.nextSiblingElement();
|
child = child.nextSiblingElement();
|
||||||
@ -561,62 +559,66 @@ QDomElement findElementWithListView(const QDomElement &parent) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QDateTime getUtcFromTurkey(const QString &d, const QString &m, const QString &t) {
|
||||||
|
const QMap<QString, int> 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<TransactionInfo> ScreenXmlZiraatParser::parseTodayAndYesterdayReportHistory() {
|
QList<TransactionInfo> ScreenXmlZiraatParser::parseTodayAndYesterdayReportHistory() {
|
||||||
QList<TransactionInfo> transactions;
|
QList<TransactionInfo> transactions;
|
||||||
|
|
||||||
QDomElement child = findElementWithListView(m_xml);
|
QDomElement child = findElementWithListView(m_xml);
|
||||||
QDomNodeList listView = findElementWithListView(m_xml).childNodes();
|
QDomNodeList listView = child.childNodes();
|
||||||
qDebug() << listView.count();
|
qDebug() << listView.count();
|
||||||
if (!listView.isEmpty()) {
|
if (!listView.isEmpty()) {
|
||||||
QString date;
|
|
||||||
for (QDomNode node: listView) {
|
for (QDomNode node: listView) {
|
||||||
if (node.hasChildNodes()) {
|
const QDomNodeList trBoxGroup = node.childNodes();
|
||||||
QString text;
|
if (trBoxGroup.count() > 0) {
|
||||||
if (node.childNodes().count() > 1) {
|
for (QDomNode item: trBoxGroup) {
|
||||||
date = node.firstChildElement().attribute("text");
|
TransactionInfo transaction;
|
||||||
text = node.lastChildElement().attribute("text");
|
QDomElement groupItem = item.toElement();
|
||||||
} else if (node.childNodes().count() == 1) {
|
if (groupItem.attribute("index") == "0") {
|
||||||
text = node.firstChildElement().attribute("text");
|
for (QDomNode subDate: groupItem.childNodes()) {
|
||||||
}
|
QDomElement dateItem = subDate.toElement();
|
||||||
|
QString date;
|
||||||
TransactionInfo transaction;
|
QString month;
|
||||||
if (text.startsWith("Перевод")) {
|
QString time;
|
||||||
QRegularExpression re(R"([+-] \d{1,3}(?: \d{3})* точка \d{2} Рубль$)");
|
if (dateItem.attribute("index") == "0") {
|
||||||
QRegularExpressionMatch match = re.match(text);
|
date = dateItem.attribute("text");
|
||||||
if (match.hasMatch()) {
|
} else if (groupItem.attribute("index") == "1") {
|
||||||
QString amount = match.captured(0);
|
month = groupItem.attribute("text");
|
||||||
transaction.amount = parseAmount(amount.trimmed());
|
} else if (groupItem.attribute("index") == "2") {
|
||||||
text.chop(amount.length());
|
time = groupItem.attribute("text");
|
||||||
}
|
}
|
||||||
transaction.description = text.trimmed();
|
transaction.bankTime = date + " " + month + " " + time;
|
||||||
parseTransferInfo(text, transaction);
|
transaction.completeTime = getUtcFromTurkey(date, month, time);
|
||||||
if (transaction.bankTime.isEmpty()) {
|
transaction.timestamp = DateUtils::getUtcNow();
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
} 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();
|
||||||
}
|
}
|
||||||
QRegularExpression re(R"([+-] \d{1,3}(?: \d{3})* точка \d{2} Рубль$)");
|
transactions.append(transaction);
|
||||||
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 {
|
} else {
|
||||||
@ -920,29 +922,39 @@ TransactionInfo ScreenXmlZiraatParser::parseTransactionInfo(int screenWidth, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QList<QPair<AccountInfo, Node> > ScreenXmlZiraatParser::parseAccountsInfo() {
|
QList<AccountInfo> ScreenXmlZiraatParser::parseAccountsInfo() {
|
||||||
QList<QPair<AccountInfo, Node> > accounts;
|
const QDomNodeList nodes = m_xml.elementsByTagName("node");
|
||||||
for (const Node &node: m_nodes) {
|
QList<AccountInfo> accounts;
|
||||||
const QString text = node.text;
|
|
||||||
|
|
||||||
// Проверяем только нужные строки
|
for (int i = 0; i < nodes.count(); ++i) {
|
||||||
if (!text.contains("Номер карты с последними цифрами")) {
|
const QDomElement el = nodes.at(i).toElement();
|
||||||
continue;
|
if (el.attribute("resource-id") == "com.ziraat.ziraatmobil:id/text_account_or_bank_name" &&
|
||||||
|
el.attribute("text").startsWith("LEVENT")
|
||||||
|
) {
|
||||||
|
// Нашли нужный аккаунт
|
||||||
|
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.split("-").last().trimmed();
|
||||||
|
info.amount = amount.toFloat();
|
||||||
|
info.description = account;
|
||||||
|
info.updateTime = DateUtils::getUtcNow();
|
||||||
|
|
||||||
|
accounts.append(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
n = n.nextSibling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,24 +20,26 @@ public:
|
|||||||
|
|
||||||
Node tryToFindSelectBottomSheet();
|
Node tryToFindSelectBottomSheet();
|
||||||
|
|
||||||
|
Node findAccountButton(const QString &numbers);
|
||||||
|
|
||||||
Node findTextNode(const QString &text);
|
Node findTextNode(const QString &text);
|
||||||
|
|
||||||
Node findTextNode(const QString &text, int x1, int y1, int x2, int y2);
|
Node findTextNode(const QString &text, int x1, int y1, int x2, int y2);
|
||||||
|
|
||||||
Node findFirstEditText();
|
Node findEditText(const QString &text);
|
||||||
|
|
||||||
|
Node findEditTextByResourceId(const QString &resourceId);
|
||||||
|
|
||||||
Node findButtonNode(const QString &text, bool contains);
|
Node findButtonNode(const QString &text, bool contains);
|
||||||
|
|
||||||
bool isHomeScreen();
|
bool isHomeScreen();
|
||||||
|
|
||||||
|
|
||||||
QList<Node> tryToFindPinCode(const QString &pinCode, const QString &title);
|
|
||||||
|
|
||||||
QList<Node> getLast2DaysTransactions();
|
QList<Node> getLast2DaysTransactions();
|
||||||
|
|
||||||
void parseAndSaveXml(const QString &xml);
|
void parseAndSaveXml(const QString &xml);
|
||||||
|
|
||||||
TransactionInfo parseTransactionInfoHistory(int screenWidth, int screenHeight);
|
TransactionInfo parseTransactionInfoHistory(int screenWidth, int screenHeight);
|
||||||
|
|
||||||
TransactionInfo parseTransactionInfo(int screenWidth, int screenHeight);
|
TransactionInfo parseTransactionInfo(int screenWidth, int screenHeight);
|
||||||
|
|
||||||
QList<TransactionInfo> parseTodayAndYesterdayReportHistory();
|
QList<TransactionInfo> parseTodayAndYesterdayReportHistory();
|
||||||
@ -45,7 +47,7 @@ public:
|
|||||||
QList<TransactionInfo> parseTodayAndYesterdayHistory();
|
QList<TransactionInfo> parseTodayAndYesterdayHistory();
|
||||||
|
|
||||||
|
|
||||||
QList<QPair<AccountInfo, Node> > parseAccountsInfo();
|
QList<AccountInfo> parseAccountsInfo();
|
||||||
|
|
||||||
void test();
|
void test();
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,10 @@ android_package_name=ru.rshb.dbo
|
|||||||
name=Альфа-Банк
|
name=Альфа-Банк
|
||||||
android_package_name=ru.alfabank.mobile.android
|
android_package_name=ru.alfabank.mobile.android
|
||||||
|
|
||||||
|
[ziraat]
|
||||||
|
name=Ziraat
|
||||||
|
android_package_name=com.ziraat.ziraatmobil
|
||||||
|
|
||||||
[santanderbank]
|
[santanderbank]
|
||||||
name=Santander
|
name=Santander
|
||||||
android_package_name=ar.com.santander.rio.mbanking
|
android_package_name=ar.com.santander.rio.mbanking
|
||||||
|
|||||||
@ -22,4 +22,4 @@ QDateTime DateUtils::fromUtcString(const QString ×tamp) {
|
|||||||
|
|
||||||
QString DateUtils::toUtcString(const QDateTime &dateTime) {
|
QString DateUtils::toUtcString(const QDateTime &dateTime) {
|
||||||
return dateTime.toString("yyyy-MM-dd HH:mm:ss");
|
return dateTime.toString("yyyy-MM-dd HH:mm:ss");
|
||||||
}
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
enum class TransactionType {
|
enum class TransactionType {
|
||||||
Card,
|
Card,
|
||||||
Phone,
|
Phone,
|
||||||
|
Iban,
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ inline QString transactionTypeToString(const TransactionType type) {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case TransactionType::Card: return "card";
|
case TransactionType::Card: return "card";
|
||||||
case TransactionType::Phone: return "phone";
|
case TransactionType::Phone: return "phone";
|
||||||
|
case TransactionType::Iban: return "iban";
|
||||||
default: return "unknown";
|
default: return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,6 +29,7 @@ inline QString transactionTypeToString(const TransactionType type) {
|
|||||||
inline TransactionType transactionTypeFromString(const QString &str) {
|
inline TransactionType transactionTypeFromString(const QString &str) {
|
||||||
if (str == "card") return TransactionType::Card;
|
if (str == "card") return TransactionType::Card;
|
||||||
if (str == "phone") return TransactionType::Phone;
|
if (str == "phone") return TransactionType::Phone;
|
||||||
|
if (str == "iban") return TransactionType::Iban;
|
||||||
return TransactionType::Unknown;
|
return TransactionType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user