mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	part 3 of the response rework
This commit is contained in:
		@@ -32,6 +32,7 @@ public class LogSetting : DbEntity
 | 
			
		||||
    //voicepresence
 | 
			
		||||
 | 
			
		||||
    public ulong? LogVoicePresenceId { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    public ulong? LogVoicePresenceTTSId { get; set; }
 | 
			
		||||
    public ulong? LogWarnsId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -19,18 +19,21 @@ public class GreetService : INService, IReadyExecutor
 | 
			
		||||
    private readonly GreetGrouper<IUser> _byes = new();
 | 
			
		||||
    private readonly BotConfigService _bss;
 | 
			
		||||
    private readonly IReplacementService _repSvc;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public GreetService(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        BotConfigService bss,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        IReplacementService repSvc)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _bss = bss;
 | 
			
		||||
        _repSvc = repSvc;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _guildConfigsCache = new(bot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
 | 
			
		||||
 | 
			
		||||
@@ -281,6 +284,7 @@ public class GreetService : INService, IReadyExecutor
 | 
			
		||||
            FullMode = BoundedChannelFullMode.DropNewest
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private async Task<bool> GreetDmUser(GreetSettings conf, IGuildUser user)
 | 
			
		||||
    {
 | 
			
		||||
        var completionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
			
		||||
@@ -298,32 +302,32 @@ public class GreetService : INService, IReadyExecutor
 | 
			
		||||
            // .Build();
 | 
			
		||||
 | 
			
		||||
            var repCtx = new ReplacementContext(client: _client, guild: user.Guild, users: user);
 | 
			
		||||
            var text = SmartText.CreateFrom(conf.DmGreetMessageText);
 | 
			
		||||
            text = await _repSvc.ReplaceAsync(text, repCtx);
 | 
			
		||||
            var smartText = SmartText.CreateFrom(conf.DmGreetMessageText);
 | 
			
		||||
            smartText = await _repSvc.ReplaceAsync(smartText, repCtx);
 | 
			
		||||
 | 
			
		||||
            if (text is SmartPlainText pt)
 | 
			
		||||
            if (smartText is SmartPlainText pt)
 | 
			
		||||
            {
 | 
			
		||||
                text = new SmartEmbedText()
 | 
			
		||||
                smartText = new SmartEmbedText()
 | 
			
		||||
                {
 | 
			
		||||
                    Description = pt.Text
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (text is SmartEmbedText set)
 | 
			
		||||
            if (smartText is SmartEmbedText set)
 | 
			
		||||
            {
 | 
			
		||||
                text = set with
 | 
			
		||||
                smartText = set with
 | 
			
		||||
                {
 | 
			
		||||
                    Footer = CreateFooterSource(user)
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            else if (text is SmartEmbedTextArray seta)
 | 
			
		||||
            else if (smartText is SmartEmbedTextArray seta)
 | 
			
		||||
            {
 | 
			
		||||
                // if the greet dm message is a text array
 | 
			
		||||
                var ebElem = seta.Embeds.LastOrDefault();
 | 
			
		||||
                if (ebElem is null)
 | 
			
		||||
                {
 | 
			
		||||
                    // if there are no embeds, add an embed with the footer
 | 
			
		||||
                    text = seta with
 | 
			
		||||
                    smartText = seta with
 | 
			
		||||
                    {
 | 
			
		||||
                        Embeds = new[]
 | 
			
		||||
                        {
 | 
			
		||||
@@ -355,7 +359,7 @@ public class GreetService : INService, IReadyExecutor
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await user.SendAsync(text);
 | 
			
		||||
            await _sender.Response(user).Text(smartText).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,13 @@ public class MuteService : INService
 | 
			
		||||
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public MuteService(DiscordSocketClient client, DbService db, IEmbedBuilderService eb)
 | 
			
		||||
    public MuteService(DiscordSocketClient client, DbService db, IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        using (var uow = db.GetDbContext())
 | 
			
		||||
        {
 | 
			
		||||
@@ -122,13 +122,13 @@ public class MuteService : INService
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(reason))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(() => user.SendMessageAsync(embed: new EmbedBuilder()
 | 
			
		||||
                                                           .WithDescription(
 | 
			
		||||
                                                               $"You've been muted in {user.Guild} server")
 | 
			
		||||
                                                           .AddField("Mute Type", type.ToString())
 | 
			
		||||
                                                           .AddField("Moderator", mod.ToString())
 | 
			
		||||
                                                           .AddField("Reason", reason)
 | 
			
		||||
                                                           .Build()));
 | 
			
		||||
        _ = Task.Run(() => _sender.Response(user)
 | 
			
		||||
                                  .Embed(new EmbedBuilder()
 | 
			
		||||
                                         .WithDescription($"You've been muted in {user.Guild} server")
 | 
			
		||||
                                         .AddField("Mute Type", type.ToString())
 | 
			
		||||
                                         .AddField("Moderator", mod.ToString())
 | 
			
		||||
                                         .AddField("Reason", reason))
 | 
			
		||||
                                  .SendAsync());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnUserUnmuted(
 | 
			
		||||
@@ -140,13 +140,13 @@ public class MuteService : INService
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(reason))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(() => user.SendMessageAsync(embed: new EmbedBuilder()
 | 
			
		||||
                                                           .WithDescription(
 | 
			
		||||
                                                               $"You've been unmuted in {user.Guild} server")
 | 
			
		||||
                                                           .AddField("Unmute Type", type.ToString())
 | 
			
		||||
                                                           .AddField("Moderator", mod.ToString())
 | 
			
		||||
                                                           .AddField("Reason", reason)
 | 
			
		||||
                                                           .Build()));
 | 
			
		||||
        _ = Task.Run(() => _sender.Response(user)
 | 
			
		||||
                                  .Embed(new EmbedBuilder()
 | 
			
		||||
                                         .WithDescription($"You've been unmuted in {user.Guild} server")
 | 
			
		||||
                                         .AddField("Unmute Type", type.ToString())
 | 
			
		||||
                                         .AddField("Moderator", mod.ToString())
 | 
			
		||||
                                         .AddField("Reason", reason))
 | 
			
		||||
                                  .SendAsync());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task Client_UserJoined(IGuildUser usr)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,16 +10,16 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
 | 
			
		||||
    private readonly IBotCredsProvider _bcp;
 | 
			
		||||
    private readonly IHttpClientFactory _httpFactory;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly IEmbedBuilderService _ebs;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public CheckForUpdatesService(BotConfigService bcs, IBotCredsProvider bcp, IHttpClientFactory httpFactory,
 | 
			
		||||
        DiscordSocketClient client, IEmbedBuilderService ebs)
 | 
			
		||||
        DiscordSocketClient client, IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _bcs = bcs;
 | 
			
		||||
        _bcp = bcp;
 | 
			
		||||
        _httpFactory = httpFactory;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _ebs = ebs;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public async Task OnReadyAsync()
 | 
			
		||||
@@ -86,7 +86,7 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
 | 
			
		||||
                                .WithDescription(thisVersionChangelog.TrimTo(4096))
 | 
			
		||||
                                .WithFooter("You may disable these messages by typing '.conf bot checkforupdates false'");
 | 
			
		||||
 | 
			
		||||
                            await user.EmbedAsync(eb);
 | 
			
		||||
                            await _sender.Response(user).Embed(eb).SendAsync();
 | 
			
		||||
                        }).WhenAll();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
    private readonly IHttpClientFactory _httpFactory;
 | 
			
		||||
    private readonly BotConfigService _bss;
 | 
			
		||||
    private readonly IPubSub _pubSub;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    //keys
 | 
			
		||||
    private readonly TypedKey<ActivityPubData> _activitySetKey;
 | 
			
		||||
@@ -40,7 +40,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
        IHttpClientFactory factory,
 | 
			
		||||
        BotConfigService bss,
 | 
			
		||||
        IPubSub pubSub,
 | 
			
		||||
        IEmbedBuilderService eb)
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _cmdHandler = cmdHandler;
 | 
			
		||||
        _db = db;
 | 
			
		||||
@@ -50,7 +50,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
        _httpFactory = factory;
 | 
			
		||||
        _bss = bss;
 | 
			
		||||
        _pubSub = pubSub;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _activitySetKey = new("activity.set");
 | 
			
		||||
        _guildLeaveKey = new("guild.leave");
 | 
			
		||||
 | 
			
		||||
@@ -225,7 +225,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        await ownerCh.Response(_strings, _eb).Confirm(title, toSend).SendAsync();
 | 
			
		||||
                        await _sender.Response(ownerCh).Confirm(title, toSend).SendAsync();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
@@ -238,7 +238,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (_client.GetChannel(cid) is ITextChannel ch)
 | 
			
		||||
                        await ch.Response(_strings, _eb).Confirm(title, toSend).SendAsync();
 | 
			
		||||
                        await _sender.Response(ch).Confirm(title, toSend).SendAsync();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
@@ -252,7 +252,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        await firstOwnerChannel.Response(_strings, _eb).Confirm(title, toSend).SendAsync();
 | 
			
		||||
                        await _sender.Response(firstOwnerChannel).Confirm(title, toSend).SendAsync();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
    private readonly MuteService _mute;
 | 
			
		||||
    private readonly ProtectionService _prot;
 | 
			
		||||
    private readonly GuildTimezoneService _tz;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMemoryCache _memoryCache;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new();
 | 
			
		||||
@@ -37,20 +36,18 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
        ProtectionService prot,
 | 
			
		||||
        GuildTimezoneService tz,
 | 
			
		||||
        IMemoryCache memoryCache,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        UserPunishService punishService,
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _memoryCache = memoryCache;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _mute = mute;
 | 
			
		||||
        _prot = prot;
 | 
			
		||||
        _tz = tz;
 | 
			
		||||
        _punishService = punishService;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        using (var uow = db.GetDbContext())
 | 
			
		||||
        {
 | 
			
		||||
@@ -73,7 +70,6 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
        _client.UserLeft += _client_UserLeft;
 | 
			
		||||
        // _client.PresenceUpdated += _client_UserPresenceUpdated;
 | 
			
		||||
        _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
 | 
			
		||||
        _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
 | 
			
		||||
        _client.GuildMemberUpdated += _client_GuildUserUpdated;
 | 
			
		||||
        _client.PresenceUpdated += _client_PresenceUpdated;
 | 
			
		||||
        _client.UserUpdated += _client_UserUpdated;
 | 
			
		||||
@@ -168,11 +164,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
 | 
			
		||||
                var title = GetText(logChannel.Guild, strs.thread_deleted);
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                await _sender.Response(logChannel).Embed(new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .WithTitle("🗑 " + title)
 | 
			
		||||
                    .WithDescription($"{ch.Name} | {ch.Id}")
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild)));
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild))).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -198,11 +194,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
 | 
			
		||||
                var title = GetText(logChannel.Guild, strs.thread_created);
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                await _sender.Response(logChannel).Embed(new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .WithTitle("🆕 " + title)
 | 
			
		||||
                    .WithDescription($"{ch.Name} | {ch.Id}")
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild)));
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild))).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -313,7 +309,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
            logSetting.UserLeftId = logSetting.UserBannedId = logSetting.UserUnbannedId = logSetting.UserUpdatedId =
 | 
			
		||||
                logSetting.ChannelCreatedId = logSetting.ChannelDestroyedId = logSetting.ChannelUpdatedId =
 | 
			
		||||
                    logSetting.LogUserPresenceId = logSetting.LogVoicePresenceId = logSetting.UserMutedId =
 | 
			
		||||
                        logSetting.LogVoicePresenceTTSId = logSetting.ThreadCreatedId = logSetting.ThreadDeletedId
 | 
			
		||||
                        logSetting.ThreadCreatedId = logSetting.ThreadDeletedId
 | 
			
		||||
                            = logSetting.LogWarnsId = value ? channelId : null;
 | 
			
		||||
        await uow.SaveChangesAsync();
 | 
			
		||||
        GuildLogSettings.AddOrUpdate(guildId, _ => logSetting, (_, _) => logSetting);
 | 
			
		||||
@@ -339,7 +335,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
            .AddField("Reason", string.IsNullOrWhiteSpace(arg.Reason) ? "-" : arg.Reason, true)
 | 
			
		||||
            .WithFooter(CurrentTime(g));
 | 
			
		||||
 | 
			
		||||
        await logChannel.EmbedAsync(embed);
 | 
			
		||||
        await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task _client_UserUpdated(SocketUser before, SocketUser uAfter)
 | 
			
		||||
@@ -389,7 +385,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                else
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -451,10 +447,6 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                case LogType.VoicePresence:
 | 
			
		||||
                    channelId = logSetting.LogVoicePresenceId = logSetting.LogVoicePresenceId is null ? cid : default;
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogType.VoicePresenceTts:
 | 
			
		||||
                    channelId = logSetting.LogVoicePresenceTTSId =
 | 
			
		||||
                        logSetting.LogVoicePresenceTTSId is null ? cid : default;
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogType.UserWarned:
 | 
			
		||||
                    channelId = logSetting.LogWarnsId = logSetting.LogWarnsId is null ? cid : default;
 | 
			
		||||
                    break;
 | 
			
		||||
