251 lines
10 KiB
C++
251 lines
10 KiB
C++
#include "GetLastTransactionsScript.h"
|
|
|
|
#include <QThread>
|
|
#include <QTimeZone>
|
|
|
|
#include "adb/AdbUtils.h"
|
|
#include "dao/MaterialDAO.h"
|
|
#include "dao/BankProfileDAO.h"
|
|
#include "dao/DeviceDAO.h"
|
|
#include "dao/TransactionDAO.h"
|
|
#include "AppLogger.h"
|
|
|
|
namespace Dushanbe {
|
|
|
|
GetLastTransactionsScript::GetLastTransactionsScript(
|
|
QString deviceId,
|
|
QString appCode,
|
|
double searchAmount,
|
|
QDateTime searchTime,
|
|
QObject *parent
|
|
) : CommonScript(parent),
|
|
m_deviceId(std::move(deviceId)),
|
|
m_appCode(std::move(appCode)),
|
|
m_searchAmount(searchAmount),
|
|
m_searchTime(std::move(searchTime)) {
|
|
}
|
|
|
|
GetLastTransactionsScript::~GetLastTransactionsScript() = default;
|
|
|
|
void GetLastTransactionsScript::doStart() {
|
|
DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); if (device.id.isEmpty()) device = DeviceDAO::findByAdbSerial(m_deviceId); if (!device.adbSerial.isEmpty()) m_deviceId = device.adbSerial;
|
|
const BankProfileInfo app = BankProfileDAO::getApplication(device.id, 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 (!xmlScreenParser.isHomeScreen()) {
|
|
qDebug() << "[Dushanbe::GetLastTransactions] Not on home screen, restarting app...";
|
|
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode,
|
|
device.screenWidth, device.screenHeight)) {
|
|
m_error = "Cannot reach home screen: " + m_deviceId;
|
|
qWarning() << m_error;
|
|
AppLogger::log("dushanbe/GetLastTransactions", m_deviceId, m_error,
|
|
AdbUtils::captureScreenshotBytes(m_deviceId),
|
|
"FETCH_TRANSACTIONS");
|
|
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
|
emit finishedWithResult(m_error);
|
|
return;
|
|
}
|
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
|
}
|
|
|
|
// Тапаем "История" в нижнем меню
|
|
Node historyBtn = xmlScreenParser.findNodeByContentDesc(QString::fromUtf8("История"));
|
|
if (historyBtn.x() == 0 && historyBtn.y() == 0) {
|
|
m_error = "Cannot find History button: " + m_deviceId;
|
|
qWarning() << m_error;
|
|
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
|
emit finishedWithResult(m_error);
|
|
return;
|
|
}
|
|
|
|
AdbUtils::makeTap(m_deviceId, historyBtn.x(), historyBtn.y());
|
|
QThread::msleep(3000);
|
|
|
|
// Ждём экран истории
|
|
bool historyLoaded = false;
|
|
for (int attempt = 0; attempt < 10; ++attempt) {
|
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
|
if (xmlScreenParser.isHistoryScreen()) {
|
|
historyLoaded = true;
|
|
break;
|
|
}
|
|
QThread::msleep(1500);
|
|
}
|
|
|
|
if (!historyLoaded) {
|
|
m_error = "History screen not loaded: " + m_deviceId;
|
|
qWarning() << m_error;
|
|
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
|
emit finishedWithResult(m_error);
|
|
return;
|
|
}
|
|
|
|
// Находим accountId
|
|
int accountId = -1;
|
|
const QList<MaterialInfo> accounts = MaterialDAO::getAccounts(device.id, m_appCode);
|
|
if (!accounts.isEmpty()) {
|
|
accountId = accounts.first().id;
|
|
} else {
|
|
qWarning() << "[Dushanbe::GetLastTransactions] No account found";
|
|
}
|
|
|
|
qDebug() << "[Dushanbe::GetLastTransactions] Searching: amount=" << m_searchAmount
|
|
<< "time=" << m_searchTime.toString(Qt::ISODate);
|
|
|
|
const QTimeZone tjTz("Asia/Dushanbe"); // UTC+5
|
|
const int maxScrolls = 10;
|
|
|
|
for (int scroll = 0; scroll <= maxScrolls; ++scroll) {
|
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
|
const QList<ScreenXmlParser::TransactionItem> items = xmlScreenParser.parseTransactionItems();
|
|
|
|
for (const auto &tx : items) {
|
|
if (qAbs(tx.amount - m_searchAmount) > 0.01) continue;
|
|
|
|
qDebug() << "[Dushanbe::GetLastTransactions] Amount match:" << tx.amount
|
|
<< "phone:" << tx.phone << "time:" << tx.time;
|
|
|
|
// Нашли по сумме — тапаем для получения деталей
|
|
// Ищем соответствующий node чтобы по нему тапнуть
|
|
Node txNode;
|
|
for (const Node &node : xmlScreenParser.findAllNodes()) {
|
|
if (node.className != "android.widget.ImageView" || !node.clickable) continue;
|
|
if (!node.contentDesc.contains("***")) continue;
|
|
// Проверяем что содержит нужную сумму
|
|
QString amountStr = QString::number(tx.amount, 'f', 2);
|
|
if (node.contentDesc.contains(amountStr)) {
|
|
txNode = node;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (txNode.x() == 0 && txNode.y() == 0) {
|
|
qWarning() << "[Dushanbe::GetLastTransactions] Cannot find node for transaction";
|
|
continue;
|
|
}
|
|
|
|
AdbUtils::makeTap(m_deviceId, txNode.x(), txNode.y());
|
|
QThread::msleep(2000);
|
|
|
|
// Ждём экран деталей
|
|
bool detailLoaded = false;
|
|
for (int attempt = 0; attempt < 10; ++attempt) {
|
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
|
if (xmlScreenParser.isTransactionDetailScreen()) {
|
|
detailLoaded = true;
|
|
break;
|
|
}
|
|
QThread::msleep(1500);
|
|
}
|
|
|
|
if (!detailLoaded) {
|
|
qWarning() << "[Dushanbe::GetLastTransactions] Detail screen not loaded";
|
|
AdbUtils::goBack(m_deviceId);
|
|
QThread::msleep(1000);
|
|
continue;
|
|
}
|
|
|
|
const auto detail = xmlScreenParser.parseTransactionDetail();
|
|
|
|
// Проверяем время ±1 час
|
|
if (m_searchTime.isValid() && !detail.date.isEmpty() && !detail.time.isEmpty()) {
|
|
QDateTime txTime = QDateTime::fromString(
|
|
detail.date + " " + detail.time, "dd.MM.yyyy HH:mm:ss");
|
|
if (!txTime.isValid()) {
|
|
txTime = QDateTime::fromString(detail.date + " " + detail.time, "dd.MM.yyyy HH:mm");
|
|
}
|
|
if (txTime.isValid()) {
|
|
txTime.setTimeZone(tjTz);
|
|
const qint64 diffSecs = qAbs(txTime.toUTC().secsTo(m_searchTime.toUTC()));
|
|
if (diffSecs > 3600) {
|
|
qDebug() << "[Dushanbe::GetLastTransactions] Time mismatch: diff=" << diffSecs << "sec, skipping";
|
|
AdbUtils::goBack(m_deviceId);
|
|
QThread::msleep(1000);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Нашли! Сохраняем
|
|
qDebug() << "[Dushanbe::GetLastTransactions] FOUND:"
|
|
<< "amount=" << detail.amount
|
|
<< "date=" << detail.date << detail.time
|
|
<< "opId=" << detail.operationId
|
|
<< "receiver=" << detail.receiverAccount
|
|
<< "status=" << detail.status;
|
|
|
|
if (accountId != -1) {
|
|
TransactionInfo saveTx;
|
|
saveTx.accountId = accountId;
|
|
saveTx.amount = detail.amount;
|
|
saveTx.fee = detail.fee;
|
|
saveTx.bankName = m_appCode;
|
|
saveTx.bankTime = detail.date + " " + detail.time;
|
|
saveTx.bankTrExternalId = detail.operationId;
|
|
saveTx.description = detail.provider;
|
|
saveTx.phone = detail.receiverAccount;
|
|
saveTx.name = detail.receiverAccount;
|
|
saveTx.status = (detail.status == QString::fromUtf8("Успешный"))
|
|
? TransactionStatus::Complete : TransactionStatus::Unknown;
|
|
saveTx.type = TransactionType::Phone;
|
|
saveTx.timestamp = QDateTime::currentDateTimeUtc();
|
|
|
|
QDateTime txTime = QDateTime::fromString(
|
|
detail.date + " " + detail.time, "dd.MM.yyyy HH:mm:ss");
|
|
if (txTime.isValid()) {
|
|
txTime.setTimeZone(tjTz);
|
|
saveTx.completeTime = txTime;
|
|
}
|
|
|
|
TransactionDAO::insertTransactionIfNotExists(saveTx);
|
|
}
|
|
|
|
// Назад к истории
|
|
Node nazadBtn = xmlScreenParser.findNodeByContentDesc(QString::fromUtf8("Назад"));
|
|
if (nazadBtn.x() > 0 && nazadBtn.y() > 0) {
|
|
AdbUtils::makeTap(m_deviceId, nazadBtn.x(), nazadBtn.y());
|
|
} else {
|
|
AdbUtils::goBack(m_deviceId);
|
|
}
|
|
QThread::msleep(1000);
|
|
|
|
emit finishedWithResult(m_error); // m_error пустой — успех
|
|
return;
|
|
}
|
|
|
|
// Скроллим вниз
|
|
if (scroll < maxScrolls) {
|
|
const int x = device.screenWidth / 2;
|
|
const int fromY = device.screenHeight * 3 / 4;
|
|
const int toY = device.screenHeight / 4;
|
|
AdbUtils::makeSwipe(m_deviceId, x, fromY, x, toY);
|
|
QThread::msleep(2000);
|
|
}
|
|
}
|
|
|
|
m_error = "Transaction not found: amount=" + QString::number(m_searchAmount, 'f', 2)
|
|
+ " time=" + m_searchTime.toString(Qt::ISODate);
|
|
qWarning() << "[Dushanbe::GetLastTransactions]" << m_error;
|
|
|
|
// Возвращаемся на главную
|
|
Node glavnayaBtn = xmlScreenParser.findNodeByContentDesc(QString::fromUtf8("Главная"));
|
|
if (glavnayaBtn.x() > 0 && glavnayaBtn.y() > 0) {
|
|
AdbUtils::makeTap(m_deviceId, glavnayaBtn.x(), glavnayaBtn.y());
|
|
} else {
|
|
AdbUtils::goBack(m_deviceId);
|
|
}
|
|
QThread::msleep(1000);
|
|
|
|
emit finishedWithResult(m_error);
|
|
}
|
|
|
|
} // namespace Dushanbe
|