334 lines
8.6 KiB
C++
334 lines
8.6 KiB
C++
#include "user_manager.h"
|
||
#include "twitchmessage.h"
|
||
#include <QFile>
|
||
#include <QJsonDocument>
|
||
#include <QJsonArray>
|
||
#include <QJsonObject>
|
||
#include <QDateTime>
|
||
#include <QDebug>
|
||
#include <QRandomGenerator>
|
||
|
||
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<User*> UserManager::getModerators() const
|
||
{
|
||
QVector<User*> moderators;
|
||
|
||
for (auto it = m_users.begin(); it != m_users.end(); ++it) {
|
||
if (it.value().isModerator) {
|
||
moderators.append(const_cast<User*>(&it.value()));
|
||
}
|
||
}
|
||
|
||
return moderators;
|
||
}
|
||
|
||
QVector<User*> UserManager::getVIPs() const
|
||
{
|
||
QVector<User*> vips;
|
||
|
||
for (auto it = m_users.begin(); it != m_users.end(); ++it) {
|
||
if (it.value().isVIP) {
|
||
vips.append(const_cast<User*>(&it.value()));
|
||
}
|
||
}
|
||
|
||
return vips;
|
||
}
|
||
|
||
QVector<User*> UserManager::getSubscribers() const
|
||
{
|
||
QVector<User*> subscribers;
|
||
|
||
for (auto it = m_users.begin(); it != m_users.end(); ++it) {
|
||
if (it.value().isSubscriber) {
|
||
subscribers.append(const_cast<User*>(&it.value()));
|
||
}
|
||
}
|
||
|
||
return subscribers;
|
||
}
|
||
|
||
QVector<User*> UserManager::getActiveUsers(int minutes) const
|
||
{
|
||
QVector<User*> 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<User*>(&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<QString> 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<User*> UserManager::findUsersByLogin(const QString &login)
|
||
{
|
||
QVector<User*> 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<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
|
||
{
|
||
return m_users;
|
||
}
|
||
|
||
QString UserManager::generateUserId() const
|
||
{
|
||
return QString("user_%1").arg(QDateTime::currentMSecsSinceEpoch());
|
||
}
|