Revised scripts for payments and history retrieval, introducing improved error management with screenshots for debugging. Added a new LoginAndCheckAccountsScript for account validation, streamlined multi-threading, and updated application logic for clarity and reliability.
336 lines
15 KiB
C++
336 lines
15 KiB
C++
#include "PayByPhoneScript.h"
|
||
|
||
#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/ApplicationInfo.h"
|
||
#include "db/DeviceInfo.h"
|
||
|
||
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<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 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<QPair<AccountInfo, Node> > 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);
|
||
|
||
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);
|
||
}
|