1
0
forked from BRT/arc

Script auto starting

This commit is contained in:
AmirS 2025-04-25 15:42:51 +04:00
parent 71500f5023
commit 9f4ae0d726
8 changed files with 470 additions and 105 deletions

View File

@ -52,6 +52,12 @@ add_executable(ARCDesktopProject
${SERVICES_SOURCES} ${SERVICES_SOURCES}
${DB_SOURCES} ${DB_SOURCES}
${MODELS_SOURCES} ${MODELS_SOURCES}
automation_script/adb_fun.cpp
automation_script/adb_fun.h
automation_script/xml_reader.cpp
automation_script/xml_reader.h
automation_script/script_worker.cpp
automation_script/script_worker.h
) )
if (WIN32) if (WIN32)

View File

@ -0,0 +1,61 @@
#include "adb_fun.h"
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <array>
#include <ctime>
#include <iomanip>
std::string exec(const std::string& cmd) {
std::array<char, 256> buffer{};
std::string result;
FILE* pipe = _popen(cmd.c_str(), "r");
if (!pipe) return "ERROR";
while (fgets(buffer.data(), buffer.size(), pipe)) {
result += buffer.data();
}
_pclose(pipe);
return result;
}
bool checkDevices() {
std::string out = exec("adb devices");
return out.find("\tdevice") != std::string::npos;
}
bool startApp(const std::string& packageName) {
std::string cmd = "adb shell monkey -p " + packageName + " -c android.intent.category.LAUNCHER 1";
return system(cmd.c_str()) == 0;
}
bool tap(int x, int y) {
std::stringstream ss;
ss << "adb shell input tap " << x << " " << y;
return system(ss.str().c_str()) == 0;
}
bool swipe(const int x1, const int y1, const int x2, const int y2) {
std::stringstream ss;
ss << "adb shell input swipe " << x1 << " " << y1 << " " << x2 << " " << y2;
return system(ss.str().c_str()) == 0;
}
bool inputText(const std::string& text) {
std::stringstream ss;
ss << "adb shell input text \"" << text << "\"";
return system(ss.str().c_str()) == 0;
}
bool takeScreenshot(const std::string& filename) {
system("mkdir screens 2>nul"); // Windows
std::string cmd = "adb exec-out screencap -p > screens/" + filename;
return system(cmd.c_str()) == 0;
}
void log(const std::string& message) {
std::ofstream out("log.txt", std::ios::app);
std::time_t t = std::time(nullptr);
out << "[" << std::put_time(std::localtime(&t), "%H:%M:%S") << "] " << message << std::endl;
std::cout << message << std::endl;
}

View File

@ -0,0 +1,18 @@
#ifndef ADB_FUN_H
#define ADB_FUN_H
#include <string>
#include <vector>
bool checkDevices();
bool startApp(const std::string& packageName);
bool tap(int x, int y);
bool inputText(const std::string& text);
bool swipe(int x1, int y1, int x2, int y2);
bool takeScreenshot(const std::string& filename);
void log(const std::string& message);
#endif //ADB_FUN_H

View File

