refactor
This commit is contained in:
+12
@@ -21,15 +21,21 @@ RC_ICONS = ico\app_icon.ico
|
|||||||
INCLUDEPATH += $$PWD
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
commandprocessor.cpp \
|
||||||
emoteprovider.cpp \
|
emoteprovider.cpp \
|
||||||
fcolorsetting.cpp \
|
fcolorsetting.cpp \
|
||||||
fcreatechat.cpp \
|
fcreatechat.cpp \
|
||||||
fcreatenotify.cpp \
|
fcreatenotify.cpp \
|
||||||
ffontsetting.cpp \
|
ffontsetting.cpp \
|
||||||
|
filemanager.cpp \
|
||||||
fsettingsws.cpp \
|
fsettingsws.cpp \
|
||||||
fsinglegrid.cpp \
|
fsinglegrid.cpp \
|
||||||
|
logmanager.cpp \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
|
mediafilemanager.cpp \
|
||||||
neuralnetworkmanager.cpp \
|
neuralnetworkmanager.cpp \
|
||||||
|
randommanager.cpp \
|
||||||
|
randomresponses.cpp \
|
||||||
soundmanager.cpp \
|
soundmanager.cpp \
|
||||||
tauth.cpp \
|
tauth.cpp \
|
||||||
ttw_api.cpp \
|
ttw_api.cpp \
|
||||||
@@ -46,15 +52,21 @@ SOURCES += \
|
|||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
badge.h \
|
badge.h \
|
||||||
|
commandprocessor.h \
|
||||||
emoteprovider.h \
|
emoteprovider.h \
|
||||||
fcolorsetting.h \
|
fcolorsetting.h \
|
||||||
fcreatechat.h \
|
fcreatechat.h \
|
||||||
fcreatenotify.h \
|
fcreatenotify.h \
|
||||||
ffontsetting.h \
|
ffontsetting.h \
|
||||||
|
filemanager.h \
|
||||||
fsettingsws.h \
|
fsettingsws.h \
|
||||||
fsinglegrid.h \
|
fsinglegrid.h \
|
||||||
|
logmanager.h \
|
||||||
|
mediafilemanager.h \
|
||||||
miniaudio.h \
|
miniaudio.h \
|
||||||
neuralnetworkmanager.h \
|
neuralnetworkmanager.h \
|
||||||
|
randommanager.h \
|
||||||
|
randomresponses.h \
|
||||||
soundmanager.h \
|
soundmanager.h \
|
||||||
tauth.h \
|
tauth.h \
|
||||||
timerinfo.h \
|
timerinfo.h \
|
||||||
|
|||||||
@@ -0,0 +1,476 @@
|
|||||||
|
#include "commandprocessor.h"
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
CommandProcessor::CommandProcessor(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandProcessor::setContext(const Context& context)
|
||||||
|
{
|
||||||
|
m_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::generateResponse(QString userIndex, const QString &command, const QString &message)
|
||||||
|
{
|
||||||
|
qDebug() << "generateResponse: userIndex =" << userIndex << "command =" << command;
|
||||||
|
|
||||||
|
// Сначала пробуем найти пользователя по displayName
|
||||||
|
QString username = "";
|
||||||
|
if (m_context.userManager) {
|
||||||
|
User* user = m_context.userManager->findUser(userIndex);
|
||||||
|
if (user) {
|
||||||
|
username = user->displayName;
|
||||||
|
qDebug() << "Найден пользователь:" << username;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Пользователь не найден в UserManager по displayName:" << userIndex;
|
||||||
|
|
||||||
|
// Попробуем найти по ID
|
||||||
|
user = m_context.userManager->findUserById(userIndex);
|
||||||
|
if (user) {
|
||||||
|
username = user->displayName;
|
||||||
|
qDebug() << "Найден пользователь по ID:" << username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
// Если не нашли в UserManager, используем переданное имя
|
||||||
|
username = userIndex;
|
||||||
|
qDebug() << "Используем переданное имя:" << username;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command cmd = findCommand(command);
|
||||||
|
if (cmd.command.isEmpty()) {
|
||||||
|
qDebug() << "Команда не найдена:" << command;
|
||||||
|
return QString("Команда '%1' не найдена").arg(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Найдена команда:" << cmd.command << "ответ:" << cmd.response;
|
||||||
|
|
||||||
|
QString fullCommand = command + (message.isEmpty() ? "" : " " + message);
|
||||||
|
QString result = processCommand(username, fullCommand, cmd.response);
|
||||||
|
|
||||||
|
qDebug() << "Итоговый результат:" << result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandProcessor::addCommand(const QString &command, const QString &response)
|
||||||
|
{
|
||||||
|
m_commands.append({command, response});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandProcessor::addCommands(const QVector<Command> &commands)
|
||||||
|
{
|
||||||
|
m_commands.append(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandProcessor::clearCommands()
|
||||||
|
{
|
||||||
|
m_commands.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandProcessor::Command CommandProcessor::findCommand(const QString &commandName) const
|
||||||
|
{
|
||||||
|
for (const Command &cmd : m_commands) {
|
||||||
|
if (cmd.command.compare(commandName, Qt::CaseInsensitive) == 0) {
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Command();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::getUsernameByIndex(QString userIndex) const
|
||||||
|
{
|
||||||
|
if (!m_context.userManager) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const User* user = m_context.userManager->getUserByIndex(userIndex);
|
||||||
|
return user ? user->displayName : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::processCommand(const QString &sender, const QString &fullCommand, const QString &rawResponse)
|
||||||
|
{
|
||||||
|
QString response = rawResponse;
|
||||||
|
QString parameters = extractParameters(fullCommand);
|
||||||
|
|
||||||
|
response = parseStatic(response, sender, parameters);
|
||||||
|
// Рекурсивная обработка случайных групп (до 5 уровней вложенности)
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
QString before = response;
|
||||||
|
response = parseRandomGroups(response);
|
||||||
|
|
||||||
|
// Если после обработки строка не изменилась, значит групп больше нет
|
||||||
|
if (before == response) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = parseRandomNumbers(response); // Затем обрабатываем случайные числа ВНУТРИ них
|
||||||
|
response = parseSounds(response);
|
||||||
|
response = parseTextFiles(response);
|
||||||
|
response = parseBan(response, sender);
|
||||||
|
response = parseAPI(response, sender);
|
||||||
|
|
||||||
|
if (response.contains("[AI]", Qt::CaseInsensitive)) {
|
||||||
|
response = parseAI(response, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::extractParameters(const QString &fullCommand)
|
||||||
|
{
|
||||||
|
int spacePos = fullCommand.indexOf(' ');
|
||||||
|
if (spacePos != -1) {
|
||||||
|
return fullCommand.mid(spacePos + 1).trimmed();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseStatic(const QString &response, const QString &sender, const QString ¶meters)
|
||||||
|
{
|
||||||
|
QString res = response;
|
||||||
|
|
||||||
|
res = res.replace("[USERNAME]", "@" + sender);
|
||||||
|
res = res.replace("[TO]", parameters);
|
||||||
|
|
||||||
|
if (res.contains("[RANDOMUSER]")) {
|
||||||
|
QString randomUserName;
|
||||||
|
if (parameters.contains('@')) {
|
||||||
|
randomUserName = parameters;
|
||||||
|
} else if (m_context.userManager) {
|
||||||
|
const User* randomUser = m_context.userManager->getRandomUser();
|
||||||
|
if (randomUser) {
|
||||||
|
randomUserName = "@" + randomUser->displayName;
|
||||||
|
} else {
|
||||||
|
randomUserName = "@viewer";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
randomUserName = "@viewer";
|
||||||
|
}
|
||||||
|
res = res.replace("[RANDOMUSER]", randomUserName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseRandomNumbers(const QString &response)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
QRegularExpression regex("\\[\\[([^\\]]+)\\]\\]");
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
|
||||||
|
|
||||||
|
qDebug() << "parseRandomNumbers: исходная строка:" << response;
|
||||||
|
qDebug() << "Найдено совпадений:" << matches.hasNext();
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString rangeName = match.captured(1);
|
||||||
|
qDebug() << "Найден диапазон:" << rangeName;
|
||||||
|
|
||||||
|
if (m_context.randomManager) {
|
||||||
|
int randomNumber = m_context.randomManager->getRandomValue(rangeName);
|
||||||
|
qDebug() << "Получено случайное число:" << randomNumber << "для диапазона" << rangeName;
|
||||||
|
result.replace("[[" + rangeName + "]]", QString::number(randomNumber));
|
||||||
|
} else {
|
||||||
|
qDebug() << "RandomManager не инициализирован!";
|
||||||
|
int fallbackNumber = QRandomGenerator::global()->bounded(1, 101);
|
||||||
|
result.replace("[[" + rangeName + "]]", QString::number(fallbackNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Результат после замены:" << result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseSounds(const QString &response)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
QRegularExpression regex("\\|\\|([^\\|]+)\\|\\|");
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString soundCommand = match.captured(1);
|
||||||
|
QString fn = m_context.mediaFileManager->getFilePathByName(soundCommand);
|
||||||
|
|
||||||
|
if (m_context.soundManager) {
|
||||||
|
m_context.soundManager->loadSound(SoundManager::SoundChannel::Channel1, fn);
|
||||||
|
m_context.soundManager->playSound(SoundManager::SoundChannel::Channel1);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.replace("||" + soundCommand + "||", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseTextFiles(const QString &response)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
QRegularExpression regex("\\|\\(([^\\)]+)\\|\\(");
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString fileName = match.captured(1);
|
||||||
|
|
||||||
|
QString content = getTextFileContent(fileName);
|
||||||
|
if (!content.isEmpty()) {
|
||||||
|
result.replace("|(" + fileName + "|(", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseRandomGroups(const QString &response)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
QRegularExpression regex("\\{\\{([^\\}]+)\\}\\}");
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString groupName = match.captured(1);
|
||||||
|
|
||||||
|
QString randomResponse = getRandomResponseFromGroup(groupName);
|
||||||
|
if (!randomResponse.isEmpty()) {
|
||||||
|
result.replace("{{" + groupName + "}}", randomResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseBan(const QString &response, const QString &sender)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
QRegularExpression regex("\\[BAN(\\d*)\\]");
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString banTimeStr = match.captured(1);
|
||||||
|
int banSeconds = banTimeStr.isEmpty() ? 0 : banTimeStr.toInt();
|
||||||
|
if (m_context.twitchAPI) {
|
||||||
|
if (m_context.userManager) {
|
||||||
|
User* user = m_context.userManager->findUser(sender);
|
||||||
|
qDebug() << user->displayName;
|
||||||
|
if (user && !user->id.isEmpty()) {
|
||||||
|
if (banSeconds > 0) {
|
||||||
|
m_context.twitchAPI->banUserTime(user->id, banSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.replace("[BAN" + banTimeStr + "]", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseAPI(const QString &response, const QString &sender)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
|
||||||
|
if (result.contains("[FOLLOW]", Qt::CaseInsensitive)) {
|
||||||
|
if (m_context.userManager && m_context.twitchAPI) {
|
||||||
|
User* user = m_context.userManager->findUser(sender);
|
||||||
|
if (user) {
|
||||||
|
if (user->id.isEmpty() && !user->login.isEmpty()) {
|
||||||
|
User fullUser = m_context.twitchAPI->getUserByLogin(user->login);
|
||||||
|
user->id = fullUser.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user->id.isEmpty()) {
|
||||||
|
QDate followDate = m_context.twitchAPI->getFollow(user->id);
|
||||||
|
if (followDate.isValid()) {
|
||||||
|
QString follow = getDateDifferenceString(followDate);
|
||||||
|
result.replace("[FOLLOW]", follow, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.contains("[FOLLOW]", Qt::CaseInsensitive)) {
|
||||||
|
result.replace("[FOLLOW]", "неизвестно", Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.contains("[AGE]", Qt::CaseInsensitive)) {
|
||||||
|
if (m_context.userManager && m_context.twitchAPI) {
|
||||||
|
User* user = m_context.userManager->findUser(sender);
|
||||||
|
if (user && !user->login.isEmpty()) {
|
||||||
|
User fullUser = m_context.twitchAPI->getUserByLogin(user->login);
|
||||||
|
if (fullUser.createdAt.isValid()) {
|
||||||
|
QString age = getDateDifferenceString(fullUser.createdAt);
|
||||||
|
result.replace("[AGE]", age, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.contains("[AGE]", Qt::CaseInsensitive)) {
|
||||||
|
result.replace("[AGE]", "неизвестно", Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.contains("[STAT]", Qt::CaseInsensitive)) {
|
||||||
|
if (m_context.twitchAPI && !m_context.channel.isEmpty()) {
|
||||||
|
int avgViewers = 0, maxViewers = 0, hoursWatched = 0, followers = 0, followersTotal = 0;
|
||||||
|
m_context.twitchAPI->getTTWStat(m_context.channel, avgViewers, maxViewers,
|
||||||
|
hoursWatched, followers, followersTotal);
|
||||||
|
|
||||||
|
QString stat = QString("Средний онлайн: %1; Максимальный онлайн: %2; "
|
||||||
|
"Часов просмотра: %3; Подписчиков за месяц: %4; "
|
||||||
|
"Всего подписчиков: %5")
|
||||||
|
.arg(avgViewers).arg(maxViewers).arg(hoursWatched)
|
||||||
|
.arg(followers).arg(followersTotal);
|
||||||
|
result.replace("[STAT]", stat, Qt::CaseInsensitive);
|
||||||
|
} else {
|
||||||
|
result.replace("[STAT]", "Статистика недоступна", Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseAI(const QString &response, const QString &question)
|
||||||
|
{
|
||||||
|
QString res = response;
|
||||||
|
if (!m_context.neuralManager) {
|
||||||
|
return res.replace("[AI]", "Нейросеть недоступна", Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (question.isEmpty()) {
|
||||||
|
return res.replace("[AI]", "Ошибка: не указан вопрос для нейросети", Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString aiResponse;
|
||||||
|
bool responseReceived = false;
|
||||||
|
bool errorOccurred = false;
|
||||||
|
QString errorMessage;
|
||||||
|
|
||||||
|
QEventLoop eventLoop;
|
||||||
|
|
||||||
|
auto conn1 = connect(m_context.neuralManager, &NeuralNetworkManager::responseReceived,
|
||||||
|
[&](const QString &response1) {
|
||||||
|
aiResponse = response1;
|
||||||
|
responseReceived = true;
|
||||||
|
eventLoop.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto conn2 = connect(m_context.neuralManager, &NeuralNetworkManager::errorOccurred,
|
||||||
|
[&](const QString &error) {
|
||||||
|
errorMessage = error;
|
||||||
|
errorOccurred = true;
|
||||||
|
eventLoop.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_context.neuralManager->sendMessage(question, NeuralNetworkManager::DeepSeek);
|
||||||
|
|
||||||
|
QTimer::singleShot(60000, &eventLoop, &QEventLoop::quit);
|
||||||
|
eventLoop.exec();
|
||||||
|
|
||||||
|
disconnect(conn1);
|
||||||
|
disconnect(conn2);
|
||||||
|
|
||||||
|
if (errorOccurred) {
|
||||||
|
return res.replace("[AI]",
|
||||||
|
QString("Ошибка нейросети: %1").arg(errorMessage),
|
||||||
|
Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!responseReceived) {
|
||||||
|
return res.replace("[AI]",
|
||||||
|
"Таймаут при ожидании ответа от нейросети",
|
||||||
|
Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.replace("[AI]", aiResponse, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::getRandomResponseFromGroup(const QString &groupName)
|
||||||
|
{
|
||||||
|
if (m_context.randomResponses) {
|
||||||
|
return m_context.randomResponses->getResponse(groupName);
|
||||||
|
}
|
||||||
|
return QString("Ответ из группы: " + groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::getTextFileContent(const QString &fileName)
|
||||||
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream.setCodec("UTF-8");
|
||||||
|
QString content = stream.readAll();
|
||||||
|
file.close();
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::getDateDifferenceString(const QDate &inputDate)
|
||||||
|
{
|
||||||
|
if (!inputDate.isValid() || inputDate.year() < 2000) {
|
||||||
|
return "неизвестно";
|
||||||
|
}
|
||||||
|
|
||||||
|
QDate currentDate = QDate::currentDate();
|
||||||
|
if (currentDate < inputDate) {
|
||||||
|
return "в будущем";
|
||||||
|
}
|
||||||
|
|
||||||
|
int years = inputDate.daysTo(currentDate) / 365;
|
||||||
|
QDate tempDate = inputDate.addYears(years);
|
||||||
|
|
||||||
|
int months = 0;
|
||||||
|
while (tempDate.addMonths(1) <= currentDate) {
|
||||||
|
months++;
|
||||||
|
tempDate = tempDate.addMonths(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int days = tempDate.daysTo(currentDate);
|
||||||
|
if (days >= 30) {
|
||||||
|
months += days / 30;
|
||||||
|
days = days % 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString result;
|
||||||
|
if (years > 0) {
|
||||||
|
result += QString("%1 %2 ").arg(years).arg(getPeriodEnding(years, 0));
|
||||||
|
}
|
||||||
|
if (months > 0) {
|
||||||
|
result += QString("%1 %2 ").arg(months).arg(getPeriodEnding(months, 1));
|
||||||
|
}
|
||||||
|
if (days > 0 || result.isEmpty()) {
|
||||||
|
result += QString("%1 %2").arg(days).arg(getPeriodEnding(days, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::getPeriodEnding(int n, int r)
|
||||||
|
{
|
||||||
|
static const QVector<QVector<QString>> endings = {
|
||||||
|
{"год", "года", "лет"},
|
||||||
|
{"месяц", "месяца", "месяцев"},
|
||||||
|
{"день", "дня", "дней"}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (r < 0 || r >= endings.size()) return "";
|
||||||
|
|
||||||
|
if (n % 10 == 1 && n % 100 != 11) {
|
||||||
|
return endings[r][0];
|
||||||
|
} else if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) {
|
||||||
|
return endings[r][1];
|
||||||
|
} else {
|
||||||
|
return endings[r][2];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#ifndef COMMANDPROCESSOR_H
|
||||||
|
#define COMMANDPROCESSOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QDate>
|
||||||
|
#include "mediafilemanager.h"
|
||||||
|
#include "user_manager.h"
|
||||||
|
#include "ttw_api.h"
|
||||||
|
#include "soundmanager.h"
|
||||||
|
#include "neuralnetworkmanager.h"
|
||||||
|
#include "randommanager.h"
|
||||||
|
#include "randomresponses.h"
|
||||||
|
|
||||||
|
class CommandProcessor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Command {
|
||||||
|
QString command;
|
||||||
|
QString response;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
UserManager* userManager = nullptr;
|
||||||
|
TTwAPI* twitchAPI = nullptr;
|
||||||
|
SoundManager* soundManager = nullptr;
|
||||||
|
NeuralNetworkManager* neuralManager = nullptr;
|
||||||
|
RandomManager* randomManager = nullptr;
|
||||||
|
RandomResponses* randomResponses = nullptr;
|
||||||
|
MediaFileManager* mediaFileManager = nullptr;
|
||||||
|
QString channel;
|
||||||
|
int notifyVolume = 50;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit CommandProcessor(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void setContext(const Context& context);
|
||||||
|
|
||||||
|
QString generateResponse(QString userIndex, const QString &command, const QString &message = "");
|
||||||
|
|
||||||
|
void addCommand(const QString &command, const QString &response);
|
||||||
|
void addCommands(const QVector<Command> &commands);
|
||||||
|
void clearCommands();
|
||||||
|
|
||||||
|
Command findCommand(const QString &commandName) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Context m_context;
|
||||||
|
QVector<Command> m_commands;
|
||||||
|
|
||||||
|
QString processCommand(const QString &sender, const QString &fullCommand, const QString &rawResponse);
|
||||||
|
QString parseStatic(const QString &response, const QString &sender, const QString ¶meters);
|
||||||
|
QString parseRandomNumbers(const QString &response);
|
||||||
|
QString parseSounds(const QString &response);
|
||||||
|
QString parseTextFiles(const QString &response);
|
||||||
|
QString parseRandomGroups(const QString &response);
|
||||||
|
QString parseBan(const QString &response, const QString &sender);
|
||||||
|
QString parseAPI(const QString &response, const QString &sender);
|
||||||
|
QString parseAI(const QString &response, const QString &question);
|
||||||
|
|
||||||
|
QString extractParameters(const QString &fullCommand);
|
||||||
|
QString getUsernameByIndex(QString userIndex) const;
|
||||||
|
QString getDateDifferenceString(const QDate &inputDate);
|
||||||
|
QString getPeriodEnding(int n, int r);
|
||||||
|
|
||||||
|
QString getRandomResponseFromGroup(const QString &groupName);
|
||||||
|
QString getTextFileContent(const QString &fileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COMMANDPROCESSOR_H
|
||||||
+1
-1
@@ -19,7 +19,7 @@ void EmoteProvider::setLogCallback(LogCallback callback) {
|
|||||||
m_logCallback = callback;
|
m_logCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteProvider::log(const QString &method, const QString &message, LogLevel level) {
|
void EmoteProvider::log(const QString &method, const QString &message, LogLevelemt level) {
|
||||||
if (m_logCallback) {
|
if (m_logCallback) {
|
||||||
m_logCallback(metaObject()->className(), method, message, level);
|
m_logCallback(metaObject()->className(), method, message, level);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -9,16 +9,18 @@
|
|||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
enum LogLevel {
|
enum LogLevelemt {
|
||||||
LOG_INFO = 0,
|
LOG_INFO = 0,
|
||||||
LOG_WARNING = 1,
|
LOG_WARNING = 1,
|
||||||
LOG_ERROR = 2
|
LOG_ERROR = 2,
|
||||||
|
LOG_DEBUG = 3
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using LogCallback = std::function<void(const QString& module,
|
using LogCallback = std::function<void(const QString& module,
|
||||||
const QString& method,
|
const QString& method,
|
||||||
const QString& message,
|
const QString& message,
|
||||||
LogLevel level)>;
|
LogLevelemt level)>;
|
||||||
|
|
||||||
struct BTTVEmote {
|
struct BTTVEmote {
|
||||||
QString id;
|
QString id;
|
||||||
@@ -50,7 +52,7 @@ signals:
|
|||||||
void emotesLoaded();
|
void emotesLoaded();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void log(const QString &method, const QString &message, LogLevel level);
|
void log(const QString &method, const QString &message, LogLevelemt level);
|
||||||
virtual QString getBaseUrl() const = 0;
|
virtual QString getBaseUrl() const = 0;
|
||||||
virtual void parseGlobalResponse(const QByteArray &data) = 0;
|
virtual void parseGlobalResponse(const QByteArray &data) = 0;
|
||||||
virtual void parseCustomResponse(const QByteArray &data, const QString &userId) = 0;
|
virtual void parseCustomResponse(const QByteArray &data, const QString &userId) = 0;
|
||||||
|
|||||||
+170
-68
@@ -1,4 +1,5 @@
|
|||||||
#include "fcreatenotify.h"
|
#include "fcreatenotify.h"
|
||||||
|
#include "udatabase.h"
|
||||||
#include "ui_fcreatenotify.h"
|
#include "ui_fcreatenotify.h"
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -7,15 +8,19 @@
|
|||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include "filemanager.h"
|
||||||
|
|
||||||
FCreateNotify::FCreateNotify(QWidget *parent) :
|
FCreateNotify::FCreateNotify(uDataBase *database, QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::FCreateNotify),
|
ui(new Ui::FCreateNotify),
|
||||||
m_server(nullptr),
|
m_isEditMode(false),
|
||||||
m_notificationCounter(0)
|
m_database(database),
|
||||||
|
m_existingServerName(""),
|
||||||
|
m_server(nullptr), // Добавьте
|
||||||
|
m_notificationCounter(0) // Добавьте
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
FileManager::instance().initializeFolderStructure();
|
||||||
// Создаем структуру папок для статических файлов
|
// Создаем структуру папок для статических файлов
|
||||||
QDir appDir(QApplication::applicationDirPath());
|
QDir appDir(QApplication::applicationDirPath());
|
||||||
appDir.mkpath("sounds");
|
appDir.mkpath("sounds");
|
||||||
@@ -27,8 +32,7 @@ FCreateNotify::FCreateNotify(QWidget *parent) :
|
|||||||
"donation", "follow", "subscription", "raid",
|
"donation", "follow", "subscription", "raid",
|
||||||
"bits", "host", "merch", "goal", "poll", "prediction"
|
"bits", "host", "merch", "goal", "poll", "prediction"
|
||||||
};
|
};
|
||||||
ui->cbEvent->addItems(events);
|
|
||||||
ui->cbEvent->setCurrentIndex(0);
|
|
||||||
// Устанавливаем значения по умолчанию
|
// Устанавливаем значения по умолчанию
|
||||||
ui->edtHeader->setText("Тестовый пользователь");
|
ui->edtHeader->setText("Тестовый пользователь");
|
||||||
ui->edtMessage->setText("Это тестовое уведомление!");
|
ui->edtMessage->setText("Это тестовое уведомление!");
|
||||||
@@ -40,7 +44,7 @@ FCreateNotify::FCreateNotify(QWidget *parent) :
|
|||||||
// Информация для пользователя
|
// Информация для пользователя
|
||||||
ui->btnTest->setToolTip("Создать тестовое уведомление и открыть браузер");
|
ui->btnTest->setToolTip("Создать тестовое уведомление и открыть браузер");
|
||||||
ui->btnAdd->setToolTip("Добавить уведомление для отображения");
|
ui->btnAdd->setToolTip("Добавить уведомление для отображения");
|
||||||
m_server = new HttpServer(nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FCreateNotify::~FCreateNotify()
|
FCreateNotify::~FCreateNotify()
|
||||||
@@ -101,25 +105,7 @@ void FCreateNotify::on_btnTest_clicked()
|
|||||||
createNotification(true);
|
createNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCreateNotify::on_btnAdd_clicked()
|
|
||||||
{
|
|
||||||
// Создаем сервер с текущими настройками
|
|
||||||
createServer();
|
|
||||||
|
|
||||||
if (!m_server) {
|
|
||||||
QMessageBox::warning(this, "Ошибка", "Не удалось создать сервер");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем название сервера
|
|
||||||
QString name = QString("Уведомления (порт %1)").arg(m_server->port());
|
|
||||||
|
|
||||||
// Отправляем сигнал с созданным сервером
|
|
||||||
emit serverCreated(m_server, name);
|
|
||||||
|
|
||||||
// Закрываем окно
|
|
||||||
accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FCreateNotify::createNotification(bool isTest)
|
void FCreateNotify::createNotification(bool isTest)
|
||||||
{
|
{
|
||||||
@@ -142,39 +128,24 @@ void FCreateNotify::createNotification(bool isTest)
|
|||||||
QString("Тестовое сообщение #%1").arg(m_notificationCounter) :
|
QString("Тестовое сообщение #%1").arg(m_notificationCounter) :
|
||||||
ui->edtMessage->text();
|
ui->edtMessage->text();
|
||||||
|
|
||||||
// Добавляем тип события
|
|
||||||
notif.content = QString("[%1] %2")
|
|
||||||
.arg(ui->cbEvent->currentText())
|
|
||||||
.arg(notif.content);
|
|
||||||
|
|
||||||
// Картинка
|
// Картинка
|
||||||
QString imgPath = ui->edtFileImg->text();
|
QString imgPath = ui->edtFileImg->text();
|
||||||
if (!imgPath.isEmpty() && QFile::exists(imgPath)) {
|
if (!imgPath.isEmpty() && QFile::exists(imgPath)) {
|
||||||
QFileInfo imgInfo(imgPath);
|
QString newFileName;
|
||||||
QString destFileName = imgInfo.fileName();
|
if (FileManager::instance().copyToUserData(imgPath, FileManager::WebServerImages,
|
||||||
QString destPath = getAbsolutePath("imgs/" + destFileName);
|
FileManager::CopyWithNewName, &newFileName)) {
|
||||||
|
notif.url = FileManager::instance().getWebPath(FileManager::WebServerImages, newFileName);
|
||||||
// Копируем, если файл еще не существует
|
|
||||||
if (!QFile::exists(destPath)) {
|
|
||||||
QFile::copy(imgPath, destPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notif.url = "/imgs/" + destFileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Звук
|
// Звук
|
||||||
QString soundPath = ui->edtFileSong->text();
|
QString soundPath = ui->edtFileSong->text();
|
||||||
if (!soundPath.isEmpty() && QFile::exists(soundPath)) {
|
if (!soundPath.isEmpty() && QFile::exists(soundPath)) {
|
||||||
QFileInfo soundInfo(soundPath);
|
QString newFileName;
|
||||||
QString destFileName = soundInfo.fileName();
|
if (FileManager::instance().copyToUserData(soundPath, FileManager::WebServerSounds,
|
||||||
QString destPath = getAbsolutePath("sounds/" + destFileName);
|
FileManager::CopyWithNewName, &newFileName)) {
|
||||||
|
notif.soundURL = FileManager::instance().getWebPath(FileManager::WebServerSounds, newFileName);
|
||||||
// Копируем, если файл еще не существует
|
|
||||||
if (!QFile::exists(destPath)) {
|
|
||||||
QFile::copy(soundPath, destPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notif.soundURL = "/sounds/" + destFileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ПОЛУЧАЕМ НАСТРОЙКИ ИЗ ВИДЖЕТОВ
|
// ПОЛУЧАЕМ НАСТРОЙКИ ИЗ ВИДЖЕТОВ
|
||||||
@@ -277,26 +248,6 @@ void FCreateNotify::createNotification(bool isTest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCreateNotify::copyFileToAppDir(const QString &sourcePath, const QString &destSubDir)
|
|
||||||
{
|
|
||||||
QFileInfo sourceInfo(sourcePath);
|
|
||||||
QString destPath = getAbsolutePath(destSubDir + "/" + sourceInfo.fileName());
|
|
||||||
|
|
||||||
// Копируем файл, если он еще не существует или изменился
|
|
||||||
if (!QFile::exists(destPath) ||
|
|
||||||
QFileInfo(sourcePath).lastModified() > QFileInfo(destPath).lastModified()) {
|
|
||||||
QFile::remove(destPath); // Удаляем старую версию
|
|
||||||
if (QFile::copy(sourcePath, destPath)) {
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FCreateNotify::getAbsolutePath(const QString &relativePath)
|
|
||||||
{
|
|
||||||
return QApplication::applicationDirPath() + "/" + relativePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FCreateNotify::on_btnOpenImg_clicked()
|
void FCreateNotify::on_btnOpenImg_clicked()
|
||||||
{
|
{
|
||||||
QString fileName = QFileDialog::getOpenFileName(this,
|
QString fileName = QFileDialog::getOpenFileName(this,
|
||||||
@@ -336,3 +287,154 @@ void FCreateNotify::onServerStarted(bool success)
|
|||||||
QMessageBox::warning(this, "Ошибка", "Не удалось запустить веб-сервер");
|
QMessageBox::warning(this, "Ошибка", "Не удалось запустить веб-сервер");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FCreateNotify::setEditMode(bool isEditMode)
|
||||||
|
{
|
||||||
|
m_isEditMode = isEditMode;
|
||||||
|
if (isEditMode) {
|
||||||
|
ui->btnAdd->setText("Изменить");
|
||||||
|
setWindowTitle("TTW Bot app: Редактировать уведомления");
|
||||||
|
} else {
|
||||||
|
ui->btnAdd->setText("Создать");
|
||||||
|
setWindowTitle("TTW Bot app: Создать уведомления");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FCreateNotify::loadExistingServer(HttpServer *server, const QString &name)
|
||||||
|
{
|
||||||
|
if (!server) return;
|
||||||
|
|
||||||
|
m_isEditMode = true;
|
||||||
|
m_server = server;
|
||||||
|
m_existingServerName = name;
|
||||||
|
|
||||||
|
setEditMode(true);
|
||||||
|
|
||||||
|
// Устанавливаем порт
|
||||||
|
FSettingsWS *settingsWS = ui->widget;
|
||||||
|
if (settingsWS) {
|
||||||
|
settingsWS->sbPort->setValue(server->port());
|
||||||
|
settingsWS->sbTime->setValue(server->getDuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем название
|
||||||
|
ui->lineEdit->setText(name); // Предполагается, что есть lineEdit для имени
|
||||||
|
|
||||||
|
// Устанавливаем цвета
|
||||||
|
FColorSetting *colorSetting = ui->wBlock;
|
||||||
|
if (colorSetting) {
|
||||||
|
colorSetting->cbBlockColor->setCurrentText(server->getBlockColor());
|
||||||
|
colorSetting->cbBorderColor->setCurrentText(server->getBorderColor());
|
||||||
|
colorSetting->sbBorderSize->setValue(server->getBorderSize());
|
||||||
|
colorSetting->cbBackgroundColor->setCurrentText(server->getPageBackgroundColor());
|
||||||
|
colorSetting->hsBlockTransparant->setValue(server->getTransparency());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем шрифты
|
||||||
|
FFontSetting *fontHeaderSetting = ui->wFont;
|
||||||
|
if (fontHeaderSetting) {
|
||||||
|
QString titleFamily;
|
||||||
|
int titleSize;
|
||||||
|
QString titleColor;
|
||||||
|
server->getTitleFont(titleFamily, titleSize, titleColor);
|
||||||
|
|
||||||
|
fontHeaderSetting->cbFontStyle->setCurrentText(titleFamily);
|
||||||
|
fontHeaderSetting->sbFontSize->setValue(titleSize);
|
||||||
|
fontHeaderSetting->cbFontColor->setCurrentText(titleColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFontSetting *fontMessageSetting = ui->wFont_2;
|
||||||
|
if (fontMessageSetting) {
|
||||||
|
QString contentFamily;
|
||||||
|
int contentSize;
|
||||||
|
QString contentColor;
|
||||||
|
server->getContentFont(contentFamily, contentSize, contentColor);
|
||||||
|
|
||||||
|
fontMessageSetting->cbFontStyle->setCurrentText(contentFamily);
|
||||||
|
fontMessageSetting->sbFontSize->setValue(contentSize);
|
||||||
|
fontMessageSetting->cbFontColor->setCurrentText(contentColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновите on_btnAdd_clicked для поддержки редактирования:
|
||||||
|
void FCreateNotify::on_btnAdd_clicked()
|
||||||
|
{
|
||||||
|
if (m_isEditMode && m_server) {
|
||||||
|
// Режим редактирования
|
||||||
|
FSettingsWS *settingsWS = ui->widget;
|
||||||
|
FColorSetting *colorSetting = ui->wBlock;
|
||||||
|
FFontSetting *fontHeaderSetting = ui->wFont;
|
||||||
|
FFontSetting *fontMessageSetting = ui->wFont_2;
|
||||||
|
|
||||||
|
if (!settingsWS || !colorSetting || !fontHeaderSetting || !fontMessageSetting) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Не найдены настройки сервера");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newPort = settingsWS->sbPort->value();
|
||||||
|
bool portChanged = (newPort != m_server->port());
|
||||||
|
|
||||||
|
if (portChanged) {
|
||||||
|
m_server->stop();
|
||||||
|
delete m_server;
|
||||||
|
m_server = nullptr;
|
||||||
|
createServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_server) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Не удалось обновить сервер");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Применяем настройки к серверу
|
||||||
|
// Цвета
|
||||||
|
m_server->setBlockColor(colorSetting->cbBlockColor->currentText());
|
||||||
|
m_server->setBorderColor(colorSetting->cbBorderColor->currentText());
|
||||||
|
m_server->setBorderSize(colorSetting->sbBorderSize->value());
|
||||||
|
m_server->setPageBackgroundColor(colorSetting->cbBackgroundColor->currentText());
|
||||||
|
m_server->setTransparency(colorSetting->hsBlockTransparant->value());
|
||||||
|
|
||||||
|
// Шрифты
|
||||||
|
m_server->setTitleFont(
|
||||||
|
fontHeaderSetting->cbFontStyle->currentText(),
|
||||||
|
fontHeaderSetting->sbFontSize->value(),
|
||||||
|
fontHeaderSetting->cbFontColor->currentText()
|
||||||
|
);
|
||||||
|
|
||||||
|
m_server->setContentFont(
|
||||||
|
fontMessageSetting->cbFontStyle->currentText(),
|
||||||
|
fontMessageSetting->sbFontSize->value(),
|
||||||
|
fontMessageSetting->cbFontColor->currentText()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Длительность
|
||||||
|
m_server->setDuration(settingsWS->sbTime->value());
|
||||||
|
|
||||||
|
QString newName = ui->lineEdit->text();
|
||||||
|
if (newName.isEmpty()) {
|
||||||
|
newName = QString("Уведомления (порт %1)").arg(m_server->port());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit serverUpdated(m_server, newName);
|
||||||
|
accept();
|
||||||
|
} else {
|
||||||
|
// Режим создания (существующий код)
|
||||||
|
createServer();
|
||||||
|
|
||||||
|
if (!m_server) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Не удалось создать сервер");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification(false);
|
||||||
|
|
||||||
|
QString name = ui->lineEdit->text();
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = QString("Уведомления (порт %1)").arg(m_server->port());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit serverCreated(m_server, name);
|
||||||
|
m_server = nullptr;
|
||||||
|
accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+9
-4
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "webservernotify.h"
|
#include "webservernotify.h"
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include "udatabase.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class FCreateNotify;
|
class FCreateNotify;
|
||||||
@@ -14,11 +15,12 @@ class FCreateNotify : public QDialog
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void serverCreated(HttpServer *server, const QString &name);
|
void serverCreated(HttpServer *server, const QString &name);
|
||||||
|
void serverUpdated(HttpServer *server, const QString &name);
|
||||||
public:
|
public:
|
||||||
explicit FCreateNotify(QWidget *parent = nullptr);
|
explicit FCreateNotify(uDataBase *database = nullptr, QWidget *parent = nullptr);
|
||||||
~FCreateNotify();
|
~FCreateNotify();
|
||||||
|
void loadExistingServer(HttpServer *server, const QString &name);
|
||||||
|
void setEditMode(bool isEditMode);
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
|
||||||
@@ -34,11 +36,14 @@ private:
|
|||||||
void createNotification(bool isTest = false);
|
void createNotification(bool isTest = false);
|
||||||
QString getAbsolutePath(const QString &relativePath);
|
QString getAbsolutePath(const QString &relativePath);
|
||||||
void copyFileToAppDir(const QString &sourcePath, const QString &destSubDir);
|
void copyFileToAppDir(const QString &sourcePath, const QString &destSubDir);
|
||||||
|
bool m_isEditMode;
|
||||||
|
uDataBase *m_database;
|
||||||
|
QString m_existingServerName;
|
||||||
// Сервер
|
// Сервер
|
||||||
HttpServer *m_server;
|
HttpServer *m_server;
|
||||||
int m_notificationCounter;
|
int m_notificationCounter;
|
||||||
void createServer();
|
void createServer();
|
||||||
|
void applyCurrentSettingsToServer();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FCREATENOTIFY_H
|
#endif // FCREATENOTIFY_H
|
||||||
|
|||||||
+23
-23
@@ -137,7 +137,7 @@
|
|||||||
<x>370</x>
|
<x>370</x>
|
||||||
<y>150</y>
|
<y>150</y>
|
||||||
<width>351</width>
|
<width>351</width>
|
||||||
<height>181</height>
|
<height>141</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
<x>9</x>
|
<x>9</x>
|
||||||
<y>19</y>
|
<y>19</y>
|
||||||
<width>331</width>
|
<width>331</width>
|
||||||
<height>152</height>
|
<height>111</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
@@ -163,8 +163,8 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QPushButton" name="btnOpenSong">
|
<widget class="QPushButton" name="btnOpenImg">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -173,6 +173,13 @@
|
|||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLineEdit" name="edtFileImg"/>
|
<widget class="QLineEdit" name="edtFileImg"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QPushButton" name="btnOpenSong">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -180,23 +187,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QPushButton" name="btnOpenImg">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Событие</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QComboBox" name="cbEvent"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -204,7 +194,7 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>370</x>
|
<x>370</x>
|
||||||
<y>340</y>
|
<y>300</y>
|
||||||
<width>80</width>
|
<width>80</width>
|
||||||
<height>24</height>
|
<height>24</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -217,7 +207,7 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>460</x>
|
<x>460</x>
|
||||||
<y>340</y>
|
<y>300</y>
|
||||||
<width>81</width>
|
<width>81</width>
|
||||||
<height>24</height>
|
<height>24</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -226,6 +216,16 @@
|
|||||||
<string>Создать</string>
|
<string>Создать</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="lineEdit">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>370</x>
|
||||||
|
<y>400</y>
|
||||||
|
<width>231</width>
|
||||||
|
<height>22</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|||||||
+27
-39
@@ -1,4 +1,5 @@
|
|||||||
#include "ffontsetting.h"
|
#include "ffontsetting.h"
|
||||||
|
#include "filemanager.h"
|
||||||
#include "qdebug.h"
|
#include "qdebug.h"
|
||||||
#include "qdir.h"
|
#include "qdir.h"
|
||||||
#include "ui_ffontsetting.h"
|
#include "ui_ffontsetting.h"
|
||||||
@@ -48,58 +49,45 @@ void FFontSetting::setupColorComboBox(QComboBox* comboBox)
|
|||||||
|
|
||||||
void FFontSetting::loadFonts()
|
void FFontSetting::loadFonts()
|
||||||
{
|
{
|
||||||
QString fontsPath = "fonts";
|
// Загружаем шрифты из пользовательской папки
|
||||||
QDir fontsDir(fontsPath);
|
QString userFontsPath = FileManager::instance().getPath(FileManager::Fonts);
|
||||||
|
QDir userFontsDir(userFontsPath);
|
||||||
|
|
||||||
if (fontsDir.exists()) {
|
|
||||||
QStringList fontFiles = fontsDir.entryList(QStringList() << "*.ttf" << "*.otf" << "*.ttc",
|
|
||||||
QDir::Files);
|
|
||||||
|
|
||||||
|
// Список всех загруженных шрифтов
|
||||||
|
QStringList allFontFamilies;
|
||||||
|
|
||||||
|
// Загружаем пользовательские шрифты
|
||||||
|
if (userFontsDir.exists()) {
|
||||||
|
QStringList fontFiles = userFontsDir.entryList(QStringList() << "*.ttf" << "*.otf" << "*.ttc", QDir::Files);
|
||||||
|
|
||||||
foreach (const QString &fontFile, fontFiles) {
|
foreach (const QString &fontFile, fontFiles) {
|
||||||
QString fontPath = fontsDir.absoluteFilePath(fontFile);
|
QString fontPath = userFontsDir.absoluteFilePath(fontFile);
|
||||||
int fontId = QFontDatabase::addApplicationFont(fontPath);
|
int fontId = QFontDatabase::addApplicationFont(fontPath);
|
||||||
|
|
||||||
if (fontId != -1) {
|
if (fontId != -1) {
|
||||||
QStringList fontFamilies = QFontDatabase::applicationFontFamilies(fontId);
|
QStringList fontFamilies = QFontDatabase::applicationFontFamilies(fontId);
|
||||||
} else {
|
allFontFamilies.append(fontFamilies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ОБНОВЛЯЕМ СПИСОК ШРИФТОВ В КОМБОБОКСЕ
|
// Добавляем системные шрифты Windows
|
||||||
ui->cbFontStyle->clear(); // Очищаем старый список
|
QFontDatabase fontDatabase;
|
||||||
|
QStringList systemFamilies = fontDatabase.families();
|
||||||
|
allFontFamilies.append(systemFamilies);
|
||||||
|
|
||||||
// Получаем все шрифты через объект QFontDatabase
|
// Удаляем дубликаты
|
||||||
QFontDatabase fontDatabase;
|
allFontFamilies.removeDuplicates();
|
||||||
QStringList fontFamilies = fontDatabase.families(); // Получаем все шрифты
|
allFontFamilies.sort();
|
||||||
|
|
||||||
// ИЛИ используйте статический метод с параметром:
|
// Заполняем ComboBox
|
||||||
// QStringList fontFamilies = QFontDatabase::families(QFontDatabase::Any);
|
ui->cbFontStyle->clear();
|
||||||
|
ui->cbFontStyle->addItems(allFontFamilies);
|
||||||
|
|
||||||
ui->cbFontStyle->addItems(fontFamilies); // Добавляем все шрифты
|
// Устанавливаем шрифт по умолчанию
|
||||||
|
int defaultIndex = ui->cbFontStyle->findText("Arial");
|
||||||
// Устанавливаем шрифт по умолчанию
|
if (defaultIndex >= 0) {
|
||||||
int defaultIndex = ui->cbFontStyle->findText("Arial");
|
ui->cbFontStyle->setCurrentIndex(defaultIndex);
|
||||||
if (defaultIndex >= 0) {
|
|
||||||
ui->cbFontStyle->setCurrentIndex(defaultIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Показываем количество доступных шрифтов
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Загружаем системные шрифты, если папки нет
|
|
||||||
QFontDatabase fontDatabase;
|
|
||||||
QStringList fontFamilies = fontDatabase.families();
|
|
||||||
ui->cbFontStyle->clear();
|
|
||||||
ui->cbFontStyle->addItems(fontFamilies);
|
|
||||||
|
|
||||||
int defaultIndex = ui->cbFontStyle->findText("Arial");
|
|
||||||
if (defaultIndex >= 0) {
|
|
||||||
ui->cbFontStyle->setCurrentIndex(defaultIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверяем альтернативные пути
|
|
||||||
QString appDir = QApplication::applicationDirPath();
|
|
||||||
QString altPath = appDir + "/fonts";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+183
@@ -0,0 +1,183 @@
|
|||||||
|
#include "filemanager.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
FileManager::FileManager()
|
||||||
|
{
|
||||||
|
m_systemPath = QCoreApplication::applicationDirPath();
|
||||||
|
m_userDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileManager& FileManager::instance()
|
||||||
|
{
|
||||||
|
static FileManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileManager::systemPath() const
|
||||||
|
{
|
||||||
|
return m_systemPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileManager::userDataPath() const
|
||||||
|
{
|
||||||
|
return m_userDataPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileManager::getPath(FileType type, const QString& subPath) const
|
||||||
|
{
|
||||||
|
QString basePath;
|
||||||
|
|
||||||
|
// Определяем базовый путь (системный или пользовательский)
|
||||||
|
switch (type) {
|
||||||
|
case Icons:
|
||||||
|
case SystemStyles:
|
||||||
|
basePath = m_systemPath;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
basePath = m_userDataPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем подпапку
|
||||||
|
QString folder;
|
||||||
|
switch (type) {
|
||||||
|
case Sounds: folder = "sounds"; break;
|
||||||
|
case Images: folder = "images"; break;
|
||||||
|
case Styles: folder = "styles"; break;
|
||||||
|
case Voices: folder = "voices"; break;
|
||||||
|
case Fonts: folder = "fonts"; break;
|
||||||
|
case Temp: folder = "temp"; break;
|
||||||
|
case Backups: folder = "backups"; break;
|
||||||
|
case Exports: folder = "exports"; break;
|
||||||
|
case Logs: folder = "logs"; break;
|
||||||
|
case Cache: folder = "cache"; break;
|
||||||
|
case WebServerImages: folder = "webserver/images"; break;
|
||||||
|
case WebServerSounds: folder = "webserver/sounds"; break;
|
||||||
|
case Icons: folder = "ico"; break;
|
||||||
|
case SystemStyles: folder = "styles"; break;
|
||||||
|
default: folder = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = QString("%1/%2").arg(basePath).arg(folder);
|
||||||
|
if (!subPath.isEmpty()) {
|
||||||
|
path += "/" + subPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileManager::getFullPath(FileType type, const QString& fileName) const
|
||||||
|
{
|
||||||
|
QString path = getPath(type);
|
||||||
|
return QString("%1/%2").arg(path).arg(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileManager::copyToUserData(const QString& sourceFile, FileType type,
|
||||||
|
CopyMode mode, QString* newFileName)
|
||||||
|
{
|
||||||
|
QFileInfo sourceInfo(sourceFile);
|
||||||
|
if (!sourceInfo.exists()) {
|
||||||
|
qWarning() << "FileManager: Source file doesn't exist:" << sourceFile;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString destFolder = getPath(type);
|
||||||
|
QDir dir(destFolder);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
if (!dir.mkpath(".")) {
|
||||||
|
qWarning() << "FileManager: Cannot create folder:" << destFolder;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString destFileName = sourceInfo.fileName();
|
||||||
|
QString destPath = getFullPath(type, destFileName);
|
||||||
|
|
||||||
|
// Обрабатываем конфликты имен
|
||||||
|
if (QFile::exists(destPath)) {
|
||||||
|
switch (mode) {
|
||||||
|
case CopyIfNotExists:
|
||||||
|
// Файл уже существует, не копируем
|
||||||
|
if (newFileName) *newFileName = destFileName;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case CopyAlways:
|
||||||
|
// Перезаписываем существующий файл
|
||||||
|
QFile::remove(destPath);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CopyWithNewName:
|
||||||
|
// Генерируем уникальное имя
|
||||||
|
int counter = 1;
|
||||||
|
QString baseName = sourceInfo.baseName();
|
||||||
|
QString suffix = sourceInfo.suffix();
|
||||||
|
|
||||||
|
while (QFile::exists(destPath)) {
|
||||||
|
destFileName = QString("%1_%2.%3").arg(baseName).arg(counter).arg(suffix);
|
||||||
|
destPath = getFullPath(type, destFileName);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Копируем файл
|
||||||
|
if (QFile::copy(sourceFile, destPath)) {
|
||||||
|
if (newFileName) *newFileName = destFileName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "FileManager: Failed to copy file from" << sourceFile << "to" << destPath;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileManager::existsInUserData(FileType type, const QString& fileName) const
|
||||||
|
{
|
||||||
|
QString path = getFullPath(type, fileName);
|
||||||
|
return QFile::exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileManager::initializeFolderStructure()
|
||||||
|
{
|
||||||
|
// Создаем все пользовательские папки
|
||||||
|
for (int i = Sounds; i <= WebServerSounds; ++i) {
|
||||||
|
FileType type = static_cast<FileType>(i);
|
||||||
|
QString path = getPath(type);
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileManager::getWebPath(FileType type, const QString& fileName) const
|
||||||
|
{
|
||||||
|
QString folder;
|
||||||
|
switch (type) {
|
||||||
|
case WebServerImages: folder = "imgs"; break;
|
||||||
|
case WebServerSounds: folder = "sounds"; break;
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef FILEMANAGER_H
|
||||||
|
#define FILEMANAGER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
class FileManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Типы файлов/папок
|
||||||
|
enum FileType {
|
||||||
|
Sounds,
|
||||||
|
Images,
|
||||||
|
Styles,
|
||||||
|
Voices,
|
||||||
|
Fonts,
|
||||||
|
Temp,
|
||||||
|
Backups,
|
||||||
|
Exports,
|
||||||
|
Logs,
|
||||||
|
Cache,
|
||||||
|
WebServerImages,
|
||||||
|
WebServerSounds,
|
||||||
|
Icons,
|
||||||
|
SystemStyles
|
||||||
|
};
|
||||||
|
|
||||||
|
// Режим копирования
|
||||||
|
enum CopyMode {
|
||||||
|
CopyIfNotExists, // Копировать только если не существует
|
||||||
|
CopyAlways, // Всегда копировать (перезаписывать)
|
||||||
|
CopyWithNewName // Копировать с новым именем (если существует)
|
||||||
|
};
|
||||||
|
|
||||||
|
static FileManager& instance();
|
||||||
|
|
||||||
|
// Основные пути
|
||||||
|
QString systemPath() const;
|
||||||
|
QString userDataPath() const;
|
||||||
|
|
||||||
|
// Получение путей
|
||||||
|
QString getPath(FileType type, const QString& subPath = "") const;
|
||||||
|
QString getFullPath(FileType type, const QString& fileName) const;
|
||||||
|
|
||||||
|
// Копирование файлов
|
||||||
|
bool copyToUserData(const QString& sourceFile, FileType type,
|
||||||
|
CopyMode mode = CopyIfNotExists,
|
||||||
|
QString* newFileName = nullptr);
|
||||||
|
|
||||||
|
// Проверка существования
|
||||||
|
bool existsInUserData(FileType type, const QString& fileName) const;
|
||||||
|
|
||||||
|
// Инициализация структуры папок
|
||||||
|
void initializeFolderStructure();
|
||||||
|
|
||||||
|
// Получение относительного пути для веб-сервера
|
||||||
|
QString getWebPath(FileType type, const QString& fileName) const;
|
||||||
|
|
||||||
|
// Копирование файлов из системных в пользовательские (при первом запуске)
|
||||||
|
void copyDefaultFiles();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileManager();
|
||||||
|
~FileManager() = default;
|
||||||
|
FileManager(const FileManager&) = delete;
|
||||||
|
FileManager& operator=(const FileManager&) = delete;
|
||||||
|
|
||||||
|
QString m_systemPath;
|
||||||
|
QString m_userDataPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILEMANAGER_H
|
||||||
+832
@@ -0,0 +1,832 @@
|
|||||||
|
#include "logmanager.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// СТАТИЧЕСКИЕ ПЕРЕМЕННЫЕ
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
LogManager* LogManager::m_instance = nullptr;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// МЕТОДЫ LOGENTRY
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
QString LogEntry::toString() const
|
||||||
|
{
|
||||||
|
QStringList parts;
|
||||||
|
|
||||||
|
if (!timestamp.isNull()) {
|
||||||
|
parts.append(timestamp.toString("hh:mm:ss"));
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.append(levelToString(level));
|
||||||
|
|
||||||
|
if (!module.isEmpty()) {
|
||||||
|
parts.append(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!method.isEmpty()) {
|
||||||
|
parts.append(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.append(message);
|
||||||
|
|
||||||
|
return parts.join(" | ");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogEntry::toHtml() const
|
||||||
|
{
|
||||||
|
QString color = levelToColor(level).name();
|
||||||
|
|
||||||
|
QString html = QString(
|
||||||
|
"<div style=\"margin: 2px 0; padding: 2px; border-left: 3px solid %1;\">"
|
||||||
|
"<span style=\"color: gray; font-size: 0.8em;\">%2</span> "
|
||||||
|
"<span style=\"color: %1; font-weight: bold;\">[%3]</span> "
|
||||||
|
"<span style=\"color: darkblue;\">%4</span> "
|
||||||
|
"<span style=\"color: green;\">%5</span>: "
|
||||||
|
"<span>%6</span>"
|
||||||
|
"</div>")
|
||||||
|
.arg(color)
|
||||||
|
.arg(timestamp.toString("hh:mm:ss"))
|
||||||
|
.arg(levelToString(level))
|
||||||
|
.arg(module)
|
||||||
|
.arg(method)
|
||||||
|
.arg(message.toHtmlEscaped());
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogEntry::toCsv() const
|
||||||
|
{
|
||||||
|
return QString("\"%1\",\"%2\",\"%3\",\"%4\",\"%5\"")
|
||||||
|
.arg(timestamp.toString("yyyy-MM-dd hh:mm:ss"))
|
||||||
|
.arg(levelToString(level))
|
||||||
|
.arg(module)
|
||||||
|
.arg(method)
|
||||||
|
.arg(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogEntry::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["timestamp"] = timestamp.toString(Qt::ISODate);
|
||||||
|
obj["level"] = levelToString(level);
|
||||||
|
obj["module"] = module;
|
||||||
|
obj["method"] = method;
|
||||||
|
obj["message"] = message;
|
||||||
|
obj["threadId"] = static_cast<qint64>(threadId);
|
||||||
|
|
||||||
|
if (!sourceFile.isEmpty()) {
|
||||||
|
obj["sourceFile"] = sourceFile;
|
||||||
|
obj["sourceLine"] = sourceLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc(obj);
|
||||||
|
return doc.toJson(QJsonDocument::Compact);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogEntry::levelToString(LogLevel level)
|
||||||
|
{
|
||||||
|
switch (level) {
|
||||||
|
case LogLevel::Info: return "INFO";
|
||||||
|
case LogLevel::Warning: return "WARNING";
|
||||||
|
case LogLevel::Error: return "ERROR";
|
||||||
|
case LogLevel::Debug: return "DEBUG";
|
||||||
|
case LogLevel::Critical: return "CRITICAL";
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor LogEntry::levelToColor(LogLevel level)
|
||||||
|
{
|
||||||
|
switch (level) {
|
||||||
|
case LogLevel::Info: return Qt::darkGreen;
|
||||||
|
case LogLevel::Warning: return Qt::darkYellow;
|
||||||
|
case LogLevel::Error: return Qt::red;
|
||||||
|
case LogLevel::Debug: return Qt::gray;
|
||||||
|
case LogLevel::Critical: return Qt::darkRed;
|
||||||
|
default: return Qt::black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogEntry::formatDateTime(const QDateTime& dt)
|
||||||
|
{
|
||||||
|
return dt.toString("dd.MM.yyyy hh:mm:ss.zzz");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// РЕАЛИЗАЦИЯ LOGMANAGER
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
LogManager::LogManager(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_maxEntries(10000)
|
||||||
|
{
|
||||||
|
// Настройки по умолчанию
|
||||||
|
m_settings.logToFile = true;
|
||||||
|
m_settings.logToConsole = true;
|
||||||
|
m_settings.logToDatabase = false;
|
||||||
|
|
||||||
|
// Стандартный путь к файлу логов
|
||||||
|
QString appDataPath = QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::AppDataLocation);
|
||||||
|
QDir dir(appDataPath);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
m_settings.logFilePath = dir.filePath("application.log");
|
||||||
|
|
||||||
|
// Цвета по умолчанию
|
||||||
|
m_settings.colors[LogLevel::Info] = Qt::darkGreen;
|
||||||
|
m_settings.colors[LogLevel::Warning] = Qt::darkYellow;
|
||||||
|
m_settings.colors[LogLevel::Error] = Qt::red;
|
||||||
|
m_settings.colors[LogLevel::Debug] = Qt::gray;
|
||||||
|
m_settings.colors[LogLevel::Critical] = Qt::darkRed;
|
||||||
|
|
||||||
|
m_settings.showTimestamp = true;
|
||||||
|
m_settings.showModule = true;
|
||||||
|
m_settings.showMethod = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager::~LogManager()
|
||||||
|
{
|
||||||
|
if (m_logFile.isOpen()) {
|
||||||
|
m_logFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager* LogManager::instance()
|
||||||
|
{
|
||||||
|
if (!m_instance) {
|
||||||
|
m_instance = new LogManager();
|
||||||
|
}
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::initialize(const LogSettings& settings)
|
||||||
|
{
|
||||||
|
if (m_instance) {
|
||||||
|
m_instance->updateSettings(settings);
|
||||||
|
} else {
|
||||||
|
m_instance = new LogManager();
|
||||||
|
m_instance->updateSettings(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::cleanup()
|
||||||
|
{
|
||||||
|
if (m_instance) {
|
||||||
|
delete m_instance;
|
||||||
|
m_instance = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::log(LogLevel level, const QString& module,
|
||||||
|
const QString& method, const QString& message)
|
||||||
|
{
|
||||||
|
log(level, module, method, message, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::log(LogLevel level, const QString& module,
|
||||||
|
const QString& method, const QString& message,
|
||||||
|
const QString& sourceFile, int sourceLine)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
// Создаем запись
|
||||||
|
LogEntry entry;
|
||||||
|
entry.timestamp = QDateTime::currentDateTime();
|
||||||
|
entry.level = level;
|
||||||
|
entry.module = module;
|
||||||
|
entry.method = method;
|
||||||
|
entry.message = message;
|
||||||
|
entry.threadId = reinterpret_cast<qint64>(QThread::currentThreadId());
|
||||||
|
|
||||||
|
if (!sourceFile.isEmpty()) {
|
||||||
|
entry.sourceFile = sourceFile;
|
||||||
|
entry.sourceLine = sourceLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем в список
|
||||||
|
m_entries.append(entry);
|
||||||
|
|
||||||
|
// Ограничиваем количество записей
|
||||||
|
if (m_entries.size() > m_maxEntries) {
|
||||||
|
m_entries.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Записываем в файл
|
||||||
|
if (m_settings.logToFile && !m_settings.logFilePath.isEmpty()) {
|
||||||
|
writeToFile(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выводим в консоль
|
||||||
|
if (m_settings.logToConsole) {
|
||||||
|
QString formatted = formatForConsole(entry);
|
||||||
|
switch (level) {
|
||||||
|
case LogLevel::Error:
|
||||||
|
case LogLevel::Critical:
|
||||||
|
qCritical().noquote() << formatted;
|
||||||
|
break;
|
||||||
|
case LogLevel::Warning:
|
||||||
|
qWarning().noquote() << formatted;
|
||||||
|
break;
|
||||||
|
case LogLevel::Debug:
|
||||||
|
qDebug().noquote() << formatted;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qInfo().noquote() << formatted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
// Отправляем сигналы
|
||||||
|
emit entryAdded(entry);
|
||||||
|
|
||||||
|
// Сигналы для конкретных уровней
|
||||||
|
switch (level) {
|
||||||
|
case LogLevel::Info:
|
||||||
|
emit infoAdded(entry);
|
||||||
|
break;
|
||||||
|
case LogLevel::Warning:
|
||||||
|
emit warningAdded(entry);
|
||||||
|
break;
|
||||||
|
case LogLevel::Error:
|
||||||
|
emit errorAdded(entry);
|
||||||
|
break;
|
||||||
|
case LogLevel::Debug:
|
||||||
|
emit debugAdded(entry);
|
||||||
|
break;
|
||||||
|
case LogLevel::Critical:
|
||||||
|
emit criticalAdded(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::info(const QString& module, const QString& method,
|
||||||
|
const QString& message)
|
||||||
|
{
|
||||||
|
log(LogLevel::Info, module, method, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::warning(const QString& module, const QString& method,
|
||||||
|
const QString& message)
|
||||||
|
{
|
||||||
|
log(LogLevel::Warning, module, method, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::error(const QString& module, const QString& method,
|
||||||
|
const QString& message)
|
||||||
|
{
|
||||||
|
log(LogLevel::Error, module, method, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::debug(const QString& module, const QString& method,
|
||||||
|
const QString& message)
|
||||||
|
{
|
||||||
|
log(LogLevel::Debug, module, method, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::critical(const QString& module, const QString& method,
|
||||||
|
const QString& message)
|
||||||
|
{
|
||||||
|
log(LogLevel::Critical, module, method, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LogEntry> LogManager::allEntries() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return m_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LogEntry> LogManager::entriesByTime(const QDateTime& from,
|
||||||
|
const QDateTime& to) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QList<LogEntry> result;
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
if (entry.timestamp >= from && entry.timestamp <= to) {
|
||||||
|
result.append(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LogEntry> LogManager::entriesByLevel(LogLevel level) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QList<LogEntry> result;
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
if (entry.level == level) {
|
||||||
|
result.append(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LogEntry> LogManager::entriesByModule(const QString& module) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QList<LogEntry> result;
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
if (entry.module == module) {
|
||||||
|
result.append(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LogEntry> LogManager::search(const QString& text, bool caseSensitive) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QList<LogEntry> result;
|
||||||
|
|
||||||
|
Qt::CaseSensitivity sensitivity = caseSensitive ?
|
||||||
|
Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
if (entry.message.contains(text, sensitivity) ||
|
||||||
|
entry.module.contains(text, sensitivity) ||
|
||||||
|
entry.method.contains(text, sensitivity)) {
|
||||||
|
result.append(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LogEntry> LogManager::filter(const QList<LogLevel>& levels,
|
||||||
|
const QString& moduleFilter,
|
||||||
|
const QString& methodFilter) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QList<LogEntry> result;
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
// Проверка уровня
|
||||||
|
if (!levels.contains(entry.level)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка модуля
|
||||||
|
if (!moduleFilter.isEmpty() && entry.module != moduleFilter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка метода
|
||||||
|
if (!methodFilter.isEmpty() && entry.method != methodFilter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::clear()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_entries.clear();
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
emit logCleared();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogManager::saveToFile(const QString& filePath, bool append)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
QIODevice::OpenMode mode = QIODevice::WriteOnly | QIODevice::Text;
|
||||||
|
if (append) {
|
||||||
|
mode |= QIODevice::Append;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.open(mode)) {
|
||||||
|
emit fileError(QString("Не удалось открыть файл: %1").arg(file.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream.setCodec("UTF-8");
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
stream << "Дата,Уровень,Модуль,Метод,Сообщение\n";
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
stream << entry.toCsv() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogManager::loadFromFile(const QString& filePath)
|
||||||
|
{
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream.setCodec("UTF-8");
|
||||||
|
|
||||||
|
// Пропускаем заголовок
|
||||||
|
QString header = stream.readLine();
|
||||||
|
|
||||||
|
QList<LogEntry> loadedEntries;
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
QString line = stream.readLine();
|
||||||
|
QStringList parts = line.split('"');
|
||||||
|
|
||||||
|
if (parts.size() < 9) continue;
|
||||||
|
|
||||||
|
LogEntry entry;
|
||||||
|
entry.timestamp = QDateTime::fromString(parts[1], "yyyy-MM-dd hh:mm:ss");
|
||||||
|
|
||||||
|
QString levelStr = parts[3];
|
||||||
|
if (levelStr == "INFO") entry.level = LogLevel::Info;
|
||||||
|
else if (levelStr == "WARNING") entry.level = LogLevel::Warning;
|
||||||
|
else if (levelStr == "ERROR") entry.level = LogLevel::Error;
|
||||||
|
else if (levelStr == "DEBUG") entry.level = LogLevel::Debug;
|
||||||
|
else if (levelStr == "CRITICAL") entry.level = LogLevel::Critical;
|
||||||
|
|
||||||
|
entry.module = parts[5];
|
||||||
|
entry.method = parts[7];
|
||||||
|
entry.message = parts[9];
|
||||||
|
|
||||||
|
loadedEntries.append(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_entries = loadedEntries;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogManager::exportToFormat(const QString& filePath, const QString& format)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream.setCodec("UTF-8");
|
||||||
|
|
||||||
|
if (format.toLower() == "html") {
|
||||||
|
stream << "<!DOCTYPE html>\n<html>\n<head>\n";
|
||||||
|
stream << "<meta charset=\"UTF-8\">\n";
|
||||||
|
stream << "<title>Логи приложения</title>\n";
|
||||||
|
stream << "<style>\n";
|
||||||
|
stream << "body { font-family: monospace; }\n";
|
||||||
|
stream << ".info { color: darkgreen; }\n";
|
||||||
|
stream << ".warning { color: darkorange; }\n";
|
||||||
|
stream << ".error { color: red; }\n";
|
||||||
|
stream << ".debug { color: gray; }\n";
|
||||||
|
stream << ".critical { color: darkred; }\n";
|
||||||
|
stream << "</style>\n";
|
||||||
|
stream << "</head>\n<body>\n";
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
stream << entry.toHtml() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << "</body>\n</html>";
|
||||||
|
|
||||||
|
} else if (format.toLower() == "json") {
|
||||||
|
QJsonArray array;
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["timestamp"] = entry.timestamp.toString(Qt::ISODate);
|
||||||
|
obj["level"] = LogEntry::levelToString(entry.level);
|
||||||
|
obj["module"] = entry.module;
|
||||||
|
obj["method"] = entry.method;
|
||||||
|
obj["message"] = entry.message;
|
||||||
|
array.append(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc(array);
|
||||||
|
stream << doc.toJson();
|
||||||
|
|
||||||
|
} else { // TXT (по умолчанию)
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
stream << entry.toString() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<LogLevel, int> LogManager::statistics() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QMap<LogLevel, int> stats;
|
||||||
|
|
||||||
|
for (const auto& entry : m_entries) {
|
||||||
|
stats[entry.level]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LogManager::count() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return m_entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int LogManager::maxEntries() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return m_maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setMaxEntries(int max)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_maxEntries = max;
|
||||||
|
|
||||||
|
// Удаляем лишние записи
|
||||||
|
while (m_entries.size() > m_maxEntries) {
|
||||||
|
m_entries.removeFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogSettings LogManager::settings() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return m_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::updateSettings(const LogSettings& newSettings)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
// Закрываем старый файл, если изменился путь
|
||||||
|
if (m_settings.logFilePath != newSettings.logFilePath && m_logFile.isOpen()) {
|
||||||
|
m_logFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings = newSettings;
|
||||||
|
|
||||||
|
// Инициализируем файл
|
||||||
|
if (m_settings.logToFile && !m_settings.logFilePath.isEmpty()) {
|
||||||
|
if (!m_logFile.isOpen()) {
|
||||||
|
m_logFile.setFileName(m_settings.logFilePath);
|
||||||
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||||
|
emit fileError(QString("Не удалось открыть файл логов: %1").arg(m_logFile.errorString()));
|
||||||
|
} else {
|
||||||
|
m_logStream.setDevice(&m_logFile);
|
||||||
|
m_logStream.setCodec("UTF-8");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locker.unlock();
|
||||||
|
emit settingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::saveSettings(QSettings& settings)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
settings.beginGroup("Logging");
|
||||||
|
settings.setValue("logToFile", m_settings.logToFile);
|
||||||
|
settings.setValue("logToConsole", m_settings.logToConsole);
|
||||||
|
settings.setValue("logFilePath", m_settings.logFilePath);
|
||||||
|
settings.setValue("maxFileSizeMB", m_settings.maxFileSizeMB);
|
||||||
|
settings.setValue("maxFileCount", m_settings.maxFileCount);
|
||||||
|
settings.setValue("showTimestamp", m_settings.showTimestamp);
|
||||||
|
settings.setValue("showModule", m_settings.showModule);
|
||||||
|
settings.setValue("showMethod", m_settings.showMethod);
|
||||||
|
settings.setValue("maxEntries", m_maxEntries);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::loadSettings(QSettings& settings)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
settings.beginGroup("Logging");
|
||||||
|
m_settings.logToFile = settings.value("logToFile", true).toBool();
|
||||||
|
m_settings.logToConsole = settings.value("logToConsole", true).toBool();
|
||||||
|
m_settings.logFilePath = settings.value("logFilePath", m_settings.logFilePath).toString();
|
||||||
|
m_settings.maxFileSizeMB = settings.value("maxFileSizeMB", 10).toInt();
|
||||||
|
m_settings.maxFileCount = settings.value("maxFileCount", 5).toInt();
|
||||||
|
m_settings.showTimestamp = settings.value("showTimestamp", true).toBool();
|
||||||
|
m_settings.showModule = settings.value("showModule", true).toBool();
|
||||||
|
m_settings.showMethod = settings.value("showMethod", true).toBool();
|
||||||
|
m_maxEntries = settings.value("maxEntries", 10000).toInt();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setLogToFileEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_settings.logToFile = enabled;
|
||||||
|
|
||||||
|
if (enabled && !m_logFile.isOpen()) {
|
||||||
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||||
|
emit fileError(QString("Не удалось открыть файл логов: %1").arg(m_logFile.errorString()));
|
||||||
|
}
|
||||||
|
} else if (!enabled && m_logFile.isOpen()) {
|
||||||
|
m_logFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setLogToConsoleEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_settings.logToConsole = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setLogFilePath(const QString& path)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (m_logFile.isOpen()) {
|
||||||
|
m_logFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings.logFilePath = path;
|
||||||
|
|
||||||
|
if (m_settings.logToFile) {
|
||||||
|
m_logFile.setFileName(path);
|
||||||
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||||
|
emit fileError(QString("Не удалось открыть файл логов: %1").arg(m_logFile.errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setLevelColors(const QMap<LogLevel, QColor>& colors)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_settings.colors = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setLevelColor(LogLevel level, const QColor& color)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_settings.colors[level] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::setFormat(bool showTimestamp, bool showModule, bool showMethod)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_settings.showTimestamp = showTimestamp;
|
||||||
|
m_settings.showModule = showModule;
|
||||||
|
m_settings.showMethod = showMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::writeToFile(const LogEntry& entry)
|
||||||
|
{
|
||||||
|
if (!m_logFile.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString formatted = formatForFile(entry);
|
||||||
|
m_logStream << formatted << "\n";
|
||||||
|
m_logStream.flush();
|
||||||
|
|
||||||
|
// Проверяем размер файла
|
||||||
|
if (checkFileSize()) {
|
||||||
|
rotateLogFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::rotateLogFile()
|
||||||
|
{
|
||||||
|
if (!m_logFile.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_logFile.close();
|
||||||
|
|
||||||
|
QString basePath = m_settings.logFilePath;
|
||||||
|
QFileInfo fi(basePath);
|
||||||
|
QString baseName = fi.baseName();
|
||||||
|
QString suffix = fi.suffix();
|
||||||
|
QString dir = fi.path();
|
||||||
|
|
||||||
|
// Удаляем самый старый файл
|
||||||
|
QString oldestFile = QString("%1/%2.%3.%4")
|
||||||
|
.arg(dir)
|
||||||
|
.arg(baseName)
|
||||||
|
.arg(m_settings.maxFileCount)
|
||||||
|
.arg(suffix);
|
||||||
|
|
||||||
|
if (QFile::exists(oldestFile)) {
|
||||||
|
QFile::remove(oldestFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переименовываем остальные файлы
|
||||||
|
for (int i = m_settings.maxFileCount - 1; i >= 1; i--) {
|
||||||
|
QString oldFile = QString("%1/%2.%3.%4")
|
||||||
|
.arg(dir)
|
||||||
|
.arg(baseName)
|
||||||
|
.arg(i)
|
||||||
|
.arg(suffix);
|
||||||
|
QString newFile = QString("%1/%2.%3.%4")
|
||||||
|
.arg(dir)
|
||||||
|
.arg(baseName)
|
||||||
|
.arg(i + 1)
|
||||||
|
.arg(suffix);
|
||||||
|
|
||||||
|
if (QFile::exists(oldFile)) {
|
||||||
|
QFile::rename(oldFile, newFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переименовываем текущий файл
|
||||||
|
QString firstBackup = QString("%1/%2.%3.%4")
|
||||||
|
.arg(dir)
|
||||||
|
.arg(baseName)
|
||||||
|
.arg(1)
|
||||||
|
.arg(suffix);
|
||||||
|
|
||||||
|
QFile::rename(basePath, firstBackup);
|
||||||
|
|
||||||
|
// Открываем новый файл
|
||||||
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
emit fileError(QString("Не удалось открыть файл логов после ротации: %1")
|
||||||
|
.arg(m_logFile.errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogManager::checkFileSize()
|
||||||
|
{
|
||||||
|
if (!m_logFile.isOpen()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 size = m_logFile.size();
|
||||||
|
qint64 maxSize = m_settings.maxFileSizeMB * 1024 * 1024;
|
||||||
|
|
||||||
|
return size > maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogManager::formatEntry(const LogEntry& entry) const
|
||||||
|
{
|
||||||
|
QStringList parts;
|
||||||
|
|
||||||
|
if (m_settings.showTimestamp) {
|
||||||
|
parts.append(entry.timestamp.toString("hh:mm:ss"));
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.append(LogEntry::levelToString(entry.level));
|
||||||
|
|
||||||
|
if (m_settings.showModule && !entry.module.isEmpty()) {
|
||||||
|
parts.append(entry.module);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings.showMethod && !entry.method.isEmpty()) {
|
||||||
|
parts.append(entry.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.append(entry.message);
|
||||||
|
|
||||||
|
return parts.join(" | ");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogManager::formatForConsole(const LogEntry& entry) const
|
||||||
|
{
|
||||||
|
QString formatted = formatEntry(entry);
|
||||||
|
|
||||||
|
// Добавляем цвет для консоли (если поддерживается)
|
||||||
|
if (m_settings.colors.contains(entry.level)) {
|
||||||
|
// Для консоли цвет добавляется через escape-последовательности
|
||||||
|
QColor color = m_settings.colors[entry.level];
|
||||||
|
// Это упрощенный вариант, можно расширить для реальной консоли
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogManager::formatForFile(const LogEntry& entry) const
|
||||||
|
{
|
||||||
|
// Для файла используем CSV формат
|
||||||
|
return entry.toCsv();
|
||||||
|
}
|
||||||
+394
@@ -0,0 +1,394 @@
|
|||||||
|
#ifndef LOGMANAGER_H
|
||||||
|
#define LOGMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ПЕРЕЧИСЛЕНИЯ И СТРУКТУРЫ
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Уровни логирования
|
||||||
|
*/
|
||||||
|
enum class LogLevel {
|
||||||
|
Info = 0, // Информационные сообщения
|
||||||
|
Warning = 1, // Предупреждения
|
||||||
|
Error = 2, // Ошибки
|
||||||
|
Debug = 3, // Отладочная информация
|
||||||
|
Critical = 4 // Критические ошибки
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Настройки логирования
|
||||||
|
*/
|
||||||
|
struct LogSettings {
|
||||||
|
bool logToFile = true; // Сохранять в файл
|
||||||
|
bool logToConsole = true; // Выводить в консоль
|
||||||
|
bool logToDatabase = false; // Сохранять в БД
|
||||||
|
QString logFilePath = ""; // Путь к файлу логов
|
||||||
|
int maxFileSizeMB = 10; // Максимальный размер файла (МБ)
|
||||||
|
int maxFileCount = 5; // Максимальное количество файлов
|
||||||
|
bool showTimestamp = true; // Показывать время
|
||||||
|
bool showModule = true; // Показывать модуль
|
||||||
|
bool showMethod = true; // Показывать метод
|
||||||
|
QMap<LogLevel, QColor> colors; // Цвета для уровней
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Запись лога
|
||||||
|
*/
|
||||||
|
struct LogEntry {
|
||||||
|
QDateTime timestamp; // Время записи
|
||||||
|
LogLevel level; // Уровень
|
||||||
|
QString module; // Модуль/компонент
|
||||||
|
QString method; // Метод/функция
|
||||||
|
QString message; // Сообщение
|
||||||
|
qint64 threadId = 0; // ID потока
|
||||||
|
QString sourceFile = ""; // Исходный файл
|
||||||
|
int sourceLine = 0; // Строка в файле
|
||||||
|
|
||||||
|
// Методы для форматирования
|
||||||
|
QString toString() const;
|
||||||
|
QString toHtml() const;
|
||||||
|
QString toCsv() const;
|
||||||
|
QString toJson() const;
|
||||||
|
|
||||||
|
// Статические методы
|
||||||
|
static QString levelToString(LogLevel level);
|
||||||
|
static QColor levelToColor(LogLevel level);
|
||||||
|
static QString formatDateTime(const QDateTime& dt);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// КЛАСС LOGMANAGER
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Менеджер логирования приложения
|
||||||
|
*/
|
||||||
|
class LogManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ========================================================================
|
||||||
|
// СИНГЛТОН
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить экземпляр менеджера (синглтон)
|
||||||
|
*/
|
||||||
|
static LogManager* instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Инициализировать менеджер
|
||||||
|
* @param settings Настройки логирования
|
||||||
|
*/
|
||||||
|
static void initialize(const LogSettings& settings = LogSettings());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Очистить менеджер (освободить ресурсы)
|
||||||
|
*/
|
||||||
|
static void cleanup();
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// ОСНОВНОЙ ИНТЕРФЕЙС
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Добавить запись в лог
|
||||||
|
* @param level Уровень логирования
|
||||||
|
* @param module Модуль/компонент
|
||||||
|
* @param method Метод/функция
|
||||||
|
* @param message Сообщение
|
||||||
|
*/
|
||||||
|
void log(LogLevel level, const QString& module,
|
||||||
|
const QString& method, const QString& message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Добавить запись в лог с информацией об исходном коде
|
||||||
|
*/
|
||||||
|
void log(LogLevel level, const QString& module,
|
||||||
|
const QString& method, const QString& message,
|
||||||
|
const QString& sourceFile, int sourceLine);
|
||||||
|
|
||||||
|
// Быстрые методы для разных уровней
|
||||||
|
void info(const QString& module, const QString& method,
|
||||||
|
const QString& message);
|
||||||
|
void warning(const QString& module, const QString& method,
|
||||||
|
const QString& message);
|
||||||
|
void error(const QString& module, const QString& method,
|
||||||
|
const QString& message);
|
||||||
|
void debug(const QString& module, const QString& method,
|
||||||
|
const QString& message);
|
||||||
|
void critical(const QString& module, const QString& method,
|
||||||
|
const QString& message);
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// ФИЛЬТРАЦИЯ И ПОИСК
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить все записи
|
||||||
|
*/
|
||||||
|
QList<LogEntry> allEntries() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить записи за указанный период
|
||||||
|
*/
|
||||||
|
QList<LogEntry> entriesByTime(const QDateTime& from,
|
||||||
|
const QDateTime& to) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить записи по уровню
|
||||||
|
*/
|
||||||
|
QList<LogEntry> entriesByLevel(LogLevel level) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить записи по модулю
|
||||||
|
*/
|
||||||
|
QList<LogEntry> entriesByModule(const QString& module) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Поиск по тексту сообщения
|
||||||
|
*/
|
||||||
|
QList<LogEntry> search(const QString& text, bool caseSensitive = false) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Применить фильтр
|
||||||
|
*/
|
||||||
|
QList<LogEntry> filter(const QList<LogLevel>& levels,
|
||||||
|
const QString& moduleFilter = "",
|
||||||
|
const QString& methodFilter = "") const;
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// УПРАВЛЕНИЕ
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Очистить все записи
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Сохранить логи в файл
|
||||||
|
*/
|
||||||
|
bool saveToFile(const QString& filePath, bool append = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Загрузить логи из файла
|
||||||
|
*/
|
||||||
|
bool loadFromFile(const QString& filePath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Экспортировать в формат
|
||||||
|
*/
|
||||||
|
bool exportToFormat(const QString& filePath, const QString& format = "txt");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить статистику
|
||||||
|
*/
|
||||||
|
QMap<LogLevel, int> statistics() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить количество записей
|
||||||
|
*/
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить максимальное количество записей в памяти
|
||||||
|
*/
|
||||||
|
int maxEntries() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установить максимальное количество записей в памяти
|
||||||
|
*/
|
||||||
|
void setMaxEntries(int max);
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// НАСТРОЙКИ
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить текущие настройки
|
||||||
|
*/
|
||||||
|
LogSettings settings() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обновить настройки
|
||||||
|
*/
|
||||||
|
void updateSettings(const LogSettings& newSettings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Сохранить настройки в конфиг
|
||||||
|
*/
|
||||||
|
void saveSettings(QSettings& settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Загрузить настройки из конфига
|
||||||
|
*/
|
||||||
|
void loadSettings(QSettings& settings);
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// УТИЛИТЫ
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Включить/выключить логирование в файл
|
||||||
|
*/
|
||||||
|
void setLogToFileEnabled(bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Включить/выключить логирование в консоль
|
||||||
|
*/
|
||||||
|
void setLogToConsoleEnabled(bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установить путь к файлу логов
|
||||||
|
*/
|
||||||
|
void setLogFilePath(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установить цвета для уровней
|
||||||
|
*/
|
||||||
|
void setLevelColors(const QMap<LogLevel, QColor>& colors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установить цвет для уровня
|
||||||
|
*/
|
||||||
|
void setLevelColor(LogLevel level, const QColor& color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установить формат вывода
|
||||||
|
*/
|
||||||
|
void setFormat(bool showTimestamp, bool showModule, bool showMethod);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// ========================================================================
|
||||||
|
// СИГНАЛЫ
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Добавлена новая запись в лог
|
||||||
|
*/
|
||||||
|
void entryAdded(const LogEntry& entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Логи очищены
|
||||||
|
*/
|
||||||
|
void logCleared();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Настройки изменены
|
||||||
|
*/
|
||||||
|
void settingsChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ошибка при записи в файл
|
||||||
|
*/
|
||||||
|
void fileError(const QString& error);
|
||||||
|
|
||||||
|
// Сигналы для конкретных уровней (для удобства)
|
||||||
|
void infoAdded(const LogEntry& entry);
|
||||||
|
void warningAdded(const LogEntry& entry);
|
||||||
|
void errorAdded(const LogEntry& entry);
|
||||||
|
void debugAdded(const LogEntry& entry);
|
||||||
|
void criticalAdded(const LogEntry& entry);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ========================================================================
|
||||||
|
// ПРИВАТНЫЙ КОНСТРУКТОР (синглтон)
|
||||||
|
// ========================================================================
|
||||||
|
explicit LogManager(QObject* parent = nullptr);
|
||||||
|
~LogManager();
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// ПРИВАТНЫЕ МЕТОДЫ
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Записать в файл
|
||||||
|
*/
|
||||||
|
void writeToFile(const LogEntry& entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ротация лог-файлов
|
||||||
|
*/
|
||||||
|
void rotateLogFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Проверить размер файла
|
||||||
|
*/
|
||||||
|
bool checkFileSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Форматирование записи для вывода
|
||||||
|
*/
|
||||||
|
QString formatEntry(const LogEntry& entry) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Форматирование для консоли
|
||||||
|
*/
|
||||||
|
QString formatForConsole(const LogEntry& entry) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Форматирование для файла
|
||||||
|
*/
|
||||||
|
QString formatForFile(const LogEntry& entry) const;
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// ЧЛЕНЫ КЛАССА
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
static LogManager* m_instance; // Экземпляр синглтона
|
||||||
|
QList<LogEntry> m_entries; // Список записей
|
||||||
|
LogSettings m_settings; // Настройки
|
||||||
|
mutable QMutex m_mutex; // Мьютекс для потокобезопасности
|
||||||
|
QFile m_logFile; // Файл логов
|
||||||
|
QTextStream m_logStream; // Поток для записи в файл
|
||||||
|
int m_maxEntries = 10000; // Максимальное количество записей
|
||||||
|
|
||||||
|
// Предварительное объявление для Q_DISABLE_COPY
|
||||||
|
LogManager(const LogManager&) = delete;
|
||||||
|
LogManager& operator=(const LogManager&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// МАКРОСЫ ДЛЯ УДОБСТВА
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Базовый макрос
|
||||||
|
#define LOG(level, module, method, message) \
|
||||||
|
LogManager::instance()->log(level, module, method, message, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
// Макросы для уровней
|
||||||
|
#define LOG_INFO(module, method, message) \
|
||||||
|
LOG(LogLevel::Info, module, method, message)
|
||||||
|
|
||||||
|
#define LOG_WARNING(module, method, message) \
|
||||||
|
LOG(LogLevel::Warning, module, method, message)
|
||||||
|
|
||||||
|
#define LOG_ERROR(module, method, message) \
|
||||||
|
LOG(LogLevel::Error, module, method, message)
|
||||||
|
|
||||||
|
#define LOG_DEBUG(module, method, message) \
|
||||||
|
LOG(LogLevel::Debug, module, method, message)
|
||||||
|
|
||||||
|
#define LOG_CRITICAL(module, method, message) \
|
||||||
|
LOG(LogLevel::Critical, module, method, message)
|
||||||
|
|
||||||
|
// Макрос для проверки на отладку
|
||||||
|
#ifdef QT_DEBUG
|
||||||
|
#define LOG_DEBUG_ONLY(module, method, message) LOG_DEBUG(module, method, message)
|
||||||
|
#else
|
||||||
|
#define LOG_DEBUG_ONLY(module, method, message) Q_UNUSED(module); Q_UNUSED(method); Q_UNUSED(message)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // LOGMANAGER_H
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#include "MediaFileManager.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
MediaFileManager::MediaFileManager()
|
||||||
|
{
|
||||||
|
// Конструктор может быть использован для инициализации
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaFileManager::addFile(const QString& name, const QString& filePath)
|
||||||
|
{
|
||||||
|
// Проверка на пустые значения
|
||||||
|
if (name.isEmpty() || filePath.isEmpty()) {
|
||||||
|
qWarning() << "Имя файла или путь не могут быть пустыми";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на уникальность имени
|
||||||
|
if (contains(name)) {
|
||||||
|
qWarning() << "Файл с именем" << name << "уже существует";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавление нового файла
|
||||||
|
mediaFiles.append(MediaFile(name, filePath));
|
||||||
|
qDebug() << "Файл" << name << "добавлен с путем:" << filePath;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaFileManager::removeFile(const QString& name)
|
||||||
|
{
|
||||||
|
int index = findFileIndex(name);
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "Файл с именем" << name << "не найден";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaFiles.remove(index);
|
||||||
|
qDebug() << "Файл" << name << "удален";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaFileManager::updateFile(const QString& oldName, const QString& newName, const QString& newFilePath)
|
||||||
|
{
|
||||||
|
int index = findFileIndex(oldName);
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "Файл с именем" << oldName << "не найден";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не используется ли новое имя другим файлом
|
||||||
|
if (oldName != newName && contains(newName)) {
|
||||||
|
qWarning() << "Файл с именем" << newName << "уже существует";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем информацию о файле
|
||||||
|
mediaFiles[index].name = newName;
|
||||||
|
mediaFiles[index].filePath = newFilePath;
|
||||||
|
|
||||||
|
qDebug() << "Файл обновлен:" << oldName << "->" << newName << "путь:" << newFilePath;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaFileManager::getFilePathByName(const QString& name) const
|
||||||
|
{
|
||||||
|
int index = findFileIndex(name);
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "Файл с именем" << name << "не найден";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaFiles[index].filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MediaFileManager::getFileCount() const
|
||||||
|
{
|
||||||
|
return mediaFiles.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<MediaFile> MediaFileManager::getAllFiles() const
|
||||||
|
{
|
||||||
|
return mediaFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaFileManager::contains(const QString& name) const
|
||||||
|
{
|
||||||
|
return findFileIndex(name) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MediaFileManager::findFileIndex(const QString& name) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mediaFiles.size(); ++i) {
|
||||||
|
if (mediaFiles[i].name == name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef MEDIAFILEMANAGER_H
|
||||||
|
#define MEDIAFILEMANAGER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
// Структура для хранения информации о медиафайле
|
||||||
|
struct MediaFile {
|
||||||
|
QString name; // Имя файла
|
||||||
|
QString filePath; // Полный путь к файлу
|
||||||
|
|
||||||
|
MediaFile() = default;
|
||||||
|
MediaFile(const QString& name, const QString& filePath)
|
||||||
|
: name(name), filePath(filePath) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaFileManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MediaFileManager();
|
||||||
|
|
||||||
|
// Добавление файла
|
||||||
|
bool addFile(const QString& name, const QString& filePath);
|
||||||
|
|
||||||
|
// Удаление файла по имени
|
||||||
|
bool removeFile(const QString& name);
|
||||||
|
|
||||||
|
// Изменение информации о файле
|
||||||
|
bool updateFile(const QString& oldName, const QString& newName, const QString& newFilePath);
|
||||||
|
|
||||||
|
// Получение пути к файлу по имени
|
||||||
|
QString getFilePathByName(const QString& name) const;
|
||||||
|
|
||||||
|
// Получение количества файлов
|
||||||
|
int getFileCount() const;
|
||||||
|
|
||||||
|
// Получение всех файлов (для отладки или отображения)
|
||||||
|
QVector<MediaFile> getAllFiles() const;
|
||||||
|
|
||||||
|
// Проверка существования файла по имени
|
||||||
|
bool contains(const QString& name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<MediaFile> mediaFiles;
|
||||||
|
|
||||||
|
// Поиск индекса файла по имени
|
||||||
|
int findFileIndex(const QString& name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MEDIAFILEMANAGER_H
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
|
debug/commandprocessor.o
|
||||||
debug/emoteprovider.o
|
debug/emoteprovider.o
|
||||||
debug/fcolorsetting.o
|
debug/fcolorsetting.o
|
||||||
debug/fcreatechat.o
|
debug/fcreatechat.o
|
||||||
debug/fcreatenotify.o
|
debug/fcreatenotify.o
|
||||||
debug/ffontsetting.o
|
debug/ffontsetting.o
|
||||||
|
debug/filemanager.o
|
||||||
debug/fsettingsws.o
|
debug/fsettingsws.o
|
||||||
debug/fsinglegrid.o
|
debug/fsinglegrid.o
|
||||||
|
debug/logmanager.o
|
||||||
debug/main.o
|
debug/main.o
|
||||||
|
debug/mediafilemanager.o
|
||||||
debug/neuralnetworkmanager.o
|
debug/neuralnetworkmanager.o
|
||||||
|
debug/randommanager.o
|
||||||
|
debug/randomresponses.o
|
||||||
debug/soundmanager.o
|
debug/soundmanager.o
|
||||||
debug/tauth.o
|
debug/tauth.o
|
||||||
debug/ttw_api.o
|
debug/ttw_api.o
|
||||||
@@ -20,6 +26,7 @@ debug/userwidget.o
|
|||||||
debug/webserverchat.o
|
debug/webserverchat.o
|
||||||
debug/webservernotify.o
|
debug/webservernotify.o
|
||||||
debug/websocketclient.o
|
debug/websocketclient.o
|
||||||
|
debug/moc_commandprocessor.o
|
||||||
debug/moc_emoteprovider.o
|
debug/moc_emoteprovider.o
|
||||||
debug/moc_fcolorsetting.o
|
debug/moc_fcolorsetting.o
|
||||||
debug/moc_fcreatechat.o
|
debug/moc_fcreatechat.o
|
||||||
@@ -27,7 +34,10 @@ debug/moc_fcreatenotify.o
|
|||||||
debug/moc_ffontsetting.o
|
debug/moc_ffontsetting.o
|
||||||
debug/moc_fsettingsws.o
|
debug/moc_fsettingsws.o
|
||||||
debug/moc_fsinglegrid.o
|
debug/moc_fsinglegrid.o
|
||||||
|
debug/moc_logmanager.o
|
||||||
debug/moc_neuralnetworkmanager.o
|
debug/moc_neuralnetworkmanager.o
|
||||||
|
debug/moc_randommanager.o
|
||||||
|
debug/moc_randomresponses.o
|
||||||
debug/moc_soundmanager.o
|
debug/moc_soundmanager.o
|
||||||
debug/moc_tauth.o
|
debug/moc_tauth.o
|
||||||
debug/moc_ttw_api.o
|
debug/moc_ttw_api.o
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
|
release/commandprocessor.o
|
||||||
release/emoteprovider.o
|
release/emoteprovider.o
|
||||||
release/fcolorsetting.o
|
release/fcolorsetting.o
|
||||||
release/fcreatechat.o
|
release/fcreatechat.o
|
||||||
release/fcreatenotify.o
|
release/fcreatenotify.o
|
||||||
release/ffontsetting.o
|
release/ffontsetting.o
|
||||||
|
release/filemanager.o
|
||||||
release/fsettingsws.o
|
release/fsettingsws.o
|
||||||
release/fsinglegrid.o
|
release/fsinglegrid.o
|
||||||
|
release/logmanager.o
|
||||||
release/main.o
|
release/main.o
|
||||||
|
release/mediafilemanager.o
|
||||||
release/neuralnetworkmanager.o
|
release/neuralnetworkmanager.o
|
||||||
|
release/randommanager.o
|
||||||
|
release/randomresponses.o
|
||||||
release/soundmanager.o
|
release/soundmanager.o
|
||||||
release/tauth.o
|
release/tauth.o
|
||||||
release/ttw_api.o
|
release/ttw_api.o
|
||||||
@@ -20,6 +26,7 @@ release/userwidget.o
|
|||||||
release/webserverchat.o
|
release/webserverchat.o
|
||||||
release/webservernotify.o
|
release/webservernotify.o
|
||||||
release/websocketclient.o
|
release/websocketclient.o
|
||||||
|
release/moc_commandprocessor.o
|
||||||
release/moc_emoteprovider.o
|
release/moc_emoteprovider.o
|
||||||
release/moc_fcolorsetting.o
|
release/moc_fcolorsetting.o
|
||||||
release/moc_fcreatechat.o
|
release/moc_fcreatechat.o
|
||||||
@@ -27,7 +34,10 @@ release/moc_fcreatenotify.o
|
|||||||
release/moc_ffontsetting.o
|
release/moc_ffontsetting.o
|
||||||
release/moc_fsettingsws.o
|
release/moc_fsettingsws.o
|
||||||
release/moc_fsinglegrid.o
|
release/moc_fsinglegrid.o
|
||||||
|
release/moc_logmanager.o
|
||||||
release/moc_neuralnetworkmanager.o
|
release/moc_neuralnetworkmanager.o
|
||||||
|
release/moc_randommanager.o
|
||||||
|
release/moc_randomresponses.o
|
||||||
release/moc_soundmanager.o
|
release/moc_soundmanager.o
|
||||||
release/moc_tauth.o
|
release/moc_tauth.o
|
||||||
release/moc_ttw_api.o
|
release/moc_ttw_api.o
|
||||||
|
|||||||
@@ -0,0 +1,294 @@
|
|||||||
|
#include "randommanager.h"
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
RandomManager::RandomManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_database(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomManager::~RandomManager()
|
||||||
|
{
|
||||||
|
// Сохраняем данные при уничтожении
|
||||||
|
if (m_database) {
|
||||||
|
saveToDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::initialize(uDataBase *database)
|
||||||
|
{
|
||||||
|
if (!database || !database->isConnected()) {
|
||||||
|
qWarning() << "RandomManager: База данных не подключена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_database = database;
|
||||||
|
return loadFromDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::addRange(const QString &name, int startValue, int endValue)
|
||||||
|
{
|
||||||
|
// Проверка входных данных
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
qWarning() << "RandomManager: Нельзя добавить диапазон с пустым именем";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidRange(startValue, endValue)) {
|
||||||
|
qWarning() << "RandomManager: Некорректный диапазон:" << startValue << "-" << endValue;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на дубликат
|
||||||
|
if (contains(name)) {
|
||||||
|
qWarning() << "RandomManager: Диапазон с именем" << name << "уже существует";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем в список
|
||||||
|
RandomStruct newRange(name, startValue, endValue);
|
||||||
|
m_ranges.append(newRange);
|
||||||
|
|
||||||
|
// Сохраняем в БД
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
m_ranges.removeLast(); // Откатываем если не удалось сохранить
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit rangeAdded(name, startValue, endValue);
|
||||||
|
emit dataChanged();
|
||||||
|
|
||||||
|
qDebug() << "RandomManager: Добавлен диапазон" << name << startValue << "-" << endValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::removeRange(const QString &name)
|
||||||
|
{
|
||||||
|
int index = findIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "RandomManager: Диапазон" << name << "не найден";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем данные для сигнала
|
||||||
|
RandomStruct removedRange = m_ranges.at(index);
|
||||||
|
|
||||||
|
// Удаляем из списка
|
||||||
|
m_ranges.removeAt(index);
|
||||||
|
|
||||||
|
// Сохраняем в БД
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
// Откатываем изменения
|
||||||
|
m_ranges.insert(index, removedRange);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit rangeRemoved(name);
|
||||||
|
emit dataChanged();
|
||||||
|
|
||||||
|
qDebug() << "RandomManager: Удален диапазон" << name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::updateRange(const QString &oldName, const QString &newName,
|
||||||
|
int startValue, int endValue)
|
||||||
|
{
|
||||||
|
int index = findIndex(oldName);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "RandomManager: Диапазон" << oldName << "не найден";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidRange(startValue, endValue)) {
|
||||||
|
qWarning() << "RandomManager: Некорректный диапазон:" << startValue << "-" << endValue;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не занято ли новое имя другим диапазоном (если имя изменилось)
|
||||||
|
if (oldName != newName && contains(newName)) {
|
||||||
|
qWarning() << "RandomManager: Имя" << newName << "уже занято";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем старые данные для отката
|
||||||
|
RandomStruct oldRange = m_ranges.at(index);
|
||||||
|
|
||||||
|
// Обновляем
|
||||||
|
m_ranges[index].name = newName;
|
||||||
|
m_ranges[index].startValue = startValue;
|
||||||
|
m_ranges[index].endValue = endValue;
|
||||||
|
|
||||||
|
// Сохраняем в БД
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
// Откатываем изменения
|
||||||
|
m_ranges[index] = oldRange;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit rangeUpdated(oldName, newName);
|
||||||
|
emit dataChanged();
|
||||||
|
|
||||||
|
qDebug() << "RandomManager: Обновлен диапазон" << oldName << "->" << newName
|
||||||
|
<< startValue << "-" << endValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::contains(const QString &name) const
|
||||||
|
{
|
||||||
|
return findIndex(name) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RandomManager::getRandomValue(const QString &name) const
|
||||||
|
{
|
||||||
|
int index = findIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "RandomManager: Диапазон" << name << "не найден";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RandomStruct &range = m_ranges.at(index);
|
||||||
|
if (range.startValue == range.endValue) {
|
||||||
|
return range.startValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int min = qMin(range.startValue, range.endValue);
|
||||||
|
int max = qMax(range.startValue, range.endValue);
|
||||||
|
|
||||||
|
return QRandomGenerator::global()->bounded(min, max + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<int, int> RandomManager::getRange(const QString &name) const
|
||||||
|
{
|
||||||
|
int index = findIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
return qMakePair(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const RandomStruct &range = m_ranges.at(index);
|
||||||
|
return qMakePair(range.startValue, range.endValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::saveToDatabase()
|
||||||
|
{
|
||||||
|
if (!m_database) {
|
||||||
|
qWarning() << "RandomManager: База данных не установлена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем временную таблицу для сохранения
|
||||||
|
QTableWidget tempTable;
|
||||||
|
|
||||||
|
// Настраиваем таблицу
|
||||||
|
QStringList headers;
|
||||||
|
headers << "Имя" << "От" << "До";
|
||||||
|
tempTable.setColumnCount(headers.size());
|
||||||
|
tempTable.setHorizontalHeaderLabels(headers);
|
||||||
|
|
||||||
|
// Заполняем таблицу данными
|
||||||
|
tempTable.setRowCount(m_ranges.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < m_ranges.size(); ++i) {
|
||||||
|
const RandomStruct &range = m_ranges.at(i);
|
||||||
|
|
||||||
|
// Имя
|
||||||
|
QTableWidgetItem *nameItem = new QTableWidgetItem(range.name);
|
||||||
|
tempTable.setItem(i, 0, nameItem);
|
||||||
|
|
||||||
|
// Начальное значение
|
||||||
|
QTableWidgetItem *startItem = new QTableWidgetItem(QString::number(range.startValue));
|
||||||
|
startItem->setTextAlignment(Qt::AlignCenter);
|
||||||
|
tempTable.setItem(i, 1, startItem);
|
||||||
|
|
||||||
|
// Конечное значение
|
||||||
|
QTableWidgetItem *endItem = new QTableWidgetItem(QString::number(range.endValue));
|
||||||
|
endItem->setTextAlignment(Qt::AlignCenter);
|
||||||
|
tempTable.setItem(i, 2, endItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем в БД
|
||||||
|
// Используем метод SaveTableWidget, который сохраняет данные в соответствующую таблицу
|
||||||
|
m_database->SaveTableWidget(&tempTable);
|
||||||
|
|
||||||
|
qDebug() << "RandomManager: Сохранено" << m_ranges.size() << "диапазонов в БД";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::loadFromDatabase()
|
||||||
|
{
|
||||||
|
if (!m_database) {
|
||||||
|
qWarning() << "RandomManager: База данных не установлена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем временную таблицу для загрузки
|
||||||
|
QTableWidget tempTable;
|
||||||
|
|
||||||
|
// Настраиваем таблицу
|
||||||
|
QStringList headers;
|
||||||
|
headers << "Имя" << "От" << "До";
|
||||||
|
tempTable.setColumnCount(headers.size());
|
||||||
|
tempTable.setHorizontalHeaderLabels(headers);
|
||||||
|
|
||||||
|
// Загружаем данные из БД
|
||||||
|
m_database->LoadTableWidget(&tempTable);
|
||||||
|
|
||||||
|
// Очищаем текущий список
|
||||||
|
m_ranges.clear();
|
||||||
|
|
||||||
|
// Заполняем список из таблицы
|
||||||
|
for (int row = 0; row < tempTable.rowCount(); ++row) {
|
||||||
|
QTableWidgetItem *nameItem = tempTable.item(row, 0);
|
||||||
|
QTableWidgetItem *startItem = tempTable.item(row, 1);
|
||||||
|
QTableWidgetItem *endItem = tempTable.item(row, 2);
|
||||||
|
|
||||||
|
if (!nameItem || !startItem || !endItem) {
|
||||||
|
qWarning() << "RandomManager: Пропущена строка" << row << "из-за отсутствия данных";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = nameItem->text().trimmed();
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
qWarning() << "RandomManager: Пропущена строка" << row << "с пустым именем";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool okStart, okEnd;
|
||||||
|
int startValue = startItem->text().toInt(&okStart);
|
||||||
|
int endValue = endItem->text().toInt(&okEnd);
|
||||||
|
|
||||||
|
if (!okStart || !okEnd) {
|
||||||
|
qWarning() << "RandomManager: Некорректные числовые значения в строке" << row;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidRange(startValue, endValue)) {
|
||||||
|
qWarning() << "RandomManager: Некорректный диапазон в строке" << row
|
||||||
|
<< startValue << "-" << endValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ranges.append(RandomStruct(name, startValue, endValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "RandomManager: Загружено" << m_ranges.size() << "диапазонов из БД";
|
||||||
|
emit dataChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomManager::isValidRange(int start, int end) const
|
||||||
|
{
|
||||||
|
// Допускаются любые целые числа, даже если start > end
|
||||||
|
// Мы будем использовать qMin и qMax при генерации случайного числа
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RandomManager::findIndex(const QString &name) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_ranges.size(); ++i) {
|
||||||
|
if (m_ranges.at(i).name == name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef RANDOMMANAGER_H
|
||||||
|
#define RANDOMMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include "udatabase.h"
|
||||||
|
|
||||||
|
class RandomManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct RandomStruct {
|
||||||
|
QString name;
|
||||||
|
int startValue;
|
||||||
|
int endValue;
|
||||||
|
|
||||||
|
RandomStruct() : startValue(0), endValue(0) {}
|
||||||
|
RandomStruct(const QString &name, int start, int end)
|
||||||
|
: name(name), startValue(start), endValue(end) {}
|
||||||
|
|
||||||
|
bool operator==(const QString &otherName) const { return name == otherName; }
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit RandomManager(QObject *parent = nullptr);
|
||||||
|
~RandomManager();
|
||||||
|
|
||||||
|
// Основные методы
|
||||||
|
bool initialize(uDataBase *database);
|
||||||
|
|
||||||
|
// Работа с диапазонами
|
||||||
|
bool addRange(const QString &name, int startValue, int endValue);
|
||||||
|
bool removeRange(const QString &name);
|
||||||
|
bool updateRange(const QString &oldName, const QString &newName, int startValue, int endValue);
|
||||||
|
|
||||||
|
// Получение данных
|
||||||
|
QList<RandomStruct> getAllRanges() const { return m_ranges; }
|
||||||
|
bool contains(const QString &name) const;
|
||||||
|
int getRandomValue(const QString &name) const;
|
||||||
|
QPair<int, int> getRange(const QString &name) const;
|
||||||
|
|
||||||
|
// Сохранение/загрузка
|
||||||
|
bool saveToDatabase();
|
||||||
|
bool loadFromDatabase();
|
||||||
|
|
||||||
|
// Валидация
|
||||||
|
bool isValidRange(int start, int end) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dataChanged();
|
||||||
|
void rangeAdded(const QString &name, int start, int end);
|
||||||
|
void rangeRemoved(const QString &name);
|
||||||
|
void rangeUpdated(const QString &oldName, const QString &newName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uDataBase *m_database;
|
||||||
|
QList<RandomStruct> m_ranges;
|
||||||
|
|
||||||
|
int findIndex(const QString &name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RANDOMMANAGER_H
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#include "RandomResponses.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
RandomResponses::RandomResponses(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_randomGenerator(QRandomGenerator::global()->generate())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RandomResponses::getResponse(const QString &groupName)
|
||||||
|
{
|
||||||
|
// Проверяем существование группы
|
||||||
|
if (!m_groups.contains(groupName)) {
|
||||||
|
qWarning() << "Группа" << groupName << "не найдена";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResponseGroup &group = m_groups[groupName];
|
||||||
|
|
||||||
|
// Проверяем наличие ответов в группе
|
||||||
|
if (group.responses.isEmpty()) {
|
||||||
|
qWarning() << "Группа" << groupName << "не содержит ответов";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем случайный индекс
|
||||||
|
int randomIndex = m_randomGenerator.bounded(group.responses.size());
|
||||||
|
|
||||||
|
// Возвращаем случайный ответ
|
||||||
|
return group.responses.at(randomIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomResponses::addResponse(const QString &groupName, const QString &response)
|
||||||
|
{
|
||||||
|
if (response.isEmpty()) {
|
||||||
|
qWarning() << "Пустой ответ не может быть добавлен";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если группа существует, добавляем ответ в существующую
|
||||||
|
if (m_groups.contains(groupName)) {
|
||||||
|
m_groups[groupName].responses.append(response);
|
||||||
|
}
|
||||||
|
// Иначе создаем новую группу
|
||||||
|
else {
|
||||||
|
ResponseGroup newGroup;
|
||||||
|
newGroup.name = groupName;
|
||||||
|
newGroup.responses.append(response);
|
||||||
|
m_groups.insert(groupName, newGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomResponses::removeResponse(const QString &groupName, const QString &response)
|
||||||
|
{
|
||||||
|
// Проверяем существование группы
|
||||||
|
if (!m_groups.contains(groupName)) {
|
||||||
|
qWarning() << "Группа" << groupName << "не найдена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseGroup &group = m_groups[groupName];
|
||||||
|
|
||||||
|
// Удаляем все вхождения ответа
|
||||||
|
int removedCount = group.responses.removeAll(response);
|
||||||
|
|
||||||
|
// Если группа стала пустой, удаляем ее
|
||||||
|
if (group.responses.isEmpty()) {
|
||||||
|
m_groups.remove(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return removedCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomResponses::removeGroup(const QString &groupName)
|
||||||
|
{
|
||||||
|
return m_groups.remove(groupName) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList RandomResponses::getGroupNames() const
|
||||||
|
{
|
||||||
|
return m_groups.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList RandomResponses::getGroupResponses(const QString &groupName) const
|
||||||
|
{
|
||||||
|
if (m_groups.contains(groupName)) {
|
||||||
|
return m_groups[groupName].responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomResponses::hasGroup(const QString &groupName) const
|
||||||
|
{
|
||||||
|
return m_groups.contains(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RandomResponses::clear()
|
||||||
|
{
|
||||||
|
m_groups.clear();
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#ifndef RANDOMRESPONSES_H
|
||||||
|
#define RANDOMRESPONSES_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QList>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
|
// Структура для хранения группы ответов
|
||||||
|
struct ResponseGroup {
|
||||||
|
QString name; // Имя группы
|
||||||
|
QStringList responses; // Список возможных ответов
|
||||||
|
};
|
||||||
|
|
||||||
|
class RandomResponses : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RandomResponses(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
// Получить случайный ответ из группы
|
||||||
|
QString getResponse(const QString &groupName);
|
||||||
|
|
||||||
|
// Добавить группу или дополнить существующую
|
||||||
|
bool addResponse(const QString &groupName, const QString &response);
|
||||||
|
|
||||||
|
// Удалить конкретный ответ из группы
|
||||||
|
bool removeResponse(const QString &groupName, const QString &response);
|
||||||
|
|
||||||
|
// Удалить всю группу
|
||||||
|
bool removeGroup(const QString &groupName);
|
||||||
|
|
||||||
|
// Получить список всех групп
|
||||||
|
QStringList getGroupNames() const;
|
||||||
|
|
||||||
|
// Получить все ответы группы
|
||||||
|
QStringList getGroupResponses(const QString &groupName) const;
|
||||||
|
|
||||||
|
// Проверить существование группы
|
||||||
|
bool hasGroup(const QString &groupName) const;
|
||||||
|
|
||||||
|
// Очистить все данные
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, ResponseGroup> m_groups; // Хранение групп по имени
|
||||||
|
QRandomGenerator m_randomGenerator; // Генератор случайных чисел
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RANDOMRESPONSES_H
|
||||||
+2
-2
@@ -323,7 +323,7 @@ void TTwAPI::banUserTime(const QString &id, int timeMinutes)
|
|||||||
toLog(2, "TTwAPI::banUserTime", "Не удалось получить botId");
|
toLog(2, "TTwAPI::banUserTime", "Не удалось получить botId");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
toLog(2, "TTwAPI::баним", id);
|
||||||
QJsonObject innerData;
|
QJsonObject innerData;
|
||||||
innerData["user_id"] = id;
|
innerData["user_id"] = id;
|
||||||
innerData["duration"] = timeMinutes * 60;
|
innerData["duration"] = timeMinutes * 60;
|
||||||
@@ -503,7 +503,7 @@ void TTwAPI::toLog(int level, const QString &method, const QString &message)
|
|||||||
Q_UNUSED(level);
|
Q_UNUSED(level);
|
||||||
Q_UNUSED(method);
|
Q_UNUSED(method);
|
||||||
Q_UNUSED(message);
|
Q_UNUSED(message);
|
||||||
|
qDebug() << message;
|
||||||
// uGeneral.toLog("ttw_api", method, message, level);
|
// uGeneral.toLog("ttw_api", method, message, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+222
@@ -1179,3 +1179,225 @@ bool uDataBase::updateChat(const QString &name, HttpServerChat *server,
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool uDataBase::createNotificationsTable()
|
||||||
|
{
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
QString sql =
|
||||||
|
"CREATE TABLE IF NOT EXISTS notifications ("
|
||||||
|
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
|
" name TEXT NOT NULL,"
|
||||||
|
" type TEXT NOT NULL," // "notification"
|
||||||
|
" port INTEGER NOT NULL,"
|
||||||
|
" block_color TEXT,"
|
||||||
|
" border_color TEXT,"
|
||||||
|
" border_size INTEGER,"
|
||||||
|
" transparency INTEGER,"
|
||||||
|
" page_background_color TEXT,"
|
||||||
|
" title_family TEXT,"
|
||||||
|
" title_size INTEGER,"
|
||||||
|
" title_color TEXT,"
|
||||||
|
" content_family TEXT,"
|
||||||
|
" content_size INTEGER,"
|
||||||
|
" content_color TEXT,"
|
||||||
|
" duration INTEGER,"
|
||||||
|
" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||||
|
")";
|
||||||
|
|
||||||
|
if (!query.exec(sql)) {
|
||||||
|
m_lastError = query.lastError().text();
|
||||||
|
qWarning() << "Failed to create notifications table:" << m_lastError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем индекс для быстрого поиска по порту
|
||||||
|
sql = "CREATE INDEX IF NOT EXISTS idx_notifications_port ON notifications (port)";
|
||||||
|
query.exec(sql);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uDataBase::saveNotification(const QString &name, HttpServer *server)
|
||||||
|
{
|
||||||
|
if (!server) {
|
||||||
|
m_lastError = "Server is null";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_db.isOpen()) {
|
||||||
|
m_lastError = "Database is not open";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем таблицу если не существует
|
||||||
|
if (!tableExists("notifications")) {
|
||||||
|
if (!createNotificationsTable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO notifications ("
|
||||||
|
" name, type, port, block_color, border_color, border_size,"
|
||||||
|
" transparency, page_background_color, title_family, title_size,"
|
||||||
|
" title_color, content_family, content_size, content_color, duration"
|
||||||
|
") VALUES ("
|
||||||
|
" :name, :type, :port, :block_color, :border_color, :border_size,"
|
||||||
|
" :transparency, :page_background_color, :title_family, :title_size,"
|
||||||
|
" :title_color, :content_family, :content_size, :content_color, :duration"
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Используем реальные значения из сервера
|
||||||
|
query.bindValue(":name", name);
|
||||||
|
query.bindValue(":type", "notification");
|
||||||
|
query.bindValue(":port", server->port());
|
||||||
|
query.bindValue(":block_color", server->getBlockColor());
|
||||||
|
query.bindValue(":border_color", server->getBorderColor());
|
||||||
|
query.bindValue(":border_size", server->getBorderSize());
|
||||||
|
query.bindValue(":transparency", server->getTransparency());
|
||||||
|
query.bindValue(":page_background_color", server->getPageBackgroundColor());
|
||||||
|
query.bindValue(":title_family", server->getTitleFamily());
|
||||||
|
query.bindValue(":title_size", server->getTitleSize());
|
||||||
|
query.bindValue(":title_color", server->getTitleColor());
|
||||||
|
query.bindValue(":content_family", server->getContentFamily());
|
||||||
|
query.bindValue(":content_size", server->getContentSize());
|
||||||
|
query.bindValue(":content_color", server->getContentColor());
|
||||||
|
query.bindValue(":duration", server->getDuration());
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
m_lastError = query.lastError().text();
|
||||||
|
qWarning() << "Failed to save notification:" << m_lastError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uDataBase::updateNotification(const QString &name, HttpServer *server, int oldPort)
|
||||||
|
{
|
||||||
|
if (!server) {
|
||||||
|
m_lastError = "Server is null";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_db.isOpen()) {
|
||||||
|
m_lastError = "Database is not open";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare(
|
||||||
|
"UPDATE notifications SET "
|
||||||
|
" name = :name, "
|
||||||
|
" port = :port, "
|
||||||
|
" block_color = :block_color, "
|
||||||
|
" border_color = :border_color, "
|
||||||
|
" border_size = :border_size, "
|
||||||
|
" transparency = :transparency, "
|
||||||
|
" page_background_color = :page_background_color, "
|
||||||
|
" title_family = :title_family, "
|
||||||
|
" title_size = :title_size, "
|
||||||
|
" title_color = :title_color, "
|
||||||
|
" content_family = :content_family, "
|
||||||
|
" content_size = :content_size, "
|
||||||
|
" content_color = :content_color, "
|
||||||
|
" duration = :duration "
|
||||||
|
"WHERE port = :old_port"
|
||||||
|
);
|
||||||
|
|
||||||
|
query.bindValue(":name", name);
|
||||||
|
query.bindValue(":port", server->port());
|
||||||
|
query.bindValue(":block_color", server->getBlockColor());
|
||||||
|
query.bindValue(":border_color", server->getBorderColor());
|
||||||
|
query.bindValue(":border_size", server->getBorderSize());
|
||||||
|
query.bindValue(":transparency", server->getTransparency());
|
||||||
|
query.bindValue(":page_background_color", server->getPageBackgroundColor());
|
||||||
|
query.bindValue(":title_family", server->getTitleFamily());
|
||||||
|
query.bindValue(":title_size", server->getTitleSize());
|
||||||
|
query.bindValue(":title_color", server->getTitleColor());
|
||||||
|
query.bindValue(":content_family", server->getContentFamily());
|
||||||
|
query.bindValue(":content_size", server->getContentSize());
|
||||||
|
query.bindValue(":content_color", server->getContentColor());
|
||||||
|
query.bindValue(":duration", server->getDuration());
|
||||||
|
query.bindValue(":old_port", oldPort);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
m_lastError = query.lastError().text();
|
||||||
|
qWarning() << "Failed to update notification:" << m_lastError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<NotificationSettings> uDataBase::loadAllNotifications()
|
||||||
|
{
|
||||||
|
QList<NotificationSettings> notifications;
|
||||||
|
|
||||||
|
if (!m_db.isOpen()) {
|
||||||
|
m_lastError = "Database is not open";
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tableExists("notifications")) {
|
||||||
|
// Если таблицы нет, ничего не загружаем
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
QString sql = "SELECT * FROM notifications ORDER BY created_at";
|
||||||
|
|
||||||
|
if (!query.exec(sql)) {
|
||||||
|
m_lastError = query.lastError().text();
|
||||||
|
qWarning() << "Failed to load notifications:" << m_lastError;
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (query.next()) {
|
||||||
|
NotificationSettings settings;
|
||||||
|
settings.id = query.value("id").toInt();
|
||||||
|
settings.name = query.value("name").toString();
|
||||||
|
settings.type = query.value("type").toString();
|
||||||
|
settings.port = query.value("port").toInt();
|
||||||
|
settings.blockColor = query.value("block_color").toString();
|
||||||
|
settings.borderColor = query.value("border_color").toString();
|
||||||
|
settings.borderSize = query.value("border_size").toInt();
|
||||||
|
settings.transparency = query.value("transparency").toInt();
|
||||||
|
settings.pageBackgroundColor = query.value("page_background_color").toString();
|
||||||
|
settings.titleFamily = query.value("title_family").toString();
|
||||||
|
settings.titleSize = query.value("title_size").toInt();
|
||||||
|
settings.titleColor = query.value("title_color").toString();
|
||||||
|
settings.contentFamily = query.value("content_family").toString();
|
||||||
|
settings.contentSize = query.value("content_size").toInt();
|
||||||
|
settings.contentColor = query.value("content_color").toString();
|
||||||
|
settings.duration = query.value("duration").toInt();
|
||||||
|
settings.createdAt = query.value("created_at").toDateTime();
|
||||||
|
|
||||||
|
notifications.append(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uDataBase::deleteNotification(int port)
|
||||||
|
{
|
||||||
|
if (!m_db.isOpen()) {
|
||||||
|
m_lastError = "Database is not open";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("DELETE FROM notifications WHERE port = :port");
|
||||||
|
query.bindValue(":port", port);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
m_lastError = query.lastError().text();
|
||||||
|
qWarning() << "Failed to delete notification:" << m_lastError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
+29
@@ -13,6 +13,7 @@
|
|||||||
#include <QTableWidget>
|
#include <QTableWidget>
|
||||||
#include "timerinfo.h"
|
#include "timerinfo.h"
|
||||||
#include "webserverchat.h"
|
#include "webserverchat.h"
|
||||||
|
#include "webservernotify.h"
|
||||||
|
|
||||||
struct ChatSettings {
|
struct ChatSettings {
|
||||||
int id;
|
int id;
|
||||||
@@ -35,6 +36,28 @@ struct ChatSettings {
|
|||||||
bool deleteByTime;
|
bool deleteByTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NotificationSettings {
|
||||||
|
int id;
|
||||||
|
QString name;
|
||||||
|
QString type;
|
||||||
|
int port;
|
||||||
|
QString blockColor;
|
||||||
|
QString borderColor;
|
||||||
|
int borderSize;
|
||||||
|
int transparency;
|
||||||
|
QString pageBackgroundColor;
|
||||||
|
QString titleFamily;
|
||||||
|
int titleSize;
|
||||||
|
QString titleColor;
|
||||||
|
QString contentFamily;
|
||||||
|
int contentSize;
|
||||||
|
QString contentColor;
|
||||||
|
int duration;
|
||||||
|
QDateTime createdAt;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class uDataBase : public QObject
|
class uDataBase : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -84,6 +107,12 @@ public:
|
|||||||
bool deleteChat(int port);
|
bool deleteChat(int port);
|
||||||
QList<ChatSettings> loadAllChats();
|
QList<ChatSettings> loadAllChats();
|
||||||
|
|
||||||
|
// Методы для уведомлений
|
||||||
|
bool createNotificationsTable();
|
||||||
|
bool saveNotification(const QString &name, HttpServer *server);
|
||||||
|
bool updateNotification(const QString &name, HttpServer *server, int oldPort);
|
||||||
|
QList<NotificationSettings> loadAllNotifications();
|
||||||
|
bool deleteNotification(int port);
|
||||||
|
|
||||||
// Проверка подключения к БД
|
// Проверка подключения к БД
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|||||||
+611
-1052
File diff suppressed because it is too large
Load Diff
+30
-28
@@ -9,8 +9,10 @@
|
|||||||
#include <ulink.h>
|
#include <ulink.h>
|
||||||
#include <udatabase.h>
|
#include <udatabase.h>
|
||||||
#include <soundmanager.h>
|
#include <soundmanager.h>
|
||||||
|
#include "commandprocessor.h"
|
||||||
#include "fcreatechat.h"
|
#include "fcreatechat.h"
|
||||||
#include "fcreatenotify.h"
|
#include "fcreatenotify.h"
|
||||||
|
#include "logmanager.h"
|
||||||
#include "neuralnetworkmanager.h"
|
#include "neuralnetworkmanager.h"
|
||||||
#include "ttw_api.h"
|
#include "ttw_api.h"
|
||||||
#include "user_manager.h"
|
#include "user_manager.h"
|
||||||
@@ -71,17 +73,6 @@ public:
|
|||||||
RListCommands(const QString &r1, const QString &r2) : R1(r1), R2(r2) {}
|
RListCommands(const QString &r1, const QString &r2) : R1(r1), R2(r2) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Запись лога
|
|
||||||
struct LogEntry {
|
|
||||||
QDateTime time;
|
|
||||||
QString module;
|
|
||||||
QString method;
|
|
||||||
QString message;
|
|
||||||
int code;
|
|
||||||
QString msgType;
|
|
||||||
QColor color;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// ПУБЛИЧНЫЕ ЧЛЕНЫ КЛАССА
|
// ПУБЛИЧНЫЕ ЧЛЕНЫ КЛАССА
|
||||||
@@ -95,7 +86,7 @@ public:
|
|||||||
TTwAPI *twitchAPI; // API для работы с Twitch
|
TTwAPI *twitchAPI; // API для работы с Twitch
|
||||||
|
|
||||||
// Методы логирования и работы с командами
|
// Методы логирования и работы с командами
|
||||||
void toLog(QString aModule, QString aMethod, QString aMessage, int aCode);
|
|
||||||
void toCommands(QString command, QString response);
|
void toCommands(QString command, QString response);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@@ -128,7 +119,6 @@ private slots:
|
|||||||
void loadSettings(); // Загрузка настроек
|
void loadSettings(); // Загрузка настроек
|
||||||
void on_btnImportSettings_clicked(); // Импорт настроек
|
void on_btnImportSettings_clicked(); // Импорт настроек
|
||||||
void on_btnExportSettings_clicked(); // Экспорт настроек
|
void on_btnExportSettings_clicked(); // Экспорт настроек
|
||||||
void on_btnSaveSettings_clicked(); // Сохранение настроек
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// СЛОТЫ ДЛЯ РАБОТЫ С УВЕДОМЛЕНИЯМИ
|
// СЛОТЫ ДЛЯ РАБОТЫ С УВЕДОМЛЕНИЯМИ
|
||||||
@@ -159,12 +149,7 @@ private slots:
|
|||||||
// СЛОТЫ ДЛЯ РАБОТЫ С КОМАНДАМИ И ОТВЕТАМИ
|
// СЛОТЫ ДЛЯ РАБОТЫ С КОМАНДАМИ И ОТВЕТАМИ
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
void execCommand(const QString &sender, const QString &message);
|
void execCommand(const QString &sender, const QString &message);
|
||||||
QString ResponsParserStatic(const QString &inMess, const QString &adName, const QString &aCommandText);
|
|
||||||
QString ResponsParserRandoms(const QString &inMess);
|
|
||||||
QString ResponsParserSounds(const QString &inMess);
|
|
||||||
QString ResponsParserText(const QString &inMess);
|
|
||||||
QString ResponsParserAI(const QString &response, const QString &pall);
|
|
||||||
QString responsParserAPI(const QString &inMess, const QString &adName);
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// СЛОТЫ ДЛЯ РАБОТЫ С ИСКУССТВЕННЫМ ИНТЕЛЛЕКТОМ
|
// СЛОТЫ ДЛЯ РАБОТЫ С ИСКУССТВЕННЫМ ИНТЕЛЛЕКТОМ
|
||||||
@@ -199,9 +184,13 @@ private slots:
|
|||||||
// ========================================================================
|
// ========================================================================
|
||||||
// СЛОТЫ ДЛЯ РАБОТЫ С ЛОГАМИ
|
// СЛОТЫ ДЛЯ РАБОТЫ С ЛОГАМИ
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
void applyLogFilter();
|
void onLogEntryAdded(const LogEntry& entry);
|
||||||
|
void onLogCleared();
|
||||||
|
|
||||||
|
// Добавляем методы для фильтрации
|
||||||
void addLogToTable(const LogEntry &entry, int row);
|
void addLogToTable(const LogEntry &entry, int row);
|
||||||
bool shouldShowLogEntry(int code);
|
void applyLogFilter();
|
||||||
|
bool shouldShowLogEntry(LogLevel level); // Изменяем тип параметра
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// СЛОТЫ ДЛЯ РАБОТЫ С ТАБЛИЦАМИ И ГРИДАМИ
|
// СЛОТЫ ДЛЯ РАБОТЫ С ТАБЛИЦАМИ И ГРИДАМИ
|
||||||
@@ -305,8 +294,6 @@ private slots:
|
|||||||
|
|
||||||
void on_edtDACode_editingFinished();
|
void on_edtDACode_editingFinished();
|
||||||
|
|
||||||
void on_cbDAAutoLogin_checkStateChanged(const Qt::CheckState &arg1);
|
|
||||||
|
|
||||||
void on_cbDAAutoLogin_stateChanged(int arg1);
|
void on_cbDAAutoLogin_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_edtGPTPrefix_editingFinished();
|
void on_edtGPTPrefix_editingFinished();
|
||||||
@@ -359,6 +346,7 @@ private slots:
|
|||||||
|
|
||||||
void on_btnThemesFolder_clicked();
|
void on_btnThemesFolder_clicked();
|
||||||
|
|
||||||
|
void onNotifyServerUpdated(HttpServer *server, const QString &name);
|
||||||
public slots:
|
public slots:
|
||||||
// Установка статуса подключения к Twitch
|
// Установка статуса подключения к Twitch
|
||||||
void setTwitchConnected(bool connected);
|
void setTwitchConnected(bool connected);
|
||||||
@@ -372,15 +360,17 @@ private:
|
|||||||
TAuth *m_authBot; // Авторизация бота
|
TAuth *m_authBot; // Авторизация бота
|
||||||
TAuth *m_authStreamer; // Авторизация стримера
|
TAuth *m_authStreamer; // Авторизация стримера
|
||||||
TAuth *m_authDA; // Авторизация DA
|
TAuth *m_authDA; // Авторизация DA
|
||||||
|
RandomResponses *m_randomResponses;
|
||||||
uLink *fLinkForm; // Форма ссылок
|
uLink *fLinkForm; // Форма ссылок
|
||||||
TTTVAuth *TTVAuth; // Данные авторизации Twitch
|
TTTVAuth *TTVAuth; // Данные авторизации Twitch
|
||||||
UserManager *m_userManager; // Менеджер пользователей
|
UserManager *m_userManager; // Менеджер пользователей
|
||||||
QList<LogEntry> allLogs; // Все записи логов
|
CommandProcessor* m_commandProcessor; // Процессор команд
|
||||||
|
|
||||||
WebSocketClient *m_twitchClient; // WebSocket клиент для Twitch
|
WebSocketClient *m_twitchClient; // WebSocket клиент для Twitch
|
||||||
UserWidget* m_userWidget; // Виджет пользователя
|
UserWidget* m_userWidget; // Виджет пользователя
|
||||||
NeuralNetworkManager *nnManager; // Менеджер нейронных сетей
|
NeuralNetworkManager *nnManager; // Менеджер нейронных сетей
|
||||||
|
RandomManager *m_randomManager;
|
||||||
|
MediaFileManager *m_SoundFiles;
|
||||||
|
MediaFileManager *m_TextFiles;
|
||||||
|
|
||||||
QList<TimerInfo> m_timers; // Список таймеров
|
QList<TimerInfo> m_timers; // Список таймеров
|
||||||
int m_nextTimerId = 1; // Следующий ID таймера
|
int m_nextTimerId = 1; // Следующий ID таймера
|
||||||
@@ -430,7 +420,7 @@ private:
|
|||||||
// Метод для применения QSS темы
|
// Метод для применения QSS темы
|
||||||
void applyStyleSheet(const QString &filename);
|
void applyStyleSheet(const QString &filename);
|
||||||
|
|
||||||
QString getUserFilePath(const QString &type, const QString &fileName) const;
|
|
||||||
QString getUserDataPath() const;
|
QString getUserDataPath() const;
|
||||||
QString getSystemPath() const;
|
QString getSystemPath() const;
|
||||||
void setupButtonIcons();
|
void setupButtonIcons();
|
||||||
@@ -455,7 +445,10 @@ private:
|
|||||||
// Воспроизведение уведомлений
|
// Воспроизведение уведомлений
|
||||||
void playNotify(const bool &isMod, const bool &isVip, const bool &isSub);
|
void playNotify(const bool &isMod, const bool &isVip, const bool &isSub);
|
||||||
|
|
||||||
|
void loadCommandsFromTableWidget();
|
||||||
|
void loadRandomRangesFromTableWidget();
|
||||||
|
void loadRandomResponseGroupsFromDatabase();
|
||||||
|
void loadSoundsFromDatabase();
|
||||||
// Инициализация базы данных
|
// Инициализация базы данных
|
||||||
void initializeDatabase();
|
void initializeDatabase();
|
||||||
// Настройка пользовательского интерфейса
|
// Настройка пользовательского интерфейса
|
||||||
@@ -476,10 +469,19 @@ private:
|
|||||||
void loadEmoties();
|
void loadEmoties();
|
||||||
void loadChatBadges();
|
void loadChatBadges();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Инициализирует звуковые уведомления
|
||||||
|
*/
|
||||||
|
void initializeNotificationSounds();
|
||||||
|
|
||||||
// Обработка эмодзи
|
// Обработка эмодзи
|
||||||
void processEmotes(QString &message);
|
void processEmotes(QString &message);
|
||||||
|
|
||||||
void loadSavedChats();
|
void loadSavedChats();
|
||||||
|
void loadSavedNotifications();
|
||||||
|
|
||||||
|
void processUserCommand(const QString &username, const QString &commandText);
|
||||||
|
void sendChatResponse(const QString &response);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UGENERAL_H
|
#endif // UGENERAL_H
|
||||||
|
|||||||
+21
-4
@@ -6,6 +6,7 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
UserManager::UserManager(QObject *parent)
|
UserManager::UserManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
@@ -295,16 +296,32 @@ QVector<User*> UserManager::findUsersByLogin(const QString &login)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
User* UserManager::getUserByIndex(int index)
|
User* UserManager::getUserByIndex(const QString& userID)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < m_users.size()) {
|
auto it = m_users.find(userID);
|
||||||
auto it = m_users.begin();
|
if (it != m_users.end()) {
|
||||||
std::advance(it, index);
|
|
||||||
return &it.value();
|
return &it.value();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const User* UserManager::getRandomUser() const {
|
||||||
|
if (m_users.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> keys = m_users.keys();
|
||||||
|
int randomIndex = QRandomGenerator::global()->bounded(keys.size());
|
||||||
|
|
||||||
|
// Возвращаем указатель на существующий объект в контейнере
|
||||||
|
const auto it = m_users.constFind(keys[randomIndex]);
|
||||||
|
if (it != m_users.constEnd()) {
|
||||||
|
return &(it.value()); // Указатель на существующий объект
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const QMap<QString, User>& UserManager::getAllUsers() const
|
const QMap<QString, User>& UserManager::getAllUsers() const
|
||||||
{
|
{
|
||||||
return m_users;
|
return m_users;
|
||||||
|
|||||||
+2
-2
@@ -38,7 +38,7 @@ public:
|
|||||||
QVector<User*> getVIPs() const;
|
QVector<User*> getVIPs() const;
|
||||||
QVector<User*> getSubscribers() const;
|
QVector<User*> getSubscribers() const;
|
||||||
QVector<User*> getActiveUsers(int minutes = 10) const;
|
QVector<User*> getActiveUsers(int minutes = 10) const;
|
||||||
|
const User* getRandomUser() const;
|
||||||
// Статистика
|
// Статистика
|
||||||
int getUserCount() const;
|
int getUserCount() const;
|
||||||
int getMessageCount() const;
|
int getMessageCount() const;
|
||||||
@@ -48,7 +48,7 @@ public:
|
|||||||
bool loadFromFile(const QString &filename);
|
bool loadFromFile(const QString &filename);
|
||||||
|
|
||||||
// Геттеры
|
// Геттеры
|
||||||
User* getUserByIndex(int index);
|
User* getUserByIndex(const QString &userID);
|
||||||
const QMap<QString, User>& getAllUsers() const;
|
const QMap<QString, User>& getAllUsers() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|||||||
+66
-6
@@ -10,6 +10,17 @@ HttpServer::HttpServer(QObject *parent)
|
|||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_server(nullptr)
|
, m_server(nullptr)
|
||||||
, m_pageBackgroundColor("transparent")
|
, m_pageBackgroundColor("transparent")
|
||||||
|
, m_blockColor("#FFFFFF")
|
||||||
|
, m_borderColor("#000000")
|
||||||
|
, m_borderSize(2)
|
||||||
|
, m_transparency(0)
|
||||||
|
, m_titleFamily("Arial")
|
||||||
|
, m_titleSize(20)
|
||||||
|
, m_titleColor("#FFFFFF")
|
||||||
|
, m_contentFamily("Arial")
|
||||||
|
, m_contentSize(16)
|
||||||
|
, m_contentColor("#F5F5F5")
|
||||||
|
, m_duration(10)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +89,6 @@ void HttpServer::addNotification(const Notification ¬ification)
|
|||||||
|
|
||||||
m_notifications.append(notif);
|
m_notifications.append(notif);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Обновляем всех подключенных клиентов
|
// Обновляем всех подключенных клиентов
|
||||||
for (auto client : m_clients) {
|
for (auto client : m_clients) {
|
||||||
if (client->state() == QAbstractSocket::ConnectedState) {
|
if (client->state() == QAbstractSocket::ConnectedState) {
|
||||||
@@ -236,7 +245,7 @@ QString HttpServer::generateHTML()
|
|||||||
"<meta charset='UTF-8'>\n"
|
"<meta charset='UTF-8'>\n"
|
||||||
"<title>Web Notifications</title>\n"
|
"<title>Web Notifications</title>\n"
|
||||||
"<style>\n"
|
"<style>\n"
|
||||||
"body { background: %1; margin: 0; padding: 0; }\n" // Используем цвет фона страницы
|
"body { background: %1; margin: 0; padding: 0; }\n" // Используем цвет фона страницы
|
||||||
"#messages { position: fixed; top: 20px; right: 20px; width: 400px; }\n"
|
"#messages { position: fixed; top: 20px; right: 20px; width: 400px; }\n"
|
||||||
".notification { \n"
|
".notification { \n"
|
||||||
" margin: 10px 0; \n"
|
" margin: 10px 0; \n"
|
||||||
@@ -257,9 +266,9 @@ QString HttpServer::generateHTML()
|
|||||||
" from { opacity: 0; transform: translateY(-20px); }\n"
|
" from { opacity: 0; transform: translateY(-20px); }\n"
|
||||||
" to { opacity: 1; transform: translateY(0); }\n"
|
" to { opacity: 1; transform: translateY(0); }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"</style>\n"
|
"</style>\n"
|
||||||
"</head>\n"
|
"</head>\n"
|
||||||
"<body>\n"
|
"<body>\n"
|
||||||
"<div id='messages'></div>\n"
|
"<div id='messages'></div>\n"
|
||||||
"<script>\n"
|
"<script>\n"
|
||||||
"let notifications = new Map(); // Храним активные уведомления по timestamp\n"
|
"let notifications = new Map(); // Храним активные уведомления по timestamp\n"
|
||||||
@@ -505,3 +514,54 @@ void HttpServer::discardClient()
|
|||||||
socket->deleteLater();
|
socket->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HttpServer::setBlockColor(const QString &color) { m_blockColor = color; }
|
||||||
|
QString HttpServer::getBlockColor() const { return m_blockColor; }
|
||||||
|
|
||||||
|
void HttpServer::setBorderColor(const QString &color) { m_borderColor = color; }
|
||||||
|
QString HttpServer::getBorderColor() const { return m_borderColor; }
|
||||||
|
|
||||||
|
void HttpServer::setBorderSize(int size) { m_borderSize = size; }
|
||||||
|
int HttpServer::getBorderSize() const { return m_borderSize; }
|
||||||
|
|
||||||
|
void HttpServer::setTransparency(int transparency) { m_transparency = transparency; }
|
||||||
|
int HttpServer::getTransparency() const { return m_transparency; }
|
||||||
|
|
||||||
|
void HttpServer::setPageBackgroundColor(const QString &color) { m_pageBackgroundColor = color; }
|
||||||
|
QString HttpServer::getPageBackgroundColor() const { return m_pageBackgroundColor; }
|
||||||
|
|
||||||
|
QString HttpServer::getTitleFamily() const { return m_titleFamily; }
|
||||||
|
int HttpServer::getTitleSize() const { return m_titleSize; }
|
||||||
|
QString HttpServer::getTitleColor() const { return m_titleColor; }
|
||||||
|
QString HttpServer::getContentFamily() const { return m_contentFamily; }
|
||||||
|
int HttpServer::getContentSize() const { return m_contentSize; }
|
||||||
|
QString HttpServer::getContentColor() const { return m_contentColor; }
|
||||||
|
|
||||||
|
void HttpServer::setTitleFont(const QString &family, int size, const QString &color) {
|
||||||
|
m_titleFamily = family;
|
||||||
|
m_titleSize = size;
|
||||||
|
m_titleColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::getTitleFont(QString &family, int &size, QString &color) const {
|
||||||
|
family = m_titleFamily;
|
||||||
|
size = m_titleSize;
|
||||||
|
color = m_titleColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::setContentFont(const QString &family, int size, const QString &color) {
|
||||||
|
m_contentFamily = family;
|
||||||
|
m_contentSize = size;
|
||||||
|
m_contentColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::getContentFont(QString &family, int &size, QString &color) const {
|
||||||
|
family = m_contentFamily;
|
||||||
|
size = m_contentSize;
|
||||||
|
color = m_contentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::setDuration(int duration) { m_duration = duration; }
|
||||||
|
int HttpServer::getDuration() const { return m_duration; }
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,33 @@ public:
|
|||||||
void addNotification(const Notification ¬ification);
|
void addNotification(const Notification ¬ification);
|
||||||
void clearNotifications();
|
void clearNotifications();
|
||||||
|
|
||||||
|
// Методы для доступа к настройкам (нужно реализовать хранение)
|
||||||
|
void setBlockColor(const QString &color);
|
||||||
|
QString getBlockColor() const;
|
||||||
|
void setBorderColor(const QString &color);
|
||||||
|
QString getBorderColor() const;
|
||||||
|
void setBorderSize(int size);
|
||||||
|
int getBorderSize() const;
|
||||||
|
void setTransparency(int transparency);
|
||||||
|
int getTransparency() const;
|
||||||
|
void setPageBackgroundColor(const QString &color);
|
||||||
|
QString getPageBackgroundColor() const;
|
||||||
|
void setTitleFont(const QString &family, int size, const QString &color);
|
||||||
|
void getTitleFont(QString &family, int &size, QString &color) const;
|
||||||
|
void setContentFont(const QString &family, int size, const QString &color);
|
||||||
|
void getContentFont(QString &family, int &size, QString &color) const;
|
||||||
|
void setDuration(int duration);
|
||||||
|
int getDuration() const;
|
||||||
|
|
||||||
|
|
||||||
|
QString getTitleFamily() const;
|
||||||
|
int getTitleSize() const;
|
||||||
|
QString getTitleColor() const;
|
||||||
|
QString getContentFamily() const;
|
||||||
|
int getContentSize() const;
|
||||||
|
QString getContentColor() const;
|
||||||
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void serverStarted(bool success);
|
void serverStarted(bool success);
|
||||||
|
|
||||||
@@ -71,6 +98,19 @@ private:
|
|||||||
QList<Notification> m_notifications;
|
QList<Notification> m_notifications;
|
||||||
QString m_pageBackgroundColor; // Цвет фона страницы
|
QString m_pageBackgroundColor; // Цвет фона страницы
|
||||||
|
|
||||||
|
|
||||||
|
QString m_blockColor;
|
||||||
|
QString m_borderColor;
|
||||||
|
int m_borderSize;
|
||||||
|
int m_transparency;
|
||||||
|
QString m_titleFamily;
|
||||||
|
int m_titleSize;
|
||||||
|
QString m_titleColor;
|
||||||
|
QString m_contentFamily;
|
||||||
|
int m_contentSize;
|
||||||
|
QString m_contentColor;
|
||||||
|
int m_duration;
|
||||||
|
|
||||||
void processRequest(QTcpSocket *socket, const QString &request);
|
void processRequest(QTcpSocket *socket, const QString &request);
|
||||||
void sendResponse(QTcpSocket *socket, const QString &contentType, const QString &content, int statusCode = 200);
|
void sendResponse(QTcpSocket *socket, const QString &contentType, const QString &content, int statusCode = 200);
|
||||||
void sendHtmlPage(QTcpSocket *socket);
|
void sendHtmlPage(QTcpSocket *socket);
|
||||||
|
|||||||
Reference in New Issue
Block a user