@@ -472,48 +464,6 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
        return channelId is not null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after)
 | 
			
		||||
    {
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (iusr is not IGuildUser usr)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                var beforeVch = before.VoiceChannel;
 | 
			
		||||
                var afterVch = after.VoiceChannel;
 | 
			
		||||
 | 
			
		||||
                if (beforeVch == afterVch)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting)
 | 
			
		||||
                    || logSetting.LogVoicePresenceTTSId is null)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                ITextChannel? logChannel;
 | 
			
		||||
                if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresenceTts)) is null)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                var str = string.Empty;
 | 
			
		||||
                if (beforeVch?.Guild == afterVch?.Guild)
 | 
			
		||||
                    str = GetText(logChannel.Guild, strs.log_vc_moved(usr.Username, beforeVch?.Name, afterVch?.Name));
 | 
			
		||||
                else if (beforeVch is null)
 | 
			
		||||
                    str = GetText(logChannel.Guild, strs.log_vc_joined(usr.Username, afterVch?.Name));
 | 
			
		||||
                else if (afterVch is null)
 | 
			
		||||
                    str = GetText(logChannel.Guild, strs.log_vc_left(usr.Username, beforeVch.Name));
 | 
			
		||||
 | 
			
		||||
                var toDelete = await logChannel.SendMessageAsync(str, true);
 | 
			
		||||
                toDelete.DeleteAfter(5);
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                // ignored
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void MuteCommands_UserMuted(
 | 
			
		||||
        IGuildUser usr,
 | 
			
		||||
        IUser mod,
 | 
			
		||||
@@ -551,7 +501,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                    .WithFooter(CurrentTime(usr.Guild))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -601,7 +551,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                if (!string.IsNullOrWhiteSpace(reason))
 | 
			
		||||
                    embed.WithDescription(reason);
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -653,7 +603,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                    .WithFooter(CurrentTime(logChannel.Guild))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -711,7 +661,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                            .AddField(GetText(logChannel.Guild, strs.new_nick),
 | 
			
		||||
                                $"{after.Nickname}#{after.Discriminator}");
 | 
			
		||||
 | 
			
		||||
                        await logChannel.EmbedAsync(embed);
 | 
			
		||||
                        await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (!before.Roles.SequenceEqual(after.Roles))
 | 
			
		||||
                    {
 | 
			
		||||
@@ -721,7 +671,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                            embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_add))
 | 
			
		||||
                                .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
 | 
			
		||||
 | 
			
		||||
                            await logChannel.EmbedAsync(embed);
 | 
			
		||||
                            await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (before.Roles.Count > after.Roles.Count)
 | 
			
		||||
                        {
 | 
			
		||||
@@ -735,7 +685,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                                embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_rem))
 | 
			
		||||
                                    .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
 | 
			
		||||
 | 
			
		||||
                                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@@ -791,7 +741,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                else
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -826,11 +776,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                else
 | 
			
		||||
                    title = GetText(logChannel.Guild, strs.text_chan_destroyed);
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                await _sender.Response(logChannel).Embed(new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .WithTitle("🆕 " + title)
 | 
			
		||||
                    .WithDescription($"{ch.Name} | {ch.Id}")
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild)));
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild))).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -862,11 +812,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                else
 | 
			
		||||
                    title = GetText(logChannel.Guild, strs.text_chan_created);
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                await _sender.Response(logChannel).Embed(new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .WithTitle("🆕 " + title)
 | 
			
		||||
                    .WithDescription($"{ch.Name} | {ch.Id}")
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild)));
 | 
			
		||||
                    .WithFooter(CurrentTime(ch.Guild))).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -975,7 +925,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
 | 
			
		||||
                    embed.WithThumbnailUrl(usr.GetAvatarUrl());
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -1014,7 +964,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
 | 
			
		||||
                    embed.WithThumbnailUrl(usr.GetAvatarUrl());
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -1049,7 +999,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
 | 
			
		||||
                    embed.WithThumbnailUrl(usr.GetAvatarUrl());
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -1099,7 +1049,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                if (Uri.IsWellFormedUriString(avatarUrl, UriKind.Absolute))
 | 
			
		||||
                    embed.WithThumbnailUrl(usr.GetAvatarUrl());
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -1152,7 +1102,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                        string.Join(", ", msg.Attachments.Select(a => a.Url)));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
@@ -1212,7 +1162,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
                    .AddField("Id", after.Id.ToString())
 | 
			
		||||
                    .WithFooter(CurrentTime(channel.Guild));
 | 
			
		||||
 | 
			
		||||
                await logChannel.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(logChannel).Embed(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -1266,9 +1216,6 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
            case LogType.VoicePresence:
 | 
			
		||||
                id = logSetting.LogVoicePresenceId;
 | 
			
		||||
                break;
 | 
			
		||||
            case LogType.VoicePresenceTts:
 | 
			
		||||
                id = logSetting.LogVoicePresenceTTSId;
 | 
			
		||||
                break;
 | 
			
		||||
            case LogType.UserMuted:
 | 
			
		||||
                id = logSetting.UserMutedId;
 | 
			
		||||
                break;
 | 
			
		||||
@@ -1348,9 +1295,6 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
 | 
			
		||||
            case LogType.VoicePresence:
 | 
			
		||||
                newLogSetting.LogVoicePresenceId = null;
 | 
			
		||||
                break;
 | 
			
		||||
            case LogType.VoicePresenceTts:
 | 
			
		||||
                newLogSetting.LogVoicePresenceTTSId = null;
 | 
			
		||||
                break;
 | 
			
		||||
            case LogType.UserWarned:
 | 
			
		||||
                newLogSetting.LogWarnsId = null;
 | 
			
		||||
                break;
 | 
			
		||||
 
 | 
			
		||||
@@ -145,8 +145,6 @@ public partial class Administration
 | 
			
		||||
                    return l.LogUserPresenceId;
 | 
			
		||||
                case LogType.VoicePresence:
 | 
			
		||||
                    return l.LogVoicePresenceId;
 | 
			
		||||
                case LogType.VoicePresenceTts:
 | 
			
		||||
                    return l.LogVoicePresenceTTSId;
 | 
			
		||||
                case LogType.UserMuted:
 | 
			
		||||
                    return l.UserMutedId;
 | 
			
		||||
                case LogType.UserWarned:
 | 
			
		||||
 
 | 
			
		||||
@@ -65,11 +65,13 @@ public partial class Administration
 | 
			
		||||
            var dmFailed = false;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await user.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                                         .WithErrorColor()
 | 
			
		||||
                                         .WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
 | 
			
		||||
                                         .AddField(GetText(strs.moderator), ctx.User.ToString())
 | 
			
		||||
                                         .AddField(GetText(strs.reason), reason ?? "-"));
 | 
			
		||||
                await _sender.Response(user)
 | 
			
		||||
                             .Embed(new EmbedBuilder()
 | 
			
		||||
                                    .WithErrorColor()
 | 
			
		||||
                                    .WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
 | 
			
		||||
                                    .AddField(GetText(strs.moderator), ctx.User.ToString())
 | 
			
		||||
                                    .AddField(GetText(strs.reason), reason ?? "-"))
 | 
			
		||||
                             .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -84,7 +86,8 @@ public partial class Administration
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Log.Warning(ex, "Exception occured while warning a user");
 | 
			
		||||
                var errorEmbed = new EmbedBuilder().WithErrorColor().WithDescription(GetText(strs.cant_apply_punishment));
 | 
			
		||||
                var errorEmbed = new EmbedBuilder().WithErrorColor()
 | 
			
		||||
                                                   .WithDescription(GetText(strs.cant_apply_punishment));
 | 
			
		||||
 | 
			
		||||
                if (dmFailed)
 | 
			
		||||
                    errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
 | 
			
		||||
@@ -261,9 +264,9 @@ public partial class Administration
 | 
			
		||||
                                     });
 | 
			
		||||
 | 
			
		||||
                    return new EmbedBuilder()
 | 
			
		||||
                              .WithOkColor()
 | 
			
		||||
                              .WithTitle(GetText(strs.warnings_list))
 | 
			
		||||
                              .WithDescription(string.Join("\n", ws));
 | 
			
		||||
                           .WithOkColor()
 | 
			
		||||
                           .WithTitle(GetText(strs.warnings_list))
 | 
			
		||||
                           .WithDescription(string.Join("\n", ws));
 | 
			
		||||
                },
 | 
			
		||||
                warnings.Length,
 | 
			
		||||
                15);
 | 
			
		||||
@@ -433,9 +436,10 @@ public partial class Administration
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
 | 
			
		||||
                    var embed = await _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, time.Time);
 | 
			
		||||
                    if (embed is not null)
 | 
			
		||||
                        await guildUser.SendAsync(embed);
 | 
			
		||||
                    var smartText =
 | 
			
		||||
                        await _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, time.Time);
 | 
			
		||||
                    if (smartText is not null)
 | 
			
		||||
                        await Response().User(guildUser).Text(smartText).SendAsync();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
@@ -447,13 +451,13 @@ public partial class Administration
 | 
			
		||||
            var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
 | 
			
		||||
            await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
 | 
			
		||||
            var toSend = new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle("⛔️ " + GetText(strs.banned_user))
 | 
			
		||||
                            .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
 | 
			
		||||
                            .AddField("ID", userId.ToString(), true)
 | 
			
		||||
                            .AddField(GetText(strs.duration),
 | 
			
		||||
                                time.Time.Humanize(3, minUnit: TimeUnit.Minute, culture: Culture),
 | 
			
		||||
                                true);
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle("⛔️ " + GetText(strs.banned_user))
 | 
			
		||||
                         .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
 | 
			
		||||
                         .AddField("ID", userId.ToString(), true)
 | 
			
		||||
                         .AddField(GetText(strs.duration),
 | 
			
		||||
                             time.Time.Humanize(3, minUnit: TimeUnit.Minute, culture: Culture),
 | 
			
		||||
                             true);
 | 
			
		||||
 | 
			
		||||
            if (dmFailed)
 | 
			
		||||
                toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
 | 
			
		||||
@@ -475,9 +479,9 @@ public partial class Administration
 | 
			
		||||
                await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
 | 
			
		||||
 | 
			
		||||
                await ctx.Channel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                                                .WithOkColor()
 | 
			
		||||
                                                .WithTitle("⛔️ " + GetText(strs.banned_user))
 | 
			
		||||
                                                .AddField("ID", userId.ToString(), true));
 | 
			
		||||
                                             .WithOkColor()
 | 
			
		||||
                                             .WithTitle("⛔️ " + GetText(strs.banned_user))
 | 
			
		||||
                                             .AddField("ID", userId.ToString(), true));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                await Ban(user, msg);
 | 
			
		||||
@@ -500,7 +504,7 @@ public partial class Administration
 | 
			
		||||
                var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
 | 
			
		||||
                var embed = await _service.GetBanUserDmEmbed(Context, user, defaultMessage, msg, null);
 | 
			
		||||
                if (embed is not null)
 | 
			
		||||
                    await user.SendAsync(embed);
 | 
			
		||||
                    await Response().User(user).Text(embed).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -511,10 +515,10 @@ public partial class Administration
 | 
			
		||||
            await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
 | 
			
		||||
 | 
			
		||||
            var toSend = new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle("⛔️ " + GetText(strs.banned_user))
 | 
			
		||||
                            .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                            .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle("⛔️ " + GetText(strs.banned_user))
 | 
			
		||||
                         .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                         .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
 | 
			
		||||
            if (dmFailed)
 | 
			
		||||
                toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
 | 
			
		||||
@@ -594,19 +598,19 @@ public partial class Administration
 | 
			
		||||
        private async Task InternalBanMessageTest(string reason, TimeSpan? duration)
 | 
			
		||||
        {
 | 
			
		||||
            var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), reason));
 | 
			
		||||
            var embed = await _service.GetBanUserDmEmbed(Context,
 | 
			
		||||
            var smartText = await _service.GetBanUserDmEmbed(Context,
 | 
			
		||||
                (IGuildUser)ctx.User,
 | 
			
		||||
                defaultMessage,
 | 
			
		||||
                reason,
 | 
			
		||||
                duration);
 | 
			
		||||
 | 
			
		||||
            if (embed is null)
 | 
			
		||||
            if (smartText is null)
 | 
			
		||||
                await Response().Confirm(strs.banmsg_disabled).SendAsync();
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await ctx.User.SendAsync(embed);
 | 
			
		||||
                    await Response().User(ctx.User).Text(smartText).SendAsync();
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception)
 | 
			
		||||
                {
 | 
			
		||||
@@ -692,7 +696,7 @@ public partial class Administration
 | 
			
		||||
            {
 | 
			
		||||
                await Response()
 | 
			
		||||
                      .Channel(await user.CreateDMChannelAsync())
 | 
			
		||||
                      .Error(GetText(strs.sbdm(Format.Bold(ctx.Guild.Name), msg)))
 | 
			
		||||
                      .Error(strs.sbdm(Format.Bold(ctx.Guild.Name), msg))
 | 
			
		||||
                      .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
@@ -706,10 +710,10 @@ public partial class Administration
 | 
			
		||||
            catch { await ctx.Guild.RemoveBanAsync(user); }
 | 
			
		||||
 | 
			
		||||
            var toSend = new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle("☣ " + GetText(strs.sb_user))
 | 
			
		||||
                            .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                            .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle("☣ " + GetText(strs.sb_user))
 | 
			
		||||
                         .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                         .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
 | 
			
		||||
            if (dmFailed)
 | 
			
		||||
                toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
 | 
			
		||||
@@ -761,10 +765,10 @@ public partial class Administration
 | 
			
		||||
            await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
 | 
			
		||||
 | 
			
		||||
            var toSend = new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle(GetText(strs.kicked_user))
 | 
			
		||||
                            .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                            .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle(GetText(strs.kicked_user))
 | 
			
		||||
                         .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                         .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
 | 
			
		||||
            if (dmFailed)
 | 
			
		||||
                toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
 | 
			
		||||
@@ -792,9 +796,11 @@ public partial class Administration
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
 | 
			
		||||
                await user.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                                         .WithPendingColor()
 | 
			
		||||
                                         .WithDescription(dmMessage));
 | 
			
		||||
                await _sender.Response(user)
 | 
			
		||||
                             .Embed(new EmbedBuilder()
 | 
			
		||||
                                    .WithPendingColor()
 | 
			
		||||
                                    .WithDescription(dmMessage))
 | 
			
		||||
                             .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
@@ -804,10 +810,10 @@ public partial class Administration
 | 
			
		||||
            await user.SetTimeOutAsync(time.Time);
 | 
			
		||||
 | 
			
		||||
            var toSend = new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle("⏳ " + GetText(strs.timedout_user))
 | 
			
		||||
                            .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                            .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle("⏳ " + GetText(strs.timedout_user))
 | 
			
		||||
                         .AddField(GetText(strs.username), user.ToString(), true)
 | 
			
		||||
                         .AddField("ID", user.Id.ToString(), true);
 | 
			
		||||
 | 
			
		||||
            if (dmFailed)
 | 
			
		||||
                toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
 | 
			
		||||
@@ -865,9 +871,9 @@ public partial class Administration
 | 
			
		||||
                missStr = "-";
 | 
			
		||||
 | 
			
		||||
            var toSend = new EmbedBuilder()
 | 
			
		||||
                            .WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
 | 
			
		||||
                            .AddField(GetText(strs.invalid(missing.Count)), missStr)
 | 
			
		||||
                            .WithPendingColor();
 | 
			
		||||
                         .WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
 | 
			
		||||
                         .AddField(GetText(strs.invalid(missing.Count)), missStr)
 | 
			
		||||
                         .WithPendingColor();
 | 
			
		||||
 | 
			
		||||
            var banningMessage = await Response().Embed(toSend).SendAsync();
 | 
			
		||||
 | 
			
		||||
@@ -885,11 +891,11 @@ public partial class Administration
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await banningMessage.ModifyAsync(x => x.Embed = new EmbedBuilder()
 | 
			
		||||
                                                               .WithDescription(
 | 
			
		||||
                                                                   GetText(strs.mass_ban_completed(banning.Count())))
 | 
			
		||||
                                                               .AddField(GetText(strs.invalid(missing.Count)), missStr)
 | 
			
		||||
                                                               .WithOkColor()
 | 
			
		||||
                                                               .Build());
 | 
			
		||||
                                                            .WithDescription(
 | 
			
		||||
                                                                GetText(strs.mass_ban_completed(banning.Count())))
 | 
			
		||||
                                                            .AddField(GetText(strs.invalid(missing.Count)), missStr)
 | 
			
		||||
                                                            .WithOkColor()
 | 
			
		||||
                                                            .Build());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
@@ -910,10 +916,10 @@ public partial class Administration
 | 
			
		||||
 | 
			
		||||
            //send a message but don't wait for it
 | 
			
		||||
            var banningMessageTask = ctx.Channel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                                                               .WithDescription(
 | 
			
		||||
                                                                   GetText(strs.mass_kill_in_progress(bans.Count())))
 | 
			
		||||
                                                               .AddField(GetText(strs.invalid(missing)), missStr)
 | 
			
		||||
                                                               .WithPendingColor());
 | 
			
		||||
                                                            .WithDescription(
 | 
			
		||||
                                                                GetText(strs.mass_kill_in_progress(bans.Count())))
 | 
			
		||||
                                                            .AddField(GetText(strs.invalid(missing)), missStr)
 | 
			
		||||
                                                            .WithPendingColor());
 | 
			
		||||
 | 
			
		||||
            var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
 | 
			
		||||
            //do the banning
 | 
			
		||||