@ -0,0 +1,240 @@
//
// Created by Misterio on 24.04.2025.
//
#include "script_worker.h"
#include "adb_fun.h"
#include "xml_reader.h"
#include <regex>
#include <string>
#include <thread>
#include <chrono>
// Вспомогательные функции
void wait(const int milliseconds) {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
// Функция для запуска скрипта с передачей параметров
void runScriptWorker(const std::string& pin, const std::string& last4digits) {
log("Запуск скрипта для ru.rshb.dbo");
if (!checkDevices()) {
log("ADB: Нет устройств");
qDebug("ADB: Нет устройств");
return;
}
if (!startApp("ru.rshb.dbo")) {
log("Не удалось запустить приложение");
qDebug("Не удалось запустить приложение");
return;
}
wait(4500);
dumpAndPullUiXml("ui_01.xml");
auto ui = parseUiDumpUsingQXml("ui_01.xml");
if (isPinScreen(ui)) {
log("Экран PIN-кода");
qDebug("Экран PIN-кода");
// Находим координаты цифр на экране и вводим ПИН
std::vector<UiElement> pinButtons = getPinButtons(ui);
for (size_t i = 0; i < pin.length(); ++i) {
char digit = pin[i];
for (const auto& el : pinButtons) {
if (el.text == std::string(1, digit)) {
tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2);
break;
}
}
}
wait(1000);
}
// Пост-логин баннеры — цикл для обработки разных состояний показов модальных окон
bool postLoginScreenFound = false;
int attempts = 5; // Максимальное количество попыток парсинга xml
while (attempts-- > 0) {
dumpAndPullUiXml("ui_02.xml");
auto ui2 = parseUiDumpUsingQXml("ui_02.xml");
// Закрытие баннеров или диалогов
if (handlePostLoginScreen(ui2)) {
postLoginScreenFound = true;
break;
}
// Проверка, если мы на главном экране
if (isMainScreen(ui2)) {
log("Главный экран подтверждён");
postLoginScreenFound = true;
break;
}
// Ждем немного перед следующим запросом дампа экрана
wait(1000);
}
if (!postLoginScreenFound) {
log("Не удалось определить пост-логин баннер.");
return;
}
// Пролистывание до кнопки "Все продукты"
swipe(1000, 2000, 1000, 1300);
wait(500);
// Нажимаем на кнопку "Все продукты"
dumpAndPullUiXml("ui_03.xml");
auto ui3 = parseUiDumpUsingQXml("ui_03.xml");
// Ищем кнопку "Все продукты" и нажимаем на неё
tapByText(ui3, "Все продукты");
wait(800);
// Шаг 6: Выбор карты на экране "Все продукты"
// Запрашиваем дамп UI для поиска карт
dumpAndPullUiXml("ui_04.xml");
auto ui4 = parseUiDumpUsingQXml("ui_04.xml");
// Определим, какую карту выбрать, например, с 4 цифрами "0823"
UiElement cardButton = findCardByLast4Digits(ui4, last4digits);
if (cardButton.text.isEmpty()) {
log("Не найдена карта с номером, содержащим " + last4digits);
return;
}
// Нажимаем на карту
tap((cardButton.x1 + cardButton.x2) / 2, (cardButton.y1 + cardButton.y2) / 2);
wait(1000);
// Нажимаем на кнопку "Оплатить"
// Запрашиваем дамп UI для поиска кнопки "Оплатить"
dumpAndPullUiXml("ui_05.xml");
auto ui5 = parseUiDumpUsingQXml("ui_05.xml");
// Ищем кнопку "Оплатить" и нажимаем на неё
UiElement payBtn = findPayButton(ui5);
if (payBtn.text.isEmpty()) {
log("Не найдена кнопка 'Оплатить'");
return;
}
// Нажимаем на кнопку "Оплатить"
tap((payBtn.x1 + payBtn.x2) / 2, (payBtn.y1 + payBtn.y2) / 2);
log("Нажата кнопка 'Оплатить'");
wait(1000);
}
// Функции для определения экранов и действий
bool isPinScreen(const std::vector<UiElement>& ui) {
for (const auto& el : ui) {
if (el.text == "Введите ПИН") {
return true;
}
}
return false;
}
std::vector<UiElement> getPinButtons(const std::vector<UiElement>& ui) {
std::vector<UiElement> pinButtons;
for (const auto& el : ui) {
if (el.text.length() == 1 && std::isdigit(el.text[0].toLatin1())) {
pinButtons.push_back(el); // Собираем все элементы с цифрами
}
}
return pinButtons;
}
void tapByText(const std::vector<UiElement>& ui, const std::string& text) {
for (const auto& el : ui) {
if (el.text == QString::fromStdString(text)) {
tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2);
break;
}
}
}
// Тап по полю ввода
void tapInputField(const std::vector<UiElement>& ui, const std::string& fieldText) {
for (const auto& el : ui) {
if (el.text == QString::fromStdString(fieldText)) {
tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2);
break;
}
}
}
// Тап по кнопке "Далее"
void tapNextButton(const std::vector<UiElement>& ui) {
for (const auto& el : ui) {
if (el.text == "Войти" || el.text == "Далее" || el.text == "Продолжить") {
tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2);
break;
}
}
}
bool handlePostLoginScreen(const std::vector<UiElement>& ui) {
for (const auto& el : ui) {
// Ищем баннеры и модальные окна, которые нужно закрыть
if (el.text == "Получайте новости с помощью" || el.text == "Не пропускайте специальные предложения и рекомендации") {
// Тапаем по пустой области
tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2);
wait(900);
return true;
}
if (el.text == "Позже" || el.text == "Окей" || el.text == "Закрыть") {
// Тапаем по кнопке закрытия или кнопке "Позже"
tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2);
wait(900);
return true;
}
}
return false;
}
bool isMainScreen(const std::vector<UiElement>& ui) {
for (const auto& el : ui) {
if (el.text == "Счета и карты" || el.text == "Доступно баллов") {
return true;
}
}
return false;
}
UiElement findCardByLast4Digits(const std::vector<UiElement>& ui, const std::string& last4digits) {
// Проходим по всем элементам UI, которые парсились из XML
for (const auto& el : ui) {
// Используем регулярное выражение для поиска 4 цифр в тексте
std::smatch m;
std::string str = el.text.toUtf8().constData(); // Преобразуем QString в std::string
// Если в тексте найдено совпадение с регулярным выражением для 4 цифр
if (std::regex_search(str, m, std::regex(R"(\*\*\s?(\d{4}))"))) {
// Проверяем, совпадают ли последние 4 цифры с переданными
if (m[1] == last4digits) {
// Если нашли совпадение, возвращаем элемент
return el;
}
}
}
// Если карта с такими последними цифрами не найдена, возвращаем пустой элемент
return {};
}
UiElement findPayButton(const std::vector<UiElement>& ui) {
for (const auto& el : ui) {
if (el.text == "Оплатить") {
return el;
}
}
return {};
}

