From c28fa817de5450447e8217bbf9fc6d3f0747af19 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sun, 26 May 2024 12:25:18 +0200 Subject: [PATCH 01/34] Added simple versioning. --- CMakeLists.txt | 22 ++++++++++++++++++++-- configured_files/CMakeLists.txt | 7 +++++++ configured_files/config.hpp.in | 13 +++++++++++++ gui/mainwindow.cpp | 5 +++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 configured_files/CMakeLists.txt create mode 100644 configured_files/config.hpp.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 1be415f..2922a26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,21 @@ cmake_minimum_required(VERSION 3.10) -project(injectionClicker) +project(injectionClicker VERSION 1.0.0 + DESCRIPTION "" + HOMEPAGE_URL "https://github.com/Napalys/Attachable-clicker" + LANGUAGES CXX C) -set(CMAKE_CXX_STANDARD 17) +if (NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(GIT_SHA "Unknown" CACHE STRING "SHA this build was generated from") + +string(SUBSTRING "${GIT_SHA}" + 0 + 8 + GIT_SHORT_SHA) + set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) @@ -11,6 +23,7 @@ set(CMAKE_AUTORCC ON) find_package(Qt5 COMPONENTS Widgets REQUIRED) add_subdirectory(clicker) +add_subdirectory(configured_files) add_executable(${PROJECT_NAME} main.cpp gui/mainwindow.cpp @@ -43,3 +56,8 @@ target_compile_options(${PROJECT_NAME} PRIVATE $<$:-Wall -Wextra -Wpedantic -Werror> $<$:/W4 /WX> ) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_BINARY_DIR}/configured_files/include/internal_use_only + gui +) diff --git a/configured_files/CMakeLists.txt b/configured_files/CMakeLists.txt new file mode 100644 index 0000000..2bf896e --- /dev/null +++ b/configured_files/CMakeLists.txt @@ -0,0 +1,7 @@ + +# A very simple example of a configured file that might need to be +# converted to one that is publicly installed in the case that +# you are developing a library +configure_file("config.hpp.in" "${CMAKE_BINARY_DIR}/configured_files/include/internal_use_only/config.hpp" ESCAPE_QUOTES) + + diff --git a/configured_files/config.hpp.in b/configured_files/config.hpp.in new file mode 100644 index 0000000..cbff221 --- /dev/null +++ b/configured_files/config.hpp.in @@ -0,0 +1,13 @@ +#ifndef InjectionClicker_CONFIG_HPP +#define InjectionClicker_CONFIG_HPP + +namespace InjectionClicker::cmake { +inline constexpr std::string_view project_name = "@PROJECT_NAME@"; +inline constexpr std::string_view project_version = "v@PROJECT_VERSION@"; +inline constexpr int project_version_major { @PROJECT_VERSION_MAJOR@ }; +inline constexpr int project_version_minor { @PROJECT_VERSION_MINOR@ }; +inline constexpr int project_version_patch { @PROJECT_VERSION_PATCH@ }; +inline constexpr std::string_view git_sha = "@GIT_SHA@"; +} + +#endif diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index fe602a2..f6af513 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -14,6 +14,7 @@ #include "delegates/numeric_delegate.hpp" #include "delegates/action_delegate.hpp" #include "dialogs/clicker_data_dialog.h" +#include "config.hpp" void setupTable(QTableWidget* table) { table->setColumnCount(4); @@ -60,6 +61,10 @@ void setupTable(QTableWidget* table) { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); + QString title = QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), + InjectionClicker::cmake::project_version.data()); + setWindowTitle(title); + ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); setupTable(ui->tableWidget); ui->label->setOpenExternalLinks(true); From 7ef07aabcc8b61eda20cb2cabbf801523b7c0c0a Mon Sep 17 00:00:00 2001 From: Napalys Date: Sun, 26 May 2024 12:41:45 +0200 Subject: [PATCH 02/34] Added version to json. --- gui/mainwindow.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index f6af513..bb3bd56 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -301,8 +301,10 @@ void MainWindow::saveRoutineData() { auto allData = extractAllDataFromTable(); nlohmann::json jsonData; + jsonData["version"] = InjectionClicker::cmake::project_version.data(); + for (auto &data: allData) { - jsonData.push_back(std::visit([](auto &&arg) -> nlohmann::json { + jsonData["routine"].push_back(std::visit([](auto &&arg) -> nlohmann::json { return arg; }, data)); } @@ -361,11 +363,20 @@ void MainWindow::loadRoutineData() { return; } + if (!jsonData.contains("version") || jsonData["version"] != InjectionClicker::cmake::project_version.data()) { + createErrorBox("Warning: JSON file version mismatch or missing version."); + } + + if (!jsonData.contains("routine") || !jsonData["routine"].is_array()) { + createErrorBox("Error in JSON structure: Missing or invalid 'routine' key."); + return; + } + ui->tableWidget->clear(); ui->tableWidget->setRowCount(0); setupTable(ui->tableWidget); bool error = false; - for (const auto& item : jsonData) { + for (const auto& item : jsonData["routine"]) { if (!item.contains("type") || !item["type"].is_string()) { error = true; continue; From 7fa5fb3967b0912bb9df079b4cbbe2b4cef9220d Mon Sep 17 00:00:00 2001 From: Napalys Date: Sun, 26 May 2024 15:46:01 +0200 Subject: [PATCH 03/34] Refactored table to be handled in its own class. --- CMakeLists.txt | 2 + gui/delegates/action_delegate.hpp | 3 + gui/delegates/numeric_delegate.hpp | 1 + gui/injectionClicker.pro | 19 ++-- gui/mainwindow.cpp | 172 +++-------------------------- gui/mainwindow.h | 7 +- gui/managers/table_manager.cpp | 139 +++++++++++++++++++++++ gui/managers/table_manager.h | 37 +++++++ 8 files changed, 211 insertions(+), 169 deletions(-) create mode 100644 gui/managers/table_manager.cpp create mode 100644 gui/managers/table_manager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2922a26..8dcd847 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,8 @@ add_executable(${PROJECT_NAME} main.cpp gui/delegates/action_delegate.hpp gui/dialogs/clicker_data_dialog.cpp gui/dialogs/clicker_data_dialog.h + gui/managers/table_manager.cpp + gui/managers/table_manager.h ) target_include_directories(${PROJECT_NAME} PRIVATE gui) diff --git a/gui/delegates/action_delegate.hpp b/gui/delegates/action_delegate.hpp index 796a974..4656763 100644 --- a/gui/delegates/action_delegate.hpp +++ b/gui/delegates/action_delegate.hpp @@ -5,6 +5,9 @@ #ifndef INJECTIONCLICKER_ACTION_DELEGATE_HPP #define INJECTIONCLICKER_ACTION_DELEGATE_HPP +#include +#include + namespace GUI::Delegates { class ActionDelegate : public QStyledItemDelegate { diff --git a/gui/delegates/numeric_delegate.hpp b/gui/delegates/numeric_delegate.hpp index 4d27519..50ec8ea 100644 --- a/gui/delegates/numeric_delegate.hpp +++ b/gui/delegates/numeric_delegate.hpp @@ -7,6 +7,7 @@ #include +#include namespace GUI::Delegates { diff --git a/gui/injectionClicker.pro b/gui/injectionClicker.pro index 099ac22..3020f84 100644 --- a/gui/injectionClicker.pro +++ b/gui/injectionClicker.pro @@ -8,15 +8,18 @@ CONFIG += c++11 # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 -SOURCES += \ - clicker.cpp \ - main.cpp \ - mainwindow.cpp - HEADERS += \ - clicker.h \ - ClickerData.h \ - mainwindow.h + mainwindow.h \ + managers/table_manager.h \ + delegates/action_delegate.hpp \ + delegates/non_editable_delegate.hpp \ + delegates/numeric_delegate.hpp \ + dialogs/clicker_data_dialog.h + +SOURCES += \ + mainwindow.cpp \ + managers/table_manager.cpp \ + dialogs/clicker_data_dialog.cpp FORMS += \ mainwindow.ui diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index bb3bd56..bc0560c 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -4,74 +4,21 @@ #include "QMessageBox" #include "process_handler/keyboard_callback.h" #include -#include -#include #include #include -#include - -#include "delegates/non_editable_delegate.hpp" -#include "delegates/numeric_delegate.hpp" -#include "delegates/action_delegate.hpp" #include "dialogs/clicker_data_dialog.h" #include "config.hpp" -void setupTable(QTableWidget* table) { - table->setColumnCount(4); - QStringList headers = {"Name", "Key", "Delay ms", "Action"}; - table->setHorizontalHeaderLabels(headers); - table->setSelectionMode(QAbstractItemView::SingleSelection); - table->setSelectionBehavior(QAbstractItemView::SelectRows); - - auto* nonEditableDelegate = new GUI::Delegates::NonEditableDelegate(table); - table->setItemDelegateForColumn(0, nonEditableDelegate); - table->setItemDelegateForColumn(1, nonEditableDelegate); - - auto* numericDelegate = new GUI::Delegates::NumericDelegate(table); - table->setItemDelegateForColumn(2, numericDelegate); - - auto* actionDelegate = new GUI::Delegates::ActionDelegate(table); - table->setItemDelegateForColumn(3, actionDelegate); - table->verticalHeader()->hide(); - QObject::connect(table, &QTableWidget::itemChanged, [table](QTableWidgetItem* item) { - int row = item->row(); - int column = item->column(); - - QTableWidgetItem* firstColumnItem = table->item(row, 0); - if (!firstColumnItem) return; - - QVariant userData = firstColumnItem->data(Qt::UserRole); - - if (userData.canConvert>()) { - auto clickerData = qvariant_cast>(userData); - if (column == 3) { - clickerData->event = (clickerData->event == ClickerData::Event::Pressed) ? - ClickerData::Event::Released : ClickerData::Event::Pressed; - } - } else if (userData.canConvert>()) { - auto delayData = qvariant_cast>(userData); - if (column == 2) { - delayData->delay = item->text().toInt(); - } - } - }); -} - - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - QString title = QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), - InjectionClicker::cmake::project_version.data()); + tableManager = new GUI::TableManager(ui->tableWidget); + tableManager->setupTable(); + QString title = QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(),InjectionClicker::cmake::project_version.data()); setWindowTitle(title); - - ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - setupTable(ui->tableWidget); ui->label->setOpenExternalLinks(true); connect(ui->actionSave, &QAction::triggered, this, &MainWindow::saveRoutineData); connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadRoutineData); - qRegisterMetaType>("std::shared_ptr"); - qRegisterMetaType>("std::shared_ptr"); qRegisterMetaType(); } @@ -94,7 +41,7 @@ void MainWindow::on_pushButton_Start_clicked() { return; } - if(ui->tableWidget->rowCount() == 0){ + if(tableManager->isEmpty()){ createErrorBox("Please add some keys to be clicked"); return; } @@ -104,7 +51,7 @@ void MainWindow::on_pushButton_Start_clicked() { } void MainWindow::enableClicker(){ - auto keyEvents = extractAllDataFromTable(); + auto keyEvents = tableManager->extractAllData(); clicker->setClickerStatus(true); ui->pushButton_Start->setText("Stop"); clicker->addRoutine(keyEvents); @@ -119,86 +66,6 @@ void MainWindow::disableClicker(){ clicker->stopRoutines(); } -std::vector> MainWindow::extractAllDataFromTable() { - std::vector> allData; - int rowCount = ui->tableWidget->rowCount(); - - for (int i = 0; i < rowCount; ++i) { - QVariant userData = ui->tableWidget->item(i, 0)->data(Qt::UserRole); - if (userData.canConvert>()) { - auto clickerData = qvariant_cast>(userData); - allData.emplace_back(*clickerData); - } else if (userData.canConvert>()) { - auto delayData = qvariant_cast>(userData); - allData.emplace_back(*delayData); - } - } - - return allData; -} - -struct PtrCreatorVisitor { - QVariant operator()(const ClickerData& data) const { - return QVariant::fromValue(std::make_shared(data)); - } - - QVariant operator()(const Delay& data) const { - return QVariant::fromValue(std::make_shared(data)); - } -}; - - - -void MainWindow::addRowToTable(const std::variant& data) { - int row = ui->tableWidget->currentRow() + 1; - if (row == 0) { - row = ui->tableWidget->rowCount(); - } - - ui->tableWidget->insertRow(row); - - std::visit([&](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - // Handling ClickerData - auto *name = new QTableWidgetItem(QString::fromStdString(arg.key_name)); - auto *keyItem = new QTableWidgetItem(QString::number(arg.key_code)); - auto *action = new QTableWidgetItem(arg.event == ClickerData::Event::Pressed ? "Pressed" : "Released"); - auto *placeholder = new QTableWidgetItem(); // Placeholder for the fourth column - - // Set items to the table - ui->tableWidget->setItem(row, 0, name); - ui->tableWidget->setItem(row, 1, keyItem); - ui->tableWidget->setItem(row, 2, placeholder); - ui->tableWidget->setItem(row, 3, action); - - // Gray out the unused fourth column - placeholder->setFlags(placeholder->flags() ^ Qt::ItemIsEditable); - placeholder->setBackground(QBrush(Qt::darkGray)); - } else if constexpr (std::is_same_v) { - // Handling Delay - auto *name = new QTableWidgetItem("Delay"); - auto *delayTime = new QTableWidgetItem(QString::number(arg.delay)); - auto *placeholder1 = new QTableWidgetItem(); - auto *placeholder2 = new QTableWidgetItem(); - - ui->tableWidget->setItem(row, 0, name); - ui->tableWidget->setItem(row, 1, placeholder1); - ui->tableWidget->setItem(row, 2, delayTime); - ui->tableWidget->setItem(row, 3, placeholder2); - - placeholder1->setFlags(placeholder1->flags() ^ Qt::ItemIsEditable); - placeholder1->setBackground(QBrush(Qt::darkGray)); - placeholder2->setFlags(placeholder2->flags() ^ Qt::ItemIsEditable); - placeholder2->setBackground(QBrush(Qt::darkGray)); - } - }, data); - - ui->tableWidget->item(row, 0)->setData(Qt::UserRole, std::visit(PtrCreatorVisitor{}, data)); - ui->tableWidget->setCurrentCell(row, 0); - ui->tableWidget->scrollToItem(ui->tableWidget->item(row, 0)); -} - void MainWindow::on_pushButton_select_window_clicked() { @@ -224,7 +91,7 @@ void MainWindow::on_pushButton_record_clicked() { void MainWindow::enableKeyStrokeRecording() { try { ProcessHandler::registerCallBack([&](const std::variant& data) { - addRowToTable(data); + tableManager->addRow(data); }); } catch (const std::exception &e) { @@ -243,8 +110,6 @@ void MainWindow::disableKeyStrokeRecording() { isRecording = !isRecording; } - - void MainWindow::on_pushButton_PID_clicked() { uint32_t process_id = ui->lineEdit_PID->text().toInt(); std::string process_name = ui->lineEdit_tittle->text().toStdString(); @@ -281,24 +146,18 @@ void MainWindow::setPIDFoundSuccessful() { void MainWindow::on_pushButton_delete_key_clicked() { - int selectedRow = ui->tableWidget->selectionModel()->currentIndex().row(); - if (selectedRow < 0) { - createErrorBox(std::string("First select row to be removed")); - return; - } - ui->tableWidget->removeRow(selectedRow); + tableManager->deleteSelectedRow(); } void MainWindow::on_pushButton_insert_key_clicked() { GUI::Dialogs::ClickerDataDialog dialog(this); - if (dialog.exec() == QDialog::Accepted) { - ClickerData data = dialog.getClickerData(); - addRowToTable(data); - } + if (dialog.exec() != QDialog::Accepted) return; + ClickerData data = dialog.getClickerData(); + tableManager->addRow(data); } void MainWindow::saveRoutineData() { - auto allData = extractAllDataFromTable(); + auto allData = tableManager->extractAllData(); nlohmann::json jsonData; jsonData["version"] = InjectionClicker::cmake::project_version.data(); @@ -372,9 +231,8 @@ void MainWindow::loadRoutineData() { return; } - ui->tableWidget->clear(); - ui->tableWidget->setRowCount(0); - setupTable(ui->tableWidget); + + tableManager->setupTable(); bool error = false; for (const auto& item : jsonData["routine"]) { if (!item.contains("type") || !item["type"].is_string()) { @@ -385,13 +243,13 @@ void MainWindow::loadRoutineData() { auto type = item["type"].get(); if (type == "ClickerData") { if (item.contains("key_name") && item.contains("key_code") && item.contains("event")) { - addRowToTable(item.get()); + tableManager->addRow(item.get()); } else { createErrorBox("Missing fields in ClickerData object."); } } else if (type == "Delay") { if (item.contains("delay")) { - addRowToTable(item.get()); + tableManager->addRow(item.get()); } else { createErrorBox("Missing 'delay' field in Delay object."); } diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 5c50cf0..1681f9c 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -4,6 +4,7 @@ #include #include "clicker_data.h" #include "clicker.h" +#include "managers/table_manager.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -16,8 +17,6 @@ QT_END_NAMESPACE #include #include -Q_DECLARE_METATYPE(std::shared_ptr) -Q_DECLARE_METATYPE(std::shared_ptr) class MainWindow : public QMainWindow { Q_OBJECT @@ -36,6 +35,7 @@ private slots: void on_pushButton_record_clicked(); void on_pushButton_select_window_clicked(); +private: void saveRoutineData(); void loadRoutineData(); void setPIDFoundSuccessful(); @@ -43,11 +43,10 @@ private slots: void disableKeyStrokeRecording(); void enableClicker(); void disableClicker(); - void addRowToTable(const std::variant& data); - std::vector> extractAllDataFromTable(); private: Ui::MainWindow *ui; + GUI::TableManager* tableManager; std::unique_ptr clicker = nullptr; bool isRecording = false; }; diff --git a/gui/managers/table_manager.cpp b/gui/managers/table_manager.cpp new file mode 100644 index 0000000..a6fd3aa --- /dev/null +++ b/gui/managers/table_manager.cpp @@ -0,0 +1,139 @@ +#include "table_manager.h" +#include "delegates/non_editable_delegate.hpp" +#include "delegates/numeric_delegate.hpp" +#include "delegates/action_delegate.hpp" +#include +#include + +namespace GUI { + + TableManager::TableManager(QTableWidget *table) : table(table) { + table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + qRegisterMetaType>("std::shared_ptr"); + qRegisterMetaType>("std::shared_ptr"); + qRegisterMetaType(); + } + + void TableManager::setupTable() { + table->clear(); + table->setRowCount(0); + table->setColumnCount(4); + QStringList headers = {"Name", "Key", "Delay ms", "Action"}; + table->setHorizontalHeaderLabels(headers); + table->setSelectionMode(QAbstractItemView::SingleSelection); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + + table->setItemDelegateForColumn(0, new GUI::Delegates::NonEditableDelegate(table)); + table->setItemDelegateForColumn(1, new GUI::Delegates::NonEditableDelegate(table)); + table->setItemDelegateForColumn(2, new GUI::Delegates::NumericDelegate(table)); + table->setItemDelegateForColumn(3, new GUI::Delegates::ActionDelegate(table)); + + table->verticalHeader()->hide(); + QObject::connect(table, &QTableWidget::itemChanged, [this](QTableWidgetItem *item) { + int row = item->row(); + int column = item->column(); + + QTableWidgetItem *firstColumnItem = table->item(row, 0); + if (!firstColumnItem) return; + + QVariant userData = firstColumnItem->data(Qt::UserRole); + + if (userData.canConvert>()) { + auto clickerData = qvariant_cast>(userData); + if (column == 3) { + clickerData->event = (clickerData->event == ClickerData::Event::Pressed) ? + ClickerData::Event::Released : ClickerData::Event::Pressed; + } + } else if (userData.canConvert>()) { + auto delayData = qvariant_cast>(userData); + if (column == 2) { + delayData->delay = item->text().toInt(); + } + } + }); + } + + struct PtrCreatorVisitor { + QVariant operator()(const ClickerData &data) const { + return QVariant::fromValue(std::make_shared(data)); + } + + QVariant operator()(const Delay &data) const { + return QVariant::fromValue(std::make_shared(data)); + } + }; + + void TableManager::addRow(const std::variant &data) { + int row = table->currentRow() + 1; + if (row == 0) row = table->rowCount(); + table->insertRow(row); + + std::visit([&](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + // Handling ClickerData + auto *name = new QTableWidgetItem(QString::fromStdString(arg.key_name)); + auto *keyItem = new QTableWidgetItem(QString::number(arg.key_code)); + auto *action = new QTableWidgetItem(arg.event == ClickerData::Event::Pressed ? "Pressed" : "Released"); + auto *placeholder = new QTableWidgetItem(); // Placeholder for the fourth column + + // Set items to the table + table->setItem(row, 0, name); + table->setItem(row, 1, keyItem); + table->setItem(row, 2, placeholder); + table->setItem(row, 3, action); + + // Gray out the unused fourth column + placeholder->setFlags(placeholder->flags() ^ Qt::ItemIsEditable); + placeholder->setBackground(QBrush(Qt::darkGray)); + } else if constexpr (std::is_same_v) { + // Handling Delay + auto *name = new QTableWidgetItem("Delay"); + auto *delayTime = new QTableWidgetItem(QString::number(arg.delay)); + auto *placeholder1 = new QTableWidgetItem(); + auto *placeholder2 = new QTableWidgetItem(); + + table->setItem(row, 0, name); + table->setItem(row, 1, placeholder1); + table->setItem(row, 2, delayTime); + table->setItem(row, 3, placeholder2); + + placeholder1->setFlags(placeholder1->flags() ^ Qt::ItemIsEditable); + placeholder1->setBackground(QBrush(Qt::darkGray)); + placeholder2->setFlags(placeholder2->flags() ^ Qt::ItemIsEditable); + placeholder2->setBackground(QBrush(Qt::darkGray)); + } + }, data); + + table->item(row, 0)->setData(Qt::UserRole, std::visit(PtrCreatorVisitor{}, data)); + table->setCurrentCell(row, 0); + table->scrollToItem(table->item(row, 0)); + emit dataChanged(); + } + + std::vector> TableManager::extractAllData() const { + std::vector> allData; + for (int i = 0; i < table->rowCount(); ++i) { + QVariant userData = table->item(i, 0)->data(Qt::UserRole); + if (userData.canConvert>()) { + auto clickerData = qvariant_cast>(userData); + allData.emplace_back(*clickerData); + } else if (userData.canConvert>()) { + auto delayData = qvariant_cast>(userData); + allData.emplace_back(*delayData); + } + } + return allData; + } + + bool TableManager::isEmpty() const noexcept { + return table->rowCount() == 0; + } + + void TableManager::deleteSelectedRow() noexcept { + QModelIndex currentIndex = table->selectionModel()->currentIndex(); + if (!currentIndex.isValid()) return; + table->removeRow(currentIndex.row()); + emit dataChanged(); + } +} \ No newline at end of file diff --git a/gui/managers/table_manager.h b/gui/managers/table_manager.h new file mode 100644 index 0000000..96110d2 --- /dev/null +++ b/gui/managers/table_manager.h @@ -0,0 +1,37 @@ +#ifndef INJECTIONCLICKER_TABLE_MANAGER_H +#define INJECTIONCLICKER_TABLE_MANAGER_H + +#include +#include +#include "clicker_data.h" + +Q_DECLARE_METATYPE(std::shared_ptr) + +Q_DECLARE_METATYPE(std::shared_ptr) + +namespace GUI { + class TableManager : public QObject { + Q_OBJECT + public: + explicit TableManager(QTableWidget *table); + + void setupTable(); + + void addRow(const std::variant &data); + + [[nodiscard]] std::vector> extractAllData() const; + + [[nodiscard]] bool isEmpty() const noexcept; + + void deleteSelectedRow() noexcept; + + signals: + void dataChanged(); + + private: + QTableWidget *table; + }; + +} // GUI + +#endif //INJECTIONCLICKER_TABLE_MANAGER_H From 2fa4c0a4661052f93d2dc8430b39dbfb611e4f4a Mon Sep 17 00:00:00 2001 From: Napalys Date: Sun, 26 May 2024 17:33:47 +0200 Subject: [PATCH 04/34] Added connection of signals and initialization of ui methods, instead of having everything in one. --- gui/mainwindow.cpp | 31 +++++++++++++++++++------------ gui/mainwindow.h | 5 ++++- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index bc0560c..f7b02e4 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -12,22 +12,30 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - tableManager = new GUI::TableManager(ui->tableWidget); + initializeUI(); + connectSignals(); +} + +void MainWindow::initializeUI() { + tableManager = std::make_unique(ui->tableWidget); tableManager->setupTable(); - QString title = QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(),InjectionClicker::cmake::project_version.data()); - setWindowTitle(title); + setWindowTitle(QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), InjectionClicker::cmake::project_version.data())); + ui->label->setEnabled(true); ui->label->setOpenExternalLinks(true); +} + +void MainWindow::connectSignals() { connect(ui->actionSave, &QAction::triggered, this, &MainWindow::saveRoutineData); connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadRoutineData); qRegisterMetaType(); } -MainWindow::~MainWindow() { - delete ui; +void MainWindow::createErrorBox(const std::string &errorMsg) { + QMessageBox::warning(nullptr, "Error", errorMsg.c_str()); } -void createErrorBox(const std::string &errorMsg) { - QMessageBox::warning(nullptr, "Error", errorMsg.c_str()); +MainWindow::~MainWindow() { + delete ui; } void MainWindow::on_pushButton_Start_clicked() { @@ -61,8 +69,7 @@ void MainWindow::enableClicker(){ void MainWindow::disableClicker(){ clicker->setClickerStatus(false); ui->pushButton_Start->setText("Start"); - QMessageBox::warning(nullptr, "Warning", - "Waiting for clicker to finish task, This may take up to max declared ms"); + createErrorBox("Waiting for clicker to finish task, This may take up to max declared ms"); clicker->stopRoutines(); } @@ -178,7 +185,7 @@ void MainWindow::saveRoutineData() { QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::warning(this, "Error", "Could not open file for writing."); + createErrorBox("Could not open file for writing."); return; } @@ -187,7 +194,7 @@ void MainWindow::saveRoutineData() { out << QString::fromStdString(jsonData.dump(4)); file.close(); } catch (const std::exception& e) { - QMessageBox::critical(this, "Error", QString("Failed to save data: %1").arg(e.what())); + createErrorBox(std::string("Failed to save data: ") + e.what()); file.close(); return; } @@ -206,7 +213,7 @@ void MainWindow::loadRoutineData() { QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox::warning(this, "Error", "Could not open file for reading."); + createErrorBox("Could not open file for reading."); return; } diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 1681f9c..5358b02 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -43,10 +43,13 @@ private slots: void disableKeyStrokeRecording(); void enableClicker(); void disableClicker(); + void initializeUI(); + void connectSignals(); + static void createErrorBox(const std::string &errorMsg); private: Ui::MainWindow *ui; - GUI::TableManager* tableManager; + std::unique_ptr tableManager; std::unique_ptr clicker = nullptr; bool isRecording = false; }; From 477285c41607c61b0a3561b15139123c909252ff Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 27 May 2024 18:20:19 +0200 Subject: [PATCH 05/34] Added basic example of discord bot, hidden under pimpl idiom. --- CMakeLists.txt | 4 ++++ discord/CMakeLists.txt | 22 ++++++++++++++++++++++ discord/include/discord_bot.h | 26 ++++++++++++++++++++++++++ discord/src/discord_bot.cpp | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 discord/CMakeLists.txt create mode 100644 discord/include/discord_bot.h create mode 100644 discord/src/discord_bot.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dcd847..3d78ad7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ set(CMAKE_AUTORCC ON) find_package(Qt5 COMPONENTS Widgets REQUIRED) add_subdirectory(clicker) +add_subdirectory(discord) add_subdirectory(configured_files) add_executable(${PROJECT_NAME} main.cpp @@ -35,12 +36,15 @@ add_executable(${PROJECT_NAME} main.cpp gui/dialogs/clicker_data_dialog.h gui/managers/table_manager.cpp gui/managers/table_manager.h + discord/src/discord_bot.cpp + discord/include/discord_bot.h ) target_include_directories(${PROJECT_NAME} PRIVATE gui) target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets clicker_lib + Notifier ) set_target_properties(${PROJECT_NAME} PROPERTIES diff --git a/discord/CMakeLists.txt b/discord/CMakeLists.txt new file mode 100644 index 0000000..311d975 --- /dev/null +++ b/discord/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) +project(Notifier) + +find_package(DPP REQUIRED) + +include_directories(include) + +add_library(${PROJECT_NAME} + src/discord_bot.cpp + include/discord_bot.h +) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${DPP_INCLUDE_DIRS} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + include +) + + +target_link_libraries(${PROJECT_NAME} dpp) diff --git a/discord/include/discord_bot.h b/discord/include/discord_bot.h new file mode 100644 index 0000000..5519692 --- /dev/null +++ b/discord/include/discord_bot.h @@ -0,0 +1,26 @@ +// +// Created by Widok on 5/27/24. +// + +#ifndef INJECTIONCLICKER_DISCORD_BOT_H +#define INJECTIONCLICKER_DISCORD_BOT_H + +#include +#include + +namespace Notification { + + class DiscordBot { + public: + explicit DiscordBot(const std::string& token); + ~DiscordBot() noexcept; + void run(); + + private: + class Impl; + std::unique_ptr pImpl; + }; + +} // namespace Notification + +#endif //INJECTIONCLICKER_DISCORD_BOT_H diff --git a/discord/src/discord_bot.cpp b/discord/src/discord_bot.cpp new file mode 100644 index 0000000..e7548f3 --- /dev/null +++ b/discord/src/discord_bot.cpp @@ -0,0 +1,33 @@ +#include "../include/discord_bot.h" +#include + +namespace Notification { + + class DiscordBot::Impl { + public: + explicit Impl(const std::string& token) : bot(token) { + bot.on_log(dpp::utility::cout_logger()); + bot.on_message_create([](const dpp::message_create_t& event) { + if (event.msg.content == "!ping") { + event.reply("Pong!"); + } + }); + } + + void run() { + bot.start(dpp::st_wait); + } + + private: + dpp::cluster bot; + }; + + DiscordBot::DiscordBot(const std::string& token) : pImpl(std::make_unique(token)) {} + + DiscordBot::~DiscordBot() noexcept = default; + + void DiscordBot::run() { + pImpl->run(); + } + +} // namespace Notification From 347a128dc63b7538e65b7f229def9a9d082c2b68 Mon Sep 17 00:00:00 2001 From: napalys Date: Tue, 28 May 2024 14:19:08 +0200 Subject: [PATCH 06/34] Added ability in the backend to send messages via discord and images. --- discord/include/discord_bot.h | 10 +++-- discord/src/discord_bot.cpp | 74 ++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/discord/include/discord_bot.h b/discord/include/discord_bot.h index 5519692..729076e 100644 --- a/discord/include/discord_bot.h +++ b/discord/include/discord_bot.h @@ -1,12 +1,9 @@ -// -// Created by Widok on 5/27/24. -// - #ifndef INJECTIONCLICKER_DISCORD_BOT_H #define INJECTIONCLICKER_DISCORD_BOT_H #include #include +#include namespace Notification { @@ -16,6 +13,11 @@ namespace Notification { ~DiscordBot() noexcept; void run(); + using Callback = std::function; + + void send_message(const std::string& channel_id, const std::string& message, const Callback& callback = nullptr) const; + void send_image(const std::string& channel_id, const std::string& image_path, const Callback& callback = nullptr) const; + private: class Impl; std::unique_ptr pImpl; diff --git a/discord/src/discord_bot.cpp b/discord/src/discord_bot.cpp index e7548f3..f6a1bb9 100644 --- a/discord/src/discord_bot.cpp +++ b/discord/src/discord_bot.cpp @@ -1,21 +1,77 @@ -#include "../include/discord_bot.h" +#include "discord_bot.h" #include +#include namespace Notification { class DiscordBot::Impl { public: explicit Impl(const std::string& token) : bot(token) { - bot.on_log(dpp::utility::cout_logger()); + bot.on_log([](const dpp::log_t& event) { + std::cout << event.message << std::endl; + }); bot.on_message_create([](const dpp::message_create_t& event) { - if (event.msg.content == "!ping") { - event.reply("Pong!"); + try { + if (event.msg.content == "!ping") { + event.reply("Pong!"); + } + } catch (const std::exception& e) { + std::cerr << "Error handling message: " << e.what() << std::endl; + event.reply("An error occurred while processing your request."); } }); } void run() { - bot.start(dpp::st_wait); + try { + bot.start(&dpp::set_nonblocking); + } catch (const dpp::exception& e) { + std::cerr << "Failed to start the Discord bot: " << e.what() << std::endl; + throw std::runtime_error(e.what()); + } catch (const std::exception& e) { + std::cerr << "A general exception occurred: " << e.what() << std::endl; + throw e; + } + } + + void send_message(const std::string& channel_id, const std::string& message, const Callback& callback) { + bot.message_create(dpp::message(channel_id, message), [callback, channel_id, message](const dpp::confirmation_callback_t& cb) { + if (cb.is_error()) { + std::cerr << "Failed to send message to channel " << channel_id << ": " << cb.http_info.body << std::endl; + if (callback) callback(false, cb.http_info.body); + } else { + std::cout << "Message sent successfully to channel " << channel_id << ": " << message << std::endl; + if (callback) callback(true, "Message sent successfully"); + } + }); + } + + void send_image(const std::string& channel_id, const std::string& image_path, const Callback& callback) { + dpp::message msg(channel_id, "Hey there, I've got a new file!"); + + // Read the image file into a string + std::string image_data = dpp::utility::read_file(image_path); + std::cerr << "Loaded image data size: " << image_data.size() << " bytes" << std::endl; + + if (image_data.empty()) { + std::cerr << "Failed to read image file: " << image_path << std::endl; + if (callback) callback(false, "Failed to read image file"); + return; + } + + // Attach the image data to the message + msg.add_file("image.jpg", image_data); + + // Send the message with the image + bot.message_create(msg, [callback, channel_id](const dpp::confirmation_callback_t& cb) { + if (cb.is_error()) { + std::cerr << "Failed to send image to channel " << channel_id << ": " << cb.http_info.body << std::endl; + if (callback) callback(false, cb.http_info.body); + } else { + std::cout << "Image sent successfully to channel " << channel_id << std::endl; + if (callback) callback(true, "Image sent successfully"); + } + }); } private: @@ -30,4 +86,12 @@ namespace Notification { pImpl->run(); } + void DiscordBot::send_message(const std::string& channel_id, const std::string& message, const Callback& callback) const { + pImpl->send_message(channel_id, message, callback); + } + + void DiscordBot::send_image(const std::string& channel_id, const std::string& image_path, const Callback& callback) const { + pImpl->send_image(channel_id, image_path, callback); + } + } // namespace Notification From 19dcc2e5721084c2b20eb4dad47d58642dc5ef77 Mon Sep 17 00:00:00 2001 From: napalys Date: Tue, 28 May 2024 14:28:24 +0200 Subject: [PATCH 07/34] Added a way to receive error's and notifications from discord. --- discord/include/discord_bot.h | 4 +++- discord/src/discord_bot.cpp | 25 +++++++------------------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/discord/include/discord_bot.h b/discord/include/discord_bot.h index 729076e..293a04f 100644 --- a/discord/include/discord_bot.h +++ b/discord/include/discord_bot.h @@ -9,7 +9,9 @@ namespace Notification { class DiscordBot { public: - explicit DiscordBot(const std::string& token); + using LogCallback = std::function; + + explicit DiscordBot(const std::string& token, LogCallback log_callback = nullptr); ~DiscordBot() noexcept; void run(); diff --git a/discord/src/discord_bot.cpp b/discord/src/discord_bot.cpp index f6a1bb9..f0d5868 100644 --- a/discord/src/discord_bot.cpp +++ b/discord/src/discord_bot.cpp @@ -6,9 +6,9 @@ namespace Notification { class DiscordBot::Impl { public: - explicit Impl(const std::string& token) : bot(token) { - bot.on_log([](const dpp::log_t& event) { - std::cout << event.message << std::endl; + explicit Impl(const std::string& token, LogCallback log_callback = nullptr) : bot(token), log_callback(std::move(log_callback)) { + bot.on_log([this](const dpp::log_t& event) { + if(this->log_callback) this->log_callback(event.message); }); bot.on_message_create([](const dpp::message_create_t& event) { try { @@ -37,38 +37,26 @@ namespace Notification { void send_message(const std::string& channel_id, const std::string& message, const Callback& callback) { bot.message_create(dpp::message(channel_id, message), [callback, channel_id, message](const dpp::confirmation_callback_t& cb) { if (cb.is_error()) { - std::cerr << "Failed to send message to channel " << channel_id << ": " << cb.http_info.body << std::endl; if (callback) callback(false, cb.http_info.body); } else { - std::cout << "Message sent successfully to channel " << channel_id << ": " << message << std::endl; if (callback) callback(true, "Message sent successfully"); } }); } void send_image(const std::string& channel_id, const std::string& image_path, const Callback& callback) { - dpp::message msg(channel_id, "Hey there, I've got a new file!"); - - // Read the image file into a string + dpp::message msg(channel_id, ""); std::string image_data = dpp::utility::read_file(image_path); - std::cerr << "Loaded image data size: " << image_data.size() << " bytes" << std::endl; - if (image_data.empty()) { - std::cerr << "Failed to read image file: " << image_path << std::endl; if (callback) callback(false, "Failed to read image file"); return; } + msg.add_file(image_path, image_data); - // Attach the image data to the message - msg.add_file("image.jpg", image_data); - - // Send the message with the image bot.message_create(msg, [callback, channel_id](const dpp::confirmation_callback_t& cb) { if (cb.is_error()) { - std::cerr << "Failed to send image to channel " << channel_id << ": " << cb.http_info.body << std::endl; if (callback) callback(false, cb.http_info.body); } else { - std::cout << "Image sent successfully to channel " << channel_id << std::endl; if (callback) callback(true, "Image sent successfully"); } }); @@ -76,9 +64,10 @@ namespace Notification { private: dpp::cluster bot; + LogCallback log_callback; }; - DiscordBot::DiscordBot(const std::string& token) : pImpl(std::make_unique(token)) {} + DiscordBot::DiscordBot(const std::string& token, LogCallback log_callback) : pImpl(std::make_unique(token, std::move(log_callback))) {} DiscordBot::~DiscordBot() noexcept = default; From 5803beb4727da0b4b439ee65d5130c6528f26e01 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 28 May 2024 17:51:47 +0200 Subject: [PATCH 08/34] Added missing dependencies to build workflow. In particular dpp. --- .github/workflows/build.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 27b489e..2c0d579 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -29,11 +29,17 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libevdev-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5widgets5 + sudo apt-get install -y wget + wget -O dpp.deb https://dl.dpp.dev/ + sudo dpkg -i dpp.deb - - name: Install aqtinstall on Windows + - name: Install aqtinstall and vcpkg on Windows if: runner.os == 'Windows' run: | python -m pip install aqtinstall + git clone https://github.com/microsoft/vcpkg.git + .\vcpkg\bootstrap-vcpkg.bat + .\vcpkg\vcpkg install dpp:x64-windows - name: List Qt versions available if: runner.os == 'Windows' From 8167f75635ebb1eb6a39d066ca9e9771b7af3074 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 28 May 2024 18:03:49 +0200 Subject: [PATCH 09/34] Added missing dependency installation on ubuntu for dpp. --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2c0d579..ed2f5af 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -31,7 +31,7 @@ jobs: sudo apt-get install -y libevdev-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5widgets5 sudo apt-get install -y wget wget -O dpp.deb https://dl.dpp.dev/ - sudo dpkg -i dpp.deb + sudo apt install ./dpp.deb --yes --no-install-recommends - name: Install aqtinstall and vcpkg on Windows if: runner.os == 'Windows' From 7a570bf3ad7e9e45d27fe12c150aa0e9e06d639f Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 28 May 2024 18:08:00 +0200 Subject: [PATCH 10/34] Removed false positive Waddress error. --- discord/src/discord_bot.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/discord/src/discord_bot.cpp b/discord/src/discord_bot.cpp index f0d5868..1376f7d 100644 --- a/discord/src/discord_bot.cpp +++ b/discord/src/discord_bot.cpp @@ -24,7 +24,10 @@ namespace Notification { void run() { try { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress" bot.start(&dpp::set_nonblocking); +#pragma GCC diagnostic pop } catch (const dpp::exception& e) { std::cerr << "Failed to start the Discord bot: " << e.what() << std::endl; throw std::runtime_error(e.what()); From 39570f5f35a61dd1e7e4bb9f5df62676873e0d01 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 30 May 2024 12:23:53 +0200 Subject: [PATCH 11/34] Added notification handler view. --- gui/mainwindow.cpp | 41 ++++++++++++++++++++++++++++----------- gui/mainwindow.h | 5 ++++- gui/mainwindow.ui | 48 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 13 deletions(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index f7b02e4..ee36434 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -8,6 +8,7 @@ #include #include "dialogs/clicker_data_dialog.h" #include "config.hpp" +#include "discord_bot.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { @@ -17,8 +18,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi } void MainWindow::initializeUI() { - tableManager = std::make_unique(ui->tableWidget); - tableManager->setupTable(); + table_manager = std::make_unique(ui->tableWidget); + table_manager->setupTable(); setWindowTitle(QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), InjectionClicker::cmake::project_version.data())); ui->label->setEnabled(true); ui->label->setOpenExternalLinks(true); @@ -49,7 +50,7 @@ void MainWindow::on_pushButton_Start_clicked() { return; } - if(tableManager->isEmpty()){ + if(table_manager->isEmpty()){ createErrorBox("Please add some keys to be clicked"); return; } @@ -59,7 +60,7 @@ void MainWindow::on_pushButton_Start_clicked() { } void MainWindow::enableClicker(){ - auto keyEvents = tableManager->extractAllData(); + auto keyEvents = table_manager->extractAllData(); clicker->setClickerStatus(true); ui->pushButton_Start->setText("Stop"); clicker->addRoutine(keyEvents); @@ -98,7 +99,7 @@ void MainWindow::on_pushButton_record_clicked() { void MainWindow::enableKeyStrokeRecording() { try { ProcessHandler::registerCallBack([&](const std::variant& data) { - tableManager->addRow(data); + table_manager->addRow(data); }); } catch (const std::exception &e) { @@ -153,18 +154,18 @@ void MainWindow::setPIDFoundSuccessful() { void MainWindow::on_pushButton_delete_key_clicked() { - tableManager->deleteSelectedRow(); + table_manager->deleteSelectedRow(); } void MainWindow::on_pushButton_insert_key_clicked() { GUI::Dialogs::ClickerDataDialog dialog(this); if (dialog.exec() != QDialog::Accepted) return; ClickerData data = dialog.getClickerData(); - tableManager->addRow(data); + table_manager->addRow(data); } void MainWindow::saveRoutineData() { - auto allData = tableManager->extractAllData(); + auto allData = table_manager->extractAllData(); nlohmann::json jsonData; jsonData["version"] = InjectionClicker::cmake::project_version.data(); @@ -239,7 +240,7 @@ void MainWindow::loadRoutineData() { } - tableManager->setupTable(); + table_manager->setupTable(); bool error = false; for (const auto& item : jsonData["routine"]) { if (!item.contains("type") || !item["type"].is_string()) { @@ -250,13 +251,13 @@ void MainWindow::loadRoutineData() { auto type = item["type"].get(); if (type == "ClickerData") { if (item.contains("key_name") && item.contains("key_code") && item.contains("event")) { - tableManager->addRow(item.get()); + table_manager->addRow(item.get()); } else { createErrorBox("Missing fields in ClickerData object."); } } else if (type == "Delay") { if (item.contains("delay")) { - tableManager->addRow(item.get()); + table_manager->addRow(item.get()); } else { createErrorBox("Missing 'delay' field in Delay object."); } @@ -266,3 +267,21 @@ void MainWindow::loadRoutineData() { } if(error)createErrorBox("Error in JSON data: Missing or invalid 'type' key."); } + +void MainWindow::on_pushButton_Register_Bot_clicked() { + std::cout << "presed" << std::endl; + + if (!bot) { + try{ + bot = std::make_unique("", [](const std::string& s){ + std::cout << s << std::endl; + }); + bot->run(); + + }catch (const std::exception& e){ + bot = nullptr; + createErrorBox(e.what()); + } + + } +} \ No newline at end of file diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 5358b02..be2dc58 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -5,6 +5,7 @@ #include "clicker_data.h" #include "clicker.h" #include "managers/table_manager.h" +#include "discord_bot.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -34,6 +35,7 @@ private slots: void on_pushButton_insert_key_clicked(); void on_pushButton_record_clicked(); void on_pushButton_select_window_clicked(); + void on_pushButton_Register_Bot_clicked(); private: void saveRoutineData(); @@ -49,8 +51,9 @@ private slots: private: Ui::MainWindow *ui; - std::unique_ptr tableManager; + std::unique_ptr table_manager; std::unique_ptr clicker = nullptr; + std::unique_ptr bot = nullptr; bool isRecording = false; }; diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index cacaf39..65c3844 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -134,7 +134,7 @@ 10 - 180 + 50 261 131 @@ -234,9 +234,55 @@ + + + + 10 + 200 + 261 + 201 + + + + Notification handler + + + + + 10 + 30 + 131 + 20 + + + + false + + + + + + Bot token + + + + + + 150 + 30 + 101 + 23 + + + + Register bot + + + injection_windows_group routine_group label + notifications From 059f5ce988b27a315e122ebd807d4f1b9e3e8940 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 31 May 2024 09:41:15 -0700 Subject: [PATCH 12/34] Fixed ms build. --- clicker/CMakeLists.txt | 3 ++- clicker/src/process_handler/keyboard_callback.cpp | 7 ++++--- discord/CMakeLists.txt | 9 ++++++--- discord/src/discord_bot.cpp | 11 +++++++++-- gui/mainwindow.cpp | 14 +++++++------- gui/managers/table_manager.h | 1 + 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/clicker/CMakeLists.txt b/clicker/CMakeLists.txt index 5c6882f..aa1422b 100644 --- a/clicker/CMakeLists.txt +++ b/clicker/CMakeLists.txt @@ -28,11 +28,12 @@ endif () add_library(clicker_lib STATIC ${SOURCE_FILES} ${HEADER_FILES}) target_include_directories(clicker_lib PUBLIC include ${json_SOURCE_DIR}/include) +target_link_libraries(clicker_lib nlohmann_json::nlohmann_json) set_target_properties(clicker_lib PROPERTIES LINKER_LANGUAGE CXX) if(UNIX AND NOT APPLE) include_directories(${LIBEVDEV_INCLUDE_DIRS}) - target_link_libraries(clicker_lib ${LIBEVDEV_LIBRARIES} nlohmann_json::nlohmann_json) + target_link_libraries(clicker_lib ${LIBEVDEV_LIBRARIES}) target_compile_options(clicker_lib PUBLIC ${LIBEVDEV_CFLAGS_OTHER}) endif () diff --git a/clicker/src/process_handler/keyboard_callback.cpp b/clicker/src/process_handler/keyboard_callback.cpp index 80c6721..b2a468b 100644 --- a/clicker/src/process_handler/keyboard_callback.cpp +++ b/clicker/src/process_handler/keyboard_callback.cpp @@ -6,8 +6,10 @@ #include #include +#ifdef linux #include #include +#endif #include #include #include @@ -154,7 +156,7 @@ namespace ProcessHandler { auto currentKeyPressTime = std::chrono::steady_clock::now(); auto delay = std::chrono::duration_cast( currentKeyPressTime - lastKeyPressTime).count(); - ClickerData clickerData(pKeyBoard->vkCode, ClickerData::Event::Pressed, + ClickerData clickerData(static_cast(pKeyBoard->vkCode), ClickerData::Event::Pressed, GetKeyName(pKeyBoard->vkCode)); safeCallback(Delay(static_cast(delay))); safeCallback(clickerData); @@ -166,7 +168,7 @@ namespace ProcessHandler { auto currentKeyPressTime = std::chrono::steady_clock::now(); auto delay = std::chrono::duration_cast( currentKeyPressTime - lastKeyPressTime).count(); - ClickerData clickerData(pKeyBoard->vkCode, ClickerData::Event::Released, + ClickerData clickerData(static_cast(pKeyBoard->vkCode), ClickerData::Event::Released, GetKeyName(pKeyBoard->vkCode)); safeCallback(Delay(static_cast(delay))); safeCallback(clickerData); @@ -205,7 +207,6 @@ namespace ProcessHandler { #ifdef WIN32 lastKeyPressTime = std::chrono::steady_clock::now(); SetHook(); - MSG msg; #endif keyboard_registered = true; diff --git a/discord/CMakeLists.txt b/discord/CMakeLists.txt index 311d975..e4589e3 100644 --- a/discord/CMakeLists.txt +++ b/discord/CMakeLists.txt @@ -1,13 +1,16 @@ cmake_minimum_required(VERSION 3.10) project(Notifier) -find_package(DPP REQUIRED) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +find_package(dpp CONFIG REQUIRED) include_directories(include) add_library(${PROJECT_NAME} src/discord_bot.cpp - include/discord_bot.h +# include/discord_bot.h ) target_include_directories(${PROJECT_NAME} PRIVATE @@ -19,4 +22,4 @@ target_include_directories(${PROJECT_NAME} PUBLIC ) -target_link_libraries(${PROJECT_NAME} dpp) +target_link_libraries(${PROJECT_NAME} dpp::dpp) diff --git a/discord/src/discord_bot.cpp b/discord/src/discord_bot.cpp index 1376f7d..977d4ec 100644 --- a/discord/src/discord_bot.cpp +++ b/discord/src/discord_bot.cpp @@ -1,5 +1,6 @@ #include "discord_bot.h" #include + #include namespace Notification { @@ -24,10 +25,16 @@ namespace Notification { void run() { try { -#pragma GCC diagnostic push +#if defined(__GNUC__) || defined(__clang__) + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" - bot.start(&dpp::set_nonblocking); +#endif + + bot.start(); + +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop +#endif } catch (const dpp::exception& e) { std::cerr << "Failed to start the Discord bot: " << e.what() << std::endl; throw std::runtime_error(e.what()); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index ee36434..120a120 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -98,8 +98,8 @@ void MainWindow::on_pushButton_record_clicked() { void MainWindow::enableKeyStrokeRecording() { try { - ProcessHandler::registerCallBack([&](const std::variant& data) { - table_manager->addRow(data); + ProcessHandler::registerCallBack([&](const std::variant& action) { + table_manager->addRow(action); }); } catch (const std::exception &e) { @@ -160,20 +160,20 @@ void MainWindow::on_pushButton_delete_key_clicked() { void MainWindow::on_pushButton_insert_key_clicked() { GUI::Dialogs::ClickerDataDialog dialog(this); if (dialog.exec() != QDialog::Accepted) return; - ClickerData data = dialog.getClickerData(); - table_manager->addRow(data); + ClickerData clicker_data = dialog.getClickerData(); + table_manager->addRow(clicker_data); } void MainWindow::saveRoutineData() { - auto allData = table_manager->extractAllData(); + auto extracted_actions = table_manager->extractAllData(); nlohmann::json jsonData; jsonData["version"] = InjectionClicker::cmake::project_version.data(); - for (auto &data: allData) { + for (auto &action: extracted_actions) { jsonData["routine"].push_back(std::visit([](auto &&arg) -> nlohmann::json { return arg; - }, data)); + }, action)); } const auto initialDir = QDir::currentPath() + "/Routines"; diff --git a/gui/managers/table_manager.h b/gui/managers/table_manager.h index 96110d2..806f50a 100644 --- a/gui/managers/table_manager.h +++ b/gui/managers/table_manager.h @@ -3,6 +3,7 @@ #include #include +#include #include "clicker_data.h" Q_DECLARE_METATYPE(std::shared_ptr) From 029ca1f1baab18de0e06957a6c4f61d591336573 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 31 May 2024 10:27:11 -0700 Subject: [PATCH 13/34] Fixed dpp being wrongly added --- discord/CMakeLists.txt | 9 ++++++--- discord/src/discord_bot.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/discord/CMakeLists.txt b/discord/CMakeLists.txt index e4589e3..ccaa5b1 100644 --- a/discord/CMakeLists.txt +++ b/discord/CMakeLists.txt @@ -10,7 +10,7 @@ include_directories(include) add_library(${PROJECT_NAME} src/discord_bot.cpp -# include/discord_bot.h + include/discord_bot.h ) target_include_directories(${PROJECT_NAME} PRIVATE @@ -21,5 +21,8 @@ target_include_directories(${PROJECT_NAME} PUBLIC include ) - -target_link_libraries(${PROJECT_NAME} dpp::dpp) +if (WIN32) + target_link_libraries(${PROJECT_NAME} PRIVATE dpp::dpp) +elseif (UNIX) + target_link_libraries(${PROJECT_NAME} PRIVATE dpp) +endif() \ No newline at end of file diff --git a/discord/src/discord_bot.cpp b/discord/src/discord_bot.cpp index 977d4ec..2aa7f97 100644 --- a/discord/src/discord_bot.cpp +++ b/discord/src/discord_bot.cpp @@ -30,7 +30,7 @@ namespace Notification { #pragma GCC diagnostic ignored "-Waddress" #endif - bot.start(); + bot.start(dpp::st_return); #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop From 83102dfffe3768534eec6e4eba82d77630f2a4d7 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 31 May 2024 11:19:08 -0700 Subject: [PATCH 14/34] Fixed issue where dpp was added multiple times. --- CMakeLists.txt | 2 -- clicker/src/process_handler/windows_process_manager.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d78ad7..682b729 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,6 @@ add_executable(${PROJECT_NAME} main.cpp gui/dialogs/clicker_data_dialog.h gui/managers/table_manager.cpp gui/managers/table_manager.h - discord/src/discord_bot.cpp - discord/include/discord_bot.h ) target_include_directories(${PROJECT_NAME} PRIVATE gui) diff --git a/clicker/src/process_handler/windows_process_manager.cpp b/clicker/src/process_handler/windows_process_manager.cpp index 68d63c1..a854a3a 100644 --- a/clicker/src/process_handler/windows_process_manager.cpp +++ b/clicker/src/process_handler/windows_process_manager.cpp @@ -17,7 +17,7 @@ HWND receiveHWND(const uint32_t &dwProcessID, const std::string &processName) { vhWnds.emplace_back(hCurWnd); // add the found hCurWnd to the vector std::wstring title(GetWindowTextLength(hCurWnd) + 1, L'\0'); - GetWindowTextW(hCurWnd, &title[0], title.size()); + GetWindowTextW(hCurWnd, &title[0], static_cast(title.size())); std::string str(title.begin(), title.end()); if (str.find(processName) != std::string::npos) targetWindow = hCurWnd; @@ -35,7 +35,7 @@ HWND receiveHWND(const uint32_t &dwProcessID, const std::string &processName) { for (auto &hwnd: vhWnds) { std::wstring title(GetWindowTextLength(hwnd) + 1, L'\0'); - GetWindowTextW(hwnd, &title[0], title.size()); + GetWindowTextW(hwnd, &title[0], static_cast(title.size())); std::string str(title.begin(), title.end()); //remove string terminator, thus after converting to char we do not lose data str.erase(std::find(str.begin(), str.end(), '\0'), str.end()); From 5cfd8e2c6e53367d0c7ecae1b180e1d130ab0ef5 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 31 May 2024 11:57:41 -0700 Subject: [PATCH 15/34] Added github self-hosted runner. --- .github/workflows/build_win.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/build_win.yaml diff --git a/.github/workflows/build_win.yaml b/.github/workflows/build_win.yaml new file mode 100644 index 0000000..f0cf8cd --- /dev/null +++ b/.github/workflows/build_win.yaml @@ -0,0 +1,16 @@ +name: Build windows + +on: push + +jobs: + build: + runs-on: [self-hosted, Windows, X64] + + steps: + - uses: actions/checkout@v3 + + - name: Configure and Build CMake for each OS + run: | + cmake -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -B build + cmake --build build --config Release + shell: cmd From 24786066a7e557db683ced04e878ffd2b974407a Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 31 May 2024 21:01:59 +0200 Subject: [PATCH 16/34] Added ubuntu docker with dependencies. --- .github/workflows/build.yaml | 84 ------------------------------ .github/workflows/build_linux.yaml | 20 +++++++ 2 files changed, 20 insertions(+), 84 deletions(-) delete mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/build_linux.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml deleted file mode 100644 index ed2f5af..0000000 --- a/.github/workflows/build.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: Build windows and linux - -on: push - -jobs: - build: - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Cache Python Packages on Windows - if: runner.os == 'Windows' - uses: actions/cache@v2 - with: - path: ~\AppData\Local\Pip\Cache - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies on Ubuntu - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y libevdev-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5widgets5 - sudo apt-get install -y wget - wget -O dpp.deb https://dl.dpp.dev/ - sudo apt install ./dpp.deb --yes --no-install-recommends - - - name: Install aqtinstall and vcpkg on Windows - if: runner.os == 'Windows' - run: | - python -m pip install aqtinstall - git clone https://github.com/microsoft/vcpkg.git - .\vcpkg\bootstrap-vcpkg.bat - .\vcpkg\vcpkg install dpp:x64-windows - - - name: List Qt versions available - if: runner.os == 'Windows' - run: | - python -m aqt list-qt windows desktop - - - name: List architectures for Qt 5.15.2 - if: runner.os == 'Windows' - run: | - python -m aqt list-qt windows desktop --arch 5.15.2 - - - name: Cache Qt Installation on Windows - if: runner.os == 'Windows' - id: qt-cache - uses: actions/cache@v2 - with: - path: C:\Qt - key: ${{ runner.os }}-qt-5.15.2-${{ hashFiles('**/CMakeLists.txt') }} - - - name: Install Qt using aqtinstall - if: runner.os == 'Windows' && steps.qt-cache.outputs.cache-hit != 'true' - run: | - python -m aqt install-qt windows desktop 5.15.2 win64_mingw81 --outputdir C:\Qt - - - name: Configure CMake for Ubuntu - if: runner.os == 'Linux' - run: | - mkdir build - cd build - cmake .. -DCMAKE_PREFIX_PATH="/usr/lib/x86_64-linux-gnu/qt5" - - - name: Configure CMake for Windows with MinGW - if: runner.os == 'Windows' - run: | - mkdir build - cd build - cmake .. -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -DCMAKE_PREFIX_PATH="C:\Qt\5.15.2\mingw81_64" - - - name: Build the Project - run: | - cd build - cmake --build . --config Release \ No newline at end of file diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml new file mode 100644 index 0000000..cbe6477 --- /dev/null +++ b/.github/workflows/build_linux.yaml @@ -0,0 +1,20 @@ +name: Build on Linux + +on: push + +jobs: + build: + runs-on: ubuntu-latest + container: + image: napalys/attachable-clicker-docker:latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Configure and Build with CMake + run: | + mkdir build + cd build + cmake .. + cmake --build . --config Release From cf20ddf2239820b50573f161c8da976f8da1df8b Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 31 May 2024 21:23:10 +0200 Subject: [PATCH 17/34] Updated json version from 9.1 to 11.3 --- clicker/CMakeLists.txt | 1 + cmake-modules/FindJSON.cmake | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clicker/CMakeLists.txt b/clicker/CMakeLists.txt index aa1422b..c2139be 100644 --- a/clicker/CMakeLists.txt +++ b/clicker/CMakeLists.txt @@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.10) project(clicker_lib) set(HEADER_FILES include/clicker_data.h include/clicker.h diff --git a/cmake-modules/FindJSON.cmake b/cmake-modules/FindJSON.cmake index 2a81b19..6ddaa44 100644 --- a/cmake-modules/FindJSON.cmake +++ b/cmake-modules/FindJSON.cmake @@ -6,7 +6,7 @@ else() include(FetchContent) FetchContent_Declare(json GIT_REPOSITORY https://github.com/nlohmann/json.git - GIT_TAG v3.9.1) + GIT_TAG v3.11.3) FetchContent_GetProperties(json) if (NOT json_POPULATED) From 6e4e8bb7dfd3d84dc1b706c306541e72eaaef949 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 09:51:07 +0200 Subject: [PATCH 18/34] Added a way to register bot for notifications. --- CMakeLists.txt | 2 + gui/dialogs/loading_dialog.cpp | 26 +++++++++++++ gui/dialogs/loading_dialog.h | 21 +++++++++++ gui/mainwindow.cpp | 69 +++++++++++++++++++++++++++------- gui/mainwindow.h | 3 ++ gui/mainwindow.ui | 21 ++++++++++- 6 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 gui/dialogs/loading_dialog.cpp create mode 100644 gui/dialogs/loading_dialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 682b729..32f3f00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ add_executable(${PROJECT_NAME} main.cpp gui/dialogs/clicker_data_dialog.h gui/managers/table_manager.cpp gui/managers/table_manager.h + gui/dialogs/loading_dialog.cpp + gui/dialogs/loading_dialog.h ) target_include_directories(${PROJECT_NAME} PRIVATE gui) diff --git a/gui/dialogs/loading_dialog.cpp b/gui/dialogs/loading_dialog.cpp new file mode 100644 index 0000000..344a447 --- /dev/null +++ b/gui/dialogs/loading_dialog.cpp @@ -0,0 +1,26 @@ +// +// Created by Widok on 5/31/24. +// + +#include +#include +#include +#include "loading_dialog.h" + + +namespace GUI::Dialogs { + LoadingDialog::LoadingDialog(QWidget *parent) : QDialog(parent) { + setWindowTitle(tr("Loading...")); + setModal(true); + auto *layout = new QVBoxLayout(this); + + auto *progressBar = new QProgressBar; + progressBar->setMinimum(0); + progressBar->setMaximum(0); + + layout->addWidget(new QLabel(tr("Please wait..."))); + layout->addWidget(progressBar); + resize(200, 100); + } +} // Dialogs +// GUI \ No newline at end of file diff --git a/gui/dialogs/loading_dialog.h b/gui/dialogs/loading_dialog.h new file mode 100644 index 0000000..a2c4fe9 --- /dev/null +++ b/gui/dialogs/loading_dialog.h @@ -0,0 +1,21 @@ +// +// Created by Widok on 5/31/24. +// + +#ifndef INJECTIONCLICKER_LOADING_DIALOG_H +#define INJECTIONCLICKER_LOADING_DIALOG_H + + +#include + +namespace GUI::Dialogs { + + class LoadingDialog : public QDialog { + public: + explicit LoadingDialog(QWidget *parent = nullptr); + }; + +} // Dialogs +// GUI + +#endif //INJECTIONCLICKER_LOADING_DIALOG_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 120a120..5c301b6 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -5,10 +5,13 @@ #include "process_handler/keyboard_callback.h" #include #include +#include #include #include "dialogs/clicker_data_dialog.h" #include "config.hpp" #include "discord_bot.h" +#include "dialogs/loading_dialog.h" +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { @@ -268,20 +271,60 @@ void MainWindow::loadRoutineData() { if(error)createErrorBox("Error in JSON data: Missing or invalid 'type' key."); } +void MainWindow::setNotificationConnected() { + ui->pushButton_Register_Bot->setEnabled(false); + ui->pushButton_Register_Bot->setAutoFillBackground(true); + ui->pushButton_Register_Bot->setStyleSheet("background-color: rgb(50, 165, 89); color: rgb(255, 255, 255)"); + ui->pushButton_Register_Bot->setText("Bot connected"); + ui->lineEdit_bot_token->setDisabled(true); + ui->lineEdit_channel_id->setDisabled(true); +} + void MainWindow::on_pushButton_Register_Bot_clicked() { - std::cout << "presed" << std::endl; + if (ui->lineEdit_channel_id->text().isEmpty()) { + createErrorBox("Please add channel id"); + return; + } if (!bot) { - try{ - bot = std::make_unique("", [](const std::string& s){ - std::cout << s << std::endl; - }); - bot->run(); - - }catch (const std::exception& e){ - bot = nullptr; - createErrorBox(e.what()); - } - + auto *loadingDialog = new GUI::Dialogs::LoadingDialog(this); + loadingDialog->setAttribute(Qt::WA_DeleteOnClose); + loadingDialog->show(); + + QtConcurrent::run([this, loadingDialog]() { + try { + const auto token = ui->lineEdit_bot_token->text().toStdString(); + const auto chan_id = ui->lineEdit_channel_id->text().toStdString(); + + bot = std::make_unique(token, [](const std::string &s) { + std::cout << s << std::endl; + }); + bot->run(); + bot->send_message(chan_id, "Successfully connected", + [this, loadingDialog](bool success, const std::string &err) { + QMetaObject::invokeMethod(this, [this, success, err, loadingDialog]() { + if (!success) { + createErrorBox(err); + bot = nullptr; + } else { + setNotificationConnected(); + } + loadingDialog->accept(); + loadingDialog->deleteLater(); + + }); + }); + } catch (const std::exception &e) { + QMetaObject::invokeMethod(this, "createErrorBoxQStr", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(e.what()))); + bot = nullptr; + loadingDialog->accept(); + loadingDialog->deleteLater(); + } + }); } -} \ No newline at end of file +} + +void MainWindow::createErrorBoxQStr(const QString &errorMsg) { + QMessageBox::warning(this, "Error", errorMsg); +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index be2dc58..ee368eb 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -36,11 +36,14 @@ private slots: void on_pushButton_record_clicked(); void on_pushButton_select_window_clicked(); void on_pushButton_Register_Bot_clicked(); + void createErrorBoxQStr(const QString &errorMsg); private: void saveRoutineData(); void loadRoutineData(); void setPIDFoundSuccessful(); + void setNotificationConnected(); + void setNotificationDisconnected(); void enableKeyStrokeRecording(); void disableKeyStrokeRecording(); void enableClicker(); diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 65c3844..59eb1ef 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -269,7 +269,7 @@ 150 - 30 + 40 101 23 @@ -278,6 +278,25 @@ Register bot + + + + 10 + 60 + 131 + 20 + + + + false + + + + + + Channel id + + injection_windows_group routine_group From 3a9ec336d2c2cf5ddff908bf022f31f8f02af91b Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 10:01:17 +0200 Subject: [PATCH 19/34] Added a way to remember bot token and channel id. --- gui/mainwindow.cpp | 91 +++++++++++++++++++++++++++------------------- gui/mainwindow.h | 2 + 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 5c301b6..3283c84 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -5,7 +5,6 @@ #include "process_handler/keyboard_callback.h" #include #include -#include #include #include "dialogs/clicker_data_dialog.h" #include "config.hpp" @@ -26,11 +25,30 @@ void MainWindow::initializeUI() { setWindowTitle(QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), InjectionClicker::cmake::project_version.data())); ui->label->setEnabled(true); ui->label->setOpenExternalLinks(true); + loadSettings(); } +void MainWindow::loadSettings() { + QSettings settings("NoName", "InjectionClicker"); + QString token = settings.value("bot_token", "").toString(); + QString chan_id = settings.value("channel_id", "").toString(); + ui->lineEdit_bot_token->setText(token); + ui->lineEdit_channel_id->setText(chan_id); +} + +void MainWindow::saveSettings() { + QSettings settings("NoName", "InjectionClicker"); + settings.setValue("bot_token", ui->lineEdit_bot_token->text()); + settings.setValue("channel_id", ui->lineEdit_channel_id->text()); +} + + void MainWindow::connectSignals() { connect(ui->actionSave, &QAction::triggered, this, &MainWindow::saveRoutineData); connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadRoutineData); + connect(ui->lineEdit_bot_token, &QLineEdit::textChanged, this, &MainWindow::saveSettings); + connect(ui->lineEdit_channel_id, &QLineEdit::textChanged, this, &MainWindow::saveSettings); + qRegisterMetaType(); } @@ -286,43 +304,42 @@ void MainWindow::on_pushButton_Register_Bot_clicked() { return; } - if (!bot) { - auto *loadingDialog = new GUI::Dialogs::LoadingDialog(this); - loadingDialog->setAttribute(Qt::WA_DeleteOnClose); - loadingDialog->show(); - - QtConcurrent::run([this, loadingDialog]() { - try { - const auto token = ui->lineEdit_bot_token->text().toStdString(); - const auto chan_id = ui->lineEdit_channel_id->text().toStdString(); - - bot = std::make_unique(token, [](const std::string &s) { - std::cout << s << std::endl; - }); - bot->run(); - bot->send_message(chan_id, "Successfully connected", - [this, loadingDialog](bool success, const std::string &err) { - QMetaObject::invokeMethod(this, [this, success, err, loadingDialog]() { - if (!success) { - createErrorBox(err); - bot = nullptr; - } else { - setNotificationConnected(); - } - loadingDialog->accept(); - loadingDialog->deleteLater(); - - }); + if (bot) return; + auto *loadingDialog = new GUI::Dialogs::LoadingDialog(this); + loadingDialog->setAttribute(Qt::WA_DeleteOnClose); + loadingDialog->show(); + + QtConcurrent::run([this, loadingDialog]() { + try { + const auto token = ui->lineEdit_bot_token->text().toStdString(); + const auto chan_id = ui->lineEdit_channel_id->text().toStdString(); + + bot = std::make_unique(token, [](const std::string &s) { + std::cout << s << std::endl; + }); + bot->run(); + bot->send_message(chan_id, "Successfully connected", + [this, loadingDialog](bool success, const std::string &err) { + QMetaObject::invokeMethod(this, [this, success, err, loadingDialog]() { + if (!success) { + createErrorBox(err); + bot = nullptr; + } else { + setNotificationConnected(); + } + loadingDialog->accept(); + loadingDialog->deleteLater(); + }); - } catch (const std::exception &e) { - QMetaObject::invokeMethod(this, "createErrorBoxQStr", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(e.what()))); - bot = nullptr; - loadingDialog->accept(); - loadingDialog->deleteLater(); - } - }); - } + }); + } catch (const std::exception &e) { + QMetaObject::invokeMethod(this, "createErrorBoxQStr", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(e.what()))); + bot = nullptr; + loadingDialog->accept(); + loadingDialog->deleteLater(); + } + }); } void MainWindow::createErrorBoxQStr(const QString &errorMsg) { diff --git a/gui/mainwindow.h b/gui/mainwindow.h index ee368eb..5d097c1 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -50,6 +50,8 @@ private slots: void disableClicker(); void initializeUI(); void connectSignals(); + void loadSettings(); + void saveSettings(); static void createErrorBox(const std::string &errorMsg); private: From 92d63484525937b88c26cdc5d080619e0464b4e3 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 12:02:19 +0200 Subject: [PATCH 20/34] Added anomaly table. --- CMakeLists.txt | 4 ++ gui/dialogs/add_anomaly_dialog.cpp | 73 ++++++++++++++++++++++++++++++ gui/dialogs/add_anomaly_dialog.h | 42 +++++++++++++++++ gui/mainwindow.cpp | 16 +++++++ gui/mainwindow.h | 4 ++ gui/mainwindow.ui | 58 +++++++++++++++++++++++- gui/managers/anomaly_manager.cpp | 53 ++++++++++++++++++++++ gui/managers/anomaly_manager.h | 34 ++++++++++++++ 8 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 gui/dialogs/add_anomaly_dialog.cpp create mode 100644 gui/dialogs/add_anomaly_dialog.h create mode 100644 gui/managers/anomaly_manager.cpp create mode 100644 gui/managers/anomaly_manager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 32f3f00..86a830d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,10 @@ add_executable(${PROJECT_NAME} main.cpp gui/managers/table_manager.h gui/dialogs/loading_dialog.cpp gui/dialogs/loading_dialog.h + gui/managers/anomaly_manager.cpp + gui/managers/anomaly_manager.h + gui/dialogs/add_anomaly_dialog.cpp + gui/dialogs/add_anomaly_dialog.h ) target_include_directories(${PROJECT_NAME} PRIVATE gui) diff --git a/gui/dialogs/add_anomaly_dialog.cpp b/gui/dialogs/add_anomaly_dialog.cpp new file mode 100644 index 0000000..44306c6 --- /dev/null +++ b/gui/dialogs/add_anomaly_dialog.cpp @@ -0,0 +1,73 @@ +// +// Created by Widok on 6/1/24. +// + +#include +#include +#include "add_anomaly_dialog.h" + + +namespace GUI::Dialogs { + AddAnomalyDialog::AddAnomalyDialog(QWidget *parent) : QDialog(parent) { + setupUI(); + } + + void AddAnomalyDialog::setupUI() { + imagePathEdit = new QLineEdit(this); + auto *browseButton = new QPushButton("Browse...", this); + messageEdit = new QLineEdit(this); + percentageSpin = new QSpinBox(this); + percentageSpin->setRange(0, 100); + + auto *layout = new QFormLayout(this); + auto *pathLayout = new QHBoxLayout(); + pathLayout->addWidget(imagePathEdit); + pathLayout->addWidget(browseButton); + layout->addRow(new QLabel("Image Path:"), pathLayout); + layout->addRow(new QLabel("Message:"), messageEdit); + layout->addRow(new QLabel("Percentage:"), percentageSpin); + + auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + layout->addWidget(buttons); + + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(browseButton, &QPushButton::clicked, this, + &AddAnomalyDialog::openFileDialog); // Connect the Browse button to slot + } + + void AddAnomalyDialog::openFileDialog() { + QString file = QFileDialog::getOpenFileName(this, tr("Select Image"), "", + tr("Image Files (*.png *.jpg *.jpeg *.bmp)")); + if (!file.isEmpty()) { + QString localDirPath = "anomalies"; + QDir localDir(localDirPath); + + if (!localDir.exists()) { + localDir.mkpath("."); + } + + QFileInfo fileInfo(file); + QString filename = fileInfo.fileName(); + QString newFilePath = localDirPath + "/" + filename; + + QFile::copy(file, newFilePath); + + imagePathEdit->setText(filename); + } + } + + + QString AddAnomalyDialog::getImagePath() const { + return imagePathEdit->text(); + } + + QString AddAnomalyDialog::getMessage() const { + return messageEdit->text(); + } + + int AddAnomalyDialog::getPercentage() const { + return percentageSpin->value(); + } +} // Dialogs +// GUI \ No newline at end of file diff --git a/gui/dialogs/add_anomaly_dialog.h b/gui/dialogs/add_anomaly_dialog.h new file mode 100644 index 0000000..0011543 --- /dev/null +++ b/gui/dialogs/add_anomaly_dialog.h @@ -0,0 +1,42 @@ +// +// Created by Widok on 6/1/24. +// + +#ifndef INJECTIONCLICKER_ADD_ANOMALY_DIALOG_H +#define INJECTIONCLICKER_ADD_ANOMALY_DIALOG_H + +#include +#include +#include +#include +#include +#include + +namespace GUI::Dialogs { + + class AddAnomalyDialog : public QDialog { + Q_OBJECT + + public: + explicit AddAnomalyDialog(QWidget *parent = nullptr); + + [[nodiscard]] QString getImagePath() const; + + [[nodiscard]] QString getMessage() const; + + [[nodiscard]] int getPercentage() const; + + void openFileDialog(); + + private: + QLineEdit *imagePathEdit{}; + QLineEdit *messageEdit{}; + QSpinBox *percentageSpin{}; + + void setupUI(); + }; + +} // Dialogs +// GUI + +#endif //INJECTIONCLICKER_ADD_ANOMALY_DIALOG_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 3283c84..402ec9b 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -10,6 +10,7 @@ #include "config.hpp" #include "discord_bot.h" #include "dialogs/loading_dialog.h" +#include "dialogs/add_anomaly_dialog.h" #include @@ -21,7 +22,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi void MainWindow::initializeUI() { table_manager = std::make_unique(ui->tableWidget); + anomaly_manager = std::make_unique(ui->tableWidget_anomaly); table_manager->setupTable(); + anomaly_manager->setupTable(); setWindowTitle(QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), InjectionClicker::cmake::project_version.data())); ui->label->setEnabled(true); ui->label->setOpenExternalLinks(true); @@ -345,3 +348,16 @@ void MainWindow::on_pushButton_Register_Bot_clicked() { void MainWindow::createErrorBoxQStr(const QString &errorMsg) { QMessageBox::warning(this, "Error", errorMsg); } + +void MainWindow::on_pushButton_add_anomaly_clicked() { + GUI::Dialogs::AddAnomalyDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + anomaly_manager->addRow(dialog.getImagePath().toStdString(), + dialog.getMessage().toStdString(), + dialog.getPercentage()); + } +} + +void MainWindow::on_pushButton_remove_anomaly_clicked() { + +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 5d097c1..a378f8e 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -6,6 +6,7 @@ #include "clicker.h" #include "managers/table_manager.h" #include "discord_bot.h" +#include "managers/anomaly_manager.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -36,6 +37,8 @@ private slots: void on_pushButton_record_clicked(); void on_pushButton_select_window_clicked(); void on_pushButton_Register_Bot_clicked(); + void on_pushButton_add_anomaly_clicked(); + void on_pushButton_remove_anomaly_clicked(); void createErrorBoxQStr(const QString &errorMsg); private: @@ -57,6 +60,7 @@ private slots: private: Ui::MainWindow *ui; std::unique_ptr table_manager; + std::unique_ptr anomaly_manager; std::unique_ptr clicker = nullptr; std::unique_ptr bot = nullptr; bool isRecording = false; diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 59eb1ef..7bab062 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -240,7 +240,7 @@ 10 200 261 - 201 + 291 @@ -297,6 +297,62 @@ Channel id + + + + 10 + 250 + 101 + 23 + + + + Add anomaly + + + + + + 140 + 250 + 101 + 23 + + + + Remove + + + + + + 10 + 90 + 231 + 141 + + + + + Image + + + + 10 + + + + + + Message + + + + + Conf + + + injection_windows_group routine_group diff --git a/gui/managers/anomaly_manager.cpp b/gui/managers/anomaly_manager.cpp new file mode 100644 index 0000000..a4380ff --- /dev/null +++ b/gui/managers/anomaly_manager.cpp @@ -0,0 +1,53 @@ +// +// Created by Widok on 6/1/24. +// + +#include "anomaly_manager.h" +#include + +namespace GUI { + AnomalyManager::AnomalyManager(QTableWidget *table) : table(table) { + } + + + void AnomalyManager::setupTable() { + table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + table->setSelectionMode(QAbstractItemView::SingleSelection); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + table->verticalHeader()->hide(); + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + + const int totalWidth = table->width(); + table->setColumnWidth(0, static_cast(totalWidth * 0.5)); + table->setColumnWidth(1, static_cast(totalWidth * 0.4)); + table->setColumnWidth(2, static_cast(totalWidth * 0.1)); + table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + } + + + void AnomalyManager::addRow(const std::string &image_path, const std::string &message, const int percentage) { + int row = table->currentRow() + 1; + if (row == 0) row = table->rowCount(); + table->insertRow(row); + auto *path = new QTableWidgetItem(QString::fromStdString(image_path)); + path->setFlags(path->flags() ^ Qt::ItemIsEditable); + + auto *msg = new QTableWidgetItem(QString::fromStdString(message)); + msg->setFlags(msg->flags() ^ Qt::ItemIsEditable); + + auto *percent = new QTableWidgetItem(QString::number(percentage)); + percent->setFlags(percent->flags() ^ Qt::ItemIsEditable); + percent->setTextAlignment(Qt::AlignCenter); + + table->setItem(row, 0, path); + table->setItem(row, 1, msg); + table->setItem(row, 2, percent); + } + + void AnomalyManager::deleteSelectedRow() noexcept { + QModelIndex currentIndex = table->selectionModel()->currentIndex(); + if (!currentIndex.isValid()) return; + table->removeRow(currentIndex.row()); + emit dataChanged(); + } +} // GUI \ No newline at end of file diff --git a/gui/managers/anomaly_manager.h b/gui/managers/anomaly_manager.h new file mode 100644 index 0000000..531bbeb --- /dev/null +++ b/gui/managers/anomaly_manager.h @@ -0,0 +1,34 @@ +// +// Created by Widok on 6/1/24. +// + +#ifndef INJECTIONCLICKER_ANOMALY_MANAGER_H +#define INJECTIONCLICKER_ANOMALY_MANAGER_H + +#include +#include +#include +#include "clicker_data.h" + +namespace GUI { + class AnomalyManager : public QObject { + Q_OBJECT + public: + explicit AnomalyManager(QTableWidget *table); + + void setupTable(); + + void addRow(const std::string& image_path, const std::string& message, const int percentage); + + void deleteSelectedRow() noexcept; + + signals: + void dataChanged(); + + private: + QTableWidget *table{}; + }; + +} // GUI + +#endif //INJECTIONCLICKER_ANOMALY_MANAGER_H From 4f06d545b6a396fa8da6eb5ac70ad61a84fdf0ec Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 13:51:22 +0200 Subject: [PATCH 21/34] Added interface for screenshot taking. --- clicker/include/process_handler/linux_process_manager.h | 2 ++ clicker/include/process_handler/process_manager.h | 1 + clicker/include/process_handler/windows_process_manager.h | 2 ++ clicker/src/process_handler/linux_process_manager.cpp | 4 ++++ clicker/src/process_handler/windows_process_manager.cpp | 4 ++++ gui/mainwindow.cpp | 2 +- 6 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clicker/include/process_handler/linux_process_manager.h b/clicker/include/process_handler/linux_process_manager.h index 743235f..e2033a1 100644 --- a/clicker/include/process_handler/linux_process_manager.h +++ b/clicker/include/process_handler/linux_process_manager.h @@ -15,6 +15,8 @@ namespace ProcessHandler { LinuxProcessManager(uint32_t dwProcessID, const std::string& processName); void sendClick(ClickerData clicker_data) override; + + std::string takeScreenshot() override; }; } // ProcessHandler diff --git a/clicker/include/process_handler/process_manager.h b/clicker/include/process_handler/process_manager.h index ce57265..3ac93bd 100644 --- a/clicker/include/process_handler/process_manager.h +++ b/clicker/include/process_handler/process_manager.h @@ -13,6 +13,7 @@ namespace ProcessHandler { public: virtual ~ProcessManager() = default; virtual void sendClick(ClickerData clicker_data) = 0; + virtual std::string takeScreenshot() = 0; }; } #endif //INJECTIONCLICKER_PROCCESS_MANAGER_H diff --git a/clicker/include/process_handler/windows_process_manager.h b/clicker/include/process_handler/windows_process_manager.h index e9e9622..d678c86 100644 --- a/clicker/include/process_handler/windows_process_manager.h +++ b/clicker/include/process_handler/windows_process_manager.h @@ -42,6 +42,8 @@ namespace ProcessHandler { void sendClick(ClickerData clicker_data) override; + std::string takeScreenshot() override; + void stopAll(); private: diff --git a/clicker/src/process_handler/linux_process_manager.cpp b/clicker/src/process_handler/linux_process_manager.cpp index 0269c28..542577d 100644 --- a/clicker/src/process_handler/linux_process_manager.cpp +++ b/clicker/src/process_handler/linux_process_manager.cpp @@ -11,5 +11,9 @@ namespace ProcessHandler { void LinuxProcessManager::sendClick(ClickerData clicker_data) { } + + void LinuxProcessManager::takeScreenshot() { + + } } // ProcessHandler #endif \ No newline at end of file diff --git a/clicker/src/process_handler/windows_process_manager.cpp b/clicker/src/process_handler/windows_process_manager.cpp index a854a3a..fbdb0d5 100644 --- a/clicker/src/process_handler/windows_process_manager.cpp +++ b/clicker/src/process_handler/windows_process_manager.cpp @@ -120,5 +120,9 @@ namespace ProcessHandler { cv.notify_one(); if(thread.joinable()) thread.join(); } + + void WindowsProcessManager::takeScreenshot() { + + } } // ProcessHandler #endif \ No newline at end of file diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 402ec9b..6383e6a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -359,5 +359,5 @@ void MainWindow::on_pushButton_add_anomaly_clicked() { } void MainWindow::on_pushButton_remove_anomaly_clicked() { - + anomaly_manager->deleteSelectedRow(); } From d82ae09b132d01ff1912a228a6ced9812b2fb9d7 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 13:58:27 +0200 Subject: [PATCH 22/34] Fixed cpp return type --- clicker/src/process_handler/linux_process_manager.cpp | 2 +- clicker/src/process_handler/windows_process_manager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clicker/src/process_handler/linux_process_manager.cpp b/clicker/src/process_handler/linux_process_manager.cpp index 542577d..7fe20e0 100644 --- a/clicker/src/process_handler/linux_process_manager.cpp +++ b/clicker/src/process_handler/linux_process_manager.cpp @@ -12,7 +12,7 @@ namespace ProcessHandler { } - void LinuxProcessManager::takeScreenshot() { + std::string LinuxProcessManager::takeScreenshot() { } } // ProcessHandler diff --git a/clicker/src/process_handler/windows_process_manager.cpp b/clicker/src/process_handler/windows_process_manager.cpp index fbdb0d5..bfcbf3c 100644 --- a/clicker/src/process_handler/windows_process_manager.cpp +++ b/clicker/src/process_handler/windows_process_manager.cpp @@ -121,7 +121,7 @@ namespace ProcessHandler { if(thread.joinable()) thread.join(); } - void WindowsProcessManager::takeScreenshot() { + std::string WindowsProcessManager::takeScreenshot() { } } // ProcessHandler From 67ea6bcf040b377f47c3548bb7401eae43b60085 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 08:36:44 -0700 Subject: [PATCH 23/34] Added a way to retrieve a hwnd ss. --- .../process_handler/linux_process_manager.cpp | 2 +- .../windows_process_manager.cpp | 65 +++++++++++++++++++ gui/mainwindow.cpp | 3 +- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/clicker/src/process_handler/linux_process_manager.cpp b/clicker/src/process_handler/linux_process_manager.cpp index 7fe20e0..7527f86 100644 --- a/clicker/src/process_handler/linux_process_manager.cpp +++ b/clicker/src/process_handler/linux_process_manager.cpp @@ -13,7 +13,7 @@ namespace ProcessHandler { } std::string LinuxProcessManager::takeScreenshot() { - + return ""; } } // ProcessHandler #endif \ No newline at end of file diff --git a/clicker/src/process_handler/windows_process_manager.cpp b/clicker/src/process_handler/windows_process_manager.cpp index bfcbf3c..5994fee 100644 --- a/clicker/src/process_handler/windows_process_manager.cpp +++ b/clicker/src/process_handler/windows_process_manager.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "process_handler/windows_process_manager.h" HWND receiveHWND(const uint32_t &dwProcessID, const std::string &processName) { @@ -122,7 +123,71 @@ namespace ProcessHandler { } std::string WindowsProcessManager::takeScreenshot() { + HDC hdcWindow = GetDC(hwnd); + HDC hdcMemDC = CreateCompatibleDC(hdcWindow); + RECT rc; + GetClientRect(hwnd, &rc); + + HBITMAP hbmScreen = CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top); + SelectObject(hdcMemDC, hbmScreen); + + if (!PrintWindow(hwnd, hdcMemDC, PW_CLIENTONLY)) { + std::cerr << "Failed to capture window!" << std::endl; + return ""; + } + + BITMAP bmpScreen; + GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen); + + BITMAPFILEHEADER bmfHeader; + BITMAPINFOHEADER bi; + + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = bmpScreen.bmWidth; + bi.biHeight = bmpScreen.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = 32; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight; + + HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize); + char *lpbitmap = (char *)GlobalLock(hDIB); + + GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS); + DWORD pid = GetCurrentProcessId(); + + std::string filename = "screenshot_" + std::to_string(pid) + ".bmp"; + + HANDLE hFile = CreateFile(filename.data(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + + bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + bmfHeader.bfSize = dwSizeofDIB; + bmfHeader.bfType = 0x4D42; + + DWORD dwBytesWritten = 0; + WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL); + WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL); + WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL); + + GlobalUnlock(hDIB); + GlobalFree(hDIB); + CloseHandle(hFile); + + DeleteObject(hbmScreen); + DeleteObject(hdcMemDC); + ReleaseDC(hwnd, hdcWindow); + return filename; } + + } // ProcessHandler #endif \ No newline at end of file diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 6383e6a..d7d97c4 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -359,5 +359,6 @@ void MainWindow::on_pushButton_add_anomaly_clicked() { } void MainWindow::on_pushButton_remove_anomaly_clicked() { - anomaly_manager->deleteSelectedRow(); + clicker->process_manager->takeScreenshot(); +// anomaly_manager->deleteSelectedRow(); } From 2b532213fa5051e7948181e30460a7b0feddc808 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 19:05:31 +0200 Subject: [PATCH 24/34] Added a way to anomalie handler. --- CMakeLists.txt | 4 +++ gui/mainwindow.cpp | 8 ++++- gui/mainwindow.h | 4 ++- gui/managers/anomaly.h | 19 ++++++++++++ gui/managers/anomaly_manager.cpp | 18 +++++++++++ gui/managers/anomaly_manager.h | 6 +++- gui/managers/anomaly_runner.cpp | 41 +++++++++++++++++++++++++ gui/managers/anomaly_runner.h | 38 +++++++++++++++++++++++ imaging/CMakeLists.txt | 21 +++++++++++++ imaging/include/image_processing.h | 16 ++++++++++ imaging/src/image_processing.cpp | 49 ++++++++++++++++++++++++++++++ 11 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 gui/managers/anomaly.h create mode 100644 gui/managers/anomaly_runner.cpp create mode 100644 gui/managers/anomaly_runner.h create mode 100644 imaging/CMakeLists.txt create mode 100644 imaging/include/image_processing.h create mode 100644 imaging/src/image_processing.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 86a830d..a3e6e58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ find_package(Qt5 COMPONENTS Widgets REQUIRED) add_subdirectory(clicker) add_subdirectory(discord) add_subdirectory(configured_files) +add_subdirectory(imaging) add_executable(${PROJECT_NAME} main.cpp gui/mainwindow.cpp @@ -42,6 +43,8 @@ add_executable(${PROJECT_NAME} main.cpp gui/managers/anomaly_manager.h gui/dialogs/add_anomaly_dialog.cpp gui/dialogs/add_anomaly_dialog.h + gui/managers/anomaly_runner.cpp + gui/managers/anomaly_runner.h ) target_include_directories(${PROJECT_NAME} PRIVATE gui) @@ -49,6 +52,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets clicker_lib Notifier + Imaging ) set_target_properties(${PROJECT_NAME} PROPERTIES diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index d7d97c4..34a1815 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -89,6 +89,11 @@ void MainWindow::enableClicker(){ ui->pushButton_Start->setText("Stop"); clicker->addRoutine(keyEvents); clicker->startRoutines(); + if(bot) { + anomaly_runner = std::make_unique(clicker->process_manager, bot, + ui->lineEdit_channel_id->text().toStdString(), anomaly_manager->extractAnomalies()); + anomaly_runner->run(); + } } void MainWindow::disableClicker(){ @@ -96,6 +101,7 @@ void MainWindow::disableClicker(){ ui->pushButton_Start->setText("Start"); createErrorBox("Waiting for clicker to finish task, This may take up to max declared ms"); clicker->stopRoutines(); + anomaly_manager = nullptr; } @@ -317,7 +323,7 @@ void MainWindow::on_pushButton_Register_Bot_clicked() { const auto token = ui->lineEdit_bot_token->text().toStdString(); const auto chan_id = ui->lineEdit_channel_id->text().toStdString(); - bot = std::make_unique(token, [](const std::string &s) { + bot = std::make_shared(token, [](const std::string &s) { std::cout << s << std::endl; }); bot->run(); diff --git a/gui/mainwindow.h b/gui/mainwindow.h index a378f8e..2e14fe4 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -7,6 +7,7 @@ #include "managers/table_manager.h" #include "discord_bot.h" #include "managers/anomaly_manager.h" +#include "managers/anomaly_runner.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -62,7 +63,8 @@ private slots: std::unique_ptr table_manager; std::unique_ptr anomaly_manager; std::unique_ptr clicker = nullptr; - std::unique_ptr bot = nullptr; + std::unique_ptr anomaly_runner = nullptr; + std::shared_ptr bot = nullptr; bool isRecording = false; }; diff --git a/gui/managers/anomaly.h b/gui/managers/anomaly.h new file mode 100644 index 0000000..c1a2027 --- /dev/null +++ b/gui/managers/anomaly.h @@ -0,0 +1,19 @@ +// +// Created by Widok on 6/1/24. +// + +#ifndef INJECTIONCLICKER_ANOMALY_H +#define INJECTIONCLICKER_ANOMALY_H + +#include + +using Percentage = int; + +struct Anomaly { + std::string template_image; + std::string message; + Percentage coefficient; +}; + + +#endif //INJECTIONCLICKER_ANOMALY_H diff --git a/gui/managers/anomaly_manager.cpp b/gui/managers/anomaly_manager.cpp index a4380ff..efc3f20 100644 --- a/gui/managers/anomaly_manager.cpp +++ b/gui/managers/anomaly_manager.cpp @@ -50,4 +50,22 @@ namespace GUI { table->removeRow(currentIndex.row()); emit dataChanged(); } + + std::vector AnomalyManager::extractAnomalies() { + std::vector data; + for (int i = 0; i < table->rowCount(); ++i) { + QTableWidgetItem* itemPath = table->item(i, 0); + QTableWidgetItem* itemMessage = table->item(i, 1); + QTableWidgetItem* itemPercentage = table->item(i, 2); + + if (itemPath && itemMessage && itemPercentage) { + std::string image_path = itemPath->text().toStdString(); + std::string message = itemMessage->text().toStdString(); + data.emplace_back(Anomaly{image_path, message, itemPercentage->text().toInt()}); + } + } + return data; + } + + } // GUI \ No newline at end of file diff --git a/gui/managers/anomaly_manager.h b/gui/managers/anomaly_manager.h index 531bbeb..a8cd8d5 100644 --- a/gui/managers/anomaly_manager.h +++ b/gui/managers/anomaly_manager.h @@ -8,7 +8,9 @@ #include #include #include +#include #include "clicker_data.h" +#include "anomaly.h" namespace GUI { class AnomalyManager : public QObject { @@ -18,10 +20,12 @@ namespace GUI { void setupTable(); - void addRow(const std::string& image_path, const std::string& message, const int percentage); + void addRow(const std::string& image_path, const std::string& message, int percentage); void deleteSelectedRow() noexcept; + std::vector extractAnomalies(); + signals: void dataChanged(); diff --git a/gui/managers/anomaly_runner.cpp b/gui/managers/anomaly_runner.cpp new file mode 100644 index 0000000..3985b55 --- /dev/null +++ b/gui/managers/anomaly_runner.cpp @@ -0,0 +1,41 @@ +#include "anomaly_runner.h" +#include "image_processing.h" + +namespace Runners { + AnomalyRunner::AnomalyRunner(const std::shared_ptr &processManager, + const std::shared_ptr &bot, std::string chan, + std::vector anomalies) + : process_manager( + processManager), bot(bot), channel_id(std::move(chan)), anomalies(std::move(anomalies)) {} + + void AnomalyRunner::run() { + running = true; + runner = std::thread([this]() { + while (running) { + using namespace std::chrono; + std::this_thread::sleep_for(10s); + const auto image_path = process_manager->takeScreenshot(); + if (image_path.empty()) continue; + + for(const auto& anomaly : anomalies){ + double percentage = anomaly.coefficient; + const auto path = ImageProcessing::isImageWithinImage(image_path, anomaly.template_image, percentage / 100); + if(path.empty()) continue; + bot->send_message(channel_id, anomaly.message); + bot->send_image(channel_id, path); + } + + } + }); + } + + AnomalyRunner::~AnomalyRunner() { + running = false; + runner.join(); + } + + void AnomalyRunner::stopRunner() { + running = false; + } + +} // Runners \ No newline at end of file diff --git a/gui/managers/anomaly_runner.h b/gui/managers/anomaly_runner.h new file mode 100644 index 0000000..2f635a1 --- /dev/null +++ b/gui/managers/anomaly_runner.h @@ -0,0 +1,38 @@ +#ifndef INJECTIONCLICKER_ANOMALY_RUNNER_H +#define INJECTIONCLICKER_ANOMALY_RUNNER_H + +#include +#include +#include "process_handler/process_manager.h" +#include "discord_bot.h" +#include "anomaly.h" + +namespace Runners { + + + + class AnomalyRunner { + public: + AnomalyRunner(const std::shared_ptr &processManager, + const std::shared_ptr &bot, std::string channel_id, + std::vector anomalies); + + void run(); + + void stopRunner(); + + virtual ~AnomalyRunner(); + + + private: + std::shared_ptr process_manager; + std::shared_ptr bot; + std::string channel_id; + std::vector anomalies; + std::thread runner; + std::atomic running = false; + }; + +} // Runners + +#endif //INJECTIONCLICKER_ANOMALY_RUNNER_H diff --git a/imaging/CMakeLists.txt b/imaging/CMakeLists.txt new file mode 100644 index 0000000..3b10f7e --- /dev/null +++ b/imaging/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.10) +project(Imaging) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +find_package(OpenCV CONFIG REQUIRED) + +include_directories(include) +include_directories(${OPENCV_INCLUDE_DIRS}) + +add_library(${PROJECT_NAME} + src/image_processing.cpp + include/image_processing.h +) + +target_include_directories(${PROJECT_NAME} PUBLIC + include +) + +target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS}) diff --git a/imaging/include/image_processing.h b/imaging/include/image_processing.h new file mode 100644 index 0000000..27cbb94 --- /dev/null +++ b/imaging/include/image_processing.h @@ -0,0 +1,16 @@ +// +// Created by Widok on 6/1/24. +// + +#ifndef INJECTIONCLICKER_IMAGEPROCESSING_H +#define INJECTIONCLICKER_IMAGEPROCESSING_H + +#include + +namespace ImageProcessing { + + std::string isImageWithinImage(const std::string &source, const std::string &template_image, double threshold = 0.8); + +} // ImageProcessing + +#endif //INJECTIONCLICKER_IMAGEPROCESSING_H diff --git a/imaging/src/image_processing.cpp b/imaging/src/image_processing.cpp new file mode 100644 index 0000000..0423f6b --- /dev/null +++ b/imaging/src/image_processing.cpp @@ -0,0 +1,49 @@ +// +// Created by Widok on 6/1/24. +// + +#include +#include +#include "image_processing.h" + +namespace ImageProcessing { + + std::pair perform(cv::Mat& img, cv::Mat& templ){ + cv::Mat result; + cv::matchTemplate(img, templ, result, cv::TM_CCOEFF_NORMED); + double maxVal; cv::Point maxLoc; + cv::minMaxLoc(result, nullptr, &maxVal, nullptr, &maxLoc); + return {maxVal, maxLoc}; + } + + std::string isImageWithinImage(const std::string &source, const std::string &template_image, double threshold) { + cv::Mat img = cv::imread(source); + cv::Mat templ = cv::imread(template_image); + + if (img.empty() || templ.empty()) { + std::cout << "Could not open or find the image(s)!\n"; + throw std::invalid_argument("Could open image"); + } + + cv::Mat img_hsv, templ_hsv; + cv::cvtColor(img, img_hsv, cv::COLOR_BGR2GRAY); + cv::cvtColor(templ, templ_hsv, cv::COLOR_BGR2GRAY); + + + int kernelSize = 9; + cv::GaussianBlur(img_hsv, img_hsv, cv::Size(kernelSize, kernelSize), 0); + cv::GaussianBlur(templ_hsv, templ_hsv, cv::Size(kernelSize, kernelSize), 0); + + auto [minVal, minLoc] = perform(img_hsv, templ_hsv); + + std::cout << "Minimum value (error score): " << minVal << std::endl; + if (minVal >= threshold) { + cv::rectangle(img, minLoc, cv::Point(minLoc.x + templ.cols, minLoc.y + templ.rows), cv::Scalar(0, 255, 0), 2, 8, 0); + cv::imwrite("detection.bmp", img); + std::cout << "Match found with adjusted confidence: " << (minVal) * 100 << "%" << std::endl; + return "detection.bmp"; + } + return ""; + } + +} // ImageProcessing \ No newline at end of file From 3f240d6a07536956cbbeb5476e6ccaed1ac4a2a0 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 11:53:43 -0700 Subject: [PATCH 25/34] Fixed visual studio bug deadlock. --- clicker/src/process_handler/windows_process_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clicker/src/process_handler/windows_process_manager.cpp b/clicker/src/process_handler/windows_process_manager.cpp index 5994fee..dd17337 100644 --- a/clicker/src/process_handler/windows_process_manager.cpp +++ b/clicker/src/process_handler/windows_process_manager.cpp @@ -98,7 +98,7 @@ namespace ProcessHandler { void HoldKey::startWorker() { const auto lam = [this] { - std::unique_lock lock(this->mtx, std::defer_lock); + std::unique_lock lock(this->mtx); while (this->running) { this->cv.wait(lock, [this]{ return pressed || !this->running; }); while (pressed && this->running) { From 210cc53b683a602bee49282523d5e40e827aa804 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 11:54:22 -0700 Subject: [PATCH 26/34] Fixed bug where image would not be displayed. --- gui/managers/anomaly_manager.cpp | 2 +- gui/managers/anomaly_runner.cpp | 17 +++++++++++------ imaging/src/image_processing.cpp | 13 ++++++++++--- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/gui/managers/anomaly_manager.cpp b/gui/managers/anomaly_manager.cpp index efc3f20..a6b336a 100644 --- a/gui/managers/anomaly_manager.cpp +++ b/gui/managers/anomaly_manager.cpp @@ -59,7 +59,7 @@ namespace GUI { QTableWidgetItem* itemPercentage = table->item(i, 2); if (itemPath && itemMessage && itemPercentage) { - std::string image_path = itemPath->text().toStdString(); + std::string image_path = std::string("anomalies/") + itemPath->text().toStdString(); std::string message = itemMessage->text().toStdString(); data.emplace_back(Anomaly{image_path, message, itemPercentage->text().toInt()}); } diff --git a/gui/managers/anomaly_runner.cpp b/gui/managers/anomaly_runner.cpp index 3985b55..5ce883c 100644 --- a/gui/managers/anomaly_runner.cpp +++ b/gui/managers/anomaly_runner.cpp @@ -13,16 +13,21 @@ namespace Runners { runner = std::thread([this]() { while (running) { using namespace std::chrono; - std::this_thread::sleep_for(10s); + std::this_thread::sleep_for(20s); const auto image_path = process_manager->takeScreenshot(); if (image_path.empty()) continue; for(const auto& anomaly : anomalies){ - double percentage = anomaly.coefficient; - const auto path = ImageProcessing::isImageWithinImage(image_path, anomaly.template_image, percentage / 100); - if(path.empty()) continue; - bot->send_message(channel_id, anomaly.message); - bot->send_image(channel_id, path); + try{ + double percentage = anomaly.coefficient; + const auto path = ImageProcessing::isImageWithinImage(image_path, anomaly.template_image, percentage / 100); + if(path.empty()) continue; + bot->send_message(channel_id, anomaly.message); + bot->send_image(channel_id, path); + }catch(const std::exception& e){ + std::cout << e.what() << std::endl; + } + } } diff --git a/imaging/src/image_processing.cpp b/imaging/src/image_processing.cpp index 0423f6b..bf8ffa8 100644 --- a/imaging/src/image_processing.cpp +++ b/imaging/src/image_processing.cpp @@ -36,12 +36,19 @@ namespace ImageProcessing { auto [minVal, minLoc] = perform(img_hsv, templ_hsv); - std::cout << "Minimum value (error score): " << minVal << std::endl; if (minVal >= threshold) { cv::rectangle(img, minLoc, cv::Point(minLoc.x + templ.cols, minLoc.y + templ.rows), cv::Scalar(0, 255, 0), 2, 8, 0); - cv::imwrite("detection.bmp", img); + std::string confidence_text = "Confidence: " + std::to_string(minVal * 100) + "%"; + + int font = cv::FONT_HERSHEY_SIMPLEX; + double font_scale = 0.5; + int thickness = 1; + cv::Point text_origin(minLoc.x, minLoc.y - 10); + cv::putText(img, confidence_text, text_origin, font, font_scale, cv::Scalar(0, 255, 0), thickness, 8, false); + const static std::string file_name = "detection.png"; + cv::imwrite(file_name, img); std::cout << "Match found with adjusted confidence: " << (minVal) * 100 << "%" << std::endl; - return "detection.bmp"; + return file_name; } return ""; } From 2952002b96f0c6fbc1631dfc37d3957d86e9f8f9 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 11:55:45 -0700 Subject: [PATCH 27/34] Fixed bug where on stop the anomaly table manager would be deleted instead of the runner. --- gui/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 34a1815..b549a75 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -101,7 +101,7 @@ void MainWindow::disableClicker(){ ui->pushButton_Start->setText("Start"); createErrorBox("Waiting for clicker to finish task, This may take up to max declared ms"); clicker->stopRoutines(); - anomaly_manager = nullptr; + anomaly_runner = nullptr; } From b474e8f8107281aa015b8e4398d4980474d13ed6 Mon Sep 17 00:00:00 2001 From: Napalys Date: Sat, 1 Jun 2024 22:39:40 +0200 Subject: [PATCH 28/34] Added ability to load and save anomalies. --- gui/mainwindow.cpp | 23 +++++++++++++++++++++++ gui/mainwindow.h | 1 + gui/managers/anomaly.h | 2 ++ gui/managers/anomaly_manager.cpp | 6 ++++++ gui/managers/anomaly_manager.h | 2 ++ 5 files changed, 34 insertions(+) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index b549a75..a0714ec 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -28,6 +28,7 @@ void MainWindow::initializeUI() { setWindowTitle(QString("%1 %2").arg(InjectionClicker::cmake::project_name.data(), InjectionClicker::cmake::project_version.data())); ui->label->setEnabled(true); ui->label->setOpenExternalLinks(true); + originalWindowTitle = windowTitle(); loadSettings(); } @@ -206,6 +207,11 @@ void MainWindow::saveRoutineData() { }, action)); } + auto extracted_anomalies = anomaly_manager->extractAnomalies(); + for (auto &anomaly: extracted_anomalies) { + jsonData["anomalies"].push_back(anomaly); + } + const auto initialDir = QDir::currentPath() + "/Routines"; QDir().mkpath(initialDir); QString defaultFileName = initialDir + "/Untitled.json"; @@ -248,6 +254,9 @@ void MainWindow::loadRoutineData() { return; } + QFileInfo fileInfo(file.fileName()); + QString fileTitle = fileInfo.fileName(); + QTextStream in(&file); std::string rawData = in.readAll().toStdString(); file.close(); @@ -268,6 +277,19 @@ void MainWindow::loadRoutineData() { createErrorBox("Error in JSON structure: Missing or invalid 'routine' key."); return; } + if(jsonData.contains("anomalies")){ + anomaly_manager->setupTable(); + for (const auto& anomaly : jsonData["anomalies"]) { + if (anomaly.contains("template_image") && anomaly.contains("message") && anomaly.contains("coefficient")) + anomaly_manager->addRow(anomaly); + else{ + createErrorBox("Failed loading anomalies. Missing fields."); + return; + } + } + } + + table_manager->setupTable(); @@ -296,6 +318,7 @@ void MainWindow::loadRoutineData() { } } if(error)createErrorBox("Error in JSON data: Missing or invalid 'type' key."); + setWindowTitle(originalWindowTitle + " " + fileTitle); } void MainWindow::setNotificationConnected() { diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 2e14fe4..e8e4c85 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -66,6 +66,7 @@ private slots: std::unique_ptr anomaly_runner = nullptr; std::shared_ptr bot = nullptr; bool isRecording = false; + QString originalWindowTitle; }; #endif // MAINWINDOW_H diff --git a/gui/managers/anomaly.h b/gui/managers/anomaly.h index c1a2027..fad7f46 100644 --- a/gui/managers/anomaly.h +++ b/gui/managers/anomaly.h @@ -6,6 +6,7 @@ #define INJECTIONCLICKER_ANOMALY_H #include +#include "nlohmann/json.hpp" using Percentage = int; @@ -14,6 +15,7 @@ struct Anomaly { std::string message; Percentage coefficient; }; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Anomaly, template_image, message, coefficient) #endif //INJECTIONCLICKER_ANOMALY_H diff --git a/gui/managers/anomaly_manager.cpp b/gui/managers/anomaly_manager.cpp index a6b336a..0c93030 100644 --- a/gui/managers/anomaly_manager.cpp +++ b/gui/managers/anomaly_manager.cpp @@ -11,6 +11,8 @@ namespace GUI { void AnomalyManager::setupTable() { + table->clear(); + table->setRowCount(0); table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table->setSelectionMode(QAbstractItemView::SingleSelection); table->setSelectionBehavior(QAbstractItemView::SelectRows); @@ -67,5 +69,9 @@ namespace GUI { return data; } + void AnomalyManager::addRow(const Anomaly &anomaly) { + addRow(anomaly.template_image, anomaly.message, anomaly.coefficient); + } + } // GUI \ No newline at end of file diff --git a/gui/managers/anomaly_manager.h b/gui/managers/anomaly_manager.h index a8cd8d5..d27ba38 100644 --- a/gui/managers/anomaly_manager.h +++ b/gui/managers/anomaly_manager.h @@ -22,6 +22,8 @@ namespace GUI { void addRow(const std::string& image_path, const std::string& message, int percentage); + void addRow(const Anomaly& anomaly); + void deleteSelectedRow() noexcept; std::vector extractAnomalies(); From 5df49f28a742b29de7d83e74bf911165dd54279a Mon Sep 17 00:00:00 2001 From: Napalys Date: Sun, 2 Jun 2024 10:16:27 +0200 Subject: [PATCH 29/34] Fixed pathing issues. Added welcoming message more tailored to specific instance. --- gui/dialogs/add_anomaly_dialog.cpp | 2 +- gui/mainwindow.cpp | 2 +- gui/managers/anomaly_manager.cpp | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gui/dialogs/add_anomaly_dialog.cpp b/gui/dialogs/add_anomaly_dialog.cpp index 44306c6..2914e7f 100644 --- a/gui/dialogs/add_anomaly_dialog.cpp +++ b/gui/dialogs/add_anomaly_dialog.cpp @@ -53,7 +53,7 @@ namespace GUI::Dialogs { QFile::copy(file, newFilePath); - imagePathEdit->setText(filename); + imagePathEdit->setText("anomalies/" + filename); } } diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index a0714ec..f3ac9c0 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -350,7 +350,7 @@ void MainWindow::on_pushButton_Register_Bot_clicked() { std::cout << s << std::endl; }); bot->run(); - bot->send_message(chan_id, "Successfully connected", + bot->send_message(chan_id, "Successfully connected: " + windowTitle().toStdString(), [this, loadingDialog](bool success, const std::string &err) { QMetaObject::invokeMethod(this, [this, success, err, loadingDialog]() { if (!success) { diff --git a/gui/managers/anomaly_manager.cpp b/gui/managers/anomaly_manager.cpp index 0c93030..caf05fd 100644 --- a/gui/managers/anomaly_manager.cpp +++ b/gui/managers/anomaly_manager.cpp @@ -4,6 +4,7 @@ #include "anomaly_manager.h" #include +#include namespace GUI { AnomalyManager::AnomalyManager(QTableWidget *table) : table(table) { @@ -24,6 +25,13 @@ namespace GUI { table->setColumnWidth(1, static_cast(totalWidth * 0.4)); table->setColumnWidth(2, static_cast(totalWidth * 0.1)); table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + + QStringList headers = {"Image", "Message", "Percent"}; + for (int i = 0; i < headers.size(); ++i) { + auto *headerItem = new QTableWidgetItem(headers[i]); + headerItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + table->setHorizontalHeaderItem(i, headerItem); + } } @@ -33,9 +41,11 @@ namespace GUI { table->insertRow(row); auto *path = new QTableWidgetItem(QString::fromStdString(image_path)); path->setFlags(path->flags() ^ Qt::ItemIsEditable); + path->setToolTip(QString::fromStdString(image_path)); auto *msg = new QTableWidgetItem(QString::fromStdString(message)); msg->setFlags(msg->flags() ^ Qt::ItemIsEditable); + path->setToolTip(QString::fromStdString(message)); auto *percent = new QTableWidgetItem(QString::number(percentage)); percent->setFlags(percent->flags() ^ Qt::ItemIsEditable); @@ -61,7 +71,7 @@ namespace GUI { QTableWidgetItem* itemPercentage = table->item(i, 2); if (itemPath && itemMessage && itemPercentage) { - std::string image_path = std::string("anomalies/") + itemPath->text().toStdString(); + std::string image_path = itemPath->text().toStdString(); std::string message = itemMessage->text().toStdString(); data.emplace_back(Anomaly{image_path, message, itemPercentage->text().toInt()}); } From 220527b28ff80234cba4df5665e28e730df90cc0 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 5 Jun 2024 19:13:51 +0200 Subject: [PATCH 30/34] Updated workflows. --- .github/workflows/build_linux.yaml | 2 +- .github/workflows/build_win.yaml | 2 +- .github/workflows/deploy_windows.yaml | 96 +++++---------------------- 3 files changed, 18 insertions(+), 82 deletions(-) diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml index cbe6477..a0eefb3 100644 --- a/.github/workflows/build_linux.yaml +++ b/.github/workflows/build_linux.yaml @@ -12,7 +12,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - - name: Configure and Build with CMake + - name: Cmake and build run: | mkdir build cd build diff --git a/.github/workflows/build_win.yaml b/.github/workflows/build_win.yaml index f0cf8cd..5a84a69 100644 --- a/.github/workflows/build_win.yaml +++ b/.github/workflows/build_win.yaml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Configure and Build CMake for each OS + - name: Cmake and build run: | cmake -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -B build cmake --build build --config Release diff --git a/.github/workflows/deploy_windows.yaml b/.github/workflows/deploy_windows.yaml index 6ba84e0..e922882 100644 --- a/.github/workflows/deploy_windows.yaml +++ b/.github/workflows/deploy_windows.yaml @@ -1,102 +1,38 @@ -name: Compile and Release Qt5 Windows +name: Deploy windows on: push: tags: - '*' - pull_request: - branches: - - main - - -env: - QT_VERSION: "5.15.2" - MINGW_VERSION: "win64_mingw81" - MINGW_PATH: "mingw81_64" - BUILD_TYPE: Release jobs: build: - runs-on: windows-latest - defaults: - run: - shell: pwsh + runs-on: [self-hosted, Windows, X64] steps: - - uses: actions/checkout@v2 - - - name: Cache Qt and MinGW - uses: actions/cache@v2 - with: - path: | - ${{github.workspace}}/Qt - C:/tools/mingw64 - key: ${{ runner.os }}-qt-${{ env.QT_VERSION }}-mingw-${{ env.MINGW_VERSION }} - restore-keys: | - ${{ runner.os }}-qt-${{ env.QT_VERSION }}-mingw-${{ env.MINGW_VERSION }} - - - name: Install correct version of mingw - if: steps.cache-qt-mingw.outputs.cache-hit != 'true' - uses: crazy-max/ghaction-chocolatey@v1 - with: - args: install mingw --version=8.1.0 -y - - - name: Install Qt - if: steps.cache-qt-mingw.outputs.cache-hit != 'true' - uses: jurplel/install-qt-action@v2 - with: - host: windows - target: "desktop" - version: ${{ env.QT_VERSION }} - arch: ${{ env.MINGW_VERSION }} - dir: "${{github.workspace}}/Qt" - install-deps: "true" - - - name: Set Qt path and refresh - run: | - $path = "${{github.workspace}}/Qt/${{ env.QT_VERSION }}/${{ env.MINGW_PATH }}/bin" - echo "$path" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 - Update-SessionEnvironment - - - name: Configure CMake Project - working-directory: ${{github.workspace}} - run: | - $Qt5_DIR="${{github.workspace}}/Qt/${{ env.QT_VERSION }}/${{ env.MINGW_PATH }}/lib/cmake/Qt5" - cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DQt5_DIR=$Qt5_DIR -G "MinGW Makefiles" -B ${{github.workspace}}/build - - - name: Build project - working-directory: ${{github.workspace}}/build - run: | - mingw32-make.exe + - uses: actions/checkout@v3 - - name: Prepare portable program + - name: Configure and Build CMake for each OS run: | - mkdir ${{github.workspace}}/build/artifacts - windeployqt.exe ${{github.workspace}}/build/injectionClicker.exe --no-translations --dir ${{github.workspace}}/build/artifacts - Copy-Item ${{github.workspace}}/build/injectionClicker.exe ${{github.workspace}}/build/artifacts/ + cmake -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -B build + cmake --build build --config Release + shell: cmd - name: Upload build artifact uses: actions/upload-artifact@v2 with: - name: injectionClicker-${{ github.run_number }} - path: ${{github.workspace}}/build/artifacts/ - + name: InjectionClicker-${{ github.run_number }} + path: build/Release/ + - name: Zip the application run: | - Compress-Archive -Path ${{ github.workspace }}/build/artifacts/* -DestinationPath ${{ github.workspace }}/injectionClicker-${{ github.run_number }}.zip - - - name: List files in the workspace - run: | - Get-ChildItem -Path ${{ github.workspace }} -Force | Select-Object Mode, LastWriteTime, Length, Name | Format-Table -AutoSize - - - name: Create Release + Compress-Archive -Path build/Release/* -DestinationPath InjectionClicker-${{ github.run_number }}.zip + shell: powershell + + - name: Create and Upload Release uses: softprops/action-gh-release@v1 with: name: Release ${{ github.ref_name }} - body: Release of version ${{ github.ref_name }} - files: ./injectionClicker-${{ github.run_number }}.zip + files: ./InjectionClicker-${{ github.run_number }}.zip env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 7b32f4d31bde97dc2848a2c6a5757ae27af0920a Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 5 Jun 2024 19:16:00 +0200 Subject: [PATCH 31/34] Gave proper name for build action. --- .github/workflows/deploy_windows.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_windows.yaml b/.github/workflows/deploy_windows.yaml index e922882..11c576d 100644 --- a/.github/workflows/deploy_windows.yaml +++ b/.github/workflows/deploy_windows.yaml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Configure and Build CMake for each OS + - name: Cmake and build run: | cmake -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -B build cmake --build build --config Release From 111d314e69ea2ad04beee0fce3184d56691a26f2 Mon Sep 17 00:00:00 2001 From: napalys Date: Thu, 6 Jun 2024 14:11:07 +0200 Subject: [PATCH 32/34] Added updated version of read me how to run the bot. --- README.md | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 42afac5..531bc3a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,40 @@ -# Attachable-clicker -Attaches itself to a specific hwnd and sends to it clicks. (The current version works only on windows machines) +# Attachable Clicker -1. Find in task manager PID of window of interest -2. Add a title -3. Click on attach to the window - if the button turns on green and says success proceed else re-check added data -4. Select buttons, which one wants to invoke and add delays -5. Enjoy +**Attachable Clicker** is a sophisticated automation tool designed for Windows, allowing users to automate click inputs with precision. It can attach to any window using its Hardware ID (HWID), enabling automation without the need for the window to be in focus; it can even be minimized. -![GUI](https://user-images.githubusercontent.com/11835209/106388860-af4fd180-63e0-11eb-87b1-bece9058c308.png) +## Key Features + +- **Window Attachment:** Securely attaches to windows using their Hardware ID (HWID) for targeted and precise automation. +- **Customizable Click Patterns:** Allows users to record specific click patterns and replay them continuously or for predetermined durations. +- **Discord Integration:** Offers support and anomaly reporting directly via Discord. +- **Anomaly Detection:** Identifies and reports user-defined anomalies, ensuring reliable automation of click patterns. + +## Installation + +1. Download the latest release from [Release](https://github.com/Napalys/Attachable-clicker/releases). +2. Extract the files to your preferred location on your Windows device. +3. Run the executable file to launch the application. + +## Getting Started + +To begin using the Attachable Clicker, follow these setup steps: + +> **Note:** It is essential to run the application as an administrator to ensure full functionality. + +### Step 1: Identify the Window +- **Option 1:** Open the Task Manager, locate the Process ID (PID) of the window you wish to automate. +- **Option 2:** Use the "Select Window" feature to click on the window directly. (If you choose this option, skip Step 2.) + +### Step 2: Attach to the Window +- Input the window's title into Attachable Clicker. +- Click 'Attach to Window'. A green 'Success' message indicates a successful attachment. If not, verify your inputs. + +### Step 3: Configure Clicks +- Click 'Record' to log the desired click patterns you want to automate. + +### Step 4: Register Discord Bot (Optional) +- Register a discord bot using the provided bot token and channel ID. +- Add the anomalies you wish to monitor. + +### Step 5: Start Automation +- Activate the clicker to begin sending automated clicks to the attached window. From 2be88e7ba98601f541218b5b8c584a6b5eed47dd Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 7 Jun 2024 15:44:18 +0200 Subject: [PATCH 33/34] Added UI sample screenshot --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 531bc3a..5f7c353 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,6 @@ To begin using the Attachable Clicker, follow these setup steps: ### Step 5: Start Automation - Activate the clicker to begin sending automated clicks to the attached window. + +![image](https://github.com/Napalys/Attachable-clicker/assets/11835209/1b2827c2-eb99-4150-8825-957971953884) + From 71f6e0385fdb4f9faf7a9e23bdc2ac26dc68c1fd Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Sun, 9 Jun 2024 08:25:56 -0700 Subject: [PATCH 34/34] Added installation section from source in README. --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f7c353..ed7835f 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,60 @@ ## Installation -1. Download the latest release from [Release](https://github.com/Napalys/Attachable-clicker/releases). +1. Download the latest release from the [Release page](https://github.com/Napalys/Attachable-clicker/releases). 2. Extract the files to your preferred location on your Windows device. -3. Run the executable file to launch the application. +3. Run the executable file to launch the application. Please ensure the software is running with administrator privileges to enable it to send clicks. + +## Installation from Source + +### Windows + +Ensure you have Visual Studio Compiler installed on your Windows machine. If you haven't installed vcpkg yet, follow these steps: + +1. Clone the vcpkg repository: + ```sh + git clone https://github.com/microsoft/vcpkg.git + ``` + +2. Navigate to the vcpkg directory: + ```sh + cd vcpkg + ``` + +3. Bootstrap vcpkg: + ```sh + .\bootstrap-vcpkg.bat + ``` + +4. Install dependencies: + ```sh + vcpkg install opencv dpp qt5 + ``` + +To compile the project, follow these steps: + +1. Open a command prompt and navigate to the root directory of your project. +2. Run the following command to configure the project: + ```sh + cmake -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=path_to_your_vcpkg/scripts/buildsystems/vcpkg.cmake -B build + ``` + +3. Once the configuration is complete, build the project with: + ```sh + cmake --build build --config Release + ``` + +### Linux + +To get started, follow these steps: + +1. Get the Docker image from Docker Hub: [Attachable Clicker Docker Image](https://hub.docker.com/r/napalys/attachable-clicker-docker/tags) + +2. Clone the repository inside the Docker container and build the project: + ```sh + mkdir build && cd build && cmake .. && make + ``` + ## Getting Started