using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using NetCord.Rest; using StreakBot.Data; using StreakBot.Data.Entities; using ChannelEntity = StreakBot.Data.Entities.Channel; using ChannelType = StreakBot.Data.Entities.ChannelType; namespace StreakBot.Services; public class ChannelService { private readonly StreakDbContext _context; private readonly RestClient _restClient; private readonly ILogger _logger; public ChannelService(StreakDbContext context, RestClient restClient, ILogger logger) { _context = context; _restClient = restClient; _logger = logger; } public async Task UpdateStreakChannelAsync(ulong serverId, int streakCount, bool endOfDay = false) { var channel = await _context.Channels .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ChannelType == ChannelType.StreakChannel); var channelName = endOfDay ? $"🧊 {streakCount}" : $"šŸ”„ {streakCount}"; if (channel == null) { var properties = new GuildChannelProperties(channelName, NetCord.ChannelType.VoiceGuildChannel); var createdChannel = await _restClient.CreateGuildChannelAsync(serverId, properties); channel = new ChannelEntity { ServerId = serverId, ChannelId = createdChannel.Id, ChannelType = ChannelType.StreakChannel }; _context.Channels.Add(channel); await _context.SaveChangesAsync(); } else { await _restClient.ModifyGuildChannelAsync(channel.ChannelId, options => options.WithName(channelName)); } } public async Task UpdateTimeChannelAsync(ulong serverId, TimeSpan resetTime) { var channel = await _context.Channels .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ChannelType == ChannelType.TimeChannel); var timeRemaining = CalculateTimeRemaining(resetTime); var channelName = $"ā° {timeRemaining:hh\\:mm}"; if (channel == null) { var properties = new GuildChannelProperties(channelName, NetCord.ChannelType.VoiceGuildChannel); var createdChannel = await _restClient.CreateGuildChannelAsync(serverId, properties); channel = new ChannelEntity { ServerId = serverId, ChannelId = createdChannel.Id, ChannelType = ChannelType.TimeChannel }; _context.Channels.Add(channel); await _context.SaveChangesAsync(); } else { await _restClient.ModifyGuildChannelAsync(channel.ChannelId, options => options.WithName(channelName)); } } private static TimeSpan CalculateTimeRemaining(TimeSpan resetTime) { var now = DateTime.UtcNow.TimeOfDay; if (now < resetTime) { return resetTime - now; } else { return TimeSpan.FromHours(24) - now + resetTime; } } public async Task GetChannelAsync(ulong serverId, ChannelType channelType) { return await _context.Channels .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ChannelType == channelType); } public async Task DeleteChannelAsync(ulong channelId) { var test = await _restClient.DeleteChannelAsync(channelId); return true; } public async Task SendStreakReminderAsync(ulong user1Id, ulong user2Id, bool user1NeedsReminder, bool user2NeedsReminder, ulong serverId) { if (user1NeedsReminder) { try { var dmChannel = await _restClient.GetDMChannelAsync(user1Id); var message = new MessageProperties() .WithContent($"Your streak with <@{user2Id}> will reset in less than an hour! Send a message in the server to keep it alive!"); await _restClient.SendMessageAsync(dmChannel.Id, message); } catch (RestException) { _logger.LogWarning("Failed to send streak reminder DM to user {UserId}", user1Id); await SendReminderToTextChannelAsync(serverId, user1Id, user2Id); } } if (user2NeedsReminder) { try { var dmChannel = await _restClient.GetDMChannelAsync(user2Id); var message = new MessageProperties() .WithContent($"Your streak with <@{user1Id}> will reset in less than an hour! Send a message in the server to keep it alive!"); await _restClient.SendMessageAsync(dmChannel.Id, message); } catch (RestException) { _logger.LogWarning("Failed to send streak reminder DM to user {UserId}", user2Id); await SendReminderToTextChannelAsync(serverId, user2Id, user1Id); } } } private async Task SendReminderToTextChannelAsync(ulong serverId, ulong userToRemind, ulong streakPartnerUserId) { try { var guildChannels = await _restClient.GetGuildChannelsAsync(serverId); var textChannel = guildChannels.FirstOrDefault(c => c is NetCord.TextChannel) as NetCord.TextChannel; if (textChannel != null) { var message = new MessageProperties() .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)"); await textChannel.SendMessageAsync(message); } else { _logger.LogWarning("No text channel found in server {ServerId} to send fallback reminder", serverId); } } catch (Exception ex) { _logger.LogError(ex, "Failed to send fallback reminder to text channel in server {ServerId}", serverId); } } public async Task CreateDmStreakMessageAsync(ulong channelId, int streakNumber, TimeSpan resetTime) { var timeRemaining = CalculateTimeRemaining(resetTime); var content = $"šŸ”„ {streakNumber}\nā° {timeRemaining:hh\\:mm}"; var messageProperties = new MessageProperties().WithContent(content); var sentMessage = await _restClient.SendMessageAsync(channelId, messageProperties); var channel = new ChannelEntity { ChannelId = channelId, ChannelType = ChannelType.DmChannel, MessageId = sentMessage.Id }; _context.Channels.Add(channel); await _context.SaveChangesAsync(); return channel; } public async Task UpdateDmStreakMessageAsync(ulong channelId, ulong messageId, int streakNumber, TimeSpan resetTime) { var timeRemaining = CalculateTimeRemaining(resetTime); var content = $"šŸ”„ {streakNumber}\nā° {timeRemaining:hh\\:mm}"; await _restClient.ModifyMessageAsync(channelId, messageId, message => message.WithContent(content)); } }