C# Discord bot made using NetCord for keeping a TikTok-style streak
at main 195 lines 7.3 kB view raw
1using Microsoft.EntityFrameworkCore; 2using Microsoft.Extensions.Logging; 3using NetCord.Rest; 4using StreakBot.Data; 5using StreakBot.Data.Entities; 6using ChannelEntity = StreakBot.Data.Entities.Channel; 7using ChannelType = StreakBot.Data.Entities.ChannelType; 8 9namespace StreakBot.Services; 10 11public class ChannelService 12{ 13 private readonly StreakDbContext _context; 14 private readonly RestClient _restClient; 15 private readonly ILogger<ChannelService> _logger; 16 17 public ChannelService(StreakDbContext context, RestClient restClient, ILogger<ChannelService> logger) 18 { 19 _context = context; 20 _restClient = restClient; 21 _logger = logger; 22 } 23 24 public async Task UpdateStreakChannelAsync(ulong serverId, int streakCount, bool endOfDay = false) 25 { 26 var channel = await _context.Channels 27 .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ChannelType == ChannelType.StreakChannel); 28 29 var channelName = endOfDay ? $"🧊 {streakCount}" : $"🔥 {streakCount}"; 30 31 if (channel == null) 32 { 33 var properties = new GuildChannelProperties(channelName, NetCord.ChannelType.VoiceGuildChannel); 34 var createdChannel = await _restClient.CreateGuildChannelAsync(serverId, properties); 35 36 channel = new ChannelEntity 37 { 38 ServerId = serverId, 39 ChannelId = createdChannel.Id, 40 ChannelType = ChannelType.StreakChannel 41 }; 42 43 _context.Channels.Add(channel); 44 await _context.SaveChangesAsync(); 45 } 46 else 47 { 48 await _restClient.ModifyGuildChannelAsync(channel.ChannelId, options => options.WithName(channelName)); 49 } 50 } 51 52 public async Task UpdateTimeChannelAsync(ulong serverId, TimeSpan resetTime) 53 { 54 var channel = await _context.Channels 55 .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ChannelType == ChannelType.TimeChannel); 56 57 var timeRemaining = CalculateTimeRemaining(resetTime); 58 var channelName = $"⏰ {timeRemaining:hh\\:mm}"; 59 60 if (channel == null) 61 { 62 var properties = new GuildChannelProperties(channelName, NetCord.ChannelType.VoiceGuildChannel); 63 var createdChannel = await _restClient.CreateGuildChannelAsync(serverId, properties); 64 65 channel = new ChannelEntity 66 { 67 ServerId = serverId, 68 ChannelId = createdChannel.Id, 69 ChannelType = ChannelType.TimeChannel 70 }; 71 72 _context.Channels.Add(channel); 73 await _context.SaveChangesAsync(); 74 } 75 else 76 { 77 await _restClient.ModifyGuildChannelAsync(channel.ChannelId, options => options.WithName(channelName)); 78 } 79 } 80 81 private static TimeSpan CalculateTimeRemaining(TimeSpan resetTime) 82 { 83 var now = DateTime.UtcNow.TimeOfDay; 84 85 if (now < resetTime) 86 { 87 return resetTime - now; 88 } 89 else 90 { 91 return TimeSpan.FromHours(24) - now + resetTime; 92 } 93 } 94 95 public async Task<ChannelEntity?> GetChannelAsync(ulong serverId, ChannelType channelType) 96 { 97 return await _context.Channels 98 .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ChannelType == channelType); 99 } 100 101 public async Task<bool> DeleteChannelAsync(ulong channelId) 102 { 103 var test = await _restClient.DeleteChannelAsync(channelId); 104 105 return true; 106 } 107 108 public async Task SendStreakReminderAsync(ulong user1Id, ulong user2Id, bool user1NeedsReminder, bool user2NeedsReminder, ulong serverId) 109 { 110 if (user1NeedsReminder) 111 { 112 try 113 { 114 var dmChannel = await _restClient.GetDMChannelAsync(user1Id); 115 var message = new MessageProperties() 116 .WithContent($"Your streak with <@{user2Id}> will reset in less than an hour! Send a message in the server to keep it alive!"); 117 await _restClient.SendMessageAsync(dmChannel.Id, message); 118 } 119 catch (RestException) 120 { 121 _logger.LogWarning("Failed to send streak reminder DM to user {UserId}", user1Id); 122 await SendReminderToTextChannelAsync(serverId, user1Id, user2Id); 123 } 124 } 125 126 if (user2NeedsReminder) 127 { 128 try 129 { 130 var dmChannel = await _restClient.GetDMChannelAsync(user2Id); 131 var message = new MessageProperties() 132 .WithContent($"Your streak with <@{user1Id}> will reset in less than an hour! Send a message in the server to keep it alive!"); 133 await _restClient.SendMessageAsync(dmChannel.Id, message); 134 } 135 catch (RestException) 136 { 137 _logger.LogWarning("Failed to send streak reminder DM to user {UserId}", user2Id); 138 await SendReminderToTextChannelAsync(serverId, user2Id, user1Id); 139 } 140 } 141 } 142 143 private async Task SendReminderToTextChannelAsync(ulong serverId, ulong userToRemind, ulong streakPartnerUserId) 144 { 145 try 146 { 147 var guildChannels = await _restClient.GetGuildChannelsAsync(serverId); 148 var textChannel = guildChannels.FirstOrDefault(c => c is NetCord.TextChannel) as NetCord.TextChannel; 149 150 if (textChannel != null) 151 { 152 var message = new MessageProperties() 153 .WithContent($"<@{userToRemind}>, your streak with <@{streakPartnerUserId}> will reset in less than an hour! Send a message in the server to keep it alive! (DM failed to send)"); 154 await textChannel.SendMessageAsync(message); 155 } 156 else 157 { 158 _logger.LogWarning("No text channel found in server {ServerId} to send fallback reminder", serverId); 159 } 160 } 161 catch (Exception ex) 162 { 163 _logger.LogError(ex, "Failed to send fallback reminder to text channel in server {ServerId}", serverId); 164 } 165 } 166 167 public async Task<ChannelEntity> CreateDmStreakMessageAsync(ulong channelId, int streakNumber, TimeSpan resetTime) 168 { 169 var timeRemaining = CalculateTimeRemaining(resetTime); 170 var content = $"🔥 {streakNumber}\n⏰ {timeRemaining:hh\\:mm}"; 171 172 var messageProperties = new MessageProperties().WithContent(content); 173 var sentMessage = await _restClient.SendMessageAsync(channelId, messageProperties); 174 175 var channel = new ChannelEntity 176 { 177 ChannelId = channelId, 178 ChannelType = ChannelType.DmChannel, 179 MessageId = sentMessage.Id 180 }; 181 182 _context.Channels.Add(channel); 183 await _context.SaveChangesAsync(); 184 185 return channel; 186 } 187 188 public async Task UpdateDmStreakMessageAsync(ulong channelId, ulong messageId, int streakNumber, TimeSpan resetTime) 189 { 190 var timeRemaining = CalculateTimeRemaining(resetTime); 191 var content = $"🔥 {streakNumber}\n⏰ {timeRemaining:hh\\:mm}"; 192 193 await _restClient.ModifyMessageAsync(channelId, messageId, message => message.WithContent(content)); 194 } 195}