@@ -930,11 +936,11 @@ public partial class Administration
 | 
			
		||||
            var banningMessage = await banningMessageTask;
 | 
			
		||||
 | 
			
		||||
            await banningMessage.ModifyAsync(x => x.Embed = new EmbedBuilder()
 | 
			
		||||
                                                               .WithDescription(
 | 
			
		||||
                                                                   GetText(strs.mass_kill_completed(bans.Count())))
 | 
			
		||||
                                                               .AddField(GetText(strs.invalid(missing)), missStr)
 | 
			
		||||
                                                               .WithOkColor()
 | 
			
		||||
                                                               .Build());
 | 
			
		||||
                                                            .WithDescription(
 | 
			
		||||
                                                                GetText(strs.mass_kill_completed(bans.Count())))
 | 
			
		||||
                                                            .AddField(GetText(strs.invalid(missing)), missStr)
 | 
			
		||||
                                                            .WithOkColor()
 | 
			
		||||
                                                            .Build());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public class WarnExpireOptions : INadekoCommandOptions
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly IBot _bot;
 | 
			
		||||
    private readonly IPubSub _pubSub;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly IReplacementService _repSvc;
 | 
			
		||||
    private readonly Random _rng;
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +85,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ICommandHandler cmd,
 | 
			
		||||
        IPubSub pubSub,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        IReplacementService repSvc,
 | 
			
		||||
        IPermissionChecker permChecker)
 | 
			
		||||
    {
 | 
			
		||||
@@ -95,7 +95,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _bot = bot;
 | 
			
		||||
        _pubSub = pubSub;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _repSvc = repSvc;
 | 
			
		||||
        _permChecker = permChecker;
 | 
			
		||||
        _rng = new NadekoRandom();
 | 
			
		||||
@@ -265,8 +265,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
 | 
			
		||||
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await msg.Channel
 | 
			
		||||
                                     .Response(_strings, _eb)
 | 
			
		||||
                            await _sender.Response(msg.Channel)
 | 
			
		||||
                                     .Error(permissionMessage)
 | 
			
		||||
                                     .SendAsync();
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -169,7 +169,7 @@ public partial class Gambling
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    await Response()
 | 
			
		||||
                          .Confirm(GetText(strs.animal_race_join(ctx.User.Mention, user.Animal.Icon)))
 | 
			
		||||
                          .Confirm(strs.animal_race_join(ctx.User.Mention, user.Animal.Icon))
 | 
			
		||||
                          .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch (ArgumentOutOfRangeException)
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ public partial class Gambling
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await ctx.User.EmbedAsync(eb);
 | 
			
		||||
                await Response().User(ctx.User).Embed(eb).SendAsync();
 | 
			
		||||
                await ctx.OkAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,7 @@ public partial class Gambling
 | 
			
		||||
                        RepostCounter++;
 | 
			
		||||
                        if (RepostCounter == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            try { msg = await ctx.Channel.SendMessageAsync("", embed: (Embed)msg.Embeds.First()); }
 | 
			
		||||
                            try { msg = await Response().Embed(msg.Embeds.First().ToEmbedBuilder()).SendAsync(); }
 | 
			
		||||
                            catch { }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +12,15 @@ public class CurrencyEventsService : INService
 | 
			
		||||
    private readonly GamblingConfigService _configService;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, ICurrencyEvent> _events = new();
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public CurrencyEventsService(DiscordSocketClient client, ICurrencyService cs, GamblingConfigService configService)
 | 
			
		||||
    public CurrencyEventsService(DiscordSocketClient client, ICurrencyService cs, GamblingConfigService configService,
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _cs = cs;
 | 
			
		||||
        _configService = configService;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> TryCreateEventAsync(
 | 
			
		||||
@@ -34,9 +37,9 @@ public class CurrencyEventsService : INService
 | 
			
		||||
        ICurrencyEvent ce;
 | 
			
		||||
 | 
			
		||||
        if (type == CurrencyEvent.Type.Reaction)
 | 
			
		||||
            ce = new ReactionEvent(_client, _cs, g, ch, opts, _configService.Data, embed);
 | 
			
		||||
            ce = new ReactionEvent(_client, _cs, g, ch, opts, _configService.Data, _sender, embed);
 | 
			
		||||
        else if (type == CurrencyEvent.Type.GameStatus)
 | 
			
		||||
            ce = new GameStatusEvent(_client, _cs, g, ch, opts, embed);
 | 
			
		||||
            ce = new GameStatusEvent(_client, _cs, g, ch, opts, _sender, embed);
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ public class GameStatusEvent : ICurrencyEvent
 | 
			
		||||
    private readonly object _stopLock = new();
 | 
			
		||||
 | 
			
		||||
    private readonly object _potLock = new();
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public GameStatusEvent(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
@@ -43,6 +44,7 @@ public class GameStatusEvent : ICurrencyEvent
 | 
			
		||||
        SocketGuild g,
 | 
			
		||||
        ITextChannel ch,
 | 
			
		||||
        EventOptions opt,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        Func<CurrencyEvent.Type, EventOptions, long, EmbedBuilder> embedFunc)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
@@ -54,6 +56,7 @@ public class GameStatusEvent : ICurrencyEvent
 | 
			
		||||
        _isPotLimited = PotSize > 0;
 | 
			
		||||
        _channel = ch;
 | 
			
		||||
        _opts = opt;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        // generate code
 | 
			
		||||
        _code = new(_sneakyGameStatusChars.Shuffle().Take(5).ToArray());
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +109,7 @@ public class GameStatusEvent : ICurrencyEvent
 | 
			
		||||
 | 
			
		||||
    public async Task StartEvent()
 | 
			
		||||
    {
 | 
			
		||||
        msg = await _channel.EmbedAsync(GetEmbed(_opts.PotSize));
 | 
			
		||||
        msg = await _sender.Response(_channel).Embed(GetEmbed(_opts.PotSize)).SendAsync();
 | 
			
		||||
        await _client.SetGameAsync(_code);
 | 
			
		||||
        _client.MessageDeleted += OnMessageDeleted;
 | 
			
		||||
        _client.MessageReceived += HandleMessage;
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ public class ReactionEvent : ICurrencyEvent
 | 
			
		||||
    private readonly object _stopLock = new();
 | 
			
		||||
 | 
			
		||||
    private readonly object _potLock = new();
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public ReactionEvent(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
@@ -38,6 +39,7 @@ public class ReactionEvent : ICurrencyEvent
 | 
			
		||||
        ITextChannel ch,
 | 
			
		||||
        EventOptions opt,
 | 
			
		||||
        GamblingConfig config,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        Func<CurrencyEvent.Type, EventOptions, long, EmbedBuilder> embedFunc)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
@@ -51,6 +53,7 @@ public class ReactionEvent : ICurrencyEvent
 | 
			
		||||
        _noRecentlyJoinedServer = false;
 | 
			
		||||
        _opts = opt;
 | 
			
		||||
        _config = config;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _t = new(OnTimerTick, null, Timeout.InfiniteTimeSpan, TimeSpan.FromSeconds(2));
 | 
			
		||||
        if (_opts.Hours > 0)
 | 
			
		||||
@@ -102,7 +105,7 @@ public class ReactionEvent : ICurrencyEvent
 | 
			
		||||
            emote = parsedEmote;
 | 
			
		||||
        else
 | 
			
		||||
            emote = new Emoji(_config.Currency.Sign);
 | 
			
		||||
        msg = await _channel.EmbedAsync(GetEmbed(_opts.PotSize));
 | 
			
		||||
        msg = await _sender.Response(_channel).Embed(GetEmbed(_opts.PotSize)).SendAsync();
 | 
			
		||||
        await msg.AddReactionAsync(emote);
 | 
			
		||||
        _client.MessageDeleted += OnMessageDeleted;
 | 
			
		||||
        _client.ReactionAdded += HandleReaction;
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        var stats = await _gamblingTxTracker.GetAllAsync();
 | 
			
		||||
 | 
			
		||||
        var eb = new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
            .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        var str = "` Feature `|`   Bet  `|`Paid Out`|`  RoI  `\n";
 | 
			
		||||
        str += "――――――――――――――――――――\n";
 | 
			
		||||
@@ -119,15 +119,15 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
 | 
			
		||||
        // [21:03] Bob Page: Kinda remids me of US economy
 | 
			
		||||
        var embed = new EmbedBuilder()
 | 
			
		||||
                       .WithTitle(GetText(strs.economy_state))
 | 
			
		||||
                       .AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
 | 
			
		||||
                       .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
 | 
			
		||||
                       .AddField(GetText(strs.currency_planted), N(ec.Planted))
 | 
			
		||||
                       .AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
 | 
			
		||||
                       .AddField(GetText(strs.bot_currency), N(ec.Bot))
 | 
			
		||||
                       .AddField(GetText(strs.bank_accounts), N(ec.Bank))
 | 
			
		||||
                       .AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
 | 
			
		||||
                       .WithOkColor();
 | 
			
		||||
                    .WithTitle(GetText(strs.economy_state))
 | 
			
		||||
                    .AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
 | 
			
		||||
                    .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
 | 
			
		||||
                    .AddField(GetText(strs.currency_planted), N(ec.Planted))
 | 
			
		||||
                    .AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
 | 
			
		||||
                    .AddField(GetText(strs.bot_currency), N(ec.Bot))
 | 
			
		||||
                    .AddField(GetText(strs.bank_accounts), N(ec.Bank))
 | 
			
		||||
                    .AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        // ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
 | 
			
		||||
        await Response().Embed(embed).SendAsync();
 | 
			
		||||
@@ -151,7 +151,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
            GetText(strs.timely_time),
 | 
			
		||||
            ReminderType.Timely);
 | 
			
		||||
 | 
			
		||||
        await smc.RespondConfirmAsync(_eb, GetText(strs.remind_timely(tt)), ephemeral: true);
 | 
			
		||||
        await smc.RespondConfirmAsync(_sender, GetText(strs.remind_timely(tt)), ephemeral: true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoInteraction CreateRemindMeInteraction(int period)
 | 
			
		||||
@@ -311,9 +311,9 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var embed = new EmbedBuilder()
 | 
			
		||||
                       .WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
 | 
			
		||||
                                                            ?? $"{userId}")))
 | 
			
		||||
                       .WithOkColor();
 | 
			
		||||
                    .WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
 | 
			
		||||
                                                         ?? $"{userId}")))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        var sb = new StringBuilder();
 | 
			
		||||
        foreach (var tr in trs)
 | 
			
		||||
@@ -415,7 +415,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        await N(balance)
 | 
			
		||||
              .Pipe(strs.bank_balance)
 | 
			
		||||
              .Pipe(GetText)
 | 
			
		||||
              .Pipe(text => smc.RespondConfirmAsync(_eb, text, ephemeral: true));
 | 
			
		||||
              .Pipe(text => smc.RespondConfirmAsync(_sender, text, ephemeral: true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoInteraction CreateCashInteraction()
 | 
			
		||||
@@ -460,7 +460,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!await _cs.TransferAsync(_eb, ctx.User, receiver, amount, msg, N(amount)))
 | 
			
		||||
        if (!await _cs.TransferAsync(_sender, ctx.User, receiver, amount, msg, N(amount)))
 | 
			
		||||
        {
 | 
			
		||||
            await Response().Error(strs.not_enough(CurrencySign)).SendAsync();
 | 
			
		||||
            return;
 | 
			
		||||
@@ -732,10 +732,10 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var eb = new EmbedBuilder()
 | 
			
		||||
                    .WithAuthor(ctx.User)
 | 
			
		||||
                    .WithDescription(Format.Bold(str))
 | 
			
		||||
                    .AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
                 .WithAuthor(ctx.User)
 | 
			
		||||
                 .WithDescription(Format.Bold(str))
 | 
			
		||||
                 .AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
 | 
			
		||||
                 .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        await Response().Embed(eb).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
@@ -923,11 +923,11 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var eb = new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .WithDescription(sb.ToString())
 | 
			
		||||
                    .AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
 | 
			
		||||
                    .AddField(GetText(strs.won), $"{(long)result.Won}", true)
 | 
			
		||||
                    .WithAuthor(ctx.User);
 | 
			
		||||
                 .WithOkColor()
 | 
			
		||||
                 .WithDescription(sb.ToString())
 | 
			
		||||
                 .AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
 | 
			
		||||
                 .AddField(GetText(strs.won), $"{(long)result.Won}", true)
 | 
			
		||||
                 .WithAuthor(ctx.User);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        await Response().Embed(eb).SendAsync();
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,8 @@ public partial class Gambling
 | 
			
		||||
 | 
			
		||||
            using var uow = _db.GetDbContext();
 | 
			
		||||
            var entries = uow.GuildConfigsForId(ctx.Guild.Id,
 | 
			
		||||
                    set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items))
 | 
			
		||||
                .ShopEntries.ToIndexed();
 | 
			
		||||
                                 set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items))
 | 
			
		||||
                             .ShopEntries.ToIndexed();
 | 
			
		||||
            return ctx.SendPaginatedConfirmAsync(page,
 | 
			
		||||
                curPage =>
 | 
			
		||||
                {
 | 
			
		||||
@@ -116,7 +116,9 @@ public partial class Gambling
 | 
			
		||||
                var guser = (IGuildUser)ctx.User;
 | 
			
		||||
                if (!guser.RoleIds.Contains(reqRoleId))
 | 
			
		||||
                {
 | 
			
		||||
                    await Response().Error(strs.shop_item_req_role_unfulfilled(Format.Bold(role.ToString()))).SendAsync();
 | 
			
		||||
                    await Response()
 | 
			
		||||
                          .Error(strs.shop_item_req_role_unfulfilled(Format.Bold(role.ToString())))
 | 
			
		||||
                          .SendAsync();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -178,17 +180,20 @@ public partial class Gambling
 | 
			
		||||
                    await using (var uow = _db.GetDbContext())
 | 
			
		||||
                    {
 | 
			
		||||
                        uow.Set<ShopEntryItem>().Remove(item);
 | 
			
		||||
                        uow.SaveChanges();
 | 
			
		||||
                        await uow.SaveChangesAsync();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        await ctx.User.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
 | 
			
		||||
                            .AddField(GetText(strs.item), item.Text)
 | 
			
		||||
                            .AddField(GetText(strs.price), entry.Price.ToString(), true)
 | 
			
		||||
                            .AddField(GetText(strs.name), entry.Name, true));
 | 
			
		||||
                        await Response()
 | 
			
		||||
                              .User(ctx.User)
 | 
			
		||||
                              .Embed(new EmbedBuilder()
 | 
			
		||||
                                     .WithOkColor()
 | 
			
		||||
                                     .WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
 | 
			
		||||
                                     .AddField(GetText(strs.item), item.Text)
 | 
			
		||||
                                     .AddField(GetText(strs.price), entry.Price.ToString(), true)
 | 
			
		||||
                                     .AddField(GetText(strs.name), entry.Name, true))
 | 
			
		||||
                              .SendAsync();
 | 
			
		||||
 | 
			
		||||
                        await _cs.AddAsync(entry.AuthorId,
 | 
			
		||||
                            GetProfitAmount(entry.Price),
 | 
			
		||||
@@ -200,9 +205,9 @@ public partial class Gambling
 | 
			
		||||
                        await using (var uow = _db.GetDbContext())
 | 
			
		||||
                        {
 | 
			
		||||
                            var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
 | 
			
		||||
                                    set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                                        .ThenInclude(x => x.Items))
 | 
			
		||||
                                .ShopEntries);
 | 
			
		||||
                                                                                  set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                                                                                      .ThenInclude(x => x.Items))
 | 
			
		||||
                                                                              .ShopEntries);
 | 
			
		||||
                            entry = entries.ElementAtOrDefault(index);
 | 
			
		||||
                            if (entry is not null)
 | 
			
		||||
                            {
 | 
			
		||||
@@ -242,16 +247,16 @@ public partial class Gambling
 | 
			
		||||
                {
 | 
			
		||||
                    var cmd = entry.Command.Replace("%you%", ctx.User.Id.ToString());
 | 
			
		||||
                    var eb = new EmbedBuilder()
 | 
			
		||||
                        .WithPendingColor()
 | 
			
		||||
                        .WithTitle("Executing shop command")
 | 
			
		||||
                        .WithDescription(cmd);
 | 
			
		||||
                             .WithPendingColor()
 | 
			
		||||
                             .WithTitle("Executing shop command")
 | 
			
		||||
                             .WithDescription(cmd);
 | 
			
		||||
 | 
			
		||||
                    var msgTask = Response().Embed(eb).SendAsync();
 | 
			
		||||
 | 
			
		||||
                    await _cs.AddAsync(entry.AuthorId,
 | 
			
		||||
                        GetProfitAmount(entry.Price),
 | 
			
		||||
                        new("shop", "sell", entry.Name));
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    await _cmdHandler.TryRunCommand(guild,
 | 
			
		||||
                        channel,
 | 
			
		||||
                        new DoAsUserMessage(
 | 
			
		||||
@@ -264,9 +269,9 @@ public partial class Gambling
 | 
			
		||||
                    {
 | 
			
		||||
                        var pendingMsg = await msgTask;
 | 
			
		||||
                        await pendingMsg.EditAsync(SmartEmbedText.FromEmbed(eb
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle("Shop command executed")
 | 
			
		||||
                            .Build()));
 | 
			
		||||
                                                                            .WithOkColor()
 | 
			
		||||
                                                                            .WithTitle("Shop command executed")
 | 
			
		||||
                                                                            .Build()));
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
@@ -314,9 +319,9 @@ public partial class Gambling
 | 
			
		||||
            await using (var uow = _db.GetDbContext())
 | 
			
		||||
            {
 | 
			
		||||
                var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
 | 
			
		||||
                        set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                            .ThenInclude(x => x.Items))
 | 
			
		||||
                    .ShopEntries)
 | 
			
		||||
                                                                      set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                                                                          .ThenInclude(x => x.Items))
 | 
			
		||||
                                                                  .ShopEntries)
 | 
			
		||||
                {
 | 
			
		||||
                    entry
 | 
			
		||||
                };
 | 
			
		||||
@@ -346,9 +351,9 @@ public partial class Gambling
 | 
			
		||||
            await using (var uow = _db.GetDbContext())
 | 
			
		||||
            {
 | 
			
		||||
                var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
 | 
			
		||||
                        set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                            .ThenInclude(x => x.Items))
 | 
			
		||||
                    .ShopEntries)
 | 
			
		||||
                                                                      set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                                                                          .ThenInclude(x => x.Items))
 | 
			
		||||
                                                                  .ShopEntries)
 | 
			
		||||
                {
 | 
			
		||||
                    entry
 | 
			
		||||
                };
 | 
			
		||||
