mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-11-04 08:34:27 -05:00
Part2 of the response system rework
This commit is contained in:
@@ -7,7 +7,7 @@ public abstract class CleanupModuleBase : NadekoModule
|
||||
{
|
||||
try
|
||||
{
|
||||
var embed = _eb.Create()
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle(GetText(strs.sql_confirm_exec))
|
||||
.WithDescription(name);
|
||||
|
||||
@@ -19,7 +19,7 @@ public abstract class CleanupModuleBase : NadekoModule
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await SendErrorAsync(ex.ToString());
|
||||
await Response().Error(ex.ToString()).SendAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,8 @@ public sealed class DmContextAdapter : DmContext
|
||||
_localization = new(_services.GetRequiredService<ILocalization>());
|
||||
}
|
||||
|
||||
public override IEmbedBuilder Embed()
|
||||
=> _ebs.Value.Create();
|
||||
public override EmbedBuilder Embed()
|
||||
=> new EmbedBuilder();
|
||||
|
||||
public override string GetText(string key, object[]? args = null)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class GuildContextAdapter : GuildContext
|
||||
|
||||
public override IGuildUser User { get; }
|
||||
|
||||
public override IEmbedBuilder Embed()
|
||||
public override EmbedBuilder Embed()
|
||||
=> _ebs.Value.Create();
|
||||
|
||||
public GuildContextAdapter(ICommandContext ctx, IMedusaStrings strings, IServiceProvider services)
|
||||
|
||||
@@ -19,6 +19,7 @@ public abstract class NadekoModule : ModuleBase
|
||||
public IEmbedBuilderService _eb { get; set; }
|
||||
public INadekoInteractionService _inter { get; set; }
|
||||
public IReplacementService repSvc { get; set; }
|
||||
public IMessageSenderService _sender { get; set; }
|
||||
|
||||
protected string prefix
|
||||
=> _cmdHandler.GetPrefix(ctx.Guild);
|
||||
@@ -26,67 +27,23 @@ public abstract class NadekoModule : ModuleBase
|
||||
protected ICommandContext ctx
|
||||
=> Context;
|
||||
|
||||
public ResponseBuilder Response()
|
||||
=> new ResponseBuilder(Strings, _eb)
|
||||
.Context(ctx);
|
||||
|
||||
protected override void BeforeExecute(CommandInfo command)
|
||||
=> Culture = _localization.GetCultureInfo(ctx.Guild?.Id);
|
||||
|
||||
protected string GetText(in LocStr data)
|
||||
=> Strings.GetText(data, Culture);
|
||||
|
||||
public Task<IUserMessage> SendErrorAsync(
|
||||
string title,
|
||||
string error,
|
||||
string url = null,
|
||||
string footer = null,
|
||||
NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
|
||||
|
||||
public Task<IUserMessage> SendConfirmAsync(
|
||||
string title,
|
||||
string text,
|
||||
string url = null,
|
||||
string footer = null)
|
||||
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
|
||||
|
||||
// public Task<IUserMessage> SendAsync(SmartText text, NadekoInteraction inter = null, IUserMessage replyTo = null)
|
||||
// => ctx.Channel.SendAsync(_eb, text, MsgType.Ok, inter, replyTo: replyTo);
|
||||
|
||||
// colored
|
||||
public Task<IUserMessage> SendErrorAsync(string text, NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendAsync(_eb, text, MsgType.Error, inter, replyTo: ctx.Message);
|
||||
|
||||
public Task<IUserMessage> SendConfirmAsync(string text, NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendAsync(_eb, text, MsgType.Ok, inter, replyTo: ctx.Message);
|
||||
|
||||
public Task<IUserMessage> SendPendingAsync(string text, NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendAsync(_eb, text, MsgType.Pending, inter, replyTo: ctx.Message);
|
||||
|
||||
|
||||
// localized normal
|
||||
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendErrorAsync(GetText(str), inter);
|
||||
|
||||
public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendPendingAsync(GetText(str), inter);
|
||||
|
||||
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendConfirmAsync(GetText(str), inter);
|
||||
|
||||
// localized replies
|
||||
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||
|
||||
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||
|
||||
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||
|
||||
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
||||
public async Task<bool> PromptUserConfirmAsync(EmbedBuilder embed)
|
||||
{
|
||||
embed.WithPendingColor()
|
||||
.WithFooter("yes/no");
|
||||
.WithFooter("yes/no");
|
||||
|
||||
var msg = await ctx.Channel.EmbedAsync(embed);
|
||||
var msg = await Response().Embed(embed).SendAsync();
|
||||
try
|
||||
{
|
||||
var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id);
|
||||
@@ -140,9 +97,6 @@ public abstract class NadekoModule : ModuleBase
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IUserMessage> EmbedAsync(IEmbedBuilder embed, string msg = "", IReadOnlyCollection<IEmbedBuilder> embeds = null)
|
||||
=> ctx.Channel.EmbedAsync(embed, msg, replyTo: ctx.Message);
|
||||
}
|
||||
|
||||
public abstract class NadekoModule<TService> : NadekoModule
|
||||
|
||||
7
src/NadekoBot/_common/Sender/IMessageSenderService.cs
Normal file
7
src/NadekoBot/_common/Sender/IMessageSenderService.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace NadekoBot.Extensions;
|
||||
|
||||
public interface IMessageSenderService
|
||||
{
|
||||
ResponseBuilder Response(IMessageChannel channel);
|
||||
ResponseBuilder Response(ICommandContext hannel);
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
namespace NadekoBot.Extensions;
|
||||
namespace NadekoBot.Extensions;
|
||||
|
||||
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,
|
||||
@@ -33,7 +37,7 @@ public static class MessageChannelExtensions
|
||||
|
||||
if (replyTo.Channel.Id != source.Id)
|
||||
return null;
|
||||
|
||||
|
||||
return new(replyTo.Id,
|
||||
replyTo.Channel.Id,
|
||||
(replyTo.Channel as ITextChannel)?.GuildId,
|
||||
@@ -86,7 +90,7 @@ public static class MessageChannelExtensions
|
||||
|
||||
public static Task<IUserMessage> EmbedAsync(
|
||||
this IMessageChannel ch,
|
||||
IEmbedBuilder? embed,
|
||||
EmbedBuilder? embed,
|
||||
string plainText = "",
|
||||
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
||||
NadekoInteraction? inter = null,
|
||||
@@ -96,89 +100,13 @@ public static class MessageChannelExtensions
|
||||
embed: embed?.Build(),
|
||||
embeds: embeds?.Map(x => x.Build()),
|
||||
replyTo: replyTo);
|
||||
|
||||
public static Task<IUserMessage> SendAsync(
|
||||
this IMessageChannel ch,
|
||||
IEmbedBuilderService eb,
|
||||
string text,
|
||||
MsgType type,
|
||||
NadekoInteraction? inter = null,
|
||||
IUserMessage? replyTo = null)
|
||||
{
|
||||
var builder = eb.Create().WithDescription(text);
|
||||
|
||||
builder = (type switch
|
||||
{
|
||||
MsgType.Error => builder.WithErrorColor(),
|
||||
MsgType.Ok => builder.WithOkColor(),
|
||||
MsgType.Pending => builder.WithPendingColor(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type))
|
||||
});
|
||||
|
||||
return ch.EmbedAsync(builder, inter: inter, replyTo: replyTo);
|
||||
}
|
||||
|
||||
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, IEmbedBuilderService eb, string text)
|
||||
=> ch.SendAsync(eb, text, MsgType.Ok);
|
||||
|
||||
public static Task<IUserMessage> SendAsync(
|
||||
this IMessageChannel ch,
|
||||
IEmbedBuilderService eb,
|
||||
MsgType type,
|
||||
string? title,
|
||||
string text,
|
||||
string? url = null,
|
||||
string? footer = null)
|
||||
{
|
||||
var embed = eb.Create()
|
||||
.WithDescription(text)
|
||||
.WithTitle(title);
|
||||
|
||||
if (url is not null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
embed.WithUrl(url);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(footer))
|
||||
embed.WithFooter(footer);
|
||||
|
||||
embed = type switch
|
||||
{
|
||||
MsgType.Error => embed.WithErrorColor(),
|
||||
MsgType.Ok => embed.WithOkColor(),
|
||||
MsgType.Pending => embed.WithPendingColor(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type))
|
||||
};
|
||||
|
||||
return ch.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
|
||||
// embed title and optional footer overloads
|
||||
|
||||
public static Task<IUserMessage> SendConfirmAsync(
|
||||
this IMessageChannel ch,
|
||||
IEmbedBuilderService eb,
|
||||
string? title,
|
||||
string text,
|
||||
string? url = null,
|
||||
string? footer = null)
|
||||
=> ch.SendAsync(eb, MsgType.Ok, title, text, url, footer);
|
||||
|
||||
public static Task<IUserMessage> SendErrorAsync(
|
||||
this IMessageChannel ch,
|
||||
IEmbedBuilderService eb,
|
||||
string title,
|
||||
string text,
|
||||
string? url = null,
|
||||
string? footer = null)
|
||||
=> ch.SendAsync(eb, MsgType.Error, title, text, url, footer);
|
||||
|
||||
// regular send overloads
|
||||
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, IEmbedBuilderService eb, string text)
|
||||
=> ch.SendAsync(eb, text, MsgType.Error);
|
||||
|
||||
public static Task SendPaginatedConfirmAsync(
|
||||
this ICommandContext ctx,
|
||||
int currentPage,
|
||||
Func<int, IEmbedBuilder> pageFunc,
|
||||
Func<int, EmbedBuilder> pageFunc,
|
||||
int totalElements,
|
||||
int itemsPerPage,
|
||||
bool addPaginatedFooter = true)
|
||||
@@ -197,7 +125,7 @@ public static class MessageChannelExtensions
|
||||
public static Task SendPaginatedConfirmAsync(
|
||||
this ICommandContext ctx,
|
||||
int currentPage,
|
||||
Func<int, Task<IEmbedBuilder>> pageFunc,
|
||||
Func<int, Task<EmbedBuilder>> pageFunc,
|
||||
int totalElements,
|
||||
int itemsPerPage,
|
||||
bool addPaginatedFooter = true)
|
||||
@@ -211,7 +139,7 @@ public static class MessageChannelExtensions
|
||||
public static async Task SendPaginatedConfirmAsync<T>(
|
||||
this ICommandContext ctx,
|
||||
int currentPage,
|
||||
Func<int, Task<IEmbedBuilder>> pageFunc,
|
||||
Func<int, Task<EmbedBuilder>> pageFunc,
|
||||
Func<int, ValueTask<SimpleInteraction<T>?>>? interFactory,
|
||||
int totalElements,
|
||||
int itemsPerPage,
|
||||
24
src/NadekoBot/_common/Sender/MessageSenderService.cs
Normal file
24
src/NadekoBot/_common/Sender/MessageSenderService.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace NadekoBot.Extensions;
|
||||
|
||||
public sealed class MessageSenderService : IMessageSenderService, INService
|
||||
{
|
||||
private readonly IBotStrings _bs;
|
||||
private readonly IEmbedBuilderService _ebs;
|
||||
|
||||
public MessageSenderService(IBotStrings bs, IEmbedBuilderService ebs)
|
||||
{
|
||||
_bs = bs;
|
||||
_ebs = ebs;
|
||||
}
|
||||
|
||||
|
||||
public ResponseBuilder Response(IMessageChannel channel)
|
||||
=> new ResponseBuilder(_bs, _ebs)
|
||||
.Channel(channel);
|
||||
|
||||
public ResponseBuilder Response(ICommandContext ctx)
|
||||
=> new ResponseBuilder(_bs, _ebs)
|
||||
.Context(ctx);
|
||||
}
|
||||
252
src/NadekoBot/_common/Sender/ResponseBuilder.cs
Normal file
252
src/NadekoBot/_common/Sender/ResponseBuilder.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
namespace NadekoBot.Extensions;
|
||||
|
||||
public sealed class ResponseBuilder
|
||||
{
|
||||
private ICommandContext? ctx = null;
|
||||
private IMessageChannel? channel = null;
|
||||
private Embed? embed = null;
|
||||
private string? plainText = null;
|
||||
private IReadOnlyCollection<EmbedBuilder>? embeds = null;
|
||||
private IUserMessage? msg = null;
|
||||
private IUser? user = null;
|
||||
private bool sanitizeMentions = true;
|
||||
private LocStr? locTxt;
|
||||
private object[] locParams = [];
|
||||
private bool shouldReply = true;
|
||||
private readonly IBotStrings _bs;
|
||||
private readonly IEmbedBuilderService _ebs;
|
||||
private EmbedBuilder? embedBuilder = null;
|
||||
|
||||
public ResponseBuilder(IBotStrings bs, IEmbedBuilderService ebs)
|
||||
{
|
||||
_bs = bs;
|
||||
_ebs = ebs;
|
||||
}
|
||||
|
||||
private MessageReference? CreateMessageReference(IMessageChannel targetChannel)
|
||||
{
|
||||
if (!shouldReply)
|
||||
return null;
|
||||
|
||||
var replyTo = msg ?? ctx?.Message;
|
||||
// what message are we replying to
|
||||
if (replyTo is null)
|
||||
return null;
|
||||
|
||||
// we have to have a channel where we are sending the message in order to know whether we can reply to it
|
||||
if (targetChannel.Id != replyTo.Channel.Id)
|
||||
return null;
|
||||
|
||||
return new(replyTo.Id,
|
||||
replyTo.Channel.Id,
|
||||
(replyTo.Channel as ITextChannel)?.GuildId,
|
||||
failIfNotExists: false);
|
||||
}
|
||||
|
||||
public async Task<IUserMessage> SendAsync()
|
||||
{
|
||||
var targetChannel = InternalResolveChannel() ?? throw new ArgumentNullException(nameof(channel));
|
||||
var msgReference = CreateMessageReference(targetChannel);
|
||||
|
||||
var txt = GetText(locTxt);
|
||||
|
||||
if (sanitizeMentions)
|
||||
txt = txt?.SanitizeMentions(true);
|
||||
|
||||
return await targetChannel.SendMessageAsync(
|
||||
txt,
|
||||
embed: embed ?? embedBuilder?.Build(),
|
||||
embeds: embeds?.Map(x => x.Build()),
|
||||
components: null,
|
||||
allowedMentions: sanitizeMentions ? new(AllowedMentionTypes.Users) : AllowedMentions.All,
|
||||
messageReference: msgReference);
|
||||
}
|
||||
|
||||
private ulong? InternalResolveGuildId(IMessageChannel? targetChannel)
|
||||
=> ctx?.Guild?.Id ?? (targetChannel as ITextChannel)?.GuildId;
|
||||
|
||||
private IMessageChannel? InternalResolveChannel()
|
||||
=> channel ?? ctx?.Channel ?? msg?.Channel;
|
||||
|
||||
private string? GetText(LocStr? locStr)
|
||||
{
|
||||
var targetChannel = InternalResolveChannel();
|
||||
var guildId = InternalResolveGuildId(targetChannel);
|
||||
return locStr is LocStr ls ? _bs.GetText(ls.Key, guildId, locParams) : plainText;
|
||||
}
|
||||
|
||||
private string GetText(LocStr locStr)
|
||||
{
|
||||
var targetChannel = InternalResolveChannel();
|
||||
var guildId = InternalResolveGuildId(targetChannel);
|
||||
return _bs.GetText(locStr.Key, guildId, locStr.Params);
|
||||
}
|
||||
|
||||
public ResponseBuilder Text(LocStr str)
|
||||
{
|
||||
locTxt = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Text(SmartText text)
|
||||
{
|
||||
if (text is SmartPlainText spt)
|
||||
plainText = spt.Text;
|
||||
else if (text is SmartEmbedText set)
|
||||
embed = set.GetEmbed().Build();
|
||||
else if (text is SmartEmbedTextArray ser)
|
||||
embeds = ser.GetEmbedBuilders();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private ResponseBuilder InternalColoredText(string text, EmbedColor color)
|
||||
{
|
||||
embed = new EmbedBuilder()
|
||||
.WithColor(color)
|
||||
.WithDescription(text)
|
||||
.Build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private EmbedBuilder CreateEmbedInternal(
|
||||
string? title,
|
||||
string? text,
|
||||
string? url,
|
||||
string? footer = null)
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle(title)
|
||||
.WithDescription(text);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(url))
|
||||
embed = embed.WithUrl(url);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(footer))
|
||||
embed = embed.WithFooter(footer);
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
private EmbedBuilder PaintEmbedInternal(EmbedBuilder eb, EmbedColor color)
|
||||
=> color switch
|
||||
{
|
||||
EmbedColor.Ok => eb.WithOkColor(),
|
||||
EmbedColor.Pending => eb.WithPendingColor(),
|
||||
EmbedColor.Error => eb.WithErrorColor(),
|
||||
};
|
||||
|
||||
public ResponseBuilder Error(
|
||||
string? title,
|
||||
string? text,
|
||||
string? url = null,
|
||||
string? footer = null)
|
||||
{
|
||||
var eb = CreateEmbedInternal(title, text, url, footer);
|
||||
embed = PaintEmbedInternal(eb, EmbedColor.Error).Build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ResponseBuilder Confirm(
|
||||
string? title,
|
||||
string? text,
|
||||
string? url = null,
|
||||
string? footer = null)
|
||||
{
|
||||
var eb = CreateEmbedInternal(title, text, url, footer);
|
||||
embed = PaintEmbedInternal(eb, EmbedColor.Error).Build();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Confirm(string text)
|
||||
=> InternalColoredText(text, EmbedColor.Ok);
|
||||
|
||||
public ResponseBuilder Confirm(LocStr str)
|
||||
=> Confirm(GetText(str));
|
||||
|
||||
public ResponseBuilder Pending(string text)
|
||||
=> InternalColoredText(text, EmbedColor.Ok);
|
||||
|
||||
public ResponseBuilder Pending(LocStr str)
|
||||
=> Pending(GetText(str));
|
||||
|
||||
public ResponseBuilder Error(string text)
|
||||
=> InternalColoredText(text, EmbedColor.Error);
|
||||
|
||||
public ResponseBuilder Error(LocStr str)
|
||||
=> Error(GetText(str));
|
||||
|
||||
|
||||
public ResponseBuilder UserBasedMentions()
|
||||
{
|
||||
sanitizeMentions = !((InternalResolveUser() as IGuildUser)?.GuildPermissions.MentionEveryone ?? false);
|
||||
return this;
|
||||
}
|
||||
|
||||
private IUser? InternalResolveUser()
|
||||
=> ctx?.User ?? user ?? msg?.Author;
|
||||
|
||||
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;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Sanitize(bool shouldSantize = true)
|
||||
{
|
||||
sanitizeMentions = shouldSantize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Context(ICommandContext ctx)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Message(IUserMessage msg)
|
||||
{
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder User(IUser user)
|
||||
{
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder NoReply()
|
||||
{
|
||||
shouldReply = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Interaction(NadekoInteraction inter)
|
||||
{
|
||||
// todo implement
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseBuilder Embeds(IReadOnlyCollection<EmbedBuilder> inputEmbeds)
|
||||
{
|
||||
embeds = inputEmbeds;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
18
src/NadekoBot/_common/Sender/ResponseBuilderExtensions.cs
Normal file
18
src/NadekoBot/_common/Sender/ResponseBuilderExtensions.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace NadekoBot.Extensions;
|
||||
|
||||
public static class ResponseBuilderExtensions
|
||||
{
|
||||
// todo delete this
|
||||
|
||||
public static EmbedBuilder WithColor(this EmbedBuilder eb, EmbedColor color)
|
||||
=> eb;
|
||||
|
||||
public static EmbedBuilder WithPendingColor(this EmbedBuilder eb)
|
||||
=> eb.WithColor(EmbedColor.Error);
|
||||
|
||||
public static EmbedBuilder WithOkColor(this EmbedBuilder eb)
|
||||
=> eb.WithColor(EmbedColor.Ok);
|
||||
|
||||
public static EmbedBuilder WithErrorColor(this EmbedBuilder eb)
|
||||
=> eb.WithColor(EmbedColor.Error);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Common.Configs;
|
||||
|
||||
// todo remove
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public interface IEmbedBuilderService
|
||||
{
|
||||
IEmbedBuilder Create(ICommandContext ctx = null);
|
||||
IEmbedBuilder Create(EmbedBuilder eb);
|
||||
EmbedBuilder Create(ICommandContext ctx = null);
|
||||
}
|
||||
|
||||
public class EmbedBuilderService : IEmbedBuilderService, INService
|
||||
@@ -16,11 +16,9 @@ public class EmbedBuilderService : IEmbedBuilderService, INService
|
||||
public EmbedBuilderService(BotConfigService botConfigService)
|
||||
=> _botConfigService = botConfigService;
|
||||
|
||||
public IEmbedBuilder Create(ICommandContext ctx = null)
|
||||
=> new DiscordEmbedBuilderWrapper(_botConfigService.Data);
|
||||
public EmbedBuilder Create(ICommandContext ctx = null)
|
||||
=> new EmbedBuilder();
|
||||
|
||||
public IEmbedBuilder Create(EmbedBuilder embed)
|
||||
=> new DiscordEmbedBuilderWrapper(_botConfigService.Data, embed);
|
||||
}
|
||||
|
||||
public sealed class DiscordEmbedBuilderWrapper : IEmbedBuilder
|
||||
@@ -34,31 +32,31 @@ public sealed class DiscordEmbedBuilderWrapper : IEmbedBuilder
|
||||
this.embed = embed ?? new EmbedBuilder();
|
||||
}
|
||||
|
||||
public IEmbedBuilder WithDescription(string desc)
|
||||
public EmbedBuilder WithDescription(string desc)
|
||||
=> Wrap(embed.WithDescription(desc));
|
||||
|
||||
public IEmbedBuilder WithTitle(string title)
|
||||
public EmbedBuilder WithTitle(string title)
|
||||
=> Wrap(embed.WithTitle(title));
|
||||
|
||||
public IEmbedBuilder AddField(string title, object value, bool isInline = false)
|
||||
public EmbedBuilder AddField(string title, object value, bool isInline = false)
|
||||
=> Wrap(embed.AddField(title, value, isInline));
|
||||
|
||||
public IEmbedBuilder WithFooter(string text, string iconUrl = null)
|
||||
public EmbedBuilder WithFooter(string text, string iconUrl = null)
|
||||
=> Wrap(embed.WithFooter(text, iconUrl));
|
||||
|
||||
public IEmbedBuilder WithAuthor(string name, string iconUrl = null, string url = null)
|
||||
public EmbedBuilder WithAuthor(string name, string iconUrl = null, string url = null)
|
||||
=> Wrap(embed.WithAuthor(name, iconUrl, url));
|
||||
|
||||
public IEmbedBuilder WithUrl(string url)
|
||||
public EmbedBuilder WithUrl(string url)
|
||||
=> Wrap(embed.WithUrl(url));
|
||||
|
||||
public IEmbedBuilder WithImageUrl(string url)
|
||||
public EmbedBuilder WithImageUrl(string url)
|
||||
=> Wrap(embed.WithImageUrl(url));
|
||||
|
||||
public IEmbedBuilder WithThumbnailUrl(string url)
|
||||
public EmbedBuilder WithThumbnailUrl(string url)
|
||||
=> Wrap(embed.WithThumbnailUrl(url));
|
||||
|
||||
public IEmbedBuilder WithColor(EmbedColor color)
|
||||
public EmbedBuilder WithColor(EmbedColor color)
|
||||
=> color switch
|
||||
{
|
||||
EmbedColor.Ok => Wrap(embed.WithColor(_botConfig.Color.Ok.ToDiscordColor())),
|
||||
@@ -67,15 +65,15 @@ public sealed class DiscordEmbedBuilderWrapper : IEmbedBuilder
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(color), "Unsupported EmbedColor type")
|
||||
};
|
||||
|
||||
public IEmbedBuilder WithDiscordColor(Color color)
|
||||
public EmbedBuilder WithDiscordColor(Color color)
|
||||
=> Wrap(embed.WithColor(color));
|
||||
|
||||
public Embed Build()
|
||||
=> embed.Build();
|
||||
|
||||
private IEmbedBuilder Wrap(EmbedBuilder eb)
|
||||
private EmbedBuilder Wrap(EmbedBuilder eb)
|
||||
{
|
||||
embed = eb;
|
||||
return this;
|
||||
return eb;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
|
||||
_medusae = medusae;
|
||||
}
|
||||
|
||||
public IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild)
|
||||
public EmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild)
|
||||
{
|
||||
var prefix = _ch.GetPrefix(guild);
|
||||
|
||||
@@ -39,17 +39,19 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
|
||||
|
||||
var culture = _loc.GetCultureInfo(guild);
|
||||
|
||||
var em = _eb.Create()
|
||||
.AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
|
||||
var em = new EmbedBuilder()
|
||||
.AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
|
||||
|
||||
_dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides);
|
||||
var reqs = GetCommandRequirements(com, (GuildPermission?)overrides);
|
||||
if (reqs.Any())
|
||||
em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs));
|
||||
|
||||
Extensions.Extensions.WithOkColor(em.AddField(_strings.GetText(strs.usage),
|
||||
string.Join("\n", com.RealRemarksArr(_strings, _medusae, culture, prefix).Map(arg => Format.Code(arg))))
|
||||
.WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild)));
|
||||
em
|
||||
.WithOkColor()
|
||||
.AddField(_strings.GetText(strs.usage),
|
||||
string.Join("\n", com.RealRemarksArr(_strings, _medusae, culture, prefix).Map(arg => Format.Code(arg))))
|
||||
.WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild));
|
||||
|
||||
var opt = GetNadekoOptionType(com.Attributes);
|
||||
if (opt is not null)
|
||||
@@ -72,31 +74,31 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
|
||||
public static List<string> GetCommandOptionHelpList(Type opt)
|
||||
{
|
||||
var strs = opt.GetProperties()
|
||||
.Select(x => x.GetCustomAttributes(true).FirstOrDefault(a => a is OptionAttribute))
|
||||
.Where(x => x is not null)
|
||||
.Cast<OptionAttribute>()
|
||||
.Select(x =>
|
||||
{
|
||||
var toReturn = $"`--{x.LongName}`";
|
||||
.Select(x => x.GetCustomAttributes(true).FirstOrDefault(a => a is OptionAttribute))
|
||||
.Where(x => x is not null)
|
||||
.Cast<OptionAttribute>()
|
||||
.Select(x =>
|
||||
{
|
||||
var toReturn = $"`--{x.LongName}`";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(x.ShortName))
|
||||
toReturn += $" (`-{x.ShortName}`)";
|
||||
if (!string.IsNullOrWhiteSpace(x.ShortName))
|
||||
toReturn += $" (`-{x.ShortName}`)";
|
||||
|
||||
toReturn += $" {x.HelpText} ";
|
||||
return toReturn;
|
||||
})
|
||||
.ToList();
|
||||
toReturn += $" {x.HelpText} ";
|
||||
return toReturn;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return strs;
|
||||
}
|
||||
|
||||
public static Type? GetNadekoOptionType(IEnumerable<Attribute> attributes)
|
||||
=> attributes
|
||||
.Select(a => a.GetType())
|
||||
.Where(a => a.IsGenericType
|
||||
&& a.GetGenericTypeDefinition() == typeof(NadekoOptionsAttribute<>))
|
||||
.Select(a => a.GenericTypeArguments[0])
|
||||
.FirstOrDefault();
|
||||
.Select(a => a.GetType())
|
||||
.Where(a => a.IsGenericType
|
||||
&& a.GetGenericTypeDefinition() == typeof(NadekoOptionsAttribute<>))
|
||||
.Select(a => a.GenericTypeArguments[0])
|
||||
.FirstOrDefault();
|
||||
|
||||
public static string[] GetCommandRequirements(CommandInfo cmd, GuildPerm? overrides = null)
|
||||
{
|
||||
@@ -107,38 +109,38 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
|
||||
|
||||
if (cmd.Preconditions.Any(x => x is NoPublicBotAttribute)
|
||||
|| cmd.Module
|
||||
.Preconditions
|
||||
.Any(x => x is NoPublicBotAttribute)
|
||||
.Preconditions
|
||||
.Any(x => x is NoPublicBotAttribute)
|
||||
|| cmd.Module.GetTopLevelModule()
|
||||
.Preconditions
|
||||
.Any(x => x is NoPublicBotAttribute))
|
||||
.Preconditions
|
||||
.Any(x => x is NoPublicBotAttribute))
|
||||
toReturn.Add("No Public Bot");
|
||||
|
||||
if (cmd.Preconditions
|
||||
.Any(x => x is OnlyPublicBotAttribute)
|
||||
.Any(x => x is OnlyPublicBotAttribute)
|
||||
|| cmd.Module
|
||||
.Preconditions
|
||||
.Any(x => x is OnlyPublicBotAttribute)
|
||||
.Preconditions
|
||||
.Any(x => x is OnlyPublicBotAttribute)
|
||||
|| cmd.Module.GetTopLevelModule()
|
||||
.Preconditions
|
||||
.Any(x => x is OnlyPublicBotAttribute))
|
||||
.Preconditions
|
||||
.Any(x => x is OnlyPublicBotAttribute))
|
||||
toReturn.Add("Only Public Bot");
|
||||
|
||||
var userPermString = cmd.Preconditions
|
||||
.Where(ca => ca is UserPermAttribute)
|
||||
.Cast<UserPermAttribute>()
|
||||
.Select(userPerm =>
|
||||
{
|
||||
if (userPerm.ChannelPermission is { } cPerm)
|
||||
return GetPreconditionString(cPerm);
|
||||
.Where(ca => ca is UserPermAttribute)
|
||||
.Cast<UserPermAttribute>()
|
||||
.Select(userPerm =>
|
||||
{
|
||||
if (userPerm.ChannelPermission is { } cPerm)
|
||||
return GetPreconditionString(cPerm);
|
||||
|
||||
if (userPerm.GuildPermission is { } gPerm)
|
||||
return GetPreconditionString(gPerm);
|
||||
if (userPerm.GuildPermission is { } gPerm)
|
||||
return GetPreconditionString(gPerm);
|
||||
|
||||
return string.Empty;
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Join('\n');
|
||||
return string.Empty;
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Join('\n');
|
||||
|
||||
if (overrides is null)
|
||||
{
|
||||
@@ -166,7 +168,7 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
|
||||
=> _strings.GetText(str, guild?.Id);
|
||||
}
|
||||
|
||||
public interface ICommandsUtilityService
|
||||
public interface ICommandsUtilityService
|
||||
{
|
||||
IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild);
|
||||
EmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild);
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public abstract record SmartText
|
||||
[JsonIgnore]
|
||||
public bool IsEmbedArray
|
||||
=> this is SmartEmbedTextArray;
|
||||
|
||||
public static implicit operator SmartText(string input)
|
||||
=> new SmartPlainText(input);
|
||||
|
||||
public static SmartText operator +(SmartText text, string input)
|
||||
=> text switch
|
||||
|
||||
@@ -18,8 +18,8 @@ public static class Extensions
|
||||
private static readonly Regex _urlRegex =
|
||||
new(@"^(https?|ftp)://(?<path>[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled);
|
||||
|
||||
public static IEmbedBuilder WithAuthor(this IEmbedBuilder eb, IUser author)
|
||||
=> eb.WithAuthor(author.ToString()!, author.RealAvatarUrl().ToString());
|
||||
// public static EmbedBuilder WithAuthor(this EmbedBuilder eb, IUser author)
|
||||
// => eb.WithAuthor(author.ToString()!, author.RealAvatarUrl().ToString());
|
||||
|
||||
public static Task EditAsync(this IUserMessage msg, SmartText text)
|
||||
=> text switch
|
||||
@@ -134,22 +134,22 @@ public static class Extensions
|
||||
private static string GetFullUsage(string commandName, string args, string prefix)
|
||||
=> $"{prefix}{commandName} {string.Format(args, prefix)}".TrimEnd();
|
||||
|
||||
public static IEmbedBuilder AddPaginatedFooter(this IEmbedBuilder embed, int curPage, int? lastPage)
|
||||
public static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage)
|
||||
{
|
||||
if (lastPage is not null)
|
||||
return embed.WithFooter($"{curPage + 1} / {lastPage + 1}");
|
||||
return embed.WithFooter(curPage.ToString());
|
||||
}
|
||||
|
||||
public static IEmbedBuilder WithOkColor(this IEmbedBuilder eb)
|
||||
=> eb.WithColor(EmbedColor.Ok);
|
||||
|
||||
public static IEmbedBuilder WithPendingColor(this IEmbedBuilder eb)
|
||||
=> eb.WithColor(EmbedColor.Pending);
|
||||
|
||||
public static IEmbedBuilder WithErrorColor(this IEmbedBuilder eb)
|
||||
=> eb.WithColor(EmbedColor.Error);
|
||||
|
||||
// public static EmbedBuilder WithOkColor(this EmbedBuilder eb)
|
||||
// => eb.WithColor(EmbedColor.Ok);
|
||||
//
|
||||
// public static EmbedBuilder WithPendingColor(this EmbedBuilder eb)
|
||||
// => eb.WithColor(EmbedColor.Pending);
|
||||
//
|
||||
// public static EmbedBuilder WithErrorColor(this EmbedBuilder eb)
|
||||
// => eb.WithColor(EmbedColor.Error);
|
||||
//
|
||||
public static IMessage DeleteAfter(this IUserMessage msg, float seconds, ILogCommandService? logService = null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
|
||||
@@ -48,9 +48,9 @@ public static class SocketMessageComponentExtensions
|
||||
|
||||
public static Task EmbedAsync(
|
||||
this SocketMessageComponent smc,
|
||||
IEmbedBuilder? embed,
|
||||
EmbedBuilder? embed,
|
||||
string plainText = "",
|
||||
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
||||
IReadOnlyCollection<EmbedBuilder>? embeds = null,
|
||||
NadekoInteraction? inter = null,
|
||||
bool ephemeral = false)
|
||||
=> smc.RespondAsync(plainText,
|
||||
@@ -66,7 +66,7 @@ public static class SocketMessageComponentExtensions
|
||||
bool ephemeral = false,
|
||||
NadekoInteraction? inter = null)
|
||||
{
|
||||
var builder = eb.Create().WithDescription(text);
|
||||
var builder = new EmbedBuilder().WithDescription(text);
|
||||
|
||||
builder = (type switch
|
||||
{
|
||||
@@ -81,13 +81,6 @@ public static class SocketMessageComponentExtensions
|
||||
|
||||
// embed title and optional footer overloads
|
||||
|
||||
public static Task RespondErrorAsync(
|
||||
this SocketMessageComponent smc,
|
||||
IEmbedBuilderService eb,
|
||||
string text,
|
||||
bool ephemeral = false)
|
||||
=> smc.RespondAsync(eb, text, MsgType.Error, ephemeral);
|
||||
|
||||
public static Task RespondConfirmAsync(
|
||||
this SocketMessageComponent smc,
|
||||
IEmbedBuilderService eb,
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace NadekoBot.Extensions;
|
||||
|
||||
public static class UserExtensions
|
||||
{
|
||||
public static async Task<IUserMessage> EmbedAsync(this IUser user, IEmbedBuilder embed, string msg = "")
|
||||
public static async Task<IUserMessage> EmbedAsync(this IUser user, EmbedBuilder embed, string msg = "")
|
||||
{
|
||||
var ch = await user.CreateDMChannelAsync();
|
||||
return await ch.EmbedAsync(embed, msg);
|
||||
@@ -17,13 +17,7 @@ public static class UserExtensions
|
||||
}
|
||||
|
||||
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, IEmbedBuilderService eb, string text)
|
||||
=> await user.SendMessageAsync("", embed: eb.Create().WithOkColor().WithDescription(text).Build());
|
||||
|
||||
public static async Task<IUserMessage> SendErrorAsync(this IUser user, IEmbedBuilderService eb, string error)
|
||||
=> await user.SendMessageAsync("", embed: eb.Create().WithErrorColor().WithDescription(error).Build());
|
||||
|
||||
public static async Task<IUserMessage> SendPendingAsync(this IUser user, IEmbedBuilderService eb, string message)
|
||||
=> await user.SendMessageAsync("", embed: eb.Create().WithPendingColor().WithDescription(message).Build());
|
||||
=> 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)
|
||||
|
||||
Reference in New Issue
Block a user