From 76439727d2c876a9b52ee1135f6b83e461f2bc98 Mon Sep 17 00:00:00 2001 From: PTyTb Date: Wed, 11 Feb 2026 22:46:34 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20"?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D0=BF=D1=82=D0=B8=D0=B2=D0=BD=D1=8B=D0=B9"?= =?UTF-8?q?=20=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ugeneral.cpp | 1332 ++++++------------ ugeneral.ui | 3584 ++++++++++++++++++++++++------------------------ userwidget.cpp | 224 ++- 3 files changed, 2264 insertions(+), 2876 deletions(-) diff --git a/ugeneral.cpp b/ugeneral.cpp index 1910e5d..7c16879 100644 --- a/ugeneral.cpp +++ b/ugeneral.cpp @@ -50,11 +50,9 @@ uGeneral::uGeneral(QWidget *parent) ui->setupUi(this); setWindowTitle("TTW Bot app"); - LogSettings logSettings; logSettings.logToFile = true; logSettings.logToConsole = true; - //logSettings.maxEntries = 5000; // Цвета для разных уровней logSettings.colors[LogLevel::Info] = Qt::darkGreen; @@ -82,15 +80,11 @@ uGeneral::uGeneral(QWidget *parent) connect(logger, &LogManager::criticalAdded, this, &uGeneral::onLogEntryAdded); - // Запуск приложения - используем новый логгер LogManager::instance()->info("uGeneral", "constructor", "Главное окно создано"); // Инициализируем структуру папок FileManager::instance().initializeFolderStructure(); - // Получаем пути через FileManager - QString sysPath = FileManager::instance().systemPath(); - // Загружаем иконки tabIcons = { QIcon(FileManager::instance().getFullPath(FileManager::Icons, "settings.png")), @@ -109,16 +103,12 @@ uGeneral::uGeneral(QWidget *parent) 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]); } setupButtonIcons(); - - // Загружаем QSS файлы при создании формы loadQssFiles(); @@ -133,7 +123,7 @@ uGeneral::uGeneral(QWidget *parent) setupUI(); // ============================================================================ - // НАСТРОЙКА ТАБЛИЦ + // НАСТРОЙКА ТАБЛИЦ (с адаптивными параметрами) // ============================================================================ setupTables(); @@ -174,36 +164,23 @@ uGeneral::uGeneral(QWidget *parent) m_createNotifyDialog = new FCreateNotify(db, this); m_createChatDialog = new FCreateChat(db, this); - // Добавьте эту строку в конец метода: initializeNotificationSounds(); - - - } // ============================================================================ // ПРИВАТНЫЕ МЕТОДЫ ДЛЯ ИНИЦИАЛИЗАЦИИ // ============================================================================ - - void uGeneral::setupButtonIcons() { - // Получаем все кнопки на форме QList buttons = ui->www->findChildren(); - // Если кнопки не в centralWidget, ищем во всем окне - // QList buttons = findChildren(); - - // Проходим по всем найденным кнопкам foreach (QPushButton *button, buttons) { - QString buttonName = button->objectName().toLower(); // Приводим к нижнему регистру + QString buttonName = button->objectName().toLower(); - // Проверяем наличие ключевых слов в имени if (buttonName.contains("add")) { button->setIcon(tabIcons[9]); } else if (buttonName.contains("del") || buttonName.contains("rm")) { - // Проверяем специальный случай RmGroup (регистрозависимый) if (button->objectName().contains("RmGroup")) { button->setIcon(tabIcons[12]); } else { @@ -217,20 +194,12 @@ void uGeneral::setupButtonIcons() { button->setIcon(tabIcons[13]); } - // Устанавливаем размер иконки (опционально) if (!button->icon().isNull()) { button->setIconSize(QSize(16, 16)); } } } - - - - -/** - * @brief Инициализация базы данных - */ void uGeneral::initializeDatabase() { QString roamingPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); @@ -251,9 +220,6 @@ void uGeneral::initializeDatabase() } } -/** - * @brief Настройка пользовательского интерфейса - */ void uGeneral::setupUI() { // Настройка видимости элементов @@ -288,31 +254,27 @@ void uGeneral::setupUI() connect(ui->cbDebug, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter); } -/** - * @brief Настройка таблиц интерфейса - */ void uGeneral::setupTables() { // ======================================================================== - // НАСТРОЙКА ТАБЛИЦЫ ЛОГОВ + // НАСТРОЙКА ТАБЛИЦЫ ЛОГОВ (адаптивная) // ======================================================================== QStringList headers; headers.clear(); - headers << "Дата" << "Тип" << "Модуль" << "Метод" << "Сообщение"; + headers << "Дата" << "Тип" << "Модуль" << "Метод" << "Сообщение"; ui->sgLog->setHorizontalHeaderLabels(headers); ui->sgLog->setSelectionBehavior(QAbstractItemView::SelectRows); ui->sgLog->setSelectionMode(QAbstractItemView::SingleSelection); ui->sgLog->setEditTriggers(QAbstractItemView::NoEditTriggers); - - // Настройка ширины колонок - ui->sgLog->setColumnWidth(0, 100); // Дата - ui->sgLog->setColumnWidth(1, 100); // Тип - ui->sgLog->setColumnWidth(2, 170); // Модуль - ui->sgLog->setColumnWidth(3, 170); // Метод - ui->sgLog->setColumnWidth(4, 390); // Сообщение + 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 << "Команда" << "Ответ"; @@ -320,11 +282,12 @@ void uGeneral::setupTables() ui->sgCommands->setSelectionBehavior(QAbstractItemView::SelectRows); ui->sgCommands->setSelectionMode(QAbstractItemView::SingleSelection); ui->sgCommands->setEditTriggers(QAbstractItemView::NoEditTriggers); - ui->sgCommands->setColumnWidth(0, 135); // Команда - ui->sgCommands->setColumnWidth(1, 410); // Ответ + ui->sgCommands->setWordWrap(true); + ui->sgCommands->horizontalHeader()->setStretchLastSection(true); + ui->sgCommands->setColumnWidth(0, 135); // ======================================================================== - // НАСТРОЙКА ТАБЛИЦЫ РАНДОМНЫХ ИНТЕРВАЛОВ + // НАСТРОЙКА ТАБЛИЦЫ РАНДОМНЫХ ИНТЕРВАЛОВ (адаптивная) // ======================================================================== headers.clear(); headers << "Имя" << "От" << "До"; @@ -332,12 +295,14 @@ void uGeneral::setupTables() ui->sgRandomInt->setSelectionBehavior(QAbstractItemView::SelectRows); ui->sgRandomInt->setSelectionMode(QAbstractItemView::SingleSelection); ui->sgRandomInt->setEditTriggers(QAbstractItemView::NoEditTriggers); - ui->sgRandomInt->setColumnWidth(0, 70); // Имя - ui->sgRandomInt->setColumnWidth(1, 30); // От - ui->sgRandomInt->setColumnWidth(2, 30); // До + 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 << "Порт" << "Тип" << "Ссылка"; @@ -346,22 +311,27 @@ void uGeneral::setupTables() ui->sgWebServers->setSelectionBehavior(QAbstractItemView::SelectRows); ui->sgWebServers->setSelectionMode(QAbstractItemView::SingleSelection); ui->sgWebServers->setEditTriggers(QAbstractItemView::NoEditTriggers); - - // Настройка ширины колонок - ui->sgWebServers->setColumnWidth(0, 80); // Порт - ui->sgWebServers->setColumnWidth(1, 100); // Тип - ui->sgWebServers->setColumnWidth(2, 250); // Ссылка + 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, @@ -372,14 +342,18 @@ void uGeneral::setupTables() 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); @@ -387,93 +361,62 @@ void uGeneral::setupTables() this, &uGeneral::onFilesGridDoubleClicked); connect(ui->widget_3->tableWidget(), &QTableWidget::cellDoubleClicked, this, &uGeneral::onNeiroGridDoubleClicked); - - } -/** - * @brief Инициализация менеджеров - */ 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()) { - // GigaChat nnManager->setCurrentNetworkType(NeuralNetworkManager::GigaChat); nnManager->setGigaChatCredentials(ui->edtAIP1->text(), ui->edtAIP2->text()); } else if (ui->rbDS->isChecked()) { - // DeepSeek nnManager->setCurrentNetworkType(NeuralNetworkManager::DeepSeek); nnManager->setApiKey(NeuralNetworkManager::DeepSeek, ui->edtAIP1->text()); } else if (ui->rbCG->isChecked()) { - // ChatGPT nnManager->setCurrentNetworkType(NeuralNetworkManager::ChatGPT); nnManager->setApiKey(NeuralNetworkManager::ChatGPT, ui->edtAIP1->text()); } else if (ui->RBCustom->isChecked()) { - // Ollama 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); // Нужно создать m_TextFiles + 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_commandProcessor = new CommandProcessor(this); if (db) { - - // Загрузка команд из таблицы sgCommands в CommandProcessor loadCommandsFromTableWidget(); - - // Загрузка случайных диапазонов из таблицы sgRandomInt в RandomManager loadRandomRangesFromTableWidget(); - - // Загрузка групп случайных ответов из базы данных loadRandomResponseGroupsFromDatabase(); - - // Загрузка звуков loadSoundsFromDatabase(); - // Загрузка текстов loadTextFromDatabase(); - // Загрузка шаблонов loadTemplatesFromDatabase(); - // Настройка контекста для обработчика команд CommandProcessor::Context context; context.userManager = m_userManager; context.twitchAPI = twitchAPI; @@ -512,31 +455,25 @@ void uGeneral::loadCommandsFromTableWidget() } } -/** - * @brief Загрузка звуков - */ void uGeneral::loadSoundsFromDatabase() { - if (!db || !m_SoundFiles) return; + 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); - } - } - } + 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); + } + } + } } -/** - * @brief Загрузка текстов - */ void uGeneral::loadTextFromDatabase() { if (!db || !m_TextFiles) return; @@ -556,9 +493,6 @@ void uGeneral::loadTextFromDatabase() } } -/** - * @brief Загрузка шаблонов - */ void uGeneral::loadTemplatesFromDatabase() { if (!db || !m_neuralTemplateManager) return; @@ -577,9 +511,7 @@ void uGeneral::loadTemplatesFromDatabase() } } } -/** - * @brief Загрузка случайных диапазонов из таблицы sgRandomInt в RandomManager - */ + void uGeneral::loadRandomRangesFromTableWidget() { if (!db || !m_randomManager) return; @@ -604,33 +536,26 @@ void uGeneral::loadRandomRangesFromTableWidget() } } -/** - * @brief Загрузка групп случайных ответов из базы данных - */ void uGeneral::loadRandomResponseGroupsFromDatabase() { if (!db || !m_randomResponses) return; - // Создаем временный QListWidget для получения списка групп 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 для получения ответов группы QListWidget tempResponseList; if (!db->LoadRandomResponses(groupName, &tempResponseList)) { qWarning() << "Failed to load responses for group:" << groupName; continue; } - // Добавляем каждый ответ в RandomResponses for (int j = 0; j < tempResponseList.count(); ++j) { QString response = tempResponseList.item(j)->text(); if (!response.isEmpty()) { @@ -640,19 +565,10 @@ void uGeneral::loadRandomResponseGroupsFromDatabase() } } - - -/** - * @brief Настройка компонентов Twitch - */ void uGeneral::setupTwitchComponents() { - // ======================================================================== - // ИНИЦИАЛИЗАЦИЯ WEBSOCKET КЛИЕНТА ДЛЯ TWITCH - // ======================================================================== m_twitchClient = new WebSocketClient(this); - // Подключение сигналов WebSocket клиента connect(m_twitchClient, &WebSocketClient::onNewMessage, this, &uGeneral::handleNewMessage); connect(m_twitchClient, &WebSocketClient::onConnected, @@ -662,121 +578,89 @@ void uGeneral::setupTwitchComponents() connect(m_twitchClient, &WebSocketClient::onError, this, &uGeneral::handleError); - // ======================================================================== - // ИНИЦИАЛИЗАЦИЯ TWITCH API - // ======================================================================== twitchAPI = new TTwAPI(this); - // Подключение сигналов Twitch API connect(twitchAPI, &TTwAPI::apiError, this, &uGeneral::onApiError); connect(twitchAPI, &TTwAPI::tokenExpired, this, &uGeneral::onTokenExpired); connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit); - // Инициализация Twitch API initTwitchAPI(); } -/** - * @brief Настройка виджета пользователей - */ void uGeneral::setupUserWidget() { - // Создание виджета пользователей m_userWidget = new UserWidget(m_userManager, this); - // Настройка layout для вкладки - QVBoxLayout* layout = new QVBoxLayout(ui->tab_7); - layout->setContentsMargins(0, 0, 0, 0); // Убираем отступы + QVBoxLayout* layout = qobject_cast(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); - ui->tab_7->setLayout(layout); - // ======================================================================== - // ПОДКЛЮЧЕНИЕ СИГНАЛОВ ВИДЖЕТА ПОЛЬЗОВАТЕЛЕЙ - // ======================================================================== - - // Бан пользователя 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); - - // Обновляем статус в UserManager 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); - - // Обновляем статус в UserManager User* user = m_userManager->findUserById(userId); if (user) { user->isModerator = false; m_userManager->updateUserFromMessage(user->displayName, TwitchMessage()); } - } }); - // Назначение VIP connect(m_userWidget, &UserWidget::setVIPRequested, this, [this](const QString &userId, const QString &userName) { if (twitchAPI) { twitchAPI->setVIP(userId); - - // Обновляем статус в UserManager User* user = m_userManager->findUserById(userId); if (user) { user->isVIP = true; m_userManager->updateUserFromMessage(user->displayName, TwitchMessage()); } - } }); - // Удаление статуса VIP connect(m_userWidget, &UserWidget::removeVIPRequested, this, [this](const QString &userId, const QString &userName) { if (twitchAPI) { twitchAPI->delVIP(userId); - - // Обновляем статус в UserManager 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); @@ -806,14 +690,8 @@ void uGeneral::setupUserWidget() }); } -/** - * @brief Настройка обработчиков авторизации - */ void uGeneral::setupAuthHandlers() { - // ======================================================================== - // НАСТРОЙКА АВТОРИЗАЦИИ БОТА - // ======================================================================== m_authBot = new TAuth(this); connect(m_authBot, &TAuth::tokenReceived, this, &uGeneral::onTokenReceived); connect(m_authBot, &TAuth::errorOccurred, this, &uGeneral::onAuthError); @@ -821,9 +699,6 @@ void uGeneral::setupAuthHandlers() 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); @@ -831,9 +706,6 @@ void uGeneral::setupAuthHandlers() LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен"); }); - // ======================================================================== - // НАСТРОЙКА АВТОРИЗАЦИИ DA - // ======================================================================== m_authDA = new TAuth(this); connect(m_authDA, &TAuth::codeReceived, this, &uGeneral::onTokenReceived3); connect(m_authDA, &TAuth::errorOccurred, this, &uGeneral::onAuthError); @@ -842,7 +714,6 @@ void uGeneral::setupAuthHandlers() }); } - uGeneral::~uGeneral() { for (HttpServer *server : m_notificationServers) { @@ -882,26 +753,14 @@ uGeneral::~uGeneral() delete m_authDA; } - - // ============================================================================ -// ОТПРАВКА СООБЩЕНИЙ +// ОСТАЛЬНЫЕ МЕТОДЫ (без изменений, кроме адаптивных настроек выше) // ============================================================================ - - - - -// ============================================================================ -// УПРАВЛЕНИЕ СЕРВЕРАМИ -// ============================================================================ - - void uGeneral::setTwitchConnected(bool connected) { m_isTwitchConnected = connected; - // Если подключение установлено, запускаем активные таймеры if (connected) { for (TimerInfo& timer : m_timers) { if (timer.isActive && !timer.timer) { @@ -909,15 +768,12 @@ void uGeneral::setTwitchConnected(bool connected) } } } else { - // Если подключение разорвано, останавливаем все таймеры for (TimerInfo& timer : m_timers) { if (timer.timer) { stopTimer(timer); } } } - - } void uGeneral::loadEmoties() @@ -942,8 +798,6 @@ void uGeneral::loadChatBadges() QVector customBadges; twitchAPI->getCustomChatBadges(customBadges); m_chatBadges.append(customBadges); - - } void uGeneral::updateTimerTable() { @@ -953,29 +807,23 @@ void uGeneral::updateTimerTable() { 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) { - // Не запускаем таймер, если нет подключения к Twitch if (!m_isTwitchConnected) { return; } @@ -1002,26 +850,21 @@ void uGeneral::stopTimer(TimerInfo& timerInfo) { } void uGeneral::sendTimedMessage(const TimerInfo& timerInfo) { - // Не отправляем сообщение, если нет подключения if (!m_isTwitchConnected) { - return; } if (timerInfo.isAnnouncement) { - // Отправка через API как оповещение 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(); @@ -1043,7 +886,7 @@ void uGeneral::on_btnTimerAdd_clicked() newTimer.id = m_nextTimerId++; newTimer.message = message; newTimer.interval = interval; - newTimer.isActive = true; // По умолчанию включен + newTimer.isActive = true; newTimer.isAnnouncement = false; newTimer.timer = nullptr; @@ -1055,10 +898,9 @@ void uGeneral::on_btnTimerAdd_clicked() updateTimerTable(); -db->SaveTimers(ui->sgTimers, m_timers); + db->SaveTimers(ui->sgTimers, m_timers); ui->edtTimerMessage->clear(); ui->edtTimerInterval->setText("10"); - } void uGeneral::on_btnTimerEdit_clicked() { @@ -1093,12 +935,12 @@ void uGeneral::on_btnTimerEdit_clicked() { timer.message = message; timer.interval = interval; - if (wasActive && m_isTwitchConnected) { // Добавлена проверка подключения + if (wasActive && m_isTwitchConnected) { startTimer(timer); } updateTimerTable(); -db->SaveTimers(ui->sgTimers, m_timers); + db->SaveTimers(ui->sgTimers, m_timers); } void uGeneral::on_btnTimerDelete_clicked() { @@ -1133,14 +975,13 @@ void uGeneral::on_sgTimers_cellClicked(int row, int column) TimerInfo& timer = m_timers[row]; - if (column == 0) { // Чекбокс "Вкл" + 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]); } @@ -1150,7 +991,7 @@ void uGeneral::on_sgTimers_cellClicked(int row, int column) db->SaveTimers(ui->sgTimers, m_timers); } } - else if (column == 3) { // Чекбокс "О" + else if (column == 3) { bool newState = ui->sgTimers->item(row, column)->checkState() == Qt::Checked; if (newState != timer.isAnnouncement) { @@ -1162,7 +1003,7 @@ void uGeneral::on_sgTimers_cellClicked(int row, int column) void uGeneral::onTimerTimeout(int timerId) { - + Q_UNUSED(timerId); } void uGeneral::on_sgTimers_cellDoubleClicked(int row, int column) { @@ -1174,7 +1015,6 @@ void uGeneral::on_sgTimers_cellDoubleClicked(int row, int column) { ui->edtTimerInterval->setText(QString::number(timer.interval)); } - void uGeneral::sendRequestToAI(const QString &q) { NeuralNetworkManager::NetworkType type; @@ -1182,8 +1022,8 @@ void uGeneral::sendRequestToAI(const QString &q) { type = NeuralNetworkManager::GigaChat; nnManager->setGigaChatCredentials( - ui->edtAIP1->text(), // RqUID - ui->edtAIP2->text() // Basic авторизация в base64 + ui->edtAIP1->text(), + ui->edtAIP2->text() ); } if (ui->rbCG->isChecked()) @@ -1200,29 +1040,26 @@ void uGeneral::sendRequestToAI(const QString &q) } nnManager->setApiKey(type, ui->edtAIP1->text()); nnManager->setPrefix(ui->edtGPTPrefix->text()); - // Настройка Ollama nnManager->setOllamaUrl(ui->edtAIP2->text()); - // Отправляем вопрос nnManager->sendMessage(q, type); } void uGeneral::onSoundGridDoubleClicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + 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(); + 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(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("<|" + ui->widget_3->tableWidget()->item(ui->widget_3->tableWidget()->currentRow(), 0)->text() + "<|"); } @@ -1241,9 +1078,7 @@ void uGeneral::initTwitchAPI() return; } - // Инициализируем только если не инициализировали ранее if (twitchAPI) { - // Если уже инициализировано, можно обновить параметры delete twitchAPI; } @@ -1264,7 +1099,6 @@ void uGeneral::initTwitchAPI() LogManager::instance()->info("uGeneral", "initTwitchAPI", "TTwAPI успешно инициализировано"); } - void uGeneral::onApiError(const QString &method, const QString &error) { LogManager::instance()->error("ttw_api", method, error); @@ -1272,7 +1106,7 @@ void uGeneral::onApiError(const QString &method, const QString &error) void uGeneral::onTokenExpired(const QString &error) { - LogManager::instance()->error("uGeneral", "onTokenExpired", error); + LogManager::instance()->error("uGeneral", "onTokenExpired", error); } void uGeneral::onRateLimit() @@ -1285,14 +1119,9 @@ UserManager* uGeneral::getUserManager() return m_userManager; } - -// Обработчик ответа от AI void uGeneral::onAIResponseReceived(const QString &response) { - - // Отправляем ответ в чат if (m_twitchClient) { - // Обрезаем ответ, если он слишком длинный для Twitch QString chatResponse = response; if (chatResponse.length() > 400) { chatResponse = chatResponse.left(397) + "..."; @@ -1301,34 +1130,29 @@ void uGeneral::onAIResponseReceived(const QString &response) } } -// Обработчик ошибок AI void uGeneral::onAIErrorOccurred(const QString &error) { - LogManager::instance()->error("General", "onAIErrorOccurred", error); + LogManager::instance()->error("General", "onAIErrorOccurred", error); - // Можно отправить сообщение об ошибке в чат if (m_twitchClient) { m_twitchClient->sendChatMessage(ui->edtChannel->text(), "Извините, нейросеть временно недоступна"); } } - - void uGeneral::handleConnected() { - - LogManager::instance()->info("General", "handleConnected", "Connectiong"); + LogManager::instance()->info("General", "handleConnected", "Connected"); setTwitchConnected(true); loadChatBadges(); - // Запускаем активные таймеры, если подключены к Twitch + if (m_isTwitchConnected) { for (TimerInfo& timer : m_timers) { if (timer.isActive && !timer.timer) { startTimer(timer); } } - LogManager::instance()->info("General", "handleConnected", "Connected"); + LogManager::instance()->info("General", "handleConnected", "Connected"); } } @@ -1336,7 +1160,7 @@ void uGeneral::handleDisconnected() { LogManager::instance()->info("General", "handleDisconnected", "Disconnected"); setTwitchConnected(false); - // Запускаем активные таймеры, если подключены к Twitch + if (m_isTwitchConnected) { for (TimerInfo& timer : m_timers) { if (timer.isActive && !timer.timer) { @@ -1349,31 +1173,25 @@ void uGeneral::handleDisconnected() 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(); - + 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()); } @@ -1394,7 +1212,7 @@ bool uGeneral::shouldShowLogEntry(LogLevel level) case LogLevel::Debug: return ui->cbDebug->isChecked(); case LogLevel::Critical: - return true; // Критические ошибки всегда показываем + return true; default: return true; } @@ -1402,32 +1220,25 @@ bool uGeneral::shouldShowLogEntry(LogLevel level) void uGeneral::addLogToTable(const LogEntry &entry, int row) { - // Добавляем новую строку ui->sgLog->insertRow(row); - // Получаем цвет для уровня из настроек LogManager LogSettings settings = LogManager::instance()->settings(); QColor color = settings.colors.value(entry.level, LogEntry::levelToColor(entry.level)); - // Колонка 0: Дата QTableWidgetItem* timeItem = new QTableWidgetItem( entry.timestamp.toString("hh:mm:ss")); timeItem->setForeground(color); - // Колонка 1: Тип QTableWidgetItem* typeItem = new QTableWidgetItem( LogEntry::levelToString(entry.level)); typeItem->setForeground(color); - // Колонка 2: Модуль QTableWidgetItem* moduleItem = new QTableWidgetItem(entry.module); moduleItem->setForeground(color); - // Колонка 3: Метод QTableWidgetItem* methodItem = new QTableWidgetItem(entry.method); methodItem->setForeground(color); - // Колонка 4: Сообщение QTableWidgetItem* messageItem = new QTableWidgetItem(entry.message); messageItem->setForeground(color); @@ -1442,13 +1253,10 @@ void uGeneral::addLogToTable(const LogEntry &entry, int row) void uGeneral::applyLogFilter() { - // Очищаем таблицу ui->sgLog->setRowCount(0); - // Получаем все записи из LogManager QList allEntries = LogManager::instance()->allEntries(); - // Добавляем только те записи, которые соответствуют фильтру for (const LogEntry &entry : allEntries) { if (shouldShowLogEntry(entry.level)) { addLogToTable(entry, ui->sgLog->rowCount()); @@ -1457,7 +1265,6 @@ void uGeneral::applyLogFilter() } 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(),"")); @@ -1465,14 +1272,12 @@ void uGeneral::loadSettings(){ 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(),"")); @@ -1507,7 +1312,6 @@ void uGeneral::loadSettings(){ 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(),"")); @@ -1527,7 +1331,6 @@ void uGeneral::loadSettings(){ db->LoadTableWidget(ui->sgCommands); db->LoadTableWidget(ui->sgRandomInt); - FSingleGrid *form = ui->widget; form->setDatabase(db); QTableWidget *table = form->findChild("sgSounds"); @@ -1548,7 +1351,6 @@ void uGeneral::loadSettings(){ loadSavedNotifications(); } - void uGeneral::on_btnGetTockenBot_clicked() { QString clientId = ui->edtBotClientID->text(); @@ -1579,38 +1381,31 @@ void uGeneral::on_btnGetTockenBot_clicked() .arg(clientId) .arg(scope); - // Создаем и показываем окно с ссылкой if (!fLinkForm) { fLinkForm = new uLink(this); } - // Устанавливаем ссылку в окно fLinkForm->setLinkText(authUrl); fLinkForm->show(); - // Запускаем сервер без открытия браузера (false) m_authBot->startServer(authUrl, false); - } - void uGeneral::on_btnOpenFolderSettings_clicked() { - QString roamingPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QUrl url = QUrl::fromLocalFile(roamingPath); - QDesktopServices::openUrl(url); + 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; } - // Для стримера могут быть другие scope, если нужно QString scope = "channel:manage:broadcast+" "channel:read:subscriptions+" "channel:read:redemptions+" @@ -1626,44 +1421,35 @@ void uGeneral::on_btnGetTokenStreamer_clicked() .arg(clientId) .arg(scope); - // Запускаем сервер с открытием браузера (true) 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() @@ -1676,7 +1462,7 @@ void uGeneral::on_btnDAGetCode_clicked() QMessageBox::warning(this, "Ошибка", "Заполните все поля для DonationAlerts"); return; } -QString encodedRedirectUri = QString::fromUtf8(QUrl::toPercentEncoding(redirectUri)); + QString encodedRedirectUri = QString::fromUtf8(QUrl::toPercentEncoding(redirectUri)); QString authUrl = QString("https://www.donationalerts.com/oauth/authorize?" "client_id=%1&" "redirect_uri=%2&" @@ -1685,43 +1471,37 @@ QString encodedRedirectUri = QString::fromUtf8(QUrl::toPercentEncoding(redirectU .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->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->lblAPI2->setText("URL"); + ui->lblAPI2->setVisible(true); + ui->edtAIP2->setVisible(true); - ui->lblAPI3->setText(""); - ui->lblAPI3->setVisible(false); - ui->edtAIP3->setVisible(false); + 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); + ui->cbOllama->setVisible(true); + ui->edtAIP1->setEchoMode(QLineEdit::Password); + ui->edtAIP2->setEchoMode(QLineEdit::Normal); + ui->edtAIP3->setEchoMode(QLineEdit::Password); } void uGeneral::on_rbGC_clicked() @@ -1746,7 +1526,6 @@ void uGeneral::on_rbGC_clicked() void uGeneral::on_rbDS_clicked() { - ui->lblAPI1->setText("API Token"); ui->lblAPI1->setVisible(true); ui->edtAIP1->setVisible(true); @@ -1787,10 +1566,10 @@ void uGeneral::on_rbCG_clicked() 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); + 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() @@ -1801,9 +1580,6 @@ void uGeneral::on_btnNotifyCheckMod_clicked() soundManager->playSound(SoundManager::Channel2); } - - - void uGeneral::on_btnNotifyCheckVip_clicked() { QString fn = ui->edtNotifyFileNameVip->text(); @@ -1828,7 +1604,7 @@ void uGeneral::on_btnNotifyOpen_clicked() "Звуковой файл (*.mp3);;Все файлы (*.*)"); if (sourceFile.isEmpty()) { - return; // Пользователь отменил выбор + return; } ui->edtNotifyFileName->setText(sourceFile); } @@ -1836,388 +1612,324 @@ void uGeneral::on_btnNotifyOpen_clicked() void uGeneral::on_btnNotifyOpenMod_clicked() { QString sourceFile = QFileDialog::getOpenFileName(this, - "Выберите файл для уведомлений", - QDir::homePath(), - "Звуковой файл (*.mp3);;Все файлы (*.*)"); + "Выберите файл для уведомлений", + QDir::homePath(), + "Звуковой файл (*.mp3);;Все файлы (*.*)"); - if (sourceFile.isEmpty()) { - return; // Пользователь отменил выбор - } + if (sourceFile.isEmpty()) { + return; + } ui->edtNotifyFileNameMod->setText(sourceFile); } void uGeneral::on_btnNotifyOpenVip_clicked() { QString sourceFile = QFileDialog::getOpenFileName(this, - "Выберите файл для уведомлений", - QDir::homePath(), - "Звуковой файл (*.mp3);;Все файлы (*.*)"); + "Выберите файл для уведомлений", + QDir::homePath(), + "Звуковой файл (*.mp3);;Все файлы (*.*)"); - if (sourceFile.isEmpty()) { - return; // Пользователь отменил выбор - } + if (sourceFile.isEmpty()) { + return; + } ui->edtNotifyFileNameVip->setText(sourceFile); } void uGeneral::on_btnNotifyOpenSub_clicked() { QString sourceFile = QFileDialog::getOpenFileName(this, - "Выберите файл для уведомлений", - QDir::homePath(), - "Звуковой файл (*.mp3);;Все файлы (*.*)"); + "Выберите файл для уведомлений", + QDir::homePath(), + "Звуковой файл (*.mp3);;Все файлы (*.*)"); - if (sourceFile.isEmpty()) { - return; // Пользователь отменил выбор - } + if (sourceFile.isEmpty()) { + return; + } ui->edtNotifyFileNameSub->setText(sourceFile); } - void uGeneral::playNotify(const bool &isMod, const bool &isVip, const bool &isSub) +void uGeneral::playNotify(const bool &isMod, const bool &isVip, const bool &isSub) +{ + if (isMod && ui->chEnNotifyMod->isChecked() && + !ui->edtNotifyFileNameMod->text().isEmpty()) { - // Проверяем модератора (самый высокий приоритет) - 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()) { - 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); - } else { - - } - return; - } - } - - // Проверяем VIP (второй приоритет) - 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); - } else { - - } - 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); - } else { - - } - 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->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeMod->value()); soundManager->playSound(SoundManager::Channel2); - } else { - } return; } } - /** - * @brief Инициализирует звуковые уведомления - */ - void uGeneral::initializeNotificationSounds() + if (isVip && ui->chEnNotifyVip->isChecked() && + !ui->edtNotifyFileNameVip->text().isEmpty()) { - // Загружаем звук для обычных уведомлений - QString notifyFile = ui->edtNotifyFileName->text(); - if (!notifyFile.isEmpty() && QFile::exists(notifyFile)) { - soundManager->loadSound(SoundManager::Channel2, notifyFile); + 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()); - - } else { - + soundManager->playSound(SoundManager::Channel2); } + return; + } +} - // Загружаем звук для модераторов - QString notifyModFile = ui->edtNotifyFileNameMod->text(); - if (!notifyModFile.isEmpty() && QFile::exists(notifyModFile)) { - // Загружаем в отдельный канал, если SoundManager поддерживает несколько каналов - // Или используем ту же логику, что и в playNotify +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)) { + // загружается по необходимости + } - // Загружаем звук для VIP - QString notifyVipFile = ui->edtNotifyFileNameVip->text(); - if (!notifyVipFile.isEmpty() && QFile::exists(notifyVipFile)) { + QString notifyVipFile = ui->edtNotifyFileNameVip->text(); + if (!notifyVipFile.isEmpty() && QFile::exists(notifyVipFile)) { + } - } + QString notifySubFile = ui->edtNotifyFileNameSub->text(); + if (!notifySubFile.isEmpty() && QFile::exists(notifySubFile)) { + } +} - // Загружаем звук для подписчиков - 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(); + } + playNotify(msg.isMod, msg.isVIP, msg.isSubscriber); + + QString processedMessage = processTwitchMessage(msg); + QString formattedNickname = formatNicknameWithBadges(msg); + + addChatMessage(formattedNickname, processedMessage); + + 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); + } +} + +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 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("\"%2\"") + .arg(emoteUrl) + .arg(message.mid(start, end - start + 1)); + + replacements.append({start, end, emoteHtml}); } } - void uGeneral::handleNewMessage( const QString &message){ - TwitchMessage msg = TwitchMessage::parse(message); + std::sort(replacements.begin(), replacements.end(), + [](const Replacement &a, const Replacement &b) { + return a.start > b.start; + }); - // Проверяем и регистрируем пользователя - QString userId = m_userManager->checkUser(msg.displayName, msg); - LogManager::instance()->debug("uGeneral", "handleNewMessage", message); - m_userManager->m_totalMessages++; - - // Обновляем статистику и воспроизводим уведомление - if (m_userWidget) { - m_userWidget->updateStatistics(); - } - playNotify(msg.isMod, msg.isVIP, msg.isSubscriber); - - // Обрабатываем сообщение Twitch (смайлы, бейджи и т.д.) - QString processedMessage = processTwitchMessage(msg); - QString formattedNickname = formatNicknameWithBadges(msg); - - // Добавляем сообщение в чат - addChatMessage(formattedNickname, processedMessage); - - // Обрабатываем команды если сообщение начинается с "!" - 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); - } else { - // execClearMessage(msg.message); - } + for (const auto &replacement : replacements) { + result.replace(replacement.start, + replacement.end - replacement.start + 1, + replacement.html); } - /** - * @brief Обработка команды от пользователя - */ - 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; - - // Генерируем ответ через CommandProcessor - QString response = m_commandProcessor->generateResponse(username, command, parameters); - - // Если есть ответ, отправляем его в чат - if (!response.isEmpty()) { - sendChatResponse(response); - } - } - - /** - * @brief Отправка ответа команды в чат - */ - void uGeneral::sendChatResponse(const QString &response) - { - m_twitchClient->sendChatMessage(ui->edtChannel->text(), response); - } - - void uGeneral::handleError(const QString &errorMessage){ - } - - // Метод для обработки Twitch сообщения с эмодзи - QString uGeneral::processTwitchMessage(const TwitchMessage &msg) - { - QString processedMessage = msg.message; - - // 1. Обрабатываем Twitch эмодзи (из поля emotes) - if (!msg.emotes.isEmpty()) { - processedMessage = replaceTwitchEmotes(msg.emotes, processedMessage); - } - - // 2. Обрабатываем BTTV и 7TV эмодзи - processedMessage = replaceCustomEmotes(processedMessage); - - return processedMessage; - } - - // Метод для замены Twitch эмодзи на HTML изображения - QString uGeneral::replaceTwitchEmotes(const QString &emotesData, const QString &message) - { - if (emotesData.isEmpty()) return message; - - QString result = message; - - // Формат emotesData: "emote_id:start-end,start-end/emote_id2:start-end" - QStringList emoteList = emotesData.split('/', Qt::SkipEmptyParts); - - // Создаем структуру для хранения замен - struct Replacement { - int start; - int end; - QString html; - }; - - QList 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; - - // Формируем HTML для эмодзи - QString emoteUrl = QString("https://static-cdn.jtvnw.net/emoticons/v2/%1/default/dark/1.0").arg(emoteId); - QString emoteHtml = QString("\"%2\"") - .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; - } + return result; +} QString uGeneral::replaceCustomEmotes(const QString &message) { - QString result = message; + QString result = message; - // Используем регулярное выражение для поиска слов - QRegularExpression wordRegex("([a-zA-Z0-9_-]+)"); - QRegularExpressionMatchIterator matches = wordRegex.globalMatch(message); + QRegularExpression wordRegex("([a-zA-Z0-9_-]+)"); + QRegularExpressionMatchIterator matches = wordRegex.globalMatch(message); - // Структура для хранения замен - struct EmoteReplacement { - int start; - int length; - QString html; - }; + struct EmoteReplacement { + int start; + int length; + QString html; + }; - QList replacements; + QList replacements; - while (matches.hasNext()) { - QRegularExpressionMatch match = matches.next(); - QString word = match.captured(1); - int start = match.capturedStart(1); - int length = match.capturedLength(1); + while (matches.hasNext()) { + QRegularExpressionMatch match = matches.next(); + QString word = match.captured(1); + int start = match.capturedStart(1); + int length = match.capturedLength(1); - // Пропускаем слишком короткие слова (меньше 2 символов) - if (word.length() < 2) continue; + if (word.length() < 2) continue; - // Проверяем BTTV эмодзи - QString bttvUrl = bttvProvider.getEmoteUrl(word); - if (!bttvUrl.isEmpty()) { - QString emoteHtml = QString("\"%2\"") - .arg(bttvUrl) - .arg(word); - replacements.append({start, length, emoteHtml}); - continue; - } - - // Проверяем 7TV эмодзи - QString sevenTVUrl = sevenTVProvider.getEmoteUrl(word); - if (!sevenTVUrl.isEmpty()) { - QString emoteHtml = QString("\"%2\"") - .arg(sevenTVUrl) - .arg(word); - replacements.append({start, length, emoteHtml}); - continue; - } + QString bttvUrl = bttvProvider.getEmoteUrl(word); + if (!bttvUrl.isEmpty()) { + QString emoteHtml = QString("\"%2\"") + .arg(bttvUrl) + .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); + QString sevenTVUrl = sevenTVProvider.getEmoteUrl(word); + if (!sevenTVUrl.isEmpty()) { + QString emoteHtml = QString("\"%2\"") + .arg(sevenTVUrl) + .arg(word); + replacements.append({start, length, emoteHtml}); + continue; } + } - return result; + 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(); } @@ -2229,27 +1941,22 @@ void uGeneral::connectToTwitch() QString botToken = ui->edtBotToken->text(); QString streamerToken = ui->edtBotTokenStreamer->text(); - // Проверка токенов if (!twitchAPI->validateTwitchToken("Бот", botToken, botTokenDays)) { - return; } if (!twitchAPI->validateTwitchToken("Стример", streamerToken, streamerTokenDays)) { - return; } - // Подключение к чату Twitch 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)); @@ -2265,30 +1972,22 @@ void uGeneral::disconnectFromTwitch() setTwitchConnected(false); } - void uGeneral::execCommand(const QString &sender, const QString &message) { LogManager::instance()->debug("uGeneral", "execCommand", QString("Обработка команды от %1: %2").arg(sender).arg(message)); - - } - - void uGeneral::on_lbRandomGroup_itemClicked(QListWidgetItem *item) { - db->LoadRandomResponses(item->text(), ui->lbRandomRespons); + db->LoadRandomResponses(item->text(), ui->lbRandomRespons); } - - void uGeneral::on_pushButton_clicked() { - LogManager::instance()->clear(); + LogManager::instance()->clear(); } - void uGeneral::on_sgCommands_cellDoubleClicked(int row, int column) { Q_UNUSED(column); @@ -2296,22 +1995,18 @@ void uGeneral::on_sgCommands_cellDoubleClicked(int row, int column) 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, "Внимание", "Значение 'От' не может быть больше значения 'До'!"); @@ -2322,60 +2017,50 @@ void uGeneral::on_btnRandAdd_clicked() 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(); + 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(); + 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; @@ -2386,37 +2071,30 @@ void uGeneral::on_btnRandomAdd_clicked() return; } - // Добавляем ответ в базу данных if (!db->AddGroupResponse(groupName, response)) { QMessageBox::critical(this, "Ошибка", "Не удалось добавить ответ в базу данных:\n" + db->lastError()); return; } - // Обновляем список групп (если новая группа) db->LoadRandomGroups(ui->lbRandomGroup); - // Находим и выбираем только что добавленную группу в списке QList 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()); @@ -2424,7 +2102,6 @@ void uGeneral::on_btnRandomDel_clicked() m_randomResponses->removeResponse(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons->currentItem()->text()); } - void uGeneral::on_btnRmGroup_clicked() { db->DeleteGroup(ui->lbRandomGroup->currentItem()->text()); @@ -2434,46 +2111,40 @@ void uGeneral::on_btnRmGroup_clicked() void uGeneral::on_btnGetDateFollow_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("[FOLLOW]"); } - void uGeneral::on_btnGetAgeAccaunt_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("[AGE]"); } - void uGeneral::on_btnGPT_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("[AI]"); } - void uGeneral::on_btnAIPic_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("[Kandinsky]"); } - void uGeneral::on_btnRandomUserName_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("[RANDOMUSER]"); } - void uGeneral::on_btnGetChannelStat_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + QTextCursor cursor = ui->textBrowser->textCursor(); cursor.insertText("[STAT]"); } - void uGeneral::on_btnAddCommand_clicked() { QString name = ui->edtCommand->text().trimmed(); @@ -2486,23 +2157,17 @@ void uGeneral::on_btnAddCommand_clicked() return; } - // Просто добавляем в таблицу и сохраняем в БД - // CommandProcessor будет читать из таблицы при обработке - - // Добавляем новую строку в таблицу 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()) { @@ -2522,7 +2187,6 @@ void uGeneral::on_btnRmCommand_clicked() QString("Удалена команда: %1").arg(commandName)); } - void uGeneral::on_btnEdtCommand_clicked() { if (!ui->sgCommands->currentItem()) { @@ -2551,12 +2215,9 @@ void uGeneral::on_btnEdtCommand_clicked() void uGeneral::loadQssFiles() { - // Очищаем ComboBox ui->cbTheme->clear(); - - // Добавляем опцию "Без темы" ui->cbTheme->addItem("Без темы", ""); - // 1. Загружаем системные стили (из папки приложения) + QString systemStylesPath = FileManager::instance().getPath(FileManager::SystemStyles); QDir systemStylesDir(systemStylesPath); @@ -2564,12 +2225,9 @@ void uGeneral::loadQssFiles() loadStylesFromDirectory(systemStylesDir, "Системные"); } else { qWarning() << "Папка системных стилей не найдена:" << systemStylesPath; - - // Создаем папку, если ее нет systemStylesDir.mkpath("."); } - // 2. Загружаем пользовательские стили (из папки данных пользователя) QString userStylesPath = FileManager::instance().getPath(FileManager::Styles); QDir userStylesDir(userStylesPath); @@ -2577,66 +2235,50 @@ void uGeneral::loadQssFiles() loadStylesFromDirectory(userStylesDir, "Пользовательские"); } else { qWarning() << "Папка пользовательских стилей не найдена:" << userStylesPath; - - // Создаем папку userStylesDir.mkpath("."); - - } - // Проверяем, есть ли темы - if (ui->cbTheme->count() <= 1) { // Только "Без темы" + if (ui->cbTheme->count() <= 1) { qWarning() << "Темы не найдены, создаем базовые"; - } } - - void uGeneral::loadStylesFromDirectory(const QDir &stylesDir, const QString &category) { if (!stylesDir.exists()) { return; } - // Получаем список QSS файлов в папке QStringList filters; filters << "*.qss" << "*.QSS"; QStringList qssFiles = stylesDir.entryList(filters, QDir::Files); - // Добавляем файлы в ComboBox 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; - // Получаем полный путь к файлу из userData QString filePath = ui->cbTheme->itemData(index).toString(); - // Если выбрана "Без темы" (пустая строка в userData) 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; @@ -2647,19 +2289,15 @@ void uGeneral::applyStyleSheet(const QString &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); @@ -2667,25 +2305,18 @@ void uGeneral::on_btnWSCreateChat_clicked() 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->setAttribute(Qt::WA_DeleteOnClose); createDialog->exec(); } - - -// Слоты для приема созданных серверов: void uGeneral::onChatServerCreated(HttpServerChat *server, const QString &name) { if (server) { - // Устанавливаем родителя - главное окно server->setParent(this); - addChatServer(server, name); } } @@ -2694,15 +2325,11 @@ 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); } @@ -2711,7 +2338,6 @@ 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); @@ -2724,26 +2350,19 @@ void uGeneral::onNotifyServerUpdated(HttpServer *server, const QString &name) } } - // Обновляем сервер в базе данных 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() == 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; @@ -2751,7 +2370,6 @@ void uGeneral::onNotifyServerUpdated(HttpServer *server, const QString &name) } } -// Методы добавления серверов: void uGeneral::addNotificationServer(HttpServer *server, const QString &name) { if (!server) return; @@ -2784,40 +2402,29 @@ void uGeneral::addServerToTable(const QString &name, const QString &type, int row = ui->sgWebServers->rowCount(); ui->sgWebServers->insertRow(row); - // Колонка 0: Название QTableWidgetItem *nameItem = new QTableWidgetItem(name); nameItem->setData(Qt::UserRole, QVariant::fromValue(serverObj)); ui->sgWebServers->setItem(row, 0, nameItem); - - // Колонка 1: Тип ui->sgWebServers->setItem(row, 1, new QTableWidgetItem(type)); - - // Колонка 2: Порт ui->sgWebServers->setItem(row, 2, new QTableWidgetItem(QString::number(port))); - - // Колонка 3: Ссылка (редактируемая для копирования) 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; } - // Берем первый активный сервер (или можно выбрать по ID) 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; @@ -2830,7 +2437,6 @@ void uGeneral::addNotification(const QString &nickname, double amount, const QSt notif.contentSize = 16; notif.pageBackgroundColor = "transparent"; - // Можно добавить звук для доната if (type == "donation") { notif.soundURL = "/sounds/donation.mp3"; } @@ -2850,15 +2456,12 @@ void uGeneral::addChatMessage(const QString &nickname, for (HttpServerChat *server : qAsConst(m_chatServers)) { sendMessageToServer(server, formattedNickname, message); } - } -// Метод для получения HTML бейджей QString uGeneral::getBadgesHTML(const TwitchMessage &msg) { QString badgesHtml; - // Если у сообщения есть бейджи for (const TwitchMessage::Badge &badge : msg.badges) { QString badgeUrl = getBadgeUrl(badge.name, badge.version); @@ -2872,7 +2475,6 @@ QString uGeneral::getBadgesHTML(const TwitchMessage &msg) } } - // Добавляем системные бейджи, если их нет в списке auto addSystemBadge = [&](const QString &badgeName, bool condition) { if (condition) { bool alreadyHas = false; @@ -2905,29 +2507,23 @@ QString uGeneral::getBadgesHTML(const TwitchMessage &msg) } return badgesHtml; - } -// Метод для получения URL бейджа 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; // или version.imageUrl2x для ретины + return version.imageUrl1x; } } - - // Если точной версии не нашли, возвращаем первую версию if (!badge.versions.isEmpty()) { return badge.versions.first().imageUrl1x; } } } - // Стандартные Twitch бейджи (если не нашли в загруженных) static const QMap 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"}, @@ -2944,28 +2540,23 @@ QString uGeneral::getBadgeUrl(const QString &setId, int versionId) return defaultBadgeUrls[setId].arg(versionId); } - // Для subscriber бейджей с уровнями 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); } - // Для bits бейджей с уровнями 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); } - // Пытаемся получить URL из стандартного API 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); - // Экранируем HTML-символы QString escapedName = msg.displayName.toHtmlEscaped(); QString escapedColor = msg.color.toHtmlEscaped(); @@ -2980,20 +2571,15 @@ QString uGeneral::formatNicknameWithBadges(const TwitchMessage &msg) 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(); @@ -3004,15 +2590,12 @@ void uGeneral::sendMessageToServer(HttpServerChat *server, const QString &nickna style.timeMsg = server->getMessageTimeout(); style.bColor = server->getBackgroundColor(); style.transparency = server->getTransparency(); - // Убираем HTML теги из обычного текста для совместимости - // (если сервер их не поддерживает, можно оставить только для ника) - style.nick = nickname; // Ник уже с цветом - style.context = message; // Сообщение уже обработано с эмодзи + style.nick = nickname; + style.context = message; server->addMessage(style); } -// Обработчик двойного клика по таблице веб-серверов void uGeneral::on_sgWebServers_cellDoubleClicked(int row, int column) { Q_UNUSED(column); @@ -3031,7 +2614,6 @@ void uGeneral::on_sgWebServers_cellDoubleClicked(int row, int column) QString serverType = typeItem->text(); if (serverType.toLower() == "чат") { - // Получаем объект сервера из user data QObject *serverObj = nameItem->data(Qt::UserRole).value(); HttpServerChat *existingServer = qobject_cast(serverObj); @@ -3043,7 +2625,6 @@ void uGeneral::on_sgWebServers_cellDoubleClicked(int row, int column) createChatDialog->exec(); } } else if (serverType.toLower() == "уведомления") { - // Получаем объект сервера из user data QObject *serverObj = nameItem->data(Qt::UserRole).value(); HttpServer *existingServer = qobject_cast(serverObj); @@ -3064,10 +2645,8 @@ void uGeneral::loadSavedNotifications() 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); @@ -3085,18 +2664,13 @@ void uGeneral::loadSavedNotifications() ); 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; - } } } @@ -3106,22 +2680,15 @@ 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; @@ -3132,245 +2699,208 @@ void uGeneral::onChatServerUpdated(HttpServerChat *server, const QString &name) 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()); + 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()); + db->writeSetting(ui->edtChannel->objectName(), ui->edtChannel->text()); } - void uGeneral::on_edtDAClientID_editingFinished() { - db->writeSetting(ui->edtDAClientID->objectName(), ui->edtDAClientID->text()); + db->writeSetting(ui->edtDAClientID->objectName(), ui->edtDAClientID->text()); } - void uGeneral::on_edtDAClientSecret_editingFinished() { - db->writeSetting(ui->edtDAClientSecret->objectName(), ui->edtDAClientSecret->text()); + db->writeSetting(ui->edtDAClientSecret->objectName(), ui->edtDAClientSecret->text()); } - void uGeneral::on_edtDARedirectURL_editingFinished() { - db->writeSetting(ui->edtDARedirectURL->objectName(), ui->edtDARedirectURL->text()); + db->writeSetting(ui->edtDARedirectURL->objectName(), ui->edtDARedirectURL->text()); } - void uGeneral::on_edtDACode_editingFinished() { - db->writeSetting(ui->edtDACode->objectName(), ui->edtDACode->text()); + db->writeSetting(ui->edtDACode->objectName(), ui->edtDACode->text()); } - - void uGeneral::on_cbDAAutoLogin_stateChanged(int arg1) { - db->writeSetting(ui->cbDAAutoLogin->objectName(), ui->cbDAAutoLogin->isChecked() ? "True" : "False"); + 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()); + 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()); + db->writeSetting(ui->edtAIP2->objectName(), ui->edtAIP2->text()); } - void uGeneral::on_edtAIP3_editingFinished() { - db->writeSetting(ui->edtAIP3->objectName(), ui->edtAIP3->text()); + 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()); + db->writeSetting(ui->edtKandiSecret->objectName(), ui->edtKandiSecret->text()); } - void uGeneral::on_cbOllama_stateChanged(int arg1) { - db->writeSetting(ui->cbOllama->objectName(), ui->cbOllama->isChecked() ? "True" : "False"); + 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()); + db->writeSetting(ui->edtNotifyFileName->objectName(), ui->edtNotifyFileName->text()); } - void uGeneral::on_edtNotifyFileNameMod_editingFinished() { - db->writeSetting(ui->edtNotifyFileNameMod->objectName(), ui->edtNotifyFileNameMod->text()); + db->writeSetting(ui->edtNotifyFileNameMod->objectName(), ui->edtNotifyFileNameMod->text()); } - void uGeneral::on_edtNotifyFileNameVip_editingFinished() { - db->writeSetting(ui->edtNotifyFileNameVip->objectName(), ui->edtNotifyFileNameVip->text()); + db->writeSetting(ui->edtNotifyFileNameVip->objectName(), ui->edtNotifyFileNameVip->text()); } - void uGeneral::on_edtNotifyFileNameSub_editingFinished() { - db->writeSetting(ui->edtNotifyFileNameSub->objectName(), ui->edtNotifyFileNameSub->text()); + db->writeSetting(ui->edtNotifyFileNameSub->objectName(), ui->edtNotifyFileNameSub->text()); } - void uGeneral::on_chEnNotify_stateChanged(int arg1) { - db->writeSetting(ui->chEnNotify->objectName(), ui->chEnNotify->isChecked() ? "True" : "False"); + Q_UNUSED(arg1); + db->writeSetting(ui->chEnNotify->objectName(), ui->chEnNotify->isChecked() ? "True" : "False"); } - void uGeneral::on_chEnNotifyMod_stateChanged(int arg1) { - db->writeSetting(ui->chEnNotifyMod->objectName(), ui->chEnNotifyMod->isChecked() ? "True" : "False"); + 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) { - db->writeSetting(ui->chEnNotifySub->objectName(), ui->chEnNotifySub->isChecked() ? "True" : "False"); + 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())); + 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())); + 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())); + 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) { - db->writeSetting(ui->cbNotifyFileAgain2->objectName(), ui->cbNotifyFileAgain2->isChecked() ? "True" : "False"); + Q_UNUSED(arg1); + db->writeSetting(ui->cbNotifyFileAgain2->objectName(), ui->cbNotifyFileAgain2->isChecked() ? "True" : "False"); } - void uGeneral::on_cbNotifyFileAgain3_stateChanged(int arg1) { - db->writeSetting(ui->cbNotifyFileAgain3->objectName(), ui->cbNotifyFileAgain3->isChecked() ? "True" : "False"); + 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)); - } - -/** - * @brief Загружает сохраненные чаты из базы данных и создает серверы - */ void uGeneral::loadSavedChats() { QList chats = db->loadAllChats(); for (const ChatSettings &settings : chats) { if (settings.type.toLower() == "chat") { - // Создаем сервер чата HttpServerChat *server = new HttpServerChat( settings.fontList, settings.port, @@ -3378,7 +2908,6 @@ void uGeneral::loadSavedChats() this ); - // Настраиваем сервер server->setBlockColor(settings.blockColor); server->setBorderColor(settings.borderColor); server->setBorderSize(settings.borderSize); @@ -3391,18 +2920,13 @@ void uGeneral::loadSavedChats() 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; - } } } @@ -3413,24 +2937,20 @@ void uGeneral::on_btnOpenStream_clicked() QDesktopServices::openUrl("https://www.twitch.tv/" + ui->edtChannel->text()); } - void uGeneral::on_btnAUserName_clicked() { - QTextCursor cursor = ui->textBrowser->textCursor(); + 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); @@ -3444,7 +2964,6 @@ void uGeneral::on_btnRmWebService_clicked() QString serviceType = typeItem->text(); quint16 port = portItem->text().toUShort(); - // Подтверждение удаления int result = QMessageBox::question(this, "Подтверждение удаления", QString("Вы уверены, что хотите удалить сервис '%1'?\n" "Тип: %2\n" @@ -3455,57 +2974,43 @@ void uGeneral::on_btnRmWebService_clicked() return; } - // Получаем объект сервера из userData QObject *serverObj = nameItem->data(Qt::UserRole).value(); bool deletedFromDB = false; - // Удаляем сервер в зависимости от типа if (serviceType.toLower() == "чат" || serviceType.toLower() == "chat") { - // Обрабатываем чат-сервер HttpServerChat *chatServer = qobject_cast(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(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)") @@ -3521,4 +3026,3 @@ void uGeneral::on_btnRmWebService_clicked() .arg(serviceName)); } } - diff --git a/ugeneral.ui b/ugeneral.ui index 0482d7e..82fcf5a 100644 --- a/ugeneral.ui +++ b/ugeneral.ui @@ -6,7 +6,7 @@ 0 0 - 1006 + 1007 819 @@ -14,413 +14,1061 @@ uGeneral - - - - 0 - 0 - 1001 - 721 - + + + 0 - - - 100000 - 100000 - + + 0 - - false + + 0 - - Qt::LeftToRight + + 0 - - 4 - - - false - - - - - ico/settings.pngico/settings.png - - - Настройки - - - - - 10 - 10 - 331 - 301 - + + + + + 100000 + 100000 + - - Twitch + + false - - - - 10 - 20 - 311 - 303 - + + Qt::LeftToRight + + + 6 + + + false + + + + + ico/settings.pngico/settings.png - + + Настройки + + - + - - - - - Логин бота - - - - - - - - - - - - Qt::Horizontal + + + Twitch - - QSizePolicy::Preferred + + + + + + + + + + + Логин бота + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 75 + 20 + + + + + + + + + + + + + + + + + Токен бота + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Получить + + + + + + + + + + + + + + + Токен стримера + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Получить + + + + + + + + + + + + + + + Client ID + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 75 + 20 + + + + + + + + + + + + + + + + + Канал + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Открыть + + + + + + + + + + + Автоподключение + + + + + + + + + + + + Donation Alerts - - - 75 - 20 - - - - - - + + + + + + + + + + + Client ID + + + + + + + + + + + + + + + + + + + + + Client Secret + + + + + + + + + + + + + + + + + + + + + Redirect URL + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Получить + + + + + + + + + + + + + Автологин + + + + + + + Подключиться + + + + + + + + + - + - - - - - Токен бота - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Получить - - - - - - - - - - - - - - - Токен стримера - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Получить - - - - - - - - - - - - - - - Client ID - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 75 - 20 - - - - - - - - - - - - - - - - - Канал - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Открыть - - - - - - - - - - - Автоподключение - - - - - - - - - - 350 - 10 - 331 - 301 - - - - Donation Alerts - - - - - 10 - 20 - 311 - 252 - - - - - - - - - - - Client ID - - - - - - - - - - - - - - - - - - - - - Client Secret - - - - - - - - - - - - - - - - - - - - - Redirect URL - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Получить - - - - - - - - - - - + - Автологин + Открыть папку с настройками - + - Подключиться + Загрузить свои настройки + + + + + + + Выгрузить настройки + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + ../../../Downloads/ico/ai.png../../../Downloads/ico/ai.png + + + Нейронки + + + + + + + + Фраза перед сообщением пользователя + + + + + + + + + + Client ID + + + + + + + 1 + + + + + + + Autorization Code + + + + + + + 2 + + + + + + + URL + + + + + + + 3 + + + + + + + Ollama + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Активная нейросеть + + + + + + + + GigaChat + + + true + + + + + + + DeepSeek + + + + + + + ChatGPT + + + + + + + Custom + + + + + + + + + + + + Kandinsky + + + + + + + + API ключ + + + + + + + + + + Секретный ключ + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + ../../../Downloads/ico/chat.png../../../Downloads/ico/chat.png + + + Чат бот + + + + + + 8 + + + + + 10 + + + + + + 0 + 0 + + + + + 0 + 129 + + + + 2 + + + + + + + + + + + Рандомные числа + + + + + + 3 + + + + + + + + + + + + + + + -999 + + + 999 + + + + + + + -999 + + + 999 + + + 100 + + + + + + + + + + + Добавить + + + + ../../../Downloads/ico/add.png../../../Downloads/ico/add.png + + + + + + + Удалить + + + + ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Рандомные группы ответов + + + + + + Группа + + + Qt::AutoText + + + + + + + Варианты ответов + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Добавить + + + + ../../../Downloads/ico/add.png../../../Downloads/ico/add.png + + + + + + + Удалить + + + + ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png + + + + + + + Удалить группу + + + + ../../../Downloads/ico/rmfolder.png../../../Downloads/ico/rmfolder.png + + + + + + + + + + + + + + Конструктор ответов + + + + + + + + Команда + + + + + + + + + + + + Озвучить после !!! + + + + + + + Приветствовать новых + + + + + + + + + + + Ответ + + + + + + + + 0 + 52 + + + + + + + + Вставки + + + + + + + + Follow + + + + + + + Age + + + + + + + Картинка + + + + + + + Нейронка + + + + + + + Счетчик + + + + + + + Обращение + + + + + + + Статистика + + + + + + + Рандом + + + + + + + + + + + + + + Добавить + + + + ../../../Downloads/ico/add.png../../../Downloads/ico/add.png + + + + + + + Изменить + + + + ../../../Downloads/ico/edit.png../../../Downloads/ico/edit.png + + + + + + + Удалить + + + + ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 239 + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + @@ -428,1464 +1076,756 @@ - - - - - 10 - 320 - 231 - 23 - - - - Открыть папку с настройками - - - - - - 10 - 350 - 231 - 23 - - - - Загрузить свои настройки - - - - - - 10 - 380 - 231 - 23 - - - - Выгрузить настройки - - - - - - - ../../../Downloads/ico/ai.png../../../Downloads/ico/ai.png - - - Нейронки - - - - - 10 - 10 - 242 - 237 - - - - - - - Фраза перед сообщением пользователя - - - - - - - - - - Client ID - - - - - - - 1 - - - - - - - Autorization Code - - - - - - - 2 - - - - - - - URL - - - - - - - 3 - - - - - - - Ollama - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 260 - 10 - 191 - 131 - - - - Активная нейросеть - - - - - 10 - 19 - 171 - 104 - + + + + ../../../Downloads/ico/skill.png../../../Downloads/ico/skill.png - + + Навыки + + + + + + + ../../../Downloads/ico/obs.png../../../Downloads/ico/obs.png + + + Интеграция с OBS + + - - - GigaChat + + + + + Создать чат + + + + + + + Создать оповещение + + + + + + + Создать Кандинский + + + + + + + Создать игру + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Удалить + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + ../../../Downloads/ico/notify.png../../../Downloads/ico/notify.png + + + Уведомления + + + + + + + + Открыть + + + + ../../../Downloads/ico/open.png../../../Downloads/ico/open.png + + + + + + + > + + + + + + + > + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + Модераторы + + + + + + + Сабы + + + + + + + + + + + + + + Открыть + + + + ../../../Downloads/ico/open.png../../../Downloads/ico/open.png + + + + + + + + + + + + + + D:\2.mp3 + + + + + + + + + + D:\1.mp3 + + + + + + + Открыть + + + + ../../../Downloads/ico/open.png../../../Downloads/ico/open.png + + + + + + + Випы + + + + + + + Qt::Horizontal + + + + + + + Общие + + + + + + + > + + + + + + + + + + Открыть + + + + ../../../Downloads/ico/open.png../../../Downloads/ico/open.png + + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + > + + + + + + + + + + + + + + Использовать общие уведомления + + + + + + + Использовать общие уведомления + + + + + + + Использовать общие уведомления + + + + + + + + + Qt::Vertical - - true + + + 20 + 40 + + + + + + + + + ../../../Downloads/ico/user.png../../../Downloads/ico/user.png + + + Зрители + + + + + + + ../../../Downloads/ico/auto.png../../../Downloads/ico/auto.png + + + Автоматика + + + + + + Таймеры сообщений + + + + + + 0 + + + 4 + + + + Вкл + + + + + Сообщение + + + + + Интервал (мин) + + + + + О + + + + + + + + + + Сообщение: + + + + + + + + + + Интервал: + + + + + + + 10 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Добавить + + + + ../../../Downloads/ico/add.png../../../Downloads/ico/add.png + + + + + + + Изменить + + + + ../../../Downloads/ico/edit.png../../../Downloads/ico/edit.png + + + + + + + Удалить + + + + ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png + + + + + + + Тест + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + - - - DeepSeek + + + Qt::Vertical - + + + 20 + 40 + + + + + + + + Qt::LeftToRight + + + + ../../../Downloads/ico/log.png../../../Downloads/ico/log.png + + + Лог + + - - - ChatGPT - - - - - - - Custom - - + + + + + Qt::LeftToRight + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + false + + + false + + + + + + QLayout::SetDefaultConstraint + + + + + Очистить + + + + + + + Инфо + + + + + + + Предупреждения + + + + + + + Ошибки + + + + + + + Отладка + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + + + 5 + + + + + + + + + - - - - 460 - 10 - 291 - 131 - - - - Kandinsky - - - - - 10 - 20 - 271 - 100 - - - - - - - API ключ - - - - - - - - - - Секретный ключ - - - - - - - - - - - - - - ../../../Downloads/ico/chat.png../../../Downloads/ico/chat.png - - - Чат бот - - - - - 10 - 450 - 320 - 241 - - - - - 0 - 0 - - - - - - - 610 - 0 - 381 - 351 - - - - Конструктор ответов - - - - - 6 - 19 - 71 - 16 - - - - Команда - - - - - - 10 - 40 - 191 - 20 - - - - - - - 210 - 20 - 141 - 16 - - - - Озвучить после !!! - - - - - - 210 - 40 - 161 - 21 - - - - Приветствовать новых - - - - - - 6 - 63 - 61 - 20 - - - - Ответ - - - - - - 10 - 80 - 361 - 111 - - - - - - - 10 - 190 - 361 - 111 - - - - Вставки - - - - - 0 - 20 - 361 - 91 - + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + Подключиться - - - - - Follow - - - - - - - Age - - - - - - - Картинка - - - - - - - Нейронка - - - - - - - Счетчик - - - - - - - Обращение - - - - - - - Статистика - - - - - - - Рандом - - - - - - - - - 10 - 320 - 91 - 23 - - - - Добавить - - - - ../../../Downloads/ico/add.png../../../Downloads/ico/add.png - - - - - - 100 - 320 - 91 - 23 - - - - Изменить - - - - ../../../Downloads/ico/edit.png../../../Downloads/ico/edit.png - - - - - - 190 - 320 - 91 - 23 - - - - Удалить - - - - ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png - - - - - - - 339 - 450 - 321 - 241 - - - - - 0 - 0 - - - - - - - 9 - 199 - 201 - 240 - - - - Рандомные числа - - - - - 10 - 21 - 181 - 151 - - - - 3 - - - - - - - - - 10 - 180 - 81 - 20 - - - - - - - 100 - 180 - 41 - 22 - - - - -999 - - - 999 - - - - - - 150 - 180 - 41 - 22 - - - - -999 - - - 999 - - - 100 - - - - - - 10 - 210 - 91 - 23 - - - - Добавить - - - - ../../../Downloads/ico/add.png../../../Downloads/ico/add.png - - - - - - 100 - 210 - 91 - 23 - - - - Удалить - - - - ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png - - - - - - - 220 - 200 - 381 - 241 - - - - Рандомные группы ответов - - - - - 10 - 40 - 131 - 131 - - - - - - - 140 - 40 - 231 - 131 - - - - - - - 16 - 20 - 81 - 20 - - - - Группа - - - Qt::AutoText - - - - - - 140 - 20 - 231 - 20 - - - - Варианты ответов - - - - - - 10 - 180 - 131 - 21 - - - - - - - 140 - 180 - 231 - 21 - - - - - - - 10 - 210 - 91 - 23 - - - - Добавить - - - - ../../../Downloads/ico/add.png../../../Downloads/ico/add.png - - - - - - 100 - 210 - 91 - 23 - - - - Удалить - - - - ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png - - - - - - 244 - 210 - 121 - 23 - - - - Удалить группу - - - - ../../../Downloads/ico/rmfolder.png../../../Downloads/ico/rmfolder.png - - - - - - - 670 - 450 - 321 - 241 - - - - - 0 - 0 - - - - - - - 10 - 11 - 591 - 181 - - - - 2 - - - - - - - - - ../../../Downloads/ico/skill.png../../../Downloads/ico/skill.png - - - Навыки - - - - - - ../../../Downloads/ico/obs.png../../../Downloads/ico/obs.png - - - Интеграция с OBS - - - - - 10 - 10 - 981 - 41 - - - - - - - Создать чат - - - - - - - Создать оповещение - - - - - - - Создать Кандинский - - - - - - - Создать игру - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 10 - 50 - 981 - 291 - - - - - - - 10 - 350 - 101 - 24 - - - - Удалить - - - - - - - ../../../Downloads/ico/notify.png../../../Downloads/ico/notify.png - - - Уведомления - - - - - 0 - 0 - 991 - 151 - - - - - - - Открыть - - - - ../../../Downloads/ico/open.png../../../Downloads/ico/open.png - - - - - - - > - - - - - - - > - - - - - - - Qt::Horizontal - - - - - - - - - - - - - - Модераторы - - - - - - - Сабы - - - - - - - - - - - - - - Открыть - - - - ../../../Downloads/ico/open.png../../../Downloads/ico/open.png - - - - - - - - - - - - - - D:\2.mp3 - - - - - - - - - - D:\1.mp3 - - - - - - - Открыть - - - - ../../../Downloads/ico/open.png../../../Downloads/ico/open.png - - - - - - - Випы - - - - - - - Qt::Horizontal - - - - - - - Общие - - - - - - - > - - - - - - - - - - Открыть - - - - ../../../Downloads/ico/open.png../../../Downloads/ico/open.png - - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - > - - - - - - - - - - - - - - Использовать общие уведомления - - - - - - - Использовать общие уведомления - - - - - - - Использовать общие уведомления - - - - - - - - - - ../../../Downloads/ico/user.png../../../Downloads/ico/user.png - - - Зрители - - - - - - ../../../Downloads/ico/auto.png../../../Downloads/ico/auto.png - - - Автоматика - - - - - 10 - 10 - 971 - 311 - - - - Таймеры сообщений - - - - - 10 - 20 - 951 - 211 - - - - 0 - - - 4 - - + + + - Вкл + Тема + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + + + + + + 0 + 0 + + + + + 33 + 0 + - - - Сообщение + ... - - - - Интервал (мин) + + + + + + Qt::Horizontal - - - - О + + QSizePolicy::Fixed - - - - - - 10 - 240 - 71 - 16 - - - - Сообщение: - - - - - - 90 - 240 - 391 - 22 - - - - - - - 490 - 240 - 71 - 16 - - - - Интервал: - - - - - - 570 - 240 - 121 - 22 - - - - 10 - - - - - - 10 - 270 - 93 - 28 - - - - Добавить - - - - ../../../Downloads/ico/add.png../../../Downloads/ico/add.png - - - - - - 110 - 270 - 93 - 28 - - - - Изменить - - - - ../../../Downloads/ico/edit.png../../../Downloads/ico/edit.png - - - - - - 210 - 270 - 93 - 28 - - - - Удалить - - - - ../../../Downloads/ico/minus-sign_3485999.png../../../Downloads/ico/minus-sign_3485999.png - - - - - - 860 - 270 - 93 - 28 - - - - Тест - - - - - - - - ../../../Downloads/ico/log.png../../../Downloads/ico/log.png - - - Лог - - - - - 10 - 10 - 981 - 41 - - - - - - - - - 14 - 10 - 71 - 23 - - - - Очистить - - - - - - 89 - 10 - 61 - 21 - - - - Инфо - - - - - - 150 - 10 - 121 - 21 - - - - Предупреждения - - - - - - 280 - 10 - 71 - 21 - - - - Ошибки - - - - - - 360 - 10 - 91 - 20 - - - - Отладка - - - - - - - 10 - 50 - 981 - 711 - - - - 0 - - - 5 - - - - - - - - - - - - - 10 - 730 - 141 - 28 - - - - Подключиться - - - - - - 160 - 740 - 31 - 16 - - - - Тема - - - - - - 200 - 730 - 211 - 24 - - - - - - - 419 - 730 - 21 - 24 - - - - ... - - - - - - 770 - 730 - 231 - 61 - - - - - - - Осталось дней токена стримера - - - - - - - Осталось дней токена бота - - - - - - - 0 - - - - - - - 0 - - - - - + + + 150 + 20 + + + + + + + + + + Осталось дней токена стримера + + + + + + + 0 + + + + + + + 0 + + + + + + + Осталось дней токена бота + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + diff --git a/userwidget.cpp b/userwidget.cpp index 4696752..a7b661b 100644 --- a/userwidget.cpp +++ b/userwidget.cpp @@ -17,16 +17,15 @@ UserWidget::UserWidget(UserManager* userManager, QWidget *parent) connect(m_tableWidget, &QTableWidget::customContextMenuRequested, this, &UserWidget::showContextMenu); - setupContextMenu(); // Добавляем инициализацию контекстного меню - // Подключаем сигналы если менеджер уже есть + setupContextMenu(); + if (m_userManager) { connectSignals(); } - // Таймер для периодического обновления m_refreshTimer = new QTimer(this); connect(m_refreshTimer, &QTimer::timeout, this, &UserWidget::onRefreshTimer); - m_refreshTimer->start(30000); // 30 секунд + m_refreshTimer->start(30000); refreshData(); } @@ -40,7 +39,6 @@ UserWidget::~UserWidget() void UserWidget::setUserManager(UserManager* userManager) { - // Отключаем старые соединения если были if (m_userManager) { disconnectSignals(); } @@ -76,14 +74,26 @@ void UserWidget::disconnectSignals() void UserWidget::setupUI() { + // ------------------------------------------------------------ + // 1. Глобальные настройки самого виджета + // ------------------------------------------------------------ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setMinimumSize(400, 300); // гарантия, что виджет не сожмётся до нуля + + // ------------------------------------------------------------ + // 2. Главный вертикальный layout + // ------------------------------------------------------------ QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(5, 5, 5, 5); mainLayout->setSpacing(5); + mainLayout->setSizeConstraint(QLayout::SetDefaultConstraint); - // Панель инструментов + // ------------------------------------------------------------ + // 3. Панель инструментов (фильтр, поиск, кнопка обновления) + // ------------------------------------------------------------ QHBoxLayout* toolbarLayout = new QHBoxLayout(); + toolbarLayout->setSpacing(5); - // Фильтр QLabel* filterLabel = new QLabel("Фильтр:", this); m_filterCombo = new QComboBox(this); m_filterCombo->addItem("Все зрители", FilterAll); @@ -91,70 +101,86 @@ void UserWidget::setupUI() m_filterCombo->addItem("VIP", FilterVIPs); m_filterCombo->addItem("Подписчики", FilterSubscribers); m_filterCombo->addItem("Активные (10 мин)", FilterActive); + m_filterCombo->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); connect(m_filterCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &UserWidget::onFilterChanged); - // Поиск QLabel* searchLabel = new QLabel("Поиск:", this); m_searchEdit = new QLineEdit(this); - m_searchEdit->setPlaceholderText("Введите имя зрителя..."); + m_searchEdit->setPlaceholderText("Имя зрителя..."); m_searchEdit->setClearButtonEnabled(true); + m_searchEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); connect(m_searchEdit, &QLineEdit::textChanged, this, &UserWidget::onSearchTextChanged); - // Кнопка обновления QPushButton* refreshBtn = new QPushButton("Обновить", this); - refreshBtn->setFixedWidth(100); + refreshBtn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + refreshBtn->setMaximumWidth(100); connect(refreshBtn, &QPushButton::clicked, this, &UserWidget::onRefreshButtonClicked); toolbarLayout->addWidget(filterLabel); toolbarLayout->addWidget(m_filterCombo); toolbarLayout->addWidget(searchLabel); - toolbarLayout->addWidget(m_searchEdit); + toolbarLayout->addWidget(m_searchEdit, 1); toolbarLayout->addStretch(); toolbarLayout->addWidget(refreshBtn); - // Таблица + // ------------------------------------------------------------ + // 4. Таблица пользователей + // ------------------------------------------------------------ m_tableWidget = new QTableWidget(this); m_tableWidget->setColumnCount(6); m_tableWidget->setHorizontalHeaderLabels( QStringList() << "Имя" << "Сообщений" << "Статус" << "VIP" << "Подписчик" << "Последняя активность"); - // Настройка таблицы m_tableWidget->setSortingEnabled(true); m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableWidget->setAlternatingRowColors(true); m_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); - m_tableWidget->horizontalHeader()->setStretchLastSection(true); - m_tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); m_tableWidget->verticalHeader()->setVisible(false); - // Настройка ширины колонок - m_tableWidget->setColumnWidth(0, 250); // Имя - m_tableWidget->setColumnWidth(1, 80); // Сообщений - m_tableWidget->setColumnWidth(2, 100); // Статус - m_tableWidget->setColumnWidth(3, 50); // VIP - m_tableWidget->setColumnWidth(4, 80); // Подписчик - // Последняя колонка растягивается + // Политика размеров таблицы – активное расширение + m_tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_tableWidget->setMinimumHeight(200); // минимальная высота для отображения хоть чего-то + + // Настройка ширины колонок (интерактивно, последняя растягивается) + QHeaderView* header = m_tableWidget->horizontalHeader(); + header->setStretchLastSection(true); + header->setSectionResizeMode(QHeaderView::Interactive); + + // Начальные предпочтительные ширины + m_tableWidget->setColumnWidth(0, 200); + m_tableWidget->setColumnWidth(1, 80); + m_tableWidget->setColumnWidth(2, 100); + m_tableWidget->setColumnWidth(3, 50); + m_tableWidget->setColumnWidth(4, 80); connect(m_tableWidget, &QTableWidget::cellDoubleClicked, this, &UserWidget::onUserDoubleClicked); - // Статистика внизу + // ------------------------------------------------------------ + // 5. Панель статистики + // ------------------------------------------------------------ QHBoxLayout* statsLayout = new QHBoxLayout(); m_statsLabel = new QLabel(this); + m_statsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); statsLayout->addWidget(m_statsLabel); statsLayout->addStretch(); - // Добавляем всё на форму + // ------------------------------------------------------------ + // 6. Сборка интерфейса + // ------------------------------------------------------------ mainLayout->addLayout(toolbarLayout); - mainLayout->addWidget(m_tableWidget); + mainLayout->addWidget(m_tableWidget, 1); // растягиваем таблицу с коэффициентом 1 mainLayout->addLayout(statsLayout); setLayout(mainLayout); + + // Принудительно обновляем геометрию после установки layout + updateGeometry(); } void UserWidget::refreshData() @@ -176,7 +202,6 @@ void UserWidget::populateTable() QVector users; - // Получаем пользователей в зависимости от фильтра switch (m_currentFilter) { case FilterAll: for (const User& user : m_userManager->getAllUsers()) { @@ -197,11 +222,9 @@ void UserWidget::populateTable() break; } - // Применяем поисковый фильтр if (!m_searchText.isEmpty()) { QString searchLower = m_searchText.toLower(); QVector filteredUsers; - for (User* user : users) { if (user->displayName.toLower().contains(searchLower) || user->login.toLower().contains(searchLower)) { @@ -211,22 +234,18 @@ void UserWidget::populateTable() users = filteredUsers; } - // Заполняем таблицу m_tableWidget->setRowCount(users.size()); for (int i = 0; i < users.size(); ++i) { User* user = users[i]; - // Имя QTableWidgetItem* nameItem = new QTableWidgetItem(user->displayName); nameItem->setData(Qt::UserRole, user->id); m_tableWidget->setItem(i, 0, nameItem); - // Количество сообщений m_tableWidget->setItem(i, 1, new QTableWidgetItem(QString::number(user->messageCount))); - // Статус QString status; if (user->isModerator) status = "👑 Модератор"; else if (user->isVIP) status = "⭐ VIP"; @@ -234,43 +253,35 @@ void UserWidget::populateTable() else status = "👤 Зритель"; m_tableWidget->setItem(i, 2, new QTableWidgetItem(status)); - // VIP QTableWidgetItem* vipItem = new QTableWidgetItem(user->isVIP ? "Да" : "Нет"); - if (user->isVIP) { - vipItem->setForeground(Qt::darkGreen); - } + if (user->isVIP) vipItem->setForeground(Qt::darkGreen); m_tableWidget->setItem(i, 3, vipItem); - // Подписчик QTableWidgetItem* subItem = new QTableWidgetItem(user->isSubscriber ? "Да" : "Нет"); - if (user->isSubscriber) { - subItem->setForeground(Qt::darkBlue); - } + if (user->isSubscriber) subItem->setForeground(Qt::darkBlue); m_tableWidget->setItem(i, 4, subItem); - // Последняя активность QString lastActive; if (user->lastMessageTime.isValid()) { QDateTime now = QDateTime::currentDateTime(); qint64 secs = user->lastMessageTime.secsTo(now); - - if (secs < 60) { - lastActive = "Только что"; - } else if (secs < 3600) { - lastActive = QString("%1 мин назад").arg(secs / 60); - } else if (secs < 86400) { - lastActive = QString("%1 ч назад").arg(secs / 3600); - } else { - lastActive = user->lastMessageTime.toString("dd.MM.yyyy hh:mm"); - } + if (secs < 60) lastActive = "Только что"; + else if (secs < 3600) lastActive = QString("%1 мин назад").arg(secs / 60); + else if (secs < 86400) lastActive = QString("%1 ч назад").arg(secs / 3600); + else lastActive = user->lastMessageTime.toString("dd.MM.yyyy hh:mm"); } else { lastActive = "Никогда"; } m_tableWidget->setItem(i, 5, new QTableWidgetItem(lastActive)); } - // Автоподгон ширины колонок после заполнения + // Автоподгон ширины колонок с ограничением m_tableWidget->resizeColumnsToContents(); + for (int col = 0; col < m_tableWidget->columnCount() - 1; ++col) { + if (m_tableWidget->columnWidth(col) > 300) { + m_tableWidget->setColumnWidth(col, 300); + } + } } void UserWidget::updateStatistics() @@ -292,16 +303,13 @@ void UserWidget::addUserToTable(const User &user) int row = m_tableWidget->rowCount(); m_tableWidget->insertRow(row); - // Имя QTableWidgetItem* nameItem = new QTableWidgetItem(user.displayName); nameItem->setData(Qt::UserRole, user.id); m_tableWidget->setItem(row, 0, nameItem); - // Количество сообщений m_tableWidget->setItem(row, 1, new QTableWidgetItem(QString::number(user.messageCount))); - // Статус QString status; if (user.isModerator) status = "👑 Модератор"; else if (user.isVIP) status = "⭐ VIP"; @@ -309,23 +317,15 @@ void UserWidget::addUserToTable(const User &user) else status = "👤 Зритель"; m_tableWidget->setItem(row, 2, new QTableWidgetItem(status)); - // VIP QTableWidgetItem* vipItem = new QTableWidgetItem(user.isVIP ? "Да" : "Нет"); - if (user.isVIP) { - vipItem->setForeground(Qt::darkGreen); - } + if (user.isVIP) vipItem->setForeground(Qt::darkGreen); m_tableWidget->setItem(row, 3, vipItem); - // Подписчик QTableWidgetItem* subItem = new QTableWidgetItem(user.isSubscriber ? "Да" : "Нет"); - if (user.isSubscriber) { - subItem->setForeground(Qt::darkBlue); - } + if (user.isSubscriber) subItem->setForeground(Qt::darkBlue); m_tableWidget->setItem(row, 4, subItem); - // Последняя активность - QString lastActive = "Только что"; - m_tableWidget->setItem(row, 5, new QTableWidgetItem(lastActive)); + m_tableWidget->setItem(row, 5, new QTableWidgetItem("Только что")); updateStatistics(); } @@ -335,11 +335,9 @@ void UserWidget::updateUserInTable(const User &user) for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->data(Qt::UserRole).toString() == user.id) { - // Обновляем данные item->setText(user.displayName); - m_tableWidget->item(row, 1)->setText( - QString::number(user.messageCount)); + m_tableWidget->item(row, 1)->setText(QString::number(user.messageCount)); QString status; if (user.isModerator) status = "👑 Модератор"; @@ -349,34 +347,19 @@ void UserWidget::updateUserInTable(const User &user) m_tableWidget->item(row, 2)->setText(status); m_tableWidget->item(row, 3)->setText(user.isVIP ? "Да" : "Нет"); - if (user.isVIP) { - m_tableWidget->item(row, 3)->setForeground(Qt::darkGreen); - } else { - m_tableWidget->item(row, 3)->setForeground(Qt::black); - } + m_tableWidget->item(row, 3)->setForeground(user.isVIP ? Qt::darkGreen : Qt::black); m_tableWidget->item(row, 4)->setText(user.isSubscriber ? "Да" : "Нет"); - if (user.isSubscriber) { - m_tableWidget->item(row, 4)->setForeground(Qt::darkBlue); - } else { - m_tableWidget->item(row, 4)->setForeground(Qt::black); - } + m_tableWidget->item(row, 4)->setForeground(user.isSubscriber ? Qt::darkBlue : Qt::black); - // Обновляем время активности QString lastActive; if (user.lastMessageTime.isValid()) { QDateTime now = QDateTime::currentDateTime(); qint64 secs = user.lastMessageTime.secsTo(now); - - if (secs < 60) { - lastActive = "Только что"; - } else if (secs < 3600) { - lastActive = QString("%1 мин назад").arg(secs / 60); - } else if (secs < 86400) { - lastActive = QString("%1 ч назад").arg(secs / 3600); - } else { - lastActive = user.lastMessageTime.toString("dd.MM hh:mm"); - } + if (secs < 60) lastActive = "Только что"; + else if (secs < 3600) lastActive = QString("%1 мин назад").arg(secs / 60); + else if (secs < 86400) lastActive = QString("%1 ч назад").arg(secs / 3600); + else lastActive = user.lastMessageTime.toString("dd.MM hh:mm"); } m_tableWidget->item(row, 5)->setText(lastActive); @@ -388,7 +371,6 @@ void UserWidget::updateUserInTable(const User &user) void UserWidget::removeUserFromTable(const QString &displayName) { - // Ищем пользователя по displayName for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->text() == displayName) { @@ -417,19 +399,12 @@ void UserWidget::onRefreshTimer() User* user = m_userManager->findUserById(userId); if (user && user->lastMessageTime.isValid()) { qint64 secs = user->lastMessageTime.secsTo(now); - QString lastActive; - if (secs < 60) { - lastActive = "Только что"; - } else if (secs < 3600) { - lastActive = QString("%1 мин назад").arg(secs / 60); - } else if (secs < 86400) { - lastActive = QString("%1 ч назад").arg(secs / 3600); - } else { - lastActive = user->lastMessageTime.toString("dd.MM hh:mm"); - } + if (secs < 60) lastActive = "Только что"; + else if (secs < 3600) lastActive = QString("%1 мин назад").arg(secs / 60); + else if (secs < 86400) lastActive = QString("%1 ч назад").arg(secs / 3600); + else lastActive = user->lastMessageTime.toString("dd.MM hh:mm"); - // Проверяем, существует ли ячейка if (m_tableWidget->item(row, 5)) { m_tableWidget->item(row, 5)->setText(lastActive); } @@ -442,27 +417,17 @@ bool UserWidget::userMatchesFilter(const User* user) const { if (!user) return false; - // Проверка фильтра switch (m_currentFilter) { - case FilterAll: - break; - case FilterModerators: - if (!user->isModerator) return false; - break; - case FilterVIPs: - if (!user->isVIP) return false; - break; - case FilterSubscribers: - if (!user->isSubscriber) return false; - break; + case FilterAll: break; + case FilterModerators: if (!user->isModerator) return false; break; + case FilterVIPs: if (!user->isVIP) return false; break; + case FilterSubscribers: if (!user->isSubscriber) return false; break; case FilterActive: - // Активные за последние 10 минут - if (user->lastMessageTime.secsTo(QDateTime::currentDateTime()) > 10*60) + if (user->lastMessageTime.secsTo(QDateTime::currentDateTime()) > 10 * 60) return false; break; } - // Проверка поиска if (!m_searchText.isEmpty()) { QString searchLower = m_searchText.toLower(); if (!user->displayName.toLower().contains(searchLower) && @@ -476,7 +441,6 @@ bool UserWidget::userMatchesFilter(const User* user) const void UserWidget::onUserAdded(const User &user) { - // Проверяем, проходит ли пользователь текущий фильтр и поиск if (userMatchesFilter(&user)) { addUserToTable(user); } @@ -484,22 +448,16 @@ void UserWidget::onUserAdded(const User &user) void UserWidget::onUserUpdated(const User &user) { - // Ищем пользователя в таблице int row = findRowByUserId(user.id); if (row >= 0) { - // Пользователь уже в таблице if (userMatchesFilter(&user)) { - // Обновляем существующую запись updateUserInTable(user); } else { - // Удаляем, так как пользователь больше не проходит фильтр removeUserFromTableByUserId(user.id); } } else { - // Пользователя нет в таблице if (userMatchesFilter(&user)) { - // Добавляем, так как пользователь теперь проходит фильтр addUserToTable(user); } } @@ -534,7 +492,6 @@ void UserWidget::onUserRemoved(const QString &userId, const QString &displayName void UserWidget::onUserDoubleClicked(int row, int column) { Q_UNUSED(column); - if (!m_userManager) return; QTableWidgetItem* item = m_tableWidget->item(row, 0); @@ -551,9 +508,6 @@ void UserWidget::onUserDoubleClicked(int row, int column) << "\nVIP:" << user->isVIP << "\nПодписчик:" << user->isSubscriber << "\nПоследнее сообщение:" << user->lastMessageTime.toString(); - - // Здесь можно открыть диалог с детальной информацией - // или выполнить другие действия } } } @@ -610,38 +564,29 @@ void UserWidget::setupContextMenu() connect(m_infoAction, &QAction::triggered, this, &UserWidget::onShowUserInfo); } -// Добавляем обработчик контекстного меню: void UserWidget::showContextMenu(const QPoint &pos) { QTableWidgetItem* item = m_tableWidget->itemAt(pos); - - if (!item) { - return; // Клик был не по ячейке (например, по заголовку или пустой области) - } + if (!item) return; int row = item->row(); QTableWidgetItem* nameItem = m_tableWidget->item(row, 0); - if (nameItem) { m_selectedUserId = nameItem->data(Qt::UserRole).toString(); m_selectedUserName = nameItem->text(); - // Получаем данные пользователя User* user = m_userManager->findUserById(m_selectedUserId); if (user) { - // Обновляем состояние пунктов меню m_setModAction->setEnabled(!user->isModerator); m_removeModAction->setEnabled(user->isModerator); m_setVIPAction->setEnabled(!user->isVIP); m_removeVIPAction->setEnabled(user->isVIP); - // Показываем меню в позиции клика m_contextMenu->exec(m_tableWidget->viewport()->mapToGlobal(pos)); } } } -// Добавляем вспомогательный метод: User* UserWidget::getSelectedUser() { if (!m_selectedUserId.isEmpty()) { @@ -650,7 +595,6 @@ User* UserWidget::getSelectedUser() return nullptr; } -// Добавляем слоты для обработки действий меню: void UserWidget::onBanUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) {