using System; using AAIntegration.SimmonsBank.API.Configs; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using AAIntegration.SimmonsBank.API.Processes; using AAIntegration.SimmonsBank.API.Services; using AAIntegration.SimmonsBank.API.Entities; using Microsoft.Extensions.Caching.Memory; namespace AAIntegration.SimmonsBank.API.Workers; public class PuppeteerWorker : BackgroundService { private readonly PuppeteerConfig _config; private readonly IPuppeteerProcess _puppeteerProcess; private readonly ILogger _logger; private readonly IMemoryCache _memoryCache; private readonly IServiceScopeFactory _serviceScopeFactory; public PuppeteerWorker( IOptions config, IPuppeteerProcess puppeteerProcess, ILogger logger, IMemoryCache memoryCache, IServiceScopeFactory serviceScopeFactory) { _config = config.Value; _puppeteerProcess = puppeteerProcess; _logger = logger; _memoryCache = memoryCache; _serviceScopeFactory = serviceScopeFactory; } private IUserService _userService; private IPuppeteerService _puppeteerService; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { DateTime? lastExecutedOn = null; bool operationOccurred = false; using (var scope = _serviceScopeFactory.CreateScope()) { _userService = scope.ServiceProvider.GetService(); _puppeteerService = scope.ServiceProvider.GetService(); _puppeteerProcess.SetStoppingToken(stoppingToken); _puppeteerProcess.SetService(_puppeteerService); // This is how we keep the app running (in the background) while (!stoppingToken.IsCancellationRequested) { // Keep Alive processing operationOccurred = await ProcessUsersKeepalive(); _logger.LogInformation($"Operation occurred? {operationOccurred}"); // If no operation occurred, waits before looping again if (lastExecutedOn != null && operationOccurred == false) { await Task.Delay(TimeSpan.FromMinutes(_config.TaskCheckIntervalMinutes), stoppingToken); } lastExecutedOn = DateTime.UtcNow; _logger.LogInformation("PuppeteerWorker executed at: {time}", DateTimeOffset.Now); // TEMPORARY DON'T COMMIT!!!! await Task.Delay(TimeSpan.FromMinutes(120), stoppingToken); } } } private async Task ProcessUsersKeepalive() { bool operationOccurred = false; foreach (User user in _userService.GetAll().ToList()) { int ka = _config.KeepAliveIntervalMinutes; int uka = (int)DateTime.UtcNow.Subtract(GetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id)).TotalMinutes; //_logger.LogInformation($"KeepAlive configured to {ka}. This user hasn't been kept alive in {uka} minute(s)."); if (_config.KeepAliveIntervalMinutes <= (int)DateTime.UtcNow.Subtract(GetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id)).TotalMinutes) { await _puppeteerProcess.StayLoggedIn(user); SetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id, DateTime.UtcNow); operationOccurred = true; _logger.LogInformation($"Operation set to true because of user {user.Id}"); } } return operationOccurred; } private DateTime GetUserLastExecution(string cacheKey, int userId) { var internalKeys = GetLastExecutionCachedDictionary(cacheKey); if (!internalKeys.TryGetValue(userId, out var lastExecution)) { _logger.LogInformation($"Could not find userId in '{cacheKey}' cache. Returning '{DateTime.MinValue}'."); return DateTime.MinValue; } _logger.LogInformation($"Just got user {userId} from cache with value {lastExecution}"); return lastExecution; } private void SetUserLastExecution(string cacheKey, int userId, DateTime lastExecution) { var internalKeys = GetLastExecutionCachedDictionary(cacheKey); if (internalKeys.ContainsKey(userId)) internalKeys[userId] = lastExecution; else internalKeys.Add(userId, lastExecution); _memoryCache.Set(cacheKey, internalKeys); _logger.LogInformation($"Just set user {userId} into cache with value {lastExecution}"); } private Dictionary GetLastExecutionCachedDictionary(string cacheKey) { // Sets cache if no dictionary is found if (!_memoryCache.TryGetValue>(cacheKey, out var internalKeys)) { internalKeys = _userService.GetAll() .ToDictionary(u => u.Id, u => DateTime.MinValue); _memoryCache.Set(cacheKey, internalKeys); _logger.LogInformation($"Updated users in '{cacheKey}' cache with new id list."); } return internalKeys; } private void PrintCacheValues(string cacheKey) { _logger.LogInformation($"Printing cache values from '{cacheKey}'"); Dictionary internalKeys = GetLastExecutionCachedDictionary(cacheKey); foreach (KeyValuePair entry in internalKeys) { _logger.LogInformation($" {entry.Key} <=> {entry.Value}"); } } }