#nullable disable using LinqToDB.EntityFrameworkCore; using EllieBot.Common.ModuleBehaviors; using EllieBot.Db.Models; using EllieBot.Modules.Administration._common.results; namespace EllieBot.Modules.Administration; public class AdministrationService : IEService, IReadyExecutor { private ConcurrentHashSet<ulong> _deleteMessagesOnCommand; private ConcurrentDictionary<ulong, bool> _delMsgOnCmdChannels; private readonly DbService _db; private readonly IReplacementService _repSvc; private readonly ILogCommandService _logService; private readonly IHttpClientFactory _httpFactory; private readonly ShardData _shardData; private readonly CommandHandler _cmdHandler; public AdministrationService( DbService db, IReplacementService repSvc, ILogCommandService logService, IHttpClientFactory factory, ShardData shardData, CommandHandler cmdHandler) { _db = db; _shardData = shardData; _repSvc = repSvc; _logService = logService; _httpFactory = factory; _cmdHandler = cmdHandler; } public async Task OnReadyAsync() { await using var uow = _db.GetDbContext(); _deleteMessagesOnCommand = new(await uow.GetTable<GuildConfig>() .Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && x.DeleteMessageOnCommand) .Select(x => x.GuildId) .ToListAsyncLinqToDB()); _delMsgOnCmdChannels = (await uow.GetTable<DelMsgOnCmdChannel>() .Where(x => _deleteMessagesOnCommand.Contains(x.GuildId)) .ToDictionaryAsyncLinqToDB(x => x.ChannelId, x => x.State)) .ToConcurrent(); _cmdHandler.CommandExecuted += DelMsgOnCmd_Handler; } public async Task<(bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels)> GetDelMsgOnCmdData(ulong guildId) { await using var uow = _db.GetDbContext(); var conf = await uow.GetTable<GuildConfig>() .Where(x => x.GuildId == guildId) .Select(x => x.DeleteMessageOnCommand) .FirstOrDefaultAsyncLinqToDB(); var channels = await uow.GetTable<DelMsgOnCmdChannel>() .Where(x => x.GuildId == guildId) .ToListAsyncLinqToDB(); return (conf, channels); } private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd) { if (msg.Channel is not ITextChannel channel) return Task.CompletedTask; _ = Task.Run(async () => { //wat ?! if (_delMsgOnCmdChannels.TryGetValue(channel.Id, out var state)) { if (state && cmd.Name != "prune" && cmd.Name != "pick") { _logService.AddDeleteIgnore(msg.Id); try { await msg.DeleteAsync(); } catch { } } //if state is false, that means do not do it } else if (_deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick") { _logService.AddDeleteIgnore(msg.Id); try { await msg.DeleteAsync(); } catch { } } }); return Task.CompletedTask; } public async Task<bool> ToggleDelMsgOnCmd(ulong guildId) { await using var uow = _db.GetDbContext(); var gc = uow.GuildConfigsForId(guildId); gc.DeleteMessageOnCommand = !gc.DeleteMessageOnCommand; if (gc.DeleteMessageOnCommand) _deleteMessagesOnCommand.Add(guildId); else _deleteMessagesOnCommand.TryRemove(guildId); await uow.SaveChangesAsync(); return gc.DeleteMessageOnCommand; } public async Task SetDelMsgOnCmdState(ulong guildId, ulong chId, Administration.State newState) { await using (var uow = _db.GetDbContext()) { var old = await uow.GetTable<DelMsgOnCmdChannel>() .Where(x => x.GuildId == guildId && x.ChannelId == chId) .FirstOrDefaultAsyncLinqToDB(); if (newState == Administration.State.Inherit) { if (old is not null) { uow.Remove(old); } } else { if (old is null) { old = new DelMsgOnCmdChannel { GuildId = guildId, ChannelId = chId, State = newState == Administration.State.Enable }; uow.Add(old); } old.State = newState == Administration.State.Enable; _delMsgOnCmdChannels[chId] = newState == Administration.State.Enable; } await uow.SaveChangesAsync(); } if (newState == Administration.State.Disable) { } else if (newState == Administration.State.Enable) { _delMsgOnCmdChannels[chId] = true; } else { _delMsgOnCmdChannels.TryRemove(chId, out _); } } public async Task DeafenUsers(bool value, params IGuildUser[] users) { if (!users.Any()) return; foreach (var u in users) { try { await u.ModifyAsync(usr => usr.Deaf = value); } catch { // ignored } } } public async Task EditMessage( ICommandContext context, ITextChannel chanl, ulong messageId, string input) { var msg = await chanl.GetMessageAsync(messageId); if (msg is not IUserMessage umsg || msg.Author.Id != context.Client.CurrentUser.Id) return; var repCtx = new ReplacementContext(context); var text = SmartText.CreateFrom(input); text = await _repSvc.ReplaceAsync(text, repCtx); await umsg.EditAsync(text); } public async Task<SetServerBannerResult> SetServerBannerAsync(IGuild guild, string img) { if (!IsValidUri(img)) return SetServerBannerResult.InvalidURL; var uri = new Uri(img); using var http = _httpFactory.CreateClient(); using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); if (!sr.IsImage()) return SetServerBannerResult.InvalidFileType; if (sr.GetContentLength() > 8.Megabytes()) { return SetServerBannerResult.Toolarge; } await using var imageStream = await sr.Content.ReadAsStreamAsync(); await guild.ModifyAsync(x => x.Banner = new Image(imageStream)); return SetServerBannerResult.Success; } public async Task<SetServerIconResult> SetServerIconAsync(IGuild guild, string img) { if (!IsValidUri(img)) return SetServerIconResult.InvalidURL; var uri = new Uri(img); using var http = _httpFactory.CreateClient(); using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); if (!sr.IsImage()) return SetServerIconResult.InvalidFileType; await using var imageStream = await sr.Content.ReadAsStreamAsync(); await guild.ModifyAsync(x => x.Icon = new Image(imageStream)); return SetServerIconResult.Success; } private bool IsValidUri(string img) => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute); }