View File

@ -0,0 +1,49 @@
//
// Created by Misterio on 24.04.2025.
//
#ifndef SCRIPT_WORKER_H
#define SCRIPT_WORKER_H
#include <vector>
#include <string>
#include "xml_reader.h"
// Функция для запуска скрипта с передачей параметров
void runScriptWorker(const std::string& pin, const std::string& last4digits);
// Функции для определения различных экранов и действий
// Проверка, что перед нами экран логина
bool isLoginScreen(const std::vector<UiElement>& ui);
// Проверка, что перед нами экран ввода ПИН
bool isPinScreen(const std::vector<UiElement>& ui);
// Получение списка кнопок с цифрами для ввода ПИН
std::vector<UiElement> getPinButtons(const std::vector<UiElement>& ui);
// Тап по элементу с текстом
void tapByText(const std::vector<UiElement>& ui, const std::string& text);
// Тап по полю ввода
void tapInputField(const std::vector<UiElement>& ui, const std::string& fieldText);
// Тап по кнопке "Далее" и её вариациям
void tapNextButton(const std::vector<UiElement>& ui);
// Обработка экрана с пост-логин баннерами или диалогами
bool handlePostLoginScreen(const std::vector<UiElement>& ui);
// Проверка, что перед нами главный экран
bool isMainScreen(const std::vector<UiElement>& ui);
// Тап по главному экрану
void tapMainScreen(const std::vector<UiElement>& ui);
// Функция для поиска карты по последним 4 цифрам
UiElement findCardByLast4Digits(const std::vector<UiElement>& ui, const std::string& last4digits);
// Поиск кнопки "Оплатить" на экране
UiElement findPayButton(const std::vector<UiElement>& ui);
#endif //SCRIPT_WORKER_H

View File

@ -0,0 +1,79 @@
#include "xml_reader.h"
#include <QFile>
#include <QXmlStreamReader>
#include <QRegularExpression>
#include <QDebug>
#include <regex>
bool dumpAndPullUiXml(const std::string& localPath) {
int r1 = system("adb shell uiautomator dump /sdcard/view.xml");
int r2 = system(("adb pull /sdcard/view.xml " + localPath).c_str());
return r1 == 0 && r2 == 0;
}
std::vector<UiElement> parseUiDumpUsingQXml(const QString& filePath) {
std::vector<UiElement> uiElements;
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Не удалось открыть файл:" << filePath;
return uiElements;
}
QXmlStreamReader xmlReader(&file);
UiElement currentElement;
while (!xmlReader.atEnd()) {
xmlReader.readNext();
if (xmlReader.hasError()) {
qDebug() << "XML parsing error at line" << xmlReader.lineNumber() << ":"
<< xmlReader.errorString();
return {}; // Возвращаем пустой вектор в случае ошибки
}
if (xmlReader.isStartElement()) {
qDebug() << "Found element:" << xmlReader.name();
if (xmlReader.name() == "node") {
for (const QXmlStreamAttribute &attr : xmlReader.attributes()) {
if (attr.name() == "bounds") {
// Извлекаем координаты из bounds
QString bounds = attr.value().toString();
QRegularExpression regex(R"(\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\])");
QRegularExpressionMatch match = regex.match(bounds);
if (match.hasMatch()) {
// Извлекаем координаты из регулярного выражения
currentElement.x1 = match.captured(1).toInt();
currentElement.y1 = match.captured(2).toInt();
currentElement.x2 = match.captured(3).toInt();
currentElement.y2 = match.captured(4).toInt();
}
}
if (attr.name() == "text") {
currentElement.text = attr.value().toString();
}
if (attr.name() == "resource-id") {
currentElement.resourceId = attr.value().toString();
}
}
uiElements.push_back(currentElement);
}
}
// Пропускаем закрывающий тег </node>
if (xmlReader.isEndElement()) {
qDebug() << "End element:" << xmlReader.name();
}
}
if (xmlReader.hasError()) {
qWarning() << "Final XML parsing error:" << xmlReader.errorString();
}
return uiElements;
}