@@ -377,9 +382,9 @@ public partial class Gambling
 | 
			
		||||
            await using (var uow = _db.GetDbContext())
 | 
			
		||||
            {
 | 
			
		||||
                var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
 | 
			
		||||
                        set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                            .ThenInclude(x => x.Items))
 | 
			
		||||
                    .ShopEntries);
 | 
			
		||||
                                                                      set => set.Include(x => x.ShopEntries)
 | 
			
		||||
                                                                          .ThenInclude(x => x.Items))
 | 
			
		||||
                                                                  .ShopEntries);
 | 
			
		||||
                entry = entries.ElementAtOrDefault(index);
 | 
			
		||||
                if (entry is not null && (rightType = entry.Type == ShopEntryType.List))
 | 
			
		||||
                {
 | 
			
		||||
@@ -531,27 +536,27 @@ public partial class Gambling
 | 
			
		||||
            if (entry.Type == ShopEntryType.Role)
 | 
			
		||||
            {
 | 
			
		||||
                return embed
 | 
			
		||||
                    .AddField(GetText(strs.name),
 | 
			
		||||
                        GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name
 | 
			
		||||
                                                           ?? "MISSING_ROLE"))),
 | 
			
		||||
                        true)
 | 
			
		||||
                    .AddField(GetText(strs.price), N(entry.Price), true)
 | 
			
		||||
                    .AddField(GetText(strs.type), entry.Type.ToString(), true);
 | 
			
		||||
                       .AddField(GetText(strs.name),
 | 
			
		||||
                           GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name
 | 
			
		||||
                                                              ?? "MISSING_ROLE"))),
 | 
			
		||||
                           true)
 | 
			
		||||
                       .AddField(GetText(strs.price), N(entry.Price), true)
 | 
			
		||||
                       .AddField(GetText(strs.type), entry.Type.ToString(), true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (entry.Type == ShopEntryType.List)
 | 
			
		||||
            {
 | 
			
		||||
                return embed.AddField(GetText(strs.name), entry.Name, true)
 | 
			
		||||
                    .AddField(GetText(strs.price), N(entry.Price), true)
 | 
			
		||||
                    .AddField(GetText(strs.type), GetText(strs.random_unique_item), true);
 | 
			
		||||
                            .AddField(GetText(strs.price), N(entry.Price), true)
 | 
			
		||||
                            .AddField(GetText(strs.type), GetText(strs.random_unique_item), true);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            else if (entry.Type == ShopEntryType.Command)
 | 
			
		||||
            {
 | 
			
		||||
                return embed
 | 
			
		||||
                    .AddField(GetText(strs.name), Format.Code(entry.Command), true)
 | 
			
		||||
                    .AddField(GetText(strs.price), N(entry.Price), true)
 | 
			
		||||
                    .AddField(GetText(strs.type), entry.Type.ToString(), true);
 | 
			
		||||
                       .AddField(GetText(strs.name), Format.Code(entry.Command), true)
 | 
			
		||||
                       .AddField(GetText(strs.price), N(entry.Price), true)
 | 
			
		||||
                       .AddField(GetText(strs.type), entry.Type.ToString(), true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //else if (entry.Type == ShopEntryType.Infinite_List)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,7 @@ public class ChatterBotService : IExecOnMessage
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly IPermissionChecker _perms;
 | 
			
		||||
    private readonly CommandHandler _cmd;
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly IBotCredentials _creds;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IHttpClientFactory _httpFactory;
 | 
			
		||||
    private readonly IPatronageService _ps;
 | 
			
		||||
    private readonly GamesConfigService _gcs;
 | 
			
		||||
@@ -33,10 +31,8 @@ public class ChatterBotService : IExecOnMessage
 | 
			
		||||
        IPermissionChecker perms,
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        CommandHandler cmd,
 | 
			
		||||
        IBotStrings strings,
 | 
			
		||||
        IHttpClientFactory factory,
 | 
			
		||||
        IBotCredentials creds,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IPatronageService ps,
 | 
			
		||||
        GamesConfigService gcs,
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
@@ -44,14 +40,12 @@ public class ChatterBotService : IExecOnMessage
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _perms = perms;
 | 
			
		||||
        _cmd = cmd;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _creds = creds;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _httpFactory = factory;
 | 
			
		||||
        _ps = ps;
 | 
			
		||||
        _perms = perms;
 | 
			
		||||
        _gcs = gcs;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _flKey = new FeatureLimitKey()
 | 
			
		||||
        {
 | 
			
		||||
@@ -166,8 +160,7 @@ public class ChatterBotService : IExecOnMessage
 | 
			
		||||
                {
 | 
			
		||||
                    if (ql.Quota == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        await channel
 | 
			
		||||
                              .Response(_strings, _eb)
 | 
			
		||||
                        await _sender.Response(channel)
 | 
			
		||||
                              .Error(null,
 | 
			
		||||
                                  text:
 | 
			
		||||
                                  "In order to use the cleverbot feature, the owner of this server should be [Patron Tier X](https://patreon.com/join/nadekobot) on patreon.",
 | 
			
		||||
@@ -178,7 +171,7 @@ public class ChatterBotService : IExecOnMessage
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    await channel.Response(_strings, _eb)
 | 
			
		||||
                    await _sender.Response(channel)
 | 
			
		||||
                                 .Error(
 | 
			
		||||
                                     null!,
 | 
			
		||||
                                     $"You've reached your quota limit of **{ql.Quota}** responses {ql.QuotaPeriod.ToFullName()} for the cleverbot feature.",
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ public partial class Games
 | 
			
		||||
            /-\
 | 
			
		||||
            """;
 | 
			
		||||
 | 
			
		||||
        public static EmbedBuilder GetEmbed(IEmbedBuilderService eb, HangmanGame.State state)
 | 
			
		||||
        public static EmbedBuilder GetEmbed(HangmanGame.State state)
 | 
			
		||||
        {
 | 
			
		||||
            if (state.Phase == HangmanGame.Phase.Running)
 | 
			
		||||
            {
 | 
			
		||||
@@ -60,7 +60,7 @@ public partial class Games
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var eb = GetEmbed(_eb, hangman);
 | 
			
		||||
            var eb = GetEmbed(hangman);
 | 
			
		||||
            eb.WithDescription(GetText(strs.hangman_game_started));
 | 
			
		||||
            await Response().Embed(eb).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ public sealed class HangmanService : IHangmanService, IExecNoCommand
 | 
			
		||||
{
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, HangmanGame> _hangmanGames = new();
 | 
			
		||||
    private readonly IHangmanSource _source;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly GamesConfigService _gcs;
 | 
			
		||||
    private readonly ICurrencyService _cs;
 | 
			
		||||
    private readonly IMemoryCache _cdCache;
 | 
			
		||||
@@ -17,13 +17,13 @@ public sealed class HangmanService : IHangmanService, IExecNoCommand
 | 
			
		||||
 | 
			
		||||
    public HangmanService(
 | 
			
		||||
        IHangmanSource source,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        GamesConfigService gcs,
 | 
			
		||||
        ICurrencyService cs,
 | 
			
		||||
        IMemoryCache cdCache)
 | 
			
		||||
    {
 | 
			
		||||
        _source = source;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _gcs = gcs;
 | 
			
		||||
        _cs = cs;
 | 
			
		||||
        _cdCache = cdCache;
 | 
			
		||||
@@ -116,7 +116,7 @@ public sealed class HangmanService : IHangmanService, IExecNoCommand
 | 
			
		||||
        string content,
 | 
			
		||||
        HangmanGame.State state)
 | 
			
		||||
    {
 | 
			
		||||
        var embed = Games.HangmanCommands.GetEmbed(_eb, state);
 | 
			
		||||
        var embed = Games.HangmanCommands.GetEmbed(state);
 | 
			
		||||
        if (state.GuessResult == HangmanGame.GuessResult.Guess)
 | 
			
		||||
            embed.WithDescription($"{user} guessed the letter {content}!").WithOkColor();
 | 
			
		||||
        else if (state.GuessResult == HangmanGame.GuessResult.Incorrect && state.Failed)
 | 
			
		||||
@@ -131,6 +131,6 @@ public sealed class HangmanService : IHangmanService, IExecNoCommand
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(state.ImageUrl) && Uri.IsWellFormedUriString(state.ImageUrl, UriKind.Absolute))
 | 
			
		||||
            embed.WithImageUrl(state.ImageUrl);
 | 
			
		||||
 | 
			
		||||
        return channel.EmbedAsync(embed);
 | 
			
		||||
        return _sender.Response(channel).Embed(embed).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -78,10 +78,11 @@ public class TypingGame
 | 
			
		||||
 | 
			
		||||
            var time = _options.StartTime;
 | 
			
		||||
 | 
			
		||||
            var msg = await Channel.SendMessageAsync($"Starting new typing contest in **{time}**...");
 | 
			
		||||
            var msg = await _sender.Response(Channel).Confirm($"Starting new typing contest in **{time}**...").SendAsync();
 | 
			
		||||
 | 
			
		||||
            do
 | 
			
		||||
            {
 | 
			
		||||
                // todo fix all modifies
 | 
			
		||||
                await Task.Delay(2000);
 | 
			
		||||
                time -= 2;
 | 
			
		||||
                try { await msg.ModifyAsync(m => m.Content = $"Starting new typing contest in **{time}**.."); }
 | 
			
		||||
@@ -144,13 +145,15 @@ public class TypingGame
 | 
			
		||||
                    var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60;
 | 
			
		||||
                    _finishedUserIds.Add(msg.Author.Id);
 | 
			
		||||
 | 
			
		||||
                    var embed = new EmbedBuilder()
 | 
			
		||||
                                .WithOkColor()
 | 
			
		||||
                                .WithTitle($"{msg.Author} finished the race!")
 | 
			
		||||
                                .AddField("Place", $"#{_finishedUserIds.Count}", true)
 | 
			
		||||
                                .AddField("WPM", $"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*", true)
 | 
			
		||||
                                .AddField("Errors", distance.ToString(), true);
 | 
			
		||||
                    
 | 
			
		||||
                    await _sender.Response(Channel)
 | 
			
		||||
                                 .Embed(eb => new EmbedBuilder()
 | 
			
		||||
                                                  .WithOkColor()
 | 
			
		||||
                                                  .WithTitle($"{msg.Author} finished the race!")
 | 
			
		||||
                                                  .AddField("Place", $"#{_finishedUserIds.Count}", true)
 | 
			
		||||
                                                  .AddField("WPM", $"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*", true)
 | 
			
		||||
                                                  .AddField("Errors", distance.ToString(), true))
 | 
			
		||||
                                 .Embed(embed)
 | 
			
		||||
                                 .SendAsync();
 | 
			
		||||
 | 
			
		||||
                    if (_finishedUserIds.Count % 4 == 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ public class TicTacToe
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly Options _options;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public TicTacToe(
 | 
			
		||||
        IBotStrings strings,
 | 
			
		||||
@@ -34,13 +34,13 @@ public class TicTacToe
 | 
			
		||||
        ITextChannel channel,
 | 
			
		||||
        IGuildUser firstUser,
 | 
			
		||||
        Options options,
 | 
			
		||||
        IEmbedBuilderService eb)
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _channel = channel;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _options = options;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _users = new[] { firstUser, null };
 | 
			
		||||
        _state = new int?[,] { { null, null, null }, { null, null, null }, { null, null, null } };
 | 
			
		||||
@@ -115,13 +115,13 @@ public class TicTacToe
 | 
			
		||||
    {
 | 
			
		||||
        if (phase is Phase.Started or Phase.Ended)
 | 
			
		||||
        {
 | 
			
		||||
            await _channel.Response(_strings, _eb).Error(user.Mention + GetText(strs.ttt_already_running)).SendAsync();
 | 
			
		||||
            await _sender.Response(_channel).Error(user.Mention + GetText(strs.ttt_already_running)).SendAsync();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_users[0] == user)
 | 
			
		||||
        {
 | 
			
		||||
            await _channel.Response(_strings, _eb).Error(user.Mention + GetText(strs.ttt_against_yourself)).SendAsync();
 | 
			
		||||
            await _sender.Response(_channel).Error(user.Mention + GetText(strs.ttt_against_yourself)).SendAsync();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -144,7 +144,7 @@ public class TicTacToe
 | 
			
		||||
                        var del = previousMessage?.DeleteAsync();
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await _channel.EmbedAsync(GetEmbed(GetText(strs.ttt_time_expired)));
 | 
			
		||||
                            await _sender.Response(_channel).Embed(GetEmbed(GetText(strs.ttt_time_expired))).SendAsync();
 | 
			
		||||
                            if (del is not null)
 | 
			
		||||
                                await del;
 | 
			
		||||
                        }
 | 
			
		||||
@@ -166,7 +166,7 @@ public class TicTacToe
 | 
			
		||||
        _client.MessageReceived += Client_MessageReceived;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        previousMessage = await _channel.EmbedAsync(GetEmbed(GetText(strs.game_started)));
 | 
			
		||||
        previousMessage = await _sender.Response(_channel).Embed(GetEmbed(GetText(strs.game_started))).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private bool IsDraw()
 | 
			
		||||
@@ -259,7 +259,7 @@ public class TicTacToe
 | 
			
		||||
                    {
 | 
			
		||||
                        var del1 = msg.DeleteAsync();
 | 
			
		||||
                        var del2 = previousMessage?.DeleteAsync();
 | 
			
		||||
                        try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); }
 | 
			
		||||
                        try { previousMessage = await _sender.Response(_channel).Embed(GetEmbed(reason)).SendAsync(); }
 | 
			
		||||
                        catch { }
 | 
			
		||||
 | 
			
		||||
                        try { await del1; }
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ public partial class Games
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                game = new(Strings, _client, channel, (IGuildUser)ctx.User, options, _eb);
 | 
			
		||||
                game = new(Strings, _client, channel, (IGuildUser)ctx.User, options, _sender);
 | 
			
		||||
                _service.TicTacToeGames.Add(channel.Id, game);
 | 
			
		||||
                await Response().Confirm(strs.ttt_created).SendAsync();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ public partial class Games
 | 
			
		||||
 | 
			
		||||
            if (_service.RunningTrivias.TryGetValue(ctx.Guild.Id, out var tg))
 | 
			
		||||
            {
 | 
			
		||||
                await Response().Error(GetText(strs.trivia_already_running)).SendAsync();
 | 
			
		||||
                await Response().Error(strs.trivia_already_running).SendAsync();
 | 
			
		||||
                await tg.TriggerQuestionAsync();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -376,7 +376,7 @@ public sealed class Help : NadekoModule<HelpService>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var embed = _cus.GetCommandHelp(com, ctx.Guild);
 | 
			
		||||
        await channel.EmbedAsync(embed);
 | 
			
		||||
        await _sender.Response(channel).Embed(embed).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
@@ -510,7 +510,7 @@ public sealed class Help : NadekoModule<HelpService>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private Task SelfhostAction(SocketMessageComponent smc, object _)
 | 
			
		||||
        => smc.RespondConfirmAsync(_eb,
 | 
			
		||||
        => smc.RespondConfirmAsync(_sender,
 | 
			
		||||
            """
 | 
			
		||||
            - In case you don't want or cannot Donate to NadekoBot project, but you
 | 
			
		||||
            - NadekoBot is a completely free and fully [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own "selfhosted" instance on your computer or server for free.
 | 
			
		||||
 
 | 
			
		||||
@@ -51,10 +51,10 @@ public sealed partial class Music
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var embed = new EmbedBuilder()
 | 
			
		||||
                           .WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL)
 | 
			
		||||
                           .WithDescription(string.Join("\n",
 | 
			
		||||
                               playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count)))))
 | 
			
		||||
                           .WithOkColor();
 | 
			
		||||
                        .WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL)
 | 
			
		||||
                        .WithDescription(string.Join("\n",
 | 
			
		||||
                            playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count)))))
 | 
			
		||||
                        .WithOkColor();
 | 
			
		||||
 | 
			
		||||
            await Response().Embed(embed).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
@@ -111,7 +111,9 @@ public sealed partial class Music
 | 
			
		||||
                        mpl.Songs.Skip(cur * 20)
 | 
			
		||||
                           .Take(20)
 | 
			
		||||
                           .Select(x => $"`{++i}.` [{x.Title.TrimTo(45)}]({x.Query}) `{x.Provider}`"));
 | 
			
		||||
                    return new EmbedBuilder().WithTitle($"\"{mpl.Name}\" by {mpl.Author}").WithOkColor().WithDescription(str);
 | 
			
		||||
                    return new EmbedBuilder().WithTitle($"\"{mpl.Name}\" by {mpl.Author}")
 | 
			
		||||
                                             .WithOkColor()
 | 
			
		||||
                                             .WithDescription(str);
 | 
			
		||||
                },
 | 
			
		||||
                mpl.Songs.Count,
 | 
			
		||||
                20);
 | 
			
		||||
