C# Discord bot made using NetCord for keeping a TikTok-style streak
1using Microsoft.EntityFrameworkCore;
2using Microsoft.Extensions.DependencyInjection;
3using Microsoft.Extensions.Hosting;
4using Microsoft.Extensions.Logging;
5using StreakBot.Data;
6
7namespace StreakBot.Services;
8
9public class MonthlyRestoreBackgroundService : BackgroundService
10{
11 private readonly IServiceScopeFactory _scopeFactory;
12 private readonly ILogger<MonthlyRestoreBackgroundService> _logger;
13 private readonly TimeSpan _checkInterval = TimeSpan.FromHours(24);
14 private int _lastResetMonth = -1;
15
16 public MonthlyRestoreBackgroundService(
17 IServiceScopeFactory scopeFactory,
18 ILogger<MonthlyRestoreBackgroundService> logger)
19 {
20 _scopeFactory = scopeFactory;
21 _logger = logger;
22 }
23
24 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
25 {
26 while (!stoppingToken.IsCancellationRequested)
27 {
28 try
29 {
30 if (_lastResetMonth == -1)
31 {
32 var now = DateTime.UtcNow;
33
34 _lastResetMonth = now.Month;
35 }
36 else
37 {
38 await CheckAndResetMonthlyRestoresAsync();
39 }
40 }
41 catch (Exception ex)
42 {
43 _logger.LogError(ex, "Error checking monthly restore reset");
44 }
45
46 await Task.Delay(_checkInterval, stoppingToken);
47 }
48 }
49
50 private async Task CheckAndResetMonthlyRestoresAsync()
51 {
52 var now = DateTime.UtcNow;
53
54 var currentMonth = now.Month;
55
56 // Only reset once per month (when the month changes)
57 if (_lastResetMonth != currentMonth)
58 {
59 using var scope = _scopeFactory.CreateScope();
60 var context = scope.ServiceProvider.GetRequiredService<StreakDbContext>();
61
62 var streaks = await context.Streaks.ToListAsync();
63
64 foreach (var streak in streaks)
65 {
66 streak.MonthlyRestores = 5;
67 }
68
69 if (context.ChangeTracker.HasChanges())
70 {
71 var count = await context.SaveChangesAsync();
72 _logger.LogInformation("Reset monthly restores for {Count} streak(s)", count);
73 }
74
75 _lastResetMonth = currentMonth;
76 }
77 }
78}