View File

@ -0,0 +1,15 @@
#ifndef XML_READER_H
#define XML_READER_H
#include <QString>
struct UiElement {
int x1, y1, x2, y2;
QString text;
QString resourceId;
};
std::vector<UiElement> parseUiDumpUsingQXml(const QString &filePath);
bool dumpAndPullUiXml(const std::string& localPath);
#endif //XML_READER_H

107
main.cpp
View File

@ -8,6 +8,7 @@
#include "database/dao/CommonDataDAO.h" #include "database/dao/CommonDataDAO.h"
#include "db/AccountInfoScreener.h" #include "db/AccountInfoScreener.h"
#include "services/DeviceScreener.h" #include "services/DeviceScreener.h"
#include "automation_script/script_worker.h"
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
@ -44,113 +45,9 @@ void setupWorkerAndThread(QCoreApplication &app) {
thread->start(); thread->start();
} }
void runADBCommand(const std::string& command) {
// Формируем команду для запуска в shell
std::string fullCommand = "adb shell " + command;
std::cout << "Executing: " << fullCommand << std::endl;
// Запуск команды
int result = std::system(fullCommand.c_str());
// Проверка на успешность выполнения
if (result != 0) {
std::cerr << "Error executing command: " << fullCommand << std::endl;
}
}
void wait(const int milliseconds) {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
// Шаг 1: запускаем приложение РСХБ runScriptWorker("1211", "0823"); // Просто запускаем script
runADBCommand("monkey -p ru.rshb.dbo -c android.intent.category.LAUNCHER 1");
wait(4500); // Ждем пока загружается приложение, перед экраном ввода пин-кода
// Шаг 2: Ввод ПИН
runADBCommand("input tap 275 900"); // Клик по цифре 1
runADBCommand("input tap 560 900"); // Клик по цифре 2
runADBCommand("input tap 275 900"); // Клик по цифре 1
runADBCommand("input tap 275 900"); // Клик по цифре 1
wait(5500);
// Шаг 3: Закрытие рекламы
runADBCommand("input tap 300 2090"); // Закрыть баннер пуш-уведомлений (Баннер высвечивается при каждом 2-м запуске)
// runADBCommand("input tap 993 163"); // Закрыть сторис (только при первом входе после логина)
// runADBCommand("input tap 920 151"); // Закрыть окно перед главным экраном (только при первом входе после логина)
// runADBCommand("input tap 990 170"); // Закрыть другое окно (только при первом входе после логина)
wait(1500);
// Шаг 4: Пролистывание до кнопки "Все продукты"
runADBCommand("input swipe 1000 2000 1000 1300");
wait(3000);
// Шаг 5: Нажимаем на кнопку "Все продукты"
runADBCommand("input tap 570 1240");
wait(3000);
// Шаг 6: Выбор карты
// runADBCommand("input tap 530 535"); // Для карты дебетовая, цифровая, **6387
runADBCommand("input tap 530 675"); // Для карты дебетовая, **0823
// runADBCommand("input tap 530 800"); // Для карты дебетовая, **7498
wait(1500);
// Шаг 7: Нажимаем на кнопку "Оплатить"
runADBCommand("input tap 430 920");
wait(1200);
// Шаг 8: Нажимаем на блок "По номеру телефона"
runADBCommand("input tap 260 1500");
wait(2000);
// Шаг 8.1: Закрытие окна разрешения доступа к контактам
// runADBCommand("input tap 540 2140");
// wait(500);
// Вводим номер телефона для перевода
runADBCommand("input tap 250 250");
runADBCommand("input text '9272960333'");
wait(1200);
// Шаг 9: Нажимаем на область "Другой банк через СБП"
runADBCommand("input tap 500 600");
wait(2000);
// Шаг 10: Выбор банка
// runADBCommand("input tap 280 280"); // Нажимаем для фокуса у поля ввода текста
// runADBCommand("input text 'Т-Банк'"); // Передаем текст 'Т-банк' (Проблема - выдает NullPointerException из-за кириллицы)
runADBCommand("input tap 420 620"); // Т-Банк (Пока выбираю хардкодовым нажатием по списку)
wait(2000);
// Шаг 11: Ввод суммы
runADBCommand("input tap 300 1040");
runADBCommand("input text '10'");
wait(2000);
// Шаг 11.1: Подтверждаем перевод
runADBCommand("input tap 500 2170");
wait(2000);
// Завершаем перевод
runADBCommand("input tap 500 2170");
std::cout << "All commands executed successfully." << std::endl;
return 0; return 0;
QApplication app(argc, argv); QApplication app(argc, argv);