@@ -151,11 +153,13 @@ public sealed partial class Music
 | 
			
		||||
                await uow.SaveChangesAsync();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await Response().Embed(new EmbedBuilder()
 | 
			
		||||
                                .WithOkColor()
 | 
			
		||||
                                .WithTitle(GetText(strs.playlist_saved))
 | 
			
		||||
                                .AddField(GetText(strs.name), name)
 | 
			
		||||
                                .AddField(GetText(strs.id), playlist.Id.ToString())).SendAsync();
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Embed(new EmbedBuilder()
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle(GetText(strs.playlist_saved))
 | 
			
		||||
                         .AddField(GetText(strs.name), name)
 | 
			
		||||
                         .AddField(GetText(strs.id), playlist.Id.ToString()))
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
@@ -208,8 +212,9 @@ public sealed partial class Music
 | 
			
		||||
                IUserMessage msg = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    msg = await ctx.Channel.SendMessageAsync(
 | 
			
		||||
                        GetText(strs.attempting_to_queue(Format.Bold(mpl.Songs.Count.ToString()))));
 | 
			
		||||
                    msg = await Response()
 | 
			
		||||
                                .Pending(strs.attempting_to_queue(Format.Bold(mpl.Songs.Count.ToString())))
 | 
			
		||||
                                .SendAsync();
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception)
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ public sealed class MusicService : IMusicService
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly IGoogleApiService _googleApiService;
 | 
			
		||||
    private readonly YtLoader _ytLoader;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, IMusicPlayer> _players;
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, (ITextChannel Default, ITextChannel? Override)> _outputChannels;
 | 
			
		||||
@@ -31,7 +31,7 @@ public sealed class MusicService : IMusicService
 | 
			
		||||
        IBotStrings strings,
 | 
			
		||||
        IGoogleApiService googleApiService,
 | 
			
		||||
        YtLoader ytLoader,
 | 
			
		||||
        IEmbedBuilderService eb)
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _voiceStateService = voiceStateService;
 | 
			
		||||
        _trackResolveProvider = trackResolveProvider;
 | 
			
		||||
@@ -42,7 +42,7 @@ public sealed class MusicService : IMusicService
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _googleApiService = googleApiService;
 | 
			
		||||
        _ytLoader = ytLoader;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _players = new();
 | 
			
		||||
        _outputChannels = new ConcurrentDictionary<ulong, (ITextChannel, ITextChannel?)>();
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ public sealed class CurrencyRewardService : INService, IDisposable
 | 
			
		||||
    private readonly ICurrencyService _cs;
 | 
			
		||||
    private readonly IPatronageService _ps;
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly GamblingConfigService _config;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
 | 
			
		||||
@@ -21,14 +21,14 @@ public sealed class CurrencyRewardService : INService, IDisposable
 | 
			
		||||
        ICurrencyService cs,
 | 
			
		||||
        IPatronageService ps,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        GamblingConfigService config,
 | 
			
		||||
        DiscordSocketClient client)
 | 
			
		||||
    {
 | 
			
		||||
        _cs = cs;
 | 
			
		||||
        _ps = ps;
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _config = config;
 | 
			
		||||
        _client = client;
 | 
			
		||||
 | 
			
		||||
@@ -175,7 +175,7 @@ public sealed class CurrencyRewardService : INService, IDisposable
 | 
			
		||||
                        .WithOkColor()
 | 
			
		||||
                        .WithDescription(message);
 | 
			
		||||
            
 | 
			
		||||
            await user.EmbedAsync(eb);
 | 
			
		||||
            await _sender.Response(user).Embed(eb).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,10 +31,12 @@ public partial class Patronage : NadekoModule
 | 
			
		||||
        _ = ctx.Channel.TriggerTypingAsync();
 | 
			
		||||
        var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message);
 | 
			
		||||
 | 
			
		||||
        await Response().Confirm(strs.patron_msg_sent(
 | 
			
		||||
            Format.Code(tierAndHigher.ToString()),
 | 
			
		||||
            Format.Bold(result.Success.ToString()),
 | 
			
		||||
            Format.Bold(result.Failed.ToString()))).SendAsync();
 | 
			
		||||
        await Response()
 | 
			
		||||
              .Confirm(strs.patron_msg_sent(
 | 
			
		||||
                  Format.Code(tierAndHigher.ToString()),
 | 
			
		||||
                  Format.Bold(result.Success.ToString()),
 | 
			
		||||
                  Format.Bold(result.Failed.ToString())))
 | 
			
		||||
              .SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // [OwnerOnly]
 | 
			
		||||
@@ -69,9 +71,9 @@ public partial class Patronage : NadekoModule
 | 
			
		||||
        var quotaStats = await _service.GetUserQuotaStatistic(user.Id);
 | 
			
		||||
 | 
			
		||||
        var eb = new EmbedBuilder()
 | 
			
		||||
            .WithAuthor(user)
 | 
			
		||||
            .WithTitle(GetText(strs.patron_info))
 | 
			
		||||
            .WithOkColor();
 | 
			
		||||
                 .WithAuthor(user)
 | 
			
		||||
                 .WithTitle(GetText(strs.patron_info))
 | 
			
		||||
                 .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        if (quotaStats.Commands.Count == 0
 | 
			
		||||
            && quotaStats.Groups.Count == 0
 | 
			
		||||
@@ -82,7 +84,7 @@ public partial class Patronage : NadekoModule
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true)
 | 
			
		||||
                .AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true);
 | 
			
		||||
              .AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true);
 | 
			
		||||
 | 
			
		||||
            if (patron.Tier != PatronTier.None)
 | 
			
		||||
                eb.AddField(GetText(strs.expires), patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(), true);
 | 
			
		||||
@@ -114,7 +116,7 @@ public partial class Patronage : NadekoModule
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ctx.User.EmbedAsync(eb);
 | 
			
		||||
            await Response().User(ctx.User).Embed(eb).SendAsync();
 | 
			
		||||
            _ = ctx.OkAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,6 @@ public sealed class PatronageService
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly ISubscriptionHandler _subsHandler;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
 | 
			
		||||
    private static readonly TypedKey<long> _quotaKey
 | 
			
		||||
        = new($"quota:last_hourly_reset");
 | 
			
		||||
@@ -43,7 +42,6 @@ public sealed class PatronageService
 | 
			
		||||
        DbService db,
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ISubscriptionHandler subsHandler,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IBotCache cache,
 | 
			
		||||
        IBotCredsProvider creds,
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
@@ -52,10 +50,9 @@ public sealed class PatronageService
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _subsHandler = subsHandler;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _cache = cache;
 | 
			
		||||
        _creds = creds;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task OnReadyAsync()
 | 
			
		||||
@@ -310,12 +307,12 @@ public sealed class PatronageService
 | 
			
		||||
            ins =>
 | 
			
		||||
            {
 | 
			
		||||
                var eb = new EmbedBuilder()
 | 
			
		||||
                            .WithPendingColor()
 | 
			
		||||
                            .WithTitle("Insufficient Patron Tier")
 | 
			
		||||
                            .AddField("For", $"{ins.FeatureType}: `{ins.Feature}`", true)
 | 
			
		||||
                            .AddField("Required Tier",
 | 
			
		||||
                                $"[{ins.RequiredTier.ToFullName()}](https://patreon.com/join/nadekobot)",
 | 
			
		||||
                                true);
 | 
			
		||||
                         .WithPendingColor()
 | 
			
		||||
                         .WithTitle("Insufficient Patron Tier")
 | 
			
		||||
                         .AddField("For", $"{ins.FeatureType}: `{ins.Feature}`", true)
 | 
			
		||||
                         .AddField("Required Tier",
 | 
			
		||||
                             $"[{ins.RequiredTier.ToFullName()}](https://patreon.com/join/nadekobot)",
 | 
			
		||||
                             true);
 | 
			
		||||
 | 
			
		||||
                if (ctx.Guild is null || ctx.Guild?.OwnerId == ctx.User.Id)
 | 
			
		||||
                    eb.WithDescription("You don't have the sufficent Patron Tier to run this command.")
 | 
			
		||||
@@ -333,15 +330,15 @@ public sealed class PatronageService
 | 
			
		||||
                               .Embed(eb)
 | 
			
		||||
                               .SendAsync();
 | 
			
		||||
                else
 | 
			
		||||
                    _ = ctx.User.EmbedAsync(eb);
 | 
			
		||||
                    _ = _sender.Response(ctx).User(ctx.User).Embed(eb).SendAsync();
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            },
 | 
			
		||||
            quota =>
 | 
			
		||||
            {
 | 
			
		||||
                var eb = new EmbedBuilder()
 | 
			
		||||
                            .WithPendingColor()
 | 
			
		||||
                            .WithTitle("Quota Limit Reached");
 | 
			
		||||
                         .WithPendingColor()
 | 
			
		||||
                         .WithTitle("Quota Limit Reached");
 | 
			
		||||
 | 
			
		||||
                if (quota.IsOwnQuota || ctx.User.Id == ownerId)
 | 
			
		||||
                {
 | 
			
		||||
@@ -369,7 +366,7 @@ public sealed class PatronageService
 | 
			
		||||
                               .Embed(eb)
 | 
			
		||||
                               .SendAsync();
 | 
			
		||||
                else
 | 
			
		||||
                    _ = ctx.User.EmbedAsync(eb);
 | 
			
		||||
                    _ = _sender.Response(ctx).User(ctx.User).Embed(eb).SendAsync();
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            });
 | 
			
		||||
@@ -782,30 +779,30 @@ public sealed class PatronageService
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var eb = new EmbedBuilder()
 | 
			
		||||
                        .WithOkColor()
 | 
			
		||||
                        .WithTitle("❤️ Thank you for supporting NadekoBot! ❤️")
 | 
			
		||||
                        .WithDescription(
 | 
			
		||||
                            "Your donation has been processed and you will receive the rewards shortly.\n"
 | 
			
		||||
                            + "You can visit <https://www.patreon.com/join/nadekobot> to see rewards for your tier. 🎉")
 | 
			
		||||
                        .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
 | 
			
		||||
                        .AddField("Pledge", $"**{patron.Amount / 100.0f:N1}$**", true)
 | 
			
		||||
                        .AddField("Expires",
 | 
			
		||||
                            patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(),
 | 
			
		||||
                            true)
 | 
			
		||||
                        .AddField("Instructions",
 | 
			
		||||
                            """
 | 
			
		||||
                            *- Within the next **1-2 minutes** you will have all of the benefits of the Tier you've subscribed to.*
 | 
			
		||||
                            *- You can check your benefits on <https://www.patreon.com/join/nadekobot>*
 | 
			
		||||
                            *- You can use the `.patron` command in this chat to check your current quota usage for the Patron-only commands*
 | 
			
		||||
                            *- **ALL** of the servers that you **own** will enjoy your Patron benefits.*
 | 
			
		||||
                            *- You can use any of the commands available in your tier on any server (assuming you have sufficient permissions to run those commands)*
 | 
			
		||||
                            *- Any user in any of your servers can use Patron-only commands, but they will spend **your quota**, which is why it's recommended to use Nadeko's command cooldown system (.h .cmdcd) or permission system to limit the command usage for your server members.*
 | 
			
		||||
                            *- Permission guide can be found here if you're not familiar with it: <https://nadekobot.readthedocs.io/en/latest/permissions-system/>*
 | 
			
		||||
                            """,
 | 
			
		||||
                            inline: false)
 | 
			
		||||
                        .WithFooter($"platform id: {patron.UniquePlatformUserId}");
 | 
			
		||||
                     .WithOkColor()
 | 
			
		||||
                     .WithTitle("❤️ Thank you for supporting NadekoBot! ❤️")
 | 
			
		||||
                     .WithDescription(
 | 
			
		||||
                         "Your donation has been processed and you will receive the rewards shortly.\n"
 | 
			
		||||
                         + "You can visit <https://www.patreon.com/join/nadekobot> to see rewards for your tier. 🎉")
 | 
			
		||||
                     .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
 | 
			
		||||
                     .AddField("Pledge", $"**{patron.Amount / 100.0f:N1}$**", true)
 | 
			
		||||
                     .AddField("Expires",
 | 
			
		||||
                         patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(),
 | 
			
		||||
                         true)
 | 
			
		||||
                     .AddField("Instructions",
 | 
			
		||||
                         """
 | 
			
		||||
                         *- Within the next **1-2 minutes** you will have all of the benefits of the Tier you've subscribed to.*
 | 
			
		||||
                         *- You can check your benefits on <https://www.patreon.com/join/nadekobot>*
 | 
			
		||||
                         *- You can use the `.patron` command in this chat to check your current quota usage for the Patron-only commands*
 | 
			
		||||
                         *- **ALL** of the servers that you **own** will enjoy your Patron benefits.*
 | 
			
		||||
                         *- You can use any of the commands available in your tier on any server (assuming you have sufficient permissions to run those commands)*
 | 
			
		||||
                         *- Any user in any of your servers can use Patron-only commands, but they will spend **your quota**, which is why it's recommended to use Nadeko's command cooldown system (.h .cmdcd) or permission system to limit the command usage for your server members.*
 | 
			
		||||
                         *- Permission guide can be found here if you're not familiar with it: <https://nadekobot.readthedocs.io/en/latest/permissions-system/>*
 | 
			
		||||
                         """,
 | 
			
		||||
                         inline: false)
 | 
			
		||||
                     .WithFooter($"platform id: {patron.UniquePlatformUserId}");
 | 
			
		||||
 | 
			
		||||
            await user.EmbedAsync(eb);
 | 
			
		||||
            await _sender.Response(user).Embed(eb).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
