From eb494ae8faba765aba1f5053cb0f50671bac33ec Mon Sep 17 00:00:00 2001 From: PTyTb Date: Sun, 22 Feb 2026 09:18:00 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BB=20=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=B4=D0=B6=D0=B5=D1=80=20=D0=B4=D0=BE=D0=BD?= =?UTF-8?q?=D0=B0=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - добавление - удаление - сохранение --- TTW_Bot.pro | 2 + donationmanager.cpp | 175 ++++++++++++++++++++++++++++++++++ donationmanager.h | 54 +++++++++++ object_script.TTW_Bot.Debug | 2 + object_script.TTW_Bot.Release | 2 + udatabase.cpp | 93 ++++++++++++++++++ udatabase.h | 5 + ugeneral.cpp | 67 +++++++++++++ ugeneral.h | 9 ++ ugeneral.ui | 101 +++++++++++++++++--- 10 files changed, 499 insertions(+), 11 deletions(-) create mode 100644 donationmanager.cpp create mode 100644 donationmanager.h diff --git a/TTW_Bot.pro b/TTW_Bot.pro index b690e28..15b3315 100644 --- a/TTW_Bot.pro +++ b/TTW_Bot.pro @@ -24,6 +24,7 @@ SOURCES += \ actionmanager.cpp \ commandprocessor.cpp \ countermanager.cpp \ + donationmanager.cpp \ emoteprovider.cpp \ fcolorsetting.cpp \ fcreatechat.cpp \ @@ -59,6 +60,7 @@ HEADERS += \ badge.h \ commandprocessor.h \ countermanager.h \ + donationmanager.h \ emoteprovider.h \ fcolorsetting.h \ fcreatechat.h \ diff --git a/donationmanager.cpp b/donationmanager.cpp new file mode 100644 index 0000000..626bcb6 --- /dev/null +++ b/donationmanager.cpp @@ -0,0 +1,175 @@ +#include "donationmanager.h" +#include "udatabase.h" +#include +#include + +DonationManager::DonationManager(uDataBase *db, QObject *parent) + : QObject(parent) + , m_db(db) +{ +} + +bool DonationManager::loadFromDatabase() +{ + if (!m_db) return false; + // Предполагаем, что в uDataBase есть метод loadAllDonationTriggers() + m_triggers = m_db->loadAllDonationTriggers(); + return true; +} + +bool DonationManager::addTrigger(const QString &name, const QString &rule) +{ + if (name.isEmpty() || rule.isEmpty()) return false; + + DonationTrigger trig; + trig.name = name; + trig.rule = rule; + + if (!parseRule(rule, trig)) { + qWarning() << "Invalid rule:" << rule; + return false; + } + + // Сохраняем в БД (метод saveDonationTrigger должен вернуть id) + int newId = m_db->saveDonationTrigger(trig); + if (newId < 0) return false; + + trig.id = newId; + m_triggers.append(trig); + emit dataChanged(); + return true; +} + +bool DonationManager::deleteTrigger(int id) +{ + if (!m_db->deleteDonationTrigger(id)) return false; + + for (int i = 0; i < m_triggers.size(); ++i) { + if (m_triggers[i].id == id) { + m_triggers.removeAt(i); + break; + } + } + emit dataChanged(); + return true; +} + +QList DonationManager::getAllTriggers() const +{ + return m_triggers; +} + +QString DonationManager::matchDonation(double amount) const +{ + const DonationTrigger *best = nullptr; + int bestPriority = 999; // чем меньше, тем выше приоритет + + for (const DonationTrigger &t : m_triggers) { + bool ok = false; + switch (t.priority) { + case 1: // exact + if (qFuzzyCompare(amount, t.minValue)) + ok = true; + break; + case 2: // range + if (amount >= t.minValue && amount <= t.maxValue) + ok = true; + break; + case 3: // greater / greater-equal + if (t.isGreaterEqual) { + if (amount >= t.minValue) ok = true; + } else { + if (amount > t.minValue) ok = true; + } + break; + default: + continue; + } + + if (ok) { + // Сравниваем приоритет + if (t.priority < bestPriority) { + best = &t; + bestPriority = t.priority; + } + // Если приоритет одинаковый, применяем дополнительные правила: + else if (t.priority == bestPriority) { + if (bestPriority == 2) { + // Для диапазонов выбираем более узкий (меньше разница max-min) + double bestRange = best->maxValue - best->minValue; + double thisRange = t.maxValue - t.minValue; + if (thisRange < bestRange) { + best = &t; + } + } + else if (bestPriority == 3) { + // Для больше/больше-равно выбираем наибольшее пороговое значение (minValue) + if (t.minValue > best->minValue) { + best = &t; + } + } + // Для exact приоритет 1 – только одно значение, совпадение уже есть. + } + } + } + + return best ? best->name : QString(); +} + +bool DonationManager::parseRule(const QString &rule, DonationTrigger &t) const +{ + QString r = rule.trimmed(); + if (r.isEmpty()) return false; + + // Точное равенство: =123 + if (r.startsWith('=')) { + QString numStr = r.mid(1); + bool ok; + double val = numStr.toDouble(&ok); + if (!ok) return false; + t.priority = 1; + t.minValue = val; + t.maxValue = val; + return true; + } + + // Диапазон: 100-200 + if (r.contains('-')) { + QStringList parts = r.split('-', Qt::SkipEmptyParts); + if (parts.size() != 2) return false; + bool ok1, ok2; + double a = parts[0].toDouble(&ok1); + double b = parts[1].toDouble(&ok2); + if (!ok1 || !ok2 || a > b) return false; + t.priority = 2; + t.minValue = a; + t.maxValue = b; + return true; + } + + // Больше/больше-равно + if (r.startsWith(">=")) { + QString numStr = r.mid(2); + bool ok; + double val = numStr.toDouble(&ok); + if (!ok) return false; + t.priority = 3; + t.minValue = val; + t.maxValue = 0; + t.isGreaterEqual = true; + return true; + } + if (r.startsWith('>')) { + QString numStr = r.mid(1); + bool ok; + double val = numStr.toDouble(&ok); + if (!ok) return false; + t.priority = 3; + t.minValue = val; + t.maxValue = 0; + t.isGreaterEqual = false; + return true; + } + + return false; +} diff --git a/donationmanager.h b/donationmanager.h new file mode 100644 index 0000000..0b09784 --- /dev/null +++ b/donationmanager.h @@ -0,0 +1,54 @@ +#ifndef DONATIONMANAGER_H +#define DONATIONMANAGER_H + +#include +#include +#include + +class uDataBase; + +struct DonationTrigger +{ + int id = -1; + QString name; // Название триггера (например "Малый донат") + QString rule; // Строка правила: "=100", ">1000", "100-200" и т.д. + int priority = 0; // Вычисляется при парсинге (1 – exact, 2 – range, 3 – greater/ge) + double minValue = 0; // Для численных сравнений + double maxValue = 0; // Для диапазона (если range) + bool isGreaterEqual = false; // true для ">=" +}; + +class DonationManager : public QObject +{ + Q_OBJECT +public: + explicit DonationManager(uDataBase *db, QObject *parent = nullptr); + + // Загрузить все триггеры из БД + bool loadFromDatabase(); + + // Сохранить новый триггер в БД и добавить в список + bool addTrigger(const QString &name, const QString &rule); + + // Удалить триггер по ID (и из БД, и из списка) + bool deleteTrigger(int id); + + // Получить все триггеры (для отображения) + QList getAllTriggers() const; + + // Поиск подходящего триггера по сумме доната + // Возвращает имя триггера или пустую строку + QString matchDonation(double amount) const; + +signals: + void dataChanged(); // для обновления таблицы + +private: + // Парсинг строки правила, заполняет поля min, max, priority, isGreaterEqual + bool parseRule(const QString &rule, DonationTrigger &trigger) const; + + uDataBase *m_db; + QList m_triggers; +}; + +#endif // DONATIONMANAGER_H diff --git a/object_script.TTW_Bot.Debug b/object_script.TTW_Bot.Debug index 8817ed1..de875f8 100644 --- a/object_script.TTW_Bot.Debug +++ b/object_script.TTW_Bot.Debug @@ -1,6 +1,7 @@ debug/actionmanager.o debug/commandprocessor.o debug/countermanager.o +debug/donationmanager.o debug/emoteprovider.o debug/fcolorsetting.o debug/fcreatechat.o @@ -32,6 +33,7 @@ debug/websocketclient.o debug/moc_actionmanager.o debug/moc_commandprocessor.o debug/moc_countermanager.o +debug/moc_donationmanager.o debug/moc_emoteprovider.o debug/moc_fcolorsetting.o debug/moc_fcreatechat.o diff --git a/object_script.TTW_Bot.Release b/object_script.TTW_Bot.Release index 03b4f2b..1bf7052 100644 --- a/object_script.TTW_Bot.Release +++ b/object_script.TTW_Bot.Release @@ -1,6 +1,7 @@ release/actionmanager.o release/commandprocessor.o release/countermanager.o +release/donationmanager.o release/emoteprovider.o release/fcolorsetting.o release/fcreatechat.o @@ -32,6 +33,7 @@ release/websocketclient.o release/moc_actionmanager.o release/moc_commandprocessor.o release/moc_countermanager.o +release/moc_donationmanager.o release/moc_emoteprovider.o release/moc_fcolorsetting.o release/moc_fcreatechat.o diff --git a/udatabase.cpp b/udatabase.cpp index 25df3b2..351662f 100644 --- a/udatabase.cpp +++ b/udatabase.cpp @@ -1571,3 +1571,96 @@ bool uDataBase::clearActionsTable() query.exec("DELETE FROM sqlite_sequence WHERE name='actions'"); return true; } + + +bool uDataBase::createDonationTriggersTable() +{ + QSqlQuery query(m_db); + QString sql = + "CREATE TABLE IF NOT EXISTS donation_triggers (" + " id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT NOT NULL," + " rule TEXT NOT NULL," + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ")"; + if (!query.exec(sql)) { + m_lastError = query.lastError().text(); + qWarning() << "Failed to create donation_triggers table:" << m_lastError; + return false; + } + return true; +} + +int uDataBase::saveDonationTrigger(const DonationTrigger &trigger) +{ + if (!m_db.isOpen()) { + m_lastError = "Database is not open"; + return -1; + } + + if (!tableExists("donation_triggers")) { + if (!createDonationTriggersTable()) return -1; + } + + QSqlQuery query(m_db); + query.prepare("INSERT INTO donation_triggers (name, rule) VALUES (:name, :rule)"); + query.bindValue(":name", trigger.name); + query.bindValue(":rule", trigger.rule); + + if (!query.exec()) { + m_lastError = query.lastError().text(); + qWarning() << "Failed to save donation trigger:" << m_lastError; + return -1; + } + + return query.lastInsertId().toInt(); +} + +bool uDataBase::deleteDonationTrigger(int id) +{ + if (!m_db.isOpen()) { + m_lastError = "Database is not open"; + return false; + } + + QSqlQuery query(m_db); + query.prepare("DELETE FROM donation_triggers WHERE id = :id"); + query.bindValue(":id", id); + if (!query.exec()) { + m_lastError = query.lastError().text(); + qWarning() << "Failed to delete donation trigger:" << m_lastError; + return false; + } + return true; +} + +QList uDataBase::loadAllDonationTriggers() +{ + QList list; + if (!m_db.isOpen()) { + m_lastError = "Database is not open"; + return list; + } + + if (!tableExists("donation_triggers")) { + return list; // таблицы нет – пусто + } + + QSqlQuery query(m_db); + query.prepare("SELECT id, name, rule FROM donation_triggers ORDER BY id"); + if (!query.exec()) { + m_lastError = query.lastError().text(); + qWarning() << "Failed to load donation triggers:" << m_lastError; + return list; + } + + while (query.next()) { + DonationTrigger t; + t.id = query.value("id").toInt(); + t.name = query.value("name").toString(); + t.rule = query.value("rule").toString(); + // Парсить rule будем в DonationManager, здесь только храним + list.append(t); + } + return list; +} diff --git a/udatabase.h b/udatabase.h index 5554b3e..9bed1b6 100644 --- a/udatabase.h +++ b/udatabase.h @@ -1,6 +1,7 @@ #ifndef UDATABASE_H #define UDATABASE_H +#include "donationmanager.h" #include "qlistwidget.h" #include #include @@ -137,6 +138,10 @@ public: QList loadAllActions(); bool clearActionsTable(); + bool createDonationTriggersTable(); + int saveDonationTrigger(const DonationTrigger &trigger); + bool deleteDonationTrigger(int id); + QList loadAllDonationTriggers(); private: diff --git a/ugeneral.cpp b/ugeneral.cpp index 33d0004..46338e9 100644 --- a/ugeneral.cpp +++ b/ugeneral.cpp @@ -365,6 +365,14 @@ void uGeneral::setupTables() this, &uGeneral::onFilesGridDoubleClicked); connect(ui->widget_3->tableWidget(), &QTableWidget::cellDoubleClicked, this, &uGeneral::onNeiroGridDoubleClicked); + + ui->sgDotateTriggers->setColumnCount(2); + + headers.clear(); + headers << "Название" << "Правило"; + ui->sgDotateTriggers->setHorizontalHeaderLabels(headers); + ui->sgDotateTriggers->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->sgDotateTriggers->setSelectionMode(QAbstractItemView::SingleSelection); } void uGeneral::initializeManagers() @@ -468,6 +476,11 @@ void uGeneral::initializeManagers() // Загружаем действия из БД m_actionManager->loadFromDatabase(); updateActionsTable(); + + m_donationManager = new DonationManager(db, this); + connect(m_donationManager, &DonationManager::dataChanged, this, &uGeneral::updateDonationTriggersTable); + m_donationManager->loadFromDatabase(); + updateDonationTriggersTable(); } @@ -3719,3 +3732,57 @@ void uGeneral::clearActionInputs() ui->edtActionPic->clear(); ui->edtActionSound->clear(); } + +void uGeneral::updateDonationTriggersTable() +{ + ui->sgDotateTriggers->setRowCount(0); + auto triggers = m_donationManager->getAllTriggers(); + for (const auto &t : triggers) { + int row = ui->sgDotateTriggers->rowCount(); + ui->sgDotateTriggers->insertRow(row); + ui->sgDotateTriggers->setItem(row, 0, new QTableWidgetItem(t.name)); + ui->sgDotateTriggers->setItem(row, 1, new QTableWidgetItem(t.rule)); + // Сохраним id в UserRole первого столбца для удаления + ui->sgDotateTriggers->item(row, 0)->setData(Qt::UserRole, t.id); + } +} + +void uGeneral::on_btnDonateAdd_clicked() +{ + QString name = ui->lineEdit->text().trimmed(); // поле для имени + QString rule = ui->lineEdit_2->text().trimmed(); // поле для правила + if (name.isEmpty() || rule.isEmpty()) { + QMessageBox::warning(this, "Ошибка", "Заполните оба поля!"); + return; + } + + if (!m_donationManager->addTrigger(name, rule)) { + QMessageBox::critical(this, "Ошибка", "Не удалось добавить триггер. Проверьте формат правила."); + } else { + ui->lineEdit->clear(); + ui->lineEdit_2->clear(); + } +} + +void uGeneral::on_btnDonateDel_clicked() +{ + int row = ui->sgDotateTriggers->currentRow(); + if (row < 0) { + QMessageBox::warning(this, "Ошибка", "Выберите триггер для удаления!"); + return; + } + + int id = ui->sgDotateTriggers->item(row, 0)->data(Qt::UserRole).toInt(); + if (QMessageBox::question(this, "Подтверждение", "Удалить выбранный триггер?") == QMessageBox::Yes) { + m_donationManager->deleteTrigger(id); + } +} + +void uGeneral::on_sgDotateTriggers_cellDoubleClicked(int row, int column) +{ + Q_UNUSED(column); + QString name = ui->sgDotateTriggers->item(row, 0)->text(); + QString rule = ui->sgDotateTriggers->item(row, 1)->text(); + ui->lineEdit->setText(name); + ui->lineEdit_2->setText(rule); +} diff --git a/ugeneral.h b/ugeneral.h index bc35185..0f81b3e 100644 --- a/ugeneral.h +++ b/ugeneral.h @@ -382,6 +382,11 @@ private slots: void updateActionsTable(); void clearActionInputs(); + void on_btnDonateAdd_clicked(); + void on_btnDonateDel_clicked(); + void on_sgDotateTriggers_cellDoubleClicked(int row, int column); + void updateDonationTriggersTable(); + public slots: // Установка статуса подключения к Twitch void setTwitchConnected(bool connected); @@ -408,6 +413,8 @@ private: MediaFileManager *m_SoundFiles; MediaFileManager *m_TextFiles; NeuralTemplateManager *m_neuralTemplateManager; + DonationManager *m_donationManager; + QList m_timers; // Список таймеров int m_nextTimerId = 1; // Следующий ID таймера bool m_isTwitchConnected = false; // Статус подключения к Twitch @@ -523,6 +530,8 @@ private: void sendChatResponse(const QString &response); QString cleanMessageFromAllEmotes(const QString& message) const; + + }; #endif // UGENERAL_H diff --git a/ugeneral.ui b/ugeneral.ui index 8b2a123..978a829 100644 --- a/ugeneral.ui +++ b/ugeneral.ui @@ -7,7 +7,7 @@ 0 0 1002 - 985 + 838 @@ -1449,17 +1449,96 @@ 0 - - - Qt::Vertical + + + 0 - - - 20 - 40 - - - + + + + Донаты + + + + + + + + + 0 + + + + + Название + + + + + + + + + + Триггер + + + + + + + + + + + + 10 + + + + + Добавить + + + + + + + Удалить + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Связи + + + + + + + + +