317 lines
8.0 KiB
C++
317 lines
8.0 KiB
C++
#include "user_manager.h"
|
|
#include "twitchmessage.h"
|
|
#include <QFile>
|
|
#include <QJsonDocument>
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
#include <QDateTime>
|
|
#include <QDebug>
|
|
|
|
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(int index)
|
|
{
|
|
if (index >= 0 && index < m_users.size()) {
|
|
auto it = m_users.begin();
|
|
std::advance(it, index);
|
|
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());
|
|
}
|