@@ -830,7 +827,7 @@ public sealed class PatronageService
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var user = await _client.GetUserAsync(patron.UserId);
 | 
			
		||||
                await user.SendAsync(text);
 | 
			
		||||
                await _sender.Response(user).Text(text).SendAsync();
 | 
			
		||||
                ++succ;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ public class PermissionService : IExecPreCommand, INService
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly CommandHandler _cmd;
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public PermissionService(
 | 
			
		||||
@@ -25,13 +24,11 @@ public class PermissionService : IExecPreCommand, INService
 | 
			
		||||
        DbService db,
 | 
			
		||||
        CommandHandler cmd,
 | 
			
		||||
        IBotStrings strings,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _cmd = cmd;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        using var uow = _db.GetDbContext();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ using System.Text.Json;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Searches;
 | 
			
		||||
 | 
			
		||||
// todo fix stock
 | 
			
		||||
public sealed class DefaultStockDataService : IStockDataService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IHttpClientFactory _httpClientFactory;
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ public class FeedsService : INService
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly ConcurrentDictionary<string, List<FeedSub>> _subs;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<string, DateTime> _lastPosts = new();
 | 
			
		||||
    private readonly Dictionary<string, uint> _errorCounters = new();
 | 
			
		||||
@@ -23,7 +23,7 @@ public class FeedsService : INService
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        IEmbedBuilderService eb)
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
 | 
			
		||||
@@ -42,7 +42,7 @@ public class FeedsService : INService
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(TrackFeeds);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ public partial class Searches
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            memeUrl += ".png";
 | 
			
		||||
            await ctx.Channel.SendMessageAsync(memeUrl);
 | 
			
		||||
            await Response().Text(memeUrl).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static string Replace(string input)
 | 
			
		||||
 
 | 
			
		||||
@@ -165,7 +165,7 @@ public partial class Searches
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await AddYoutubeUrlToCacheAsync(query, result.Url);
 | 
			
		||||
            await ctx.Channel.SendMessageAsync(result.Url);
 | 
			
		||||
            await Response().Text(result.Url).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
//     [Cmd]
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,7 @@ public partial class Searches : NadekoModule<SearchesService>
 | 
			
		||||
                 .AddField(GetText(strs.location), string.Join('\n', data.Address.Split(", ")), true)
 | 
			
		||||
                 .AddField(GetText(strs.timezone), data.TimeZoneName, true);
 | 
			
		||||
 | 
			
		||||
        await ctx.Channel.SendMessageAsync(embed: eb.Build());
 | 
			
		||||
        await Response().Embed(eb).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
@@ -441,7 +441,7 @@ public partial class Searches : NadekoModule<SearchesService>
 | 
			
		||||
        if (data.Query.Pages[0].Missing || string.IsNullOrWhiteSpace(data.Query.Pages[0].FullUrl))
 | 
			
		||||
            await Response().Error(strs.wiki_page_not_found).SendAsync();
 | 
			
		||||
        else
 | 
			
		||||
            await ctx.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl);
 | 
			
		||||
            await Response().Text(data.Query.Pages[0].FullUrl).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
@@ -514,7 +514,7 @@ public partial class Searches : NadekoModule<SearchesService>
 | 
			
		||||
            var url = Uri.EscapeDataString($"https://{target}.fandom.com/wiki/{title}");
 | 
			
		||||
            var response = $@"`{GetText(strs.title)}` {title.SanitizeMentions()}
 | 
			
		||||
`{GetText(strs.url)}:` {url}";
 | 
			
		||||
            await ctx.Channel.SendMessageAsync(response);
 | 
			
		||||
            await Response().Text(response).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
@@ -575,7 +575,7 @@ public partial class Searches : NadekoModule<SearchesService>
 | 
			
		||||
        //    .AddField(GetText(strs.price), gameData.IsFree ? GetText(strs.FREE) : game, true)
 | 
			
		||||
        //    .AddField(GetText(strs.links), gameData.GetGenresString(), true)
 | 
			
		||||
        //    .WithFooter(GetText(strs.recommendations(gameData.TotalRecommendations)));
 | 
			
		||||
        await ctx.Channel.SendMessageAsync($"https://store.steampowered.com/app/{appId}");
 | 
			
		||||
        await Response().Text($"https://store.steampowered.com/app/{appId}").SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<bool> ValidateQuery([MaybeNullWhen(false)] string query)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
    private readonly ConcurrentHashSet<ulong> _deleteOnOfflineServers;
 | 
			
		||||
 | 
			
		||||
    private readonly IPubSub _pubSub;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly SearchesConfigService _config;
 | 
			
		||||
    private readonly IReplacementService _repSvc;
 | 
			
		||||
 | 
			
		||||
