324 lines
15 KiB
C++
324 lines
15 KiB
C++
#pragma once
|
||
|
||
#include <QObject>
|
||
#include <QString>
|
||
#include <QVariantList>
|
||
#include <QVariantMap>
|
||
|
||
#include "android/xml/Node.h"
|
||
|
||
class QJSEngine;
|
||
|
||
namespace Black { class ScreenXmlParser; }
|
||
namespace Ozon { class ScreenXmlParser; }
|
||
|
||
namespace Scripting {
|
||
|
||
// Лог из JS попадает в qDebug/qWarning с префиксом банка/скрипта.
|
||
class LogBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
LogBridge(QString tag, QObject *parent = nullptr) : QObject(parent), m_tag(std::move(tag)) {}
|
||
public slots:
|
||
void debug(const QString &message);
|
||
void info(const QString &message);
|
||
void warn(const QString &message);
|
||
void error(const QString &message);
|
||
private:
|
||
QString m_tag;
|
||
};
|
||
|
||
// Sleep + проверка interrupt (скрипт крутится в worker-потоке EventHandler'а).
|
||
class TimerBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit TimerBridge(QObject *parent = nullptr) : QObject(parent) {}
|
||
public slots:
|
||
void sleep(int ms); // блокирующий, проверяет interrupt
|
||
bool isInterrupted() const; // ручная проверка из JS
|
||
};
|
||
|
||
// Прокси над AdbUtils. Все методы статические в C++ — здесь оборачиваем как Q_INVOKABLE,
|
||
// чтобы JS видел их как host.adb.<method>.
|
||
class AdbBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit AdbBridge(QObject *parent = nullptr) : QObject(parent) {}
|
||
public slots:
|
||
QString getScreenDumpAsXml(const QString &deviceId, int idx);
|
||
QString getRawScreenDumpAsXml(const QString &deviceId);
|
||
bool makeTap(const QString &deviceId, int x, int y);
|
||
bool makeSwipe(const QString &deviceId, int x1, int y1, int x2, int y2);
|
||
bool makeSwipeWithDuration(const QString &deviceId, int x1, int y1, int x2, int y2, int durationMs);
|
||
bool tryToStartApplication(const QString &deviceId, const QString &packageName);
|
||
bool tryToKillApplication(const QString &deviceId, const QString &packageName);
|
||
QString tryToGetRunningAppPid(const QString &deviceId, const QString &packageName);
|
||
bool takeScreenshot(const QString &deviceId, const QString &name);
|
||
bool inputText(const QString &deviceId, const QString &text);
|
||
bool inputAdbIMEText(const QString &deviceId, const QString &text);
|
||
bool enableAdbIMEKeyboard(const QString &deviceId);
|
||
bool disableAdbIMEKeyboard(const QString &deviceId);
|
||
bool hideKeyboard(const QString &deviceId);
|
||
bool goBack(const QString &deviceId);
|
||
void unlockScreen(const QString &deviceId, int screenWidth, int screenHeight);
|
||
QString getDeviceTimezone(const QString &deviceId);
|
||
QString captureScreenshotBase64(const QString &deviceId);
|
||
};
|
||
|
||
// DAO-прокси: read для устройства/профиля/материалов, write для баланса/профиля.
|
||
class DaoBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit DaoBridge(QObject *parent = nullptr) : QObject(parent) {}
|
||
public slots:
|
||
// DeviceDAO
|
||
QVariantMap deviceGetById(const QString &id);
|
||
QVariantMap deviceFindByAdbSerial(const QString &serial);
|
||
|
||
// BankProfileDAO
|
||
QVariantMap bankProfileGet(const QString &deviceId, const QString &appCode);
|
||
bool bankProfileUpdateAmount(int profileId, double amount);
|
||
bool bankProfileUpdateProfileInfo(int profileId, const QString &fullName,
|
||
const QString &phone, const QString &email);
|
||
bool bankProfileUpdateComment(int profileId, const QString &comment);
|
||
|
||
// MaterialDAO
|
||
QVariantList materialGetAccounts(const QString &deviceId, const QString &appCode);
|
||
QVariantMap materialFindAppAccount(const QString &deviceId, const QString &appCode,
|
||
const QString &lastNumbers);
|
||
bool materialUpsert(const QVariantMap &material);
|
||
bool materialUpdateCardNumber(int accountId, const QString &cardNumber);
|
||
bool materialUpdateAccountById(int id, const QString &status,
|
||
const QString &description, const QString ¤cy);
|
||
|
||
// SettingsDAO
|
||
QString settingsGet(const QString &key);
|
||
|
||
// TransactionDAO — для платёжных скриптов и сборщика транзакций.
|
||
// tx map: {accountId, amount, fee, name, phone, bankName, type, status,
|
||
// timestamp (ISO UTC string), completeTime (ISO local string),
|
||
// bankTime, description, updatedDescription, bankTrExternalId}
|
||
// Возвращает id вставленной транзакции (-1 при ошибке).
|
||
int transactionInsert(const QVariantMap &tx);
|
||
bool transactionUpdateBankTrExternalId(int id, const QString &externalId);
|
||
};
|
||
|
||
// Скоуп дампа задачи. JS-style API: begin/end вместо RAII (вешаем на try/finally).
|
||
class DumpBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit DumpBridge(QObject *parent = nullptr) : QObject(parent) {}
|
||
~DumpBridge() override { endScope(QString()); }
|
||
public slots:
|
||
void beginScope(const QString &bank, const QString &taskKind, const QString &adbDeviceId,
|
||
const QVariantMap &meta);
|
||
void endScope(const QString &errorMessage); // пусто = success
|
||
private:
|
||
void *m_scope = nullptr;
|
||
QString *m_errorPtr = nullptr;
|
||
};
|
||
|
||
// Прокси к C++ скрипту-родителю: позволяет JS эмитить Qt-сигналы на исходном
|
||
// скрипте (например, transactionParsed → EventHandler). Если signalSink не
|
||
// передан в ScriptRuntime::run — методы no-op (но не throw).
|
||
class ScriptObjectBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit ScriptObjectBridge(QObject *sink = nullptr, QObject *parent = nullptr)
|
||
: QObject(parent), m_sink(sink) {}
|
||
public slots:
|
||
// Парсит jsonStr и вызывает на m_sink сигнал/слот `transactionParsed(QJsonObject)`.
|
||
// Если sink нет или JSON битый — лог warning, продолжение работы.
|
||
void emitTransactionParsed(const QString &jsonStr);
|
||
private:
|
||
QObject *m_sink;
|
||
};
|
||
|
||
// OCR-хелперы: распознавание текста на скриншоте и извлечение ID транзакции.
|
||
// Реализация в android/ocr/OcrUtils — здесь чистый wrapper для JS.
|
||
class OcrBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit OcrBridge(QObject *parent = nullptr) : QObject(parent) {}
|
||
public slots:
|
||
// Захватывает экран и прогоняет через OCR (grayscale + 300dpi, rus+eng).
|
||
QString recognizeFromScreen(const QString &deviceId);
|
||
// Извлекает "ID операции ..." / "№ Ф-..." из распознанного OCR-текста.
|
||
QString extractTransactionId(const QString &ocrText);
|
||
};
|
||
|
||
// Парсер экрана для банка Black. Каждый запуск скрипта получает свой инстанс,
|
||
// поэтому m_xml/m_nodes не разделяются между задачами.
|
||
class BlackParserBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit BlackParserBridge(QObject *parent = nullptr);
|
||
~BlackParserBridge() override;
|
||
|
||
Black::ScreenXmlParser *parser() const { return m_parser; }
|
||
|
||
public slots:
|
||
void parseAndSaveXml(const QString &xml);
|
||
bool isHomeScreen();
|
||
bool isSideMenu();
|
||
bool isPinCodeScreen();
|
||
bool isProfilePage();
|
||
bool isCVUScreen();
|
||
bool isTransactionLoadingScreen();
|
||
bool isTransactionListScreen();
|
||
bool isPayInputCardNumberScreen();
|
||
bool isPayTransferConfirmScreen();
|
||
bool isPayInputSumScreen();
|
||
bool isPayConfirmScreen();
|
||
bool isPaySuccessScreen();
|
||
|
||
QVariantMap findHolaButton();
|
||
QVariantMap findTextNode(const QString &text);
|
||
QVariantMap findNodeByContentDesc(const QString &contentDesc);
|
||
QVariantMap findNodeByContentDescContains(const QString &contentDesc);
|
||
QVariantMap findButtonNode(const QString &text, bool contains);
|
||
QVariantMap findFirstEditText();
|
||
QVariantMap findPinCodeEditText();
|
||
QVariantMap findVerMasAfterTuActividad();
|
||
QVariantMap findEditTextByHint(const QString &hint);
|
||
|
||
QString parseUserNameFromHomeScreen();
|
||
double parseBalanceFromHomeScreen();
|
||
QString parseProfileNombre();
|
||
QString parseProfileApellido();
|
||
QString parseProfileEmail();
|
||
QString parseProfilePhone();
|
||
QString parseCVUNumber();
|
||
QString parseRecipientNameFromConfirm();
|
||
QString parsePaySuccessDateTime();
|
||
QString parsePaySuccessCoelsaId();
|
||
|
||
private:
|
||
Black::ScreenXmlParser *m_parser;
|
||
};
|
||
|
||
// Высокоуровневые операции, повторяющие методы Black::CommonScript:
|
||
// runAppAndGoToHomeScreen, postAccountsData, createBankProfileIfNeeded.
|
||
// Используют ТОТ ЖЕ парсер, что и BlackParserBridge — состояние (XML) общее.
|
||
class BlackHelpersBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
BlackHelpersBridge(BlackParserBridge *parser, QObject *parent = nullptr)
|
||
: QObject(parent), m_parserBridge(parser) {}
|
||
public slots:
|
||
bool runAppAndGoToHomeScreen(const QString &deviceId, const QString &packageName,
|
||
const QString &pinCode, int width, int height);
|
||
void postAccountsData(const QVariantList &accountsWithEmptyMaterialId);
|
||
void createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance);
|
||
private:
|
||
BlackParserBridge *m_parserBridge;
|
||
};
|
||
|
||
// Парсер экрана для банка Ozon. Минимальный набор для get_profile_info — при
|
||
// миграции login_and_check / get_card_info добавлять методы сюда же.
|
||
class OzonParserBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
explicit OzonParserBridge(QObject *parent = nullptr);
|
||
~OzonParserBridge() override;
|
||
|
||
Ozon::ScreenXmlParser *parser() const { return m_parser; }
|
||
|
||
public slots:
|
||
void parseAndSaveXml(const QString &xml);
|
||
|
||
bool isHomeScreen();
|
||
bool isProfileScreen();
|
||
|
||
QVariantMap tryToFindAdBanner();
|
||
QVariantMap tryToFindGooglePlayBanner(int screenWidth, int screenHeight);
|
||
QVariantMap findUserNameOnHomeScreen(int screenHeight);
|
||
QVariantMap findCardButtonByLastNumbers(const QString &lastNumbers);
|
||
QVariantMap findButtonNode(const QString &text, bool contains);
|
||
QVariantMap findTextNode(const QString &text);
|
||
QVariantMap findFirstEditText();
|
||
QVariantMap findEditTextByHint(const QString &hintPrefix);
|
||
QVariantMap findPanCardNumberInput();
|
||
QVariantMap findMainAccountButton();
|
||
QVariantMap findNodeByContentDesc(const QString &contentDesc);
|
||
|
||
// Все Node'ы текущего дампа как массив map — нужно для JS-итерации в
|
||
// payment-скриптах (поиск EditText'ов с input___ rid, других банков по
|
||
// content-desc="other-bank-item" и т.д.).
|
||
QVariantList findAllNodes();
|
||
QVariantList findAllButtons();
|
||
|
||
// pinCode + title — возвращает массив node-маn в порядке цифр для тапа.
|
||
QVariantList tryToFindPinCode(const QString &pinCode, const QString &title);
|
||
|
||
// Карточки с главного экрана: lastNumbers, amount, description, currency.
|
||
// id == -1, materialId пусто — это стабы (используются для парсинга баланса).
|
||
QVariantList parseCardsOnHomeScreen();
|
||
|
||
// Аккаунты на экране "Все продукты" — массив {material: {...}, node: {...}}.
|
||
QVariantList parseAccountsInfo();
|
||
|
||
// Для GetLastTransactionsScript:
|
||
bool isTransactionListScreen();
|
||
bool hasCollapsedTransactionsBelow(int screenHeight);
|
||
QVariantList findTransactionButtons(int maxCount);
|
||
QVariantList getLast2DaysTransactions();
|
||
QVariantList parseTodayAndYesterdayHistory();
|
||
QVariantList parseTodayAndYesterdayReportHistory();
|
||
QVariantMap parseTransactionInfo(int screenWidth, int screenHeight);
|
||
QVariantMap parseTransactionInfoHistory(int screenWidth, int screenHeight);
|
||
QVariantMap parseTransactionDetail();
|
||
QVariantMap parseTransactionButtonText(const QString &text);
|
||
|
||
QString parseProfileFullName();
|
||
QString parseProfilePhone();
|
||
QString findFullCardNumber();
|
||
|
||
private:
|
||
Ozon::ScreenXmlParser *m_parser;
|
||
};
|
||
|
||
// Высокоуровневые операции, повторяющие Ozon::CommonScript: фактический
|
||
// запуск приложения, закрытие баннеров, регистрация bank_profile на сервере,
|
||
// постинг аккаунтов. Используют parser() из OzonParserBridge — состояние XML
|
||
// общее, как и у Black.
|
||
class OzonHelpersBridge final : public QObject {
|
||
Q_OBJECT
|
||
public:
|
||
OzonHelpersBridge(OzonParserBridge *parser, QObject *parent = nullptr)
|
||
: QObject(parent), m_parserBridge(parser) {}
|
||
public slots:
|
||
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 closeGooglePlayBannerIfVisible(const QString &deviceId, int width, int height);
|
||
bool goToAllProducts(const QString &deviceId, int width, int height);
|
||
void createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance);
|
||
void postAccountsData(const QVariantList &accountsWithEmptyMaterialId);
|
||
// POST /api/v1/device — создаёт устройство на сервере, если apiId пуст.
|
||
// deviceObj: { id, name, screenWidth, screenHeight, battery, apiId } (как из dao.deviceGetById).
|
||
void createDeviceIfNeeded(const QVariantMap &deviceObj);
|
||
// POST /api/v1/profile/material — отправляет одну карту на сервер.
|
||
// material: полный MaterialInfo-объект как из dao.materialFindAppAccount.
|
||
void postMaterial(const QVariantMap &material, const QString &bankProfileId, const QString &fullName);
|
||
// Для платёжных скриптов:
|
||
bool isAppRunning(const QString &deviceId, const QString &packageName);
|
||
// pincodeNodes: список node-map из parser.tryToFindPinCode.
|
||
bool inputPinCode(const QString &deviceId, const QVariantList &pincodeNodes);
|
||
// FreezeDetector::isUsefulDump — статически-чистая проверка.
|
||
bool isUsefulDump(const QString &xml);
|
||
// Захват скриншота + сохранение в TransactionDAO как receipt-image.
|
||
void saveTransactionReceipt(const QString &deviceId, int transactionId);
|
||
// POST /api/v1/profile/transaction — вставка новой транзакции (для GetLastTx).
|
||
// tx: как в dao.transactionInsert + account: {appCode, deviceId, lastNumbers}.
|
||
void postNewTransaction(const QVariantMap &account, const QVariantMap &tx);
|
||
private:
|
||
OzonParserBridge *m_parserBridge;
|
||
};
|
||
|
||
// Утилиты конверсии Node ↔ QVariantMap. Объявлены здесь чтобы парсер-бридж
|
||
// мог их использовать без зависимости от QJSEngine.
|
||
QVariantMap nodeToVariantMap(const Node &node);
|
||
|
||
} // namespace Scripting
|