#include "user_manager.h" #include "twitchmessage.h" #include #include #include #include #include #include #include UserManager::UserManager(QObject *parent) : QObject(parent) , m_totalMessages(0) , m_maxUsers(10000) , m_cleanupInterval(60) , m_lastCleanup(QDateTime::currentDateTime()) { } UserManager::~UserManager() { clear(); } QString UserManager::checkUser(const QString &displayName, const TwitchMessage &msg) { QString normalizedDisplayName = displayName.trimmed(); // Проверяем, есть ли пользователь if (m_users.contains(normalizedDisplayName)) { User &user = m_users[normalizedDisplayName]; // Обновляем из сообщения, если передано if (!msg.displayName.isEmpty()) { updateUserFromMessage(normalizedDisplayName, msg); } emit userMessageCountChanged(user.id, user.messageCount); emit userUpdated(user); return user.id; } // Пользователя нет - создаем нового User newUser; if (!msg.displayName.isEmpty()) { // Заполняем из сообщения newUser.updateFromTwitchMessage(msg); } else { // Минимальные данные newUser.displayName = normalizedDisplayName; newUser.login = normalizedDisplayName.toLower(); newUser.id = generateUserId(); } // Добавляем в базу addUser(newUser); return newUser.id; } User* UserManager::findUser(const QString &displayName) { if (m_users.contains(displayName)) { return &m_users[displayName]; } // Пробуем найти по login if (m_loginToDisplayName.contains(displayName.toLower())) { QString actualDisplayName = m_loginToDisplayName[displayName.toLower()]; return &m_users[actualDisplayName]; } return nullptr; } User* UserManager::findUserById(const QString &userId) { if (m_userIdToDisplayName.contains(userId)) { QString displayName = m_userIdToDisplayName[userId]; return &m_users[displayName]; } return nullptr; } void UserManager::updateUserFromMessage(const QString &displayName, const TwitchMessage &msg) { if (!m_users.contains(displayName)) { return; } User &user = m_users[displayName]; // ВАЖНО: передаем displayName как параметр, а не берем из msg // если displayName из параметра отличается от msg.displayName, // нам нужно обновить ключ в map QString newDisplayName = msg.displayName; if (!newDisplayName.isEmpty() && newDisplayName != displayName) { // Display name изменился - нужно переместить пользователя User userCopy = user; userCopy.updateFromTwitchMessage(msg); // Удаляем старую запись removeUser(displayName); // Добавляем с новым displayName userCopy.displayName = newDisplayName; addUser(userCopy); return; } // Display name не изменился - просто обновляем user.updateFromTwitchMessage(msg); // Обновляем индексы updateIndexes(displayName, user); emit userUpdated(user); } void UserManager::updateUserStatus(const QString &userId, bool isMod, bool isVIP, bool isSubscriber) { User* user = findUserById(userId); if (user) { user->isModerator = isMod; user->isVIP = isVIP; user->isSubscriber = isSubscriber; emit userUpdated(*user); } } void UserManager::addUser(const User &user) { QString displayName = user.displayName; if (displayName.isEmpty()) { qWarning() << "Cannot add user with empty display name"; return; } // Очистка старых пользователей, если нужно if (m_users.size() >= m_maxUsers) { cleanupOldUsers(); } m_users[displayName] = user; updateIndexes(displayName, user); emit userAdded(user); } void UserManager::removeUser(const QString &displayName) { if (m_users.contains(displayName)) { User user = m_users[displayName]; // Удаляем из индексов m_userIdToDisplayName.remove(user.id); m_loginToDisplayName.remove(user.login.toLower()); // Удаляем из основного списка m_users.remove(displayName); emit userRemoved(user.id, displayName); // Передаем и id, и displayName } } void UserManager::clear() { m_users.clear(); m_userIdToDisplayName.clear(); m_loginToDisplayName.clear(); m_totalMessages = 0; } QVector UserManager::getModerators() const { QVector moderators; for (auto it = m_users.begin(); it != m_users.end(); ++it) { if (it.value().isModerator) { moderators.append(const_cast(&it.value())); } } return moderators; } QVector UserManager::getVIPs() const { QVector vips; for (auto it = m_users.begin(); it != m_users.end(); ++it) { if (it.value().isVIP) { vips.append(const_cast(&it.value())); } } return vips; } QVector UserManager::getSubscribers() const { QVector subscribers; for (auto it = m_users.begin(); it != m_users.end(); ++it) { if (it.value().isSubscriber) { subscribers.append(const_cast(&it.value())); } } return subscribers; } QVector UserManager::getActiveUsers(int minutes) const { QVector activeUsers; QDateTime cutoff = QDateTime::currentDateTime().addSecs(-minutes * 60); for (auto it = m_users.begin(); it != m_users.end(); ++it) { if (it.value().lastMessageTime >= cutoff) { activeUsers.append(const_cast(&it.value())); } } return activeUsers; } void UserManager::updateIndexes(const QString &displayName, const User &user) { if (!user.id.isEmpty()) { m_userIdToDisplayName[user.id] = displayName; } if (!user.login.isEmpty()) { m_loginToDisplayName[user.login.toLower()] = displayName; } } void UserManager::cleanupOldUsers() { QDateTime cutoff = QDateTime::currentDateTime().addDays(-7); // Удаляем неактивных неделю QVector usersToRemove; for (auto it = m_users.begin(); it != m_users.end(); ++it) { // Удаляем если: // 1. Не писал больше недели // 2. Не модератор/VIP/подписчик if (it.value().lastMessageTime < cutoff && !it.value().isModerator && !it.value().isVIP && !it.value().isSubscriber && it.value().messageCount < 5) { // И написано меньше 5 сообщений usersToRemove.append(it.key()); } } for (const QString &displayName : usersToRemove) { removeUser(displayName); } m_lastCleanup = QDateTime::currentDateTime(); } int UserManager::getUserCount() const { return m_users.size(); } int UserManager::getMessageCount() const { return m_totalMessages; } QVector UserManager::findUsersByLogin(const QString &login) { QVector result; QString lowerLogin = login.toLower(); for (auto it = m_users.begin(); it != m_users.end(); ++it) { if (it.value().login.toLower().contains(lowerLogin)) { result.append(&it.value()); } } return result; } User* UserManager::getUserByIndex(const QString& userID) { auto it = m_users.find(userID); if (it != m_users.end()) { return &it.value(); } return nullptr; } const User* UserManager::getRandomUser() const { if (m_users.isEmpty()) { return nullptr; } QList 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& UserManager::getAllUsers() const { return m_users; } QString UserManager::generateUserId() const { return QString("user_%1").arg(QDateTime::currentMSecsSinceEpoch()); }