@@ -49,7 +49,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
        IHttpClientFactory httpFactory,
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        IPubSub pubSub,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        SearchesConfigService config,
 | 
			
		||||
        IReplacementService repSvc)
 | 
			
		||||
    {
 | 
			
		||||
@@ -57,7 +57,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _pubSub = pubSub;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _config = config;
 | 
			
		||||
        _repSvc = repSvc;
 | 
			
		||||
 | 
			
		||||
@@ -285,7 +285,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
                            ? ""
 | 
			
		||||
                            : await _repSvc.ReplaceAsync(fs.Message, repCtx);
 | 
			
		||||
 | 
			
		||||
                        var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream, false), message);
 | 
			
		||||
                        var msg = await _sender.Response(textChannel)
 | 
			
		||||
                                               .Embed(GetEmbed(fs.GuildId, stream, false))
 | 
			
		||||
                                               .Text(message)
 | 
			
		||||
                                               .SendAsync();
 | 
			
		||||
 | 
			
		||||
                        // only cache the ids of channel/message pairs 
 | 
			
		||||
                        if (_deleteOnOfflineServers.Contains(fs.GuildId))
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
 | 
			
		||||
{
 | 
			
		||||
    private readonly IGoogleApiService _google;
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly IBot _bot;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, bool> _atcs = new();
 | 
			
		||||
@@ -21,12 +21,12 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
 | 
			
		||||
    public TranslateService(
 | 
			
		||||
        IGoogleApiService google,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        IBot bot)
 | 
			
		||||
    {
 | 
			
		||||
        _google = google;
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _bot = bot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +77,7 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
 | 
			
		||||
                     .AddField(langs.From, um.Content)
 | 
			
		||||
                     .AddField(langs.To, output);
 | 
			
		||||
 | 
			
		||||
                await tch.EmbedAsync(embed);
 | 
			
		||||
                await _sender.Response(tch).Embed(embed).SendAsync();
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ public sealed class GiveawayService : INService, IReadyExecutor
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly IBotCredentials _creds;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly ILocalization _localization;
 | 
			
		||||
    private readonly IMemoryCache _cache;
 | 
			
		||||
@@ -22,12 +22,12 @@ public sealed class GiveawayService : INService, IReadyExecutor
 | 
			
		||||
    private readonly ConcurrentDictionary<int, GiveawayRerollData> _rerolls = new();
 | 
			
		||||
 | 
			
		||||
    public GiveawayService(DbService db, IBotCredentials creds, DiscordSocketClient client,
 | 
			
		||||
        IEmbedBuilderService eb, IBotStrings strings, ILocalization localization, IMemoryCache cache)
 | 
			
		||||
        IMessageSenderService sender, IBotStrings strings, ILocalization localization, IMemoryCache cache)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _creds = creds;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _localization = localization;
 | 
			
		||||
        _cache = cache;
 | 
			
		||||
@@ -317,8 +317,7 @@ public sealed class GiveawayService : INService, IReadyExecutor
 | 
			
		||||
               {Format.Code(winner.UserId.ToString())}
 | 
			
		||||
               """;
 | 
			
		||||
 | 
			
		||||
        var eb = _eb
 | 
			
		||||
            .Create()
 | 
			
		||||
        var eb = new EmbedBuilder()
 | 
			
		||||
            .WithOkColor()
 | 
			
		||||
            .WithTitle(GetText(strs.giveaway_ended))
 | 
			
		||||
            .WithDescription(ga.Message)
 | 
			
		||||
@@ -334,7 +333,7 @@ public sealed class GiveawayService : INService, IReadyExecutor
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            _ = msg.DeleteAsync();
 | 
			
		||||
            await ch.EmbedAsync(eb);
 | 
			
		||||
            await _sender.Response(ch).Embed(eb).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -133,23 +133,23 @@ public partial class Utility
 | 
			
		||||
        private async Task ShowQuoteData(Quote data)
 | 
			
		||||
        {
 | 
			
		||||
            var eb = new EmbedBuilder()
 | 
			
		||||
                        .WithOkColor()
 | 
			
		||||
                        .WithTitle($"{GetText(strs.quote_id($"#{data.Id}"))} | {GetText(strs.response)}:")
 | 
			
		||||
                        .WithDescription(Format.Sanitize(data.Text).Replace("](", "]\\(").TrimTo(4096))
 | 
			
		||||
                        .AddField(GetText(strs.trigger), data.Keyword)
 | 
			
		||||
                        .WithFooter(
 | 
			
		||||
                            GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})")))
 | 
			
		||||
                        .Build();
 | 
			
		||||
                     .WithOkColor()
 | 
			
		||||
                     .WithTitle($"{GetText(strs.quote_id($"#{data.Id}"))} | {GetText(strs.response)}:")
 | 
			
		||||
                     .WithDescription(Format.Sanitize(data.Text).Replace("](", "]\\(").TrimTo(4096))
 | 
			
		||||
                     .AddField(GetText(strs.trigger), data.Keyword)
 | 
			
		||||
                     .WithFooter(
 | 
			
		||||
                         GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})")));
 | 
			
		||||
 | 
			
		||||
            if (!(data.Text.Length > 4096))
 | 
			
		||||
            {
 | 
			
		||||
                await ctx.Channel.SendMessageAsync(embed: eb);
 | 
			
		||||
                await Response().Embed(eb).SendAsync();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // todo all send files should go through response system too
 | 
			
		||||
            await ctx.Channel.SendFileAsync(
 | 
			
		||||
                attachment: new FileAttachment(await data.Text.ToStream(), "quote.txt"),
 | 
			
		||||
                embed: eb);
 | 
			
		||||
                embed: eb.Build());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor)
 | 
			
		||||
@@ -168,10 +168,12 @@ public partial class Utility
 | 
			
		||||
            if (quote is null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            await ctx.Channel.SendMessageAsync($"`#{quote.Id}` 💬 "
 | 
			
		||||
                                               + quote.Keyword.ToLowerInvariant()
 | 
			
		||||
                                               + ":  "
 | 
			
		||||
                                               + quote.Text.SanitizeAllMentions());
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Confirm($"`#{quote.Id}` 💬 ",
 | 
			
		||||
                      quote.Keyword.ToLowerInvariant()
 | 
			
		||||
                      + ":  "
 | 
			
		||||
                      + quote.Text.SanitizeAllMentions())
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
@@ -204,7 +206,7 @@ public partial class Utility
 | 
			
		||||
 | 
			
		||||
            if (quote is null || quote.GuildId != ctx.Guild.Id)
 | 
			
		||||
            {
 | 
			
		||||
                await Response().Error(GetText(strs.quotes_notfound)).SendAsync();
 | 
			
		||||
                await Response().Error(strs.quotes_notfound).SendAsync();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,25 +11,26 @@ namespace NadekoBot.Modules.Utility.Services;
 | 
			
		||||
public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
{
 | 
			
		||||
    private readonly Regex _regex =
 | 
			
		||||
        new(@"^(?:(?:at|on(?:\sthe)?)?\s*(?<date>(?:\d{2}:\d{2}\s)?\d{1,2}\.\d{1,2}(?:\.\d{2,4})?)|(?:in\s?)?\s*(?:(?<mo>\d+)(?:\s?(?:months?|mos?),?))?(?:(?:\sand\s|\s*)?(?<w>\d+)(?:\s?(?:weeks?|w),?))?(?:(?:\sand\s|\s*)?(?<d>\d+)(?:\s?(?:days?|d),?))?(?:(?:\sand\s|\s*)?(?<h>\d+)(?:\s?(?:hours?|h),?))?(?:(?:\sand\s|\s*)?(?<m>\d+)(?:\s?(?:minutes?|mins?|m),?))?)\s+(?:to:?\s+)?(?<what>(?:\r\n|[\r\n]|.)+)",
 | 
			
		||||
        new(
 | 
			
		||||
            @"^(?:(?:at|on(?:\sthe)?)?\s*(?<date>(?:\d{2}:\d{2}\s)?\d{1,2}\.\d{1,2}(?:\.\d{2,4})?)|(?:in\s?)?\s*(?:(?<mo>\d+)(?:\s?(?:months?|mos?),?))?(?:(?:\sand\s|\s*)?(?<w>\d+)(?:\s?(?:weeks?|w),?))?(?:(?:\sand\s|\s*)?(?<d>\d+)(?:\s?(?:days?|d),?))?(?:(?:\sand\s|\s*)?(?<h>\d+)(?:\s?(?:hours?|h),?))?(?:(?:\sand\s|\s*)?(?<m>\d+)(?:\s?(?:minutes?|mins?|m),?))?)\s+(?:to:?\s+)?(?<what>(?:\r\n|[\r\n]|.)+)",
 | 
			
		||||
            RegexOptions.Compiled | RegexOptions.Multiline);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly IBotCredentials _creds;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly CultureInfo _culture;
 | 
			
		||||
 | 
			
		||||
    public RemindService(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        IBotCredentials creds,
 | 
			
		||||
        IEmbedBuilderService eb)
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _creds = creds;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
@@ -120,7 +121,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(dateString))
 | 
			
		||||
        {
 | 
			
		||||
            var now = DateTime.UtcNow;
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            if (!DateTime.TryParse(dateString, _culture, DateTimeStyles.None, out var dt))
 | 
			
		||||
            {
 | 
			
		||||
                Log.Warning("Invalid remind datetime format");
 | 
			
		||||
@@ -162,6 +163,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
 | 
			
		||||
                values[groupName] = value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ts = new TimeSpan((30 * values["mo"]) + (7 * values["w"]) + values["d"], values["h"], values["m"], 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -197,22 +199,24 @@ public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
 | 
			
		||||
            if (st is SmartEmbedText set)
 | 
			
		||||
            {
 | 
			
		||||
                await ch.SendMessageAsync(null, embed: set.GetEmbed().Build());
 | 
			
		||||
                await _sender.Response(ch).Embed(set.GetEmbed()).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            else if (st is SmartEmbedTextArray seta)
 | 
			
		||||
            {
 | 
			
		||||
                await ch.SendMessageAsync(null, embeds: seta.GetEmbedBuilders().Map(x => x.Build()));
 | 
			
		||||
                await _sender.Response(ch).Embeds(seta.GetEmbedBuilders()).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await ch.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                        .WithOkColor()
 | 
			
		||||
                        .WithTitle("Reminder")
 | 
			
		||||
                        .AddField("Created At",
 | 
			
		||||
                            r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?")
 | 
			
		||||
                        .AddField("By",
 | 
			
		||||
                            (await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString()),
 | 
			
		||||
                    r.Message);
 | 
			
		||||
                await _sender.Response(ch)
 | 
			
		||||
                             .Embed(new EmbedBuilder()
 | 
			
		||||
                                    .WithOkColor()
 | 
			
		||||
                                    .WithTitle("Reminder")
 | 
			
		||||
                                    .AddField("Created At",
 | 
			
		||||
                                        r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?")
 | 
			
		||||
                                    .AddField("By",
 | 
			
		||||
                                        (await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString()))
 | 
			
		||||
                             .Text(r.Message)
 | 
			
		||||
                             .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
@@ -227,7 +231,8 @@ public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
        public TimeSpan Time { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task AddReminderAsync(ulong userId,
 | 
			
		||||
    public async Task AddReminderAsync(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        ulong targetId,
 | 
			
		||||
        ulong? guildId,
 | 
			
		||||
        bool isPrivate,
 | 
			
		||||
@@ -242,7 +247,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
            ServerId = guildId ?? 0,
 | 
			
		||||
            IsPrivate = isPrivate,
 | 
			
		||||
            When = time,
 | 
			
		||||
            Message = message, 
 | 
			
		||||
            Message = message,
 | 
			
		||||
            Type = reminderType
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Utility;
 | 
			
		||||
 | 
			
		||||
@@ -86,8 +87,8 @@ public partial class Utility
 | 
			
		||||
                (curPage) =>
 | 
			
		||||
                {
 | 
			
		||||
                    var eb = new EmbedBuilder()
 | 
			
		||||
                        .WithOkColor()
 | 
			
		||||
                        .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
                             .WithOkColor()
 | 
			
		||||
                             .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
 | 
			
		||||
                    ShowTodoItem(todos, curPage, eb);
 | 
			
		||||
 | 
			
		||||
@@ -99,15 +100,15 @@ public partial class Utility
 | 
			
		||||
 | 
			
		||||
        private static void ShowTodoItem(IReadOnlyCollection<TodoModel> todos, int curPage, EmbedBuilder eb)
 | 
			
		||||
        {
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            foreach (var todo in todos.Skip(curPage * 9).Take(9))
 | 
			
		||||
            {
 | 
			
		||||
                // green circle and yellow circle emojis
 | 
			
		||||
                eb.AddField($"-",
 | 
			
		||||
                    $"{(todo.IsDone
 | 
			
		||||
                        ? "✅"
 | 
			
		||||
                        : "🟡")} {Format.Code(new kwum(todo.Id).ToString())} {todo.Todo}",
 | 
			
		||||
                    false);
 | 
			
		||||
                sb.AppendLine($"{(todo.IsDone ? "✔" : "□")} {Format.Code(new kwum(todo.Id).ToString())} {todo.Todo}");
 | 
			
		||||
 | 
			
		||||
                sb.AppendLine("---");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            eb.WithDescription(sb.ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Group("archive")]
 | 
			
		||||
@@ -150,8 +151,8 @@ public partial class Utility
 | 
			
		||||
                    (curPage) =>
 | 
			
		||||
                    {
 | 
			
		||||
                        var eb = new EmbedBuilder()
 | 
			
		||||
                            .WithTitle(GetText(strs.todo_archive_list))
 | 
			
		||||
                            .WithOkColor();
 | 
			
		||||
                                 .WithTitle(GetText(strs.todo_archive_list))
 | 
			
		||||
                                 .WithOkColor();
 | 
			
		||||
 | 
			
		||||
                        foreach (var archivedList in archivedTodoLists.Skip(curPage * 9).Take(9))
 | 
			
		||||
                        {
 | 
			
		||||
@@ -179,8 +180,8 @@ public partial class Utility
 | 
			
		||||
                    (curPage) =>
 | 
			
		||||
                    {
 | 
			
		||||
                        var eb = new EmbedBuilder()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
                                 .WithOkColor()
 | 
			
		||||
                                 .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
 | 
			
		||||
                        ShowTodoItem(list.Items, curPage, eb);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -90,10 +90,10 @@ public partial class Utility
 | 
			
		||||
            res = Math.Round(res, 4);
 | 
			
		||||
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Confirm(GetText(strs.convert(value,
 | 
			
		||||
                  .Confirm(strs.convert(value,
 | 
			
		||||
                      originUnit.Triggers.Last(),
 | 
			
		||||
                      res,
 | 
			
		||||
                      targetUnit.Triggers.Last())))
 | 
			
		||||
                      targetUnit.Triggers.Last()))
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -337,7 +337,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(result))
 | 
			
		||||
            await Response().Error(strs.showemojis_none).SendAsync();
 | 
			
		||||
        else
 | 
			
		||||
            await ctx.Channel.SendMessageAsync(result.TrimTo(2000));
 | 
			
		||||
            await Response().Text(result.TrimTo(2000)).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
@@ -613,7 +613,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var sw = Stopwatch.StartNew();
 | 
			
		||||
            var msg = await ctx.Channel.SendMessageAsync("🏓");
 | 
			
		||||
            var msg = await Response().Text("🏓").SendAsync();
 | 
			
		||||
            sw.Stop();
 | 
			
		||||
            msg.DeleteAfter(0);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,16 +9,19 @@ public class VerboseErrorsService : INService
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly CommandHandler _ch;
 | 
			
		||||
    private readonly ICommandsUtilityService _hs;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
 | 
			
		||||
    public VerboseErrorsService(
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        CommandHandler ch,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        ICommandsUtilityService hs)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _ch = ch;
 | 
			
		||||
        _hs = hs;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        _ch.CommandErrored += LogVerboseError;
 | 
			
		||||
 | 
			
		||||
@@ -38,7 +41,7 @@ public class VerboseErrorsService : INService
 | 
			
		||||
                           .WithFooter("Admin may disable verbose errors via `.ve` command")
 | 
			
		||||
                           .WithErrorColor();
 | 
			
		||||
 | 
			
		||||
            await channel.EmbedAsync(embed);
 | 
			
		||||
            await _sender.Response(channel).Embed(embed).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
			
		||||
    private readonly IHttpClientFactory _httpFactory;
 | 
			
		||||
    private readonly XpConfigService _xpConfig;
 | 
			
		||||
    private readonly IPubSub _pubSub;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedRoles;
 | 
			
		||||
    private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedChannels;
 | 
			
		||||
@@ -62,7 +61,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
			
		||||
        IHttpClientFactory http,
 | 
			
		||||
        XpConfigService xpConfig,
 | 
			
		||||
        IPubSub pubSub,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IPatronageService ps,
 | 
			
		||||
        IMessageSenderService sender)
 | 
			
		||||
    {
 | 
			
		||||
@@ -75,14 +73,13 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
			
		||||
        _httpFactory = http;
 | 
			
		||||
        _xpConfig = xpConfig;
 | 
			
		||||
        _pubSub = pubSub;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _excludedServers = new();
 | 
			
		||||
        _excludedChannels = new();
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _xpTemplateReloadKey = new("xp.template.reload");
 | 
			
		||||
        _ps = ps;
 | 
			
		||||
        _c = c;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
 | 
			
		||||
        InternalReloadXpTemplate();
 | 
			
		||||
 | 
			
		||||
@@ -393,11 +390,12 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
			
		||||
        {
 | 
			
		||||
            if (notifyLoc == XpNotificationLocation.Dm)
 | 
			
		||||
            {
 | 
			
		||||
                await user.SendConfirmAsync(_eb,
 | 
			
		||||
                    _strings.GetText(strs.level_up_dm(user.Mention,
 | 
			
		||||
                            Format.Bold(newLevel.ToString()),
 | 
			
		||||
                            Format.Bold(guild.ToString() ?? "-")),
 | 
			
		||||
                        guild.Id));
 | 
			
		||||
                await _sender.Response(user)
 | 
			
		||||
                             .Confirm(_strings.GetText(strs.level_up_dm(user.Mention,
 | 
			
		||||
                                     Format.Bold(newLevel.ToString()),
 | 
			
		||||
                                     Format.Bold(guild.ToString() ?? "-")),
 | 
			
		||||
                                 guild.Id))
 | 
			
		||||
                             .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            else // channel
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,20 +8,20 @@ public sealed class PermissionChecker : IPermissionChecker, INService
 | 
			
		||||
    private readonly PermissionService _perms;
 | 
			
		||||
    private readonly GlobalPermissionService _gperm;
 | 
			
		||||
    private readonly CmdCdService _cmdCds;
 | 
			
		||||
    private readonly IEmbedBuilderService _ebs;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly CommandHandler _ch;
 | 
			
		||||
 | 
			
		||||
    public PermissionChecker(
 | 
			
		||||
        PermissionService perms,
 | 
			
		||||
        GlobalPermissionService gperm,
 | 
			
		||||
        CmdCdService cmdCds,
 | 
			
		||||
        IEmbedBuilderService ebs,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        CommandHandler ch)
 | 
			
		||||
    {
 | 
			
		||||
        _perms = perms;
 | 
			
		||||
        _gperm = gperm;
 | 
			
		||||
        _cmdCds = cmdCds;
 | 
			
		||||
        _ebs = ebs;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _ch = ch;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,6 @@ public enum LogType
 | 
			
		||||
    ChannelUpdated,
 | 
			
		||||
    UserPresence,
 | 
			
		||||
    VoicePresence,
 | 
			
		||||
    VoicePresenceTts,
 | 
			
		||||
    UserMuted,
 | 
			
		||||
    UserWarned,
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ public sealed class DmContextAdapter : DmContext
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    private readonly IServiceProvider _services;
 | 
			
		||||
    private readonly Lazy<IEmbedBuilderService> _ebs;
 | 
			
		||||
    private readonly Lazy<IBotStrings> _botStrings;
 | 
			
		||||
    private readonly Lazy<ILocalization> _localization;
 | 
			
		||||
 | 
			
		||||
@@ -32,14 +31,10 @@ public sealed class DmContextAdapter : DmContext
 | 
			
		||||
        Bot = ctx.Client.CurrentUser;
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
        _ebs = new(_services.GetRequiredService<IEmbedBuilderService>());
 | 
			
		||||
        _botStrings = new(_services.GetRequiredService<IBotStrings>);
 | 
			
		||||
        _localization = new(_services.GetRequiredService<ILocalization>());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override EmbedBuilder Embed()
 | 
			
		||||
        => new EmbedBuilder();
 | 
			
		||||
 | 
			
		||||
    public override string GetText(string key, object[]? args = null)
 | 
			
		||||
    {
 | 
			
		||||
        var cultureInfo = _localization.Value.GetCultureInfo(default(ulong?));
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ public sealed class GuildContextAdapter : GuildContext
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceProvider _services;
 | 
			
		||||
    private readonly ICommandContext _ctx;
 | 
			
		||||
    private readonly Lazy<IEmbedBuilderService> _ebs;
 | 
			
		||||
    private readonly Lazy<IBotStrings> _botStrings;
 | 
			
		||||
    private readonly Lazy<ILocalization> _localization;
 | 
			
		||||
    
 | 
			
		||||
@@ -18,9 +17,6 @@ public sealed class GuildContextAdapter : GuildContext
 | 
			
		||||
 | 
			
		||||
    public override IGuildUser User { get; } 
 | 
			
		||||
 | 
			
		||||
    public override EmbedBuilder Embed()
 | 
			
		||||
        => _ebs.Value.Create();
 | 
			
		||||
 | 
			
		||||
    public GuildContextAdapter(ICommandContext ctx, IMedusaStrings strings, IServiceProvider services)
 | 
			
		||||
    {
 | 
			
		||||
        if (ctx.Guild is not IGuild guild || ctx.Channel is not ITextChannel channel)
 | 
			
		||||
@@ -33,7 +29,6 @@ public sealed class GuildContextAdapter : GuildContext
 | 
			
		||||
        Bot = ctx.Client.CurrentUser;
 | 
			
		||||
 | 
			
		||||
        _services = services;
 | 
			
		||||
        _ebs = new(_services.GetRequiredService<IEmbedBuilderService>());
 | 
			
		||||
        _botStrings = new(_services.GetRequiredService<IBotStrings>);
 | 
			
		||||
        _localization = new(_services.GetRequiredService<ILocalization>());
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -342,7 +342,6 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
 | 
			
		||||
        var sis = LoadSneksFromAssembly(safeName, a);
 | 
			
		||||
        typeReaders = LoadTypeReadersFromAssembly(a, strings);
 | 
			
		||||
        
 | 
			
		||||
        // todo allow this
 | 
			
		||||
        if (sis.Count == 0)
 | 
			
		||||
        {
 | 
			
		||||
            _kernel.Unload(safeName);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
    public IBotStrings Strings { get; set; }
 | 
			
		||||
    public ICommandHandler _cmdHandler { get; set; }
 | 
			
		||||
    public ILocalization _localization { get; set; }
 | 
			
		||||
    public IEmbedBuilderService _eb { get; set; }
 | 
			
		||||
    public INadekoInteractionService _inter { get; set; }
 | 
			
		||||
    public IReplacementService repSvc { get; set; }
 | 
			
		||||
    public IMessageSenderService _sender { get; set; }
 | 
			
		||||
@@ -28,7 +27,7 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
        => Context;
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Response()
 | 
			
		||||
        => new ResponseBuilder(Strings, _eb)
 | 
			
		||||
        => new ResponseBuilder(Strings)
 | 
			
		||||
            .Context(ctx);
 | 
			
		||||
 | 
			
		||||
    protected override void BeforeExecute(CommandInfo command)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,5 +3,8 @@
 | 
			
		||||
public interface IMessageSenderService
 | 
			
		||||
{
 | 
			
		||||
    ResponseBuilder Response(IMessageChannel channel);
 | 
			
		||||
    ResponseBuilder Response(ICommandContext hannel);
 | 
			
		||||
    ResponseBuilder Response(ICommandContext ctx);
 | 
			
		||||
    ResponseBuilder Response(IUser user);
 | 
			
		||||
 | 
			
		||||
    ResponseBuilder Response(SocketMessageComponent smc);
 | 
			
		||||
}
 | 
			
		||||
@@ -2,10 +2,6 @@
 | 
			
		||||
 | 
			
		||||
public static class MessageChannelExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static ResponseBuilder Response(this IMessageChannel channel, IBotStrings bs, IEmbedBuilderService ebs)
 | 
			
		||||
        => new ResponseBuilder(bs, ebs)
 | 
			
		||||
            .Channel(channel);
 | 
			
		||||
 | 
			
		||||
    // main overload that all other send methods reduce to
 | 
			
		||||
    public static Task<IUserMessage> SendAsync(
 | 
			
		||||
        this IMessageChannel channel,
 | 
			
		||||
@@ -92,7 +88,7 @@ public static class MessageChannelExtensions
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        EmbedBuilder? embed,
 | 
			
		||||
        string plainText = "",
 | 
			
		||||
        IReadOnlyCollection<IEmbedBuilder>? embeds = null,
 | 
			
		||||
        IReadOnlyCollection<EmbedBuilder>? embeds = null,
 | 
			
		||||
        NadekoInteraction? inter = null,
 | 
			
		||||
        IUserMessage? replyTo = null)
 | 
			
		||||
        => ch.SendAsync(plainText,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,27 @@ namespace NadekoBot.Extensions;
 | 
			
		||||
public sealed class MessageSenderService : IMessageSenderService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IBotStrings _bs;
 | 
			
		||||
    private readonly IEmbedBuilderService _ebs;
 | 
			
		||||
 | 
			
		||||
    public MessageSenderService(IBotStrings bs, IEmbedBuilderService ebs)
 | 
			
		||||
    public MessageSenderService(IBotStrings bs)
 | 
			
		||||
    {
 | 
			
		||||
        _bs = bs;
 | 
			
		||||
        _ebs = ebs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Response(IMessageChannel channel)
 | 
			
		||||
        => new ResponseBuilder(_bs, _ebs)
 | 
			
		||||
        => new ResponseBuilder(_bs)
 | 
			
		||||
            .Channel(channel);
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Response(ICommandContext ctx)
 | 
			
		||||
        => new ResponseBuilder(_bs, _ebs)
 | 
			
		||||
        => new ResponseBuilder(_bs)
 | 
			
		||||
            .Context(ctx);
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Response(IUser user)
 | 
			
		||||
        => new ResponseBuilder(_bs)
 | 
			
		||||
            .User(user);
 | 
			
		||||
    
 | 
			
		||||
    // todo fix interactions
 | 
			
		||||
    public ResponseBuilder Response(SocketMessageComponent smc)
 | 
			
		||||
        => new ResponseBuilder(_bs)
 | 
			
		||||
            .Channel(smc.Channel);
 | 
			
		||||
}
 | 
			
		||||
@@ -14,13 +14,14 @@ public sealed class ResponseBuilder
 | 
			
		||||
    private object[] locParams = [];
 | 
			
		||||
    private bool shouldReply = true;
 | 
			
		||||
    private readonly IBotStrings _bs;
 | 
			
		||||
    private readonly IEmbedBuilderService _ebs;
 | 
			
		||||
    private EmbedBuilder? embedBuilder = null;
 | 
			
		||||
    private NadekoInteraction? inter;
 | 
			
		||||
    private Stream? fileStream = null;
 | 
			
		||||
    private string? fileName = null;
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder(IBotStrings bs, IEmbedBuilderService ebs)
 | 
			
		||||
    public ResponseBuilder(IBotStrings bs)
 | 
			
		||||
    {
 | 
			
		||||
        _bs = bs;
 | 
			
		||||
        _ebs = ebs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private MessageReference? CreateMessageReference(IMessageChannel targetChannel)
 | 
			
		||||
@@ -43,8 +44,9 @@ public sealed class ResponseBuilder
 | 
			
		||||
            failIfNotExists: false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<IUserMessage> SendAsync()
 | 
			
		||||
    public async Task<IUserMessage> SendAsync(bool ephemeral = false)
 | 
			
		||||
    {
 | 
			
		||||
        // todo use ephemeral in interactions
 | 
			
		||||
        var targetChannel = InternalResolveChannel() ?? throw new ArgumentNullException(nameof(channel));
 | 
			
		||||
        var msgReference = CreateMessageReference(targetChannel);
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +55,15 @@ public sealed class ResponseBuilder
 | 
			
		||||
        if (sanitizeMentions)
 | 
			
		||||
            txt = txt?.SanitizeMentions(true);
 | 
			
		||||
 | 
			
		||||
        if (this.fileStream is Stream stream)
 | 
			
		||||
            return await targetChannel.SendFileAsync(stream,
 | 
			
		||||
                filename: fileName,
 | 
			
		||||
                txt,
 | 
			
		||||
                embed: embed ?? embedBuilder?.Build(),
 | 
			
		||||
                components: null,
 | 
			
		||||
                allowedMentions: sanitizeMentions ? new(AllowedMentionTypes.Users) : AllowedMentions.All,
 | 
			
		||||
                messageReference: msgReference);
 | 
			
		||||
 | 
			
		||||
        return await targetChannel.SendMessageAsync(
 | 
			
		||||
            txt,
 | 
			
		||||
            embed: embed ?? embedBuilder?.Build(),
 | 
			
		||||
@@ -65,6 +76,7 @@ public sealed class ResponseBuilder
 | 
			
		||||
    private ulong? InternalResolveGuildId(IMessageChannel? targetChannel)
 | 
			
		||||
        => ctx?.Guild?.Id ?? (targetChannel as ITextChannel)?.GuildId;
 | 
			
		||||
 | 
			
		||||
    // todo not good, has to go to the user
 | 
			
		||||
    private IMessageChannel? InternalResolveChannel()
 | 
			
		||||
        => channel ?? ctx?.Channel ?? msg?.Channel;
 | 
			
		||||
 | 
			
		||||
@@ -188,20 +200,14 @@ public sealed class ResponseBuilder
 | 
			
		||||
    private IUser? InternalResolveUser()
 | 
			
		||||
        => ctx?.User ?? user ?? msg?.Author;
 | 
			
		||||
 | 
			
		||||
    // todo embed colors
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Embed(EmbedBuilder eb)
 | 
			
		||||
    {
 | 
			
		||||
        embedBuilder = eb;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Embed(Func<IEmbedBuilderService, EmbedBuilder> embedFactory)
 | 
			
		||||
    {
 | 
			
		||||
        // todo colors
 | 
			
		||||
        this.embed = embedFactory(_ebs).Build();
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Channel(IMessageChannel channel)
 | 
			
		||||
    {
 | 
			
		||||
        this.channel = channel;
 | 
			
		||||
@@ -238,9 +244,10 @@ public sealed class ResponseBuilder
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Interaction(NadekoInteraction inter)
 | 
			
		||||
    public ResponseBuilder Interaction(NadekoInteraction? interaction)
 | 
			
		||||
    {
 | 
			
		||||
        // todo implement
 | 
			
		||||
        inter = interaction;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -249,4 +256,11 @@ public sealed class ResponseBuilder
 | 
			
		||||
        embeds = inputEmbeds;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder FileName(Stream fileStream, string fileName)
 | 
			
		||||
    {
 | 
			
		||||
        this.fileStream = fileStream;
 | 
			
		||||
        this.fileName = fileName;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -9,11 +9,11 @@ public static class CurrencyServiceExtensions
 | 
			
		||||
        var wallet = await cs.GetWalletAsync(userId);
 | 
			
		||||
        return await wallet.GetBalance();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // FUTURE should be a transaction
 | 
			
		||||
    public static async Task<bool> TransferAsync(
 | 
			
		||||
        this ICurrencyService cs,
 | 
			
		||||
        IEmbedBuilderService ebs,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        IUser from,
 | 
			
		||||
        IUser to,
 | 
			
		||||
        long amount,
 | 
			
		||||
@@ -29,17 +29,20 @@ public static class CurrencyServiceExtensions
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await to.SendConfirmAsync(ebs,
 | 
			
		||||
                    string.IsNullOrWhiteSpace(note)
 | 
			
		||||
                        ? $"Received {formattedAmount} from {from} "
 | 
			
		||||
                        : $"Received {formattedAmount} from {from}: {note}");
 | 
			
		||||
                await sender.Response(to)
 | 
			
		||||
                            .Confirm(string.IsNullOrWhiteSpace(note)
 | 
			
		||||
                                ? $"Received {formattedAmount} from {from} "
 | 
			
		||||
                                : $"Received {formattedAmount} from {from}: {note}")
 | 
			
		||||
                            .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                //ignored
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,79 +1,17 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Common.Configs;
 | 
			
		||||
 | 
			
		||||
// todo remove
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
public interface IEmbedBuilderService
 | 
			
		||||
// todo remove 
 | 
			
		||||
public sealed class DiscordEmbedBuilderWrapper 
 | 
			
		||||
{
 | 
			
		||||
    EmbedBuilder Create(ICommandContext ctx = null);
 | 
			
		||||
}
 | 
			
		||||
    // public EmbedBuilder WithColor(EmbedColor color)
 | 
			
		||||
    //     => color switch
 | 
			
		||||
    //     {
 | 
			
		||||
    //         EmbedColor.Ok => Wrap(embed.WithColor(_botConfig.Color.Ok.ToDiscordColor())),
 | 
			
		||||
    //         EmbedColor.Pending => Wrap(embed.WithColor(_botConfig.Color.Pending.ToDiscordColor())),
 | 
			
		||||
    //         EmbedColor.Error => Wrap(embed.WithColor(_botConfig.Color.Error.ToDiscordColor())),
 | 
			
		||||
    //         _ => throw new ArgumentOutOfRangeException(nameof(color), "Unsupported EmbedColor type")
 | 
			
		||||
    //     };
 | 
			
		||||
 | 
			
		||||
public class EmbedBuilderService : IEmbedBuilderService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly BotConfigService _botConfigService;
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilderService(BotConfigService botConfigService)
 | 
			
		||||
        => _botConfigService = botConfigService;
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder Create(ICommandContext ctx = null)
 | 
			
		||||
        => new EmbedBuilder();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public sealed class DiscordEmbedBuilderWrapper : IEmbedBuilder
 | 
			
		||||
{
 | 
			
		||||
    private readonly BotConfig _botConfig;
 | 
			
		||||
    private EmbedBuilder embed;
 | 
			
		||||
 | 
			
		||||
    public DiscordEmbedBuilderWrapper(in BotConfig botConfig, EmbedBuilder embed = null)
 | 
			
		||||
    {
 | 
			
		||||
        _botConfig = botConfig;
 | 
			
		||||
        this.embed = embed ?? new EmbedBuilder();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithDescription(string desc)
 | 
			
		||||
        => Wrap(embed.WithDescription(desc));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithTitle(string title)
 | 
			
		||||
        => Wrap(embed.WithTitle(title));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder AddField(string title, object value, bool isInline = false)
 | 
			
		||||
        => Wrap(embed.AddField(title, value, isInline));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithFooter(string text, string iconUrl = null)
 | 
			
		||||
        => Wrap(embed.WithFooter(text, iconUrl));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithAuthor(string name, string iconUrl = null, string url = null)
 | 
			
		||||
        => Wrap(embed.WithAuthor(name, iconUrl, url));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithUrl(string url)
 | 
			
		||||
        => Wrap(embed.WithUrl(url));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithImageUrl(string url)
 | 
			
		||||
        => Wrap(embed.WithImageUrl(url));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithThumbnailUrl(string url)
 | 
			
		||||
        => Wrap(embed.WithThumbnailUrl(url));
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithColor(EmbedColor color)
 | 
			
		||||
        => color switch
 | 
			
		||||
        {
 | 
			
		||||
            EmbedColor.Ok => Wrap(embed.WithColor(_botConfig.Color.Ok.ToDiscordColor())),
 | 
			
		||||
            EmbedColor.Pending => Wrap(embed.WithColor(_botConfig.Color.Pending.ToDiscordColor())),
 | 
			
		||||
            EmbedColor.Error => Wrap(embed.WithColor(_botConfig.Color.Error.ToDiscordColor())),
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(color), "Unsupported EmbedColor type")
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder WithDiscordColor(Color color)
 | 
			
		||||
        => Wrap(embed.WithColor(color));
 | 
			
		||||
 | 
			
		||||
    public Embed Build()
 | 
			
		||||
        => embed.Build();
 | 
			
		||||
 | 
			
		||||
    private EmbedBuilder Wrap(EmbedBuilder eb)
 | 
			
		||||
    {
 | 
			
		||||
        embed = eb;
 | 
			
		||||
        return eb;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -8,7 +8,7 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
 | 
			
		||||
    private readonly CommandHandler _ch;
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly DiscordPermOverrideService _dpos;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IMessageSenderService _sender;
 | 
			
		||||
    private readonly ILocalization _loc;
 | 
			
		||||
    private readonly IMedusaLoaderService _medusae;
 | 
			
		||||
 | 
			
		||||
@@ -16,14 +16,14 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
 | 
			
		||||
        CommandHandler ch,
 | 
			
		||||
        IBotStrings strings,
 | 
			
		||||
        DiscordPermOverrideService dpos,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        ILocalization loc,
 | 
			
		||||
        IMedusaLoaderService medusae)
 | 
			
		||||
    {
 | 
			
		||||
        _ch = ch;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _dpos = dpos;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _sender = sender;
 | 
			
		||||
        _loc = loc;
 | 
			
		||||
        _medusae = medusae;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -60,31 +60,34 @@ public static class SocketMessageComponentExtensions
 | 
			
		||||
    
 | 
			
		||||
    public static Task RespondAsync(
 | 
			
		||||
        this SocketMessageComponent ch,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        string text,
 | 
			
		||||
        MsgType type,
 | 
			
		||||
        bool ephemeral = false,
 | 
			
		||||
        NadekoInteraction? inter = null)
 | 
			
		||||
    {
 | 
			
		||||
        var builder = new EmbedBuilder().WithDescription(text);
 | 
			
		||||
        var embed = new EmbedBuilder().WithDescription(text);
 | 
			
		||||
 | 
			
		||||
        builder = (type switch
 | 
			
		||||
        embed = (type switch
 | 
			
		||||
        {
 | 
			
		||||
            MsgType.Error => builder.WithErrorColor(),
 | 
			
		||||
            MsgType.Ok => builder.WithOkColor(),
 | 
			
		||||
            MsgType.Pending => builder.WithPendingColor(),
 | 
			
		||||
            MsgType.Error => embed.WithErrorColor(),
 | 
			
		||||
            MsgType.Ok => embed.WithOkColor(),
 | 
			
		||||
            MsgType.Pending => embed.WithPendingColor(),
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(type))
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return ch.EmbedAsync(builder, inter: inter, ephemeral: ephemeral);
 | 
			
		||||
        return sender.Response(ch)
 | 
			
		||||
                     .Embed(embed)
 | 
			
		||||
                     .Interaction(inter)
 | 
			
		||||
                     .SendAsync(ephemeral: ephemeral);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // embed title and optional footer overloads
 | 
			
		||||
 | 
			
		||||
    public static Task RespondConfirmAsync(
 | 
			
		||||
        this SocketMessageComponent smc,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IMessageSenderService sender,
 | 
			
		||||
        string text,
 | 
			
		||||
        bool ephemeral = false)
 | 
			
		||||
        => smc.RespondAsync(eb, text, MsgType.Ok, ephemeral);
 | 
			
		||||
        => smc.RespondAsync(sender, text, MsgType.Ok, ephemeral);
 | 
			
		||||
}
 | 
			
		||||
@@ -4,21 +4,6 @@ namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
public static class UserExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static async Task<IUserMessage> EmbedAsync(this IUser user, EmbedBuilder embed, string msg = "")
 | 
			
		||||
    {
 | 
			
		||||
        var ch = await user.CreateDMChannelAsync();
 | 
			
		||||
        return await ch.EmbedAsync(embed, msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task<IUserMessage> SendAsync(this IUser user, SmartText text, bool sanitizeAll = false)
 | 
			
		||||
    {
 | 
			
		||||
        var ch = await user.CreateDMChannelAsync();
 | 
			
		||||
        return await ch.SendAsync(text, sanitizeAll);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task<IUserMessage> SendConfirmAsync(this IUser user, IEmbedBuilderService eb, string text)
 | 
			
		||||
        => await user.SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text).Build());
 | 
			
		||||
 | 
			
		||||
    // This method is used by everything that fetches the avatar from a user
 | 
			
		||||
    public static Uri RealAvatarUrl(this IUser usr, ushort size = 256)
 | 
			
		||||
        => usr.AvatarId is null ? new(usr.GetDefaultAvatarUrl()) : new Uri(usr.GetAvatarUrl(ImageFormat.Auto, size));
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user