39f0c447c1
исправил поиск пользователя убрал лишние qDebug
3375 lines
116 KiB
C++
3375 lines
116 KiB
C++
#include "ugeneral.h"
|
||
#include "fcreatenotify.h"
|
||
#include "filemanager.h"
|
||
#include "logmanager.h"
|
||
#include "ui_ugeneral.h"
|
||
#include <QStandardPaths>
|
||
#include <QDesktopServices>
|
||
#include <QUrl>
|
||
#include <QDir>
|
||
#include <QFileDialog>
|
||
#include <QMessageBox>
|
||
#include <QStandardItem>
|
||
#include <QDateTime>
|
||
#include <QRandomGenerator>
|
||
#include "ulink.h"
|
||
#include "udatabase.h"
|
||
#include "soundmanager.h"
|
||
#include "userwidget.h"
|
||
#include "websocketclient.h"
|
||
#include "twitchmessage.h"
|
||
#include "user.h"
|
||
#include "user_manager.h"
|
||
#include "emoteprovider.h"
|
||
#include "fcreatechat.h"
|
||
#include <QInputDialog>
|
||
#include <QPair>
|
||
#include <QCache>
|
||
#include <QSettings>
|
||
#include "timerinfo.h"
|
||
#include "mediafilemanager.h"
|
||
|
||
uGeneral::uGeneral(QWidget *parent)
|
||
: QMainWindow(parent)
|
||
, ui(new Ui::uGeneral)
|
||
, m_authBot(nullptr)
|
||
, m_authStreamer(nullptr)
|
||
, m_authDA(nullptr)
|
||
, fLinkForm(nullptr)
|
||
, m_randomManager(nullptr)
|
||
, m_neuralTemplateManager(nullptr)
|
||
, m_isTwitchConnected(false)
|
||
, m_notificationServers()
|
||
, m_chatServers()
|
||
, m_createNotifyDialog(nullptr)
|
||
, m_createChatDialog(nullptr)
|
||
{
|
||
// ============================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ ИНТЕРФЕЙСА
|
||
// ============================================================================
|
||
ui->setupUi(this);
|
||
setWindowTitle("TTW Bot app");
|
||
|
||
|
||
LogSettings logSettings;
|
||
logSettings.logToFile = true;
|
||
logSettings.logToConsole = true;
|
||
//logSettings.maxEntries = 5000;
|
||
|
||
// Цвета для разных уровней
|
||
logSettings.colors[LogLevel::Info] = Qt::darkGreen;
|
||
logSettings.colors[LogLevel::Warning] = Qt::darkYellow;
|
||
logSettings.colors[LogLevel::Error] = Qt::red;
|
||
logSettings.colors[LogLevel::Debug] = Qt::gray;
|
||
logSettings.colors[LogLevel::Critical] = Qt::darkRed;
|
||
|
||
LogManager::initialize(logSettings);
|
||
|
||
// Подключение сигналов к UI
|
||
LogManager* logger = LogManager::instance();
|
||
connect(logger, &LogManager::entryAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::logCleared,
|
||
this, &uGeneral::onLogCleared);
|
||
connect(logger, &LogManager::errorAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::warningAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::infoAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::debugAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
connect(logger, &LogManager::criticalAdded,
|
||
this, &uGeneral::onLogEntryAdded);
|
||
|
||
// Запуск приложения - используем новый логгер
|
||
LogManager::instance()->info("uGeneral", "constructor", "Главное окно создано");
|
||
|
||
// Инициализируем структуру папок
|
||
FileManager::instance().initializeFolderStructure();
|
||
|
||
// Получаем пути через FileManager
|
||
QString sysPath = FileManager::instance().systemPath();
|
||
|
||
// Загружаем иконки
|
||
tabIcons = {
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "settings.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "ai.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "chat.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "skill.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "obs.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "notify.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "user.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "auto.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "log.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "add.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "edit.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "rm.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "rmfolder.png")),
|
||
QIcon(FileManager::instance().getFullPath(FileManager::Icons, "open.png")),
|
||
};
|
||
|
||
|
||
|
||
for (int i = 0; i < ui->www->count() && i < tabIcons.size(); i++) {
|
||
ui->www->setTabIcon(i, tabIcons[i]);
|
||
}
|
||
|
||
setupButtonIcons();
|
||
|
||
|
||
|
||
// Загружаем QSS файлы при создании формы
|
||
loadQssFiles();
|
||
|
||
// ============================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ БАЗЫ ДАННЫХ
|
||
// ============================================================================
|
||
initializeDatabase();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА ИНТЕРФЕЙСА ПОЛЬЗОВАТЕЛЯ
|
||
// ============================================================================
|
||
setupUI();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА ТАБЛИЦ
|
||
// ============================================================================
|
||
setupTables();
|
||
|
||
// ============================================================================
|
||
// ЗАГРУЗКА НАСТРОЕК
|
||
// ============================================================================
|
||
loadSettings();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА TWITCH API И WEBSOCKET
|
||
// ============================================================================
|
||
setupTwitchComponents();
|
||
|
||
// ============================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ МЕНЕДЖЕРОВ
|
||
// ============================================================================
|
||
initializeManagers();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА ВИДЖЕТА ПОЛЬЗОВАТЕЛЕЙ
|
||
// ============================================================================
|
||
setupUserWidget();
|
||
|
||
// ============================================================================
|
||
// НАСТРОЙКА АВТОРИЗАЦИИ
|
||
// ============================================================================
|
||
setupAuthHandlers();
|
||
|
||
loadEmoties();
|
||
|
||
// Настройка таблицы веб-серверов
|
||
QStringList headers = {"Название", "Тип", "Порт", "Ссылка", "Статус"};
|
||
ui->sgWebServers->setColumnCount(headers.size());
|
||
ui->sgWebServers->setHorizontalHeaderLabels(headers);
|
||
ui->sgWebServers->horizontalHeader()->setStretchLastSection(true);
|
||
|
||
// Инициализируем окна создания
|
||
m_createNotifyDialog = new FCreateNotify(db, this);
|
||
m_createChatDialog = new FCreateChat(db, this);
|
||
|
||
// Добавьте эту строку в конец метода:
|
||
initializeNotificationSounds();
|
||
|
||
|
||
|
||
}
|
||
|
||
// ============================================================================
|
||
// ПРИВАТНЫЕ МЕТОДЫ ДЛЯ ИНИЦИАЛИЗАЦИИ
|
||
// ============================================================================
|
||
|
||
|
||
|
||
void uGeneral::setupButtonIcons() {
|
||
// Получаем все кнопки на форме
|
||
QList<QPushButton*> buttons = ui->www->findChildren<QPushButton*>();
|
||
|
||
// Если кнопки не в centralWidget, ищем во всем окне
|
||
// QList<QPushButton*> buttons = findChildren<QPushButton*>();
|
||
|
||
// Проходим по всем найденным кнопкам
|
||
foreach (QPushButton *button, buttons) {
|
||
QString buttonName = button->objectName().toLower(); // Приводим к нижнему регистру
|
||
|
||
// Проверяем наличие ключевых слов в имени
|
||
if (buttonName.contains("add")) {
|
||
button->setIcon(tabIcons[9]);
|
||
}
|
||
else if (buttonName.contains("del") || buttonName.contains("rm")) {
|
||
// Проверяем специальный случай RmGroup (регистрозависимый)
|
||
if (button->objectName().contains("RmGroup")) {
|
||
button->setIcon(tabIcons[12]);
|
||
} else {
|
||
button->setIcon(tabIcons[11]);
|
||
}
|
||
}
|
||
else if (buttonName.contains("edt")) {
|
||
button->setIcon(tabIcons[10]);
|
||
}
|
||
else if (buttonName.contains("open")) {
|
||
button->setIcon(tabIcons[13]);
|
||
}
|
||
|
||
// Устанавливаем размер иконки (опционально)
|
||
if (!button->icon().isNull()) {
|
||
button->setIconSize(QSize(16, 16));
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/**
|
||
* @brief Инициализация базы данных
|
||
*/
|
||
void uGeneral::initializeDatabase()
|
||
{
|
||
QString roamingPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||
|
||
QDir dir(roamingPath);
|
||
if (!dir.exists()) {
|
||
dir.mkpath(".");
|
||
}
|
||
|
||
roamingPath += "/settings.db";
|
||
|
||
db = new uDataBase(roamingPath, this);
|
||
|
||
if (!db->isConnected()) {
|
||
LogManager::instance()->error("uGeneral", "Create", "Ошибка подключения к БД");
|
||
} else {
|
||
LogManager::instance()->info("uGeneral", "Create", "Успешное подключение к БД");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Настройка пользовательского интерфейса
|
||
*/
|
||
void uGeneral::setupUI()
|
||
{
|
||
// Настройка видимости элементов
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
ui->cbOllama->setVisible(false);
|
||
|
||
// Настройка режимов отображения паролей
|
||
QList<QLineEdit*> passwordFields = {
|
||
ui->edtDACode,
|
||
ui->edtBotToken,
|
||
ui->edtKandiKey,
|
||
ui->edtDAClientID,
|
||
ui->edtBotClientID,
|
||
ui->edtKandiSecret,
|
||
ui->edtDAClientSecret,
|
||
ui->edtBotTokenStreamer,
|
||
ui->edtAIP1,
|
||
ui->edtAIP2,
|
||
ui->edtAIP3
|
||
};
|
||
|
||
for (auto field : passwordFields) {
|
||
field->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
// Подключение фильтров логов
|
||
connect(ui->cbInfo, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
connect(ui->cbWarning, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
connect(ui->cbError, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
connect(ui->cbDebug, &QCheckBox::stateChanged, this, &uGeneral::applyLogFilter);
|
||
}
|
||
|
||
/**
|
||
* @brief Настройка таблиц интерфейса
|
||
*/
|
||
void uGeneral::setupTables()
|
||
{
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ ЛОГОВ
|
||
// ========================================================================
|
||
QStringList headers;
|
||
headers.clear();
|
||
headers << "Дата" << "Тип" << "Модуль" << "Метод" << "Сообщение";
|
||
ui->sgLog->setHorizontalHeaderLabels(headers);
|
||
ui->sgLog->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgLog->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgLog->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
|
||
// Настройка ширины колонок
|
||
ui->sgLog->setColumnWidth(0, 100); // Дата
|
||
ui->sgLog->setColumnWidth(1, 100); // Тип
|
||
ui->sgLog->setColumnWidth(2, 170); // Модуль
|
||
ui->sgLog->setColumnWidth(3, 170); // Метод
|
||
ui->sgLog->setColumnWidth(4, 390); // Сообщение
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ КОМАНД
|
||
// ========================================================================
|
||
headers.clear();
|
||
headers << "Команда" << "Ответ";
|
||
ui->sgCommands->setHorizontalHeaderLabels(headers);
|
||
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); // Ответ
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ РАНДОМНЫХ ИНТЕРВАЛОВ
|
||
// ========================================================================
|
||
headers.clear();
|
||
headers << "Имя" << "От" << "До";
|
||
ui->sgRandomInt->setHorizontalHeaderLabels(headers);
|
||
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); // До
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА ТАБЛИЦЫ ВЕБ СЕРВЕРОВ
|
||
// ========================================================================
|
||
headers.clear();
|
||
headers << "Порт" << "Тип" << "Ссылка";
|
||
ui->sgWebServers->setColumnCount(headers.size());
|
||
ui->sgWebServers->setHorizontalHeaderLabels(headers);
|
||
ui->sgWebServers->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
ui->sgWebServers->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
ui->sgWebServers->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
|
||
// Настройка ширины колонок
|
||
ui->sgWebServers->setColumnWidth(0, 80); // Порт
|
||
ui->sgWebServers->setColumnWidth(1, 100); // Тип
|
||
ui->sgWebServers->setColumnWidth(2, 250); // Ссылка
|
||
|
||
// Подключение двойного клика
|
||
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);
|
||
|
||
// Подключение сигналов таблицы таймеров
|
||
connect(ui->sgTimers, &QTableWidget::cellClicked,
|
||
this, &uGeneral::on_sgTimers_cellClicked);
|
||
connect(ui->sgTimers, &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::on_sgTimers_cellDoubleClicked);
|
||
|
||
db->LoadTimers(ui->sgTimers, m_timers);
|
||
updateTimerTable();
|
||
|
||
|
||
// ========================================================================
|
||
// ИНИЦИАЛИЗАЦИЯ СПЕЦИАЛЬНЫХ ВИДЖЕТОВ
|
||
// ========================================================================
|
||
ui->widget->initForm("Звук", "sgSounds", true);
|
||
ui->widget_2->initForm("Файлы", "sgFiles", true);
|
||
ui->widget_3->initForm("Нейроконструктор", "sgNeiro");
|
||
|
||
// Подключение двойных кликов для специальных виджетов
|
||
connect(ui->widget->tableWidget(), &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::onSoundGridDoubleClicked);
|
||
connect(ui->widget_2->tableWidget(), &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::onFilesGridDoubleClicked);
|
||
connect(ui->widget_3->tableWidget(), &QTableWidget::cellDoubleClicked,
|
||
this, &uGeneral::onNeiroGridDoubleClicked);
|
||
|
||
|
||
}
|
||
|
||
/**
|
||
* @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->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;
|
||
context.soundManager = soundManager;
|
||
context.neuralManager = nnManager;
|
||
context.randomManager = m_randomManager;
|
||
context.neuralTemplateManager = m_neuralTemplateManager;
|
||
context.randomResponses = m_randomResponses;
|
||
context.mediaFileManager = m_SoundFiles;
|
||
context.channel = ui->edtChannel->text();
|
||
context.notifyVolume = ui->tbNotifyVolume->value();
|
||
|
||
m_commandProcessor->setContext(context);
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadCommandsFromTableWidget()
|
||
{
|
||
if (!db || !m_commandProcessor) return;
|
||
|
||
QTableWidget* table = ui->sgCommands;
|
||
if (!table) return;
|
||
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *commandItem = table->item(row, 0);
|
||
QTableWidgetItem *responseItem = table->item(row, 1);
|
||
|
||
if (commandItem && responseItem) {
|
||
QString command = commandItem->text().trimmed();
|
||
QString response = responseItem->text();
|
||
|
||
if (!command.isEmpty() && !response.isEmpty()) {
|
||
m_commandProcessor->addCommand(command, response);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Загрузка звуков
|
||
*/
|
||
void uGeneral::loadSoundsFromDatabase()
|
||
{
|
||
if (!db || !m_SoundFiles) return;
|
||
|
||
QTableWidget* table = ui->widget->tableWidget();
|
||
if (!table) return;
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *fileItem = table->item(row, 1);
|
||
if (nameItem && fileItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
QString file = fileItem->text().trimmed();
|
||
if (!name.isEmpty() && !file.isEmpty()) {
|
||
m_SoundFiles->addFile(name, file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Загрузка текстов
|
||
*/
|
||
void uGeneral::loadTextFromDatabase()
|
||
{
|
||
if (!db || !m_TextFiles) return;
|
||
|
||
QTableWidget* table = ui->widget_2->tableWidget();
|
||
if (!table) return;
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *fileItem = table->item(row, 1);
|
||
if (nameItem && fileItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
QString file = fileItem->text().trimmed();
|
||
if (!name.isEmpty() && !file.isEmpty()) {
|
||
m_TextFiles->addFile(name, file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Загрузка шаблонов
|
||
*/
|
||
void uGeneral::loadTemplatesFromDatabase()
|
||
{
|
||
if (!db || !m_neuralTemplateManager) return;
|
||
|
||
QTableWidget* table = ui->widget_3->tableWidget();
|
||
if (!table) return;
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *fileItem = table->item(row, 1);
|
||
if (nameItem && fileItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
QString file = fileItem->text().trimmed();
|
||
if (!name.isEmpty() && !file.isEmpty()) {
|
||
m_neuralTemplateManager->addTemplate(name, file);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* @brief Загрузка случайных диапазонов из таблицы sgRandomInt в RandomManager
|
||
*/
|
||
void uGeneral::loadRandomRangesFromTableWidget()
|
||
{
|
||
if (!db || !m_randomManager) return;
|
||
|
||
QTableWidget* table = ui->sgRandomInt;
|
||
if (!table) return;
|
||
|
||
for (int row = 0; row < table->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = table->item(row, 0);
|
||
QTableWidgetItem *startItem = table->item(row, 1);
|
||
QTableWidgetItem *endItem = table->item(row, 2);
|
||
|
||
if (nameItem && startItem && endItem) {
|
||
QString name = nameItem->text().trimmed();
|
||
int start = startItem->text().toInt();
|
||
int end = endItem->text().toInt();
|
||
|
||
if (!name.isEmpty() && start <= end) {
|
||
m_randomManager->addRange(name, start, end);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @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()) {
|
||
m_randomResponses->addResponse(groupName, response);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @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,
|
||
this, &uGeneral::handleConnected);
|
||
connect(m_twitchClient, &WebSocketClient::onDisconnected,
|
||
this, &uGeneral::handleDisconnected);
|
||
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); // Убираем отступы
|
||
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);
|
||
if (user) {
|
||
QString info = QString(
|
||
"Информация о пользователе:\n"
|
||
"Имя: %1\n"
|
||
"ID: %2\n"
|
||
"Логин: %3\n"
|
||
"Сообщений: %4\n"
|
||
"Модератор: %5\n"
|
||
"VIP: %6\n"
|
||
"Подписчик: %7\n"
|
||
"Последняя активность: %8"
|
||
).arg(user->displayName)
|
||
.arg(user->id)
|
||
.arg(user->login)
|
||
.arg(user->messageCount)
|
||
.arg(user->isModerator ? "Да" : "Нет")
|
||
.arg(user->isVIP ? "Да" : "Нет")
|
||
.arg(user->isSubscriber ? "Да" : "Нет")
|
||
.arg(user->lastMessageTime.isValid() ?
|
||
user->lastMessageTime.toString("dd.MM.yyyy hh:mm:ss") : "Неизвестно");
|
||
|
||
QMessageBox::information(this, "Информация о пользователе", info);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @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);
|
||
connect(m_authBot, &TAuth::serverStarted, this, [this](int port) {
|
||
LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен");
|
||
});
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА АВТОРИЗАЦИИ СТРИМЕРА
|
||
// ========================================================================
|
||
m_authStreamer = new TAuth(this);
|
||
connect(m_authStreamer, &TAuth::tokenReceived, this, &uGeneral::onTokenReceived2);
|
||
connect(m_authStreamer, &TAuth::errorOccurred, this, &uGeneral::onAuthError);
|
||
connect(m_authStreamer, &TAuth::serverStarted, this, [this](int port) {
|
||
LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен");
|
||
});
|
||
|
||
// ========================================================================
|
||
// НАСТРОЙКА АВТОРИЗАЦИИ DA
|
||
// ========================================================================
|
||
m_authDA = new TAuth(this);
|
||
connect(m_authDA, &TAuth::codeReceived, this, &uGeneral::onTokenReceived3);
|
||
connect(m_authDA, &TAuth::errorOccurred, this, &uGeneral::onAuthError);
|
||
connect(m_authDA, &TAuth::serverStarted, this, [this](int port) {
|
||
LogManager::instance()->info("General", "setupAuthHandlers", "Сервер авторизации запущен");
|
||
});
|
||
}
|
||
|
||
|
||
uGeneral::~uGeneral()
|
||
{
|
||
for (HttpServer *server : m_notificationServers) {
|
||
server->stop();
|
||
delete server;
|
||
}
|
||
m_notificationServers.clear();
|
||
|
||
for (HttpServerChat *server : m_chatServers) {
|
||
server->stop();
|
||
delete server;
|
||
}
|
||
m_chatServers.clear();
|
||
|
||
if (m_commandProcessor) {
|
||
delete m_commandProcessor;
|
||
m_commandProcessor = nullptr;
|
||
}
|
||
|
||
if (m_randomManager) {
|
||
delete m_randomManager;
|
||
m_randomManager = nullptr;
|
||
}
|
||
|
||
if (m_neuralTemplateManager) {
|
||
delete m_neuralTemplateManager;
|
||
m_neuralTemplateManager = nullptr;
|
||
}
|
||
delete m_createNotifyDialog;
|
||
delete m_createChatDialog;
|
||
delete ui;
|
||
delete m_userManager;
|
||
delete fLinkForm;
|
||
delete m_twitchClient;
|
||
delete m_authBot;
|
||
delete m_authStreamer;
|
||
delete m_authDA;
|
||
}
|
||
|
||
|
||
|
||
// ============================================================================
|
||
// ОТПРАВКА СООБЩЕНИЙ
|
||
// ============================================================================
|
||
|
||
|
||
|
||
|
||
|
||
// ============================================================================
|
||
// УПРАВЛЕНИЕ СЕРВЕРАМИ
|
||
// ============================================================================
|
||
|
||
|
||
void uGeneral::setTwitchConnected(bool connected)
|
||
{
|
||
m_isTwitchConnected = connected;
|
||
|
||
// Если подключение установлено, запускаем активные таймеры
|
||
if (connected) {
|
||
for (TimerInfo& timer : m_timers) {
|
||
if (timer.isActive && !timer.timer) {
|
||
startTimer(timer);
|
||
}
|
||
}
|
||
} else {
|
||
// Если подключение разорвано, останавливаем все таймеры
|
||
for (TimerInfo& timer : m_timers) {
|
||
if (timer.timer) {
|
||
stopTimer(timer);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
void uGeneral::loadEmoties()
|
||
{
|
||
bttvProvider.fetchGlobal();
|
||
sevenTVProvider.fetchGlobal();
|
||
User user = twitchAPI->getUserByLogin(ui->edtChannel->text());
|
||
bttvProvider.fetchCustom(user.id);
|
||
sevenTVProvider.fetchCustom(user.id);
|
||
}
|
||
|
||
void uGeneral::loadChatBadges()
|
||
{
|
||
if (!twitchAPI) return;
|
||
|
||
m_chatBadges.clear();
|
||
|
||
QVector<ChatBadge> globalBadges;
|
||
twitchAPI->getGlobalChatBadges(globalBadges);
|
||
m_chatBadges.append(globalBadges);
|
||
|
||
QVector<ChatBadge> customBadges;
|
||
twitchAPI->getCustomChatBadges(customBadges);
|
||
m_chatBadges.append(customBadges);
|
||
|
||
|
||
}
|
||
|
||
void uGeneral::updateTimerTable() {
|
||
ui->sgTimers->setRowCount(0);
|
||
|
||
for (const TimerInfo& timer : m_timers) {
|
||
int row = ui->sgTimers->rowCount();
|
||
ui->sgTimers->insertRow(row);
|
||
|
||
// Чекбокс "Вкл"
|
||
QTableWidgetItem* enabledItem = new QTableWidgetItem();
|
||
enabledItem->setCheckState(timer.isActive ? Qt::Checked : Qt::Unchecked);
|
||
ui->sgTimers->setItem(row, 0, enabledItem);
|
||
|
||
// Сообщение
|
||
QTableWidgetItem* messageItem = new QTableWidgetItem(timer.message);
|
||
ui->sgTimers->setItem(row, 1, messageItem);
|
||
|
||
// Интервал
|
||
QTableWidgetItem* intervalItem = new QTableWidgetItem(QString::number(timer.interval));
|
||
ui->sgTimers->setItem(row, 2, intervalItem);
|
||
|
||
// Чекбокс "О"
|
||
QTableWidgetItem* announcementItem = new QTableWidgetItem();
|
||
announcementItem->setCheckState(timer.isAnnouncement ? Qt::Checked : Qt::Unchecked);
|
||
ui->sgTimers->setItem(row, 3, announcementItem);
|
||
}
|
||
|
||
}
|
||
|
||
void uGeneral::startTimer(TimerInfo& timerInfo) {
|
||
// Не запускаем таймер, если нет подключения к Twitch
|
||
if (!m_isTwitchConnected) {
|
||
return;
|
||
}
|
||
|
||
if (timerInfo.timer) {
|
||
timerInfo.timer->stop();
|
||
delete timerInfo.timer;
|
||
}
|
||
|
||
timerInfo.timer = new QTimer(this);
|
||
connect(timerInfo.timer, &QTimer::timeout, this, [this, timerInfo]() {
|
||
sendTimedMessage(timerInfo);
|
||
});
|
||
|
||
timerInfo.timer->start(timerInfo.interval * 60000);
|
||
}
|
||
|
||
void uGeneral::stopTimer(TimerInfo& timerInfo) {
|
||
if (timerInfo.timer) {
|
||
timerInfo.timer->stop();
|
||
delete timerInfo.timer;
|
||
timerInfo.timer = nullptr;
|
||
}
|
||
}
|
||
|
||
void uGeneral::sendTimedMessage(const TimerInfo& timerInfo) {
|
||
// Не отправляем сообщение, если нет подключения
|
||
if (!m_isTwitchConnected) {
|
||
|
||
return;
|
||
}
|
||
|
||
if (timerInfo.isAnnouncement) {
|
||
// Отправка через 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();
|
||
QString intervalStr = ui->edtTimerInterval->text().trimmed();
|
||
|
||
if (message.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите сообщение!");
|
||
return;
|
||
}
|
||
|
||
bool ok;
|
||
int interval = intervalStr.toInt(&ok);
|
||
if (!ok || interval <= 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите корректный интервал в минутах!");
|
||
return;
|
||
}
|
||
|
||
TimerInfo newTimer;
|
||
newTimer.id = m_nextTimerId++;
|
||
newTimer.message = message;
|
||
newTimer.interval = interval;
|
||
newTimer.isActive = true; // По умолчанию включен
|
||
newTimer.isAnnouncement = false;
|
||
newTimer.timer = nullptr;
|
||
|
||
m_timers.append(newTimer);
|
||
|
||
if (m_isTwitchConnected) {
|
||
startTimer(m_timers.last());
|
||
}
|
||
|
||
updateTimerTable();
|
||
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
ui->edtTimerMessage->clear();
|
||
ui->edtTimerInterval->setText("10");
|
||
|
||
}
|
||
|
||
void uGeneral::on_btnTimerEdit_clicked() {
|
||
int currentRow = ui->sgTimers->currentRow();
|
||
if (currentRow < 0 || currentRow >= m_timers.size()) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите таймер для редактирования!");
|
||
return;
|
||
}
|
||
|
||
QString message = ui->edtTimerMessage->text().trimmed();
|
||
QString intervalStr = ui->edtTimerInterval->text().trimmed();
|
||
|
||
if (message.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите сообщение!");
|
||
return;
|
||
}
|
||
|
||
bool ok;
|
||
int interval = intervalStr.toInt(&ok);
|
||
if (!ok || interval <= 0) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите корректный интервал в минутах!");
|
||
return;
|
||
}
|
||
|
||
TimerInfo& timer = m_timers[currentRow];
|
||
bool wasActive = timer.isActive;
|
||
|
||
if (wasActive) {
|
||
stopTimer(timer);
|
||
}
|
||
|
||
timer.message = message;
|
||
timer.interval = interval;
|
||
|
||
if (wasActive && m_isTwitchConnected) { // Добавлена проверка подключения
|
||
startTimer(timer);
|
||
}
|
||
|
||
updateTimerTable();
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
|
||
void uGeneral::on_btnTimerDelete_clicked() {
|
||
int currentRow = ui->sgTimers->currentRow();
|
||
if (currentRow < 0 || currentRow >= m_timers.size()) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите таймер для удаления!");
|
||
return;
|
||
}
|
||
|
||
TimerInfo timer = m_timers[currentRow];
|
||
stopTimer(timer);
|
||
|
||
m_timers.removeAt(currentRow);
|
||
updateTimerTable();
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
|
||
void uGeneral::on_btnTimerTest_clicked() {
|
||
int currentRow = ui->sgTimers->currentRow();
|
||
if (currentRow < 0 || currentRow >= m_timers.size()) {
|
||
QMessageBox::warning(this, "Ошибка", "Выберите таймер для теста!");
|
||
return;
|
||
}
|
||
|
||
const TimerInfo& timer = m_timers[currentRow];
|
||
sendTimedMessage(timer);
|
||
}
|
||
|
||
void uGeneral::on_sgTimers_cellClicked(int row, int column)
|
||
{
|
||
if (row < 0 || row >= m_timers.size()) return;
|
||
|
||
TimerInfo& timer = m_timers[row];
|
||
|
||
if (column == 0) { // Чекбокс "Вкл"
|
||
bool newState = ui->sgTimers->item(row, column)->checkState() == Qt::Checked;
|
||
|
||
if (newState != timer.isActive) {
|
||
m_timers[row].isActive = newState;
|
||
|
||
if (newState) {
|
||
// Запускаем только если есть подключение
|
||
if (m_isTwitchConnected) {
|
||
startTimer(m_timers[row]);
|
||
}
|
||
} else {
|
||
stopTimer(m_timers[row]);
|
||
}
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
}
|
||
else if (column == 3) { // Чекбокс "О"
|
||
bool newState = ui->sgTimers->item(row, column)->checkState() == Qt::Checked;
|
||
|
||
if (newState != timer.isAnnouncement) {
|
||
m_timers[row].isAnnouncement = newState;
|
||
db->SaveTimers(ui->sgTimers, m_timers);
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::onTimerTimeout(int timerId)
|
||
{
|
||
|
||
}
|
||
|
||
void uGeneral::on_sgTimers_cellDoubleClicked(int row, int column) {
|
||
Q_UNUSED(column);
|
||
if (row < 0 || row >= m_timers.size()) return;
|
||
|
||
const TimerInfo& timer = m_timers[row];
|
||
ui->edtTimerMessage->setText(timer.message);
|
||
ui->edtTimerInterval->setText(QString::number(timer.interval));
|
||
}
|
||
|
||
|
||
void uGeneral::sendRequestToAI(const QString &q)
|
||
{
|
||
NeuralNetworkManager::NetworkType type;
|
||
if (ui->rbGC->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::GigaChat;
|
||
nnManager->setGigaChatCredentials(
|
||
ui->edtAIP1->text(), // RqUID
|
||
ui->edtAIP2->text() // Basic авторизация в base64
|
||
);
|
||
}
|
||
if (ui->rbCG->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::ChatGPT;
|
||
}
|
||
if (ui->rbDS->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::DeepSeek;
|
||
}
|
||
if (ui->RBCustom->isChecked())
|
||
{
|
||
type = NeuralNetworkManager::Ollama;
|
||
}
|
||
nnManager->setApiKey(type, ui->edtAIP1->text());
|
||
nnManager->setPrefix(ui->edtGPTPrefix->text());
|
||
// Настройка Ollama
|
||
nnManager->setOllamaUrl(ui->edtAIP2->text());
|
||
|
||
// Отправляем вопрос
|
||
nnManager->sendMessage(q, type);
|
||
}
|
||
|
||
void uGeneral::onSoundGridDoubleClicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("||" + ui->widget->tableWidget()->item(ui->widget->tableWidget()->currentRow(), 0)->text() + "||");
|
||
|
||
}
|
||
|
||
void uGeneral::onFilesGridDoubleClicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("|(" + ui->widget_2->tableWidget()->item(ui->widget_2->tableWidget()->currentRow(), 0)->text() + "|(");
|
||
}
|
||
|
||
void uGeneral::onNeiroGridDoubleClicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("<|" + ui->widget_3->tableWidget()->item(ui->widget_3->tableWidget()->currentRow(), 0)->text() + "<|");
|
||
}
|
||
|
||
void uGeneral::initTwitchAPI()
|
||
{
|
||
QString botClientID = ui->edtBotClientID->text();
|
||
QString botToken = ui->edtBotToken->text();
|
||
QString botTokenStreamer = ui->edtBotTokenStreamer->text();
|
||
QString channel = ui->edtChannel->text();
|
||
QString botName = ui->edtBotName->text();
|
||
|
||
if (botClientID.isEmpty() || botToken.isEmpty() ||
|
||
botTokenStreamer.isEmpty() || channel.isEmpty() || botName.isEmpty()) {
|
||
LogManager::instance()->warning("uGeneral", "initTwitchAPI",
|
||
"Не все данные для инициализации TTwAPI заполнены. API не инициализировано.");
|
||
return;
|
||
}
|
||
|
||
// Инициализируем только если не инициализировали ранее
|
||
if (twitchAPI) {
|
||
// Если уже инициализировано, можно обновить параметры
|
||
delete twitchAPI;
|
||
}
|
||
|
||
twitchAPI = new TTwAPI(this);
|
||
|
||
connect(twitchAPI, &TTwAPI::apiError, this, &uGeneral::onApiError);
|
||
connect(twitchAPI, &TTwAPI::tokenExpired, this, &uGeneral::onTokenExpired);
|
||
connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit);
|
||
|
||
twitchAPI->init(
|
||
botClientID,
|
||
botToken,
|
||
botTokenStreamer,
|
||
channel,
|
||
botName
|
||
);
|
||
|
||
LogManager::instance()->info("uGeneral", "initTwitchAPI", "TTwAPI успешно инициализировано");
|
||
}
|
||
|
||
|
||
void uGeneral::onApiError(const QString &method, const QString &error)
|
||
{
|
||
LogManager::instance()->error("ttw_api", method, error);
|
||
}
|
||
|
||
void uGeneral::onTokenExpired(const QString &error)
|
||
{
|
||
LogManager::instance()->error("uGeneral", "onTokenExpired", error);
|
||
}
|
||
|
||
void uGeneral::onRateLimit()
|
||
{
|
||
LogManager::instance()->warning("uGeneral", "onRateLimit", "Превышен лимит запросов");
|
||
}
|
||
|
||
UserManager* uGeneral::getUserManager()
|
||
{
|
||
return m_userManager;
|
||
}
|
||
|
||
|
||
// Обработчик ответа от AI
|
||
void uGeneral::onAIResponseReceived(const QString &response)
|
||
{
|
||
|
||
// Отправляем ответ в чат
|
||
if (m_twitchClient) {
|
||
// Обрезаем ответ, если он слишком длинный для Twitch
|
||
QString chatResponse = response;
|
||
if (chatResponse.length() > 400) {
|
||
chatResponse = chatResponse.left(397) + "...";
|
||
}
|
||
m_twitchClient->sendChatMessage(ui->edtChannel->text(), chatResponse);
|
||
}
|
||
}
|
||
|
||
// Обработчик ошибок AI
|
||
void uGeneral::onAIErrorOccurred(const QString &error)
|
||
{
|
||
LogManager::instance()->error("General", "onAIErrorOccurred", error);
|
||
|
||
// Можно отправить сообщение об ошибке в чат
|
||
if (m_twitchClient) {
|
||
m_twitchClient->sendChatMessage(ui->edtChannel->text(),
|
||
"Извините, нейросеть временно недоступна");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
void uGeneral::handleConnected()
|
||
{
|
||
|
||
LogManager::instance()->info("General", "handleConnected", "Connectiong");
|
||
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");
|
||
}
|
||
}
|
||
|
||
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) {
|
||
stopTimer(timer);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::closeEvent(QCloseEvent *event)
|
||
{
|
||
Q_UNUSED(event);
|
||
// Выполнение действий перед закрытием
|
||
}
|
||
|
||
void uGeneral::toCommands(QString command, QString response)
|
||
{
|
||
// Добавляем новую строку в таблицу
|
||
int row = ui->sgCommands->rowCount();
|
||
ui->sgCommands->insertRow(row);
|
||
QTableWidgetItem *commandItem = new QTableWidgetItem(command);
|
||
QTableWidgetItem *responseItem = new QTableWidgetItem(response);
|
||
ui->sgCommands->setItem(row, 0, commandItem);
|
||
ui->sgCommands->setItem(row, 1, responseItem);
|
||
ui->sgCommands->scrollToBottom();
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::onLogEntryAdded(const LogEntry& entry)
|
||
{
|
||
// Проверяем фильтры
|
||
if (!shouldShowLogEntry(entry.level)) {
|
||
return;
|
||
}
|
||
|
||
// Добавляем запись в таблицу
|
||
addLogToTable(entry, ui->sgLog->rowCount());
|
||
}
|
||
|
||
void uGeneral::onLogCleared()
|
||
{
|
||
ui->sgLog->setRowCount(0);
|
||
}
|
||
|
||
bool uGeneral::shouldShowLogEntry(LogLevel level)
|
||
{
|
||
switch (level) {
|
||
case LogLevel::Info:
|
||
return ui->cbInfo->isChecked();
|
||
case LogLevel::Warning:
|
||
return ui->cbWarning->isChecked();
|
||
case LogLevel::Error:
|
||
return ui->cbError->isChecked();
|
||
case LogLevel::Debug:
|
||
return ui->cbDebug->isChecked();
|
||
case LogLevel::Critical:
|
||
return true; // Критические ошибки всегда показываем
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
void uGeneral::addLogToTable(const LogEntry &entry, int row)
|
||
{
|
||
// Добавляем новую строку
|
||
ui->sgLog->insertRow(row);
|
||
|
||
// Получаем цвет для уровня из настроек 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);
|
||
|
||
ui->sgLog->setItem(row, 0, timeItem);
|
||
ui->sgLog->setItem(row, 1, typeItem);
|
||
ui->sgLog->setItem(row, 2, moduleItem);
|
||
ui->sgLog->setItem(row, 3, methodItem);
|
||
ui->sgLog->setItem(row, 4, messageItem);
|
||
|
||
ui->sgLog->scrollToBottom();
|
||
}
|
||
|
||
void uGeneral::applyLogFilter()
|
||
{
|
||
// Очищаем таблицу
|
||
ui->sgLog->setRowCount(0);
|
||
|
||
// Получаем все записи из LogManager
|
||
QList<LogEntry> allEntries = LogManager::instance()->allEntries();
|
||
|
||
// Добавляем только те записи, которые соответствуют фильтру
|
||
for (const LogEntry &entry : allEntries) {
|
||
if (shouldShowLogEntry(entry.level)) {
|
||
addLogToTable(entry, ui->sgLog->rowCount());
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadSettings(){
|
||
//авторизация твитча
|
||
ui->edtBotName->setText(db->readSetting(ui->edtBotName->objectName(),""));
|
||
ui->edtChannel->setText(db->readSetting(ui->edtChannel->objectName(),""));
|
||
ui->edtBotToken->setText(db->readSetting(ui->edtBotToken->objectName(),""));
|
||
ui->edtBotClientID->setText(db->readSetting(ui->edtBotClientID->objectName(),""));
|
||
ui->edtBotTokenStreamer->setText(db->readSetting(ui->edtBotTokenStreamer->objectName(),""));
|
||
ui->cbTTVAutoLogin->setChecked(db->readSetting(ui->cbTTVAutoLogin->objectName()) == "True");
|
||
|
||
//авторизация донейшон алертс
|
||
ui->edtDACode->setText(db->readSetting(ui->edtDACode->objectName(),""));
|
||
ui->edtDAClientID->setText(db->readSetting(ui->edtDAClientID->objectName(),""));
|
||
ui->edtDARedirectURL->setText(db->readSetting(ui->edtDARedirectURL->objectName(),""));
|
||
ui->edtDAClientSecret->setText(db->readSetting(ui->edtDAClientSecret->objectName(),""));
|
||
ui->cbDAAutoLogin->setChecked(db->readSetting(ui->cbDAAutoLogin->objectName()) == "True");
|
||
|
||
//Нейронки
|
||
ui->edtGPTPrefix->setText(db->readSetting(ui->edtGPTPrefix->objectName(),""));
|
||
ui->edtAIP1->setText(db->readSetting(ui->edtAIP1->objectName(),""));
|
||
ui->edtAIP2->setText(db->readSetting(ui->edtAIP2->objectName(),""));
|
||
ui->edtKandiKey->setText(db->readSetting(ui->edtKandiKey->objectName(),""));
|
||
ui->edtKandiSecret->setText(db->readSetting(ui->edtKandiSecret->objectName(),""));
|
||
int rg = db->readSetting("aiIndex","0").toInt();
|
||
|
||
switch (rg) {
|
||
case 0:
|
||
ui->rbGC->setChecked(true);
|
||
ui->rbDS->setChecked(false);
|
||
ui->rbCG->setChecked(false);
|
||
ui->RBCustom->setChecked(false);
|
||
break;
|
||
case 1:
|
||
ui->rbGC->setChecked(false);
|
||
ui->rbDS->setChecked(true);
|
||
ui->rbCG->setChecked(false);
|
||
ui->RBCustom->setChecked(false);
|
||
break;
|
||
case 2:
|
||
ui->rbGC->setChecked(false);
|
||
ui->rbDS->setChecked(false);
|
||
ui->rbCG->setChecked(true);
|
||
ui->RBCustom->setChecked(false);
|
||
break;
|
||
case 3:
|
||
ui->rbGC->setChecked(false);
|
||
ui->rbDS->setChecked(false);
|
||
ui->rbCG->setChecked(false);
|
||
ui->RBCustom->setChecked(true);
|
||
break;
|
||
}
|
||
|
||
//уведомления
|
||
ui->edtNotifyFileName->setText(db->readSetting(ui->edtNotifyFileName->objectName(),""));
|
||
ui->edtNotifyFileNameMod->setText(db->readSetting(ui->edtNotifyFileNameMod->objectName(),""));
|
||
ui->edtNotifyFileNameSub->setText(db->readSetting(ui->edtNotifyFileNameSub->objectName(),""));
|
||
ui->edtNotifyFileNameVip->setText(db->readSetting(ui->edtNotifyFileNameVip->objectName(),""));
|
||
ui->chEnNotify->setChecked(db->readSetting(ui->chEnNotify->objectName()) == "True");
|
||
ui->chEnNotifyMod->setChecked(db->readSetting(ui->chEnNotifyMod->objectName()) == "True");
|
||
ui->chEnNotifySub->setChecked(db->readSetting(ui->chEnNotifySub->objectName()) == "True");
|
||
ui->chEnNotifyVip->setChecked(db->readSetting(ui->chEnNotifyVip->objectName()) == "True");
|
||
ui->tbNotifyVolume->setValue(db->readSetting(ui->tbNotifyVolume->objectName()).toInt());
|
||
ui->tbNotifyVolumeMod->setValue(db->readSetting(ui->tbNotifyVolumeMod->objectName()).toInt());
|
||
ui->tbNotifyVolumeSub->setValue(db->readSetting(ui->tbNotifyVolumeSub->objectName()).toInt());
|
||
ui->tbNotifyVolumeVip->setValue(db->readSetting(ui->tbNotifyVolumeVip->objectName()).toInt());
|
||
ui->cbNotifyFileAgain1->setChecked(db->readSetting(ui->cbNotifyFileAgain1->objectName()) == "True");
|
||
ui->cbNotifyFileAgain2->setChecked(db->readSetting(ui->cbNotifyFileAgain2->objectName()) == "True");
|
||
ui->cbNotifyFileAgain3->setChecked(db->readSetting(ui->cbNotifyFileAgain3->objectName()) == "True");
|
||
|
||
db->LoadTableWidget(ui->sgCommands);
|
||
db->LoadTableWidget(ui->sgRandomInt);
|
||
|
||
|
||
FSingleGrid *form = ui->widget;
|
||
form->setDatabase(db);
|
||
QTableWidget *table = form->findChild<QTableWidget*>("sgSounds");
|
||
db->LoadTableWidget(table);
|
||
form = ui->widget_2;
|
||
form->setDatabase(db);
|
||
table = form->findChild<QTableWidget*>("sgFiles");
|
||
db->LoadTableWidget(table);
|
||
form = ui->widget_3;
|
||
form->setDatabase(db);
|
||
table = form->findChild<QTableWidget*>("sgNeiro");
|
||
db->LoadTableWidget(table);
|
||
db->LoadRandomGroups(ui->lbRandomGroup);
|
||
ui->cbTheme->setCurrentIndex(db->readSetting(ui->cbTheme->objectName(), "0").toInt());
|
||
|
||
db->LoadTableWidget(ui->sgWebServers);
|
||
loadSavedChats();
|
||
loadSavedNotifications();
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnGetTockenBot_clicked()
|
||
{
|
||
QString clientId = ui->edtBotClientID->text();
|
||
if (clientId.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите Client ID");
|
||
return;
|
||
}
|
||
|
||
QString scope = "moderator:manage:shoutouts+"
|
||
"moderator:manage:announcements+"
|
||
"moderator:manage:banned_users+"
|
||
"moderator:manage:warnings+"
|
||
"moderator:read:followers+"
|
||
"channel:manage:raids+"
|
||
"channel:manage:moderators+"
|
||
"channel:read:redemptions+"
|
||
"chat:read+"
|
||
"chat:edit+"
|
||
"user:read:emotes";
|
||
|
||
scope = scope.replace(":", "%3A");
|
||
|
||
QString authUrl = QString("https://id.twitch.tv/oauth2/authorize?"
|
||
"client_id=%1&"
|
||
"redirect_uri=http://localhost:8089&"
|
||
"response_type=token&"
|
||
"scope=%2")
|
||
.arg(clientId)
|
||
.arg(scope);
|
||
|
||
// Создаем и показываем окно с ссылкой
|
||
if (!fLinkForm) {
|
||
fLinkForm = new uLink(this);
|
||
}
|
||
|
||
// Устанавливаем ссылку в окно (предполагается, что в uLink есть метод setLink)
|
||
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);
|
||
}
|
||
|
||
void uGeneral::on_btnGetTokenStreamer_clicked()
|
||
{
|
||
|
||
//ykui0quht3tvr06vfqhdj5idmhginn
|
||
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+"
|
||
"user:read:email";
|
||
|
||
scope = scope.replace(":", "%3A");
|
||
|
||
QString authUrl = QString("https://id.twitch.tv/oauth2/authorize?"
|
||
"client_id=%1&"
|
||
"redirect_uri=http://localhost:8089&"
|
||
"response_type=token&"
|
||
"scope=%2")
|
||
.arg(clientId)
|
||
.arg(scope);
|
||
|
||
// Запускаем сервер с открытием браузера (true)
|
||
m_authStreamer->startServer(authUrl, true);
|
||
|
||
|
||
}
|
||
|
||
// Обработчик получения токена
|
||
void uGeneral::onTokenReceived(const QString &token)
|
||
{
|
||
ui->edtBotToken->setText(token);
|
||
}
|
||
|
||
void uGeneral::onTokenReceived2(const QString &token)
|
||
{
|
||
ui->edtBotTokenStreamer->setText(token);
|
||
fLinkForm->close();
|
||
}
|
||
|
||
void uGeneral::onTokenReceived3(const QString &token)
|
||
{
|
||
ui->edtDACode->setText(token);
|
||
}
|
||
|
||
void uGeneral::onAuthError(const QString &error)
|
||
{
|
||
|
||
}
|
||
|
||
void uGeneral::on_btnDAGetCode_clicked()
|
||
{
|
||
QString clientId = ui->edtDAClientID->text();
|
||
QString clientSecret = ui->edtDAClientSecret->text();
|
||
QString redirectUri = ui->edtDARedirectURL->text();
|
||
|
||
if (clientId.isEmpty() || clientSecret.isEmpty() || redirectUri.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Заполните все поля для DonationAlerts");
|
||
return;
|
||
}
|
||
QString encodedRedirectUri = QString::fromUtf8(QUrl::toPercentEncoding(redirectUri));
|
||
QString authUrl = QString("https://www.donationalerts.com/oauth/authorize?"
|
||
"client_id=%1&"
|
||
"redirect_uri=%2&"
|
||
"response_type=code&"
|
||
"scope=oauth-donation-index")
|
||
.arg(clientId)
|
||
.arg(encodedRedirectUri);
|
||
|
||
// Запускаем сервер с открытием браузера
|
||
m_authDA->startServer(authUrl, true);
|
||
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnImportSettings_clicked()
|
||
{
|
||
|
||
|
||
}
|
||
|
||
void uGeneral::on_btnExportSettings_clicked()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_RBCustom_pressed()
|
||
{
|
||
ui->lblAPI1->setText("API Token");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("URL");
|
||
ui->lblAPI2->setVisible(true);
|
||
ui->edtAIP2->setVisible(true);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(true);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Normal);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_rbGC_clicked()
|
||
{
|
||
ui->lblAPI1->setText("ClientID");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("Autorization Code");
|
||
ui->lblAPI2->setVisible(true);
|
||
ui->edtAIP2->setVisible(true);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(false);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_rbDS_clicked()
|
||
{
|
||
|
||
ui->lblAPI1->setText("API Token");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("");
|
||
ui->lblAPI2->setVisible(false);
|
||
ui->edtAIP2->setVisible(false);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(false);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_rbCG_clicked()
|
||
{
|
||
ui->lblAPI1->setText("API Token");
|
||
ui->lblAPI1->setVisible(true);
|
||
ui->edtAIP1->setVisible(true);
|
||
|
||
ui->lblAPI2->setText("");
|
||
ui->lblAPI2->setVisible(false);
|
||
ui->edtAIP2->setVisible(false);
|
||
|
||
ui->lblAPI3->setText("");
|
||
ui->lblAPI3->setVisible(false);
|
||
ui->edtAIP3->setVisible(false);
|
||
|
||
ui->cbOllama->setVisible(false);
|
||
ui->edtAIP1->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP2->setEchoMode(QLineEdit::Password);
|
||
ui->edtAIP3->setEchoMode(QLineEdit::Password);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheck_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileName->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolume->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheckMod_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileNameMod->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeMod->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
|
||
|
||
|
||
void uGeneral::on_btnNotifyCheckVip_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileNameVip->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeVip->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyCheckSub_clicked()
|
||
{
|
||
QString fn = ui->edtNotifyFileNameSub->text();
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeSub->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpen_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return; // Пользователь отменил выбор
|
||
}
|
||
ui->edtNotifyFileName->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpenMod_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return; // Пользователь отменил выбор
|
||
}
|
||
ui->edtNotifyFileNameMod->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpenVip_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return; // Пользователь отменил выбор
|
||
}
|
||
ui->edtNotifyFileNameVip->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::on_btnNotifyOpenSub_clicked()
|
||
{
|
||
QString sourceFile = QFileDialog::getOpenFileName(this,
|
||
"Выберите файл для уведомлений",
|
||
QDir::homePath(),
|
||
"Звуковой файл (*.mp3);;Все файлы (*.*)");
|
||
|
||
if (sourceFile.isEmpty()) {
|
||
return; // Пользователь отменил выбор
|
||
}
|
||
ui->edtNotifyFileNameSub->setText(sourceFile);
|
||
}
|
||
|
||
void uGeneral::playNotify(const bool &isMod, const bool &isVip, const bool &isSub)
|
||
{
|
||
// Проверяем модератора (самый высокий приоритет)
|
||
if (isMod && ui->chEnNotifyMod->isChecked() &&
|
||
!ui->edtNotifyFileNameMod->text().isEmpty())
|
||
{
|
||
QString fn;
|
||
if (ui->cbNotifyFileAgain1->isChecked())
|
||
fn = ui->edtNotifyFileName->text();
|
||
else
|
||
fn = ui->edtNotifyFileNameMod->text();
|
||
|
||
if (!fn.isEmpty())
|
||
{
|
||
// Проверяем, существует ли файл
|
||
if (QFile::exists(fn)) {
|
||
// Загружаем и воспроизводим звук
|
||
soundManager->loadSound(SoundManager::Channel2, fn);
|
||
soundManager->setVolume(SoundManager::Channel2, ui->tbNotifyVolumeMod->value());
|
||
soundManager->playSound(SoundManager::Channel2);
|
||
} 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->playSound(SoundManager::Channel2);
|
||
} else {
|
||
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Инициализирует звуковые уведомления
|
||
*/
|
||
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());
|
||
|
||
} else {
|
||
|
||
}
|
||
|
||
// Загружаем звук для модераторов
|
||
QString notifyModFile = ui->edtNotifyFileNameMod->text();
|
||
if (!notifyModFile.isEmpty() && QFile::exists(notifyModFile)) {
|
||
// Загружаем в отдельный канал, если SoundManager поддерживает несколько каналов
|
||
// Или используем ту же логику, что и в playNotify
|
||
|
||
}
|
||
|
||
// Загружаем звук для VIP
|
||
QString notifyVipFile = ui->edtNotifyFileNameVip->text();
|
||
if (!notifyVipFile.isEmpty() && QFile::exists(notifyVipFile)) {
|
||
|
||
}
|
||
|
||
// Загружаем звук для подписчиков
|
||
QString notifySubFile = ui->edtNotifyFileNameSub->text();
|
||
if (!notifySubFile.isEmpty() && QFile::exists(notifySubFile)) {
|
||
|
||
}
|
||
}
|
||
|
||
void uGeneral::handleNewMessage( const QString &message){
|
||
TwitchMessage msg = TwitchMessage::parse(message);
|
||
|
||
// Проверяем и регистрируем пользователя
|
||
QString userId = m_userManager->checkUser(msg.displayName, msg);
|
||
LogManager::instance()->debug("uGeneral", "handleNewMessage", message);
|
||
m_userManager->m_totalMessages++;
|
||
|
||
// Обновляем статистику и воспроизводим уведомление
|
||
if (m_userWidget) {
|
||
m_userWidget->updateStatistics();
|
||
}
|
||
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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @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<Replacement> replacements;
|
||
|
||
for (const QString &emote : emoteList) {
|
||
QStringList parts = emote.split(':');
|
||
if (parts.size() != 2) continue;
|
||
|
||
QString emoteId = parts[0];
|
||
QStringList positions = parts[1].split(',', Qt::SkipEmptyParts);
|
||
|
||
for (const QString &pos : positions) {
|
||
QStringList startEnd = pos.split('-');
|
||
if (startEnd.size() != 2) continue;
|
||
|
||
bool ok;
|
||
int start = startEnd[0].toInt(&ok);
|
||
if (!ok) continue;
|
||
int end = startEnd[1].toInt(&ok);
|
||
if (!ok) continue;
|
||
|
||
// Формируем HTML для эмодзи
|
||
QString emoteUrl = QString("https://static-cdn.jtvnw.net/emoticons/v2/%1/default/dark/1.0").arg(emoteId);
|
||
QString emoteHtml = QString("<img src=\"%1\" alt=\"%2\" style=\"height: 1em; vertical-align: middle;\">")
|
||
.arg(emoteUrl)
|
||
.arg(message.mid(start, end - start + 1));
|
||
|
||
replacements.append({start, end, emoteHtml});
|
||
}
|
||
}
|
||
|
||
// Сортируем по убыванию позиции начала, чтобы не сбивались индексы при замене
|
||
std::sort(replacements.begin(), replacements.end(),
|
||
[](const Replacement &a, const Replacement &b) {
|
||
return a.start > b.start;
|
||
});
|
||
|
||
// Выполняем замены
|
||
for (const auto &replacement : replacements) {
|
||
result.replace(replacement.start,
|
||
replacement.end - replacement.start + 1,
|
||
replacement.html);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
QString uGeneral::replaceCustomEmotes(const QString &message)
|
||
{
|
||
QString result = message;
|
||
|
||
// Используем регулярное выражение для поиска слов
|
||
QRegularExpression wordRegex("([a-zA-Z0-9_-]+)");
|
||
QRegularExpressionMatchIterator matches = wordRegex.globalMatch(message);
|
||
|
||
// Структура для хранения замен
|
||
struct EmoteReplacement {
|
||
int start;
|
||
int length;
|
||
QString html;
|
||
};
|
||
|
||
QList<EmoteReplacement> replacements;
|
||
|
||
while (matches.hasNext()) {
|
||
QRegularExpressionMatch match = matches.next();
|
||
QString word = match.captured(1);
|
||
int start = match.capturedStart(1);
|
||
int length = match.capturedLength(1);
|
||
|
||
// Пропускаем слишком короткие слова (меньше 2 символов)
|
||
if (word.length() < 2) continue;
|
||
|
||
// Проверяем BTTV эмодзи
|
||
QString bttvUrl = bttvProvider.getEmoteUrl(word);
|
||
if (!bttvUrl.isEmpty()) {
|
||
QString emoteHtml = QString("<img src=\"%1\" alt=\"%2\" style=\"height: 1em; vertical-align: middle;\">")
|
||
.arg(bttvUrl)
|
||
.arg(word);
|
||
replacements.append({start, length, emoteHtml});
|
||
continue;
|
||
}
|
||
|
||
// Проверяем 7TV эмодзи
|
||
QString sevenTVUrl = sevenTVProvider.getEmoteUrl(word);
|
||
if (!sevenTVUrl.isEmpty()) {
|
||
QString emoteHtml = QString("<img src=\"%1\" alt=\"%2\" style=\"height: 1em; vertical-align: middle;\">")
|
||
.arg(sevenTVUrl)
|
||
.arg(word);
|
||
replacements.append({start, length, emoteHtml});
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// Сортируем по убыванию позиции начала (заменяем с конца к началу)
|
||
std::sort(replacements.begin(), replacements.end(),
|
||
[](const EmoteReplacement &a, const EmoteReplacement &b) {
|
||
return a.start > b.start;
|
||
});
|
||
|
||
// Выполняем замены с конца к началу
|
||
for (const auto &replacement : replacements) {
|
||
result.replace(replacement.start, replacement.length, replacement.html);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
void uGeneral::on_pushButton_2_clicked()
|
||
{
|
||
if (ui->pushButton_2->text() != "Отключиться")
|
||
{
|
||
QString oauthToken = "oauth:" + ui->edtBotToken->text();
|
||
QString nickname = ui->edtBotName->text();
|
||
QString channel = ui->edtChannel->text();
|
||
m_twitchClient->connectToTwitchChat(oauthToken, nickname, channel);
|
||
ui->pushButton_2->setText("Отключиться");
|
||
}
|
||
else
|
||
{
|
||
m_twitchClient->disconnectFromServer();
|
||
m_userManager->clear();
|
||
ui->pushButton_2->setText("Подключиться");
|
||
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);
|
||
}
|
||
|
||
|
||
|
||
void uGeneral::on_pushButton_clicked()
|
||
{
|
||
LogManager::instance()->clear();
|
||
}
|
||
|
||
|
||
void uGeneral::on_sgCommands_cellDoubleClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
ui->edtCommand->setText(ui->sgCommands->item(row, 0)->text());
|
||
ui->textBrowser->setPlainText(ui->sgCommands->item(row, 1)->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnRandAdd_clicked()
|
||
{
|
||
// Получаем данные из полей ввода
|
||
QString name = ui->edtRandomName->text().trimmed();
|
||
int otValue = ui->edtOt->value();
|
||
int toValue = ui->edtTo->value();
|
||
|
||
// Проверяем, что имя не пустое
|
||
if (name.isEmpty()) {
|
||
QMessageBox::warning(this, "Внимание", "Введите название!");
|
||
ui->edtRandomName->setFocus();
|
||
return;
|
||
}
|
||
|
||
// Проверяем корректность диапазона
|
||
if (otValue > toValue) {
|
||
QMessageBox::warning(this, "Внимание",
|
||
"Значение 'От' не может быть больше значения 'До'!");
|
||
ui->edtOt->setFocus();
|
||
return;
|
||
}
|
||
|
||
m_randomManager->rangeAdded(name, otValue, toValue);
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnRandDel_clicked()
|
||
{
|
||
// Проверяем, есть ли выделенная строка
|
||
if (!ui->sgRandomInt->currentItem()) {
|
||
QMessageBox::warning(this, "Внимание", "Выберите строку для удаления!");
|
||
return;
|
||
}
|
||
|
||
// Получаем индекс выделенной строки
|
||
int row = ui->sgRandomInt->currentItem()->row();
|
||
|
||
m_randomManager->rangeRemoved(ui->sgRandomInt->item(row, 0)->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_sgRandomInt_cellClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
ui->edtRandomName->setText(ui->sgRandomInt->item(row, 0)->text());
|
||
ui->edtOt->setValue(ui->sgRandomInt->item(row, 1)->text().toInt());
|
||
ui->edtTo->setValue(ui->sgRandomInt->item(row, 2)->text().toInt());
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_sgRandomInt_cellDoubleClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[[" + ui->sgRandomInt->item(row, 0)->text() + "]]");
|
||
}
|
||
|
||
void uGeneral::on_lbRandomGroup_doubleClicked(const QModelIndex &index)
|
||
{
|
||
Q_UNUSED(index);
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("{{" + ui->lbRandomGroup->currentItem()->text() + "}}");
|
||
}
|
||
|
||
|
||
void uGeneral::on_lbRandomRespons_itemDoubleClicked(QListWidgetItem *item)
|
||
{
|
||
ui->edtRandomGroup->setText(ui->lbRandomGroup->currentItem()->text());
|
||
ui->edtRandomRespons->setText(item->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnRandomAdd_clicked()
|
||
{
|
||
QString groupName = ui->edtRandomGroup->text().trimmed();
|
||
QString response = ui->edtRandomRespons->text().trimmed();
|
||
|
||
// Проверка входных данных
|
||
if (groupName.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите название группы!");
|
||
return;
|
||
}
|
||
|
||
if (response.isEmpty()) {
|
||
QMessageBox::warning(this, "Ошибка", "Введите ответ!");
|
||
return;
|
||
}
|
||
|
||
// Добавляем ответ в базу данных
|
||
if (!db->AddGroupResponse(groupName, response)) {
|
||
QMessageBox::critical(this, "Ошибка",
|
||
"Не удалось добавить ответ в базу данных:\n" + db->lastError());
|
||
return;
|
||
}
|
||
|
||
// Обновляем список групп (если новая группа)
|
||
db->LoadRandomGroups(ui->lbRandomGroup);
|
||
|
||
// Находим и выбираем только что добавленную группу в списке
|
||
QList<QListWidgetItem*> items = ui->lbRandomGroup->findItems(groupName, Qt::MatchExactly);
|
||
if (!items.isEmpty()) {
|
||
ui->lbRandomGroup->setCurrentItem(items.first());
|
||
}
|
||
|
||
// Обновляем список ответов для выбранной группы
|
||
if (ui->lbRandomGroup->currentItem()) {
|
||
db->LoadRandomResponses(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons);
|
||
} else {
|
||
// Если группа не выбрана, очищаем список ответов
|
||
ui->lbRandomRespons->clear();
|
||
}
|
||
|
||
// Очищаем поля ввода
|
||
ui->edtRandomRespons->clear();
|
||
|
||
m_randomResponses->addResponse(groupName, response);
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnRandomDel_clicked()
|
||
{
|
||
db->DeleteResponse(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons->currentItem()->text());
|
||
db->LoadRandomResponses(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons);
|
||
m_randomResponses->removeResponse(ui->lbRandomGroup->currentItem()->text(), ui->lbRandomRespons->currentItem()->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnRmGroup_clicked()
|
||
{
|
||
db->DeleteGroup(ui->lbRandomGroup->currentItem()->text());
|
||
db->LoadRandomGroups(ui->lbRandomGroup);
|
||
m_randomResponses->removeGroup(ui->lbRandomGroup->currentItem()->text());
|
||
}
|
||
|
||
void uGeneral::on_btnGetDateFollow_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[FOLLOW]");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnGetAgeAccaunt_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[AGE]");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnGPT_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[AI]");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnAIPic_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[Kandinsky]");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnRandomUserName_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[RANDOMUSER]");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnGetChannelStat_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[STAT]");
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnAddCommand_clicked()
|
||
{
|
||
QString name = ui->edtCommand->text().trimmed();
|
||
QString response = ui->textBrowser->toPlainText();
|
||
|
||
if (name.isEmpty()) {
|
||
QMessageBox::warning(this, "Внимание", "Введите команду!");
|
||
LogManager::instance()->warning("uGeneral", "on_btnAddCommand_clicked",
|
||
"Попытка добавить команду с пустым именем");
|
||
return;
|
||
}
|
||
|
||
// Просто добавляем в таблицу и сохраняем в БД
|
||
// 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()) {
|
||
QMessageBox::warning(this, "Внимание", "Выберите команду для удаления!");
|
||
LogManager::instance()->warning("uGeneral", "on_btnRmCommand_clicked",
|
||
"Не выбрана команда для удаления");
|
||
return;
|
||
}
|
||
|
||
int row = ui->sgCommands->currentItem()->row();
|
||
QString commandName = ui->sgCommands->item(row, 0)->text();
|
||
|
||
ui->sgCommands->removeRow(row);
|
||
db->SaveTableWidget(ui->sgCommands);
|
||
m_commandProcessor->deleteCommand(commandName);
|
||
LogManager::instance()->info("uGeneral", "on_btnRmCommand_clicked",
|
||
QString("Удалена команда: %1").arg(commandName));
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnEdtCommand_clicked()
|
||
{
|
||
if (!ui->sgCommands->currentItem()) {
|
||
QMessageBox::warning(this, "Внимание", "Выберите команду для редактирования!");
|
||
LogManager::instance()->warning("uGeneral", "on_btnEdtCommand_clicked",
|
||
"Не выбрана команда для редактирования");
|
||
return;
|
||
}
|
||
|
||
int row = ui->sgCommands->currentItem()->row();
|
||
QString newName = ui->edtCommand->text().trimmed();
|
||
QString response = ui->textBrowser->toPlainText();
|
||
|
||
if (newName.isEmpty()) {
|
||
QMessageBox::warning(this, "Внимание", "Введите новое имя команды!");
|
||
return;
|
||
}
|
||
QString oldCommand = ui->sgCommands->item(row, 0)->text();
|
||
ui->sgCommands->item(row, 0)->setText(newName);
|
||
ui->sgCommands->item(row, 1)->setText(response);
|
||
db->SaveTableWidget(ui->sgCommands);
|
||
m_commandProcessor->editCommand(oldCommand, newName, response);
|
||
LogManager::instance()->info("uGeneral", "on_btnEdtCommand_clicked",
|
||
QString("Отредактирована команда в строке %1").arg(row));
|
||
}
|
||
|
||
void uGeneral::loadQssFiles()
|
||
{
|
||
// Очищаем ComboBox
|
||
ui->cbTheme->clear();
|
||
|
||
// Добавляем опцию "Без темы"
|
||
ui->cbTheme->addItem("Без темы", "");
|
||
// 1. Загружаем системные стили (из папки приложения)
|
||
QString systemStylesPath = FileManager::instance().getPath(FileManager::SystemStyles);
|
||
QDir systemStylesDir(systemStylesPath);
|
||
|
||
if (systemStylesDir.exists()) {
|
||
loadStylesFromDirectory(systemStylesDir, "Системные");
|
||
} else {
|
||
qWarning() << "Папка системных стилей не найдена:" << systemStylesPath;
|
||
|
||
// Создаем папку, если ее нет
|
||
systemStylesDir.mkpath(".");
|
||
}
|
||
|
||
// 2. Загружаем пользовательские стили (из папки данных пользователя)
|
||
QString userStylesPath = FileManager::instance().getPath(FileManager::Styles);
|
||
QDir userStylesDir(userStylesPath);
|
||
|
||
if (userStylesDir.exists()) {
|
||
loadStylesFromDirectory(userStylesDir, "Пользовательские");
|
||
} else {
|
||
qWarning() << "Папка пользовательских стилей не найдена:" << userStylesPath;
|
||
|
||
// Создаем папку
|
||
userStylesDir.mkpath(".");
|
||
|
||
|
||
}
|
||
|
||
// Проверяем, есть ли темы
|
||
if (ui->cbTheme->count() <= 1) { // Только "Без темы"
|
||
qWarning() << "Темы не найдены, создаем базовые";
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
void uGeneral::loadStylesFromDirectory(const QDir &stylesDir, const QString &category)
|
||
{
|
||
if (!stylesDir.exists()) {
|
||
return;
|
||
}
|
||
|
||
// Получаем список 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;
|
||
}
|
||
|
||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||
qWarning() << "Не удалось открыть QSS файл:" << filename;
|
||
return;
|
||
}
|
||
|
||
// Читаем содержимое файла
|
||
QTextStream stream(&file);
|
||
QString styleSheet = stream.readAll();
|
||
file.close();
|
||
|
||
// Применяем стили ко всему приложению
|
||
qApp->setStyleSheet(styleSheet);
|
||
|
||
}
|
||
|
||
void uGeneral::on_btnWSCreateChat_clicked()
|
||
{
|
||
// Создаем диалог каждый раз
|
||
FCreateChat *createDialog = new FCreateChat(db, this);
|
||
connect(createDialog, &FCreateChat::serverCreated, this, &uGeneral::onChatServerCreated);
|
||
connect(createDialog, &FCreateChat::serverUpdated, this, &uGeneral::onChatServerUpdated);
|
||
createDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createDialog->exec();
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnWSCreateNotify_clicked()
|
||
{
|
||
// Создаем диалог каждый раз
|
||
FCreateNotify *createDialog = new FCreateNotify(db, this);
|
||
connect(createDialog, &FCreateNotify::serverCreated, this, &uGeneral::onNotifyServerCreated);
|
||
createDialog->setAttribute(Qt::WA_DeleteOnClose); // Автоматическое удаление при закрытии
|
||
createDialog->exec();
|
||
}
|
||
|
||
|
||
|
||
// Слоты для приема созданных серверов:
|
||
void uGeneral::onChatServerCreated(HttpServerChat *server, const QString &name)
|
||
{
|
||
if (server) {
|
||
// Устанавливаем родителя - главное окно
|
||
server->setParent(this);
|
||
|
||
addChatServer(server, name);
|
||
}
|
||
}
|
||
|
||
void uGeneral::onNotifyServerCreated(HttpServer *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
// Сохраняем сервер в базе данных
|
||
if (!db->saveNotification(name, server)) {
|
||
|
||
}
|
||
|
||
// Добавляем сервер в список
|
||
m_notificationServers.append(server);
|
||
|
||
// Добавляем в таблицу
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
addServerToTable(name, "Уведомления", server->port(), url, server);
|
||
}
|
||
|
||
void uGeneral::onNotifyServerUpdated(HttpServer *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
// Найдем старый порт из таблицы
|
||
int oldPort = 0;
|
||
for (int row = 0; row < ui->sgWebServers->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(row, 0);
|
||
if (nameItem && nameItem->data(Qt::UserRole).value<QObject*>() == server) {
|
||
QTableWidgetItem *portItem = ui->sgWebServers->item(row, 2);
|
||
if (portItem) {
|
||
oldPort = portItem->text().toInt();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Обновляем сервер в базе данных
|
||
if (!db->updateNotification(name, server, oldPort)) {
|
||
|
||
}
|
||
|
||
// Обновляем сервер в списке
|
||
int index = m_notificationServers.indexOf(server);
|
||
if (index >= 0) {
|
||
m_notificationServers[index] = server;
|
||
}
|
||
|
||
// Обновляем строку в таблице
|
||
for (int row = 0; row < ui->sgWebServers->rowCount(); ++row) {
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(row, 0);
|
||
if (nameItem && nameItem->data(Qt::UserRole).value<QObject*>() == server) {
|
||
// Обновляем имя
|
||
nameItem->setText(name);
|
||
// Обновляем порт
|
||
ui->sgWebServers->item(row, 2)->setText(QString::number(server->port()));
|
||
// Обновляем ссылку
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
ui->sgWebServers->item(row, 3)->setText(url);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Методы добавления серверов:
|
||
void uGeneral::addNotificationServer(HttpServer *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
m_notificationServers.append(server);
|
||
|
||
QString serverName = name.isEmpty() ?
|
||
QString("Уведомления %1").arg(m_notificationServers.size()) : name;
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
|
||
addServerToTable(serverName, "Уведомления", server->port(), url, server);
|
||
}
|
||
|
||
void uGeneral::addChatServer(HttpServerChat *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
m_chatServers.append(server);
|
||
|
||
QString serverName = name.isEmpty() ?
|
||
QString("Чат %1").arg(m_chatServers.size()) : name;
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
|
||
addServerToTable(serverName, "Чат", server->port(), url, server);
|
||
}
|
||
|
||
void uGeneral::addServerToTable(const QString &name, const QString &type,
|
||
quint16 port, const QString &url, QObject *serverObj)
|
||
{
|
||
int row = ui->sgWebServers->rowCount();
|
||
ui->sgWebServers->insertRow(row);
|
||
|
||
// Колонка 0: Название
|
||
QTableWidgetItem *nameItem = new QTableWidgetItem(name);
|
||
nameItem->setData(Qt::UserRole, QVariant::fromValue<QObject*>(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;
|
||
notif.duration = 10;
|
||
notif.titleColor = "#FFFFFF";
|
||
notif.titleFamily = "Arial";
|
||
notif.titleSize = 20;
|
||
notif.contentColor = "#F5F5F5";
|
||
notif.contentFamily = "Arial";
|
||
notif.contentSize = 16;
|
||
notif.pageBackgroundColor = "transparent";
|
||
|
||
// Можно добавить звук для доната
|
||
if (type == "donation") {
|
||
notif.soundURL = "/sounds/donation.mp3";
|
||
}
|
||
|
||
server->addNotification(notif);
|
||
}
|
||
|
||
void uGeneral::addChatMessage(const QString &nickname,
|
||
const QString &message)
|
||
{
|
||
if (m_chatServers.isEmpty()) {
|
||
return;
|
||
}
|
||
|
||
QString formattedNickname = nickname;
|
||
|
||
for (HttpServerChat *server : qAsConst(m_chatServers)) {
|
||
sendMessageToServer(server, formattedNickname, message);
|
||
}
|
||
|
||
}
|
||
|
||
// Метод для получения HTML бейджей
|
||
QString uGeneral::getBadgesHTML(const TwitchMessage &msg)
|
||
{
|
||
QString badgesHtml;
|
||
|
||
// Если у сообщения есть бейджи
|
||
for (const TwitchMessage::Badge &badge : msg.badges) {
|
||
QString badgeUrl = getBadgeUrl(badge.name, badge.version);
|
||
|
||
if (!badgeUrl.isEmpty()) {
|
||
badgesHtml += QString("<img src=\"%1\" alt=\"%2\" title=\"%3\" "
|
||
"style=\"height: 18px; width: 18px; "
|
||
"vertical-align: middle; margin-right: 2px;\">")
|
||
.arg(badgeUrl)
|
||
.arg(badge.name)
|
||
.arg(QString("%1 (версия %2)").arg(badge.name).arg(badge.version));
|
||
}
|
||
}
|
||
|
||
// Добавляем системные бейджи, если их нет в списке
|
||
auto addSystemBadge = [&](const QString &badgeName, bool condition) {
|
||
if (condition) {
|
||
bool alreadyHas = false;
|
||
for (const TwitchMessage::Badge &badge : msg.badges) {
|
||
if (badge.name == badgeName) {
|
||
alreadyHas = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!alreadyHas) {
|
||
QString badgeUrl = getBadgeUrl(badgeName, 1);
|
||
if (!badgeUrl.isEmpty()) {
|
||
badgesHtml += QString("<img src=\"%1\" alt=\"%2\" title=\"%2\" "
|
||
"style=\"height: 18px; width: 18px; "
|
||
"vertical-align: middle; margin-right: 2px;\">")
|
||
.arg(badgeUrl)
|
||
.arg(badgeName);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
addSystemBadge("moderator", msg.isMod);
|
||
addSystemBadge("vip", msg.isVIP);
|
||
addSystemBadge("subscriber", msg.isSubscriber);
|
||
|
||
if (!badgesHtml.isEmpty()) {
|
||
badgesHtml += " ";
|
||
}
|
||
|
||
return badgesHtml;
|
||
|
||
}
|
||
|
||
// Метод для получения 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 для ретины
|
||
}
|
||
}
|
||
|
||
// Если точной версии не нашли, возвращаем первую версию
|
||
if (!badge.versions.isEmpty()) {
|
||
return badge.versions.first().imageUrl1x;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Стандартные Twitch бейджи (если не нашли в загруженных)
|
||
static const QMap<QString, QString> defaultBadgeUrls = {
|
||
{"moderator", "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/%1"},
|
||
{"vip", "https://static-cdn.jtvnw.net/badges/v1/1923c73d-70c9-4b1a-9c93-c8d8bd9e2f31/%1"},
|
||
{"subscriber", "https://static-cdn.jtvnw.net/badges/v1/5d9f2208-5dd8-11e7-8513-2ff4adfae661/%1"},
|
||
{"broadcaster", "https://static-cdn.jtvnw.net/badges/v1/5527c58c-fb7d-422d-b71b-f309dcb85cc1/%1"},
|
||
{"admin", "https://static-cdn.jtvnw.net/badges/v1/9ef7e029-4cdf-4d4d-a0d5-e2b3fb2583fe/%1"},
|
||
{"staff", "https://static-cdn.jtvnw.net/badges/v1/d97c37bd-a6f5-4c38-8f57-4e4bef88af34/%1"},
|
||
{"turbo", "https://static-cdn.jtvnw.net/badges/v1/bd444ec6-8f34-4bf9-91f4-af1e3428d80f/%1"},
|
||
{"partner", "https://static-cdn.jtvnw.net/badges/v1/d12a2e27-16f6-41d0-ab77-b780518f00a3/%1"},
|
||
{"bits", "https://static-cdn.jtvnw.net/badges/v1/73b5c3fb-24f9-4a82-a852-2f475b59411c/%1"}
|
||
};
|
||
|
||
if (defaultBadgeUrls.contains(setId)) {
|
||
return defaultBadgeUrls[setId].arg(versionId);
|
||
}
|
||
|
||
// Для 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();
|
||
|
||
if (!escapedColor.isEmpty() && escapedColor != "#000000") {
|
||
result += QString("<span style=\"color: %1;\">%2</span>")
|
||
.arg(escapedColor)
|
||
.arg(escapedName);
|
||
} else {
|
||
result += escapedName;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
// Вспомогательный метод для отправки сообщения на сервер
|
||
void uGeneral::sendMessageToServer(HttpServerChat *server, const QString &nickname,
|
||
const QString &message)
|
||
{
|
||
if (!server) return;
|
||
|
||
// Создаем стиль сообщения
|
||
StyleChat style;
|
||
style.nick = nickname;
|
||
style.context = message;
|
||
|
||
// Используем настройки сервера
|
||
style.fontFamily = server->getFontFamily();
|
||
style.fontSize = server->getFontSize();
|
||
style.fontColor = server->getFontColor();
|
||
style.blockColor = server->getBlockColor();
|
||
style.borderColor = server->getBorderColor();
|
||
style.borderSize = server->getBorderSize();
|
||
style.padding = server->getPadding();
|
||
style.timeMsg = server->getMessageTimeout();
|
||
style.bColor = server->getBackgroundColor();
|
||
style.transparency = server->getTransparency();
|
||
// Убираем HTML теги из обычного текста для совместимости
|
||
// (если сервер их не поддерживает, можно оставить только для ника)
|
||
style.nick = nickname; // Ник уже с цветом
|
||
style.context = message; // Сообщение уже обработано с эмодзи
|
||
|
||
server->addMessage(style);
|
||
}
|
||
|
||
// Обработчик двойного клика по таблице веб-серверов
|
||
void uGeneral::on_sgWebServers_cellDoubleClicked(int row, int column)
|
||
{
|
||
Q_UNUSED(column);
|
||
|
||
if (row < 0 || row >= ui->sgWebServers->rowCount()) {
|
||
return;
|
||
}
|
||
|
||
QTableWidgetItem *nameItem = ui->sgWebServers->item(row, 0);
|
||
QTableWidgetItem *typeItem = ui->sgWebServers->item(row, 1);
|
||
|
||
if (!nameItem || !typeItem) {
|
||
return;
|
||
}
|
||
|
||
QString serverType = typeItem->text();
|
||
|
||
if (serverType.toLower() == "чат") {
|
||
// Получаем объект сервера из user data
|
||
QObject *serverObj = nameItem->data(Qt::UserRole).value<QObject*>();
|
||
HttpServerChat *existingServer = qobject_cast<HttpServerChat*>(serverObj);
|
||
|
||
if (existingServer) {
|
||
FCreateChat *createChatDialog = new FCreateChat(db, this);
|
||
createChatDialog->loadExistingServer(existingServer, nameItem->text());
|
||
connect(createChatDialog, &FCreateChat::serverUpdated, this, &uGeneral::onChatServerUpdated);
|
||
createChatDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createChatDialog->exec();
|
||
}
|
||
} else if (serverType.toLower() == "уведомления") {
|
||
// Получаем объект сервера из user data
|
||
QObject *serverObj = nameItem->data(Qt::UserRole).value<QObject*>();
|
||
HttpServer *existingServer = qobject_cast<HttpServer*>(serverObj);
|
||
|
||
if (existingServer) {
|
||
FCreateNotify *createNotifyDialog = new FCreateNotify(db, this);
|
||
createNotifyDialog->loadExistingServer(existingServer, nameItem->text());
|
||
connect(createNotifyDialog, &FCreateNotify::serverUpdated,
|
||
this, &uGeneral::onNotifyServerUpdated);
|
||
createNotifyDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||
createNotifyDialog->exec();
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::loadSavedNotifications()
|
||
{
|
||
QList<NotificationSettings> notifications = db->loadAllNotifications();
|
||
|
||
for (const NotificationSettings &settings : notifications) {
|
||
if (settings.type.toLower() == "notification") {
|
||
// Создаем сервер уведомлений
|
||
HttpServer *server = new HttpServer(this);
|
||
|
||
// Настраиваем сервер
|
||
server->setBlockColor(settings.blockColor);
|
||
server->setBorderColor(settings.borderColor);
|
||
server->setBorderSize(settings.borderSize);
|
||
server->setTransparency(settings.transparency);
|
||
server->setPageBackgroundColor(settings.pageBackgroundColor);
|
||
server->setTitleFont(
|
||
settings.titleFamily,
|
||
settings.titleSize,
|
||
settings.titleColor
|
||
);
|
||
server->setContentFont(
|
||
settings.contentFamily,
|
||
settings.contentSize,
|
||
settings.contentColor
|
||
);
|
||
server->setDuration(settings.duration);
|
||
|
||
// Запускаем сервер
|
||
if (server->start(settings.port)) {
|
||
m_notificationServers.append(server);
|
||
|
||
// Добавляем в таблицу
|
||
QString url = QString("http://localhost:%1").arg(settings.port);
|
||
addServerToTable(settings.name, "Уведомления", settings.port, url, server);
|
||
|
||
|
||
} else {
|
||
delete server;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::onChatServerUpdated(HttpServerChat *server, const QString &name)
|
||
{
|
||
if (!server) return;
|
||
|
||
// Обновляем сервер в списке
|
||
int index = m_chatServers.indexOf(server);
|
||
if (index >= 0) {
|
||
m_chatServers[index] = server;
|
||
|
||
// Обновляем строку в таблице
|
||
for (int row = 0; row < ui->sgWebServers->rowCount(); ++row) {
|
||
QTableWidgetItem *portItem = ui->sgWebServers->item(row, 0);
|
||
if (portItem && portItem->text().toUShort() == server->port()) {
|
||
// Обновляем имя (если изменилось)
|
||
ui->sgWebServers->setItem(row, 0, new QTableWidgetItem(name));
|
||
|
||
// Обновляем порт (если изменился)
|
||
ui->sgWebServers->setItem(row, 0, new QTableWidgetItem(QString::number(server->port())));
|
||
|
||
// Обновляем ссылку
|
||
QString url = QString("http://localhost:%1").arg(server->port());
|
||
ui->sgWebServers->setItem(row, 2, new QTableWidgetItem(url));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_www_currentChanged(int index)
|
||
{
|
||
|
||
}
|
||
|
||
|
||
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)
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtBotToken_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotToken->objectName(), ui->edtBotToken->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtBotTokenStreamer_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotTokenStreamer->objectName(), ui->edtBotTokenStreamer->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtBotClientID_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtBotClientID->objectName(), ui->edtBotClientID->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtChannel_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtChannel->objectName(), ui->edtChannel->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtDAClientID_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDAClientID->objectName(), ui->edtDAClientID->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtDAClientSecret_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDAClientSecret->objectName(), ui->edtDAClientSecret->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtDARedirectURL_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDARedirectURL->objectName(), ui->edtDARedirectURL->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtDACode_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtDACode->objectName(), ui->edtDACode->text());
|
||
}
|
||
|
||
|
||
|
||
void uGeneral::on_cbDAAutoLogin_stateChanged(int arg1)
|
||
{
|
||
db->writeSetting(ui->cbDAAutoLogin->objectName(), ui->cbDAAutoLogin->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtGPTPrefix_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtGPTPrefix->objectName(), ui->edtGPTPrefix->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtAIP1_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtAIP1->objectName(), ui->edtAIP1->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtAIP2_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtAIP2->objectName(), ui->edtAIP2->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtAIP3_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtAIP3->objectName(), ui->edtAIP3->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtKandiKey_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtKandiKey->objectName(), ui->edtKandiKey->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtKandiSecret_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtKandiSecret->objectName(), ui->edtKandiSecret->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_cbOllama_stateChanged(int arg1)
|
||
{
|
||
db->writeSetting(ui->cbOllama->objectName(), ui->cbOllama->isChecked() ? "True" : "False");
|
||
}
|
||
|
||
|
||
void uGeneral::on_tbNotifyVolume_valueChanged(int value)
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtNotifyFileName_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileName->objectName(), ui->edtNotifyFileName->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtNotifyFileNameMod_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileNameMod->objectName(), ui->edtNotifyFileNameMod->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtNotifyFileNameVip_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileNameVip->objectName(), ui->edtNotifyFileNameVip->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_edtNotifyFileNameSub_editingFinished()
|
||
{
|
||
db->writeSetting(ui->edtNotifyFileNameSub->objectName(), ui->edtNotifyFileNameSub->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_chEnNotify_stateChanged(int arg1)
|
||
{
|
||
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");
|
||
}
|
||
|
||
|
||
void uGeneral::on_chEnNotifyVip_stateChanged(int 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");
|
||
}
|
||
|
||
|
||
void uGeneral::on_tbNotifyVolume_sliderPressed()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void uGeneral::on_tbNotifyVolume_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolume->objectName(), QString::number(ui->tbNotifyVolume->value()));
|
||
}
|
||
|
||
|
||
void uGeneral::on_tbNotifyVolumeMod_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolumeMod->objectName(), QString::number(ui->tbNotifyVolumeMod->value()));
|
||
}
|
||
|
||
|
||
void uGeneral::on_tbNotifyVolumeVip_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolumeVip->objectName(), QString::number(ui->tbNotifyVolumeVip->value()));
|
||
}
|
||
|
||
|
||
void uGeneral::on_tbNotifyVolumeSub_sliderReleased()
|
||
{
|
||
db->writeSetting(ui->tbNotifyVolumeSub->objectName(), QString::number(ui->tbNotifyVolumeSub->value()));
|
||
}
|
||
|
||
|
||
void uGeneral::on_cbNotifyFileAgain1_stateChanged(int arg1)
|
||
{
|
||
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");
|
||
}
|
||
|
||
|
||
void uGeneral::on_cbNotifyFileAgain3_stateChanged(int 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<ChatSettings> chats = db->loadAllChats();
|
||
|
||
for (const ChatSettings &settings : chats) {
|
||
if (settings.type.toLower() == "chat") {
|
||
// Создаем сервер чата
|
||
HttpServerChat *server = new HttpServerChat(
|
||
settings.fontList,
|
||
settings.port,
|
||
settings.backgroundColor,
|
||
this
|
||
);
|
||
|
||
// Настраиваем сервер
|
||
server->setBlockColor(settings.blockColor);
|
||
server->setBorderColor(settings.borderColor);
|
||
server->setBorderSize(settings.borderSize);
|
||
server->setPadding(settings.padding);
|
||
server->setTransparency(settings.transparency);
|
||
server->setFontFamily(settings.fontFamily);
|
||
server->setFontSize(settings.fontSize);
|
||
server->setFontColor(settings.fontColor);
|
||
server->setFreez(settings.freez);
|
||
server->setMessageTimeout(settings.messageTimeout);
|
||
server->setDeleteMode(settings.deleteByTime, settings.maxMsgCount);
|
||
|
||
// Запускаем сервер
|
||
if (server->start()) {
|
||
m_chatServers.append(server);
|
||
|
||
// Добавляем в таблицу
|
||
QString url = QString("http://localhost:%1").arg(settings.port);
|
||
addServerToTable(settings.name, "Чат", settings.port, url, server);
|
||
|
||
|
||
} else {
|
||
delete server;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void uGeneral::on_btnOpenStream_clicked()
|
||
{
|
||
QDesktopServices::openUrl("https://www.twitch.tv/" + ui->edtChannel->text());
|
||
}
|
||
|
||
|
||
void uGeneral::on_btnAUserName_clicked()
|
||
{
|
||
QTextCursor cursor = ui->textBrowser->textCursor();
|
||
cursor.insertText("[USERNAME]");
|
||
}
|
||
|