Files
TTW_Bot/commandprocessor.cpp
T
2026-02-07 08:28:56 +03:00

477 lines
16 KiB
C++

#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 &parameters)
{
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];
}
}