починил нейроконструктор, исправил распознавание сообщений из чата

This commit is contained in:
2026-02-08 17:29:49 +03:00
parent 451ddd9ae0
commit cbb875f3f8
15 changed files with 531 additions and 223 deletions
+2
View File
@@ -34,6 +34,7 @@ SOURCES += \
main.cpp \ main.cpp \
mediafilemanager.cpp \ mediafilemanager.cpp \
neuralnetworkmanager.cpp \ neuralnetworkmanager.cpp \
neuraltemplatemanager.cpp \
randommanager.cpp \ randommanager.cpp \
randomresponses.cpp \ randomresponses.cpp \
soundmanager.cpp \ soundmanager.cpp \
@@ -65,6 +66,7 @@ HEADERS += \
mediafilemanager.h \ mediafilemanager.h \
miniaudio.h \ miniaudio.h \
neuralnetworkmanager.h \ neuralnetworkmanager.h \
neuraltemplatemanager.h \
randommanager.h \ randommanager.h \
randomresponses.h \ randomresponses.h \
soundmanager.h \ soundmanager.h \
+134 -3
View File
@@ -1,4 +1,5 @@
#include "commandprocessor.h" #include "commandprocessor.h"
#include "neuraltemplatemanager.h"
#include <QRegularExpression> #include <QRegularExpression>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QFile> #include <QFile>
@@ -6,6 +7,12 @@
#include <QEventLoop> #include <QEventLoop>
#include <QTimer> #include <QTimer>
struct Replacement {
int start;
int length;
QString text;
};
CommandProcessor::CommandProcessor(QObject *parent) CommandProcessor::CommandProcessor(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
@@ -48,14 +55,13 @@ QString CommandProcessor::generateResponse(QString userIndex, const QString &com
Command cmd = findCommand(command); Command cmd = findCommand(command);
if (cmd.command.isEmpty()) { if (cmd.command.isEmpty()) {
qDebug() << "Команда не найдена:" << command; qDebug() << "Команда не найдена:" << command;
return QString("Команда '%1' не найдена").arg(command); return "";
} }
qDebug() << "Найдена команда:" << cmd.command << "ответ:" << cmd.response; qDebug() << "Найдена команда:" << cmd.command << "ответ:" << cmd.response;
QString fullCommand = command + (message.isEmpty() ? "" : " " + message); QString fullCommand = command + (message.isEmpty() ? "" : " " + message);
QString result = processCommand(username, fullCommand, cmd.response); QString result = processCommand(username, fullCommand, cmd.response);
qDebug() << "Итоговый результат:" << result; qDebug() << "Итоговый результат:" << result;
return result; return result;
} }
@@ -111,6 +117,9 @@ QString CommandProcessor::processCommand(const QString &sender, const QString &f
break; break;
} }
} }
response = parseNeuralTemplates(response, sender, parameters);
response = parseRandomNumbers(response); // Затем обрабатываем случайные числа ВНУТРИ них response = parseRandomNumbers(response); // Затем обрабатываем случайные числа ВНУТРИ них
response = parseSounds(response); response = parseSounds(response);
response = parseTextFiles(response); response = parseTextFiles(response);
@@ -372,7 +381,7 @@ QString CommandProcessor::parseAI(const QString &response, const QString &questi
eventLoop.quit(); eventLoop.quit();
}); });
m_context.neuralManager->sendMessage(question, NeuralNetworkManager::DeepSeek); m_context.neuralManager->sendMessage(question);
QTimer::singleShot(60000, &eventLoop, &QEventLoop::quit); QTimer::singleShot(60000, &eventLoop, &QEventLoop::quit);
eventLoop.exec(); eventLoop.exec();
@@ -474,3 +483,125 @@ QString CommandProcessor::getPeriodEnding(int n, int r)
return endings[r][2]; return endings[r][2];
} }
} }
QString CommandProcessor::parseNeuralTemplates(const QString &response, const QString &sender, const QString &parameters)
{
QString result = response;
qDebug() << "parseNeuralTemplates: входная строка:" << response;
// Исправленное регулярное выражение
QRegularExpression regex("<\\|([^<]+)<\\|");
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
QList<Replacement> replacements;
int matchCount = 0;
while (matches.hasNext()) {
matchCount++;
QRegularExpressionMatch match = matches.next();
QString templateName = match.captured(1).trimmed();
qDebug() << "Найден шаблон:" << templateName << "на позиции" << match.capturedStart();
if (m_context.neuralTemplateManager) {
QString templateText = m_context.neuralTemplateManager->getTemplateText(templateName);
qDebug() << "Текст шаблона:" << templateText;
if (!templateText.isEmpty()) {
// Заменяем плейсхолдеры в шаблоне
QString processedTemplate = templateText;
processedTemplate.replace("[TO]", parameters);
processedTemplate.replace("[USERNAME]", sender);
qDebug() << "Шаблон после замены плейсхолдеров:" << processedTemplate;
// Получаем ответ от нейросети
QString aiResponse;
if (m_context.neuralManager) {
// Создаем event loop для асинхронного ожидания
QEventLoop eventLoop;
bool responseReceived = false;
bool errorOccurred = false;
QString errorMessage;
// Подключаем сигналы от нейросети
auto conn1 = connect(m_context.neuralManager, &NeuralNetworkManager::responseReceived,
[&](const QString &responseText) {
aiResponse = responseText;
responseReceived = true;
eventLoop.quit();
});
auto conn2 = connect(m_context.neuralManager, &NeuralNetworkManager::errorOccurred,
[&](const QString &error) {
errorMessage = error;
errorOccurred = true;
eventLoop.quit();
});
// Отправляем запрос к нейросети
qDebug() << "Отправляем запрос к нейросети:" << processedTemplate;
m_context.neuralManager->sendMessage(processedTemplate);
// Таймаут 30 секунд
QTimer::singleShot(30000, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
disconnect(conn1);
disconnect(conn2);
if (errorOccurred) {
aiResponse = QString("Ошибка нейросети: %1").arg(errorMessage);
qDebug() << "Ошибка нейросети:" << errorMessage;
} else if (!responseReceived) {
aiResponse = "Таймаут при ожидании ответа от нейросети";
qDebug() << "Таймаут нейросети";
} else {
qDebug() << "Получен ответ от нейросети:" << aiResponse;
}
} else {
aiResponse = "Нейросеть недоступна";
qDebug() << "NeuralManager не доступен";
}
// Заменяем шаблон на ответ от нейросети
Replacement repl;
repl.start = match.capturedStart();
repl.length = match.capturedLength();
repl.text = aiResponse;
replacements.append(repl);
} else {
qDebug() << "Шаблон не найден или пустой:" << templateName;
// Если шаблон не найден, оставляем как есть или заменяем на заглушку
Replacement repl;
repl.start = match.capturedStart();
repl.length = match.capturedLength();
repl.text = "[Шаблон не найден]";
replacements.append(repl);
}
} else {
qDebug() << "NeuralTemplateManager не доступен";
// Если менеджер не доступен, заменяем на заглушку
Replacement repl;
repl.start = match.capturedStart();
repl.length = match.capturedLength();
repl.text = "[Нейросеть недоступна]";
replacements.append(repl);
}
}
qDebug() << "Всего найдено шаблонов:" << matchCount;
// Выполняем замены с конца к началу
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.length, replacement.text);
}
qDebug() << "parseNeuralTemplates: результат:" << result;
return result;
}
+3
View File
@@ -13,6 +13,7 @@
#include "neuralnetworkmanager.h" #include "neuralnetworkmanager.h"
#include "randommanager.h" #include "randommanager.h"
#include "randomresponses.h" #include "randomresponses.h"
#include "neuraltemplatemanager.h"
class CommandProcessor : public QObject class CommandProcessor : public QObject
{ {
@@ -32,6 +33,7 @@ public:
RandomManager* randomManager = nullptr; RandomManager* randomManager = nullptr;
RandomResponses* randomResponses = nullptr; RandomResponses* randomResponses = nullptr;
MediaFileManager* mediaFileManager = nullptr; MediaFileManager* mediaFileManager = nullptr;
NeuralTemplateManager *neuralTemplateManager = nullptr;
QString channel; QString channel;
int notifyVolume = 50; int notifyVolume = 50;
}; };
@@ -61,6 +63,7 @@ private:
QString parseBan(const QString &response, const QString &sender); QString parseBan(const QString &response, const QString &sender);
QString parseAPI(const QString &response, const QString &sender); QString parseAPI(const QString &response, const QString &sender);
QString parseAI(const QString &response, const QString &question); QString parseAI(const QString &response, const QString &question);
QString parseNeuralTemplates(const QString &response, const QString &sender, const QString &parameters);
QString extractParameters(const QString &fullCommand); QString extractParameters(const QString &fullCommand);
QString getUsernameByIndex(QString userIndex) const; QString getUsernameByIndex(QString userIndex) const;
-18
View File
@@ -162,22 +162,4 @@ QString FileManager::getWebPath(FileType type, const QString& fileName) const
return QString("/%1/%2").arg(folder).arg(fileName); return QString("/%1/%2").arg(folder).arg(fileName);
} }
void FileManager::copyDefaultFiles()
{
// Копируем системные стили в пользовательскую папку при первом запуске
QString systemStylesPath = getPath(SystemStyles);
QDir systemStylesDir(systemStylesPath);
if (systemStylesDir.exists()) {
QStringList qssFiles = systemStylesDir.entryList(QStringList() << "*.qss" << "*.QSS", QDir::Files);
for (const QString& file : qssFiles) {
QString sourcePath = systemStylesDir.absoluteFilePath(file);
QString destPath = getFullPath(Styles, file);
if (!QFile::exists(destPath)) {
QFile::copy(sourcePath, destPath);
}
}
}
}
+1 -2
View File
@@ -59,8 +59,7 @@ public:
// Получение относительного пути для веб-сервера // Получение относительного пути для веб-сервера
QString getWebPath(FileType type, const QString& fileName) const; QString getWebPath(FileType type, const QString& fileName) const;
// Копирование файлов из системных в пользовательские (при первом запуске)
void copyDefaultFiles();
private: private:
FileManager(); FileManager();
+29 -10
View File
@@ -124,6 +124,18 @@ void NeuralNetworkManager::setupDefaultConfigs()
networkConfigs[Ollama] = ollamaConfig; networkConfigs[Ollama] = ollamaConfig;
} }
void NeuralNetworkManager::setCurrentNetworkType(NetworkType type)
{
QMutexLocker locker(&mutex);
m_currentNetworkType = type;
}
void NeuralNetworkManager::sendMessage(const QString &message)
{
// Используем текущий тип сети
sendMessage(message, m_currentNetworkType);
}
void NeuralNetworkManager::sendMessage(const QString &message, NetworkType networkType) void NeuralNetworkManager::sendMessage(const QString &message, NetworkType networkType)
{ {
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
@@ -138,8 +150,6 @@ void NeuralNetworkManager::sendMessage(const QString &message, NetworkType netwo
qint64 currentTime = QDateTime::currentSecsSinceEpoch(); qint64 currentTime = QDateTime::currentSecsSinceEpoch();
if (!isGigaChatTokenValid()) { if (!isGigaChatTokenValid()) {
// Добавляем сообщение в очередь // Добавляем сообщение в очередь
PendingMessage pendingMsg; PendingMessage pendingMsg;
pendingMsg.message = message; pendingMsg.message = message;
@@ -154,7 +164,6 @@ void NeuralNetworkManager::sendMessage(const QString &message, NetworkType netwo
return; return;
} }
} }
// Если токен валиден, отправляем сообщение сразу // Если токен валиден, отправляем сообщение сразу
sendMessageInternal(message, networkType); sendMessageInternal(message, networkType);
} }
@@ -424,17 +433,15 @@ void NeuralNetworkManager::handleGigaChatTokenReply(QNetworkReply *reply)
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
QString errorMsg = QString("Ошибка получения токена GigaChat: %1").arg(reply->errorString()); QString errorMsg = QString("Ошибка получения токена GigaChat: %1").arg(reply->errorString());
// Очищаем очередь сообщений если не удалось получить токен gigaChatAuth.tokenExpiry = 0;
pendingMessages.clear();
emit errorOccurred(errorMsg); emit errorOccurred(errorMsg);
reply->deleteLater(); reply->deleteLater();
return; return;
} }
QJsonDocument doc = QJsonDocument::fromJson(data); QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull()) { if (doc.isNull()) {
pendingMessages.clear();
emit errorOccurred("Неверный формат ответа от GigaChat"); emit errorOccurred("Неверный формат ответа от GigaChat");
reply->deleteLater(); reply->deleteLater();
@@ -444,6 +451,7 @@ void NeuralNetworkManager::handleGigaChatTokenReply(QNetworkReply *reply)
QJsonObject json = doc.object(); QJsonObject json = doc.object();
if (json.contains("access_token")) { if (json.contains("access_token")) {
m_tokenRetryCount = 0;
gigaChatAuth.accessToken = json["access_token"].toString(); gigaChatAuth.accessToken = json["access_token"].toString();
int expiresIn = json.contains("expires_in") int expiresIn = json.contains("expires_in")
@@ -463,10 +471,10 @@ void NeuralNetworkManager::handleGigaChatTokenReply(QNetworkReply *reply)
QString errorMsg = json["error"].toString(); QString errorMsg = json["error"].toString();
// Очищаем очередь сообщений // Очищаем очередь сообщений
pendingMessages.clear();
emit errorOccurred("Ошибка GigaChat: " + errorMsg); emit errorOccurred("Ошибка GigaChat: " + errorMsg);
} else { } else {
pendingMessages.clear();
emit errorOccurred("Неизвестный формат ответа от GigaChat"); emit errorOccurred("Неизвестный формат ответа от GigaChat");
} }
@@ -513,9 +521,20 @@ void NeuralNetworkManager::requestGigaChatToken()
return; return;
} }
// Проверяем количество попыток
if (m_tokenRetryCount >= MAX_TOKEN_RETRIES) {
qDebug() << "Превышено максимальное количество попыток получения токена";
m_tokenRetryCount = 0;
pendingMessages.clear(); // Только после превышения лимита очищаем очередь
emit errorOccurred("Не удалось получить токен GigaChat после нескольких попыток");
return;
}
m_tokenRetryCount++;
isProcessingGigaChatToken = true;
if (gigaChatAuth.clientId.isEmpty() || gigaChatAuth.authorizationCode.isEmpty()) { if (gigaChatAuth.clientId.isEmpty() || gigaChatAuth.authorizationCode.isEmpty()) {
emit errorOccurred("Не установлены учетные данные GigaChat"); emit errorOccurred("Не установлены учетные данные GigaChat");
// Очищаем очередь сообщений // Очищаем очередь сообщений
pendingMessages.clear(); pendingMessages.clear();
return; return;
+6 -1
View File
@@ -27,6 +27,8 @@ public:
~NeuralNetworkManager(); ~NeuralNetworkManager();
// Основные методы // Основные методы
void setCurrentNetworkType(NetworkType type);
void sendMessage(const QString &message);
void sendMessage(const QString &message, NetworkType networkType); void sendMessage(const QString &message, NetworkType networkType);
void setPrefix(const QString &prefix); void setPrefix(const QString &prefix);
void setApiKey(NetworkType networkType, const QString &apiKey); void setApiKey(NetworkType networkType, const QString &apiKey);
@@ -63,7 +65,7 @@ private:
// SSL настройки // SSL настройки
void setupSSL(); void setupSSL();
void setupSSLForGigaChat(); void setupSSLForGigaChat();
NetworkType m_currentNetworkType = DeepSeek;
// Основные переменные // Основные переменные
QNetworkAccessManager *networkManager; QNetworkAccessManager *networkManager;
QMap<NetworkType, NetworkConfig> networkConfigs; QMap<NetworkType, NetworkConfig> networkConfigs;
@@ -86,6 +88,9 @@ private:
bool isGigaChatTokenValid() const; bool isGigaChatTokenValid() const;
bool shouldRefreshGigaChatToken() const; bool shouldRefreshGigaChatToken() const;
int m_tokenRetryCount = 0;
const int MAX_TOKEN_RETRIES = 3;
// Очередь сообщений для GigaChat // Очередь сообщений для GigaChat
struct PendingMessage { struct PendingMessage {
QString message; QString message;
+55
View File
@@ -0,0 +1,55 @@
// neuratemplatemanager.cpp
#include "neuraltemplatemanager.h"
#include "qdebug.h"
#include <QTableWidget>
NeuralTemplateManager::NeuralTemplateManager(QObject *parent)
: QObject(parent)
{
}
void NeuralTemplateManager::addTemplate(const QString &name, const QString &templateText)
{
// Проверяем, нет ли уже шаблона с таким именем
for (int i = 0; i < m_templates.size(); ++i) {
if (m_templates[i].name.compare(name, Qt::CaseInsensitive) == 0) {
m_templates[i].templateText = templateText;
return;
}
}
m_templates.append({name, templateText});
}
QString NeuralTemplateManager::getTemplateText(const QString &name) const
{
for (const Template &t : m_templates) {
if (t.name.compare(name, Qt::CaseInsensitive) == 0) {
return t.templateText;
}
}
return QString();
}
void NeuralTemplateManager::clear()
{
m_templates.clear();
}
void NeuralTemplateManager::loadFromTableWidget(QTableWidget *table)
{
clear();
if (!table) return;
qDebug()<< "Table Found";
for (int row = 0; row < table->rowCount(); ++row) {
QTableWidgetItem *nameItem = table->item(row, 0);
QTableWidgetItem *templateItem = table->item(row, 1);
if (nameItem && templateItem) {
QString name = nameItem->text().trimmed();
QString templateText = templateItem->text().trimmed();
qDebug()<< "Template add " << name;
if (!name.isEmpty() && !templateText.isEmpty()) {
addTemplate(name, templateText);
}
}
}
}
+33
View File
@@ -0,0 +1,33 @@
// neuratemplatemanager.h
#ifndef NEURALTEMPLATEMANAGER_H
#define NEURALTEMPLATEMANAGER_H
#include "qtablewidget.h"
#include <QObject>
#include <QVector>
#include <QString>
class NeuralTemplateManager : public QObject
{
Q_OBJECT
public:
struct Template {
QString name;
QString templateText;
};
explicit NeuralTemplateManager(QObject *parent = nullptr);
void addTemplate(const QString &name, const QString &templateText);
QString getTemplateText(const QString &name) const;
void clear();
void loadFromTableWidget(QTableWidget *table);
const QVector<Template>& templates() const { return m_templates; }
private:
QVector<Template> m_templates;
};
#endif // NEURALTEMPLATEMANAGER_H
+2
View File
@@ -11,6 +11,7 @@ debug/logmanager.o
debug/main.o debug/main.o
debug/mediafilemanager.o debug/mediafilemanager.o
debug/neuralnetworkmanager.o debug/neuralnetworkmanager.o
debug/neuraltemplatemanager.o
debug/randommanager.o debug/randommanager.o
debug/randomresponses.o debug/randomresponses.o
debug/soundmanager.o debug/soundmanager.o
@@ -36,6 +37,7 @@ debug/moc_fsettingsws.o
debug/moc_fsinglegrid.o debug/moc_fsinglegrid.o
debug/moc_logmanager.o debug/moc_logmanager.o
debug/moc_neuralnetworkmanager.o debug/moc_neuralnetworkmanager.o
debug/moc_neuraltemplatemanager.o
debug/moc_randommanager.o debug/moc_randommanager.o
debug/moc_randomresponses.o debug/moc_randomresponses.o
debug/moc_soundmanager.o debug/moc_soundmanager.o
+2
View File
@@ -11,6 +11,7 @@ release/logmanager.o
release/main.o release/main.o
release/mediafilemanager.o release/mediafilemanager.o
release/neuralnetworkmanager.o release/neuralnetworkmanager.o
release/neuraltemplatemanager.o
release/randommanager.o release/randommanager.o
release/randomresponses.o release/randomresponses.o
release/soundmanager.o release/soundmanager.o
@@ -36,6 +37,7 @@ release/moc_fsettingsws.o
release/moc_fsinglegrid.o release/moc_fsinglegrid.o
release/moc_logmanager.o release/moc_logmanager.o
release/moc_neuralnetworkmanager.o release/moc_neuralnetworkmanager.o
release/moc_neuraltemplatemanager.o
release/moc_randommanager.o release/moc_randommanager.o
release/moc_randomresponses.o release/moc_randomresponses.o
release/moc_soundmanager.o release/moc_soundmanager.o
+109 -19
View File
@@ -36,12 +36,13 @@ uGeneral::uGeneral(QWidget *parent)
, m_authStreamer(nullptr) , m_authStreamer(nullptr)
, m_authDA(nullptr) , m_authDA(nullptr)
, fLinkForm(nullptr) , fLinkForm(nullptr)
, m_randomManager(nullptr)
, m_neuralTemplateManager(nullptr)
, m_isTwitchConnected(false) , m_isTwitchConnected(false)
, m_notificationServers() , m_notificationServers()
, m_chatServers() , m_chatServers()
, m_createNotifyDialog(nullptr) , m_createNotifyDialog(nullptr)
, m_createChatDialog(nullptr) , m_createChatDialog(nullptr)
, m_randomManager(nullptr)
{ {
// ============================================================================ // ============================================================================
// ИНИЦИАЛИЗАЦИЯ ИНТЕРФЕЙСА // ИНИЦИАЛИЗАЦИЯ ИНТЕРФЕЙСА
@@ -86,7 +87,7 @@ uGeneral::uGeneral(QWidget *parent)
// Инициализируем структуру папок // Инициализируем структуру папок
FileManager::instance().initializeFolderStructure(); FileManager::instance().initializeFolderStructure();
FileManager::instance().copyDefaultFiles();
// Получаем пути через FileManager // Получаем пути через FileManager
QString sysPath = FileManager::instance().systemPath(); QString sysPath = FileManager::instance().systemPath();
@@ -181,10 +182,7 @@ uGeneral::uGeneral(QWidget *parent)
// Добавьте эту строку в конец метода: // Добавьте эту строку в конец метода:
initializeNotificationSounds(); initializeNotificationSounds();
if (db) {
m_randomManager = new RandomManager(this);
m_randomManager->initialize(db);
}
} }
@@ -394,6 +392,8 @@ void uGeneral::setupTables()
this, &uGeneral::onFilesGridDoubleClicked); this, &uGeneral::onFilesGridDoubleClicked);
connect(ui->widget_3->tableWidget(), &QTableWidget::cellDoubleClicked, connect(ui->widget_3->tableWidget(), &QTableWidget::cellDoubleClicked,
this, &uGeneral::onNeiroGridDoubleClicked); this, &uGeneral::onNeiroGridDoubleClicked);
} }
/** /**
@@ -416,12 +416,40 @@ void uGeneral::initializeManagers()
// Менеджер случайных ответов (групп) // Менеджер случайных ответов (групп)
m_randomResponses = new RandomResponses(this); m_randomResponses = new RandomResponses(this);
m_neuralTemplateManager = new NeuralTemplateManager(this);
QTableWidget* table = ui->widget_3->tableWidget();
m_neuralTemplateManager->loadFromTableWidget(table);
// Менеджер нейросети (если есть) // Менеджер нейросети (если есть)
nnManager = new NeuralNetworkManager(this); nnManager = new NeuralNetworkManager(this);
// Настройка нейросети (пример) // Настройка нейросети (пример)
// nnManager->setApiKey(NeuralNetworkManager::DeepSeek, "ваш_api_ключ"); nnManager->setPrefix(ui->edtGPTPrefix->text());
// nnManager->setModel(NeuralNetworkManager::DeepSeek, "deepseek-chat"); if (ui->rbGC->isChecked()) {
// GigaChat
nnManager->setCurrentNetworkType(NeuralNetworkManager::GigaChat);
nnManager->setGigaChatCredentials(ui->edtAIP1->text(), ui->edtAIP2->text());
qDebug() << "GigaChat";
}
else if (ui->rbDS->isChecked()) {
// DeepSeek
nnManager->setCurrentNetworkType(NeuralNetworkManager::DeepSeek);
nnManager->setApiKey(NeuralNetworkManager::DeepSeek, ui->edtAIP1->text());
qDebug() << "DeepSeek";
}
else if (ui->rbCG->isChecked()) {
// ChatGPT
nnManager->setCurrentNetworkType(NeuralNetworkManager::ChatGPT);
nnManager->setApiKey(NeuralNetworkManager::ChatGPT, ui->edtAIP1->text());
qDebug() << "ChatGPT";
}
else if (ui->RBCustom->isChecked()) {
// Ollama
nnManager->setCurrentNetworkType(NeuralNetworkManager::Ollama);
nnManager->setApiKey(NeuralNetworkManager::Ollama, ui->edtAIP1->text());
nnManager->setOllamaUrl(ui->edtAIP2->text());
qDebug() << "Ollama";
}
m_SoundFiles = new MediaFileManager(); m_SoundFiles = new MediaFileManager();
@@ -450,6 +478,7 @@ void uGeneral::initializeManagers()
context.soundManager = soundManager; context.soundManager = soundManager;
context.neuralManager = nnManager; context.neuralManager = nnManager;
context.randomManager = m_randomManager; context.randomManager = m_randomManager;
context.neuralTemplateManager = m_neuralTemplateManager;
context.randomResponses = m_randomResponses; context.randomResponses = m_randomResponses;
context.mediaFileManager = m_SoundFiles; context.mediaFileManager = m_SoundFiles;
context.channel = ui->edtChannel->text(); context.channel = ui->edtChannel->text();
@@ -805,7 +834,10 @@ uGeneral::~uGeneral()
m_randomManager = nullptr; m_randomManager = nullptr;
} }
if (m_neuralTemplateManager) {
delete m_neuralTemplateManager;
m_neuralTemplateManager = nullptr;
}
delete m_createNotifyDialog; delete m_createNotifyDialog;
delete m_createChatDialog; delete m_createChatDialog;
delete ui; delete ui;
@@ -1755,7 +1787,15 @@ void uGeneral::on_btnNotifyCheckSub_clicked()
void uGeneral::on_btnNotifyOpen_clicked() 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() void uGeneral::on_btnNotifyOpenMod_clicked()
@@ -1946,7 +1986,7 @@ void uGeneral::on_btnNotifyOpenSub_clicked()
// Проверяем и регистрируем пользователя // Проверяем и регистрируем пользователя
QString userId = m_userManager->checkUser(msg.displayName, msg); QString userId = m_userManager->checkUser(msg.displayName, msg);
LogManager::instance()->debug("uGeneral", "handleNewMessage", message);
m_userManager->m_totalMessages++; m_userManager->m_totalMessages++;
// Обновляем статистику и воспроизводим уведомление // Обновляем статистику и воспроизводим уведомление
@@ -2338,14 +2378,6 @@ void uGeneral::on_btnRmGroup_clicked()
db->LoadRandomGroups(ui->lbRandomGroup); db->LoadRandomGroups(ui->lbRandomGroup);
} }
void uGeneral::on_btnAddUserName_clicked()
{
QTextCursor cursor = ui->textBrowser->textCursor();
cursor.insertText("[USERNAME]");
}
void uGeneral::on_btnGetDateFollow_clicked() void uGeneral::on_btnGetDateFollow_clicked()
{ {
QTextCursor cursor = ui->textBrowser->textCursor(); QTextCursor cursor = ui->textBrowser->textCursor();
@@ -2465,9 +2497,52 @@ void uGeneral::on_btnEdtCommand_clicked()
void uGeneral::loadQssFiles() void uGeneral::loadQssFiles()
{ {
// Очищаем ComboBox
ui->cbTheme->clear();
// Добавляем опцию "Без темы"
ui->cbTheme->addItem("Без темы", "");
// 1. Загружаем системные стили (из папки приложения)
QString systemStylesPath = FileManager::instance().getPath(FileManager::SystemStyles);
QDir systemStylesDir(systemStylesPath);
if (systemStylesDir.exists()) {
qDebug() << "Системные стили из:" << systemStylesPath;
loadStylesFromDirectory(systemStylesDir, "Системные");
} else {
qWarning() << "Папка системных стилей не найдена:" << systemStylesPath;
// Создаем папку, если ее нет
systemStylesDir.mkpath(".");
}
// 2. Загружаем пользовательские стили (из папки данных пользователя)
QString userStylesPath = FileManager::instance().getPath(FileManager::Styles);
QDir userStylesDir(userStylesPath);
if (userStylesDir.exists()) {
qDebug() << "Пользовательские стили из:" << userStylesPath;
loadStylesFromDirectory(userStylesDir, "Пользовательские");
} else {
qWarning() << "Папка пользовательских стилей не найдена:" << userStylesPath;
// Создаем папку
userStylesDir.mkpath(".");
} }
// Проверяем, есть ли темы
if (ui->cbTheme->count() <= 1) { // Только "Без темы"
qWarning() << "Темы не найдены, создаем базовые";
}
qDebug() << "Загружено тем:" << ui->cbTheme->count() - 1; // Минус "Без темы"
}
void uGeneral::loadStylesFromDirectory(const QDir &stylesDir, const QString &category) void uGeneral::loadStylesFromDirectory(const QDir &stylesDir, const QString &category)
{ {
if (!stylesDir.exists()) { if (!stylesDir.exists()) {
@@ -3231,6 +3306,8 @@ void uGeneral::on_cbNotifyFileAgain3_stateChanged(int arg1)
void uGeneral::on_btnThemesFolder_clicked() void uGeneral::on_btnThemesFolder_clicked()
{ {
QString userThemesPath = FileManager::instance().getPath(FileManager::Styles);
QDesktopServices::openUrl(QUrl::fromLocalFile(userThemesPath));
} }
@@ -3281,3 +3358,16 @@ void uGeneral::loadSavedChats()
} }
} }
} }
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]");
}
+6 -3
View File
@@ -219,7 +219,6 @@ private slots:
// ======================================================================== // ========================================================================
// СЛОТЫ ДЛЯ РАБОТЫ С ПОЛЬЗОВАТЕЛЯМИ // СЛОТЫ ДЛЯ РАБОТЫ С ПОЛЬЗОВАТЕЛЯМИ
// ======================================================================== // ========================================================================
void on_btnAddUserName_clicked();
void on_btnGetDateFollow_clicked(); void on_btnGetDateFollow_clicked();
void on_btnGetAgeAccaunt_clicked(); void on_btnGetAgeAccaunt_clicked();
void on_btnRandomUserName_clicked(); void on_btnRandomUserName_clicked();
@@ -347,6 +346,10 @@ private slots:
void on_btnThemesFolder_clicked(); void on_btnThemesFolder_clicked();
void onNotifyServerUpdated(HttpServer *server, const QString &name); void onNotifyServerUpdated(HttpServer *server, const QString &name);
void on_btnOpenStream_clicked();
void on_btnAUserName_clicked();
public slots: public slots:
// Установка статуса подключения к Twitch // Установка статуса подключения к Twitch
void setTwitchConnected(bool connected); void setTwitchConnected(bool connected);
@@ -371,7 +374,7 @@ private:
RandomManager *m_randomManager; RandomManager *m_randomManager;
MediaFileManager *m_SoundFiles; MediaFileManager *m_SoundFiles;
MediaFileManager *m_TextFiles; MediaFileManager *m_TextFiles;
NeuralTemplateManager *m_neuralTemplateManager;
QList<TimerInfo> m_timers; // Список таймеров QList<TimerInfo> m_timers; // Список таймеров
int m_nextTimerId = 1; // Следующий ID таймера int m_nextTimerId = 1; // Следующий ID таймера
bool m_isTwitchConnected = false; // Статус подключения к Twitch bool m_isTwitchConnected = false; // Статус подключения к Twitch
@@ -479,7 +482,7 @@ private:
void loadSavedChats(); void loadSavedChats();
void loadSavedNotifications(); void loadSavedNotifications();
void loadNeuralTemplatesFromTableWidget();
void processUserCommand(const QString &username, const QString &commandText); void processUserCommand(const QString &username, const QString &commandText);
void sendChatResponse(const QString &response); void sendChatResponse(const QString &response);
}; };
+58 -109
View File
@@ -92,9 +92,12 @@
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>40</width> <width>75</width>
<height>20</height> <height>20</height>
</size> </size>
</property> </property>
@@ -206,28 +209,23 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_10"> <spacer name="horizontalSpacer_2">
<item>
<spacer name="verticalSpacer_4">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>75</width>
<height>40</height> <height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QPushButton" name="btnGetClientID"> <layout class="QVBoxLayout" name="verticalLayout_10"/>
<property name="text">
<string>Получить</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</item> </item>
@@ -430,26 +428,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QPushButton" name="btnGetDADef">
<property name="geometry">
<rect>
<x>10</x>
<y>270</y>
<width>151</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Получить данные DA</string>
</property>
</widget>
</widget> </widget>
<widget class="QPushButton" name="btnOpenFolderSettings"> <widget class="QPushButton" name="btnOpenFolderSettings">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>320</y> <y>320</y>
<width>181</width> <width>231</width>
<height>23</height> <height>23</height>
</rect> </rect>
</property> </property>
@@ -462,7 +447,7 @@
<rect> <rect>
<x>10</x> <x>10</x>
<y>350</y> <y>350</y>
<width>181</width> <width>231</width>
<height>23</height> <height>23</height>
</rect> </rect>
</property> </property>
@@ -475,7 +460,7 @@
<rect> <rect>
<x>10</x> <x>10</x>
<y>380</y> <y>380</y>
<width>181</width> <width>231</width>
<height>23</height> <height>23</height>
</rect> </rect>
</property> </property>
@@ -790,7 +775,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>200</y> <y>190</y>
<width>361</width> <width>361</width>
<height>111</height> <height>111</height>
</rect> </rect>
@@ -798,110 +783,74 @@
<property name="title"> <property name="title">
<string>Вставки</string> <string>Вставки</string>
</property> </property>
<widget class="QPushButton" name="btnAddUserName"> <widget class="QWidget" name="gridLayoutWidget_2">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>0</x>
<y>20</y> <y>20</y>
<width>75</width> <width>361</width>
<height>23</height> <height>91</height>
</rect> </rect>
</property> </property>
<property name="text"> <layout class="QGridLayout" name="gridLayout_2">
<string>Обращение</string> <item row="0" column="1">
</property>
</widget>
<widget class="QPushButton" name="btnGetDateFollow"> <widget class="QPushButton" name="btnGetDateFollow">
<property name="geometry">
<rect>
<x>90</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Follow</string> <string>Follow</string>
</property> </property>
</widget> </widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnGetAgeAccaunt"> <widget class="QPushButton" name="btnGetAgeAccaunt">
<property name="geometry">
<rect>
<x>170</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Age</string> <string>Age</string>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="btnGPT"> </item>
<property name="geometry"> <item row="1" column="3">
<rect>
<x>250</x>
<y>20</y>
<width>81</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Нейронка</string>
</property>
</widget>
<widget class="QPushButton" name="btnCounterAddtoText">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Счетчик</string>
</property>
</widget>
<widget class="QPushButton" name="btnAIPic"> <widget class="QPushButton" name="btnAIPic">
<property name="geometry">
<rect>
<x>250</x>
<y>50</y>
<width>81</width>
<height>23</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Картинка</string> <string>Картинка</string>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="btnRandomUserName"> </item>
<property name="geometry"> <item row="0" column="3">
<rect> <widget class="QPushButton" name="btnGPT">
<x>10</x>
<y>80</y>
<width>151</width>
<height>23</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Рандомный пользователь</string> <string>Нейронка</string>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="btnGetChannelStat"> </item>
<property name="geometry"> <item row="1" column="0">
<rect> <widget class="QPushButton" name="btnCounterAtoText">
<x>170</x>
<y>80</y>
<width>161</width>
<height>23</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Статистика канала</string> <string>Счетчик</string>
</property> </property>
</widget> </widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="btnAUserName">
<property name="text">
<string>Обращение</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="btnGetChannelStat">
<property name="text">
<string>Статистика</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="btnRandomUserName">
<property name="text">
<string>Рандом</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
<widget class="QPushButton" name="btnAddCommand"> <widget class="QPushButton" name="btnAddCommand">
<property name="geometry"> <property name="geometry">
+62 -29
View File
@@ -5,6 +5,8 @@
#include <QDateTime> #include <QDateTime>
#include <QCoreApplication> #include <QCoreApplication>
#include <synchapi.h> #include <synchapi.h>
#include "logmanager.h"
#include "qregularexpression.h"
WebSocketClient::WebSocketClient(QObject *parent) : WebSocketClient::WebSocketClient(QObject *parent) :
QObject(parent), QObject(parent),
@@ -24,6 +26,7 @@ WebSocketClient::~WebSocketClient()
delete m_webSocket; delete m_webSocket;
} }
bool WebSocketClient::connectToServer(const QString &url) bool WebSocketClient::connectToServer(const QString &url)
{ {
if (m_webSocket) { if (m_webSocket) {
@@ -154,44 +157,74 @@ void WebSocketClient::onPingTimeout()
void WebSocketClient::onTextMessageReceived(const QString &message) void WebSocketClient::onTextMessageReceived(const QString &message)
{ {
QStringList lines = message.split("\r\n", Qt::SkipEmptyParts);
// Обработка PING от сервера for (const QString &line : lines) {
if (message.startsWith("PING")) { if (line.isEmpty()) continue;
qDebug() << "RAW LINE:" << line;
// Определяем тип сообщения
bool isPing = line.startsWith("PING");
bool isPrivmsg = false;
bool isUserState = line.contains("USERSTATE");
bool isRoomState = line.contains("ROOMSTATE");
bool isUserNotice = line.contains("USERNOTICE");
bool isJoin = line.contains(" JOIN ");
bool isNamesList = (line.contains("353") || line.contains("366"));
bool isCapAck = line.contains("CAP * ACK");
// Проверяем PRIVMSG отдельно, чтобы избежать ложных срабатываний
if (line.contains("PRIVMSG", Qt::CaseInsensitive)) {
// Проверяем структуру PRIVMSG сообщения
// Должно быть: @теги :префикс PRIVMSG #канал :сообщение
if (!isUserState && !isRoomState && !isUserNotice) {
// Проверяем наличие канала и текста сообщения
if (line.contains(" #") && line.contains(" :")) {
isPrivmsg = true;
}
}
}
// Обработка в зависимости от типа
if (isPing) {
send("PONG :tmi.twitch.tv\r\n"); send("PONG :tmi.twitch.tv\r\n");
return;
} }
else if (isCapAck) {
// Обработка успешной аутентификации qDebug() << "Capabilities acknowledged";
if (message.contains("001") && !m_currentChannel.isEmpty()) { }
else if (isPrivmsg) {
qDebug() << "PRIVMSG detected (final):" << line;
emit onNewMessage(line);
}
else if (isJoin) {
qDebug() << "JOIN detected:" << line;
// emit onUserJoined(line);
}
else if (isUserState) {
qDebug() << "USERSTATE detected:" << line;
// emit onUserState(line);
}
else if (isRoomState) {
qDebug() << "ROOMSTATE detected:" << line;
// emit onRoomState(line);
}
else if (isUserNotice) {
qDebug() << "USERNOTICE detected:" << line;
// emit onUserNotice(line);
}
else if (isNamesList) {
qDebug() << "NAMES list:" << line;
}
else if (line.contains("001") && !m_currentChannel.isEmpty()) {
joinChannel(m_currentChannel); joinChannel(m_currentChannel);
return; // Добавить return после первого успешного подключения
} }
else {
// Обработка сообщений чата qDebug() << "OTHER message:" << line;
if (message.contains("PRIVMSG")) {
// Извлекаем информацию из сообщения
// Формат: :nick!nick@nick.tmi.twitch.tv PRIVMSG #channel :message
QString cleanMessage = message;
emit onNewMessage(cleanMessage);
// Удаляем служебную информацию для чистого отображения
int msgIndex = cleanMessage.indexOf("PRIVMSG");
if (msgIndex != -1) {
int channelIndex = cleanMessage.indexOf("#", msgIndex);
int messageIndex = cleanMessage.indexOf(" :", channelIndex);
if (messageIndex != -1) {
QString chatMessage = cleanMessage.mid(messageIndex + 2).trimmed();
QString sender = cleanMessage.mid(1, cleanMessage.indexOf("!") - 1);
QString channel = cleanMessage.mid(channelIndex + 1,
messageIndex - channelIndex - 1);
} }
} }
} }
}
void WebSocketClient::onErrorInternal(QAbstractSocket::SocketError error) void WebSocketClient::onErrorInternal(QAbstractSocket::SocketError error)
{ {
QString errorMsg = QString("WebSocket error: %1 - %2") QString errorMsg = QString("WebSocket error: %1 - %2")