Files
TTW_Bot/ugeneral.cpp
T

3193 lines
108 KiB
C++
Raw Blame History

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