#include "MonitoringModule.h" #include #include namespace aissia { MonitoringModule::MonitoringModule() { m_logger = spdlog::get("MonitoringModule"); if (!m_logger) { m_logger = spdlog::stdout_color_mt("MonitoringModule"); } m_config = std::make_unique("config"); } void MonitoringModule::setConfiguration(const grove::IDataNode& configNode, grove::IIO* io, grove::ITaskScheduler* scheduler) { m_io = io; m_config = std::make_unique("config"); m_pollIntervalMs = configNode.getInt("poll_interval_ms", 1000); m_idleThresholdSeconds = configNode.getInt("idle_threshold_seconds", 300); m_enabled = configNode.getBool("enabled", true); // Load productive apps list m_productiveApps.clear(); auto* prodNode = configNode.getChildReadOnly("productive_apps"); if (prodNode) { for (const auto& name : prodNode->getChildNames()) { m_productiveApps.insert(prodNode->getString(name, "")); } } // Default productive apps if (m_productiveApps.empty()) { m_productiveApps = {"Code", "code", "CLion", "clion", "Visual Studio", "devenv", "rider", "idea", "pycharm"}; } // Load distracting apps list m_distractingApps.clear(); auto* distNode = configNode.getChildReadOnly("distracting_apps"); if (distNode) { for (const auto& name : distNode->getChildNames()) { m_distractingApps.insert(distNode->getString(name, "")); } } if (m_distractingApps.empty()) { m_distractingApps = {"Discord", "discord", "Steam", "steam", "firefox", "chrome", "YouTube"}; } // Create window tracker m_tracker = WindowTrackerFactory::create(); m_logger->info("MonitoringModule configure: poll={}ms, idle={}s, platform={}", m_pollIntervalMs, m_idleThresholdSeconds, m_tracker ? m_tracker->getPlatformName() : "none"); } const grove::IDataNode& MonitoringModule::getConfiguration() { return *m_config; } void MonitoringModule::process(const grove::IDataNode& input) { if (!m_enabled || !m_tracker || !m_tracker->isAvailable()) return; float currentTime = input.getDouble("gameTime", 0.0); // Poll based on interval float pollIntervalSec = m_pollIntervalMs / 1000.0f; if (currentTime - m_lastPollTime < pollIntervalSec) return; m_lastPollTime = currentTime; checkCurrentApp(currentTime); checkIdleState(currentTime); } void MonitoringModule::checkCurrentApp(float currentTime) { std::string newApp = m_tracker->getCurrentAppName(); std::string newTitle = m_tracker->getCurrentWindowTitle(); if (newApp != m_currentApp) { // App changed int duration = static_cast(currentTime - m_appStartTime); if (!m_currentApp.empty() && duration > 0) { m_appDurations[m_currentApp] += duration; // Update productivity counters if (isProductiveApp(m_currentApp)) { m_totalProductiveSeconds += duration; } else if (isDistractingApp(m_currentApp)) { m_totalDistractingSeconds += duration; } publishAppChanged(m_currentApp, newApp, duration); } m_currentApp = newApp; m_currentWindowTitle = newTitle; m_appStartTime = currentTime; m_logger->debug("App: {} - {}", m_currentApp, m_currentWindowTitle.substr(0, 50)); } } void MonitoringModule::checkIdleState(float currentTime) { bool wasIdle = m_isIdle; m_isIdle = m_tracker->isUserIdle(m_idleThresholdSeconds); if (m_isIdle && !wasIdle) { m_logger->info("Utilisateur inactif ({}s)", m_idleThresholdSeconds); if (m_io) { auto event = std::make_unique("idle"); event->setString("type", "idle_detected"); event->setInt("idleSeconds", m_tracker->getIdleTimeSeconds()); m_io->publish("monitoring:idle_detected", std::move(event)); } } else if (!m_isIdle && wasIdle) { m_logger->info("Activite reprise"); if (m_io) { auto event = std::make_unique("active"); event->setString("type", "activity_resumed"); m_io->publish("monitoring:activity_resumed", std::move(event)); } } } bool MonitoringModule::isProductiveApp(const std::string& appName) const { // Check exact match if (m_productiveApps.count(appName)) return true; // Check if app name contains productive keyword std::string lowerApp = appName; std::transform(lowerApp.begin(), lowerApp.end(), lowerApp.begin(), ::tolower); for (const auto& prod : m_productiveApps) { std::string lowerProd = prod; std::transform(lowerProd.begin(), lowerProd.end(), lowerProd.begin(), ::tolower); if (lowerApp.find(lowerProd) != std::string::npos) return true; } return false; } bool MonitoringModule::isDistractingApp(const std::string& appName) const { if (m_distractingApps.count(appName)) return true; std::string lowerApp = appName; std::transform(lowerApp.begin(), lowerApp.end(), lowerApp.begin(), ::tolower); for (const auto& dist : m_distractingApps) { std::string lowerDist = dist; std::transform(lowerDist.begin(), lowerDist.end(), lowerDist.begin(), ::tolower); if (lowerApp.find(lowerDist) != std::string::npos) return true; } return false; } void MonitoringModule::publishAppChanged(const std::string& oldApp, const std::string& newApp, int duration) { if (!m_io) return; auto event = std::make_unique("app_changed"); event->setString("oldApp", oldApp); event->setString("newApp", newApp); event->setInt("duration", duration); event->setBool("wasProductive", isProductiveApp(oldApp)); event->setBool("wasDistracting", isDistractingApp(oldApp)); m_io->publish("monitoring:app_changed", std::move(event)); } std::unique_ptr MonitoringModule::getHealthStatus() { auto status = std::make_unique("status"); status->setString("status", m_enabled ? "running" : "disabled"); status->setString("currentApp", m_currentApp); status->setBool("isIdle", m_isIdle); status->setInt("totalProductiveSeconds", m_totalProductiveSeconds); status->setInt("totalDistractingSeconds", m_totalDistractingSeconds); status->setString("platform", m_tracker ? m_tracker->getPlatformName() : "none"); return status; } void MonitoringModule::shutdown() { m_logger->info("MonitoringModule arrete. Productif: {}s, Distrait: {}s", m_totalProductiveSeconds, m_totalDistractingSeconds); } std::unique_ptr MonitoringModule::getState() { auto state = std::make_unique("state"); state->setString("currentApp", m_currentApp); state->setDouble("appStartTime", m_appStartTime); state->setBool("isIdle", m_isIdle); state->setInt("totalProductiveSeconds", m_totalProductiveSeconds); state->setInt("totalDistractingSeconds", m_totalDistractingSeconds); return state; } void MonitoringModule::setState(const grove::IDataNode& state) { m_currentApp = state.getString("currentApp", ""); m_appStartTime = state.getDouble("appStartTime", 0.0); m_isIdle = state.getBool("isIdle", false); m_totalProductiveSeconds = state.getInt("totalProductiveSeconds", 0); m_totalDistractingSeconds = state.getInt("totalDistractingSeconds", 0); m_logger->info("Etat restore: app={}, productif={}s", m_currentApp, m_totalProductiveSeconds); } } // namespace aissia extern "C" { grove::IModule* createModule() { return new aissia::MonitoringModule(); } void destroyModule(grove::IModule* module) { delete module; } }