3553 lines
122 KiB
C++
3553 lines
122 KiB
C++
#include "ugeneral.h"
|
||
#include "fcreatenotify.h"
|
||
#include "filemanager.h"
|
||
#include "logmanager.h"
|
||
#include "ttw_types.h"
|
||
#include "ui_ugeneral.h"
|
||
#include <QStandardPaths>
|
||
#include <QDesktopServices>
|
||
#include <QUrl>
|
||
#include <QDir>
|
||
#include <QFileDialog>
|
||
#include <QMessageBox>
|
||
#include <QStandardItem>
|
||
#include <QDateTime>
|
||
#include <QRandomGenerator>
|
||
#include "ulink.h"
|
||
#include "udatabase.h"
|
||
#include "soundmanager.h"
|
||
#include "userwidget.h"
|
||
#include "websocketclient.h"
|
||
#include "twitchmessage.h"
|
||
#include "user.h"
|
||
#include "user_manager.h"
|
||
#include "emoteprovider.h"
|
||
#include "fcreatechat.h"
|
||
#include <QInputDialog>
|
||
#include <QPair>
|
||
#include <QCache>
|
||
#include <QSettings>
|
||
#include "timerinfo.h"
|
||
#include "mediafilemanager.h"
|
||
|
||
uGeneral::uGeneral(QWidget *parent)
|
||
: QMainWindow(parent)
|
||
, ui(new Ui::uGeneral)
|
||
, m_authBot(nullptr)
|
||
, m_authStreamer(nullptr)
|
||
, m_authDA(nullptr)
|
||
, fLinkForm(nullptr)
|
||
, m_randomManager(nullptr)
|
||
, m_neuralTemplateManager(nullptr)
|
||
, m_isTwitchConnected(false)
|
||
, m_notificationServers()
|
||
, m_chatServers()
|
||
, m_createNotifyDialog(nullptr)
|
||
, m_createChatDialog(nullptr)
|
||
{
|
||
// ============================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ ИНТЕРФЕЙСА
|
||
// ============================================================================
|
||
ui->setupUi(this);
|
||
setWindowTitle("TTW Bot app");
|
||
|
||
LogSettings logSettings;
|
||
logSettings.logToFile = true;
|
||
logSettings.logToConsole = true;
|
||
|
||
// Цвета для разных уровней
|
||
logSettings.colors[LogLevel::Info] = Qt::darkGreen;
|
||
logSettings.colors[LogLevel::Warning] = Qt::darkYellow;
|
||
logSettings.colors[LogLevel::Error] = Qt::red;
|
||
logSettings.colors[LogLevel::Debug] = Qt::gray;
|
||
logSettings.colors[LogLevel::Critical] = Qt::darkRed;
|
||
|
||
LogManager::initialize(logSettings);
|
||
|
||
// Подключение сигналов к UI
|
||
LogManager* logger = LogManager::instance();
|
||
connect(logger, &LogManager::entryAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::logCleared,
|
||
this, &uGeneral::onLogCleared);
|
||
connect(logger, &LogManager::errorAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::warningAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::infoAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::debugAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::criticalAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
|
||
LogManager::instance()->info("uGeneral", "constructor", "Главное окно создано");
|
||
|
||
// Инициализируем структуру папок
|
||
FileManager::instance().initializeFolderStructure();
|
||
|
||
// Загружаем иконки
|
||
tabIcons = {
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "settings.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "ai.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "chat.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "skill.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "obs.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "notify.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "user.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "auto.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "log.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "add.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "edit.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "rm.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "rmfolder.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "open.png")),
|
||
};
|
||
|
||
for (int i = 0; i < ui->www->count() && i < tabIcons.size(); i++) {
|
||
ui->www->setTabIcon(i, tabIcons[i]);
|
||
}
|
||
|
||
ui->gbActionsAudio->setVisible(false);
|
||
ui->gbActionsNotify->setVisible(false);
|
||
|
||
setupButtonIcons();
|
||
|
||
// Загружаем QSS файлы при создании формы
|
||
loadQssFiles();
|
||
|
||
// ============================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ БАЗЫ ДАННЫХ
|
||
// ============================================================================
|
||
initializeDatabase();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА ИНТЕРФЕЙСА ПОЛЬЗОВАТЕЛЯ
|
||
// ============================================================================
|
||
setupUI();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА ТАБЛИЦ (с адаптивными параметрами)
|
||
// ============================================================================
|
||
setupTables();
|
||
|
||
// ============================================================================
|
||
// ЗАГРУЗКА НАСТРОЕК
|
||
// ============================================================================
|
||
loadSettings();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА TWITCH API И WEBSOCKET
|
||
// ============================================================================
|
||
setupTwitchComponents();
|
||
|
||
// ============================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ МЕНЕДЖЕРОВ
|
||
// ============================================================================
|
||
initializeManagers();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА ВИДЖЕТА ПОЛЬЗОВАТЕЛЕЙ
|
||
// ============================================================================
|
||
setupUserWidget();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА АВТОРИЗАЦИИ
|
||
// ============================================================================
|
||
setupAuthHandlers();
|
||
|
||
loadEmoties();
|
||
|
||
// Настройка таблицы веб-серверов
|
||
QStringList headers = {"Название", "Тип", "Порт", "Ссылка", "Статус"};
|
||
ui->sgWebServers->setColumnCount(headers.size());
|
||
ui->sgWebServers->setHorizontalHeaderLabels(headers);
|
||
ui->sgWebServers->horizontalHeader()->setStretchLastSection(true);
|
||
|
||
// Инициализируем окна создания
|
||
m_createNotifyDialog = new FCreateNotify(db, this);
|
||
m_createChatDialog = new FCreateChat(db, this);
|
||
|
||
initializeNotificationSounds();
|
||
}
|
||
|
||
// ============================================================================
|
||
// ПРИВАТНЫЕ МЕТОДЫ ДЛЯ ИНИЦИАЛИЗАЦИИ
|
||
// ============================================================================
|
||
|
||
void uGeneral::setupButtonIcons() {
|
||
QList<QPushButton*> buttons = ui->www->findChildren<QPushButton*>();
|
||
|
||
foreach (QPushButton *button, buttons) {
|
||
QString buttonName = button->objectName().toLower();
|
||
|
||
if (buttonName.contains("add")) {
|
||
button->setIcon(tabIcons[9]);
|
||
}
|
||
else if (buttonName.contains("del") || buttonName.contains("rm")) {
|
||
if (button->objectName().contains("RmGroup")) {
|
||
button->setIcon(tabIcons[12]);
|
||
} else {
|
||
button->setIcon(tabIcons[11]);
|
||
}
|
||
}
|
||
else if (buttonName.contains("edt") || (buttonName.contains("edit"))) {
|
||
button->setIcon(tabIcons[10]);
|
||
}
|
||
else if (buttonName.contains("open")) {
|
||
button->setIcon(tabIcons[13]);
|
||
}
|
||
|
||
if (!button->icon().isNull()) {
|
||
button->setIconSize(QSize(16, 16));
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::initializeDatabase()
|
||
{
|
||
QString roamingPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||
|
||
QDir dir(roamingPath);
|
||
if (!dir.exists()) {
|
||
dir.mkpath(".");
|
||
}
|
||
|
||
roamingPath += "/settings.db";
|
||
|
||
db = new uDataBase(roamingPath, this);
|
||
|
||
if (!db->isConnected()) {
|
||
LogManager::instance()->error("uGeneral", "Create", "Ошибка подключения к БД");
|
||
} else {
|
||
LogManager::instance()->info("uGeneral", "Create", "Успешное подключение к БД");
|
||
}
|
||
}
|
||
|
||
void uGeneral::setupUI()
|
||
{
|
||
// Настройка видимости элементов
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
ui->cbOllama->setVisible(false);
|
||
|
||
// Настройка режимов отображения паролей
|
||
QList<QLineEdit*> passwordFields = {
|
||
ui->edtDACode,
|
||
ui->edtBotToken,
|
||
ui->edtKandiKey,
|
||
ui->edtDAClientID,
|
||
ui->edtBotClientID,
|
||
ui->edtKandiSecret,
|
||
ui->edtDAClientSecret,
|
||
ui->edtBotTokenStreamer,
|
||
ui->edtAIP1,
|
||
ui->edtAIP2,
|
||
ui->edtAIP3
|
||
};
|
||
|
||
for (auto field : passwordFields) {
|
||
field->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
// Подключение фильтров логов
|
||
connect(ui->cbInfo, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
connect(ui->cbWarning, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
connect(ui->cbError, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
connect(ui->cbDebug, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
}
|
||
|
||
void uGeneral::setupTables()
|
||
{
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ ЛОГОВ (адаптивная)
|
||
// ========================================================================
|
||
QStringList headers;
|
||
headers.clear();
|
||
headers << "Дата" << "Тип" << "Модуль" << "Метод" << "Сообщение";
|
||
ui->sgLog->setHorizontalHeaderLabels(headers);
|
||
ui->sgLog->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgLog->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgLog->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
ui->sgLog->setWordWrap(true);
|
||
ui->sgLog->horizontalHeader()->setStretchLastSection(true);
|
||
ui->sgLog->setColumnWidth(0, 100);
|
||
ui->sgLog->setColumnWidth(1, 100);
|
||
ui->sgLog->setColumnWidth(2, 170);
|
||
ui->sgLog->setColumnWidth(3, 170);
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ КОМАНД (адаптивная)
|
||
// ========================================================================
|
||
headers.clear();
|
||
headers << "Команда" << "Ответ";
|
||
ui->sgCommands->setHorizontalHeaderLabels(headers);
|
||
ui->sgCommands->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgCommands->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgCommands->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
ui->sgCommands->setWordWrap(true);
|
||
ui->sgCommands->horizontalHeader()->setStretchLastSection(true);
|
||
ui->sgCommands->setColumnWidth(0, 135);
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ РАНДОМНЫХ ИНТЕРВАЛОВ (адаптивная)
|
||
// ========================================================================
|
||
headers.clear();
|
||
headers << "Имя" << "От" << "До";
|
||
ui->sgRandomInt->setHorizontalHeaderLabels(headers);
|
||
ui->sgRandomInt->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgRandomInt->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgRandomInt->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
ui->sgRandomInt->setWordWrap(true);
|
||
ui->sgRandomInt->horizontalHeader()->setStretchLastSection(true);
|
||
ui->sgRandomInt->setColumnWidth(0, 70);
|
||
ui->sgRandomInt->setColumnWidth(1, 30);
|
||
ui->sgRandomInt->setColumnWidth(2, 30);
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ ВЕБ СЕРВЕРОВ (адаптивная)
|
||
// ========================================================================
|
||
headers.clear();
|
||
headers << "Порт" << "Тип" << "Ссылка";
|
||
ui->sgWebServers->setColumnCount(headers.size());
|
||
ui->sgWebServers->setHorizontalHeaderLabels(headers);
|
||
ui->sgWebServers->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgWebServers->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgWebServers->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
ui->sgWebServers->setWordWrap(true);
|
||
ui->sgWebServers->horizontalHeader()->setStretchLastSection(true);
|
||
ui->sgWebServers->setColumnWidth(0, 80);
|
||
ui->sgWebServers->setColumnWidth(1, 100);
|
||
|
||
// Подключение двойного клика
|
||
connect(ui->sgWebServers, &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::on_sgWebServers_cellDoubleClicked);
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ ТАЙМЕРОВ (адаптивная)
|
||
// ========================================================================
|
||
ui->sgTimers->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgTimers->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgTimers->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
ui->sgTimers->setWordWrap(true);
|
||
ui->sgTimers->horizontalHeader()->setStretchLastSection(true);
|
||
ui->sgTimers->setColumnWidth(0, 40);
|
||
ui->sgTimers->setColumnWidth(1, 400);
|
||
ui->sgTimers->setColumnWidth(2, 80);
|
||
ui->sgTimers->setColumnWidth(3, 40);
|
||
|
||
// Подключение сигналов таблицы таймеров
|
||
connect(ui->sgTimers, &QTableWidget::cellClicked,
|
||
this, &uGeneral::on_sgTimers_cellClicked);
|
||
connect(ui->sgTimers, &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::on_sgTimers_cellDoubleClicked);
|
||
|
||
db->LoadTimers(ui->sgTimers, m_timers);
|
||
updateTimerTable();
|
||
|
||
// ========================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ СПЕЦИАЛЬНЫХ ВИДЖЕТОВ (адаптивные)
|
||
// ========================================================================
|
||
ui->widget->initForm("Звук", "sgSounds", true);
|
||
ui->widget_2->initForm("Файлы", "sgFiles", true);
|
||
ui->widget_3->initForm("Нейроконструктор", "sgNeiro");
|
||
|
||
// Принудительно устанавливаем Expanding политику для правильной работы в layout
|
||
ui->widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
ui->widget_2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
ui->widget_3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
|
||
// Подключение двойных кликов для специальных виджетов
|
||
connect(ui->widget->tableWidget(), &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::onSoundGridDoubleClicked);
|
||
connect(ui->widget_2->tableWidget(), &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::onFilesGridDoubleClicked);
|
||
connect(ui->widget_3->tableWidget(), &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::onNeiroGridDoubleClicked);
|
||
}
|
||
|
||
void uGeneral::initializeManagers()
|
||
{
|
||
soundManager = new SoundManager(this);
|
||
m_userManager = new UserManager(this);
|
||
m_randomManager = new RandomManager(this);
|
||
if (db) {
|
||
m_randomManager->initialize(db);
|
||
m_randomManager->loadFromDatabase();
|
||
}
|
||
|
||
m_randomResponses = new RandomResponses(this);
|
||
nnManager = new NeuralNetworkManager(this);
|
||
nnManager->setPrefix(ui->edtGPTPrefix->text());
|
||
if (ui->rbGC->isChecked()) {
|
||
nnManager->setCurrentNetworkType(NeuralNetworkManager::GigaChat);
|
||
nnManager->setGigaChatCredentials(ui->edtAIP1->text(), ui->edtAIP2->text());
|
||
}
|
||
else if (ui->rbDS->isChecked()) {
|
||
nnManager->setCurrentNetworkType(NeuralNetworkManager::DeepSeek);
|
||
nnManager->setApiKey(NeuralNetworkManager::DeepSeek, ui->edtAIP1->text());
|
||
}
|
||
else if (ui->rbCG->isChecked()) {
|
||
nnManager->setCurrentNetworkType(NeuralNetworkManager::ChatGPT);
|
||
nnManager->setApiKey(NeuralNetworkManager::ChatGPT, ui->edtAIP1->text());
|
||
}
|
||
else if (ui->RBCustom->isChecked()) {
|
||
nnManager->setCurrentNetworkType(NeuralNetworkManager::Ollama);
|
||
nnManager->setApiKey(NeuralNetworkManager::Ollama, ui->edtAIP1->text());
|
||
nnManager->setOllamaUrl(ui->edtAIP2->text());
|
||
}
|
||
|
||
m_SoundFiles = new MediaFileManager();
|
||
ui->widget->setSoundManager(m_SoundFiles);
|
||
ui->widget->setManagerType(FSingleGrid::SoundManager);
|
||
ui->widget->setDatabase(db);
|
||
m_TextFiles = new MediaFileManager();
|
||
ui->widget_2->setTextManager(m_TextFiles);
|
||
ui->widget_2->setManagerType(FSingleGrid::TextManager);
|
||
ui->widget_2->setDatabase(db);
|
||
m_neuralTemplateManager = new NeuralTemplateManager(this);
|
||
ui->widget_3->setTemplateManager(m_neuralTemplateManager);
|
||
ui->widget_3->setManagerType(FSingleGrid::TemplateManager);
|
||
ui->widget_3->setDatabase(db);
|
||
|
||
m_counterManager = new CounterManager(this);
|
||
if (db) {
|
||
m_counterManager->initialize(db);
|
||
}
|
||
|
||
// Настраиваем таблицу счётчиков (предполагаем, что она уже есть в .ui с objectName "sgCounters")
|
||
setupCountersTable();
|
||
|
||
// Подключаем сигналы для обновления таблицы при изменениях
|
||
connect(m_counterManager, &CounterManager::dataChanged, this, &uGeneral::updateCountersTable);
|
||
connect(m_counterManager, &CounterManager::counterAdded, this, [this](const QString&, int) { updateCountersTable(); });
|
||
connect(m_counterManager, &CounterManager::counterRemoved, this, [this](const QString&) { updateCountersTable(); });
|
||
connect(m_counterManager, &CounterManager::counterUpdated, this, [this](const QString&, const QString&) { updateCountersTable(); });
|
||
connect(m_counterManager, &CounterManager::counterIncremented, this, [this](const QString&, int) { updateCountersTable(); });
|
||
|
||
|
||
m_commandProcessor = new CommandProcessor(this);
|
||
|
||
if (db) {
|
||
loadCommandsFromTableWidget();
|
||
loadRandomRangesFromTableWidget();
|
||
loadRandomResponseGroupsFromDatabase();
|
||
loadSoundsFromDatabase();
|
||
loadTextFromDatabase();
|
||
loadTemplatesFromDatabase();
|
||
|
||
CommandProcessor::Context context;
|
||
context.userManager = m_userManager;
|
||
context.twitchAPI = twitchAPI;
|
||
context.soundManager = soundManager;
|
||
context.neuralManager = nnManager;
|
||
context.randomManager = m_randomManager;
|
||
context.neuralTemplateManager = m_neuralTemplateManager;
|
||
context.randomResponses = m_randomResponses;
|
||
context.mediaFileManager = m_SoundFiles;
|
||
context.channel = ui->edtChannel->text();
|
||
context.notifyVolume = ui->tbNotifyVolume->value();
|
||
context.counterManager = m_counterManager;
|
||
m_commandProcessor->setContext(context);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
void uGeneral::setupCountersTable(){
|
||
// Предполагаем, что sgCounters уже есть в ui
|
||
ui->sgCounters->setColumnCount(2);
|
||
QStringList headers;
|
||
headers << "Счётчик" << "Значение";
|
||
ui->sgCounters->setHorizontalHeaderLabels(headers);
|
||
ui->sgCounters->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgCounters->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgCounters->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
ui->sgCounters->horizontalHeader()->setStretchLastSection(true);
|
||
ui->sgCounters->setColumnWidth(0, 200);
|
||
ui->sgCounters->setColumnWidth(1, 80);
|
||
|
||
updateCountersTable();
|
||
}
|
||
|
||
void uGeneral::updateCountersTable()
|
||
{
|
||
|
||
ui->sgCounters->setRowCount(0);
|
||
ui->cbCounters->clear();
|
||
if (!m_counterManager) return;
|
||
|
||
QVector<CounterManager::Counter> counters = m_counterManager->getAllCounters();
|
||
for (const auto &c : counters) {
|
||
int row = ui->sgCounters->rowCount();
|
||
ui->sgCounters->insertRow(row);
|
||
ui->sgCounters->setItem(row, 0, new QTableWidgetItem(c.name));
|
||
ui->sgCounters->setItem(row, 1, new QTableWidgetItem(QString::number(c.count)));
|
||
ui->cbCounters->addItem(c.name);
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadCommandsFromTableWidget()
|
||
{
|
||
if (!db || !m_commandProcessor) return;
|
||
|
||
QTableWidget* table = ui->sgCommands;
|
||
if (!table) return;
|
||
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *commandItem = table->item(row, 0);
|
||
QTableWidgetItem *responseItem = table->item(row, 1);
|
||
|
||
if (commandItem && responseItem) {
|
||
QString command = commandItem->text().trimmed();
|
||
QString response = responseItem->text();
|
||
|
||
if (!command.isEmpty() && !response.isEmpty()) {
|
||
m_commandProcessor->addCommand(command, response);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadSoundsFromDatabase()
|
||
{
|
||
if (!db || !m_SoundFiles) return;
|
||
|
||
QTableWidget* table = ui->widget->tableWidget();
|
||
if (!table) return;
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *fileItem = table->item(row, 1);
|
||
if (nameItem && fileItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
QString file = fileItem->text().trimmed();
|
||
if (!name.isEmpty() && !file.isEmpty()) {
|
||
m_SoundFiles->addFile(name, file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadTextFromDatabase()
|
||
{
|
||
if (!db || !m_TextFiles) return;
|
||
|
||
QTableWidget* table = ui->widget_2->tableWidget();
|
||
if (!table) return;
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *fileItem = table->item(row, 1);
|
||
if (nameItem && fileItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
QString file = fileItem->text().trimmed();
|
||
if (!name.isEmpty() && !file.isEmpty()) {
|
||
m_TextFiles->addFile(name, file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadTemplatesFromDatabase()
|
||
{
|
||
if (!db || !m_neuralTemplateManager) return;
|
||
|
||
QTableWidget* table = ui->widget_3->tableWidget();
|
||
if (!table) return;
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *fileItem = table->item(row, 1);
|
||
if (nameItem && fileItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
QString file = fileItem->text().trimmed();
|
||
if (!name.isEmpty() && !file.isEmpty()) {
|
||
m_neuralTemplateManager->addTemplate(name, file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadRandomRangesFromTableWidget()
|
||
{
|
||
if (!db || !m_randomManager) return;
|
||
|
||
QTableWidget* table = ui->sgRandomInt;
|
||
if (!table) return;
|
||
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *startItem = table->item(row, 1);
|
||
QTableWidgetItem *endItem = table->item(row, 2);
|
||
|
||
if (nameItem && startItem && endItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
int start = startItem->text().toInt();
|
||
int end = endItem->text().toInt();
|
||
|
||
if (!name.isEmpty() && start <= end) {
|
||
m_randomManager->addRange(name, start, end);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadRandomResponseGroupsFromDatabase()
|
||
{
|
||
if (!db || !m_randomResponses) return;
|
||
|
||
QListWidget tempGroupList;
|
||
if (!db->LoadRandomGroups(&tempGroupList)) {
|
||
qWarning() << "Failed to load random groups from database";
|
||
return;
|
||
}
|
||
|
||
for (int i = 0; i < tempGroupList.count(); ++i) {
|
||
QString groupName = tempGroupList.item(i)->text().trimmed();
|
||
if (groupName.isEmpty()) continue;
|
||
|
||
QListWidget tempResponseList;
|
||
if (!db->LoadRandomResponses(groupName, &tempResponseList)) {
|
||
qWarning() << "Failed to load responses for group:" << groupName;
|
||
continue;
|
||
}
|
||
|
||
for (int j = 0; j < tempResponseList.count(); ++j) {
|
||
QString response = tempResponseList.item(j)->text();
|
||
if (!response.isEmpty()) {
|
||
m_randomResponses->addResponse(groupName, response);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::setupTwitchComponents()
|
||
{
|
||
m_twitchClient = new WebSocketClient(this);
|
||
|
||
connect(m_twitchClient, &WebSocketClient::onNewMessage,
|
||
this, &uGeneral::handleNewMessage);
|
||
connect(m_twitchClient, &WebSocketClient::onConnected,
|
||
this, &uGeneral::handleConnected);
|
||
connect(m_twitchClient, &WebSocketClient::onDisconnected,
|
||
this, &uGeneral::handleDisconnected);
|
||
connect(m_twitchClient, &WebSocketClient::onError,
|
||
this, &uGeneral::handleError);
|
||
|
||
twitchAPI = new TTwAPI(this);
|
||
|
||
connect(twitchAPI, &TTwAPI::apiError, this, &uGeneral::onApiError);
|
||
connect(twitchAPI, &TTwAPI::tokenExpired, this, &uGeneral::onTokenExpired);
|
||
connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit);
|
||
|
||
initTwitchAPI();
|
||
}
|
||
|
||
void uGeneral::setupUserWidget()
|
||
{
|
||
m_userWidget = new UserWidget(m_userManager, this);
|
||
|
||
QVBoxLayout* layout = qobject_cast<QVBoxLayout*>(ui->tab_7->layout());
|
||
if (!layout) {
|
||
layout = new QVBoxLayout(ui->tab_7);
|
||
ui->tab_7->setLayout(layout);
|
||
}
|
||
layout->setContentsMargins(0, 0, 0, 0);
|
||
layout->addWidget(m_userWidget);
|
||
|
||
connect(m_userWidget, &UserWidget::banUserRequested, this, [this](const QString &userId, const QString &userName) {
|
||
if (twitchAPI) {
|
||
twitchAPI->banUser(userId);
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::timeoutUserRequested, this, [this](const QString &userId, const QString &userName, int minutes) {
|
||
if (twitchAPI) {
|
||
twitchAPI->banUserTime(userId, minutes);
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::unbanUserRequested, this, [this](const QString &userId, const QString &userName) {
|
||
if (twitchAPI) {
|
||
twitchAPI->unbanUser(userId);
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::setModeratorRequested, this, [this](const QString &userId, const QString &userName) {
|
||
if (twitchAPI) {
|
||
twitchAPI->setModerator(userId);
|
||
User* user = m_userManager->findUserById(userId);
|
||
if (user) {
|
||
user->isModerator = true;
|
||
m_userManager->updateUserFromMessage(user->displayName, TwitchMessage());
|
||
}
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::removeModeratorRequested, this, [this](const QString &userId, const QString &userName) {
|
||
if (twitchAPI) {
|
||
twitchAPI->delModerator(userId);
|
||
User* user = m_userManager->findUserById(userId);
|
||
if (user) {
|
||
user->isModerator = false;
|
||
m_userManager->updateUserFromMessage(user->displayName, TwitchMessage());
|
||
}
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::setVIPRequested, this, [this](const QString &userId, const QString &userName) {
|
||
if (twitchAPI) {
|
||
twitchAPI->setVIP(userId);
|
||
User* user = m_userManager->findUserById(userId);
|
||
if (user) {
|
||
user->isVIP = true;
|
||
m_userManager->updateUserFromMessage(user->displayName, TwitchMessage());
|
||
}
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::removeVIPRequested, this, [this](const QString &userId, const QString &userName) {
|
||
if (twitchAPI) {
|
||
twitchAPI->delVIP(userId);
|
||
User* user = m_userManager->findUserById(userId);
|
||
if (user) {
|
||
user->isVIP = false;
|
||
m_userManager->updateUserFromMessage(user->displayName, TwitchMessage());
|
||
}
|
||
}
|
||
});
|
||
|
||
connect(m_userWidget, &UserWidget::userInfoRequested, this, [this](const QString &userId, const QString &userName) {
|
||
Q_UNUSED(userName);
|
||
User* user = m_userManager->findUserById(userId);
|
||
if (user) {
|
||
QString info = QString(
|
||
"Информация о пользователе:\n"
|
||
"Имя: %1\n"
|
||
"ID: %2\n"
|
||
"Логин: %3\n"
|
||
"Сообщений: %4\n"
|
||
"Модератор: %5\n"
|
||
"VIP: %6\n"
|
||
"Подписчик: %7\n"
|
||
"Последняя активность: %8"
|
||
).arg(user->displayName)
|
||
.arg(user->id)
|
||
.arg(user->login)
|
||
.arg(user->messageCount)
|
||
.arg(user->isModerator ? "Да" : "Нет")
|
||
.arg(user->isVIP ? "Да" : "Нет")
|
||
.arg(user->isSubscriber ? "Да" : "Нет")
|
||
.arg(user->lastMessageTime.isValid() ?
|
||
user->lastMessageTime.toString("dd.MM.yyyy hh:mm:ss") : "Неизвестно");
|
||
|
||
QMessageBox::information(this, "Информация о пользователе", info);
|
||
}
|
||
});
|
||
}
|
||
|
||
void uGeneral::setupAuthHandlers()
|
||
{
|
||
m_authBot = new TAuth(this);
|
||
connect(m_authBot, &TAuth::tokenReceived, this, &uGeneral::onTokenReceived);
|
||
connect(m_authBot, &TAuth::errorOccurred, this, &uGeneral::onAuthError);
|
||
connect(m_authBot, &TAuth::serverStarted, this, [this](int port) {
|
||
LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен");
|
||
});
|
||
|
||
m_authStreamer = new TAuth(this);
|
||
connect(m_authStreamer, &TAuth::tokenReceived, this, &uGeneral::onTokenReceived2);
|
||
connect(m_authStreamer, &TAuth::errorOccurred, this, &uGeneral::onAuthError);
|
||
connect(m_authStreamer, &TAuth::serverStarted, this, [this](int port) {
|
||
LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен");
|
||
});
|
||
|
||
m_authDA = new TAuth(this);
|
||
connect(m_authDA, &TAuth::codeReceived, this, &uGeneral::onTokenReceived3);
|
||
connect(m_authDA, &TAuth::errorOccurred, this, &uGeneral::onAuthError);
|
||
connect(m_authDA, &TAuth::serverStarted, this, [this](int port) {
|
||
LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен");
|
||
});
|
||
}
|
||
|
||
uGeneral::~uGeneral()
|
||
{
|
||
for (HttpServer *server : m_notificationServers) {
|
||
server->stop();
|
||
delete server;
|
||
}
|
||
m_notificationServers.clear();
|
||
|
||
for (HttpServerChat *server : m_chatServers) {
|
||
server->stop();
|
||
delete server;
|
||
}
|
||
m_chatServers.clear();
|
||
|
||
if (m_commandProcessor) {
|
||
delete m_commandProcessor;
|
||
m_commandProcessor = nullptr;
|
||
}
|
||
|
||
if (m_randomManager) {
|
||
delete m_randomManager;
|
||
m_randomManager = nullptr;
|
||
}
|
||
|
||
if (m_neuralTemplateManager) {
|
||
delete m_neuralTemplateManager;
|
||
m_neuralTemplateManager = nullptr;
|
||
}
|
||
delete m_counterManager;
|
||
delete m_createNotifyDialog;
|
||
delete m_createChatDialog;
|
||
delete ui;
|
||
delete m_userManager;
|
||
delete fLinkForm;
|
||
delete m_twitchClient;
|
||
delete m_authBot;
|
||
delete m_authStreamer;
|
||
delete m_authDA;
|
||
}
|
||
|
||
// ============================================================================
|
||
// ОСТАЛЬНЫЕ МЕТОДЫ (без изменений, кроме адаптивных настроек выше)
|
||
// ============================================================================
|
||
|
||
void uGeneral::setTwitchConnected(bool connected)
|
||
{
|
||
m_isTwitchConnected = connected;
|
||
|
||
if (connected) {
|
||
for (TimerInfo& timer : m_timers) {
|
||
if (timer.isActive && !timer.timer) {
|
||
startTimer(timer);
|
||
}
|
||
}
|
||
} else {
|
||
for (TimerInfo& timer : m_timers) {
|
||
if (timer.timer) {
|
||
stopTimer(timer);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadEmoties()
|
||
{
|
||
bttvProvider.fetchGlobal();
|
||
sevenTVProvider.fetchGlobal();
|
||
User user = twitchAPI->getUserByLogin(ui->edtChannel->text());
|
||
bttvProvider.fetchCustom(user.id);
|
||
sevenTVProvider.fetchCustom(user.id);
|
||
}
|
||
|
||
void uGeneral::loadChatBadges()
|
||
{
|
||
if (!twitchAPI) return;
|
||
|
||
m_chatBadges.clear();
|
||
|
||
QVector<ChatBadge> globalBadges;
|
||
twitchAPI->getGlobalChatBadges(globalBadges);
|
||
m_chatBadges.append(globalBadges);
|
||
|
||
QVector<ChatBadge> customBadges;
|
||
twitchAPI->getCustomChatBadges(customBadges);
|
||
m_chatBadges.append(customBadges);
|
||
}
|
||
|
||
void uGeneral::updateTimerTable() {
|
||
ui->sgTimers->setRowCount(0);
|
||
|
||
for (const TimerInfo& timer : m_timers) {
|
||
int row = ui->sgTimers->rowCount();
|
||
ui->sgTimers->insertRow(row);
|
||
|
||
QTableWidgetItem* enabledItem = new QTableWidgetItem();
|
||
enabledItem->setCheckState(timer.isActive ? Qt::Checked : Qt::Unchecked);
|
||
ui->sgTimers->setItem(row, 0, enabledItem);
|
||
|
||
QTableWidgetItem* messageItem = new QTableWidgetItem(timer.message);
|
||
ui->sgTimers->setItem(row, 1, messageItem);
|
||
|
||
QTableWidgetItem* intervalItem = new QTableWidgetItem(QString::number(timer.interval));
|
||
ui->sgTimers->setItem(row, 2, intervalItem);
|
||
|
||
QTableWidgetItem* announcementItem = new QTableWidgetItem();
|
||
announcementItem->setCheckState(timer.isAnnouncement ? Qt::Checked : Qt::Unchecked);
|
||
ui->sgTimers->setItem(row, 3, announcementItem);
|
||
}
|
||
}
|
||
|
||
void uGeneral::startTimer(TimerInfo& timerInfo) {
|
||
if (!m_isTwitchConnected) {
|
||
return;
|
||
}
|
||
|
||
if (timerInfo.timer) {
|
||
timerInfo.timer->stop();
|
||
delete timerInfo.timer;
|
||
}
|
||
|
||
timerInfo.timer = new QTimer(this);
|
||
connect(timerInfo.timer, &QTimer::timeout, this, [this, timerInfo]() {
|
||
sendTimedMessage(timerInfo);
|
||
});
|
||
|
||
timerInfo.timer->start(timerInfo.interval * 60000);
|
||
}
|
||
|
||
void uGeneral::stopTimer(TimerInfo& timerInfo) {
|
||
if (timerInfo.timer) {
|
||
timerInfo.timer->stop();
|
||
delete timerInfo.timer;
|
||
timerInfo.timer = nullptr;
|
||
}
|
||
}
|
||
|
||
void uGeneral::sendTimedMessage(const TimerInfo& timerInfo) {
|
||
if (!m_isTwitchConnected) {
|
||
return;
|
||
}
|
||
|
||
if (timerInfo.isAnnouncement) {
|
||
if (twitchAPI) {
|
||
twitchAPI->sendAnnouncement(timerInfo.message);
|
||
}
|
||
} else {
|
||
if (m_twitchClient) {
|
||
m_twitchClient->sendChatMessage(ui->edtChannel->text(), timerInfo.message);
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_btnTimerAdd_clicked()
|
||
{
|
||
QString message = ui->edtTimerMessage->text().trimmed();
|
||
QString intervalStr = ui->edtTimerInterval->text().trimmed();
|
||
|
||
if (message.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите сообщение!");
|
||
return;
|
||
}
|
||
|
||
bool ok;
|
||
int interval = intervalStr.toInt(&ok);
|
||
if (!ok || interval <= 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите корректный интервал в минутах!");
|
||
return;
|
||
}
|
||
|
||
TimerInfo newTimer;
|
||
newTimer.id = m_nextTimerId++;
|
||
newTimer.message = message;
|
||
newTimer.interval = interval;
|
||
newTimer.isActive = true;
|
||
newTimer.isAnnouncement = false;
|
||
newTimer.timer = nullptr;
|
||
|
||
m_timers.append(newTimer);
|
||
|
||
if (m_isTwitchConnected) {
|
||
startTimer(m_timers.last());
|
||
}
|
||
|
||
updateTimerTable();
|
||
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
ui->edtTimerMessage->clear();
|
||
ui->edtTimerInterval->setText("10");
|
||
}
|
||
|
||
void uGeneral::on_btnTimerEdit_clicked() {
|
||
int currentRow = ui->sgTimers->currentRow();
|
||
if (currentRow < 0 || currentRow >= m_timers.size()) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите таймер для редактирования!");
|
||
return;
|
||
}
|
||
|
||
QString message = ui->edtTimerMessage->text().trimmed();
|
||
QString intervalStr = ui->edtTimerInterval->text().trimmed();
|
||
|
||
if (message.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите сообщение!");
|
||
return;
|
||
}
|
||
|
||
bool ok;
|
||
int interval = intervalStr.toInt(&ok);
|
||
if (!ok || interval <= 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите корректный интервал в минутах!");
|
||
return;
|
||
}
|
||
|
||
TimerInfo& timer = m_timers[currentRow];
|
||
bool wasActive = timer.isActive;
|
||
|
||
if (wasActive) {
|
||
stopTimer(timer);
|
||
}
|
||
|
||
timer.message = message;
|
||
timer.interval = interval;
|
||
|
||
if (wasActive && m_isTwitchConnected) {
|
||
startTimer(timer);
|
||
}
|
||
|
||
updateTimerTable();
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
|
||
void uGeneral::on_btnTimerDelete_clicked() {
|
||
int currentRow = ui->sgTimers->currentRow();
|
||
if (currentRow < 0 || currentRow >= m_timers.size()) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите таймер для удаления!");
|
||
return;
|
||
}
|
||
|
||
TimerInfo timer = m_timers[currentRow];
|
||
stopTimer(timer);
|
||
|
||
m_timers.removeAt(currentRow);
|
||
updateTimerTable();
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
|
||
void uGeneral::on_btnTimerTest_clicked() {
|
||
int currentRow = ui->sgTimers->currentRow();
|
||
if (currentRow < 0 || currentRow >= m_timers.size()) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите таймер для теста!");
|
||
return;
|
||
}
|
||
|
||
const TimerInfo& timer = m_timers[currentRow];
|
||
sendTimedMessage(timer);
|
||
}
|
||
|
||
void uGeneral::on_sgTimers_cellClicked(int row, int column)
|
||
{
|
||
if (row < 0 || row >= m_timers.size()) return;
|
||
|
||
TimerInfo& timer = m_timers[row];
|
||
|
||
if (column == 0) {
|
||
bool newState = ui->sgTimers->item(row, column)->checkState() == Qt::Checked;
|
||
|
||
if (newState != timer.isActive) {
|
||
m_timers[row].isActive = newState;
|
||
|
||
if (newState) {
|
||
if (m_isTwitchConnected) {
|
||
startTimer(m_timers[row]);
|
||
}
|
||
} else {
|
||
stopTimer(m_timers[row]);
|
||
}
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
}
|
||
else if (column == 3) {
|
||
bool newState = ui->sgTimers->item(row, column)->checkState() == Qt::Checked;
|
||
|
||
if (newState != timer.isAnnouncement) {
|
||
m_timers[row].isAnnouncement = newState;
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::onTimerTimeout(int timerId)
|
||
{
|
||
Q_UNUSED(timerId);
|
||
}
|
||
|
||
void uGeneral::on_sgTimers_cellDoubleClicked(int row, int column) {
|
||
Q_UNUSED(column);
|
||
if (row < 0 || row >= m_timers.size()) return;
|
||
|
||
const TimerInfo& timer = m_timers[row];
|
||
ui->edtTimerMessage->setText(timer.message);
|
||
ui->edtTimerInterval->setText(QString::number(timer.interval));
|
||
}
|
||
|
||
void uGeneral::sendRequestToAI(const QString &q)
|
||
{
|
||
NeuralNetworkManager::NetworkType type;
|
||
if (ui->rbGC->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::GigaChat;
|
||
nnManager->setGigaChatCredentials(
|
||
ui->edtAIP1->text(),
|
||
ui->edtAIP2->text()
|
||
);
|
||
}
|
||
if (ui->rbCG->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::ChatGPT;
|
||
}
|
||
if (ui->rbDS->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::DeepSeek;
|
||
}
|
||
if (ui->RBCustom->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::Ollama;
|
||
}
|
||
nnManager->setApiKey(type, ui->edtAIP1->text());
|
||
nnManager->setPrefix(ui->edtGPTPrefix->text());
|
||
nnManager->setOllamaUrl(ui->edtAIP2->text());
|
||
|
||
nnManager->sendMessage(q, type);
|
||
}
|
||
|
||
void uGeneral::onSoundGridDoubleClicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("||" + ui->widget->tableWidget()->item(ui->widget->tableWidget()->currentRow(), 0)->text() + "||");
|
||
}
|
||
|
||
void uGeneral::onFilesGridDoubleClicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("|(" + ui->widget_2->tableWidget()->item(ui->widget_2->tableWidget()->currentRow(), 0)->text() + "|(");
|
||
}
|
||
|
||
void uGeneral::onNeiroGridDoubleClicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("<|" + ui->widget_3->tableWidget()->item(ui->widget_3->tableWidget()->currentRow(), 0)->text() + "<|");
|
||
}
|
||
|
||
void uGeneral::initTwitchAPI()
|
||
{
|
||
QString botClientID = ui->edtBotClientID->text();
|
||
QString botToken = ui->edtBotToken->text();
|
||
QString botTokenStreamer = ui->edtBotTokenStreamer->text();
|
||
QString channel = ui->edtChannel->text();
|
||
QString botName = ui->edtBotName->text();
|
||
|
||
if (botClientID.isEmpty() || botToken.isEmpty() ||
|
||
botTokenStreamer.isEmpty() || channel.isEmpty() || botName.isEmpty()) {
|
||
LogManager::instance()->warning("uGeneral", "initTwitchAPI",
|
||
"Не все данные для инициализации TTwAPI заполнены. API не инициализировано.");
|
||
return;
|
||
}
|
||
|
||
if (twitchAPI) {
|
||
delete twitchAPI;
|
||
}
|
||
|
||
twitchAPI = new TTwAPI(this);
|
||
|
||
connect(twitchAPI, &TTwAPI::apiError, this, &uGeneral::onApiError);
|
||
connect(twitchAPI, &TTwAPI::tokenExpired, this, &uGeneral::onTokenExpired);
|
||
connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit);
|
||
|
||
twitchAPI->init(
|
||
botClientID,
|
||
botToken,
|
||
botTokenStreamer,
|
||
channel,
|
||
botName
|
||
);
|
||
|
||
LogManager::instance()->info("uGeneral", "initTwitchAPI", "TTwAPI успешно инициализировано");
|
||
}
|
||
|
||
void uGeneral::onApiError(const QString &method, const QString &error)
|
||
{
|
||
LogManager::instance()->error("ttw_api", method, error);
|
||
}
|
||
|
||
void uGeneral::onTokenExpired(const QString &error)
|
||
{
|
||
LogManager::instance()->error("uGeneral", "onTokenExpired", error);
|
||
}
|
||
|
||
void uGeneral::onRateLimit()
|
||
{
|
||
LogManager::instance()->warning("uGeneral", "onRateLimit", "Превышен лимит запросов");
|
||
}
|
||
|
||
UserManager* uGeneral::getUserManager()
|
||
{
|
||
return m_userManager;
|
||
}
|
||
|
||
void uGeneral::onAIResponseReceived(const QString &response)
|
||
{
|
||
if (m_twitchClient) {
|
||
QString chatResponse = response;
|
||
if (chatResponse.length() > 400) {
|
||
chatResponse = chatResponse.left(397) + "...";
|
||
}
|
||
m_twitchClient->sendChatMessage(ui->edtChannel->text(), chatResponse);
|
||
}
|
||
}
|
||
|
||
void uGeneral::onAIErrorOccurred(const QString &error)
|
||
{
|
||
LogManager::instance()->error("General", "onAIErrorOccurred", error);
|
||
|
||
if (m_twitchClient) {
|
||
m_twitchClient->sendChatMessage(ui->edtChannel->text(),
|
||
"Извините, нейросеть временно недоступна");
|
||
}
|
||
}
|
||
|
||
void uGeneral::handleConnected()
|
||
{
|
||
LogManager::instance()->info("General", "handleConnected", "Connected");
|
||
setTwitchConnected(true);
|
||
loadChatBadges();
|
||
|
||
if (m_isTwitchConnected) {
|
||
for (TimerInfo& timer : m_timers) {
|
||
if (timer.isActive && !timer.timer) {
|
||
startTimer(timer);
|
||
}
|
||
}
|
||
LogManager::instance()->info("General", "handleConnected", "Connected");
|
||
}
|
||
}
|
||
|
||
void uGeneral::handleDisconnected()
|
||
{
|
||
LogManager::instance()->info("General", "handleDisconnected", "Disconnected");
|
||
setTwitchConnected(false);
|
||
|
||
if (m_isTwitchConnected) {
|
||
for (TimerInfo& timer : m_timers) {
|
||
if (timer.isActive && !timer.timer) {
|
||
stopTimer(timer);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::closeEvent(QCloseEvent *event)
|
||
{
|
||
Q_UNUSED(event);
|
||
}
|
||
|
||
void uGeneral::toCommands(QString command, QString response)
|
||
{
|
||
int row = ui->sgCommands->rowCount();
|
||
ui->sgCommands->insertRow(row);
|
||
QTableWidgetItem *commandItem = new QTableWidgetItem(command);
|
||
QTableWidgetItem *responseItem = new QTableWidgetItem(response);
|
||
ui->sgCommands->setItem(row, 0, commandItem);
|
||
ui->sgCommands->setItem(row, 1, responseItem);
|
||
ui->sgCommands->scrollToBottom();
|
||
}
|
||
|
||
void uGeneral::onLogEntryAdded(const LogEntry& entry)
|
||
{
|
||
if (!shouldShowLogEntry(entry.level)) {
|
||
return;
|
||
}
|
||
|
||
addLogToTable(entry, ui->sgLog->rowCount());
|
||
}
|
||
|
||
void uGeneral::onLogCleared()
|
||
{
|
||
ui->sgLog->setRowCount(0);
|
||
}
|
||
|
||
bool uGeneral::shouldShowLogEntry(LogLevel level)
|
||
{
|
||
switch (level) {
|
||
case LogLevel::Info:
|
||
return ui->cbInfo->isChecked();
|
||
case LogLevel::Warning:
|
||
return ui->cbWarning->isChecked();
|
||
case LogLevel::Error:
|
||
return ui->cbError->isChecked();
|
||
case LogLevel::Debug:
|
||
return ui->cbDebug->isChecked();
|
||
case LogLevel::Critical:
|
||
return true;
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
void uGeneral::addLogToTable(const LogEntry &entry, int row)
|
||
{
|
||
ui->sgLog->insertRow(row);
|
||
|
||
LogSettings settings = LogManager::instance()->settings();
|
||
QColor color = settings.colors.value(entry.level, LogEntry::levelToColor(entry.level));
|
||
|
||
QTableWidgetItem* timeItem = new QTableWidgetItem(
|
||
entry.timestamp.toString("hh:mm:ss"));
|
||
timeItem->setForeground(color);
|
||
|
||
QTableWidgetItem* typeItem = new QTableWidgetItem(
|
||
LogEntry::levelToString(entry.level));
|
||
typeItem->setForeground(color);
|
||
|
||
QTableWidgetItem* moduleItem = new QTableWidgetItem(entry.module);
|
||
moduleItem->setForeground(color);
|
||
|
||
QTableWidgetItem* methodItem = new QTableWidgetItem(entry.method);
|
||
methodItem->setForeground(color);
|
||
|
||
QTableWidgetItem* messageItem = new QTableWidgetItem(entry.message);
|
||
messageItem->setForeground(color);
|
||
|
||
ui->sgLog->setItem(row, 0, timeItem);
|
||
ui->sgLog->setItem(row, 1, typeItem);
|
||
ui->sgLog->setItem(row, 2, moduleItem);
|
||
ui->sgLog->setItem(row, 3, methodItem);
|
||
ui->sgLog->setItem(row, 4, messageItem);
|
||
|
||
ui->sgLog->scrollToBottom();
|
||
}
|
||
|
||
void uGeneral::applyLogFilter()
|
||
{
|
||
ui->sgLog->setRowCount(0);
|
||
|
||
QList<LogEntry> allEntries = LogManager::instance()->allEntries();
|
||
|
||
for (const LogEntry &entry : allEntries) {
|
||
if (shouldShowLogEntry(entry.level)) {
|
||
addLogToTable(entry, ui->sgLog->rowCount());
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadSettings(){
|
||
ui->edtBotName->setText(db->readSetting(ui->edtBotName->objectName(),""));
|
||
ui->edtChannel->setText(db->readSetting(ui->edtChannel->objectName(),""));
|
||
ui->edtBotToken->setText(db->readSetting(ui->edtBotToken->objectName(),""));
|
||
ui->edtBotClientID->setText(db->readSetting(ui->edtBotClientID->objectName(),""));
|
||
ui->edtBotTokenStreamer->setText(db->readSetting(ui->edtBotTokenStreamer->objectName(),""));
|
||
ui->cbTTVAutoLogin->setChecked(db->readSetting(ui->cbTTVAutoLogin->objectName()) == "True");
|
||
|
||
ui->edtDACode->setText(db->readSetting(ui->edtDACode->objectName(),""));
|
||
ui->edtDAClientID->setText(db->readSetting(ui->edtDAClientID->objectName(),""));
|
||
ui->edtDARedirectURL->setText(db->readSetting(ui->edtDARedirectURL->objectName(),""));
|
||
ui->edtDAClientSecret->setText(db->readSetting(ui->edtDAClientSecret->objectName(),""));
|
||
ui->cbDAAutoLogin->setChecked(db->readSetting(ui->cbDAAutoLogin->objectName()) == "True");
|
||
|
||
ui->edtGPTPrefix->setText(db->readSetting(ui->edtGPTPrefix->objectName(),""));
|
||
ui->edtAIP1->setText(db->readSetting(ui->edtAIP1->objectName(),""));
|
||
ui->edtAIP2->setText(db->readSetting(ui->edtAIP2->objectName(),""));
|
||
ui->edtKandiKey->setText(db->readSetting(ui->edtKandiKey->objectName(),""));
|
||
ui->edtKandiSecret->setText(db->readSetting(ui->edtKandiSecret->objectName(),""));
|
||
int rg = db->readSetting("aiIndex","0").toInt();
|
||
|
||
switch (rg) {
|
||
case 0:
|
||
ui->rbGC->setChecked(true);
|
||
ui->rbDS->setChecked(false);
|
||
ui->rbCG->setChecked(false);
|
||
ui->RBCustom->setChecked(false);
|
||
break;
|
||
case 1:
|
||
ui->rbGC->setChecked(false);
|
||
ui->rbDS->setChecked(true);
|
||
ui->rbCG->setChecked(false);
|
||
ui->RBCustom->setChecked(false);
|
||
break;
|
||
case 2:
|
||
ui->rbGC->setChecked(false);
|
||
ui->rbDS->setChecked(false);
|
||
ui->rbCG->setChecked(true);
|
||
ui->RBCustom->setChecked(false);
|
||
break;
|
||
case 3:
|
||
ui->rbGC->setChecked(false);
|
||
ui->rbDS->setChecked(false);
|
||
ui->rbCG->setChecked(false);
|
||
ui->RBCustom->setChecked(true);
|
||
break;
|
||
}
|
||
|
||
ui->edtNotifyFileName->setText(db->readSetting(ui->edtNotifyFileName->objectName(),""));
|
||
ui->edtNotifyFileNameMod->setText(db->readSetting(ui->edtNotifyFileNameMod->objectName(),""));
|
||
ui->edtNotifyFileNameSub->setText(db->readSetting(ui->edtNotifyFileNameSub->objectName(),""));
|
||
ui->edtNotifyFileNameVip->setText(db->readSetting(ui->edtNotifyFileNameVip->objectName(),""));
|
||
ui->chEnNotify->setChecked(db->readSetting(ui->chEnNotify->objectName()) == "True");
|
||
ui->chEnNotifyMod->setChecked(db->readSetting(ui->chEnNotifyMod->objectName()) == "True");
|
||
ui->chEnNotifySub->setChecked(db->readSetting(ui->chEnNotifySub->objectName()) == "True");
|
||
ui->chEnNotifyVip->setChecked(db->readSetting(ui->chEnNotifyVip->objectName()) == "True");
|
||
ui->tbNotifyVolume->setValue(db->readSetting(ui->tbNotifyVolume->objectName()).toInt());
|
||
ui->tbNotifyVolumeMod->setValue(db->readSetting(ui->tbNotifyVolumeMod->objectName()).toInt());
|
||
ui->tbNotifyVolumeSub->setValue(db->readSetting(ui->tbNotifyVolumeSub->objectName()).toInt());
|
||
ui->tbNotifyVolumeVip->setValue(db->readSetting(ui->tbNotifyVolumeVip->objectName()).toInt());
|
||
ui->cbNotifyFileAgain1->setChecked(db->readSetting(ui->cbNotifyFileAgain1->objectName()) == "True");
|
||
ui->cbNotifyFileAgain2->setChecked(db->readSetting(ui->cbNotifyFileAgain2->objectName()) == "True");
|
||
ui->cbNotifyFileAgain3->setChecked(db->readSetting(ui->cbNotifyFileAgain3->objectName()) == "True");
|
||
|
||
db->LoadTableWidget(ui->sgCommands);
|
||
db->LoadTableWidget(ui->sgRandomInt);
|
||
|
||
FSingleGrid *form = ui->widget;
|
||
form->setDatabase(db);
|
||
QTableWidget *table = form->findChild<QTableWidget*>("sgSounds");
|
||
db->LoadTableWidget(table);
|
||
form = ui->widget_2;
|
||
form->setDatabase(db);
|
||
table = form->findChild<QTableWidget*>("sgFiles");
|
||
db->LoadTableWidget(table);
|
||
form = ui->widget_3;
|
||
form->setDatabase(db);
|
||
table = form->findChild<QTableWidget*>("sgNeiro");
|
||
db->LoadTableWidget(table);
|
||
db->LoadRandomGroups(ui->lbRandomGroup);
|
||
ui->cbTheme->setCurrentIndex(db->readSetting(ui->cbTheme->objectName(), "0").toInt());
|
||
|
||
db->LoadTableWidget(ui->sgWebServers);
|
||
loadSavedChats();
|
||
loadSavedNotifications();
|
||
}
|
||
|
||
void uGeneral::on_btnGetTockenBot_clicked()
|
||
{
|
||
QString clientId = ui->edtBotClientID->text();
|
||
if (clientId.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите Client ID");
|
||
return;
|
||
}
|
||
|
||
QString scope = "moderator:manage:shoutouts+"
|
||
"moderator:manage:announcements+"
|
||
"moderator:manage:banned_users+"
|
||
"moderator:manage:warnings+"
|
||
"moderator:read:followers+"
|
||
"channel:manage:raids+"
|
||
"channel:manage:moderators+"
|
||
"channel:read:redemptions+"
|
||
"chat:read+"
|
||
"chat:edit+"
|
||
"user:read:emotes";
|
||
|
||
scope = scope.replace(":", "%3A");
|
||
|
||
QString authUrl = QString("https://id.twitch.tv/oauth2/authorize?"
|
||
"client_id=%1&"
|
||
"redirect_uri=http://localhost:8089&"
|
||
"response_type=token&"
|
||
"scope=%2")
|
||
.arg(clientId)
|
||
.arg(scope);
|
||
|
||
if (!fLinkForm) {
|
||
fLinkForm = new uLink(this);
|
||
}
|
||
|
||
fLinkForm->setLinkText(authUrl);
|
||
fLinkForm->show();
|
||
|
||
m_authBot->startServer(authUrl, false);
|
||
}
|
||
|
||
void uGeneral::on_btnOpenFolderSettings_clicked()
|
||
{
|
||
QString roamingPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||
QUrl url = QUrl::fromLocalFile(roamingPath);
|
||
QDesktopServices::openUrl(url);
|
||
}
|
||
|
||
void uGeneral::on_btnGetTokenStreamer_clicked()
|
||
{
|
||
QString clientId = ui->edtBotClientID->text();
|
||
if (clientId.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите Client ID");
|
||
return;
|
||
}
|
||
|
||
QString scope = "channel:manage:broadcast+"
|
||
"channel:read:subscriptions+"
|
||
"channel:read:redemptions+"
|
||
"user:read:email";
|
||
|
||
scope = scope.replace(":", "%3A");
|
||
|
||
QString authUrl = QString("https://id.twitch.tv/oauth2/authorize?"
|
||
"client_id=%1&"
|
||
"redirect_uri=http://localhost:8089&"
|
||
"response_type=token&"
|
||
"scope=%2")
|
||
.arg(clientId)
|
||
.arg(scope);
|
||
|
||
m_authStreamer->startServer(authUrl, true);
|
||
}
|
||
|
||
void uGeneral::onTokenReceived(const QString &token)
|
||
{
|
||
ui->edtBotToken->setText(token);
|
||
|
||
if (fLinkForm && fLinkForm->isVisible()) {
|
||
fLinkForm->close();
|
||
}
|
||
|
||
db->writeSetting(ui->edtBotToken->objectName(), ui->edtBotToken->text());
|
||
}
|
||
|
||
void uGeneral::onTokenReceived2(const QString &token)
|
||
{
|
||
ui->edtBotTokenStreamer->setText(token);
|
||
db->writeSetting(ui->edtBotTokenStreamer->objectName(), ui->edtBotTokenStreamer->text());
|
||
}
|
||
|
||
void uGeneral::onTokenReceived3(const QString &token)
|
||
{
|
||
ui->edtDACode->setText(token);
|
||
db->writeSetting(ui->edtDACode->objectName(), ui->edtDACode->text());
|
||
}
|
||
|
||
void uGeneral::onAuthError(const QString &error)
|
||
{
|
||
Q_UNUSED(error);
|
||
}
|
||
|
||
void uGeneral::on_btnDAGetCode_clicked()
|
||
{
|
||
QString clientId = ui->edtDAClientID->text();
|
||
QString clientSecret = ui->edtDAClientSecret->text();
|
||
QString redirectUri = ui->edtDARedirectURL->text();
|
||
|
||
if (clientId.isEmpty() || clientSecret.isEmpty() || redirectUri.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Заполните все поля для DonationAlerts");
|
||
return;
|
||
}
|
||
QString encodedRedirectUri = QString::fromUtf8(QUrl::toPercentEncoding(redirectUri));
|
||
QString authUrl = QString("https://www.donationalerts.com/oauth/authorize?"
|
||
"client_id=%1&"
|
||
"redirect_uri=%2&"
|
||
"response_type=code&"
|
||
"scope=oauth-donation-index")
|
||
.arg(clientId)
|
||
.arg(encodedRedirectUri);
|
||
|
||
m_authDA->startServer(authUrl, true);
|
||
}
|
||
|
||
void uGeneral::on_btnImportSettings_clicked()
|
||
{
|
||
// TODO
|
||
}
|
||
|
||
void uGeneral::on_btnExportSettings_clicked()
|
||
{
|
||
// TODO
|
||
}
|
||
|
||
void uGeneral::on_RBCustom_pressed()
|
||
{
|
||
ui->lblAPI1->setText("API Token");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("URL");
|
||
ui->lblAPI2->setVisible(true);
|
||
ui->edtAIP2->setVisible(true);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(true);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Normal);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_rbGC_clicked()
|
||
{
|
||
ui->lblAPI1->setText("ClientID");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("Autorization Code");
|
||
ui->lblAPI2->setVisible(true);
|
||
ui->edtAIP2->setVisible(true);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(false);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_rbDS_clicked()
|
||
{
|
||
ui->lblAPI1->setText("API Token");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("");
|
||
ui->lblAPI2->setVisible(false);
|
||
ui->edtAIP2->setVisible(false);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(false);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_rbCG_clicked()
|
||
{
|
||
ui->lblAPI1->setText("API Token");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("");
|
||
ui->lblAPI2->setVisible(false);
|
||
ui->edtAIP2->setVisible(false);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(false);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheck_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileName->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolume->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheckMod_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileNameMod->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeMod->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheckVip_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileNameVip->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeVip->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheckSub_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileNameSub->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeSub->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpen_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtNotifyFileName->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpenMod_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtNotifyFileNameMod->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpenVip_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtNotifyFileNameVip->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpenSub_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtNotifyFileNameSub->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::playNotify(const bool &isMod, const bool &isVip, const bool &isSub)
|
||
{
|
||
if (isMod && ui->chEnNotifyMod->isChecked() &&
|
||
!ui->edtNotifyFileNameMod->text().isEmpty())
|
||
{
|
||
QString fn;
|
||
if (ui->cbNotifyFileAgain1->isChecked())
|
||
fn = ui->edtNotifyFileName->text();
|
||
else
|
||
fn = ui->edtNotifyFileNameMod->text();
|
||
|
||
if (!fn.isEmpty())
|
||
{
|
||
if (QFile::exists(fn)) {
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeMod->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (isVip && ui->chEnNotifyVip->isChecked() &&
|
||
!ui->edtNotifyFileNameVip->text().isEmpty())
|
||
{
|
||
QString fn;
|
||
if (ui->cbNotifyFileAgain2->isChecked())
|
||
fn = ui->edtNotifyFileName->text();
|
||
else
|
||
fn = ui->edtNotifyFileNameVip->text();
|
||
|
||
if (!fn.isEmpty())
|
||
{
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeVip->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (isSub && ui->chEnNotifySub->isChecked() &&
|
||
!ui->edtNotifyFileNameSub->text().isEmpty())
|
||
{
|
||
QString fn;
|
||
if (ui->cbNotifyFileAgain3->isChecked())
|
||
fn = ui->edtNotifyFileName->text();
|
||
else
|
||
fn = ui->edtNotifyFileNameSub->text();
|
||
|
||
if (!fn.isEmpty())
|
||
{
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeSub->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (ui->chEnNotify->isChecked() && !ui->edtNotifyFileName->text().isEmpty())
|
||
{
|
||
QString fn = ui->edtNotifyFileName->text();
|
||
if (QFile::exists(fn)) {
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolume->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
void uGeneral::initializeNotificationSounds()
|
||
{
|
||
QString notifyFile = ui->edtNotifyFileName->text();
|
||
if (!notifyFile.isEmpty() && QFile::exists(notifyFile)) {
|
||
soundManager->loadSound(SoundManager::Channel2, notifyFile);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolume->value());
|
||
}
|
||
|
||
QString notifyModFile = ui->edtNotifyFileNameMod->text();
|
||
if (!notifyModFile.isEmpty() && QFile::exists(notifyModFile)) {
|
||
// загружается по необходимости
|
||
}
|
||
|
||
QString notifyVipFile = ui->edtNotifyFileNameVip->text();
|
||
if (!notifyVipFile.isEmpty() && QFile::exists(notifyVipFile)) {
|
||
}
|
||
|
||
QString notifySubFile = ui->edtNotifyFileNameSub->text();
|
||
if (!notifySubFile.isEmpty() && QFile::exists(notifySubFile)) {
|
||
}
|
||
}
|
||
|
||
void uGeneral::handleNewMessage(const QString &message)
|
||
{
|
||
TwitchMessage msg = TwitchMessage::parse(message);
|
||
|
||
QString userId = m_userManager->checkUser(msg.displayName, msg);
|
||
LogManager::instance()->debug("uGeneral", "handleNewMessage", message);
|
||
m_userManager->m_totalMessages++;
|
||
|
||
if (m_userWidget) {
|
||
m_userWidget->updateStatistics();
|
||
}
|
||
|
||
if (ui->cbTextToSpeach->isChecked())
|
||
{
|
||
return;
|
||
}
|
||
|
||
playNotify(msg.isMod, msg.isVIP, msg.isSubscriber);
|
||
|
||
QString processedMessage = processTwitchMessage(msg);
|
||
QString formattedNickname = formatNicknameWithBadges(msg);
|
||
addChatMessage(formattedNickname, processedMessage);
|
||
if (m_counterManager) {
|
||
m_counterManager->processMessage(msg.message); // или cleanedText
|
||
}
|
||
QString cleanedText = cleanMessageFromAllEmotes(msg.message);
|
||
// Удаляем ссылки (опционально)
|
||
cleanedText.remove(QRegularExpression("https?://\\S+"));
|
||
cleanedText = cleanedText.trimmed();
|
||
|
||
// Проверяем наличие русских букв
|
||
bool hasRussian = false;
|
||
for (const QChar& ch : cleanedText) {
|
||
if (ch.unicode() >= 0x0400 && ch.unicode() <= 0x04FF) {
|
||
hasRussian = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!hasRussian && !cleanedText.isEmpty()) {
|
||
// Здесь нужно вызвать перевод (например, через API)
|
||
// sendToTranslate(cleanedText);
|
||
LogManager::instance()->debug("uGeneral", "handleNewMessage",
|
||
"Требуется перевод: " + cleanedText);
|
||
}
|
||
|
||
if (msg.message.startsWith("!!!")) {
|
||
QString cleanedMessage = msg.message.mid(3);
|
||
// speachMessage(cleanedMessage);
|
||
} else if (msg.message.startsWith("!")) {
|
||
QString cleanedMessage = msg.message.mid(1);
|
||
processUserCommand(userId, cleanedMessage);
|
||
}
|
||
}
|
||
|
||
QString uGeneral::cleanMessageFromAllEmotes(const QString& message) const
|
||
{
|
||
QString cleaned = message;
|
||
cleaned = bttvProvider.cleanMessage(cleaned);
|
||
cleaned = sevenTVProvider.cleanMessage(cleaned);
|
||
return cleaned;
|
||
}
|
||
|
||
void uGeneral::processUserCommand(const QString &username, const QString &commandText)
|
||
{
|
||
if (!m_commandProcessor || !m_userManager) return;
|
||
|
||
QString command;
|
||
QString parameters;
|
||
int spacePos = commandText.indexOf(' ');
|
||
|
||
if (spacePos != -1) {
|
||
command = commandText.left(spacePos).trimmed().toLower();
|
||
parameters = commandText.mid(spacePos + 1).trimmed();
|
||
} else {
|
||
command = commandText.trimmed().toLower();
|
||
}
|
||
|
||
if (command.isEmpty()) return;
|
||
|
||
QString response = m_commandProcessor->generateResponse(username, command, parameters);
|
||
|
||
if (!response.isEmpty()) {
|
||
sendChatResponse(response);
|
||
}
|
||
}
|
||
|
||
void uGeneral::sendChatResponse(const QString &response)
|
||
{
|
||
m_twitchClient->sendChatMessage(ui->edtChannel->text(), response);
|
||
}
|
||
|
||
void uGeneral::handleError(const QString &errorMessage){
|
||
Q_UNUSED(errorMessage);
|
||
}
|
||
|
||
QString uGeneral::processTwitchMessage(const TwitchMessage &msg)
|
||
{
|
||
QString processedMessage = msg.message;
|
||
|
||
if (!msg.emotes.isEmpty()) {
|
||
processedMessage = replaceTwitchEmotes(msg.emotes, processedMessage);
|
||
}
|
||
|
||
processedMessage = replaceCustomEmotes(processedMessage);
|
||
|
||
return processedMessage;
|
||
}
|
||
|
||
QString uGeneral::replaceTwitchEmotes(const QString &emotesData, const QString &message)
|
||
{
|
||
if (emotesData.isEmpty()) return message;
|
||
|
||
QString result = message;
|
||
|
||
QStringList emoteList = emotesData.split('/', Qt::SkipEmptyParts);
|
||
|
||
struct Replacement {
|
||
int start;
|
||
int end;
|
||
QString html;
|
||
};
|
||
|
||
QList<Replacement> replacements;
|
||
|
||
for (const QString &emote : emoteList) {
|
||
QStringList parts = emote.split(':');
|
||
if (parts.size() != 2) continue;
|
||
|
||
QString emoteId = parts[0];
|
||
QStringList positions = parts[1].split(',', Qt::SkipEmptyParts);
|
||
|
||
for (const QString &pos : positions) {
|
||
QStringList startEnd = pos.split('-');
|
||
if (startEnd.size() != 2) continue;
|
||
|
||
bool ok;
|
||
int start = startEnd[0].toInt(&ok);
|
||
if (!ok) continue;
|
||
int end = startEnd[1].toInt(&ok);
|
||
if (!ok) continue;
|
||
|
||
QString emoteUrl = QString("https://static-cdn.jtvnw.net/emoticons/v2/%1/default/dark/1.0").arg(emoteId);
|
||
QString emoteHtml = QString("<img src=\"%1\" alt=\"%2\" style=\"height: 1em; vertical-align: middle;\">")
|
||
.arg(emoteUrl)
|
||
.arg(message.mid(start, end - start + 1));
|
||
|
||
replacements.append({start, end, emoteHtml});
|
||
}
|
||
}
|
||
|
||
std::sort(replacements.begin(), replacements.end(),
|
||
[](const Replacement &a, const Replacement &b) {
|
||
return a.start > b.start;
|
||
});
|
||
|
||
for (const auto &replacement : replacements) {
|
||
result.replace(replacement.start,
|
||
replacement.end - replacement.start + 1,
|
||
replacement.html);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
QString uGeneral::replaceCustomEmotes(const QString &message)
|
||
{
|
||
QString result = message;
|
||
|
||
QRegularExpression wordRegex("([a-zA-Z0-9_-]+)");
|
||
QRegularExpressionMatchIterator matches = wordRegex.globalMatch(message);
|
||
|
||
struct EmoteReplacement {
|
||
int start;
|
||
int length;
|
||
QString html;
|
||
};
|
||
|
||
QList<EmoteReplacement> replacements;
|
||
|
||
while (matches.hasNext()) {
|
||
QRegularExpressionMatch match = matches.next();
|
||
QString word = match.captured(1);
|
||
int start = match.capturedStart(1);
|
||
int length = match.capturedLength(1);
|
||
|
||
if (word.length() < 2) continue;
|
||
|
||
QString bttvUrl = bttvProvider.getEmoteUrl(word);
|
||
if (!bttvUrl.isEmpty()) {
|
||
QString emoteHtml = QString("<img src=\"%1\" alt=\"%2\" style=\"height: 1em; vertical-align: middle;\">")
|
||
.arg(bttvUrl)
|
||
.arg(word);
|
||
replacements.append({start, length, emoteHtml});
|
||
continue;
|
||
}
|
||
|
||
QString sevenTVUrl = sevenTVProvider.getEmoteUrl(word);
|
||
if (!sevenTVUrl.isEmpty()) {
|
||
QString emoteHtml = QString("<img src=\"%1\" alt=\"%2\" style=\"height: 1em; vertical-align: middle;\">")
|
||
.arg(sevenTVUrl)
|
||
.arg(word);
|
||
replacements.append({start, length, emoteHtml});
|
||
continue;
|
||
}
|
||
}
|
||
|
||
std::sort(replacements.begin(), replacements.end(),
|
||
[](const EmoteReplacement &a, const EmoteReplacement &b) {
|
||
return a.start > b.start;
|
||
});
|
||
|
||
for (const auto &replacement : replacements) {
|
||
result.replace(replacement.start, replacement.length, replacement.html);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
void uGeneral::on_pushButton_2_clicked()
|
||
{
|
||
if (ui->pushButton_2->text() == "Отключиться")
|
||
{
|
||
disconnectFromTwitch();
|
||
return;
|
||
}
|
||
|
||
connectToTwitch();
|
||
}
|
||
|
||
void uGeneral::connectToTwitch()
|
||
{
|
||
int botTokenDays = 0;
|
||
int streamerTokenDays = 0;
|
||
|
||
QString botToken = ui->edtBotToken->text();
|
||
QString streamerToken = ui->edtBotTokenStreamer->text();
|
||
|
||
if (!twitchAPI->validateTwitchToken("Бот", botToken, botTokenDays))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!twitchAPI->validateTwitchToken("Стример", streamerToken, streamerTokenDays))
|
||
{
|
||
return;
|
||
}
|
||
|
||
QString oauthToken = "oauth:" + botToken;
|
||
QString nickname = ui->edtBotName->text();
|
||
QString channel = ui->edtChannel->text();
|
||
|
||
if (m_twitchClient->connectToTwitchChat(oauthToken, nickname, channel))
|
||
{
|
||
ui->pushButton_2->setText("Отключиться");
|
||
ui->lbBotDays->setText(QString::number(botTokenDays));
|
||
ui->lbStreamerDays->setText(QString::number(streamerTokenDays));
|
||
setTwitchConnected(true);
|
||
}
|
||
}
|
||
|
||
void uGeneral::disconnectFromTwitch()
|
||
{
|
||
m_twitchClient->disconnectFromServer();
|
||
m_userManager->clear();
|
||
ui->pushButton_2->setText("Подключиться");
|
||
setTwitchConnected(false);
|
||
}
|
||
|
||
|
||
void uGeneral::on_lbRandomGroup_itemClicked(QListWidgetItem *item)
|
||
{
|
||
db->LoadRandomResponses(item->text(), ui->lbRandomRespons);
|
||
}
|
||
|
||
void uGeneral::on_pushButton_clicked()
|
||
{
|
||
LogManager::instance()->clear();
|
||
}
|
||
|
||
void uGeneral::on_sgCommands_cellDoubleClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
ui->edtCommand->setText(ui->sgCommands->item(row, 0)->text());
|
||
ui->textBrowser->setPlainText(ui->sgCommands->item(row, 1)->text());
|
||
}
|
||
|
||
void uGeneral::on_btnRandAdd_clicked()
|
||
{
|
||
QString name = ui->edtRandomName->text().trimmed();
|
||
int otValue = ui->edtOt->value();
|
||
int toValue = ui->edtTo->value();
|
||
|
||
if (name.isEmpty()) {
|
||
QMessageBox::warning(this, "Внимание", "Введите название!");
|
||
ui->edtRandomName->setFocus();
|
||
return;
|
||
}
|
||
|
||
if (otValue > toValue) {
|
||
QMessageBox::warning(this, "Внимание",
|
||
"Значение 'От' не может быть больше значения 'До'!");
|
||
ui->edtOt->setFocus();
|
||
return;
|
||
}
|
||
|
||
m_randomManager->rangeAdded(name, otValue, toValue);
|
||
}
|
||
|
||
void uGeneral::on_btnRandDel_clicked()
|
||
{
|
||
if (!ui->sgRandomInt->currentItem()) {
|
||
QMessageBox::warning(this, "Внимание", "Выберите строку для удаления!");
|
||
return;
|
||
}
|
||
|
||
int row = ui->sgRandomInt->currentItem()->row();
|
||
m_randomManager->rangeRemoved(ui->sgRandomInt->item(row, 0)->text());
|
||
}
|
||
|
||
void uGeneral::on_sgRandomInt_cellClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
ui->edtRandomName->setText(ui->sgRandomInt->item(row, 0)->text());
|
||
ui->edtOt->setValue(ui->sgRandomInt->item(row, 1)->text().toInt());
|
||
ui->edtTo->setValue(ui->sgRandomInt->item(row, 2)->text().toInt());
|
||
}
|
||
|
||
void uGeneral::on_sgRandomInt_cellDoubleClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[[" + ui->sgRandomInt->item(row, 0)->text() + "]]");
|
||
}
|
||
|
||
void uGeneral::on_lbRandomGroup_doubleClicked(const QModelIndex &index)
|
||
{
|
||
Q_UNUSED(index);
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("{{" + ui->lbRandomGroup->currentItem()->text() + "}}");
|
||
}
|
||
|
||
void uGeneral::on_lbRandomRespons_itemDoubleClicked(QListWidgetItem *item)
|
||
{
|
||
ui->edtRandomGroup->setText(ui->lbRandomGroup->currentItem()->text());
|
||
ui->edtRandomRespons->setText(item->text());
|
||
}
|
||
|
||
void uGeneral::on_btnRandomAdd_clicked()
|
||
{
|
||
QString groupName = ui->edtRandomGroup->text().trimmed();
|
||
QString response = ui->edtRandomRespons->text().trimmed();
|
||
|
||
if (groupName.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите название группы!");
|
||
return;
|
||
}
|
||
|
||
if (response.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите ответ!");
|
||
return;
|
||
}
|
||
|
||
if (!db->AddGroupResponse(groupName, response)) {
|
||
QMessageBox::critical(this, "Ошибка",
|
||
"Не удалось добавить ответ в базу данных:\n" + db->lastError());
|
||
return;
|
||
}
|
||
|
||
db->LoadRandomGroups(ui->lbRandomGroup);
|
||
|
||
QList<QListWidgetItem*> items = ui->lbRandomGroup->findItems(groupName, Qt::MatchExactly);
|
||
if (!items.isEmpty()) {
|
||
ui->lbRandomGroup->setCurrentItem(items.first());
|
||
}
|
||
|
||
if (ui->lbRandomGroup->currentItem()) {
|
||
db->LoadRandomResponses(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons);
|
||
} else {
|
||
ui->lbRandomRespons->clear();
|
||
}
|
||
|
||
ui->edtRandomRespons->clear();
|
||
|
||
m_randomResponses->addResponse(groupName, response);
|
||
}
|
||
|
||
void uGeneral::on_btnRandomDel_clicked()
|
||
{
|
||
db->DeleteResponse(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons->currentItem()->text());
|
||
db->LoadRandomResponses(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons);
|
||
m_randomResponses->removeResponse(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons->currentItem()->text());
|
||
}
|
||
|
||
void uGeneral::on_btnRmGroup_clicked()
|
||
{
|
||
db->DeleteGroup(ui->lbRandomGroup->currentItem()->text());
|
||
db->LoadRandomGroups(ui->lbRandomGroup);
|
||
m_randomResponses->removeGroup(ui->lbRandomGroup->currentItem()->text());
|
||
}
|
||
|
||
void uGeneral::on_btnGetDateFollow_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[FOLLOW]");
|
||
}
|
||
|
||
void uGeneral::on_btnGetAgeAccaunt_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[AGE]");
|
||
}
|
||
|
||
void uGeneral::on_btnGPT_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[AI]");
|
||
}
|
||
|
||
void uGeneral::on_btnAIPic_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[Kandinsky]");
|
||
}
|
||
|
||
void uGeneral::on_btnRandomUserName_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[RANDOMUSER]");
|
||
}
|
||
|
||
void uGeneral::on_btnGetChannelStat_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[STAT]");
|
||
}
|
||
|
||
void uGeneral::on_btnAddCommand_clicked()
|
||
{
|
||
QString name = ui->edtCommand->text().trimmed();
|
||
QString response = ui->textBrowser->toPlainText();
|
||
|
||
if (name.isEmpty()) {
|
||
QMessageBox::warning(this, "Внимание", "Введите команду!");
|
||
LogManager::instance()->warning("uGeneral", "on_btnAddCommand_clicked",
|
||
"Попытка добавить команду с пустым именем");
|
||
return;
|
||
}
|
||
|
||
int row = ui->sgCommands->rowCount();
|
||
ui->sgCommands->insertRow(row);
|
||
ui->sgCommands->setItem(row, 0, new QTableWidgetItem(name));
|
||
ui->sgCommands->setItem(row, 1, new QTableWidgetItem(response));
|
||
|
||
db->SaveTableWidget(ui->sgCommands);
|
||
m_commandProcessor->addCommand(name, response);
|
||
LogManager::instance()->info("uGeneral", "on_btnAddCommand_clicked",
|
||
QString("Добавлена команда: %1").arg(name));
|
||
}
|
||
|
||
void uGeneral::on_btnRmCommand_clicked()
|
||
{
|
||
if (!ui->sgCommands->currentItem()) {
|
||
QMessageBox::warning(this, "Внимание", "Выберите команду для удаления!");
|
||
LogManager::instance()->warning("uGeneral", "on_btnRmCommand_clicked",
|
||
"Не выбрана команда для удаления");
|
||
return;
|
||
}
|
||
|
||
int row = ui->sgCommands->currentItem()->row();
|
||
QString commandName = ui->sgCommands->item(row, 0)->text();
|
||
|
||
ui->sgCommands->removeRow(row);
|
||
db->SaveTableWidget(ui->sgCommands);
|
||
m_commandProcessor->deleteCommand(commandName);
|
||
LogManager::instance()->info("uGeneral", "on_btnRmCommand_clicked",
|
||
QString("Удалена команда: %1").arg(commandName));
|
||
}
|
||
|
||
void uGeneral::on_btnEdtCommand_clicked()
|
||
{
|
||
if (!ui->sgCommands->currentItem()) {
|
||
QMessageBox::warning(this, "Внимание", "Выберите команду для редактирования!");
|
||
LogManager::instance()->warning("uGeneral", "on_btnEdtCommand_clicked",
|
||
"Не выбрана команда для редактирования");
|
||
return;
|
||
}
|
||
|
||
int row = ui->sgCommands->currentItem()->row();
|
||
QString newName = ui->edtCommand->text().trimmed();
|
||
QString response = ui->textBrowser->toPlainText();
|
||
|
||
if (newName.isEmpty()) {
|
||
QMessageBox::warning(this, "Внимание", "Введите новое имя команды!");
|
||
return;
|
||
}
|
||
QString oldCommand = ui->sgCommands->item(row, 0)->text();
|
||
ui->sgCommands->item(row, 0)->setText(newName);
|
||
ui->sgCommands->item(row, 1)->setText(response);
|
||
db->SaveTableWidget(ui->sgCommands);
|
||
m_commandProcessor->editCommand(oldCommand, newName, response);
|
||
LogManager::instance()->info("uGeneral", "on_btnEdtCommand_clicked",
|
||
QString("Отредактирована команда в строке %1").arg(row));
|
||
}
|
||
|
||
void uGeneral::loadQssFiles()
|
||
{
|
||
ui->cbTheme->clear();
|
||
ui->cbTheme->addItem("Без темы", "");
|
||
|
||
QString systemStylesPath = FileManager::instance().getPath(FileManager::SystemStyles);
|
||
QDir systemStylesDir(systemStylesPath);
|
||
|
||
if (systemStylesDir.exists()) {
|
||
loadStylesFromDirectory(systemStylesDir, "Системные");
|
||
} else {
|
||
qWarning() << "Папка системных стилей не найдена:" << systemStylesPath;
|
||
systemStylesDir.mkpath(".");
|
||
}
|
||
|
||
QString userStylesPath = FileManager::instance().getPath(FileManager::Styles);
|
||
QDir userStylesDir(userStylesPath);
|
||
|
||
if (userStylesDir.exists()) {
|
||
loadStylesFromDirectory(userStylesDir, "Пользовательские");
|
||
} else {
|
||
qWarning() << "Папка пользовательских стилей не найдена:" << userStylesPath;
|
||
userStylesDir.mkpath(".");
|
||
}
|
||
|
||
if (ui->cbTheme->count() <= 1) {
|
||
qWarning() << "Темы не найдены, создаем базовые";
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadStylesFromDirectory(const QDir &stylesDir, const QString &category)
|
||
{
|
||
if (!stylesDir.exists()) {
|
||
return;
|
||
}
|
||
|
||
QStringList filters;
|
||
filters << "*.qss" << "*.QSS";
|
||
QStringList qssFiles = stylesDir.entryList(filters, QDir::Files);
|
||
|
||
foreach (const QString &file, qssFiles) {
|
||
QString displayName = QString("%1: %2").arg(category).arg(file);
|
||
ui->cbTheme->addItem(displayName, stylesDir.absoluteFilePath(file));
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_cbTheme_currentIndexChanged(int index)
|
||
{
|
||
if (index < 0) return;
|
||
|
||
QString filePath = ui->cbTheme->itemData(index).toString();
|
||
|
||
if (filePath.isEmpty()) {
|
||
qApp->setStyleSheet("");
|
||
return;
|
||
}
|
||
|
||
applyStyleSheet(filePath);
|
||
|
||
db->writeSetting(ui->cbTheme->objectName(), QString::number(index));
|
||
}
|
||
|
||
void uGeneral::applyStyleSheet(const QString &filename)
|
||
{
|
||
QFile file(filename);
|
||
|
||
if (!file.exists()) {
|
||
qWarning() << "QSS файл не найден:" << filename;
|
||
return;
|
||
}
|
||
|
||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||
qWarning() << "Не удалось открыть QSS файл:" << filename;
|
||
return;
|
||
}
|
||
|
||
QTextStream stream(&file);
|
||
QString styleSheet = stream.readAll();
|
||
file.close();
|
||
|
||
qApp->setStyleSheet(styleSheet);
|
||
}
|
||
|
||
void uGeneral::on_btnWSCreateChat_clicked()
|
||
{
|
||
FCreateChat *createDialog = new FCreateChat(db, this);
|
||
connect(createDialog, &FCreateChat::serverCreated, this, &uGeneral::onChatServerCreated);
|
||
connect(createDialog, &FCreateChat::serverUpdated, this, &uGeneral::onChatServerUpdated);
|
||
createDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createDialog->exec();
|
||
}
|
||
|
||
void uGeneral::on_btnWSCreateNotify_clicked()
|
||
{
|
||
FCreateNotify *createDialog = new FCreateNotify(db, this);
|
||
connect(createDialog, &FCreateNotify::serverCreated, this, &uGeneral::onNotifyServerCreated);
|
||
createDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createDialog->exec();
|
||
}
|
||
|
||
void uGeneral::onChatServerCreated(HttpServerChat *server, const QString &name)
|
||
{
|
||
if (server) {
|
||
server->setParent(this);
|
||
addChatServer(server, name);
|
||
}
|
||
}
|
||
|
||
void uGeneral::onNotifyServerCreated(HttpServer *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
if (!db->saveNotification(name, server)) {
|
||
}
|
||
|
||
m_notificationServers.append(server);
|
||
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
addServerToTable(name, "Уведомления", server->port(), url, server);
|
||
}
|
||
|
||
void uGeneral::onNotifyServerUpdated(HttpServer *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
int oldPort = 0;
|
||
for (int row = 0; row < ui->sgWebServers->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(row, 0);
|
||
if (nameItem && nameItem->data(Qt::UserRole).value<QObject*>() == server) {
|
||
QTableWidgetItem *portItem = ui->sgWebServers->item(row, 2);
|
||
if (portItem) {
|
||
oldPort = portItem->text().toInt();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!db->updateNotification(name, server, oldPort)) {
|
||
}
|
||
|
||
int index = m_notificationServers.indexOf(server);
|
||
if (index >= 0) {
|
||
m_notificationServers[index] = server;
|
||
}
|
||
|
||
for (int row = 0; row < ui->sgWebServers->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(row, 0);
|
||
if (nameItem && nameItem->data(Qt::UserRole).value<QObject*>() == server) {
|
||
nameItem->setText(name);
|
||
ui->sgWebServers->item(row, 2)->setText(QString::number(server->port()));
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
ui->sgWebServers->item(row, 3)->setText(url);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::addNotificationServer(HttpServer *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
m_notificationServers.append(server);
|
||
|
||
QString serverName = name.isEmpty() ?
|
||
QString("Уведомления %1").arg(m_notificationServers.size()) : name;
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
|
||
addServerToTable(serverName, "Уведомления", server->port(), url, server);
|
||
}
|
||
|
||
void uGeneral::addChatServer(HttpServerChat *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
m_chatServers.append(server);
|
||
|
||
QString serverName = name.isEmpty() ?
|
||
QString("Чат %1").arg(m_chatServers.size()) : name;
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
|
||
addServerToTable(serverName, "Чат", server->port(), url, server);
|
||
}
|
||
|
||
void uGeneral::addServerToTable(const QString &name, const QString &type,
|
||
quint16 port, const QString &url, QObject *serverObj)
|
||
{
|
||
int row = ui->sgWebServers->rowCount();
|
||
ui->sgWebServers->insertRow(row);
|
||
|
||
QTableWidgetItem *nameItem = new QTableWidgetItem(name);
|
||
nameItem->setData(Qt::UserRole, QVariant::fromValue<QObject*>(serverObj));
|
||
ui->sgWebServers->setItem(row, 0, nameItem);
|
||
ui->sgWebServers->setItem(row, 1, new QTableWidgetItem(type));
|
||
ui->sgWebServers->setItem(row, 2, new QTableWidgetItem(QString::number(port)));
|
||
QTableWidgetItem *linkItem = new QTableWidgetItem(url);
|
||
linkItem->setForeground(QBrush(Qt::blue));
|
||
linkItem->setFlags(linkItem->flags() | Qt::ItemIsEditable);
|
||
ui->sgWebServers->setItem(row, 3, linkItem);
|
||
}
|
||
|
||
void uGeneral::addNotification(const QString &nickname, double amount, const QString &type)
|
||
{
|
||
if (m_notificationServers.isEmpty()) {
|
||
return;
|
||
}
|
||
|
||
HttpServer *server = m_notificationServers.first();
|
||
|
||
Notification notif;
|
||
notif.nickname = nickname.isEmpty() ? "Аноним" : nickname;
|
||
notif.content = QString("%1 пожертвовал %2 руб.").arg(notif.nickname).arg(amount);
|
||
|
||
notif.blockColor = "#4CAF50";
|
||
notif.borderColor = "#2E7D32";
|
||
notif.borderSize = 2;
|
||
notif.duration = 10;
|
||
notif.titleColor = "#FFFFFF";
|
||
notif.titleFamily = "Arial";
|
||
notif.titleSize = 20;
|
||
notif.contentColor = "#F5F5F5";
|
||
notif.contentFamily = "Arial";
|
||
notif.contentSize = 16;
|
||
notif.pageBackgroundColor = "transparent";
|
||
|
||
if (type == "donation") {
|
||
notif.soundURL = "/sounds/donation.mp3";
|
||
}
|
||
|
||
server->addNotification(notif);
|
||
}
|
||
|
||
void uGeneral::addChatMessage(const QString &nickname,
|
||
const QString &message)
|
||
{
|
||
if (m_chatServers.isEmpty()) {
|
||
return;
|
||
}
|
||
|
||
QString formattedNickname = nickname;
|
||
|
||
for (HttpServerChat *server : qAsConst(m_chatServers)) {
|
||
sendMessageToServer(server, formattedNickname, message);
|
||
}
|
||
}
|
||
|
||
QString uGeneral::getBadgesHTML(const TwitchMessage &msg)
|
||
{
|
||
QString badgesHtml;
|
||
|
||
for (const TwitchMessage::Badge &badge : msg.badges) {
|
||
QString badgeUrl = getBadgeUrl(badge.name, badge.version);
|
||
|
||
if (!badgeUrl.isEmpty()) {
|
||
badgesHtml += QString("<img src=\"%1\" alt=\"%2\" title=\"%3\" "
|
||
"style=\"height: 18px; width: 18px; "
|
||
"vertical-align: middle; margin-right: 2px;\">")
|
||
.arg(badgeUrl)
|
||
.arg(badge.name)
|
||
.arg(QString("%1 (версия %2)").arg(badge.name).arg(badge.version));
|
||
}
|
||
}
|
||
|
||
auto addSystemBadge = [&](const QString &badgeName, bool condition) {
|
||
if (condition) {
|
||
bool alreadyHas = false;
|
||
for (const TwitchMessage::Badge &badge : msg.badges) {
|
||
if (badge.name == badgeName) {
|
||
alreadyHas = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!alreadyHas) {
|
||
QString badgeUrl = getBadgeUrl(badgeName, 1);
|
||
if (!badgeUrl.isEmpty()) {
|
||
badgesHtml += QString("<img src=\"%1\" alt=\"%2\" title=\"%2\" "
|
||
"style=\"height: 18px; width: 18px; "
|
||
"vertical-align: middle; margin-right: 2px;\">")
|
||
.arg(badgeUrl)
|
||
.arg(badgeName);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
addSystemBadge("moderator", msg.isMod);
|
||
addSystemBadge("vip", msg.isVIP);
|
||
addSystemBadge("subscriber", msg.isSubscriber);
|
||
|
||
if (!badgesHtml.isEmpty()) {
|
||
badgesHtml += " ";
|
||
}
|
||
|
||
return badgesHtml;
|
||
}
|
||
|
||
QString uGeneral::getBadgeUrl(const QString &setId, int versionId)
|
||
{
|
||
for (const ChatBadge &badge : m_chatBadges) {
|
||
if (badge.setId == setId) {
|
||
for (const BadgeVersion &version : badge.versions) {
|
||
if (version.id == versionId) {
|
||
return version.imageUrl1x;
|
||
}
|
||
}
|
||
if (!badge.versions.isEmpty()) {
|
||
return badge.versions.first().imageUrl1x;
|
||
}
|
||
}
|
||
}
|
||
|
||
static const QMap<QString, QString> defaultBadgeUrls = {
|
||
{"moderator", "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/%1"},
|
||
{"vip", "https://static-cdn.jtvnw.net/badges/v1/1923c73d-70c9-4b1a-9c93-c8d8bd9e2f31/%1"},
|
||
{"subscriber", "https://static-cdn.jtvnw.net/badges/v1/5d9f2208-5dd8-11e7-8513-2ff4adfae661/%1"},
|
||
{"broadcaster", "https://static-cdn.jtvnw.net/badges/v1/5527c58c-fb7d-422d-b71b-f309dcb85cc1/%1"},
|
||
{"admin", "https://static-cdn.jtvnw.net/badges/v1/9ef7e029-4cdf-4d4d-a0d5-e2b3fb2583fe/%1"},
|
||
{"staff", "https://static-cdn.jtvnw.net/badges/v1/d97c37bd-a6f5-4c38-8f57-4e4bef88af34/%1"},
|
||
{"turbo", "https://static-cdn.jtvnw.net/badges/v1/bd444ec6-8f34-4bf9-91f4-af1e3428d80f/%1"},
|
||
{"partner", "https://static-cdn.jtvnw.net/badges/v1/d12a2e27-16f6-41d0-ab77-b780518f00a3/%1"},
|
||
{"bits", "https://static-cdn.jtvnw.net/badges/v1/73b5c3fb-24f9-4a82-a852-2f475b59411c/%1"}
|
||
};
|
||
|
||
if (defaultBadgeUrls.contains(setId)) {
|
||
return defaultBadgeUrls[setId].arg(versionId);
|
||
}
|
||
|
||
if (setId.startsWith("subscriber-")) {
|
||
QString level = setId.mid(11);
|
||
return QString("https://static-cdn.jtvnw.net/badges/v1/5d9f2208-5dd8-11e7-8513-2ff4adfae661/%1").arg(level);
|
||
}
|
||
|
||
if (setId.startsWith("bits-")) {
|
||
QString level = setId.mid(5);
|
||
return QString("https://static-cdn.jtvnw.net/badges/v1/73b5c3fb-24f9-4a82-a852-2f475b59411c/%1").arg(level);
|
||
}
|
||
|
||
return QString("https://static-cdn.jtvnw.net/badges/v1/%1/%2").arg(setId).arg(versionId);
|
||
}
|
||
|
||
QString uGeneral::formatNicknameWithBadges(const TwitchMessage &msg)
|
||
{
|
||
QString result = getBadgesHTML(msg);
|
||
|
||
QString escapedName = msg.displayName.toHtmlEscaped();
|
||
QString escapedColor = msg.color.toHtmlEscaped();
|
||
|
||
if (!escapedColor.isEmpty() && escapedColor != "#000000") {
|
||
result += QString("<span style=\"color: %1;\">%2</span>")
|
||
.arg(escapedColor)
|
||
.arg(escapedName);
|
||
} else {
|
||
result += escapedName;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
void uGeneral::sendMessageToServer(HttpServerChat *server, const QString &nickname,
|
||
const QString &message)
|
||
{
|
||
if (!server) return;
|
||
|
||
StyleChat style;
|
||
style.nick = nickname;
|
||
style.context = message;
|
||
|
||
style.fontFamily = server->getFontFamily();
|
||
style.fontSize = server->getFontSize();
|
||
style.fontColor = server->getFontColor();
|
||
style.blockColor = server->getBlockColor();
|
||
style.borderColor = server->getBorderColor();
|
||
style.borderSize = server->getBorderSize();
|
||
style.padding = server->getPadding();
|
||
style.timeMsg = server->getMessageTimeout();
|
||
style.bColor = server->getBackgroundColor();
|
||
style.transparency = server->getTransparency();
|
||
style.nick = nickname;
|
||
style.context = message;
|
||
|
||
server->addMessage(style);
|
||
}
|
||
|
||
void uGeneral::on_sgWebServers_cellDoubleClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
|
||
if (row < 0 || row >= ui->sgWebServers->rowCount()) {
|
||
return;
|
||
}
|
||
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(row, 0);
|
||
QTableWidgetItem *typeItem = ui->sgWebServers->item(row, 1);
|
||
|
||
if (!nameItem || !typeItem) {
|
||
return;
|
||
}
|
||
|
||
QString serverType = typeItem->text();
|
||
|
||
if (serverType.toLower() == "чат") {
|
||
QObject *serverObj = nameItem->data(Qt::UserRole).value<QObject*>();
|
||
HttpServerChat *existingServer = qobject_cast<HttpServerChat*>(serverObj);
|
||
|
||
if (existingServer) {
|
||
FCreateChat *createChatDialog = new FCreateChat(db, this);
|
||
createChatDialog->loadExistingServer(existingServer, nameItem->text());
|
||
connect(createChatDialog, &FCreateChat::serverUpdated, this, &uGeneral::onChatServerUpdated);
|
||
createChatDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createChatDialog->exec();
|
||
}
|
||
} else if (serverType.toLower() == "уведомления") {
|
||
QObject *serverObj = nameItem->data(Qt::UserRole).value<QObject*>();
|
||
HttpServer *existingServer = qobject_cast<HttpServer*>(serverObj);
|
||
|
||
if (existingServer) {
|
||
FCreateNotify *createNotifyDialog = new FCreateNotify(db, this);
|
||
createNotifyDialog->loadExistingServer(existingServer, nameItem->text());
|
||
connect(createNotifyDialog, &FCreateNotify::serverUpdated,
|
||
this, &uGeneral::onNotifyServerUpdated);
|
||
createNotifyDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createNotifyDialog->exec();
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadSavedNotifications()
|
||
{
|
||
QList<NotificationSettings> notifications = db->loadAllNotifications();
|
||
|
||
for (const NotificationSettings &settings : notifications) {
|
||
if (settings.type.toLower() == "notification") {
|
||
HttpServer *server = new HttpServer(this);
|
||
|
||
server->setBlockColor(settings.blockColor);
|
||
server->setBorderColor(settings.borderColor);
|
||
server->setBorderSize(settings.borderSize);
|
||
server->setTransparency(settings.transparency);
|
||
server->setPageBackgroundColor(settings.pageBackgroundColor);
|
||
server->setTitleFont(
|
||
settings.titleFamily,
|
||
settings.titleSize,
|
||
settings.titleColor
|
||
);
|
||
server->setContentFont(
|
||
settings.contentFamily,
|
||
settings.contentSize,
|
||
settings.contentColor
|
||
);
|
||
server->setDuration(settings.duration);
|
||
|
||
if (server->start(settings.port)) {
|
||
m_notificationServers.append(server);
|
||
|
||
QString url = QString("http://localhost:%1").arg(settings.port);
|
||
addServerToTable(settings.name, "Уведомления", settings.port, url, server);
|
||
} else {
|
||
delete server;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::onChatServerUpdated(HttpServerChat *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
int index = m_chatServers.indexOf(server);
|
||
if (index >= 0) {
|
||
m_chatServers[index] = server;
|
||
|
||
for (int row = 0; row < ui->sgWebServers->rowCount(); ++row) {
|
||
QTableWidgetItem *portItem = ui->sgWebServers->item(row, 0);
|
||
if (portItem && portItem->text().toUShort() == server->port()) {
|
||
ui->sgWebServers->setItem(row, 0, new QTableWidgetItem(name));
|
||
ui->sgWebServers->setItem(row, 0, new QTableWidgetItem(QString::number(server->port())));
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
ui->sgWebServers->setItem(row, 2, new QTableWidgetItem(url));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_www_currentChanged(int index)
|
||
{
|
||
Q_UNUSED(index);
|
||
}
|
||
|
||
void uGeneral::on_edtBotName_selectionChanged()
|
||
{
|
||
}
|
||
|
||
void uGeneral::on_edtBotName_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotName->objectName(), ui->edtBotName->text());
|
||
}
|
||
|
||
void uGeneral::on_edtBotToken_textEdited(const QString &arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
}
|
||
|
||
void uGeneral::on_edtBotToken_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotToken->objectName(), ui->edtBotToken->text());
|
||
}
|
||
|
||
void uGeneral::on_edtBotTokenStreamer_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotTokenStreamer->objectName(), ui->edtBotTokenStreamer->text());
|
||
}
|
||
|
||
void uGeneral::on_edtBotClientID_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotClientID->objectName(), ui->edtBotClientID->text());
|
||
}
|
||
|
||
void uGeneral::on_edtChannel_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtChannel->objectName(), ui->edtChannel->text());
|
||
}
|
||
|
||
void uGeneral::on_edtDAClientID_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDAClientID->objectName(), ui->edtDAClientID->text());
|
||
}
|
||
|
||
void uGeneral::on_edtDAClientSecret_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDAClientSecret->objectName(), ui->edtDAClientSecret->text());
|
||
}
|
||
|
||
void uGeneral::on_edtDARedirectURL_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDARedirectURL->objectName(), ui->edtDARedirectURL->text());
|
||
}
|
||
|
||
void uGeneral::on_edtDACode_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDACode->objectName(), ui->edtDACode->text());
|
||
}
|
||
|
||
void uGeneral::on_cbDAAutoLogin_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->cbDAAutoLogin->objectName(), ui->cbDAAutoLogin->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_edtGPTPrefix_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtGPTPrefix->objectName(), ui->edtGPTPrefix->text());
|
||
}
|
||
|
||
void uGeneral::on_edtAIP1_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtAIP1->objectName(), ui->edtAIP1->text());
|
||
}
|
||
|
||
void uGeneral::on_edtAIP2_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtAIP2->objectName(), ui->edtAIP2->text());
|
||
}
|
||
|
||
void uGeneral::on_edtAIP3_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtAIP3->objectName(), ui->edtAIP3->text());
|
||
}
|
||
|
||
void uGeneral::on_edtKandiKey_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtKandiKey->objectName(), ui->edtKandiKey->text());
|
||
}
|
||
|
||
void uGeneral::on_edtKandiSecret_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtKandiSecret->objectName(), ui->edtKandiSecret->text());
|
||
}
|
||
|
||
void uGeneral::on_cbOllama_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->cbOllama->objectName(), ui->cbOllama->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_tbNotifyVolume_valueChanged(int value)
|
||
{
|
||
Q_UNUSED(value);
|
||
}
|
||
|
||
void uGeneral::on_edtNotifyFileName_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileName->objectName(), ui->edtNotifyFileName->text());
|
||
}
|
||
|
||
void uGeneral::on_edtNotifyFileNameMod_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileNameMod->objectName(), ui->edtNotifyFileNameMod->text());
|
||
}
|
||
|
||
void uGeneral::on_edtNotifyFileNameVip_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileNameVip->objectName(), ui->edtNotifyFileNameVip->text());
|
||
}
|
||
|
||
void uGeneral::on_edtNotifyFileNameSub_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileNameSub->objectName(), ui->edtNotifyFileNameSub->text());
|
||
}
|
||
|
||
void uGeneral::on_chEnNotify_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->chEnNotify->objectName(), ui->chEnNotify->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_chEnNotifyMod_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->chEnNotifyMod->objectName(), ui->chEnNotifyMod->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_chEnNotifyVip_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->chEnNotifyVip->objectName(), ui->chEnNotifyVip->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_chEnNotifySub_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->chEnNotifySub->objectName(), ui->chEnNotifySub->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_tbNotifyVolume_sliderPressed()
|
||
{
|
||
}
|
||
|
||
void uGeneral::on_tbNotifyVolume_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolume->objectName(), QString::number(ui->tbNotifyVolume->value()));
|
||
}
|
||
|
||
void uGeneral::on_tbNotifyVolumeMod_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolumeMod->objectName(), QString::number(ui->tbNotifyVolumeMod->value()));
|
||
}
|
||
|
||
void uGeneral::on_tbNotifyVolumeVip_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolumeVip->objectName(), QString::number(ui->tbNotifyVolumeVip->value()));
|
||
}
|
||
|
||
void uGeneral::on_tbNotifyVolumeSub_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolumeSub->objectName(), QString::number(ui->tbNotifyVolumeSub->value()));
|
||
}
|
||
|
||
void uGeneral::on_cbNotifyFileAgain1_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->cbNotifyFileAgain1->objectName(), ui->cbNotifyFileAgain1->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_cbNotifyFileAgain2_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->cbNotifyFileAgain2->objectName(), ui->cbNotifyFileAgain2->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_cbNotifyFileAgain3_stateChanged(int arg1)
|
||
{
|
||
Q_UNUSED(arg1);
|
||
db->writeSetting(ui->cbNotifyFileAgain3->objectName(), ui->cbNotifyFileAgain3->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
void uGeneral::on_btnThemesFolder_clicked()
|
||
{
|
||
QString userThemesPath = FileManager::instance().getPath(FileManager::Styles);
|
||
QDesktopServices::openUrl(QUrl::fromLocalFile(userThemesPath));
|
||
}
|
||
|
||
void uGeneral::loadSavedChats()
|
||
{
|
||
QList<ChatSettings> chats = db->loadAllChats();
|
||
|
||
for (const ChatSettings &settings : chats) {
|
||
if (settings.type.toLower() == "chat") {
|
||
HttpServerChat *server = new HttpServerChat(
|
||
settings.fontList,
|
||
settings.port,
|
||
settings.backgroundColor,
|
||
this
|
||
);
|
||
|
||
server->setBlockColor(settings.blockColor);
|
||
server->setBorderColor(settings.borderColor);
|
||
server->setBorderSize(settings.borderSize);
|
||
server->setPadding(settings.padding);
|
||
server->setTransparency(settings.transparency);
|
||
server->setFontFamily(settings.fontFamily);
|
||
server->setFontSize(settings.fontSize);
|
||
server->setFontColor(settings.fontColor);
|
||
server->setFreez(settings.freez);
|
||
server->setMessageTimeout(settings.messageTimeout);
|
||
server->setDeleteMode(settings.deleteByTime, settings.maxMsgCount);
|
||
|
||
if (server->start()) {
|
||
m_chatServers.append(server);
|
||
|
||
QString url = QString("http://localhost:%1").arg(settings.port);
|
||
addServerToTable(settings.name, "Чат", settings.port, url, server);
|
||
} else {
|
||
delete server;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_btnOpenStream_clicked()
|
||
{
|
||
QDesktopServices::openUrl("https://www.twitch.tv/" + ui->edtChannel->text());
|
||
}
|
||
|
||
void uGeneral::on_btnAUserName_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[USERNAME]");
|
||
}
|
||
|
||
void uGeneral::on_btnRmWebService_clicked()
|
||
{
|
||
int currentRow = ui->sgWebServers->currentRow();
|
||
if (currentRow < 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите веб-сервис для удаления!");
|
||
return;
|
||
}
|
||
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(currentRow, 0);
|
||
QTableWidgetItem *typeItem = ui->sgWebServers->item(currentRow, 1);
|
||
QTableWidgetItem *portItem = ui->sgWebServers->item(currentRow, 2);
|
||
|
||
if (!nameItem || !typeItem || !portItem) {
|
||
QMessageBox::warning(this, "Ошибка", "Не удалось получить данные о сервисе!");
|
||
return;
|
||
}
|
||
|
||
QString serviceName = nameItem->text();
|
||
QString serviceType = typeItem->text();
|
||
quint16 port = portItem->text().toUShort();
|
||
|
||
int result = QMessageBox::question(this, "Подтверждение удаления",
|
||
QString("Вы уверены, что хотите удалить сервис '%1'?\n"
|
||
"Тип: %2\n"
|
||
"Порт: %3").arg(serviceName, serviceType, QString::number(port)),
|
||
QMessageBox::Yes | QMessageBox::No);
|
||
|
||
if (result != QMessageBox::Yes) {
|
||
return;
|
||
}
|
||
|
||
QObject *serverObj = nameItem->data(Qt::UserRole).value<QObject*>();
|
||
|
||
bool deletedFromDB = false;
|
||
|
||
if (serviceType.toLower() == "чат" || serviceType.toLower() == "chat") {
|
||
HttpServerChat *chatServer = qobject_cast<HttpServerChat*>(serverObj);
|
||
if (chatServer) {
|
||
chatServer->stop();
|
||
|
||
int chatIndex = m_chatServers.indexOf(chatServer);
|
||
if (chatIndex >= 0) {
|
||
m_chatServers.removeAt(chatIndex);
|
||
}
|
||
|
||
deletedFromDB = db->deleteChat(port);
|
||
|
||
delete chatServer;
|
||
}
|
||
}
|
||
else if (serviceType.toLower() == "уведомления" || serviceType.toLower() == "notification") {
|
||
HttpServer *notifyServer = qobject_cast<HttpServer*>(serverObj);
|
||
if (notifyServer) {
|
||
notifyServer->stop();
|
||
|
||
int notifyIndex = m_notificationServers.indexOf(notifyServer);
|
||
if (notifyIndex >= 0) {
|
||
m_notificationServers.removeAt(notifyIndex);
|
||
}
|
||
|
||
deletedFromDB = db->deleteNotification(port);
|
||
|
||
delete notifyServer;
|
||
}
|
||
}
|
||
|
||
ui->sgWebServers->removeRow(currentRow);
|
||
|
||
if (deletedFromDB) {
|
||
LogManager::instance()->info("uGeneral", "on_btnRmWebService_clicked",
|
||
QString("Удален веб-сервис: %1 (тип: %2, порт: %3)")
|
||
.arg(serviceName, serviceType, QString::number(port)));
|
||
QMessageBox::information(this, "Успех",
|
||
QString("Сервис '%1' успешно удален!").arg(serviceName));
|
||
} else {
|
||
LogManager::instance()->warning("uGeneral", "on_btnRmWebService_clicked",
|
||
QString("Сервис удален из интерфейса, но возникли проблемы с БД: %1")
|
||
.arg(serviceName));
|
||
QMessageBox::warning(this, "Внимание",
|
||
QString("Сервис '%1' удален из интерфейса, но не удалось удалить его из базы данных!")
|
||
.arg(serviceName));
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_sgCounters_cellClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
QString name = ui->sgCounters->item(row, 0)->text();
|
||
int value = ui->sgCounters->item(row, 1)->text().toInt();
|
||
ui->edtWordCounter->setText(name);
|
||
ui->sbStartCounter->setValue(value);
|
||
}
|
||
|
||
|
||
void uGeneral::on_sgCounters_cellDoubleClicked(int row, int column)
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCounterAdd_clicked()
|
||
{
|
||
QString name = ui->edtWordCounter->text().trimmed(); // предположим, есть поле ввода
|
||
if (name.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите название счётчика!");
|
||
return;
|
||
}
|
||
|
||
int initial = ui->sbStartCounter->value(); // спинбокс для начального значения
|
||
if (m_counterManager->addCounter(name, initial)) {
|
||
ui->edtWordCounter->clear();
|
||
ui->sbStartCounter->setValue(0);
|
||
updateCountersTable();
|
||
} else {
|
||
QMessageBox::warning(this, "Ошибка", "Не удалось добавить счётчик (возможно, уже существует)");
|
||
}
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCounterDelete_clicked()
|
||
{
|
||
int row = ui->sgCounters->currentRow();
|
||
if (row < 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите счётчик для удаления!");
|
||
return;
|
||
}
|
||
|
||
QString name = ui->sgCounters->item(row, 0)->text();
|
||
if (m_counterManager->removeCounter(name)) {
|
||
updateCountersTable();
|
||
}
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCounterEdit_clicked()
|
||
{
|
||
int row = ui->sgCounters->currentRow();
|
||
if (row < 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите счётчик для редактирования!");
|
||
return;
|
||
}
|
||
|
||
QString oldName = ui->sgCounters->item(row, 0)->text();
|
||
QString newName = ui->edtWordCounter->text().trimmed();
|
||
int newValue = ui->sbStartCounter->value();
|
||
|
||
if (newName.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите новое название счётчика!");
|
||
return;
|
||
}
|
||
|
||
if (m_counterManager->updateCounter(oldName, newName, newValue)) {
|
||
ui->edtWordCounter->clear();
|
||
ui->sbStartCounter->setValue(0);
|
||
updateCountersTable();
|
||
} else {
|
||
QMessageBox::warning(this, "Ошибка", "Не удалось обновить счётчик");
|
||
}
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCounterAtoText_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("|)" + ui->cbCounters->currentText() + "|)");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCRGet_clicked()
|
||
{
|
||
// Очистка старых данных
|
||
qDeleteAll(m_rewards);
|
||
m_rewards.clear();
|
||
|
||
// 1. Получаем ВСЕ награды канала
|
||
QVector<TCustomReward*> allRewards;
|
||
twitchAPI->getCustomRewards(allRewards, false);
|
||
|
||
// 2. Получаем только управляемые ботом
|
||
QVector<TCustomReward*> manageableRewards;
|
||
twitchAPI->getCustomRewards(manageableRewards, true);
|
||
|
||
// 3. Формируем множество ID управляемых наград
|
||
QSet<QString> manageableIds;
|
||
for (auto *r : manageableRewards) {
|
||
manageableIds.insert(r->id);
|
||
}
|
||
|
||
// 4. Освобождаем manageableRewards (они больше не нужны)
|
||
qDeleteAll(manageableRewards);
|
||
manageableRewards.clear();
|
||
|
||
// 5. Проставляем флаг для всех наград
|
||
for (auto *r : allRewards) {
|
||
r->isManagedByBroadcaster = manageableIds.contains(r->id);
|
||
}
|
||
|
||
// 6. Сохраняем в член класса
|
||
m_rewards = allRewards;
|
||
|
||
// 7. Заполняем таблицу
|
||
ui->sgCustomRewards->setRowCount(0);
|
||
ui->sgCustomRewards->setColumnCount(3);
|
||
QStringList headers = {"Название", "Цена", "Описание"};
|
||
ui->sgCustomRewards->setHorizontalHeaderLabels(headers);
|
||
ui->sgCustomRewards->setRowCount(m_rewards.size());
|
||
|
||
for (int row = 0; row < m_rewards.size(); ++row) {
|
||
TCustomReward *reward = m_rewards[row];
|
||
|
||
QTableWidgetItem *nameItem = new QTableWidgetItem(reward->title);
|
||
nameItem->setData(Qt::UserRole, reward->id);
|
||
ui->sgCustomRewards->setItem(row, 0, nameItem);
|
||
|
||
QTableWidgetItem *costItem = new QTableWidgetItem(QString::number(reward->cost));
|
||
ui->sgCustomRewards->setItem(row, 1, costItem);
|
||
|
||
QTableWidgetItem *descItem = new QTableWidgetItem(reward->prompt);
|
||
ui->sgCustomRewards->setItem(row, 2, descItem);
|
||
|
||
// Устанавливаем цвет фона
|
||
QColor bgColor = reward->isManagedByBroadcaster ? QColor(144,238,144) : QColor(255,200,200);
|
||
nameItem->setForeground(bgColor);
|
||
costItem->setForeground(bgColor);
|
||
descItem->setForeground(bgColor);
|
||
}
|
||
|
||
// Сбрасываем поля и кнопки
|
||
ui->edtCRName->clear();
|
||
ui->edtCRPrompt->clear();
|
||
ui->sbCRCost->setValue(0);
|
||
ui->btnCREdit->setEnabled(false);
|
||
ui->btnCRDelete->setEnabled(false);
|
||
}
|
||
|
||
|
||
void uGeneral::on_sgCustomRewards_cellClicked(int row, int column)
|
||
{
|
||
// Получаем ID награды из первого столбца
|
||
QTableWidgetItem *idItem = ui->sgCustomRewards->item(row, 0);
|
||
if (!idItem) return;
|
||
QString rewardId = idItem->data(Qt::UserRole).toString();
|
||
|
||
// Ищем объект награды в m_rewards по ID
|
||
TCustomReward *reward = nullptr;
|
||
for (auto *r : m_rewards) {
|
||
if (r->id == rewardId) {
|
||
reward = r;
|
||
break;
|
||
}
|
||
}
|
||
if (!reward) return; // защита от ошибок
|
||
|
||
// Заполняем поля ввода
|
||
ui->edtCRName->setText(reward->title);
|
||
ui->edtCRPrompt->setText(reward->prompt);
|
||
ui->sbCRCost->setValue(reward->cost);
|
||
|
||
// Активируем кнопки только если награда управляется ботом
|
||
bool canEdit = reward->isManagedByBroadcaster;
|
||
ui->btnCREdit->setEnabled(canEdit);
|
||
ui->btnCRDelete->setEnabled(canEdit);
|
||
}
|
||
|
||
|
||
void uGeneral::on_sgCustomRewards_cellDoubleClicked(int row, int column)
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCRAdd_clicked()
|
||
{
|
||
// 1. Получаем данные из интерфейса
|
||
QString title = ui->edtCRName->text().trimmed();
|
||
QString prompt = ui->edtCRPrompt->text().trimmed();
|
||
int cost = ui->sbCRCost->value();
|
||
|
||
// 2. Простейшая валидация
|
||
if (title.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Название награды не может быть пустым.");
|
||
return;
|
||
}
|
||
if (cost <= 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Стоимость должна быть больше 0.");
|
||
return;
|
||
}
|
||
|
||
// 3. Вызываем API для создания награды
|
||
// Предполагаем, что чекбокс "Требуется ввод пользователя" отсутствует, ставим false
|
||
TCustomReward *newReward = twitchAPI->createCustomReward(title, QString::number(cost), prompt, false);
|
||
|
||
if (!newReward) {
|
||
QMessageBox::critical(this, "Ошибка", "Не удалось создать награду. Проверьте подключение к Twitch и права токена.");
|
||
return;
|
||
}
|
||
|
||
// 4. Уведомляем пользователя об успехе
|
||
QMessageBox::information(this, "Успех", "Награда успешно создана.");
|
||
|
||
// 5. Очищаем поля ввода (опционально)
|
||
ui->edtCRName->clear();
|
||
ui->edtCRPrompt->clear();
|
||
ui->sbCRCost->setValue(0);
|
||
|
||
// 6. Обновляем список наград, чтобы новая появилась в таблице
|
||
on_btnCRGet_clicked(); // перезагружает и обновляет таблицу
|
||
|
||
// Важно: newReward был создан в куче, но после вызова on_btnCRGet_clicked()
|
||
// старые указатели будут удалены, а новые получены. Указатель newReward
|
||
// становится недействительным, поэтому не используем его дальше.
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCREdit_clicked()
|
||
{
|
||
// 1. Проверяем, что выбрана строка в таблице
|
||
int currentRow = ui->sgCustomRewards->currentRow();
|
||
if (currentRow < 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите награду для редактирования.");
|
||
return;
|
||
}
|
||
|
||
// 2. Извлекаем ID награды из первого столбца (хранится в Qt::UserRole)
|
||
QTableWidgetItem *idItem = ui->sgCustomRewards->item(currentRow, 0);
|
||
if (!idItem) return;
|
||
QString rewardId = idItem->data(Qt::UserRole).toString();
|
||
|
||
// 3. Ищем объект награды в векторе m_rewards
|
||
TCustomReward *reward = nullptr;
|
||
for (auto *r : m_rewards) {
|
||
if (r->id == rewardId) {
|
||
reward = r;
|
||
break;
|
||
}
|
||
}
|
||
if (!reward) {
|
||
QMessageBox::critical(this, "Ошибка", "Не удалось найти данные награды.");
|
||
return;
|
||
}
|
||
|
||
// 4. Проверяем, что награда управляется ботом (кнопка должна быть активна только для таких)
|
||
if (!reward->isManagedByBroadcaster) {
|
||
QMessageBox::warning(this, "Ошибка", "Нельзя редактировать награду, созданную не ботом.");
|
||
return;
|
||
}
|
||
|
||
// 5. Получаем новые значения из полей ввода
|
||
QString newTitle = ui->edtCRName->text().trimmed();
|
||
QString newPrompt = ui->edtCRPrompt->text().trimmed();
|
||
int newCost = ui->sbCRCost->value();
|
||
|
||
// 6. Валидация
|
||
if (newTitle.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Название не может быть пустым.");
|
||
return;
|
||
}
|
||
if (newCost <= 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Стоимость должна быть больше 0.");
|
||
return;
|
||
}
|
||
|
||
// 7. Обновляем данные в объекте
|
||
reward->title = newTitle;
|
||
reward->prompt = newPrompt;
|
||
reward->cost = newCost;
|
||
|
||
// 8. Вызываем API для обновления награды
|
||
twitchAPI->updateCustomReward(reward); // предполагается, что метод возвращает void
|
||
|
||
// 9. Обновляем список наград (гарантирует синхронизацию с Twitch)
|
||
on_btnCRGet_clicked();
|
||
|
||
// 10. Уведомляем пользователя об успехе
|
||
QMessageBox::information(this, "Успех", "Награда обновлена.");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnCRDelete_clicked()
|
||
{
|
||
// 1. Проверяем, что выбрана строка в таблице
|
||
int currentRow = ui->sgCustomRewards->currentRow();
|
||
if (currentRow < 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите награду для удаления.");
|
||
return;
|
||
}
|
||
|
||
// 2. Извлекаем ID награды из первого столбца
|
||
QTableWidgetItem *idItem = ui->sgCustomRewards->item(currentRow, 0);
|
||
if (!idItem) return;
|
||
QString rewardId = idItem->data(Qt::UserRole).toString();
|
||
|
||
// 3. Находим объект награды в m_rewards (для проверки управляемости)
|
||
TCustomReward *reward = nullptr;
|
||
for (auto *r : m_rewards) {
|
||
if (r->id == rewardId) {
|
||
reward = r;
|
||
break;
|
||
}
|
||
}
|
||
if (!reward) {
|
||
QMessageBox::critical(this, "Ошибка", "Не удалось найти данные награды.");
|
||
return;
|
||
}
|
||
|
||
// 4. Проверяем, что награда управляется ботом
|
||
if (!reward->isManagedByBroadcaster) {
|
||
QMessageBox::warning(this, "Ошибка", "Нельзя удалить награду, созданную не ботом.");
|
||
return;
|
||
}
|
||
|
||
// 5. Запрашиваем подтверждение
|
||
QString title = reward->title;
|
||
QMessageBox::StandardButton reply = QMessageBox::question(
|
||
this,
|
||
"Подтверждение удаления",
|
||
QString("Вы уверены, что хотите удалить награду \"%1\"?").arg(title),
|
||
QMessageBox::Yes | QMessageBox::No
|
||
);
|
||
|
||
if (reply != QMessageBox::Yes) {
|
||
return; // пользователь отменил
|
||
}
|
||
|
||
// 6. Вызываем API для удаления
|
||
bool success = twitchAPI->deleteCustomReward(rewardId); // предполагаем, что метод возвращает bool
|
||
|
||
if (!success) {
|
||
QMessageBox::critical(this, "Ошибка", "Не удалось удалить награду. Проверьте подключение к Twitch и права токена.");
|
||
return;
|
||
}
|
||
|
||
// 7. Уведомляем об успехе
|
||
QMessageBox::information(this, "Успех", "Награда успешно удалена.");
|
||
|
||
// 8. Обновляем список наград (удалённая исчезнет)
|
||
on_btnCRGet_clicked();
|
||
|
||
// 9. Сбрасываем поля ввода и отключаем кнопки (так как выбор пропал)
|
||
ui->edtCRName->clear();
|
||
ui->edtCRPrompt->clear();
|
||
ui->sbCRCost->setValue(0);
|
||
ui->btnCREdit->setEnabled(false);
|
||
ui->btnCRDelete->setEnabled(false);
|
||
}
|
||
|
||
|
||
void uGeneral::on_cbActions_currentIndexChanged(int index)
|
||
{
|
||
// 0 - ножатие кнопок
|
||
// 1 - возспроизвести звук (вызвать веб сервис для звука)
|
||
// 2 - вызвать вебсервис с параметрами
|
||
switch (index) {
|
||
case 0:
|
||
ui->gbActionsKeys->setVisible(true);
|
||
ui->gbActionsAudio->setVisible(false);
|
||
ui->gbActionsNotify->setVisible(false);
|
||
break;
|
||
case 1:
|
||
ui->gbActionsKeys->setVisible(false);
|
||
ui->gbActionsAudio->setVisible(true);
|
||
ui->gbActionsNotify->setVisible(false);
|
||
break;
|
||
case 2:
|
||
ui->gbActionsKeys->setVisible(false);
|
||
ui->gbActionsAudio->setVisible(false);
|
||
ui->gbActionsNotify->setVisible(true);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnOpenAudioFile_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtActionAudio->setText(sourceFile);
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnActionPicOpen_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Картинка (*.jpg;*.jpeg;*.png);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtActionPic->setText(sourceFile);
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnActionAudioOpen_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return;
|
||
}
|
||
ui->edtActionSound->setText(sourceFile);
|
||
|
||
}
|
||
|