- Add Proof of Concept (PoC) for executing JavaScript scripts fetched remotely. - Integrate `Black::GetProfileInfoScript` logic into a JS version with fallback to C++ on errors. - Extend `ScreenXmlParser` with additional JS-to-C++ bridge methods. - Update `config.ini` for scripting configuration. - Ensure scripts are securely fetched with SHA-256 validation and use an atomic caching mechanism. - Add developer tools for debugging JS scripts and enable easier script updates without app re-deployment.
175 lines
7.1 KiB
C++
175 lines
7.1 KiB
C++
#pragma once
|
||
|
||
#include <QObject>
|
||
#include <QString>
|
||
#include <QVariantList>
|
||
#include <QVariantMap>
|
||
|
||
#include "android/xml/Node.h"
|
||
|
||
class QJSEngine;
|
||
|
||
namespace Black { 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);
|
||
};
|
||
|
||
// Скоуп дампа задачи. 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;
|
||
};
|
||
|
||
// Парсер экрана для банка 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;
|
||
};
|
||
|
||
// Утилиты конверсии Node ↔ QVariantMap. Объявлены здесь чтобы парсер-бридж
|
||
// мог их использовать без зависимости от QJSEngine.
|
||
QVariantMap nodeToVariantMap(const Node &node);
|
||
|
||
} // namespace Scripting
|