C# Discord bot made using NetCord for keeping a TikTok-style streak
at main 232 lines 7.4 kB view raw
1using Microsoft.EntityFrameworkCore; 2using NetCord.Services; 3using StreakBot.Data; 4using StreakBot.Data.Entities; 5 6namespace StreakBot.Services; 7 8public class StreakService 9{ 10 private readonly StreakDbContext _context; 11 private readonly ChannelService _channelService; 12 13 public StreakService(StreakDbContext context, ChannelService channelService) 14 { 15 _context = context; 16 _channelService = channelService; 17 } 18 19 public async Task ProcessMessageAsync(ulong userId, ulong serverId) 20 { 21 //Streaks are per server 22 var streak = await _context.Streaks 23 .Where(s => s.ServerId == serverId && 24 (s.User1Id == userId || s.User2Id == userId)) 25 .FirstOrDefaultAsync(); 26 27 if (streak == null) 28 { 29 return; 30 } 31 32 ProcessStreakMessage(streak, userId); 33 34 if (_context.ChangeTracker.HasChanges()) 35 { 36 await _context.SaveChangesAsync(); 37 38 if (streak.User1MessageSent && streak.User2MessageSent) 39 { 40 await _channelService.UpdateStreakChannelAsync(serverId, streak.StreakNumber); 41 } 42 } 43 } 44 45 public async Task ProcessDmMessageAsync(ulong userId, ulong channelId) 46 { 47 var streaks = await _context.Streaks 48 .Where(s => s.ServerId == 0 && 49 s.ChannelId == channelId && 50 (s.User1Id == userId || s.User2Id == userId)) 51 .ToListAsync(); 52 53 foreach (var streak in streaks) 54 { 55 ProcessStreakMessage(streak, userId); 56 } 57 58 if (_context.ChangeTracker.HasChanges()) 59 { 60 await _context.SaveChangesAsync(); 61 62 // Update DM message for each affected streak 63 foreach (var streak in streaks) 64 { 65 var channel = await _context.Channels 66 .FirstOrDefaultAsync(c => c.ChannelId == channelId && c.ChannelType == Data.Entities.ChannelType.DmChannel); 67 68 if (channel?.MessageId != null) 69 { 70 await _channelService.UpdateDmStreakMessageAsync(channelId, channel.MessageId.Value, streak.StreakNumber, streak.CreatedDate.TimeOfDay); 71 } 72 } 73 } 74 } 75 76 private static void ProcessStreakMessage(Streak streak, ulong userId) 77 { 78 bool wasUser1 = streak.User1Id == userId; 79 bool wasUser2 = streak.User2Id == userId; 80 81 bool previousUser1Sent = streak.User1MessageSent; 82 bool previousUser2Sent = streak.User2MessageSent; 83 84 if (wasUser1 && !streak.User1MessageSent) 85 { 86 streak.User1MessageSent = true; 87 } 88 else if (wasUser2 && !streak.User2MessageSent) 89 { 90 streak.User2MessageSent = true; 91 } 92 93 if (streak.User1MessageSent && streak.User2MessageSent && 94 !(previousUser1Sent && previousUser2Sent)) 95 { 96 streak.StreakNumber++; 97 } 98 } 99 100 public async Task<Streak?> CreateStreakAsync(ulong user1Id, ulong user2Id, ulong serverId, ulong channelId = 0) 101 { 102 var streaks = serverId == 0 103 ? await _context.Streaks 104 .Where(s => s.ChannelId == channelId && 105 (s.User1Id == user1Id || s.User2Id == user1Id)) 106 .ToListAsync() 107 : await _context.Streaks 108 .Where(s => s.ServerId == serverId && 109 (s.User1Id == user1Id || s.User2Id == user1Id)) 110 .ToListAsync(); 111 112 if (streaks.Any(c => 113 (c.User1Id == user1Id && c.User2Id == user2Id) || (c.User1Id == user2Id && c.User2Id == user1Id))) 114 { 115 return null; 116 } 117 118 var now = DateTime.UtcNow; 119 var streak = new Streak 120 { 121 CreatedDate = now, 122 User1Id = user1Id, 123 User2Id = user2Id, 124 ServerId = serverId, 125 ChannelId = channelId, 126 User1MessageSent = false, 127 User2MessageSent = false, 128 StreakNumber = 0, 129 LastResetCheck = now.AddDays(1) 130 }; 131 132 _context.Streaks.Add(streak); 133 await _context.SaveChangesAsync(); 134 135 if (serverId == 0) 136 { 137 await _channelService.CreateDmStreakMessageAsync(channelId, streak.StreakNumber, streak.CreatedDate.TimeOfDay); 138 } 139 else 140 { 141 await _channelService.UpdateStreakChannelAsync(serverId, streak.StreakNumber); 142 await _channelService.UpdateTimeChannelAsync(serverId, streak.CreatedDate.TimeOfDay); 143 } 144 145 return streak; 146 } 147 148 public async Task<List<Streak>> CheckStreaksAsync(ulong user1Id) 149 { 150 var streaks = await _context.Streaks 151 .Where(s => s.User1Id == user1Id || s.User2Id == user1Id) 152 .ToListAsync(); 153 154 return streaks; 155 } 156 157 public async Task<Streak?> GetStreakAsync(ulong user1Id, ulong user2Id) 158 { 159 return await _context.Streaks 160 .Where(s => (s.User1Id == user1Id && s.User2Id == user2Id) || (s.User1Id == user2Id && s.User2Id == user1Id)) 161 .FirstOrDefaultAsync(); 162 } 163 164 public async Task<string> RestoreStreakAsync(ulong user1Id, ulong user2Id) 165 { 166 var streak = await GetStreakAsync(user1Id, user2Id); 167 168 if (streak == null) 169 { 170 return $"No streak found between <@{user1Id}> and <@{user2Id}>."; 171 } 172 173 if (streak.MonthlyRestores <= 0) 174 { 175 return "No restores available this month. Restores reset at the start of each month."; 176 } 177 178 if (streak.StreakNumberToRestore == null || streak.StreakNumber >= streak.StreakNumberToRestore) 179 { 180 return "No broken streak to restore."; 181 } 182 183 var restoredNumber = streak.StreakNumberToRestore.Value; 184 if (streak.User1MessageSent && streak.User2MessageSent) 185 { 186 restoredNumber++; 187 } 188 streak.StreakNumber = restoredNumber; 189 streak.StreakNumberToRestore = null; 190 streak.MonthlyRestores--; 191 192 await _context.SaveChangesAsync(); 193 194 if (streak.ServerId != 0) 195 { 196 await _channelService.UpdateStreakChannelAsync(streak.ServerId, streak.StreakNumber, !(streak.User1MessageSent && streak.User2MessageSent)); 197 } 198 else 199 { 200 var channel = await _context.Channels 201 .FirstOrDefaultAsync(c => c.ChannelId == streak.ChannelId && c.ChannelType == ChannelType.DmChannel); 202 203 if (channel?.MessageId != null) 204 { 205 await _channelService.UpdateDmStreakMessageAsync(streak.ChannelId, channel.MessageId.Value, streak.StreakNumber, streak.CreatedDate.TimeOfDay); 206 } 207 } 208 209 return $"Streak restored to {restoredNumber}! You have {streak.MonthlyRestores} restore(s) remaining this month."; 210 } 211 212 public async Task<bool?> DeleteStreakAsync(ulong user1Id, ulong user2Id) 213 { 214 var streak = await GetStreakAsync(user1Id, user2Id); 215 216 if (streak == null) 217 { 218 return null; 219 } 220 221 var channels = await _context.Channels 222 .Where(c => c.ServerId == streak.ServerId) 223 .ToListAsync(); 224 225 foreach (var channel in channels) 226 { 227 await _channelService.DeleteChannelAsync(channel.ChannelId); 228 } 229 230 return true; 231 } 232}