Finished new response system

This commit is contained in:
Kwoth
2024-05-02 06:47:01 +00:00
parent a25adefc65
commit fc4858830c
102 changed files with 1811 additions and 1818 deletions

View File

@@ -32,6 +32,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
- Added `.clubrename` command to uh rename your club - Added `.clubrename` command to uh rename your club
- For self-hosters: - For self-hosters:
- Added `.sqlselectcsv` which will return results in a csv file instead of an embed. - Added `.sqlselectcsv` which will return results in a csv file instead of an embed.
- Added a page parameter to `.feedlist`
### Changed ### Changed
@@ -46,6 +47,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
- `.feed` should now correctly accept (and show) the message which can be passed as the third parameter - `.feed` should now correctly accept (and show) the message which can be passed as the third parameter
- `.say` will now correctly report errors if the user or the bot don't have sufficent perms to send a message in the targeted channel - `.say` will now correctly report errors if the user or the bot don't have sufficent perms to send a message in the targeted channel
- Fixed a bug in .invitelist not paginating correctly
### Removed ### Removed

View File

@@ -95,7 +95,7 @@ public partial class Administration : NadekoModule<AdministrationService>
var guild = (SocketGuild)ctx.Guild; var guild = (SocketGuild)ctx.Guild;
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id); var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.server_delmsgoncmd)) .WithTitle(GetText(strs.server_delmsgoncmd))
.WithDescription(enabled ? "✅" : "❌"); .WithDescription(enabled ? "✅" : "❌");

View File

@@ -35,22 +35,22 @@ public partial class Administration
{ {
var result = _ds.SelectSql(sql); var result = _ds.SelectSql(sql);
return ctx.SendPaginatedConfirmAsync(0, return Response()
cur => .Paginated()
{ .Items(result.Results)
var items = result.Results.Skip(cur * 20).Take(20).ToList(); .PageSize(20)
.Page((items, _) =>
{
if (!items.Any())
return _sender.CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
if (!items.Any()) return _sender.CreateEmbed()
return new EmbedBuilder().WithErrorColor().WithFooter(sql).WithDescription("-");
return new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithFooter(sql) .WithFooter(sql)
.WithTitle(string.Join(" ║ ", result.ColumnNames)) .WithTitle(string.Join(" ║ ", result.ColumnNames))
.WithDescription(string.Join('\n', items.Select(x => string.Join(" ║ ", x)))); .WithDescription(string.Join('\n', items.Select(x => string.Join(" ║ ", x))));
}, })
result.Results.Count, .SendAsync();
20);
} }
[Cmd] [Cmd]
@@ -99,9 +99,9 @@ public partial class Administration
{ {
try try
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.sql_confirm_exec)) .WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(Format.Code(sql)); .WithDescription(Format.Code(sql));
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))
return; return;
@@ -119,8 +119,8 @@ public partial class Administration
[OwnerOnly] [OwnerOnly]
public async Task PurgeUser(ulong userId) public async Task PurgeUser(ulong userId)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString())))); .WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))
return; return;

View File

@@ -97,7 +97,7 @@ public class GreetService : INService, IReadyExecutor
{ {
var newContent = await _repSvc.ReplaceAsync(toSend, var newContent = await _repSvc.ReplaceAsync(toSend,
new(client: _client, guild: user.Guild, channel: channel, users: user)); new(client: _client, guild: user.Guild, channel: channel, users: user));
var toDelete = await channel.SendAsync(newContent); var toDelete = await _sender.Response(channel).Text(newContent).SendAsync();
if (conf.BoostMessageDeleteAfter > 0) if (conf.BoostMessageDeleteAfter > 0)
toDelete.DeleteAfter(conf.BoostMessageDeleteAfter); toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
@@ -217,12 +217,12 @@ public class GreetService : INService, IReadyExecutor
text = await _repSvc.ReplaceAsync(text, repCtx); text = await _repSvc.ReplaceAsync(text, repCtx);
try try
{ {
var toDelete = await channel.SendAsync(text); var toDelete = await _sender.Response(channel).Text(text).SendAsync();
if (conf.AutoDeleteByeMessagesTimer > 0) if (conf.AutoDeleteByeMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions || catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
ex.DiscordCode == DiscordErrorCode.UnknownChannel) || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{ {
Log.Warning(ex, Log.Warning(ex,
"Missing permissions to send a bye message, the bye message will be disabled on server: {GuildId}", "Missing permissions to send a bye message, the bye message will be disabled on server: {GuildId}",
@@ -258,12 +258,12 @@ public class GreetService : INService, IReadyExecutor
text = await _repSvc.ReplaceAsync(text, repCtx); text = await _repSvc.ReplaceAsync(text, repCtx);
try try
{ {
var toDelete = await channel.SendAsync(text); var toDelete = await _sender.Response(channel).Text(text).SendAsync();
if (conf.AutoDeleteGreetMessagesTimer > 0) if (conf.AutoDeleteGreetMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions || catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
ex.DiscordCode == DiscordErrorCode.UnknownChannel) || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{ {
Log.Warning(ex, Log.Warning(ex,
"Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}", "Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}",
@@ -352,9 +352,10 @@ public class GreetService : INService, IReadyExecutor
{ {
// if there is less than 10 embeds, add an embed with footer only // if there is less than 10 embeds, add an embed with footer only
seta.Embeds = seta.Embeds.Append(new SmartEmbedArrayElementText() seta.Embeds = seta.Embeds.Append(new SmartEmbedArrayElementText()
{ {
Footer = CreateFooterSource(user) Footer = CreateFooterSource(user)
}).ToArray(); })
.ToArray();
} }
} }
} }

View File

@@ -112,7 +112,7 @@ public partial class Administration
[Cmd] [Cmd]
public async Task LanguagesList() public async Task LanguagesList()
=> await Response().Embed(new EmbedBuilder() => await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.lang_list)) .WithTitle(GetText(strs.lang_list))
.WithDescription(string.Join("\n", .WithDescription(string.Join("\n",

View File

@@ -123,7 +123,7 @@ public class MuteService : INService
return; return;
_ = Task.Run(() => _sender.Response(user) _ = Task.Run(() => _sender.Response(user)
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithDescription($"You've been muted in {user.Guild} server") .WithDescription($"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString()) .AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString()) .AddField("Moderator", mod.ToString())
@@ -141,7 +141,7 @@ public class MuteService : INService
return; return;
_ = Task.Run(() => _sender.Response(user) _ = Task.Run(() => _sender.Response(user)
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithDescription($"You've been unmuted in {user.Guild} server") .WithDescription($"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString()) .AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString()) .AddField("Moderator", mod.ToString())

View File

@@ -25,8 +25,10 @@ public partial class Administration
var aggregatePerms = perms.Aggregate((acc, seed) => seed | acc); var aggregatePerms = perms.Aggregate((acc, seed) => seed | acc);
await _service.AddOverride(ctx.Guild.Id, cmd.Name, aggregatePerms); await _service.AddOverride(ctx.Guild.Id, cmd.Name, aggregatePerms);
await Response().Confirm(strs.perm_override(Format.Bold(aggregatePerms.ToString()), await Response()
Format.Code(cmd.Name))).SendAsync(); .Confirm(strs.perm_override(Format.Bold(aggregatePerms.ToString()),
Format.Code(cmd.Name)))
.SendAsync();
} }
[Cmd] [Cmd]
@@ -34,9 +36,9 @@ public partial class Administration
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task DiscordPermOverrideReset() public async Task DiscordPermOverrideReset()
{ {
var result = await PromptUserConfirmAsync(new EmbedBuilder() var result = await PromptUserConfirmAsync(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText(strs.perm_override_all_confirm))); .WithDescription(GetText(strs.perm_override_all_confirm)));
if (!result) if (!result)
return; return;
@@ -54,27 +56,28 @@ public partial class Administration
if (--page < 0) if (--page < 0)
return; return;
var overrides = await _service.GetAllOverrides(ctx.Guild.Id); var allOverrides = await _service.GetAllOverrides(ctx.Guild.Id);
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .Items(allOverrides)
var eb = new EmbedBuilder().WithTitle(GetText(strs.perm_overrides)).WithOkColor(); .PageSize(9)
.CurrentPage(page)
.Page((items, _) =>
{
var eb = _sender.CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
var thisPageOverrides = overrides.Skip(9 * curPage).Take(9).ToList(); if (items.Count == 0)
eb.WithDescription(GetText(strs.perm_override_page_none));
else
{
eb.WithDescription(items.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}")
.Join("\n"));
}
if (thisPageOverrides.Count == 0) return eb;
eb.WithDescription(GetText(strs.perm_override_page_none)); })
else .SendAsync();
{
eb.WithDescription(thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}")
.Join("\n"));
}
return eb;
},
overrides.Count,
9);
} }
} }
} }

View File

@@ -241,7 +241,7 @@ public partial class Administration
return; return;
} }
var embed = new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.prot_active)); var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active));
if (spam is not null) if (spam is not null)
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true); embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);

View File

@@ -37,7 +37,8 @@ public partial class Administration
return; return;
} }
if (ctx.User.Id != ctx.Guild.OwnerId && ((IGuildUser)ctx.User).GetRoles().Max(x => x.Position) <= role.Position) if (ctx.User.Id != ctx.Guild.OwnerId
&& ((IGuildUser)ctx.User).GetRoles().Max(x => x.Position) <= role.Position)
{ {
await Response().Error(strs.hierarchy).SendAsync(); await Response().Error(strs.hierarchy).SendAsync();
return; return;
@@ -72,47 +73,51 @@ public partial class Administration
if (--page < 0) if (--page < 0)
return; return;
var reros = await _rero.GetReactionRolesAsync(ctx.Guild.Id); var allReros = await _rero.GetReactionRolesAsync(ctx.Guild.Id);
await ctx.SendPaginatedConfirmAsync(page, curPage => await Response()
{ .Paginated()
var embed = new EmbedBuilder() .Items(allReros.OrderBy(x => x.Group).ToList())
.WithOkColor(); .PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var embed = _sender.CreateEmbed()
.WithOkColor();
var content = string.Empty; var content = string.Empty;
foreach (var g in reros.OrderBy(x => x.Group) foreach (var g in items
.Skip(curPage * 10) .GroupBy(x => x.MessageId)
.GroupBy(x => x.MessageId) .OrderBy(x => x.Key))
.OrderBy(x => x.Key)) {
{ var messageId = g.Key;
var messageId = g.Key; content +=
content += $"[{messageId}](https://discord.com/channels/{ctx.Guild.Id}/{g.First().ChannelId}/{g.Key})\n";
$"[{messageId}](https://discord.com/channels/{ctx.Guild.Id}/{g.First().ChannelId}/{g.Key})\n";
var groupGroups = g.GroupBy(x => x.Group); var groupGroups = g.GroupBy(x => x.Group);
foreach (var ggs in groupGroups) foreach (var ggs in groupGroups)
{ {
content += $"`< {(g.Key == 0 ? ("Not Exclusive (Group 0)") : ($"Group {ggs.Key}"))} >`\n"; content += $"`< {(g.Key == 0 ? ("Not Exclusive (Group 0)") : ($"Group {ggs.Key}"))} >`\n";
foreach (var rero in ggs) foreach (var rero in ggs)
{ {
content += content +=
$"\t{rero.Emote} -> {(ctx.Guild.GetRole(rero.RoleId)?.Mention ?? "<missing role>")}"; $"\t{rero.Emote} -> {(ctx.Guild.GetRole(rero.RoleId)?.Mention ?? "<missing role>")}";
if (rero.LevelReq > 0) if (rero.LevelReq > 0)
content += $" (lvl {rero.LevelReq}+)"; content += $" (lvl {rero.LevelReq}+)";
content += '\n'; content += '\n';
} }
} }
}
} embed.WithDescription(string.IsNullOrWhiteSpace(content)
? "There are no reaction roles on this server"
: content);
embed.WithDescription(string.IsNullOrWhiteSpace(content) return embed;
? "There are no reaction roles on this server" })
: content); .SendAsync();
return embed;
}, reros.Count, 10);
} }
[Cmd] [Cmd]

View File

@@ -78,7 +78,7 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
if (user is null) if (user is null)
return; return;
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor($"NadekoBot v{latestVersion} Released!") .WithAuthor($"NadekoBot v{latestVersion} Released!")
.WithTitle("Changelog") .WithTitle("Changelog")

View File

@@ -51,7 +51,6 @@ public partial class Administration
{ {
var downloadUsersTask = guild.DownloadUsersAsync(); var downloadUsersTask = guild.DownloadUsersAsync();
var message = await Response().Pending(strs.cache_users_pending).SendAsync(); var message = await Response().Pending(strs.cache_users_pending).SendAsync();
using var dbContext = _db.GetDbContext();
await downloadUsersTask; await downloadUsersTask;
@@ -62,10 +61,10 @@ public partial class Administration
var (added, updated) = await _service.RefreshUsersAsync(users); var (added, updated) = await _service.RefreshUsersAsync(users);
await message.ModifyAsync(x => await message.ModifyAsync(x =>
x.Embed = new EmbedBuilder() x.Embed = _sender.CreateEmbed()
.WithDescription(GetText(strs.cache_users_done(added, updated))) .WithDescription(GetText(strs.cache_users_done(added, updated)))
.WithOkColor() .WithOkColor()
.Build() .Build()
); );
} }
@@ -82,6 +81,7 @@ public partial class Administration
{ {
var fakeMessage = new DoAsUserMessage(msg, user, message); var fakeMessage = new DoAsUserMessage(msg, user, message);
await _cmdHandler.TryRunCommand(sg, ch, fakeMessage); await _cmdHandler.TryRunCommand(sg, ch, fakeMessage);
} }
else else
@@ -113,14 +113,16 @@ public partial class Administration
}; };
_service.AddNewAutoCommand(cmd); _service.AddNewAutoCommand(cmd);
await Response().Embed(new EmbedBuilder() await Response()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithTitle(GetText(strs.scadd)) .WithOkColor()
.AddField(GetText(strs.server), .WithTitle(GetText(strs.scadd))
cmd.GuildId is null ? "-" : $"{cmd.GuildName}/{cmd.GuildId}", .AddField(GetText(strs.server),
true) cmd.GuildId is null ? "-" : $"{cmd.GuildName}/{cmd.GuildId}",
.AddField(GetText(strs.channel), $"{cmd.ChannelName}/{cmd.ChannelId}", true) true)
.AddField(GetText(strs.command_text), cmdText)).SendAsync(); .AddField(GetText(strs.channel), $"{cmd.ChannelName}/{cmd.ChannelId}", true)
.AddField(GetText(strs.command_text), cmdText))
.SendAsync();
} }
[Cmd] [Cmd]
@@ -328,18 +330,21 @@ public partial class Administration
+ $"| {st.GuildCount.ToString().PadBoth(maxGuildCountLength)} `"; + $"| {st.GuildCount.ToString().PadBoth(maxGuildCountLength)} `";
}) })
.ToArray(); .ToArray();
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .Items(allShardStrings)
var str = string.Join("\n", allShardStrings.Skip(25 * curPage).Take(25)); .PageSize(25)
.CurrentPage(page)
.Page((items, _) =>
{
var str = string.Join("\n", items);
if (string.IsNullOrWhiteSpace(str)) if (string.IsNullOrWhiteSpace(str))
str = GetText(strs.no_shards_on_page); str = GetText(strs.no_shards_on_page);
return new EmbedBuilder().WithOkColor().WithDescription($"{status}\n\n{str}"); return _sender.CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}");
}, })
allShardStrings.Length, .SendAsync();
25);
} }
private static string ConnectionStateToEmoji(ShardStatus status) private static string ConnectionStateToEmoji(ShardStatus status)
@@ -566,7 +571,7 @@ public partial class Administration
return; return;
text = await repSvc.ReplaceAsync(text, repCtx); text = await repSvc.ReplaceAsync(text, repCtx);
await ch.SendAsync(text); await Response().Channel(ch).Text(text).SendAsync();
} }
else if (ids[1].ToUpperInvariant().StartsWith("U:", StringComparison.InvariantCulture)) else if (ids[1].ToUpperInvariant().StartsWith("U:", StringComparison.InvariantCulture))
{ {
@@ -577,7 +582,7 @@ public partial class Administration
var ch = await user.CreateDMChannelAsync(); var ch = await user.CreateDMChannelAsync();
text = await repSvc.ReplaceAsync(text, repCtx); text = await repSvc.ReplaceAsync(text, repCtx);
await ch.SendAsync(text); await Response().Channel(ch).Text(text).SendAsync();
} }
else else
{ {

View File

@@ -98,53 +98,54 @@ public partial class Administration
var (exclusive, roles, groups) = _service.GetRoles(ctx.Guild); var (exclusive, roles, groups) = _service.GetRoles(ctx.Guild);
await ctx.SendPaginatedConfirmAsync(page, await Response()
cur => .Paginated()
{ .Items(roles.OrderBy(x => x.Model.Group).ToList())
var rolesStr = new StringBuilder(); .PageSize(20)
var roleGroups = roles.OrderBy(x => x.Model.Group) .CurrentPage(page)
.Skip(cur * 20) .Page((items, _) =>
.Take(20) {
.GroupBy(x => x.Model.Group) var rolesStr = new StringBuilder();
.OrderBy(x => x.Key); var roleGroups = items
.GroupBy(x => x.Model.Group)
.OrderBy(x => x.Key);
foreach (var kvp in roleGroups) foreach (var kvp in roleGroups)
{ {
string groupNameText; string groupNameText;
if (!groups.TryGetValue(kvp.Key, out var name)) if (!groups.TryGetValue(kvp.Key, out var name))
groupNameText = Format.Bold(GetText(strs.self_assign_group(kvp.Key))); groupNameText = Format.Bold(GetText(strs.self_assign_group(kvp.Key)));
else else
groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}"); groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}");
rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫"); rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫");
foreach (var (model, role) in kvp.AsEnumerable()) foreach (var (model, role) in kvp.AsEnumerable())
{ {
if (role is null) if (role is null)
{ {
} }
else else
{ {
// first character is invisible space // first character is invisible space
if (model.LevelRequirement == 0) if (model.LevelRequirement == 0)
rolesStr.AppendLine(" " + role.Name); rolesStr.AppendLine(" " + role.Name);
else else
rolesStr.AppendLine(" " + role.Name + $" (lvl {model.LevelRequirement}+)"); rolesStr.AppendLine(" " + role.Name + $" (lvl {model.LevelRequirement}+)");
} }
} }
rolesStr.AppendLine(); rolesStr.AppendLine();
} }
return new EmbedBuilder() return _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(Format.Bold(GetText(strs.self_assign_list(roles.Count())))) .WithTitle(Format.Bold(GetText(strs.self_assign_list(roles.Count()))))
.WithDescription(rolesStr.ToString()) .WithDescription(rolesStr.ToString())
.WithFooter(exclusive .WithFooter(exclusive
? GetText(strs.self_assign_are_exclusive) ? GetText(strs.self_assign_are_exclusive)
: GetText(strs.self_assign_are_not_exclusive)); : GetText(strs.self_assign_are_not_exclusive));
}, })
roles.Count(), .SendAsync();
20);
} }
[Cmd] [Cmd]

View File

@@ -164,7 +164,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
var title = GetText(logChannel.Guild, strs.thread_deleted); var title = GetText(logChannel.Guild, strs.thread_deleted);
await _sender.Response(logChannel).Embed(new EmbedBuilder() await _sender.Response(logChannel).Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("🗑 " + title) .WithTitle("🗑 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
@@ -194,7 +194,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
var title = GetText(logChannel.Guild, strs.thread_created); var title = GetText(logChannel.Guild, strs.thread_created);
await _sender.Response(logChannel).Embed(new EmbedBuilder() await _sender.Response(logChannel).Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + title) .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
@@ -327,7 +327,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserWarned)) is null) if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserWarned)) is null)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"⚠️ User Warned") .WithTitle($"⚠️ User Warned")
.WithDescription($"<@{arg.UserId}> | {arg.UserId}") .WithDescription($"<@{arg.UserId}> | {arg.UserId}")
@@ -356,7 +356,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) is null) if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) is null)
return; return;
var embed = new EmbedBuilder(); var embed = _sender.CreateEmbed();
if (before.Username != after.Username) if (before.Username != after.Username)
{ {
@@ -495,7 +495,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
break; break;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor(mutes) .WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter(CurrentTime(usr.Guild)) .WithFooter(CurrentTime(usr.Guild))
@@ -542,7 +542,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
break; break;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor(mutes) .WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter($"{CurrentTime(usr.Guild)}") .WithFooter($"{CurrentTime(usr.Guild)}")
@@ -596,7 +596,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
break; break;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor($"🛡 Anti-{protection}") .WithAuthor($"🛡 Anti-{protection}")
.WithTitle(GetText(logChannel.Guild, strs.users) + " " + punishment) .WithTitle(GetText(logChannel.Guild, strs.users) + " " + punishment)
.WithDescription(string.Join("\n", users.Select(u => u.ToString()))) .WithDescription(string.Join("\n", users.Select(u => u.ToString())))
@@ -649,7 +649,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if (logSetting.UserUpdatedId is not null if (logSetting.UserUpdatedId is not null
&& (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) is not null) && (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) is not null)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithFooter(CurrentTime(before.Guild)) .WithFooter(CurrentTime(before.Guild))
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
@@ -720,7 +720,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) is null) if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) is null)
return; return;
var embed = new EmbedBuilder().WithOkColor().WithFooter(CurrentTime(before.Guild)); var embed = _sender.CreateEmbed().WithOkColor().WithFooter(CurrentTime(before.Guild));
var beforeTextChannel = cbefore as ITextChannel; var beforeTextChannel = cbefore as ITextChannel;
var afterTextChannel = cafter as ITextChannel; var afterTextChannel = cafter as ITextChannel;
@@ -776,7 +776,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
else else
title = GetText(logChannel.Guild, strs.text_chan_destroyed); title = GetText(logChannel.Guild, strs.text_chan_destroyed);
await _sender.Response(logChannel).Embed(new EmbedBuilder() await _sender.Response(logChannel).Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + title) .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
@@ -812,7 +812,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
else else
title = GetText(logChannel.Guild, strs.text_chan_created); title = GetText(logChannel.Guild, strs.text_chan_created);
await _sender.Response(logChannel).Embed(new EmbedBuilder() await _sender.Response(logChannel).Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + title) .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
@@ -915,7 +915,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
ITextChannel? logChannel; ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserLeft)) is null) if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserLeft)) is null)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("❌ " + GetText(logChannel.Guild, strs.user_left)) .WithTitle("❌ " + GetText(logChannel.Guild, strs.user_left))
.WithDescription(usr.ToString()) .WithDescription(usr.ToString())
@@ -948,7 +948,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) is null) if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) is null)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("✅ " + GetText(logChannel.Guild, strs.user_joined)) .WithTitle("✅ " + GetText(logChannel.Guild, strs.user_joined))
.WithDescription($"{usr.Mention} `{usr}`") .WithDescription($"{usr.Mention} `{usr}`")
@@ -989,7 +989,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
ITextChannel? logChannel; ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) is null) if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) is null)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("♻️ " + GetText(logChannel.Guild, strs.user_unbanned)) .WithTitle("♻️ " + GetText(logChannel.Guild, strs.user_unbanned))
.WithDescription(usr.ToString()!) .WithDescription(usr.ToString()!)
@@ -1036,7 +1036,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
{ {
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned)) .WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned))
.WithDescription(usr.ToString()!) .WithDescription(usr.ToString()!)
@@ -1087,7 +1087,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
return; return;
var resolvedMessage = msg.Resolve(TagHandling.FullName); var resolvedMessage = msg.Resolve(TagHandling.FullName);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("🗑 " .WithTitle("🗑 "
+ GetText(logChannel.Guild, strs.msg_del(((ITextChannel)msg.Channel).Name))) + GetText(logChannel.Guild, strs.msg_del(((ITextChannel)msg.Channel).Name)))
@@ -1147,7 +1147,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|| logChannel.Id == after.Channel.Id) || logChannel.Id == after.Channel.Id)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("📝 " .WithTitle("📝 "
+ GetText(logChannel.Guild, + GetText(logChannel.Guild,

View File

@@ -35,7 +35,7 @@ public partial class Administration
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList() var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
?? new List<IgnoredLogItem>(); ?? new List<IgnoredLogItem>();
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField(GetText(strs.log_ignored_channels), .AddField(GetText(strs.log_ignored_channels),
chs.Count == 0 chs.Count == 0

View File

@@ -33,17 +33,20 @@ public partial class Administration
if (flip) if (flip)
return $"{offset} {Format.Code(nameStr)}"; return $"{offset} {Format.Code(nameStr)}";
return $"{Format.Code(offset)} {nameStr}"; return $"{Format.Code(offset)} {nameStr}";
}); })
.ToList();
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => new EmbedBuilder() .Paginated()
.WithOkColor() .Items(timezoneStrings)
.WithTitle(GetText(strs.timezones_available)) .PageSize(timezonesPerPage)
.WithDescription(string.Join("\n", .CurrentPage(page)
timezoneStrings.Skip(curPage * timezonesPerPage).Take(timezonesPerPage))), .Page((items, _) => _sender.CreateEmbed()
timezones.Length, .WithOkColor()
timezonesPerPage); .WithTitle(GetText(strs.timezones_available))
.WithDescription(string.Join("\n", items)))
.SendAsync();
} }
[Cmd] [Cmd]

View File

@@ -66,7 +66,7 @@ public partial class Administration
try try
{ {
await _sender.Response(user) await _sender.Response(user)
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString()))) .WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
.AddField(GetText(strs.moderator), ctx.User.ToString()) .AddField(GetText(strs.moderator), ctx.User.ToString())
@@ -86,7 +86,7 @@ public partial class Administration
catch (Exception ex) catch (Exception ex)
{ {
Log.Warning(ex, "Exception occured while warning a user"); Log.Warning(ex, "Exception occured while warning a user");
var errorEmbed = new EmbedBuilder().WithErrorColor() var errorEmbed = _sender.CreateEmbed().WithErrorColor()
.WithDescription(GetText(strs.cant_apply_punishment)); .WithDescription(GetText(strs.cant_apply_punishment));
if (dmFailed) if (dmFailed)
@@ -96,7 +96,7 @@ public partial class Administration
return; return;
} }
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
if (punishment is null) if (punishment is null)
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString())))); embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
else else
@@ -197,45 +197,46 @@ public partial class Administration
var allWarnings = _service.UserWarnings(ctx.Guild.Id, userId); var allWarnings = _service.UserWarnings(ctx.Guild.Id, userId);
await ctx.SendPaginatedConfirmAsync(inputPage, await Response()
page => .Paginated()
{ .Items(allWarnings)
var warnings = allWarnings.Skip(page * 9).Take(9).ToArray(); .PageSize(9)
.CurrentPage(inputPage)
.Page((warnings, page) =>
{
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString(); if (!warnings.Any())
var embed = new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.warnlog_for(user))); embed.WithDescription(GetText(strs.warnings_none));
else
{
var descText = GetText(strs.warn_count(
Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()),
Format.Bold(warnings.Sum(x => x.Weight).ToString())));
if (!warnings.Any()) embed.WithDescription(descText);
embed.WithDescription(GetText(strs.warnings_none));
else
{
var descText = GetText(strs.warn_count(
Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()),
Format.Bold(warnings.Sum(x => x.Weight).ToString())));
embed.WithDescription(descText); var i = page * 9;
foreach (var w in warnings)
{
i++;
var name = GetText(strs.warned_on_by(w.DateAdded?.ToString("dd.MM.yyy"),
w.DateAdded?.ToString("HH:mm"),
w.Moderator));
var i = page * 9; if (w.Forgiven)
foreach (var w in warnings) name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
{
i++;
var name = GetText(strs.warned_on_by(w.DateAdded?.ToString("dd.MM.yyy"),
w.DateAdded?.ToString("HH:mm"),
w.Moderator));
if (w.Forgiven)
name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
embed.AddField($"#`{i}` " + name, embed.AddField($"#`{i}` " + name,
Format.Code(GetText(strs.warn_weight(w.Weight))) + '\n' + w.Reason.TrimTo(1000)); Format.Code(GetText(strs.warn_weight(w.Weight))) + '\n' + w.Reason.TrimTo(1000));
} }
} }
return embed; return embed;
}, })
allWarnings.Length, .SendAsync();
9);
} }
[Cmd] [Cmd]
@@ -245,31 +246,32 @@ public partial class Administration
{ {
if (--page < 0) if (--page < 0)
return; return;
var warnings = _service.WarnlogAll(ctx.Guild.Id); var allWarnings = _service.WarnlogAll(ctx.Guild.Id);
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .Items(allWarnings)
var ws = warnings.Skip(curPage * 15) .PageSize(15)
.Take(15) .CurrentPage(page)
.ToArray() .Page((warnings, _) =>
.Select(x => {
{ var ws = warnings
var all = x.Count(); .Select(x =>
var forgiven = x.Count(y => y.Forgiven); {
var total = all - forgiven; var all = x.Count();
var usr = ((SocketGuild)ctx.Guild).GetUser(x.Key); var forgiven = x.Count(y => y.Forgiven);
return (usr?.ToString() ?? x.Key.ToString()) var total = all - forgiven;
+ $" | {total} ({all} - {forgiven})"; var usr = ((SocketGuild)ctx.Guild).GetUser(x.Key);
}); return (usr?.ToString() ?? x.Key.ToString())
+ $" | {total} ({all} - {forgiven})";
});
return new EmbedBuilder() return _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.warnings_list)) .WithTitle(GetText(strs.warnings_list))
.WithDescription(string.Join("\n", ws)); .WithDescription(string.Join("\n", ws));
}, })
warnings.Length, .SendAsync();
15);
} }
[Cmd] [Cmd]
@@ -450,7 +452,7 @@ public partial class Administration
var user = await ctx.Client.GetUserAsync(userId); var user = await ctx.Client.GetUserAsync(userId);
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune); await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true) .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
@@ -478,10 +480,11 @@ public partial class Administration
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
await ctx.Channel.EmbedAsync(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField("ID", userId.ToString(), true)); .AddField("ID", userId.ToString(), true))
.SendAsync();
} }
else else
await Ban(user, msg); await Ban(user, msg);
@@ -514,7 +517,7 @@ public partial class Administration
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@@ -709,7 +712,7 @@ public partial class Administration
try { await ctx.Guild.RemoveBanAsync(user); } try { await ctx.Guild.RemoveBanAsync(user); }
catch { await ctx.Guild.RemoveBanAsync(user); } catch { await ctx.Guild.RemoveBanAsync(user); }
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("☣ " + GetText(strs.sb_user)) .WithTitle("☣ " + GetText(strs.sb_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@@ -764,7 +767,7 @@ public partial class Administration
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512)); await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.kicked_user)) .WithTitle(GetText(strs.kicked_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@@ -797,7 +800,7 @@ public partial class Administration
{ {
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg)); var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
await _sender.Response(user) await _sender.Response(user)
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(dmMessage)) .WithDescription(dmMessage))
.SendAsync(); .SendAsync();
@@ -809,7 +812,7 @@ public partial class Administration
await user.SetTimeOutAsync(time.Time); await user.SetTimeOutAsync(time.Time);
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⏳ " + GetText(strs.timedout_user)) .WithTitle("⏳ " + GetText(strs.timedout_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@@ -870,7 +873,7 @@ public partial class Administration
if (string.IsNullOrWhiteSpace(missStr)) if (string.IsNullOrWhiteSpace(missStr))
missStr = "-"; missStr = "-";
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count))) .WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
.AddField(GetText(strs.invalid(missing.Count)), missStr) .AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithPendingColor(); .WithPendingColor();
@@ -890,7 +893,7 @@ public partial class Administration
} }
} }
await banningMessage.ModifyAsync(x => x.Embed = new EmbedBuilder() await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_ban_completed(banning.Count()))) GetText(strs.mass_ban_completed(banning.Count())))
.AddField(GetText(strs.invalid(missing.Count)), missStr) .AddField(GetText(strs.invalid(missing.Count)), missStr)
@@ -915,11 +918,13 @@ public partial class Administration
missStr = "-"; missStr = "-";
//send a message but don't wait for it //send a message but don't wait for it
var banningMessageTask = ctx.Channel.EmbedAsync(new EmbedBuilder() var banningMessageTask = Response()
.WithDescription( .Embed(_sender.CreateEmbed()
GetText(strs.mass_kill_in_progress(bans.Count()))) .WithDescription(
.AddField(GetText(strs.invalid(missing)), missStr) GetText(strs.mass_kill_in_progress(bans.Count())))
.WithPendingColor()); .AddField(GetText(strs.invalid(missing)), missStr)
.WithPendingColor())
.SendAsync();
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
//do the banning //do the banning
@@ -935,7 +940,7 @@ public partial class Administration
//wait for the message and edit it //wait for the message and edit it
var banningMessage = await banningMessageTask; var banningMessage = await banningMessageTask;
await banningMessage.ModifyAsync(x => x.Embed = new EmbedBuilder() await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_completed(bans.Count()))) GetText(strs.mass_kill_completed(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)

View File

@@ -68,7 +68,7 @@ public partial class Administration
else else
text = GetText(strs.no_vcroles); text = GetText(strs.no_vcroles);
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.vc_role_list)) .WithTitle(GetText(strs.vc_role_list))
.WithDescription(text)).SendAsync(); .WithDescription(text)).SendAsync();

View File

@@ -14,7 +14,7 @@ public static class NadekoExpressionExtensions
IUserMessage ctx, IUserMessage ctx,
IReplacementService repSvc, IReplacementService repSvc,
DiscordSocketClient client, DiscordSocketClient client,
bool sanitize) IMessageSenderService sender)
{ {
var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel; var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel;
@@ -46,7 +46,7 @@ public static class NadekoExpressionExtensions
var text = SmartText.CreateFrom(cr.Response); var text = SmartText.CreateFrom(cr.Response);
text = await repSvc.ReplaceAsync(text, repCtx); text = await repSvc.ReplaceAsync(text, repCtx);
return await channel.SendAsync(text, sanitize); return await sender.Response(channel).Text(text).Sanitize(false).SendAsync();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -33,7 +33,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message); var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
await Response() await Response()
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.expr_new)) .WithTitle(GetText(strs.expr_new))
.WithDescription($"#{new kwum(ex.Id)}") .WithDescription($"#{new kwum(ex.Id)}")
@@ -66,6 +66,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
await ExprAddInternalAsync(key, message); await ExprAddInternalAsync(key, message);
} }
[Cmd] [Cmd]
public async Task ExprAdd(string key, [Leftover] string message) public async Task ExprAdd(string key, [Leftover] string message)
{ {
@@ -102,13 +103,15 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
var ex = await _service.EditAsync(ctx.Guild?.Id, id, message); var ex = await _service.EditAsync(ctx.Guild?.Id, id, message);
if (ex is not null) if (ex is not null)
{ {
await ctx.Channel.EmbedAsync(new EmbedBuilder() await Response()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithTitle(GetText(strs.expr_edited)) .WithOkColor()
.WithDescription($"#{id}") .WithTitle(GetText(strs.expr_edited))
.AddField(GetText(strs.trigger), ex.Trigger) .WithDescription($"#{id}")
.AddField(GetText(strs.response), .AddField(GetText(strs.trigger), ex.Trigger)
message.Length > 1024 ? GetText(strs.redacted_too_long) : message)); .AddField(GetText(strs.response),
message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
.SendAsync();
} }
else else
{ {
@@ -152,7 +155,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
: " // " + string.Join(" ", ex.GetReactions()))) : " // " + string.Join(" ", ex.GetReactions())))
.Join('\n'); .Join('\n');
return new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc); return _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
}) })
.SendAsync(); .SendAsync();
} }
@@ -168,12 +171,14 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
return; return;
} }
await ctx.Channel.EmbedAsync(new EmbedBuilder() await Response()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithDescription($"#{id}") .WithOkColor()
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024)) .WithDescription($"#{id}")
.AddField(GetText(strs.response), .AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
found.Response.TrimTo(1000).Replace("](", "]\\("))); .AddField(GetText(strs.response),
found.Response.TrimTo(1000).Replace("](", "]\\(")))
.SendAsync();
} }
public async Task ExprDeleteInternalAsync(kwum id) public async Task ExprDeleteInternalAsync(kwum id)
@@ -182,12 +187,14 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
if (ex is not null) if (ex is not null)
{ {
await ctx.Channel.EmbedAsync(new EmbedBuilder() await Response()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithTitle(GetText(strs.expr_deleted)) .WithOkColor()
.WithDescription($"#{id}") .WithTitle(GetText(strs.expr_deleted))
.AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024)) .WithDescription($"#{id}")
.AddField(GetText(strs.response), ex.Response.TrimTo(1024))); .AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
.AddField(GetText(strs.response), ex.Response.TrimTo(1024)))
.SendAsync();
} }
else else
{ {
@@ -332,7 +339,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task ExprClear() public async Task ExprClear()
{ {
if (await PromptUserConfirmAsync(new EmbedBuilder() if (await PromptUserConfirmAsync(_sender.CreateEmbed()
.WithTitle("Expression clear") .WithTitle("Expression clear")
.WithDescription("This will delete all expressions on this server."))) .WithDescription("This will delete all expressions on this server.")))
{ {

View File

@@ -280,7 +280,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
} }
} }
var sentMsg = await expr.Send(msg, _repSvc, _client, false); var sentMsg = await expr.Send(msg, _repSvc, _client, _sender);
var reactions = expr.GetReactions(); var reactions = expr.GetReactions();
foreach (var reaction in reactions) foreach (var reaction in reactions)

View File

@@ -128,7 +128,7 @@ public partial class Gambling
raceMessage = await Response().Confirm(text).SendAsync(); raceMessage = await Response().Confirm(text).SendAsync();
else else
{ {
await msg.ModifyAsync(x => x.Embed = new EmbedBuilder() await msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.animal_race)) .WithTitle(GetText(strs.animal_race))
.WithDescription(text) .WithDescription(text)
.WithOkColor() .WithOkColor()

View File

@@ -59,7 +59,7 @@ public partial class Gambling
{ {
var bal = await _bank.GetBalanceAsync(ctx.User.Id); var bal = await _bank.GetBalanceAsync(ctx.User.Id);
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText(strs.bank_balance(N(bal)))); .WithDescription(GetText(strs.bank_balance(N(bal))));

View File

@@ -95,7 +95,7 @@ public partial class Gambling
var cStr = string.Concat(c.Select(x => x[..^1] + " ")); var cStr = string.Concat(c.Select(x => x[..^1] + " "));
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " ")); cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("BlackJack") .WithTitle("BlackJack")
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr); .AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);

View File

@@ -150,7 +150,7 @@ public partial class Gambling
else else
title = GetText(strs.connect4_draw); title = GetText(strs.connect4_draw);
return msg.ModifyAsync(x => x.Embed = new EmbedBuilder() return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithTitle(title) .WithTitle(title)
.WithDescription(GetGameStateText(game)) .WithDescription(GetGameStateText(game))
.WithOkColor() .WithOkColor()
@@ -160,7 +160,7 @@ public partial class Gambling
private async Task Game_OnGameStateUpdated(Connect4Game game) private async Task Game_OnGameStateUpdated(Connect4Game game)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}") .WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
.WithDescription(GetGameStateText(game)) .WithDescription(GetGameStateText(game))
.WithOkColor(); .WithOkColor();

View File

@@ -38,7 +38,7 @@ public partial class Gambling
var fileName = $"dice.{format.FileExtensions.First()}"; var fileName = $"dice.{format.FileExtensions.First()}";
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.AddField(GetText(strs.roll2), gen) .AddField(GetText(strs.roll2), gen)
@@ -115,7 +115,7 @@ public partial class Gambling
d.Dispose(); d.Dispose();
var imageName = $"dice.{format.FileExtensions.First()}"; var imageName = $"dice.{format.FileExtensions.First()}";
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true) .AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
@@ -141,7 +141,7 @@ public partial class Gambling
for (var i = 0; i < n1; i++) for (var i = 0; i < n1; i++)
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]); rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString())))) .WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
@@ -170,7 +170,7 @@ public partial class Gambling
arr[i] = rng.Next(1, n2 + 1); arr[i] = rng.Next(1, n2 + 1);
var sum = arr.Sum(); var sum = arr.Sum();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`"))) .WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))

View File

@@ -55,7 +55,7 @@ public partial class Gambling
foreach (var i in images) foreach (var i in images)
i.Dispose(); i.Dispose();
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor(); .WithOkColor();
var toSend = string.Empty; var toSend = string.Empty;
@@ -160,7 +160,7 @@ public partial class Gambling
return; return;
} }
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(result.Card.GetEmoji()) .WithDescription(result.Card.GetEmoji())

View File

@@ -30,12 +30,12 @@ public partial class Gambling
private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot) private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot)
=> type switch => type switch
{ {
CurrencyEvent.Type.Reaction => new EmbedBuilder() CurrencyEvent.Type.Reaction => _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.event_title(type.ToString()))) .WithTitle(GetText(strs.event_title(type.ToString())))
.WithDescription(GetReactionDescription(opts.Amount, currentPot)) .WithDescription(GetReactionDescription(opts.Amount, currentPot))
.WithFooter(GetText(strs.event_duration_footer(opts.Hours))), .WithFooter(GetText(strs.event_duration_footer(opts.Hours))),
CurrencyEvent.Type.GameStatus => new EmbedBuilder() CurrencyEvent.Type.GameStatus => _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.event_title(type.ToString()))) .WithTitle(GetText(strs.event_title(type.ToString())))
.WithDescription(GetGameStatusDescription(opts.Amount, currentPot)) .WithDescription(GetGameStatusDescription(opts.Amount, currentPot))

View File

@@ -84,7 +84,7 @@ public partial class Gambling
? Format.Bold(GetText(strs.heads)) ? Format.Bold(GetText(strs.heads))
: Format.Bold(GetText(strs.tails)))); : Format.Bold(GetText(strs.tails))));
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(msg) .WithDescription(msg)
@@ -130,7 +130,7 @@ public partial class Gambling
str = Format.Bold(GetText(strs.better_luck)); str = Format.Bold(GetText(strs.better_luck));
} }
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(str) .WithDescription(str)
.WithOkColor() .WithOkColor()

View File

@@ -73,7 +73,7 @@ public partial class Gambling : GamblingModule<GamblingService>
{ {
var stats = await _gamblingTxTracker.GetAllAsync(); var stats = await _gamblingTxTracker.GetAllAsync();
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor(); .WithOkColor();
var str = "` Feature `` Bet ``Paid Out`` RoI `\n"; var str = "` Feature `` Bet ``Paid Out`` RoI `\n";
@@ -118,7 +118,7 @@ public partial class Gambling : GamblingModule<GamblingService>
} }
// [21:03] Bob Page: Kinda remids me of US economy // [21:03] Bob Page: Kinda remids me of US economy
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.economy_state)) .WithTitle(GetText(strs.economy_state))
.AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot)) .AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%") .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
@@ -310,7 +310,7 @@ public partial class Gambling : GamblingModule<GamblingService>
trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page); trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page);
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString() .WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
?? $"{userId}"))) ?? $"{userId}")))
.WithOkColor(); .WithOkColor();
@@ -360,7 +360,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return; return;
} }
var eb = new EmbedBuilder().WithOkColor(); var eb = _sender.CreateEmbed().WithOkColor();
eb.WithAuthor(ctx.User); eb.WithAuthor(ctx.User);
eb.WithTitle(GetText(strs.transaction)); eb.WithTitle(GetText(strs.transaction));
@@ -624,7 +624,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return; return;
} }
var embed = new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.roll_duel)); var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.roll_duel));
var description = string.Empty; var description = string.Empty;
@@ -731,7 +731,7 @@ public partial class Gambling : GamblingModule<GamblingService>
str = GetText(strs.better_luck); str = GetText(strs.better_luck);
} }
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(Format.Bold(str)) .WithDescription(Format.Bold(str))
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture)) .AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
@@ -758,68 +758,64 @@ public partial class Gambling : GamblingModule<GamblingService>
var (opts, _) = OptionsParser.ParseFrom(new LbOpts(), args); var (opts, _) = OptionsParser.ParseFrom(new LbOpts(), args);
List<DiscordUser> cleanRichest; // List<DiscordUser> cleanRichest;
// it's pointless to have clean on dm context // it's pointless to have clean on dm context
if (ctx.Guild is null) if (ctx.Guild is null)
{ {
opts.Clean = false; opts.Clean = false;
} }
if (opts.Clean)
async Task<IEnumerable<DiscordUser>> GetTopRichest(int curPage)
{ {
await using (var uow = _db.GetDbContext()) if (opts.Clean)
{ {
cleanRichest = await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, 0, 10_000); await ctx.Channel.TriggerTypingAsync();
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
await using var uow = _db.GetDbContext();
var cleanRichest = await uow.Set<DiscordUser>()
.GetTopRichest(_client.CurrentUser.Id, 0, 10_000);
var sg = (SocketGuild)ctx.Guild!;
return cleanRichest.Where(x => sg.GetUser(x.UserId) is not null).ToList();
} }
else
await ctx.Channel.TriggerTypingAsync();
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
var sg = (SocketGuild)ctx.Guild!;
cleanRichest = cleanRichest.Where(x => sg.GetUser(x.UserId) is not null).ToList();
}
else
{
await using var uow = _db.GetDbContext();
cleanRichest = await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, page);
}
await ctx.SendPaginatedConfirmAsync(page,
async curPage =>
{ {
var embed = new EmbedBuilder().WithOkColor().WithTitle(CurrencySign + " " + GetText(strs.leaderboard)); await using var uow = _db.GetDbContext();
return await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, curPage);
}
}
List<DiscordUser> toSend; await Response()
if (!opts.Clean) .Paginated()
{ .PageItems(GetTopRichest)
await using var uow = _db.GetDbContext(); .PageSize(9)
toSend = await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, curPage); .CurrentPage(page)
} .Page((toSend, curPage) =>
else {
{ var embed = _sender.CreateEmbed().WithOkColor()
toSend = cleanRichest.Skip(curPage * 9).Take(9).ToList(); .WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
}
if (!toSend.Any()) if (!toSend.Any())
{ {
embed.WithDescription(GetText(strs.no_user_on_this_page)); embed.WithDescription(GetText(strs.no_user_on_this_page));
return embed; return Task.FromResult(embed);
} }
for (var i = 0; i < toSend.Count; i++) for (var i = 0; i < toSend.Count; i++)
{ {
var x = toSend[i]; var x = toSend[i];
var usrStr = x.ToString().TrimTo(20, true); var usrStr = x.ToString().TrimTo(20, true);
var j = i; var j = i;
embed.AddField("#" + ((9 * curPage) + j + 1) + " " + usrStr, N(x.CurrencyAmount), true); embed.AddField("#" + ((9 * curPage) + j + 1) + " " + usrStr, N(x.CurrencyAmount), true);
} }
return embed; return Task.FromResult(embed);
}, })
opts.Clean ? cleanRichest.Count() : 9000, .SendAsync();
9,
opts.Clean);
} }
public enum InputRpsPick : byte public enum InputRpsPick : byte
@@ -861,7 +857,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return; return;
} }
var embed = new EmbedBuilder(); var embed = _sender.CreateEmbed();
string msg; string msg;
if (result.Result == RpsResultType.Draw) if (result.Result == RpsResultType.Draw)
@@ -922,7 +918,7 @@ public partial class Gambling : GamblingModule<GamblingService>
sb.AppendLine(); sb.AppendLine();
} }
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(sb.ToString()) .WithDescription(sb.ToString())
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true) .AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)

View File

@@ -92,21 +92,23 @@ public partial class Gambling
{ {
if (--page < 0) if (--page < 0)
return Task.CompletedTask; return Task.CompletedTask;
var enabledIn = _service.GetAllGeneratingChannels(); var enabledIn = _service.GetAllGeneratingChannels();
return ctx.SendPaginatedConfirmAsync(page, return Response()
_ => .Paginated()
{ .Items(enabledIn.ToList())
var items = enabledIn.Skip(page * 9).Take(9).ToList(); .PageSize(9)
.CurrentPage(page)
.Page((items, _) =>
{
if (!items.Any())
return _sender.CreateEmbed().WithErrorColor().WithDescription("-");
if (!items.Any()) return items.Aggregate(_sender.CreateEmbed().WithOkColor(),
return new EmbedBuilder().WithErrorColor().WithDescription("-"); (eb, i) => eb.AddField(i.GuildId.ToString(), i.ChannelId));
})
return items.Aggregate(new EmbedBuilder().WithOkColor(), .SendAsync();
(eb, i) => eb.AddField(i.GuildId.ToString(), i.ChannelId));
},
enabledIn.Count(),
9);
} }
} }
} }

View File

@@ -48,27 +48,29 @@ public partial class Gambling
var entries = uow.GuildConfigsForId(ctx.Guild.Id, var entries = uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)) set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items))
.ShopEntries.ToIndexed(); .ShopEntries.ToIndexed();
return ctx.SendPaginatedConfirmAsync(page,
curPage =>
{
var theseEntries = entries.Skip(curPage * 9).Take(9).ToArray();
if (!theseEntries.Any()) return Response()
return new EmbedBuilder().WithErrorColor().WithDescription(GetText(strs.shop_none)); .Paginated()
var embed = new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.shop)); .Items(entries.ToList())
.PageSize(9)
.CurrentPage(page)
.Page((items, curPage) =>
{
if (!items.Any())
return _sender.CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none));
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop));
for (var i = 0; i < theseEntries.Length; i++) for (var i = 0; i < items.Count; i++)
{ {
var entry = theseEntries[i]; var entry = items[i];
embed.AddField($"#{(curPage * 9) + i + 1} - {N(entry.Price)}", embed.AddField($"#{(curPage * 9) + i + 1} - {N(entry.Price)}",
EntryToString(entry), EntryToString(entry),
true); true);
} }
return embed; return embed;
}, })
entries.Count, .SendAsync();
9);
} }
[Cmd] [Cmd]
@@ -187,7 +189,7 @@ public partial class Gambling
{ {
await Response() await Response()
.User(ctx.User) .User(ctx.User)
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name))) .WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
.AddField(GetText(strs.item), item.Text) .AddField(GetText(strs.item), item.Text)
@@ -246,7 +248,7 @@ public partial class Gambling
else else
{ {
var cmd = entry.Command.Replace("%you%", ctx.User.Id.ToString()); var cmd = entry.Command.Replace("%you%", ctx.User.Id.ToString());
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle("Executing shop command") .WithTitle("Executing shop command")
.WithDescription(cmd); .WithDescription(cmd);
@@ -268,10 +270,11 @@ public partial class Gambling
try try
{ {
var pendingMsg = await msgTask; var pendingMsg = await msgTask;
await pendingMsg.EditAsync(SmartEmbedText.FromEmbed(eb await pendingMsg.EditAsync(
.WithOkColor() SmartEmbedText.FromEmbed(eb
.WithTitle("Shop command executed") .WithOkColor()
.Build())); .WithTitle("Shop command executed")
.Build()));
} }
catch catch
{ {
@@ -531,7 +534,7 @@ public partial class Gambling
public EmbedBuilder EntryToEmbed(ShopEntry entry) public EmbedBuilder EntryToEmbed(ShopEntry entry)
{ {
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
if (entry.Type == ShopEntryType.Role) if (entry.Type == ShopEntryType.Role)
{ {

View File

@@ -69,7 +69,7 @@ public partial class Gambling
await using var imgStream = await image.ToStreamAsync(); await using var imgStream = await image.ToStreamAsync();
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(Format.Bold(text)) .WithDescription(Format.Bold(text))
.WithImageUrl($"attachment://result.png") .WithImageUrl($"attachment://result.png")

View File

@@ -20,9 +20,9 @@ public partial class Gambling
public async Task WaifuReset() public async Task WaifuReset()
{ {
var price = _service.GetResetPrice(ctx.User); var price = _service.GetResetPrice(ctx.User);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.waifu_reset_confirm)) .WithTitle(GetText(strs.waifu_reset_confirm))
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price))))); .WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))
return; return;
@@ -222,7 +222,7 @@ public partial class Gambling
return; return;
} }
var embed = new EmbedBuilder().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor(); var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor();
var i = 0; var i = 0;
foreach (var w in waifus) foreach (var w in waifus)
@@ -306,25 +306,25 @@ public partial class Gambling
if (string.IsNullOrWhiteSpace(fansStr)) if (string.IsNullOrWhiteSpace(fansStr))
fansStr = "-"; fansStr = "-";
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.waifu) .WithTitle(GetText(strs.waifu)
+ " " + " "
+ (wi.FullName ?? name ?? targetId.ToString()) + (wi.FullName ?? name ?? targetId.ToString())
+ " - \"the " + " - \"the "
+ _service.GetClaimTitle(wi.ClaimCount) + _service.GetClaimTitle(wi.ClaimCount)
+ "\"") + "\"")
.AddField(GetText(strs.price), N(wi.Price), true) .AddField(GetText(strs.price), N(wi.Price), true)
.AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true) .AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true)
.AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true) .AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true)
.AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true) .AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true)
.AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true) .AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true)
.AddField("\u200B", "\u200B", true) .AddField("\u200B", "\u200B", true)
.AddField(GetText(strs.fans(fansList.Count)), fansStr, true) .AddField(GetText(strs.fans(fansList.Count)), fansStr, true)
.AddField($"Waifus ({wi.ClaimCount})", .AddField($"Waifus ({wi.ClaimCount})",
wi.ClaimCount == 0 ? nobody : claimsStr, wi.ClaimCount == 0 ? nobody : claimsStr,
true) true)
.AddField(GetText(strs.gifts), itemsStr, true); .AddField(GetText(strs.gifts), itemsStr, true);
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
@@ -338,25 +338,27 @@ public partial class Gambling
return; return;
var waifuItems = _service.GetWaifuItems(); var waifuItems = _service.GetWaifuItems();
await ctx.SendPaginatedConfirmAsync(page, await Response()
cur => .Paginated()
{ .Items(waifuItems.OrderBy(x => x.Negative)
var embed = new EmbedBuilder().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor(); .ThenBy(x => x.Price)
.ToList())
.PageSize(9)
.CurrentPage(page)
.Page((items, _) =>
{
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
waifuItems.OrderBy(x => x.Negative) items
.ThenBy(x => x.Price) .ToList()
.Skip(9 * cur) .ForEach(x => embed.AddField(
.Take(9) $"{(!x.Negative ? string.Empty : "\\💔")} {x.ItemEmoji} {x.Name}",
.ToList() Format.Bold(N(x.Price)),
.ForEach(x => embed.AddField( true));
$"{(!x.Negative ? string.Empty : "\\💔")} {x.ItemEmoji} {x.Name}",
Format.Bold(N(x.Price)),
true));
return embed; return embed;
}, })
waifuItems.Count, .SendAsync();
9);
} }
[Cmd] [Cmd]

View File

@@ -67,7 +67,7 @@ public partial class Games
private Task Game_OnStarted(AcrophobiaGame game) private Task Game_OnStarted(AcrophobiaGame game)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.acrophobia)) .WithTitle(GetText(strs.acrophobia))
.WithDescription( .WithDescription(
@@ -92,7 +92,7 @@ public partial class Games
if (submissions.Length == 1) if (submissions.Length == 1)
{ {
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText( .WithDescription(GetText(
strs.acro_winner_only( strs.acro_winner_only(
@@ -103,7 +103,7 @@ public partial class Games
var i = 0; var i = 0;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed)) .WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed))
.WithDescription(GetText(strs.acro_nym_was( .WithDescription(GetText(strs.acro_nym_was(
@@ -127,7 +127,7 @@ public partial class Games
var table = votes.OrderByDescending(v => v.Value); var table = votes.OrderByDescending(v => v.Value);
var winner = table.First(); var winner = table.First();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.acrophobia)) .WithTitle(GetText(strs.acrophobia))
.WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName), .WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName),

View File

@@ -38,7 +38,7 @@ public partial class Games : NadekoModule<GamesService>
return; return;
var res = _service.GetEightballResponse(ctx.User.Id, question); var res = _service.GetEightballResponse(ctx.User.Id, question);
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(ctx.User.ToString()) .WithDescription(ctx.User.ToString())
.AddField("❓ " + GetText(strs.question), question) .AddField("❓ " + GetText(strs.question), question)

View File

@@ -23,11 +23,11 @@ public partial class Games
/-\ /-\
"""; """;
public static EmbedBuilder GetEmbed(HangmanGame.State state) public static EmbedBuilder GetEmbed(IMessageSenderService sender, HangmanGame.State state)
{ {
if (state.Phase == HangmanGame.Phase.Running) if (state.Phase == HangmanGame.Phase.Running)
{ {
return new EmbedBuilder() return sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Hangman", Draw(state)) .AddField("Hangman", Draw(state))
.AddField("Guess", Format.Code(state.Word)) .AddField("Guess", Format.Code(state.Word))
@@ -36,14 +36,14 @@ public partial class Games
if (state.Phase == HangmanGame.Phase.Ended && state.Failed) if (state.Phase == HangmanGame.Phase.Ended && state.Failed)
{ {
return new EmbedBuilder() return sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.AddField("Hangman", Draw(state)) .AddField("Hangman", Draw(state))
.AddField("Guess", Format.Code(state.Word)) .AddField("Guess", Format.Code(state.Word))
.WithFooter(state.MissedLetters.Join(' ')); .WithFooter(state.MissedLetters.Join(' '));
} }
return new EmbedBuilder() return sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Hangman", Draw(state)) .AddField("Hangman", Draw(state))
.AddField("Guess", Format.Code(state.Word)) .AddField("Guess", Format.Code(state.Word))
@@ -60,7 +60,7 @@ public partial class Games
return; return;
} }
var eb = GetEmbed(hangman); var eb = GetEmbed(_sender, hangman);
eb.WithDescription(GetText(strs.hangman_game_started)); eb.WithDescription(GetText(strs.hangman_game_started));
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();
} }

View File

@@ -116,7 +116,7 @@ public sealed class HangmanService : IHangmanService, IExecNoCommand
string content, string content,
HangmanGame.State state) HangmanGame.State state)
{ {
var embed = Games.HangmanCommands.GetEmbed(state); var embed = Games.HangmanCommands.GetEmbed(_sender, state);
if (state.GuessResult == HangmanGame.GuessResult.Guess) if (state.GuessResult == HangmanGame.GuessResult.Guess)
embed.WithDescription($"{user} guessed the letter {content}!").WithOkColor(); embed.WithDescription($"{user} guessed the letter {content}!").WithOkColor();
else if (state.GuessResult == HangmanGame.GuessResult.Incorrect && state.Failed) else if (state.GuessResult == HangmanGame.GuessResult.Incorrect && state.Failed)

View File

@@ -94,7 +94,7 @@ public partial class Games
if (removed is null) if (removed is null)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle($"Removed typing article #{index + 1}") .WithTitle($"Removed typing article #{index + 1}")
.WithDescription(removed.Text.TrimTo(50)) .WithDescription(removed.Text.TrimTo(50))
.WithOkColor(); .WithOkColor();

View File

@@ -82,7 +82,6 @@ public class TypingGame
do do
{ {
// todo fix all modifies
await Task.Delay(2000); await Task.Delay(2000);
time -= 2; time -= 2;
try { await msg.ModifyAsync(m => m.Content = $"Starting new typing contest in **{time}**.."); } try { await msg.ModifyAsync(m => m.Content = $"Starting new typing contest in **{time}**.."); }
@@ -145,7 +144,7 @@ public class TypingGame
var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60; var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60;
_finishedUserIds.Add(msg.Author.Id); _finishedUserIds.Add(msg.Author.Id);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{msg.Author} finished the race!") .WithTitle($"{msg.Author} finished the race!")
.AddField("Place", $"#{_finishedUserIds.Count}", true) .AddField("Place", $"#{_finishedUserIds.Count}", true)

View File

@@ -73,7 +73,7 @@ public class TicTacToe
public EmbedBuilder GetEmbed(string title = null) public EmbedBuilder GetEmbed(string title = null)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(Environment.NewLine + GetState()) .WithDescription(Environment.NewLine + GetState())
.WithAuthor(GetText(strs.vs(_users[0], _users[1]))); .WithAuthor(GetText(strs.vs(_users[0], _users[1])));

View File

@@ -160,7 +160,7 @@ public partial class Games
{ {
try try
{ {
questionEmbed = new EmbedBuilder() questionEmbed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.trivia_game)) .WithTitle(GetText(strs.trivia_game))
.AddField(GetText(strs.category), question.Category) .AddField(GetText(strs.category), question.Category)
@@ -189,7 +189,7 @@ public partial class Games
{ {
try try
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithTitle(GetText(strs.trivia_game)) .WithTitle(GetText(strs.trivia_game))
.WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer)))); .WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer))));
@@ -221,7 +221,7 @@ public partial class Games
{ {
try try
{ {
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.trivia_ended)) .WithAuthor(GetText(strs.trivia_ended))
.WithTitle(GetText(strs.leaderboard)) .WithTitle(GetText(strs.leaderboard))
@@ -247,7 +247,7 @@ public partial class Games
{ {
try try
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.trivia_game)) .WithTitle(GetText(strs.trivia_game))
.WithDescription(GetText(strs.trivia_win(user.Name, .WithDescription(GetText(strs.trivia_win(user.Name,

View File

@@ -84,32 +84,32 @@ public sealed class Help : NadekoModule<HelpService>
topLevelModules.Add(m); topLevelModules.Add(m);
} }
await ctx.SendPaginatedConfirmAsync(page, await Response()
cur => .Paginated()
{ .Items(topLevelModules)
var embed = new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.list_of_modules)); .CurrentPage(page)
.AddFooter(false)
.Page((items, _) =>
{
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.list_of_modules));
var localModules = topLevelModules.Skip(12 * cur).Take(12).ToList(); if (!items.Any())
{
embed = embed.WithOkColor().WithDescription(GetText(strs.module_page_empty));
return embed;
}
if (!localModules.Any()) items.OrderBy(module => module.Name)
{ .ToList()
embed = embed.WithOkColor().WithDescription(GetText(strs.module_page_empty)); .ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}",
return embed; GetModuleDescription(module.Name)
} + "\n"
+ Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))),
true));
localModules.OrderBy(module => module.Name) return embed;
.ToList() })
.ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}", .SendAsync();
GetModuleDescription(module.Name)
+ "\n"
+ Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))),
true));
return embed;
},
topLevelModules.Count(),
12,
false);
} }
private string GetModuleDescription(string moduleName) private string GetModuleDescription(string moduleName)
@@ -271,7 +271,7 @@ public sealed class Help : NadekoModule<HelpService>
var cnt = 0; var cnt = 0;
var groups = cmdsWithGroup.GroupBy(_ => cnt++ / 48).ToArray(); var groups = cmdsWithGroup.GroupBy(_ => cnt++ / 48).ToArray();
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
foreach (var g in groups) foreach (var g in groups)
{ {
var last = g.Count(); var last = g.Count();
@@ -303,9 +303,9 @@ public sealed class Help : NadekoModule<HelpService>
private async Task Group(ModuleInfo group) private async Task Group(ModuleInfo group)
{ {
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.cmd_group_commands(group.Name))) .WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor(); .WithOkColor();
foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0])) foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0]))
{ {
@@ -358,7 +358,8 @@ public sealed class Help : NadekoModule<HelpService>
var data = await GetHelpString(); var data = await GetHelpString();
if (data == default) if (data == default)
return; return;
await ch.SendAsync(data);
await Response().Text(data).SendAsync();
try try
{ {
await ctx.OkAsync(); await ctx.OkAsync();
@@ -534,9 +535,9 @@ public sealed class Help : NadekoModule<HelpService>
label: "Selfhosting"), label: "Selfhosting"),
SelfhostAction)); SelfhostAction));
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("Thank you for considering to donate to the NadekoBot project!"); .WithTitle("Thank you for considering to donate to the NadekoBot project!");
eb eb
.WithDescription("NadekoBot relies on donations to keep the servers, services and APIs running.\n" .WithDescription("NadekoBot relies on donations to keep the servers, services and APIs running.\n"
@@ -575,7 +576,12 @@ Nadeko will DM you the welcome instructions, and you may start using the patron-
try try
{ {
await (await ctx.User.CreateDMChannelAsync()).EmbedAsync(eb, inter: selfhostInter); await Response()
.Channel(await ctx.User.CreateDMChannelAsync())
.Embed(eb)
.Interaction(selfhostInter)
.SendAsync();
_ = ctx.OkAsync(); _ = ctx.OkAsync();
} }
catch catch

View File

@@ -2,11 +2,22 @@ using NadekoBot.Common.ModuleBehaviors;
namespace NadekoBot.Modules.Help.Services; namespace NadekoBot.Modules.Help.Services;
public class HelpService(BotConfigService bss, IReplacementService repSvc) : IExecNoCommand, INService public class HelpService : IExecNoCommand, INService
{ {
private readonly BotConfigService _bss;
private readonly IReplacementService _rs;
private readonly IMessageSenderService _sender;
public HelpService(BotConfigService bss, IReplacementService repSvc, IMessageSenderService sender)
{
_bss = bss;
_rs = repSvc;
_sender = sender;
}
public async Task ExecOnNoCommandAsync(IGuild? guild, IUserMessage msg) public async Task ExecOnNoCommandAsync(IGuild? guild, IUserMessage msg)
{ {
var settings = bss.Data; var settings = _bss.Data;
if (guild is null) if (guild is null)
{ {
if (string.IsNullOrWhiteSpace(settings.DmHelpText) || settings.DmHelpText == "-") if (string.IsNullOrWhiteSpace(settings.DmHelpText) || settings.DmHelpText == "-")
@@ -14,20 +25,20 @@ public class HelpService(BotConfigService bss, IReplacementService repSvc) : IEx
// only send dm help text if it contains one of the keywords, if they're specified // only send dm help text if it contains one of the keywords, if they're specified
// if they're not, then reply to every DM // if they're not, then reply to every DM
if (settings.DmHelpTextKeywords is not null && if (settings.DmHelpTextKeywords is not null
!settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k))) && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
{ {
return; return;
} }
var repCtx = new ReplacementContext(guild: guild, channel: msg.Channel, users: msg.Author) var repCtx = new ReplacementContext(guild: guild, channel: msg.Channel, users: msg.Author)
.WithOverride("%prefix%", () => bss.Data.Prefix) .WithOverride("%prefix%", () => _bss.Data.Prefix)
.WithOverride("%bot.prefix%", () => bss.Data.Prefix); .WithOverride("%bot.prefix%", () => _bss.Data.Prefix);
var text = SmartText.CreateFrom(settings.DmHelpText); var text = SmartText.CreateFrom(settings.DmHelpText);
text = await repSvc.ReplaceAsync(text, repCtx); text = await _rs.ReplaceAsync(text, repCtx);
await msg.Channel.SendAsync(text); await _sender.Response(msg.Channel).Text(text).SendAsync();
} }
} }
} }

View File

@@ -23,9 +23,9 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
.ToHashSet(); .ToHashSet();
var unloaded = _service.GetAllMedusae() var unloaded = _service.GetAllMedusae()
.Where(x => !loaded.Contains(x)) .Where(x => !loaded.Contains(x))
.Select(x => Format.Code(x.ToString())) .Select(x => Format.Code(x.ToString()))
.ToArray(); .ToArray();
if (unloaded.Length == 0) if (unloaded.Length == 0)
{ {
@@ -33,16 +33,18 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
return; return;
} }
await ctx.SendPaginatedConfirmAsync(0, await Response()
page => .Paginated()
{ .Items(unloaded)
return new EmbedBuilder() .PageSize(10)
.WithOkColor() .Page((items, _) =>
.WithTitle(GetText(strs.list_of_unloaded)) {
.WithDescription(unloaded.Skip(10 * page).Take(10).Join('\n')); return _sender.CreateEmbed()
}, .WithOkColor()
unloaded.Length, .WithTitle(GetText(strs.list_of_unloaded))
10); .WithDescription(items.Join('\n'));
})
.SendAsync();
return; return;
} }
@@ -77,11 +79,13 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
return; return;
} }
await Response().Embed(new EmbedBuilder() await Response()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithTitle(GetText(strs.loaded_medusae)) .WithOkColor()
.WithDescription(loaded.Select(x => x.Name) .WithTitle(GetText(strs.loaded_medusae))
.Join("\n"))).SendAsync(); .WithDescription(loaded.Select(x => x.Name)
.Join("\n")))
.SendAsync();
return; return;
} }
@@ -119,21 +123,23 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
.ToHashSet(); .ToHashSet();
var output = all var output = all
.Select(m => .Select(m =>
{ {
var emoji = loaded.Contains(m) ? "`✅`" : "`🔴`"; var emoji = loaded.Contains(m) ? "`✅`" : "`🔴`";
return $"{emoji} `{m}`"; return $"{emoji} `{m}`";
}) })
.ToArray(); .ToArray();
await ctx.SendPaginatedConfirmAsync(0, await Response()
page => new EmbedBuilder() .Paginated()
.WithOkColor() .Items(output)
.WithTitle(GetText(strs.list_of_medusae)) .PageSize(10)
.WithDescription(output.Skip(page * 10).Take(10).Join('\n')), .Page((items, _) => _sender.CreateEmbed()
output.Length, .WithOkColor()
10); .WithTitle(GetText(strs.list_of_medusae))
.WithDescription(items.Join('\n')))
.SendAsync();
} }
[Cmd] [Cmd]
@@ -156,26 +162,26 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
var cmdCount = found.Sneks.Sum(x => x.Commands.Count); var cmdCount = found.Sneks.Sum(x => x.Commands.Count);
var cmdNames = found.Sneks var cmdNames = found.Sneks
.SelectMany(x => Format.Code(string.IsNullOrWhiteSpace(x.Prefix) .SelectMany(x => Format.Code(string.IsNullOrWhiteSpace(x.Prefix)
? x.Name ? x.Name
: $"{x.Prefix} {x.Name}")) : $"{x.Prefix} {x.Name}"))
.Join("\n"); .Join("\n");
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.medusa_info)) .WithAuthor(GetText(strs.medusa_info))
.WithTitle(found.Name) .WithTitle(found.Name)
.WithDescription(found.Description) .WithDescription(found.Description)
.AddField(GetText(strs.sneks_count(found.Sneks.Count)), .AddField(GetText(strs.sneks_count(found.Sneks.Count)),
found.Sneks.Count == 0 found.Sneks.Count == 0
? "-" ? "-"
: found.Sneks.Select(x => x.Name).Join('\n'), : found.Sneks.Select(x => x.Name).Join('\n'),
true) true)
.AddField(GetText(strs.commands_count(cmdCount)), .AddField(GetText(strs.commands_count(cmdCount)),
string.IsNullOrWhiteSpace(cmdNames) string.IsNullOrWhiteSpace(cmdNames)
? "-" ? "-"
: cmdNames, : cmdNames,
true); true);
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();
return; return;
@@ -187,41 +193,48 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
return; return;
} }
await ctx.SendPaginatedConfirmAsync(0, await Response()
page => .Paginated()
{ .Items(medusae)
var eb = new EmbedBuilder() .PageSize(9)
.WithOkColor(); .CurrentPage(0)
.Page((items, _) =>
{
var eb = _sender.CreateEmbed()
.WithOkColor();
foreach (var medusa in medusae.Skip(page * 9).Take(9)) foreach (var medusa in items)
{ {
eb.AddField(medusa.Name, eb.AddField(medusa.Name,
$""" $"""
`Sneks:` {medusa.Sneks.Count} `Sneks:` {medusa.Sneks.Count}
`Commands:` {medusa.Sneks.Sum(x => x.Commands.Count)} `Commands:` {medusa.Sneks.Sum(x => x.Commands.Count)}
-- --
{medusa.Description} {medusa.Description}
"""); """);
} }
return eb; return eb;
}, medusae.Count, 9); })
.SendAsync();
} }
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public async Task MedusaSearch() public async Task MedusaSearch()
{ {
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.list_of_medusae)) .WithTitle(GetText(strs.list_of_medusae))
.WithOkColor(); .WithOkColor();
foreach (var item in await _repo.GetModuleItemsAsync()) foreach (var item in await _repo.GetModuleItemsAsync())
{ {
eb.AddField(item.Name, $""" eb.AddField(item.Name,
{item.Description} $"""
`{item.Command}` {item.Description}
""", true); `{item.Command}`
""",
true);
} }
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();

View File

@@ -109,11 +109,11 @@ public sealed partial class Music : NadekoModule<IMusicService>
try try
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.queued_track) + " #" + (index + 1), MUSIC_ICON_URL) .WithAuthor(GetText(strs.queued_track) + " #" + (index + 1), MUSIC_ICON_URL)
.WithDescription($"{trackInfo.PrettyName()}\n{GetText(strs.queue)} ") .WithDescription($"{trackInfo.PrettyName()}\n{GetText(strs.queue)} ")
.WithFooter(trackInfo.Platform.ToString()); .WithFooter(trackInfo.Platform.ToString());
if (!string.IsNullOrWhiteSpace(trackInfo.Thumbnail)) if (!string.IsNullOrWhiteSpace(trackInfo.Thumbnail))
embed.WithThumbnailUrl(trackInfo.Thumbnail); embed.WithThumbnailUrl(trackInfo.Thumbnail);
@@ -273,7 +273,7 @@ public sealed partial class Music : NadekoModule<IMusicService>
return; return;
} }
EmbedBuilder PrintAction(int curPage) EmbedBuilder PrintAction(IReadOnlyList<IQueuedTrackInfo> tracks, int curPage)
{ {
var desc = string.Empty; var desc = string.Empty;
var current = mp.GetCurrentTrack(out var currentIndex); var current = mp.GetCurrentTrack(out var currentIndex);
@@ -300,32 +300,38 @@ public sealed partial class Music : NadekoModule<IMusicService>
} }
desc += tracks.Skip(LQ_ITEMS_PER_PAGE * curPage) desc += tracks
.Take(LQ_ITEMS_PER_PAGE) .Select((v, index) =>
.Select((v, index) => {
{ index += LQ_ITEMS_PER_PAGE * curPage;
index += LQ_ITEMS_PER_PAGE * curPage; if (index == currentIndex)
if (index == currentIndex) return $"**⇒**`{index + 1}.` {v.PrettyFullName()}";
return $"**⇒**`{index + 1}.` {v.PrettyFullName()}";
return $"`{index + 1}.` {v.PrettyFullName()}"; return $"`{index + 1}.` {v.PrettyFullName()}";
}) })
.Join('\n'); .Join('\n');
if (!string.IsNullOrWhiteSpace(add)) if (!string.IsNullOrWhiteSpace(add))
desc = add + "\n" + desc; desc = add + "\n" + desc;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor(GetText(strs.player_queue(curPage + 1, (tracks.Count / LQ_ITEMS_PER_PAGE) + 1)), .WithAuthor(GetText(strs.player_queue(curPage + 1, (tracks.Count / LQ_ITEMS_PER_PAGE) + 1)),
MUSIC_ICON_URL) MUSIC_ICON_URL)
.WithDescription(desc) .WithDescription(desc)
.WithFooter($" {mp.PrettyVolume()} | 🎶 {tracks.Count} | ⌛ {mp.PrettyTotalTime()} ") .WithFooter($" {mp.PrettyVolume()} | 🎶 {tracks.Count} | ⌛ {mp.PrettyTotalTime()} ")
.WithOkColor(); .WithOkColor();
return embed; return embed;
} }
await ctx.SendPaginatedConfirmAsync(page, PrintAction, tracks.Count, LQ_ITEMS_PER_PAGE, false); await Response()
.Paginated()
.Items(tracks)
.PageSize(LQ_ITEMS_PER_PAGE)
.CurrentPage(page)
.AddFooter(false)
.Page(PrintAction)
.SendAsync();
} }
// search // search
@@ -408,11 +414,11 @@ public sealed partial class Music : NadekoModule<IMusicService>
return; return;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor(GetText(strs.removed_track) + " #" + index, MUSIC_ICON_URL) .WithAuthor(GetText(strs.removed_track) + " #" + index, MUSIC_ICON_URL)
.WithDescription(track.PrettyName()) .WithDescription(track.PrettyName())
.WithFooter(track.PrettyInfo()) .WithFooter(track.PrettyInfo())
.WithErrorColor(); .WithErrorColor();
await _service.SendToOutputAsync(ctx.Guild.Id, embed); await _service.SendToOutputAsync(ctx.Guild.Id, embed);
} }
@@ -576,12 +582,12 @@ public sealed partial class Music : NadekoModule<IMusicService>
return; return;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(track.Title.TrimTo(65)) .WithTitle(track.Title.TrimTo(65))
.WithAuthor(GetText(strs.track_moved), MUSIC_ICON_URL) .WithAuthor(GetText(strs.track_moved), MUSIC_ICON_URL)
.AddField(GetText(strs.from_position), $"#{from + 1}", true) .AddField(GetText(strs.from_position), $"#{from + 1}", true)
.AddField(GetText(strs.to_position), $"#{to + 1}", true) .AddField(GetText(strs.to_position), $"#{to + 1}", true)
.WithOkColor(); .WithOkColor();
if (Uri.IsWellFormedUriString(track.Url, UriKind.Absolute)) if (Uri.IsWellFormedUriString(track.Url, UriKind.Absolute))
embed.WithUrl(track.Url); embed.WithUrl(track.Url);
@@ -635,13 +641,13 @@ public sealed partial class Music : NadekoModule<IMusicService>
if (currentTrack is null) if (currentTrack is null)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.now_playing), MUSIC_ICON_URL) .WithAuthor(GetText(strs.now_playing), MUSIC_ICON_URL)
.WithDescription(currentTrack.PrettyName()) .WithDescription(currentTrack.PrettyName())
.WithThumbnailUrl(currentTrack.Thumbnail) .WithThumbnailUrl(currentTrack.Thumbnail)
.WithFooter( .WithFooter(
$"{mp.PrettyVolume()} | {mp.PrettyTotalTime()} | {currentTrack.Platform} | {currentTrack.Queuer}"); $"{mp.PrettyVolume()} | {mp.PrettyTotalTime()} | {currentTrack.Platform} | {currentTrack.Queuer}");
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }

View File

@@ -50,7 +50,7 @@ public sealed partial class Music
playlists = uow.Set<MusicPlaylist>().GetPlaylistsOnPage(num); playlists = uow.Set<MusicPlaylist>().GetPlaylistsOnPage(num);
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL) .WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL)
.WithDescription(string.Join("\n", .WithDescription(string.Join("\n",
playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count))))) playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count)))))
@@ -103,20 +103,22 @@ public sealed partial class Music
mpl = uow.Set<MusicPlaylist>().GetWithSongs(id); mpl = uow.Set<MusicPlaylist>().GetWithSongs(id);
} }
await ctx.SendPaginatedConfirmAsync(page, await Response()
cur => .Paginated()
{ .Items(mpl.Songs)
var i = 0; .PageSize(20)
var str = string.Join("\n", .CurrentPage(page)
mpl.Songs.Skip(cur * 20) .Page((items, _) =>
.Take(20) {
.Select(x => $"`{++i}.` [{x.Title.TrimTo(45)}]({x.Query}) `{x.Provider}`")); var i = 0;
return new EmbedBuilder().WithTitle($"\"{mpl.Name}\" by {mpl.Author}") var str = string.Join("\n",
.WithOkColor() items
.WithDescription(str); .Select(x => $"`{++i}.` [{x.Title.TrimTo(45)}]({x.Query}) `{x.Provider}`"));
}, return _sender.CreateEmbed().WithTitle($"\"{mpl.Name}\" by {mpl.Author}")
mpl.Songs.Count, .WithOkColor()
20); .WithDescription(str);
})
.SendAsync();
} }
[Cmd] [Cmd]
@@ -154,7 +156,7 @@ public sealed partial class Music
} }
await Response() await Response()
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.playlist_saved)) .WithTitle(GetText(strs.playlist_saved))
.AddField(GetText(strs.name), name) .AddField(GetText(strs.name), name)

View File

@@ -163,7 +163,9 @@ public sealed class MusicService : IMusicService
{ {
if (_outputChannels.TryGetValue(guildId, out var chan)) if (_outputChannels.TryGetValue(guildId, out var chan))
{ {
var msg = await (chan.Override ?? chan.Default).EmbedAsync(embed); var msg = await _sender.Response(chan.Override ?? chan.Default)
.Embed(embed)
.SendAsync();
return msg; return msg;
} }
@@ -176,11 +178,11 @@ public sealed class MusicService : IMusicService
return async (mp, trackInfo) => return async (mp, trackInfo) =>
{ {
_ = lastFinishedMessage?.DeleteAsync(); _ = lastFinishedMessage?.DeleteAsync();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(guildId, strs.finished_track), Music.MUSIC_ICON_URL) .WithAuthor(GetText(guildId, strs.finished_track), Music.MUSIC_ICON_URL)
.WithDescription(trackInfo.PrettyName()) .WithDescription(trackInfo.PrettyName())
.WithFooter(trackInfo.PrettyTotalTime()); .WithFooter(trackInfo.PrettyTotalTime());
lastFinishedMessage = await SendToOutputAsync(guildId, embed); lastFinishedMessage = await SendToOutputAsync(guildId, embed);
}; };
@@ -192,11 +194,11 @@ public sealed class MusicService : IMusicService
return async (mp, trackInfo, index) => return async (mp, trackInfo, index) =>
{ {
_ = lastPlayingMessage?.DeleteAsync(); _ = lastPlayingMessage?.DeleteAsync();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(guildId, strs.playing_track(index + 1)), Music.MUSIC_ICON_URL) .WithAuthor(GetText(guildId, strs.playing_track(index + 1)), Music.MUSIC_ICON_URL)
.WithDescription(trackInfo.PrettyName()) .WithDescription(trackInfo.PrettyName())
.WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}"); .WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}");
lastPlayingMessage = await SendToOutputAsync(guildId, embed); lastPlayingMessage = await SendToOutputAsync(guildId, embed);
}; };
@@ -279,9 +281,9 @@ public sealed class MusicService : IMusicService
yield return ("%music.playing%", () => yield return ("%music.playing%", () =>
{ {
var randomPlayingTrack = _players.Select(x => x.Value.GetCurrentTrack(out _)) var randomPlayingTrack = _players.Select(x => x.Value.GetCurrentTrack(out _))
.Where(x => x is not null) .Where(x => x is not null)
.Shuffle() .Shuffle()
.FirstOrDefault(); .FirstOrDefault();
if (randomPlayingTrack is null) if (randomPlayingTrack is null)
return "-"; return "-";

View File

@@ -171,7 +171,7 @@ public sealed class CurrencyRewardService : INService, IDisposable
if (user is null) if (user is null)
return; return;
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(message); .WithDescription(message);

View File

@@ -49,7 +49,7 @@ public partial class Patronage : NadekoModule
// //
// var patron = _service.GiftPatronAsync(user, amount); // var patron = _service.GiftPatronAsync(user, amount);
// //
// var eb = new EmbedBuilder(); // var eb = _sender.CreateEmbed();
// //
// await Response().Embed(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!") // await Response().Embed(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!")
// .AddField("Tier", Format.Bold(patron.Tier.ToString()), true) // .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
@@ -70,7 +70,7 @@ public partial class Patronage : NadekoModule
var patron = await _service.GetPatronAsync(user.Id); var patron = await _service.GetPatronAsync(user.Id);
var quotaStats = await _service.GetUserQuotaStatistic(user.Id); var quotaStats = await _service.GetUserQuotaStatistic(user.Id);
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithAuthor(user) .WithAuthor(user)
.WithTitle(GetText(strs.patron_info)) .WithTitle(GetText(strs.patron_info))
.WithOkColor(); .WithOkColor();

View File

@@ -306,7 +306,7 @@ public sealed class PatronageService
_ => false, _ => false,
ins => ins =>
{ {
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle("Insufficient Patron Tier") .WithTitle("Insufficient Patron Tier")
.AddField("For", $"{ins.FeatureType}: `{ins.Feature}`", true) .AddField("For", $"{ins.FeatureType}: `{ins.Feature}`", true)
@@ -336,7 +336,7 @@ public sealed class PatronageService
}, },
quota => quota =>
{ {
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle("Quota Limit Reached"); .WithTitle("Quota Limit Reached");
@@ -778,7 +778,7 @@ public sealed class PatronageService
if (user is null) if (user is null)
return; return;
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("❤️ Thank you for supporting NadekoBot! ❤️") .WithTitle("❤️ Thank you for supporting NadekoBot! ❤️")
.WithDescription( .WithDescription(

View File

@@ -60,12 +60,12 @@ public partial class Permissions
.Page((pageItems, _) => .Page((pageItems, _) =>
{ {
if (pageItems.Count == 0) if (pageItems.Count == 0)
return new EmbedBuilder() return _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(title) .WithTitle(title)
.WithDescription(GetText(strs.empty_page)); .WithDescription(GetText(strs.empty_page));
return new EmbedBuilder() return _sender.CreateEmbed()
.WithTitle(title) .WithTitle(title)
.WithDescription(allItems.Join('\n')) .WithDescription(allItems.Join('\n'))
.WithOkColor(); .WithOkColor();

View File

@@ -88,17 +88,21 @@ public partial class Permissions
await Response().Confirm(strs.cmdcd_none).SendAsync(); await Response().Confirm(strs.cmdcd_none).SendAsync();
else else
{ {
await ctx.SendPaginatedConfirmAsync(page, curPage => await Response()
{ .Paginated()
var items = localSet.Skip(curPage * 15) .Items(localSet)
.Take(15) .PageSize(15)
.Select(x => $"{Format.Code(x.CommandName)}: {x.Seconds.Seconds().Humanize(maxUnit: TimeUnit.Second, culture: Culture)}"); .CurrentPage(page)
.Page((items, _) =>
{
var output = items.Select(x =>
$"{Format.Code(x.CommandName)}: {x.Seconds.Seconds().Humanize(maxUnit: TimeUnit.Second, culture: Culture)}");
return new EmbedBuilder() return _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(items.Join("\n")); .WithDescription(output.Join("\n"));
})
}, localSet.Count, 15); .SendAsync();
} }
} }
} }

View File

@@ -29,9 +29,9 @@ public partial class Permissions
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task FilterList() public async Task FilterList()
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("Server filter settings"); .WithTitle("Server filter settings");
var config = await _service.GetFilterSettings(ctx.Guild.Id); var config = await _service.GetFilterSettings(ctx.Guild.Id);
@@ -41,14 +41,14 @@ public partial class Permissions
async Task<string> GetChannelListAsync(IReadOnlyCollection<ulong> channels) async Task<string> GetChannelListAsync(IReadOnlyCollection<ulong> channels)
{ {
var toReturn = (await channels var toReturn = (await channels
.Select(async cid => .Select(async cid =>
{ {
var ch = await ctx.Guild.GetChannelAsync(cid); var ch = await ctx.Guild.GetChannelAsync(cid);
return ch is null return ch is null
? $"{cid} *missing*" ? $"{cid} *missing*"
: $"<#{cid}>"; : $"<#{cid}>";
}) })
.WhenAll()) .WhenAll())
.Join('\n'); .Join('\n');
if (string.IsNullOrWhiteSpace(toReturn)) if (string.IsNullOrWhiteSpace(toReturn))
@@ -312,13 +312,16 @@ public partial class Permissions
var fws = fwHash.ToArray(); var fws = fwHash.ToArray();
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => new EmbedBuilder() .Paginated()
.WithTitle(GetText(strs.filter_word_list)) .Items(fws)
.WithDescription(string.Join("\n", fws.Skip(curPage * 10).Take(10))) .PageSize(10)
.WithOkColor(), .CurrentPage(page)
fws.Length, .Page((items, _) => _sender.CreateEmbed()
10); .WithTitle(GetText(strs.filter_word_list))
.WithDescription(string.Join("\n", items))
.WithOkColor())
.SendAsync();
} }
} }
} }

View File

@@ -30,7 +30,7 @@ public partial class Permissions
return; return;
} }
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
if (blockedModule.Any()) if (blockedModule.Any())
embed.AddField(GetText(strs.blocked_modules), string.Join("\n", _service.BlockedModules)); embed.AddField(GetText(strs.blocked_modules), string.Join("\n", _service.BlockedModules));

View File

@@ -24,7 +24,7 @@ public partial class Searches
// return; // return;
// } // }
// //
// var embed = new EmbedBuilder() // var embed = _sender.CreateEmbed()
// .WithOkColor() // .WithOkColor()
// .WithDescription(novelData.Description.Replace("<br>", Environment.NewLine, StringComparison.InvariantCulture)) // .WithDescription(novelData.Description.Replace("<br>", Environment.NewLine, StringComparison.InvariantCulture))
// .WithTitle(novelData.Title) // .WithTitle(novelData.Title)
@@ -86,7 +86,7 @@ public partial class Searches
.Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray()) .Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray())
.ToArray(); .ToArray();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.mal_profile(name))) .WithTitle(GetText(strs.mal_profile(name)))
.AddField("💚 " + GetText(strs.watching), stats[0], true) .AddField("💚 " + GetText(strs.watching), stats[0], true)
@@ -151,7 +151,7 @@ public partial class Searches
return; return;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(animeData.Synopsis.Replace("<br>", .WithDescription(animeData.Synopsis.Replace("<br>",
Environment.NewLine, Environment.NewLine,
@@ -183,7 +183,7 @@ public partial class Searches
return; return;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(mangaData.Synopsis.Replace("<br>", .WithDescription(mangaData.Synopsis.Replace("<br>",
Environment.NewLine, Environment.NewLine,

View File

@@ -35,7 +35,7 @@ public partial class Searches
} }
var symbol = symbols.First(); var symbol = symbols.First();
var promptEmbed = new EmbedBuilder() var promptEmbed = _sender.CreateEmbed()
.WithDescription(symbol.Description) .WithDescription(symbol.Description)
.WithTitle(GetText(strs.did_you_mean(symbol.Symbol))); .WithTitle(GetText(strs.did_you_mean(symbol.Symbol)));
@@ -79,7 +79,7 @@ public partial class Searches
var price = stock.Price.ToString("C2", localCulture); var price = stock.Price.ToString("C2", localCulture);
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(stock.Symbol) .WithAuthor(stock.Symbol)
.WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}") .WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}")
@@ -127,7 +127,7 @@ public partial class Searches
if (nearest is not null) if (nearest is not null)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.crypto_not_found)) .WithTitle(GetText(strs.crypto_not_found))
.WithDescription( .WithDescription(
GetText(strs.did_you_mean(Format.Bold($"{nearest.Name} ({nearest.Symbol})")))); GetText(strs.did_you_mean(Format.Bold($"{nearest.Name} ({nearest.Symbol})"))));
@@ -160,7 +160,7 @@ public partial class Searches
await using var sparkline = await _service.GetSparklineAsync(crypto.Id, usd.PercentChange7d >= 0); await using var sparkline = await _service.GetSparklineAsync(crypto.Id, usd.PercentChange7d >= 0);
var fileName = $"{crypto.Slug}_7d.png"; var fileName = $"{crypto.Slug}_7d.png";
var toSend = new EmbedBuilder() var toSend = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor($"#{crypto.CmcRank}") .WithAuthor($"#{crypto.CmcRank}")
.WithTitle($"{crypto.Name} ({crypto.Symbol})") .WithTitle($"{crypto.Name} ({crypto.Symbol})")

View File

@@ -111,28 +111,36 @@ public partial class Searches
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
public async Task FeedList() public async Task FeedList(int page = 1)
{ {
if (--page < 0)
return;
var feeds = _service.GetFeeds(ctx.Guild.Id); var feeds = _service.GetFeeds(ctx.Guild.Id);
if (!feeds.Any()) if (!feeds.Any())
{ {
await Response().Embed(new EmbedBuilder().WithOkColor().WithDescription(GetText(strs.feed_no_feed))).SendAsync(); await Response()
.Embed(_sender.CreateEmbed().WithOkColor().WithDescription(GetText(strs.feed_no_feed)))
.SendAsync();
return; return;
} }
await ctx.SendPaginatedConfirmAsync(0, await Response()
cur => .Paginated()
{ .Items(feeds)
var embed = new EmbedBuilder().WithOkColor(); .PageSize(10)
var i = 0; .CurrentPage(page)
var fs = string.Join("\n", .Page((items, cur) =>
feeds.Skip(cur * 10).Take(10).Select(x => $"`{(cur * 10) + ++i}.` <#{x.ChannelId}> {x.Url}")); {
var embed = _sender.CreateEmbed().WithOkColor();
var i = 0;
var fs = string.Join("\n",
items.Select(x => $"`{(cur * 10) + ++i}.` <#{x.ChannelId}> {x.Url}"));
return embed.WithDescription(fs); return embed.WithDescription(fs);
}, })
feeds.Count, .SendAsync();
10);
} }
} }
} }

View File

@@ -31,14 +31,14 @@ public class FeedsService : INService
{ {
var guildConfigIds = bot.AllGuildConfigs.Select(x => x.Id).ToList(); var guildConfigIds = bot.AllGuildConfigs.Select(x => x.Id).ToList();
_subs = uow.Set<GuildConfig>() _subs = uow.Set<GuildConfig>()
.AsQueryable() .AsQueryable()
.Where(x => guildConfigIds.Contains(x.Id)) .Where(x => guildConfigIds.Contains(x.Id))
.Include(x => x.FeedSubs) .Include(x => x.FeedSubs)
.ToList() .ToList()
.SelectMany(x => x.FeedSubs) .SelectMany(x => x.FeedSubs)
.GroupBy(x => x.Url.ToLower()) .GroupBy(x => x.Url.ToLower())
.ToDictionary(x => x.Key, x => x.ToList()) .ToDictionary(x => x.Key, x => x.ToList())
.ToConcurrent(); .ToConcurrent();
} }
_client = client; _client = client;
@@ -61,7 +61,7 @@ public class FeedsService : INService
// remove from db // remove from db
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
await ctx.GetTable<FeedSub>() await ctx.GetTable<FeedSub>()
.DeleteAsync(x => ids.Contains(x.Id)); .DeleteAsync(x => ids.Contains(x.Id));
// remove from the local cache // remove from the local cache
_subs.TryRemove(url, out _); _subs.TryRemove(url, out _);
@@ -95,14 +95,14 @@ public class FeedsService : INService
var feed = await FeedReader.ReadAsync(rssUrl); var feed = await FeedReader.ReadAsync(rssUrl);
var items = feed var items = feed
.Items.Select(item => (Item: item, .Items.Select(item => (Item: item,
LastUpdate: item.PublishingDate?.ToUniversalTime() LastUpdate: item.PublishingDate?.ToUniversalTime()
?? (item.SpecificItem as AtomFeedItem)?.UpdatedDate?.ToUniversalTime())) ?? (item.SpecificItem as AtomFeedItem)?.UpdatedDate?.ToUniversalTime()))
.Where(data => data.LastUpdate is not null) .Where(data => data.LastUpdate is not null)
.Select(data => (data.Item, LastUpdate: (DateTime)data.LastUpdate)) .Select(data => (data.Item, LastUpdate: (DateTime)data.LastUpdate))
.OrderByDescending(data => data.LastUpdate) .OrderByDescending(data => data.LastUpdate)
.Reverse() // start from the oldest .Reverse() // start from the oldest
.ToList(); .ToList();
if (!_lastPosts.TryGetValue(kvp.Key, out var lastFeedUpdate)) if (!_lastPosts.TryGetValue(kvp.Key, out var lastFeedUpdate))
{ {
@@ -115,7 +115,7 @@ public class FeedsService : INService
if (itemUpdateDate <= lastFeedUpdate) if (itemUpdateDate <= lastFeedUpdate)
continue; continue;
var embed = new EmbedBuilder().WithFooter(rssUrl); var embed = _sender.CreateEmbed().WithFooter(rssUrl);
_lastPosts[kvp.Key] = itemUpdateDate; _lastPosts[kvp.Key] = itemUpdateDate;
@@ -141,12 +141,12 @@ public class FeedsService : INService
if (!gotImage && feedItem.SpecificItem is AtomFeedItem afi) if (!gotImage && feedItem.SpecificItem is AtomFeedItem afi)
{ {
var previewElement = afi.Element.Elements() var previewElement = afi.Element.Elements()
.FirstOrDefault(x => x.Name.LocalName == "preview"); .FirstOrDefault(x => x.Name.LocalName == "preview");
if (previewElement is null) if (previewElement is null)
{ {
previewElement = afi.Element.Elements() previewElement = afi.Element.Elements()
.FirstOrDefault(x => x.Name.LocalName == "thumbnail"); .FirstOrDefault(x => x.Name.LocalName == "thumbnail");
} }
if (previewElement is not null) if (previewElement is not null)
@@ -170,11 +170,23 @@ public class FeedsService : INService
//send the created embed to all subscribed channels //send the created embed to all subscribed channels
var feedSendTasks = kvp.Value var feedSendTasks = kvp.Value
.Where(x => x.GuildConfig is not null) .Where(x => x.GuildConfig is not null)
.Select(x => _client.GetGuild(x.GuildConfig.GuildId) .Select(x =>
?.GetTextChannel(x.ChannelId) {
?.EmbedAsync(embed, string.IsNullOrWhiteSpace(x.Message) ? "" : x.Message)) var ch = _client.GetGuild(x.GuildConfig.GuildId)
.Where(x => x is not null); ?.GetTextChannel(x.ChannelId);
if (ch is null)
return null;
return _sender.Response(ch)
.Embed(embed)
.Text(string.IsNullOrWhiteSpace(x.Message)
? string.Empty
: x.Message)
.SendAsync();
})
.Where(x => x is not null);
allSendTasks.Add(feedSendTasks.WhenAll()); allSendTasks.Add(feedSendTasks.WhenAll());
@@ -202,11 +214,15 @@ public class FeedsService : INService
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs)) return uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs))
.FeedSubs.OrderBy(x => x.Id) .FeedSubs.OrderBy(x => x.Id)
.ToList(); .ToList();
} }
public FeedAddResult AddFeed(ulong guildId, ulong channelId, string rssFeed, string message) public FeedAddResult AddFeed(
ulong guildId,
ulong channelId,
string rssFeed,
string message)
{ {
ArgumentNullException.ThrowIfNull(rssFeed, nameof(rssFeed)); ArgumentNullException.ThrowIfNull(rssFeed, nameof(rssFeed));
@@ -252,8 +268,8 @@ public class FeedsService : INService
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var items = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs)) var items = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs))
.FeedSubs.OrderBy(x => x.Id) .FeedSubs.OrderBy(x => x.Id)
.ToList(); .ToList();
if (items.Count <= index) if (items.Count <= index)
return false; return false;

View File

@@ -40,18 +40,21 @@ public partial class Searches
var data = JsonConvert.DeserializeObject<List<MemegenTemplate>>(rawJson)!; var data = JsonConvert.DeserializeObject<List<MemegenTemplate>>(rawJson)!;
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .Items(data)
var templates = string.Empty; .PageSize(15)
foreach (var template in data.Skip(curPage * 15).Take(15)) .CurrentPage(page)
templates += $"**{template.Name}:**\n key: `{template.Id}`\n"; .Page((items, curPage) =>
var embed = new EmbedBuilder().WithOkColor().WithDescription(templates); {
var templates = string.Empty;
foreach (var template in items)
templates += $"**{template.Name}:**\n key: `{template.Id}`\n";
var embed = _sender.CreateEmbed().WithOkColor().WithDescription(templates);
return embed; return embed;
}, })
data.Count, .SendAsync();
15);
} }
[Cmd] [Cmd]

View File

@@ -49,7 +49,7 @@ public partial class Searches
var obj = objs[0]; var obj = objs[0];
var userId = obj.UserId; var userId = obj.UserId;
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"osu! {smode} profile for {user}") .WithTitle($"osu! {smode} profile for {user}")
.WithThumbnailUrl($"https://a.ppy.sh/{userId}") .WithThumbnailUrl($"https://a.ppy.sh/{userId}")
@@ -95,7 +95,7 @@ public partial class Searches
var userData = JsonConvert.DeserializeObject<GatariUserResponse>(usrResString).Users[0]; var userData = JsonConvert.DeserializeObject<GatariUserResponse>(usrResString).Users[0];
var userStats = statsResponse.Stats; var userStats = statsResponse.Stats;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"osu!Gatari {modeStr} profile for {user}") .WithTitle($"osu!Gatari {modeStr} profile for {user}")
.WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}") .WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}")
@@ -166,7 +166,7 @@ public partial class Searches
return (title, desc); return (title, desc);
}); });
var eb = new EmbedBuilder().WithOkColor().WithTitle($"Top 5 plays for {user}"); var eb = _sender.CreateEmbed().WithOkColor().WithTitle($"Top 5 plays for {user}");
var mapData = await mapTasks.WhenAll(); var mapData = await mapTasks.WhenAll();
foreach (var (title, desc) in mapData.Where(x => x != default)) foreach (var (title, desc) in mapData.Where(x => x != default))

View File

@@ -135,7 +135,7 @@ public partial class Searches
} }
catch catch
{ {
var embed = new EmbedBuilder().WithDescription(GetText(strs.account_not_found)).WithErrorColor(); var embed = _sender.CreateEmbed().WithDescription(GetText(strs.account_not_found)).WithErrorColor();
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
return; return;
@@ -144,37 +144,38 @@ public partial class Searches
if (!string.IsNullOrWhiteSpace(league)) if (!string.IsNullOrWhiteSpace(league))
characters.RemoveAll(c => c.League != league); characters.RemoveAll(c => c.League != league);
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .Items(characters)
var embed = new EmbedBuilder() .PageSize(9)
.WithAuthor($"Characters on {usr}'s account", .CurrentPage(page)
"https://web.poecdn.com/image/favicon/ogimage.png", .Page((items, curPage) =>
$"{PROFILE_URL}{usr}") {
.WithOkColor(); var embed = _sender.CreateEmbed()
.WithAuthor($"Characters on {usr}'s account",
"https://web.poecdn.com/image/favicon/ogimage.png",
$"{PROFILE_URL}{usr}")
.WithOkColor();
var tempList = characters.Skip(curPage * 9).Take(9).ToList(); if (characters.Count == 0)
return embed.WithDescription("This account has no characters.");
if (characters.Count == 0) var sb = new StringBuilder();
return embed.WithDescription("This account has no characters."); sb.AppendLine($"```{"#",-5}{"Character Name",-23}{"League",-10}{"Class",-13}{"Level",-3}");
for (var i = 0; i < items.Count; i++)
{
var character = items[i];
var sb = new StringBuilder(); sb.AppendLine(
sb.AppendLine($"```{"#",-5}{"Character Name",-23}{"League",-10}{"Class",-13}{"Level",-3}"); $"#{i + 1 + (curPage * 9),-4}{character.Name,-23}{ShortLeagueName(character.League),-10}{character.Class,-13}{character.Level,-3}");
for (var i = 0; i < tempList.Count; i++) }
{
var character = tempList[i];
sb.AppendLine( sb.AppendLine("```");
$"#{i + 1 + (curPage * 9),-4}{character.Name,-23}{ShortLeagueName(character.League),-10}{character.Class,-13}{character.Level,-3}"); embed.WithDescription(sb.ToString());
}
sb.AppendLine("```"); return embed;
embed.WithDescription(sb.ToString()); })
.SendAsync();
return embed;
},
characters.Count,
9);
} }
[Cmd] [Cmd]
@@ -190,17 +191,17 @@ public partial class Searches
} }
catch catch
{ {
var eembed = new EmbedBuilder().WithDescription(GetText(strs.leagues_not_found)).WithErrorColor(); var eembed = _sender.CreateEmbed().WithDescription(GetText(strs.leagues_not_found)).WithErrorColor();
await Response().Embed(eembed).SendAsync(); await Response().Embed(eembed).SendAsync();
return; return;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor("Path of Exile Leagues", .WithAuthor("Path of Exile Leagues",
"https://web.poecdn.com/image/favicon/ogimage.png", "https://web.poecdn.com/image/favicon/ogimage.png",
"https://www.pathofexile.com") "https://www.pathofexile.com")
.WithOkColor(); .WithOkColor();
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"```{"#",-5}{"League Name",-23}"); sb.AppendLine($"```{"#",-5}{"League Name",-23}");
@@ -273,19 +274,19 @@ public partial class Searches
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor($"{leagueName} Currency Exchange", .WithAuthor($"{leagueName} Currency Exchange",
"https://web.poecdn.com/image/favicon/ogimage.png", "https://web.poecdn.com/image/favicon/ogimage.png",
"http://poe.ninja") "http://poe.ninja")
.AddField("Currency Type", cleanCurrency, true) .AddField("Currency Type", cleanCurrency, true)
.AddField($"{cleanConvert} Equivalent", chaosEquivalent / conversionEquivalent, true) .AddField($"{cleanConvert} Equivalent", chaosEquivalent / conversionEquivalent, true)
.WithOkColor(); .WithOkColor();
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
catch catch
{ {
var embed = new EmbedBuilder().WithDescription(GetText(strs.ninja_not_found)).WithErrorColor(); var embed = _sender.CreateEmbed().WithDescription(GetText(strs.ninja_not_found)).WithErrorColor();
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }

View File

@@ -25,7 +25,7 @@ public partial class Searches
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant()) if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
{ {
var p = kvp.Value; var p = kvp.Value;
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(kvp.Key.ToTitleCase()) .WithTitle(kvp.Key.ToTitleCase())
.WithDescription(p.BaseStats.ToString()) .WithDescription(p.BaseStats.ToString())
@@ -55,7 +55,7 @@ public partial class Searches
{ {
if (kvp.Key.ToUpperInvariant() == ability) if (kvp.Key.ToUpperInvariant() == ability)
{ {
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(kvp.Value.Name) .WithTitle(kvp.Value.Name)
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc) .WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)

View File

@@ -59,7 +59,7 @@ public partial class Searches
descStr = descStr.TrimTo(4096); descStr = descStr.TrimTo(4096);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithTitle(query.TrimTo(64)!) .WithTitle(query.TrimTo(64)!)
@@ -98,7 +98,7 @@ public partial class Searches
EmbedBuilder CreateEmbed(IImageSearchResultEntry entry) EmbedBuilder CreateEmbed(IImageSearchResultEntry entry)
{ {
return new EmbedBuilder() return _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithTitle(query) .WithTitle(query)
@@ -190,7 +190,7 @@ public partial class Searches
// //
// var descStr = string.Join("\n\n", desc); // var descStr = string.Join("\n\n", desc);
// //
// var embed = new EmbedBuilder() // var embed = _sender.CreateEmbed()
// .WithAuthor(ctx.User.ToString(), // .WithAuthor(ctx.User.ToString(),
// "https://upload.wikimedia.org/wikipedia/en/9/90/The_DuckDuckGo_Duck.png") // "https://upload.wikimedia.org/wikipedia/en/9/90/The_DuckDuckGo_Duck.png")
// .WithDescription($"{GetText(strs.search_for)} **{query}**\n\n" + descStr) // .WithDescription($"{GetText(strs.search_for)} **{query}**\n\n" + descStr)

View File

@@ -54,7 +54,7 @@ public partial class Searches : NadekoModule<SearchesService>
if (!await ValidateQuery(query)) if (!await ValidateQuery(query))
return; return;
var embed = new EmbedBuilder(); var embed = _sender.CreateEmbed();
var data = await _service.GetWeatherDataAsync(query); var data = await _service.GetWeatherDataAsync(query);
if (data is null) if (data is null)
@@ -134,7 +134,7 @@ public partial class Searches : NadekoModule<SearchesService>
return; return;
} }
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.time_new)) .WithTitle(GetText(strs.time_new))
.WithDescription(Format.Code(data.Time.ToString(Culture))) .WithDescription(Format.Code(data.Time.ToString(Culture)))
@@ -160,7 +160,7 @@ public partial class Searches : NadekoModule<SearchesService>
} }
await Response() await Response()
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(movie.Title) .WithTitle(movie.Title)
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/") .WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
@@ -191,7 +191,7 @@ public partial class Searches : NadekoModule<SearchesService>
private Task InternalRandomImage(SearchesService.ImageTag tag) private Task InternalRandomImage(SearchesService.ImageTag tag)
{ {
var url = _service.GetRandomImageUrl(tag); var url = _service.GetRandomImageUrl(tag);
return Response().Embed(new EmbedBuilder().WithOkColor().WithImageUrl(url)).SendAsync(); return Response().Embed(_sender.CreateEmbed().WithOkColor().WithImageUrl(url)).SendAsync();
} }
[Cmd] [Cmd]
@@ -242,7 +242,7 @@ public partial class Searches : NadekoModule<SearchesService>
} }
await Response() await Response()
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField(GetText(strs.original_url), $"<{query}>") .AddField(GetText(strs.original_url), $"<{query}>")
.AddField(GetText(strs.short_url), $"<{shortLink}>")) .AddField(GetText(strs.short_url), $"<{shortLink}>"))
@@ -264,7 +264,7 @@ public partial class Searches : NadekoModule<SearchesService>
return; return;
} }
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(card.Name) .WithTitle(card.Name)
.WithDescription(card.Description) .WithDescription(card.Description)
@@ -297,7 +297,7 @@ public partial class Searches : NadekoModule<SearchesService>
return; return;
} }
var embed = new EmbedBuilder().WithOkColor().WithImageUrl(card.Img); var embed = _sender.CreateEmbed().WithOkColor().WithImageUrl(card.Img);
if (!string.IsNullOrWhiteSpace(card.Flavor)) if (!string.IsNullOrWhiteSpace(card.Flavor))
embed.WithDescription(card.Flavor); embed.WithDescription(card.Flavor);
@@ -318,21 +318,24 @@ public partial class Searches : NadekoModule<SearchesService>
$"https://api.urbandictionary.com/v0/define?term={Uri.EscapeDataString(query)}"); $"https://api.urbandictionary.com/v0/define?term={Uri.EscapeDataString(query)}");
try try
{ {
var items = JsonConvert.DeserializeObject<UrbanResponse>(res).List; var allItems = JsonConvert.DeserializeObject<UrbanResponse>(res).List;
if (items.Any()) if (allItems.Any())
{ {
await ctx.SendPaginatedConfirmAsync(0, await Response()
p => .Paginated()
{ .Items(allItems)
var item = items[p]; .PageSize(1)
return new EmbedBuilder() .CurrentPage(0)
.WithOkColor() .Page((items, _) =>
.WithUrl(item.Permalink) {
.WithTitle(item.Word) var item = items[0];
.WithDescription(item.Definition); return _sender.CreateEmbed()
}, .WithOkColor()
items.Length, .WithUrl(item.Permalink)
1); .WithTitle(item.Word)
.WithDescription(item.Definition);
})
.SendAsync();
return; return;
} }
} }
@@ -362,52 +365,54 @@ public partial class Searches : NadekoModule<SearchesService>
+ WebUtility.UrlEncode(word)); + WebUtility.UrlEncode(word));
}); });
var data = JsonConvert.DeserializeObject<DefineModel>(res); var responseModel = JsonConvert.DeserializeObject<DefineModel>(res);
var datas = data.Results var data = responseModel.Results
.Where(x => x.Senses is not null .Where(x => x.Senses is not null
&& x.Senses.Count > 0 && x.Senses.Count > 0
&& x.Senses[0].Definition is not null) && x.Senses[0].Definition is not null)
.Select(x => (Sense: x.Senses[0], x.PartOfSpeech)) .Select(x => (Sense: x.Senses[0], x.PartOfSpeech))
.ToList(); .ToList();
if (!datas.Any()) if (!data.Any())
{ {
Log.Warning("Definition not found: {Word}", word); Log.Warning("Definition not found: {Word}", word);
await Response().Error(strs.define_unknown).SendAsync(); await Response().Error(strs.define_unknown).SendAsync();
} }
var col = datas.Select(x => ( var col = data.Select(x => (
Definition: x.Sense.Definition is string Definition: x.Sense.Definition is string
? x.Sense.Definition.ToString() ? x.Sense.Definition.ToString()
: ((JArray)JToken.Parse(x.Sense.Definition.ToString())).First.ToString(), : ((JArray)JToken.Parse(x.Sense.Definition.ToString())).First.ToString(),
Example: x.Sense.Examples is null || x.Sense.Examples.Count == 0 Example: x.Sense.Examples is null || x.Sense.Examples.Count == 0
? string.Empty ? string.Empty
: x.Sense.Examples[0].Text, Word: word, : x.Sense.Examples[0].Text, Word: word,
WordType: string.IsNullOrWhiteSpace(x.PartOfSpeech) ? "-" : x.PartOfSpeech)) WordType: string.IsNullOrWhiteSpace(x.PartOfSpeech) ? "-" : x.PartOfSpeech))
.ToList(); .ToList();
Log.Information("Sending {Count} definition for: {Word}", col.Count, word); Log.Information("Sending {Count} definition for: {Word}", col.Count, word);
await ctx.SendPaginatedConfirmAsync(0, await Response()
page => .Paginated()
{ .Items(col)
var model = col.Skip(page).First(); .PageSize(1)
var embed = new EmbedBuilder() .Page((items, _) =>
.WithDescription(ctx.User.Mention) {
.AddField(GetText(strs.word), model.Word, true) var model = items.First();
.AddField(GetText(strs._class), model.WordType, true) var embed = _sender.CreateEmbed()
.AddField(GetText(strs.definition), model.Definition) .WithDescription(ctx.User.Mention)
.WithOkColor(); .AddField(GetText(strs.word), model.Word, true)
.AddField(GetText(strs._class), model.WordType, true)
.AddField(GetText(strs.definition), model.Definition)
.WithOkColor();
if (!string.IsNullOrWhiteSpace(model.Example)) if (!string.IsNullOrWhiteSpace(model.Example))
embed.AddField(GetText(strs.example), model.Example); embed.AddField(GetText(strs.example), model.Example);
return embed; return embed;
}, })
col.Count, .SendAsync();
1);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -474,7 +479,7 @@ public partial class Searches : NadekoModule<SearchesService>
await Response() await Response()
.Embed( .Embed(
new EmbedBuilder() _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Username", usr.ToString()) .AddField("Username", usr.ToString())
.AddField("Avatar Url", avatarUrl) .AddField("Avatar Url", avatarUrl)
@@ -542,7 +547,7 @@ public partial class Searches : NadekoModule<SearchesService>
{ {
var v = obj.Verses[0]; var v = obj.Verses[0];
await Response() await Response()
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{v.BookName} {v.Chapter}:{v.Verse}") .WithTitle($"{v.BookName} {v.Chapter}:{v.Verse}")
.WithDescription(v.Text)) .WithDescription(v.Text))
@@ -565,7 +570,7 @@ public partial class Searches : NadekoModule<SearchesService>
return; return;
} }
//var embed = new EmbedBuilder() //var embed = _sender.CreateEmbed()
// .WithOkColor() // .WithOkColor()
// .WithDescription(gameData.ShortDescription) // .WithDescription(gameData.ShortDescription)
// .WithTitle(gameData.Name) // .WithTitle(gameData.Name)

View File

@@ -95,9 +95,9 @@ public partial class Searches
.Page((elements, cur) => .Page((elements, cur) =>
{ {
if (elements.Count == 0) if (elements.Count == 0)
return new EmbedBuilder().WithDescription(GetText(strs.streams_none)).WithErrorColor(); return _sender.CreateEmbed().WithDescription(GetText(strs.streams_none)).WithErrorColor();
var eb = new EmbedBuilder().WithTitle(GetText(strs.streams_follow_title)).WithOkColor(); var eb = _sender.CreateEmbed().WithTitle(GetText(strs.streams_follow_title)).WithOkColor();
for (var index = 0; index < elements.Count; index++) for (var index = 0; index < elements.Count; index++)
{ {
var elem = elements[index]; var elem = elements[index];

View File

@@ -73,34 +73,34 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
{ {
var ids = client.GetGuildIds(); var ids = client.GetGuildIds();
var guildConfigs = uow.Set<GuildConfig>() var guildConfigs = uow.Set<GuildConfig>()
.AsQueryable() .AsQueryable()
.Include(x => x.FollowedStreams) .Include(x => x.FollowedStreams)
.Where(x => ids.Contains(x.GuildId)) .Where(x => ids.Contains(x.GuildId))
.ToList(); .ToList();
_offlineNotificationServers = new(guildConfigs _offlineNotificationServers = new(guildConfigs
.Where(gc => gc.NotifyStreamOffline) .Where(gc => gc.NotifyStreamOffline)
.Select(x => x.GuildId) .Select(x => x.GuildId)
.ToList()); .ToList());
_deleteOnOfflineServers = new(guildConfigs _deleteOnOfflineServers = new(guildConfigs
.Where(gc => gc.DeleteStreamOnlineMessage) .Where(gc => gc.DeleteStreamOnlineMessage)
.Select(x => x.GuildId) .Select(x => x.GuildId)
.ToList()); .ToList());
var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList(); var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList();
_shardTrackedStreams = followedStreams.GroupBy(x => new _shardTrackedStreams = followedStreams.GroupBy(x => new
{ {
x.Type, x.Type,
Name = x.Username.ToLower() Name = x.Username.ToLower()
}) })
.ToList() .ToList()
.ToDictionary( .ToDictionary(
x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()), x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
x => x.GroupBy(y => y.GuildId) x => x.GroupBy(y => y.GuildId)
.ToDictionary(y => y.Key, .ToDictionary(y => y.Key,
y => y.AsEnumerable().ToHashSet())); y => y.AsEnumerable().ToHashSet()));
// shard 0 will keep track of when there are no more guilds which track a stream // shard 0 will keep track of when there are no more guilds which track a stream
if (client.ShardId == 0) if (client.ShardId == 0)
@@ -111,12 +111,12 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
_streamTracker.AddLastData(fs.CreateKey(), null, false); _streamTracker.AddLastData(fs.CreateKey(), null, false);
_trackCounter = allFollowedStreams.GroupBy(x => new _trackCounter = allFollowedStreams.GroupBy(x => new
{ {
x.Type, x.Type,
Name = x.Username.ToLower() Name = x.Username.ToLower()
}) })
.ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name), .ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
x => x.Select(fs => fs.GuildId).ToHashSet()); x => x.Select(fs => fs.GuildId).ToHashSet());
} }
} }
@@ -156,7 +156,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
continue; continue;
var deleteGroups = failingStreams.GroupBy(x => x.Type) var deleteGroups = failingStreams.GroupBy(x => x.Type)
.ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList()); .ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList());
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
foreach (var kvp in deleteGroups) foreach (var kvp in deleteGroups)
@@ -169,9 +169,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
string.Join(", ", kvp.Value)); string.Join(", ", kvp.Value));
var toDelete = uow.Set<FollowedStream>() var toDelete = uow.Set<FollowedStream>()
.AsQueryable() .AsQueryable()
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username)) .Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
.ToList(); .ToList();
uow.RemoveRange(toDelete); uow.RemoveRange(toDelete);
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
@@ -250,13 +250,20 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
if (_shardTrackedStreams.TryGetValue(key, out var fss)) if (_shardTrackedStreams.TryGetValue(key, out var fss))
{ {
await fss await fss
// send offline stream notifications only to guilds which enable it with .stoff // send offline stream notifications only to guilds which enable it with .stoff
.SelectMany(x => x.Value) .SelectMany(x => x.Value)
.Where(x => _offlineNotificationServers.Contains(x.GuildId)) .Where(x => _offlineNotificationServers.Contains(x.GuildId))
.Select(fs => _client.GetGuild(fs.GuildId) .Select(fs =>
?.GetTextChannel(fs.ChannelId) {
?.EmbedAsync(GetEmbed(fs.GuildId, stream))) var ch = _client.GetGuild(fs.GuildId)
.WhenAll(); ?.GetTextChannel(fs.ChannelId);
if (ch is null)
return Task.CompletedTask;
return _sender.Response(ch).Embed(GetEmbed(fs.GuildId, stream)).SendAsync();
})
.WhenAll();
} }
} }
} }
@@ -270,33 +277,35 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
if (_shardTrackedStreams.TryGetValue(key, out var fss)) if (_shardTrackedStreams.TryGetValue(key, out var fss))
{ {
var messages = await fss.SelectMany(x => x.Value) var messages = await fss.SelectMany(x => x.Value)
.Select(async fs => .Select(async fs =>
{ {
var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId); var textChannel = _client.GetGuild(fs.GuildId)
?.GetTextChannel(fs.ChannelId);
if (textChannel is null) if (textChannel is null)
return default; return default;
var repCtx = new ReplacementContext(guild: textChannel.Guild, client: _client) var repCtx = new ReplacementContext(guild: textChannel.Guild,
.WithOverride("%platform%", () => fs.Type.ToString()); client: _client)
.WithOverride("%platform%", () => fs.Type.ToString());
var message = string.IsNullOrWhiteSpace(fs.Message) var message = string.IsNullOrWhiteSpace(fs.Message)
? "" ? ""
: await _repSvc.ReplaceAsync(fs.Message, repCtx); : await _repSvc.ReplaceAsync(fs.Message, repCtx);
var msg = await _sender.Response(textChannel) var msg = await _sender.Response(textChannel)
.Embed(GetEmbed(fs.GuildId, stream, false)) .Embed(GetEmbed(fs.GuildId, stream, false))
.Text(message) .Text(message)
.SendAsync(); .SendAsync();
// only cache the ids of channel/message pairs // only cache the ids of channel/message pairs
if (_deleteOnOfflineServers.Contains(fs.GuildId)) if (_deleteOnOfflineServers.Contains(fs.GuildId))
return (textChannel.Id, msg.Id); return (textChannel.Id, msg.Id);
else else
return default; return default;
}) })
.WhenAll(); .WhenAll();
// push online stream messages to redis // push online stream messages to redis
@@ -306,9 +315,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
try try
{ {
var pairs = messages var pairs = messages
.Where(x => x != default) .Where(x => x != default)
.Select(x => (x.Item1, x.Item2)) .Select(x => (x.Item1, x.Item2))
.ToList(); .ToList();
if (pairs.Count > 0) if (pairs.Count > 0)
await OnlineMessagesSent(key.Type, key.Name, pairs); await OnlineMessagesSent(key.Type, key.Name, pairs);
@@ -330,9 +339,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
{ {
using (var uow = _db.GetDbContext()) using (var uow = _db.GetDbContext())
{ {
var gc = uow.Set<GuildConfig>().AsQueryable() var gc = uow.Set<GuildConfig>()
.Include(x => x.FollowedStreams) .AsQueryable()
.FirstOrDefault(x => x.GuildId == guildConfig.GuildId); .Include(x => x.FollowedStreams)
.FirstOrDefault(x => x.GuildId == guildConfig.GuildId);
if (gc is null) if (gc is null)
return Task.CompletedTask; return Task.CompletedTask;
@@ -392,10 +402,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var fss = uow.Set<FollowedStream>() var fss = uow.Set<FollowedStream>()
.AsQueryable() .AsQueryable()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.ToList(); .ToList();
// out of range // out of range
if (fss.Count <= index) if (fss.Count <= index)
@@ -484,11 +494,11 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
public EmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true) public EmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(status.Name) .WithTitle(status.Name)
.WithUrl(status.StreamUrl) .WithUrl(status.StreamUrl)
.WithDescription(status.StreamUrl) .WithDescription(status.StreamUrl)
.AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true); .AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
if (showViewers) if (showViewers)
{ {

View File

@@ -69,7 +69,7 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
|| msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase)) || msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase))
return; return;
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
if (autoDelete) if (autoDelete)
{ {

View File

@@ -20,7 +20,7 @@ public partial class Searches
await ctx.Channel.TriggerTypingAsync(); await ctx.Channel.TriggerTypingAsync();
var translation = await _service.Translate(from, to, text); var translation = await _service.Translate(from, to, text);
var embed = new EmbedBuilder().WithOkColor().AddField(from, text).AddField(to, translation); var embed = _sender.CreateEmbed().WithOkColor().AddField(from, text).AddField(to, translation);
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
@@ -80,7 +80,7 @@ public partial class Searches
{ {
var langs = _service.GetLanguages().ToList(); var langs = _service.GetLanguages().ToList();
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.supported_languages)) .WithTitle(GetText(strs.supported_languages))
.WithOkColor(); .WithOkColor();

View File

@@ -25,7 +25,7 @@ public partial class Searches
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync($"{XKCD_URL}/info.0.json"); var res = await http.GetStringAsync($"{XKCD_URL}/info.0.json");
var comic = JsonConvert.DeserializeObject<XkcdComic>(res); var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithImageUrl(comic.ImageLink) .WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{comic.Num}") .WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{comic.Num}")
@@ -60,7 +60,7 @@ public partial class Searches
var res = await http.GetStringAsync($"{XKCD_URL}/{num}/info.0.json"); var res = await http.GetStringAsync($"{XKCD_URL}/{num}/info.0.json");
var comic = JsonConvert.DeserializeObject<XkcdComic>(res); var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithImageUrl(comic.ImageLink) .WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{num}") .WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{num}")

View File

@@ -121,17 +121,19 @@ public partial class Utility
var arr = maps.ToArray(); var arr = maps.ToArray();
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .Items(arr)
return new EmbedBuilder() .PageSize(10)
.WithOkColor() .CurrentPage(page)
.WithTitle(GetText(strs.alias_list)) .Page((items, _) =>
.WithDescription(string.Join("\n", {
arr.Skip(curPage * 10).Take(10).Select(x => $"`{x.Key}` => `{x.Value}`"))); return _sender.CreateEmbed()
}, .WithOkColor()
arr.Length, .WithTitle(GetText(strs.alias_list))
10); .WithDescription(string.Join("\n", items.Select(x => $"`{x.Key}` => `{x.Value}`")));
})
.SendAsync();
} }
} }
} }

View File

@@ -20,7 +20,7 @@ public partial class Utility
if (setting is null) if (setting is null)
{ {
var configNames = _settingServices.Select(x => x.Name); var configNames = _settingServices.Select(x => x.Name);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.config_not_found(Format.Code(name)))) .WithDescription(GetText(strs.config_not_found(Format.Code(name))))
.AddField(GetText(strs.config_list), string.Join("\n", configNames)); .AddField(GetText(strs.config_list), string.Join("\n", configNames));
@@ -43,7 +43,7 @@ public partial class Utility
name = name?.ToLowerInvariant(); name = name?.ToLowerInvariant();
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.config_list)) .WithTitle(GetText(strs.config_list))
.WithDescription(string.Join("\n", configNames)); .WithDescription(string.Join("\n", configNames));
@@ -58,7 +58,7 @@ public partial class Utility
// if config name is not found, print error and the list of configs // if config name is not found, print error and the list of configs
if (setting is null) if (setting is null)
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.config_not_found(Format.Code(name)))) .WithDescription(GetText(strs.config_not_found(Format.Code(name))))
.AddField(GetText(strs.config_list), string.Join("\n", configNames)); .AddField(GetText(strs.config_list), string.Join("\n", configNames));
@@ -75,7 +75,7 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(prop)) if (string.IsNullOrWhiteSpace(prop))
{ {
var propStrings = GetPropsAndValuesString(setting, propNames); var propStrings = GetPropsAndValuesString(setting, propNames);
var embed = new EmbedBuilder().WithOkColor().WithTitle($"⚙️ {setting.Name}").WithDescription(propStrings); var embed = _sender.CreateEmbed().WithOkColor().WithTitle($"⚙️ {setting.Name}").WithDescription(propStrings);
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
@@ -88,7 +88,7 @@ public partial class Utility
if (!exists) if (!exists)
{ {
var propStrings = GetPropsAndValuesString(setting, propNames); var propStrings = GetPropsAndValuesString(setting, propNames);
var propErrorEmbed = new EmbedBuilder() var propErrorEmbed = _sender.CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText( .WithDescription(GetText(
strs.config_prop_not_found(Format.Code(prop), Format.Code(name)))) strs.config_prop_not_found(Format.Code(prop), Format.Code(name))))
@@ -110,7 +110,7 @@ public partial class Utility
if (prop != "currency.sign") if (prop != "currency.sign")
value = Format.Code(Format.Sanitize(value.TrimTo(1000)), "json"); value = Format.Code(Format.Sanitize(value.TrimTo(1000)), "json");
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Config", Format.Code(setting.Name), true) .AddField("Config", Format.Code(setting.Name), true)
.AddField("Prop", Format.Code(prop), true) .AddField("Prop", Format.Code(prop), true)

View File

@@ -17,7 +17,7 @@ public partial class Utility
return; return;
} }
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle(GetText(strs.giveaway_starting)) .WithTitle(GetText(strs.giveaway_starting))
.WithDescription(message); .WithDescription(message);
@@ -103,7 +103,7 @@ public partial class Utility
return; return;
} }
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.giveaway_list)) .WithTitle(GetText(strs.giveaway_list))
.WithOkColor(); .WithOkColor();

View File

@@ -317,7 +317,7 @@ public sealed class GiveawayService : INService, IReadyExecutor
{Format.Code(winner.UserId.ToString())} {Format.Code(winner.UserId.ToString())}
"""; """;
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.giveaway_ended)) .WithTitle(GetText(strs.giveaway_ended))
.WithDescription(ga.Message) .WithDescription(ga.Message)

View File

@@ -54,7 +54,7 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(features)) if (string.IsNullOrWhiteSpace(features))
features = "-"; features = "-";
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithAuthor(GetText(strs.server_info)) .WithAuthor(GetText(strs.server_info))
.WithTitle(guild.Name) .WithTitle(guild.Name)
.AddField(GetText(strs.id), guild.Id.ToString(), true) .AddField(GetText(strs.id), guild.Id.ToString(), true)
@@ -87,7 +87,7 @@ public partial class Utility
return; return;
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22); var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22);
var usercount = (await ch.GetUsersAsync().FlattenAsync()).Count(); var usercount = (await ch.GetUsersAsync().FlattenAsync()).Count();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(ch.Name) .WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions(true)) .WithDescription(ch.Topic?.SanitizeMentions(true))
.AddField(GetText(strs.id), ch.Id.ToString(), true) .AddField(GetText(strs.id), ch.Id.ToString(), true)
@@ -107,7 +107,7 @@ public partial class Utility
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
.AddMilliseconds(role.Id >> 22); .AddMilliseconds(role.Id >> 22);
var usercount = role.Members.LongCount(); var usercount = role.Members.LongCount();
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(role.Name.TrimTo(128)) .WithTitle(role.Name.TrimTo(128))
.WithDescription(role.Permissions.ToList().Join(" | ")) .WithDescription(role.Permissions.ToList().Join(" | "))
.AddField(GetText(strs.id), role.Id.ToString(), true) .AddField(GetText(strs.id), role.Id.ToString(), true)
@@ -133,7 +133,7 @@ public partial class Utility
if (user is null) if (user is null)
return; return;
var embed = new EmbedBuilder().AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true); var embed = _sender.CreateEmbed().AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true);
if (!string.IsNullOrWhiteSpace(user.Nickname)) if (!string.IsNullOrWhiteSpace(user.Nickname))
embed.AddField(GetText(strs.nickname), user.Nickname, true); embed.AddField(GetText(strs.nickname), user.Nickname, true);
@@ -204,7 +204,7 @@ public partial class Utility
kvp.Value))); kvp.Value)));
} }
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithTitle(GetText(strs.activity_page(page + 1))) .WithTitle(GetText(strs.activity_page(page + 1)))
.WithOkColor() .WithOkColor()
.WithFooter(GetText( .WithFooter(GetText(

View File

@@ -47,9 +47,9 @@ public partial class Utility
var i = 1; var i = 1;
if (!invs.Any()) if (!invs.Any())
return new EmbedBuilder().WithErrorColor().WithDescription(GetText(strs.no_invites)); return _sender.CreateEmbed().WithErrorColor().WithDescription(GetText(strs.no_invites));
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
foreach (var inv in invs) foreach (var inv in invs)
{ {
var expiryString = inv.MaxAge is null or 0 || inv.CreatedAt is null var expiryString = inv.MaxAge is null or 0 || inv.CreatedAt is null

View File

@@ -106,7 +106,10 @@ public partial class Utility
var text = SmartText.CreateFrom(quote.Text); var text = SmartText.CreateFrom(quote.Text);
text = await repSvc.ReplaceAsync(text, repCtx); text = await repSvc.ReplaceAsync(text, repCtx);
await ctx.Channel.SendAsync($"`#{quote.Id}` 📣 " + text, true, replyTo: ctx.Message); await Response()
.Text($"`#{quote.Id}` 📣 " + text)
.Sanitize()
.SendAsync();
} }
[Cmd] [Cmd]
@@ -132,13 +135,13 @@ public partial class Utility
private async Task ShowQuoteData(Quote data) private async Task ShowQuoteData(Quote data)
{ {
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{GetText(strs.quote_id($"#{data.Id}"))} | {GetText(strs.response)}:") .WithTitle($"{GetText(strs.quote_id($"#{data.Id}"))} | {GetText(strs.response)}:")
.WithDescription(Format.Sanitize(data.Text).Replace("](", "]\\(").TrimTo(4096)) .WithDescription(Format.Sanitize(data.Text).Replace("](", "]\\(").TrimTo(4096))
.AddField(GetText(strs.trigger), data.Keyword) .AddField(GetText(strs.trigger), data.Keyword)
.WithFooter( .WithFooter(
GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})"))); GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})")));
if (!(data.Text.Length > 4096)) if (!(data.Text.Length > 4096))
{ {
@@ -146,10 +149,12 @@ public partial class Utility
return; return;
} }
// todo all send files should go through response system too await using var textStream = await data.Text.ToStream();
await ctx.Channel.SendFileAsync(
attachment: new FileAttachment(await data.Text.ToStream(), "quote.txt"), await Response()
embed: eb.Build()); .Embed(eb)
.File(textStream, "quote.txt")
.SendAsync();
} }
private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor) private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor)
@@ -217,7 +222,10 @@ public partial class Utility
var text = SmartText.CreateFrom(quote.Text); var text = SmartText.CreateFrom(quote.Text);
text = await repSvc.ReplaceAsync(text, repCtx); text = await repSvc.ReplaceAsync(text, repCtx);
await ctx.Channel.SendAsync(infoText + text, true, replyTo: ctx.Message); await Response()
.Text(infoText + text)
.Sanitize()
.SendAsync();
} }
[Cmd] [Cmd]

View File

@@ -96,7 +96,7 @@ public partial class Utility
if (--page < 0) if (--page < 0)
return; return;
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(isServer ? strs.reminder_server_list : strs.reminder_list)); .WithTitle(GetText(isServer ? strs.reminder_server_list : strs.reminder_list));

View File

@@ -208,7 +208,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
else else
{ {
await _sender.Response(ch) await _sender.Response(ch)
.Embed(new EmbedBuilder() .Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("Reminder") .WithTitle("Reminder")
.AddField("Created At", .AddField("Created At",

View File

@@ -64,7 +64,7 @@ public partial class Utility
} }
var description = GetRepeaterInfoString(removed); var description = GetRepeaterInfoString(removed);
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.repeater_removed(index + 1))) .WithTitle(GetText(strs.repeater_removed(index + 1)))
.WithDescription(description)).SendAsync(); .WithDescription(description)).SendAsync();
@@ -187,7 +187,7 @@ public partial class Utility
} }
var description = GetRepeaterInfoString(runner); var description = GetRepeaterInfoString(runner);
await Response().Embed(new EmbedBuilder() await Response().Embed(_sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.repeater_created)) .WithTitle(GetText(strs.repeater_created))
.WithDescription(description)).SendAsync(); .WithDescription(description)).SendAsync();
@@ -205,7 +205,7 @@ public partial class Utility
return; return;
} }
var embed = new EmbedBuilder().WithTitle(GetText(strs.list_of_repeaters)).WithOkColor(); var embed = _sender.CreateEmbed().WithTitle(GetText(strs.list_of_repeaters)).WithOkColor();
var i = 0; var i = 0;
foreach (var runner in repeaters.OrderBy(r => r.Repeater.Id)) foreach (var runner in repeaters.OrderBy(r => r.Repeater.Id))

View File

@@ -19,24 +19,27 @@ public sealed class RepeaterService : IReadyExecutor, INService
private readonly ConcurrentHashSet<int> _skipNext = new(); private readonly ConcurrentHashSet<int> _skipNext = new();
private readonly object _queueLocker = new(); private readonly object _queueLocker = new();
private readonly IMessageSenderService _sender;
public RepeaterService( public RepeaterService(
DiscordSocketClient client, DiscordSocketClient client,
DbService db, DbService db,
IReplacementService repSvc, IReplacementService repSvc,
IBotCredentials creds) IBotCredentials creds,
IMessageSenderService sender)
{ {
_db = db; _db = db;
_repSvc = repSvc; _repSvc = repSvc;
_creds = creds; _creds = creds;
_client = client; _client = client;
_sender = sender;
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var shardRepeaters = uow.Set<Repeater>() var shardRepeaters = uow.Set<Repeater>()
.Where(x => (int)(x.GuildId / Math.Pow(2, 22)) % _creds.TotalShards .Where(x => (int)(x.GuildId / Math.Pow(2, 22)) % _creds.TotalShards
== _client.ShardId) == _client.ShardId)
.AsNoTracking() .AsNoTracking()
.ToList(); .ToList();
_noRedundant = new(shardRepeaters.Where(x => x.NoRedundant).Select(x => x.Id)); _noRedundant = new(shardRepeaters.Where(x => x.NoRedundant).Select(x => x.Id));
@@ -125,10 +128,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var toTrigger = await uow.Set<Repeater>().AsNoTracking() var toTrigger = await uow.Set<Repeater>()
.Where(x => x.GuildId == guildId) .AsNoTracking()
.Skip(index) .Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF(); .Skip(index)
.FirstOrDefaultAsyncEF();
if (toTrigger is null) if (toTrigger is null)
return false; return false;
@@ -265,7 +269,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
var text = SmartText.CreateFrom(repeater.Message); var text = SmartText.CreateFrom(repeater.Message);
text = await _repSvc.ReplaceAsync(text, repCtx); text = await _repSvc.ReplaceAsync(text, repCtx);
var newMsg = await channel.SendAsync(text); var newMsg = await _sender.Response(channel).Text(text).SendAsync();
_ = newMsg.AddReactionAsync(new Emoji("🔄")); _ = newMsg.AddReactionAsync(new Emoji("🔄"));
if (_noRedundant.Contains(repeater.Id)) if (_noRedundant.Contains(repeater.Id))
@@ -308,12 +312,13 @@ public sealed class RepeaterService : IReadyExecutor, INService
private async Task SetRepeaterLastMessageInternal(int repeaterId, ulong lastMsgId) private async Task SetRepeaterLastMessageInternal(int repeaterId, ulong lastMsgId)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
await uow.Set<Repeater>().AsQueryable() await uow.Set<Repeater>()
.Where(x => x.Id == repeaterId) .AsQueryable()
.UpdateAsync(rep => new() .Where(x => x.Id == repeaterId)
{ .UpdateAsync(rep => new()
LastMessageId = lastMsgId {
}); LastMessageId = lastMsgId
});
} }
public async Task<RunningRepeater?> AddRepeaterAsync( public async Task<RunningRepeater?> AddRepeaterAsync(
@@ -358,10 +363,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
throw new ArgumentOutOfRangeException(nameof(index)); throw new ArgumentOutOfRangeException(nameof(index));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var toRemove = await uow.Set<Repeater>().AsNoTracking() var toRemove = await uow.Set<Repeater>()
.Where(x => x.GuildId == guildId) .AsNoTracking()
.Skip(index) .Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF(); .Skip(index)
.FirstOrDefaultAsyncEF();
if (toRemove is null) if (toRemove is null)
return null; return null;
@@ -389,10 +395,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
public async Task<bool?> ToggleRedundantAsync(ulong guildId, int index) public async Task<bool?> ToggleRedundantAsync(ulong guildId, int index)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var toToggle = await uow.Set<Repeater>().AsQueryable() var toToggle = await uow.Set<Repeater>()
.Where(x => x.GuildId == guildId) .AsQueryable()
.Skip(index) .Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF(); .Skip(index)
.FirstOrDefaultAsyncEF();
if (toToggle is null) if (toToggle is null)
return null; return null;
@@ -411,9 +418,9 @@ public sealed class RepeaterService : IReadyExecutor, INService
{ {
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var toSkip = await ctx.Set<Repeater>() var toSkip = await ctx.Set<Repeater>()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.Skip(index) .Skip(index)
.FirstOrDefaultAsyncEF(); .FirstOrDefaultAsyncEF();
if (toSkip is null) if (toSkip is null)
return null; return null;

View File

@@ -81,27 +81,27 @@ public partial class Utility
} }
private async Task ShowTodosAsync(TodoModel[] todos) private Task ShowTodosAsync(TodoModel[] todos)
{ => Response()
await ctx.SendPaginatedConfirmAsync(0, .Paginated()
(curPage) => .Items(todos)
{ .PageSize(9)
var eb = new EmbedBuilder() .Page((items, _) =>
.WithOkColor() {
.WithTitle(GetText(strs.todo_list)); var eb = _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.todo_list));
ShowTodoItem(todos, curPage, eb); ShowTodoItem(items, eb);
return eb; return eb;
}, })
todos.Length, .SendAsync();
9);
}
private static void ShowTodoItem(IReadOnlyCollection<TodoModel> todos, int curPage, EmbedBuilder eb) private static void ShowTodoItem(IReadOnlyCollection<TodoModel> todos, EmbedBuilder eb)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var todo in todos.Skip(curPage * 9).Take(9)) foreach (var todo in todos)
{ {
sb.AppendLine($"{(todo.IsDone ? "" : "")} {Format.Code(new kwum(todo.Id).ToString())} {todo.Todo}"); sb.AppendLine($"{(todo.IsDone ? "" : "")} {Format.Code(new kwum(todo.Id).ToString())} {todo.Todo}");
@@ -147,23 +147,25 @@ public partial class Utility
return; return;
} }
await ctx.SendPaginatedConfirmAsync(page, await Response()
(curPage) => .Paginated()
{ .Items(archivedTodoLists)
var eb = new EmbedBuilder() .PageSize(9)
.WithTitle(GetText(strs.todo_archive_list)) .CurrentPage(page)
.WithOkColor(); .Page((items, _) =>
{
var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.todo_archive_list))
.WithOkColor();
foreach (var archivedList in archivedTodoLists.Skip(curPage * 9).Take(9)) foreach (var archivedList in items)
{ {
eb.AddField($"id: {archivedList.Id.ToString()}", archivedList.Name, true); eb.AddField($"id: {archivedList.Id.ToString()}", archivedList.Name, true);
} }
return eb; return eb;
}, })
archivedTodoLists.Count, .SendAsync();
9,
true);
} }
[Cmd] [Cmd]
@@ -176,19 +178,21 @@ public partial class Utility
return; return;
} }
await ctx.SendPaginatedConfirmAsync(0, await Response()
(curPage) => .Paginated()
{ .Items(list.Items)
var eb = new EmbedBuilder() .PageSize(9)
.WithOkColor() .Page((items, _) =>
.WithTitle(GetText(strs.todo_list)); {
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.todo_list));
ShowTodoItem(list.Items, curPage, eb); ShowTodoItem(items, eb);
return eb; return eb;
}, })
list.Items.Count, .SendAsync();
9);
} }
[Cmd] [Cmd]

View File

@@ -13,7 +13,7 @@ public partial class Utility
{ {
var units = await _service.GetUnitsAsync(); var units = await _service.GetUnitsAsync();
var embed = new EmbedBuilder().WithTitle(GetText(strs.convertlist)).WithOkColor(); var embed = _sender.CreateEmbed().WithTitle(GetText(strs.convertlist)).WithOkColor();
foreach (var g in units.GroupBy(x => x.UnitType)) foreach (var g in units.GroupBy(x => x.UnitType))

View File

@@ -83,9 +83,10 @@ public partial class Utility : NadekoModule
var repCtx = new ReplacementContext(Context); var repCtx = new ReplacementContext(Context);
message = await repSvc.ReplaceAsync(message, repCtx); message = await repSvc.ReplaceAsync(message, repCtx);
await channel.SendAsync(message, await Response()
!((IGuildUser)ctx.User).GuildPermissions.MentionEveryone, .Text(message)
replyTo: ctx.Message); .UserBasedMentions()
.SendAsync();
} }
[Cmd] [Cmd]
@@ -154,21 +155,22 @@ public partial class Utility : NadekoModule
.Select(u => $"`{u.Id,18}` {u}") .Select(u => $"`{u.Id,18}` {u}")
.ToArray(); .ToArray();
await ctx.SendPaginatedConfirmAsync(page, await Response()
cur => .Paginated()
{ .Items(roleUsers)
var pageUsers = roleUsers.Skip(cur * 20).Take(20).ToList(); .PageSize(20)
.CurrentPage(page)
.Page((pageUsers, _) =>
{
if (pageUsers.Count == 0)
return _sender.CreateEmbed().WithOkColor().WithDescription(GetText(strs.no_user_on_this_page));
if (pageUsers.Count == 0) return _sender.CreateEmbed()
return new EmbedBuilder().WithOkColor().WithDescription(GetText(strs.no_user_on_this_page)); .WithOkColor()
.WithTitle(GetText(strs.inrole_list(Format.Bold(role?.Name ?? "No Role"), roleUsers.Length)))
return new EmbedBuilder() .WithDescription(string.Join("\n", pageUsers));
.WithOkColor() })
.WithTitle(GetText(strs.inrole_list(Format.Bold(role?.Name ?? "No Role"), roleUsers.Length))) .SendAsync();
.WithDescription(string.Join("\n", pageUsers));
},
roleUsers.Length,
20);
} }
[Cmd] [Cmd]
@@ -301,30 +303,32 @@ public partial class Utility : NadekoModule
if (string.IsNullOrWhiteSpace(ownerIds)) if (string.IsNullOrWhiteSpace(ownerIds))
ownerIds = "-"; ownerIds = "-";
await Response().Embed(new EmbedBuilder() await Response()
.WithOkColor() .Embed(_sender.CreateEmbed()
.WithAuthor($"NadekoBot v{StatsService.BotVersion}", .WithOkColor()
"https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/avatar.png", .WithAuthor($"NadekoBot v{StatsService.BotVersion}",
"https://nadekobot.readthedocs.io/en/latest/") "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/avatar.png",
.AddField(GetText(strs.author), _stats.Author, true) "https://nadekobot.readthedocs.io/en/latest/")
.AddField(GetText(strs.botid), _client.CurrentUser.Id.ToString(), true) .AddField(GetText(strs.author), _stats.Author, true)
.AddField(GetText(strs.shard), .AddField(GetText(strs.botid), _client.CurrentUser.Id.ToString(), true)
$"#{_client.ShardId} / {_creds.TotalShards}", .AddField(GetText(strs.shard),
true) $"#{_client.ShardId} / {_creds.TotalShards}",
.AddField(GetText(strs.commands_ran), _stats.CommandsRan.ToString(), true) true)
.AddField(GetText(strs.messages), .AddField(GetText(strs.commands_ran), _stats.CommandsRan.ToString(), true)
$"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)", .AddField(GetText(strs.messages),
true) $"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)",
.AddField(GetText(strs.memory), true)
FormattableString.Invariant($"{_stats.GetPrivateMemoryMegabytes():F2} MB"), .AddField(GetText(strs.memory),
true) FormattableString.Invariant($"{_stats.GetPrivateMemoryMegabytes():F2} MB"),
.AddField(GetText(strs.owner_ids), ownerIds, true) true)
.AddField(GetText(strs.uptime), _stats.GetUptimeString("\n"), true) .AddField(GetText(strs.owner_ids), ownerIds, true)
.AddField(GetText(strs.presence), .AddField(GetText(strs.uptime), _stats.GetUptimeString("\n"), true)
GetText(strs.presence_txt(_coord.GetGuildCount(), .AddField(GetText(strs.presence),
_stats.TextChannels, GetText(strs.presence_txt(_coord.GetGuildCount(),
_stats.VoiceChannels)), _stats.TextChannels,
true)).SendAsync(); _stats.VoiceChannels)),
true))
.SendAsync();
} }
[Cmd] [Cmd]
@@ -517,7 +521,7 @@ public partial class Utility : NadekoModule
return; return;
} }
var embed = new EmbedBuilder().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
foreach (var guild in guilds) foreach (var guild in guilds)
embed.AddField(guild.Name, GetText(strs.listservers(guild.Id, guild.MemberCount, guild.OwnerId))); embed.AddField(guild.Name, GetText(strs.listservers(guild.Id, guild.MemberCount, guild.OwnerId)));
@@ -660,6 +664,7 @@ public partial class Utility : NadekoModule
.WithReferences(this.GetType().Assembly) .WithReferences(this.GetType().Assembly)
.WithImports( .WithImports(
"System", "System",
"System.Linq",
"NadekoBot", "NadekoBot",
"NadekoBot.Extensions", "NadekoBot.Extensions",
"Microsoft.Extensions.DependencyInjection", "Microsoft.Extensions.DependencyInjection",
@@ -683,10 +688,10 @@ public partial class Utility : NadekoModule
var output = result.ReturnValue?.ToString(); var output = result.ReturnValue?.ToString();
if (!string.IsNullOrWhiteSpace(output)) if (!string.IsNullOrWhiteSpace(output))
{ {
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Code", scriptText) .AddField("Code", scriptText)
.AddField("Output", output.TrimTo(512)!); .AddField("Output", output.TrimTo(512)!);
_ = Response().Embed(eb).SendAsync(); _ = Response().Embed(eb).SendAsync();
} }

View File

@@ -127,10 +127,9 @@ public partial class Xp
.Paginated() .Paginated()
.Items(allUsers) .Items(allUsers)
.PageSize(10) .PageSize(10)
.CurrentPage(0)
.Page((users, _) => .Page((users, _) =>
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{club}") .WithTitle($"{club}")
.WithDescription(GetText(strs.level_x(lvl.Level + $" ({club.Xp} xp)"))) .WithDescription(GetText(strs.level_x(lvl.Level + $" ({club.Xp} xp)")))
@@ -206,22 +205,21 @@ public partial class Xp
var bans = club.Bans.Select(x => x.User).ToArray(); var bans = club.Bans.Select(x => x.User).ToArray();
return ctx.SendPaginatedConfirmAsync(page, return Response()
_ => .Paginated()
{ .Items(bans)
var toShow = string.Join("\n", .PageSize(10)
bans .CurrentPage(page)
.Skip(page * 10) .Page((items, _) =>
.Take(10) {
.Select(x => x.ToString())); var toShow = string.Join("\n", items.Select(x => x.ToString()));
return new EmbedBuilder() return _sender.CreateEmbed()
.WithTitle(GetText(strs.club_bans_for(club.ToString()))) .WithTitle(GetText(strs.club_bans_for(club.ToString())))
.WithDescription(toShow) .WithDescription(toShow)
.WithOkColor(); .WithOkColor();
}, })
bans.Length, .SendAsync();
10);
} }
[Cmd] [Cmd]
@@ -236,18 +234,21 @@ public partial class Xp
var apps = club.Applicants.Select(x => x.User).ToArray(); var apps = club.Applicants.Select(x => x.User).ToArray();
return ctx.SendPaginatedConfirmAsync(page, return Response()
_ => .Paginated()
{ .Items(apps)
var toShow = string.Join("\n", apps.Skip(page * 10).Take(10).Select(x => x.ToString())); .PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
return new EmbedBuilder() return _sender.CreateEmbed()
.WithTitle(GetText(strs.club_apps_for(club.ToString()))) .WithTitle(GetText(strs.club_apps_for(club.ToString())))
.WithDescription(toShow) .WithDescription(toShow)
.WithOkColor(); .WithOkColor();
}, })
apps.Length, .SendAsync();
10);
} }
[Cmd] [Cmd]
@@ -412,7 +413,7 @@ public partial class Xp
? "-" ? "-"
: desc; : desc;
var eb = new EmbedBuilder() var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithTitle(GetText(strs.club_desc_update)) .WithTitle(GetText(strs.club_desc_update))
.WithOkColor() .WithOkColor()
@@ -443,7 +444,7 @@ public partial class Xp
var clubs = _service.GetClubLeaderboardPage(page); var clubs = _service.GetClubLeaderboardPage(page);
var embed = new EmbedBuilder().WithTitle(GetText(strs.club_leaderboard(page + 1))).WithOkColor(); var embed = _sender.CreateEmbed().WithTitle(GetText(strs.club_leaderboard(page + 1))).WithOkColor();
var i = page * 9; var i = page * 9;
foreach (var club in clubs) foreach (var club in clubs)
@@ -464,7 +465,7 @@ public partial class Xp
return; return;
case ClubRenameResult.Success: case ClubRenameResult.Success:
{ {
var embed = new EmbedBuilder().WithTitle(GetText(strs.club_renamed(clubName))).WithOkColor(); var embed = _sender.CreateEmbed().WithTitle(GetText(strs.club_renamed(clubName))).WithOkColor();
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
return; return;
} }

View File

@@ -59,10 +59,10 @@ public partial class Xp : NadekoModule<XpService>
var globalSetting = _service.GetNotificationType(ctx.User); var globalSetting = _service.GetNotificationType(ctx.User);
var serverSetting = _service.GetNotificationType(ctx.User.Id, ctx.Guild.Id); var serverSetting = _service.GetNotificationType(ctx.User.Id, ctx.Guild.Id);
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField(GetText(strs.xpn_setting_global), GetNotifLocationString(globalSetting)) .AddField(GetText(strs.xpn_setting_global), GetNotifLocationString(globalSetting))
.AddField(GetText(strs.xpn_setting_server), GetNotifLocationString(serverSetting)); .AddField(GetText(strs.xpn_setting_server), GetNotifLocationString(serverSetting));
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
@@ -147,18 +147,21 @@ public partial class Xp : NadekoModule<XpService>
desc += "\n\n" + rolesStr + chansStr; desc += "\n\n" + rolesStr + chansStr;
var lines = desc.Split('\n'); var lines = desc.Split('\n');
await ctx.SendPaginatedConfirmAsync(0, await Response()
curpage => .Paginated()
{ .Items(lines)
var embed = new EmbedBuilder() .PageSize(15)
.WithTitle(GetText(strs.exclusion_list)) .CurrentPage(0)
.WithDescription(string.Join('\n', lines.Skip(15 * curpage).Take(15))) .Page((items, _) =>
.WithOkColor(); {
var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.exclusion_list))
.WithDescription(string.Join('\n', items))
.WithOkColor();
return embed; return embed;
}, })
lines.Length, .SendAsync();
15);
} }
[Cmd] [Cmd]
@@ -182,53 +185,54 @@ public partial class Xp : NadekoModule<XpService>
await ctx.Channel.TriggerTypingAsync(); await ctx.Channel.TriggerTypingAsync();
var socketGuild = (SocketGuild)ctx.Guild; var socketGuild = (SocketGuild)ctx.Guild;
var allUsers = new List<UserXpStats>(); var allCleanUsers = new List<UserXpStats>();
if (opts.Clean) if (opts.Clean)
{ {
await ctx.Channel.TriggerTypingAsync(); await ctx.Channel.TriggerTypingAsync();
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild); await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
allUsers = _service.GetTopUserXps(ctx.Guild.Id, 1000) allCleanUsers = _service.GetTopUserXps(ctx.Guild.Id, 1000)
.Where(user => socketGuild.GetUser(user.UserId) is not null) .Where(user => socketGuild.GetUser(user.UserId) is not null)
.ToList(); .ToList();
} }
await ctx.SendPaginatedConfirmAsync(page, await Response()
curPage => .Paginated()
{ .PageItems<UserXpStats>(opts.Clean
var embed = new EmbedBuilder().WithTitle(GetText(strs.server_leaderboard)).WithOkColor(); ? (curPage) => Task.FromResult<IEnumerable<UserXpStats>>(allCleanUsers.Skip(curPage * 9)
.Take(9)
.ToList())
: (curPage) => Task.FromResult<IEnumerable<UserXpStats>>(_service.GetUserXps(ctx.Guild.Id, curPage)))
.PageSize(9)
.CurrentPage(page)
.AddFooter(false)
.Page((users, curPage) =>
{
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.server_leaderboard)).WithOkColor();
List<UserXpStats> users; if (!users.Any())
if (opts.Clean) return embed.WithDescription("-");
users = allUsers.Skip(curPage * 9).Take(9).ToList();
else
users = _service.GetUserXps(ctx.Guild.Id, curPage);
if (!users.Any()) for (var i = 0; i < users.Count; i++)
return embed.WithDescription("-"); {
var levelStats = new LevelStats(users[i].Xp + users[i].AwardedXp);
var user = ((SocketGuild)ctx.Guild).GetUser(users[i].UserId);
for (var i = 0; i < users.Count; i++) var userXpData = users[i];
{
var levelStats = new LevelStats(users[i].Xp + users[i].AwardedXp);
var user = ((SocketGuild)ctx.Guild).GetUser(users[i].UserId);
var userXpData = users[i]; var awardStr = string.Empty;
if (userXpData.AwardedXp > 0)
awardStr = $"(+{userXpData.AwardedXp})";
else if (userXpData.AwardedXp < 0)
awardStr = $"({userXpData.AwardedXp})";
var awardStr = string.Empty; embed.AddField($"#{i + 1 + (curPage * 9)} {user?.ToString() ?? users[i].UserId.ToString()}",
if (userXpData.AwardedXp > 0) $"{GetText(strs.level_x(levelStats.Level))} - {levelStats.TotalXp}xp {awardStr}");
awardStr = $"(+{userXpData.AwardedXp})"; }
else if (userXpData.AwardedXp < 0)
awardStr = $"({userXpData.AwardedXp})";
embed.AddField($"#{i + 1 + (curPage * 9)} {user?.ToString() ?? users[i].UserId.ToString()}", return embed;
$"{GetText(strs.level_x(levelStats.Level))} - {levelStats.TotalXp}xp {awardStr}"); })
} .SendAsync();
return embed;
},
900,
9,
false);
} }
[Cmd] [Cmd]
@@ -239,7 +243,7 @@ public partial class Xp : NadekoModule<XpService>
return; return;
var users = _service.GetUserXps(page); var users = _service.GetUserXps(page);
var embed = new EmbedBuilder().WithTitle(GetText(strs.global_leaderboard)).WithOkColor(); var embed = _sender.CreateEmbed().WithTitle(GetText(strs.global_leaderboard)).WithOkColor();
if (!users.Any()) if (!users.Any())
embed.WithDescription("-"); embed.WithDescription("-");
@@ -317,7 +321,9 @@ public partial class Xp : NadekoModule<XpService>
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task XpReset(ulong userId) public async Task XpReset(ulong userId)
{ {
var embed = new EmbedBuilder().WithTitle(GetText(strs.reset)).WithDescription(GetText(strs.reset_user_confirm)); var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.reset))
.WithDescription(GetText(strs.reset_user_confirm));
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))
return; return;
@@ -332,7 +338,9 @@ public partial class Xp : NadekoModule<XpService>
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task XpReset() public async Task XpReset()
{ {
var embed = new EmbedBuilder().WithTitle(GetText(strs.reset)).WithDescription(GetText(strs.reset_server_confirm)); var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.reset))
.WithDescription(GetText(strs.reset_server_confirm));
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))
return; return;
@@ -383,94 +391,102 @@ public partial class Xp : NadekoModule<XpService>
if (page < 0) if (page < 0)
return; return;
var items = type == XpShopInputType.Backgrounds var allItems = type == XpShopInputType.Backgrounds
? await _service.GetShopBgs() ? await _service.GetShopBgs()
: await _service.GetShopFrames(); : await _service.GetShopFrames();
if (items is null) if (allItems is null)
{ {
await Response().Error(strs.xp_shop_disabled).SendAsync(); await Response().Error(strs.xp_shop_disabled).SendAsync();
return; return;
} }
if (items.Count == 0) if (allItems.Count == 0)
{ {
await Response().Error(strs.not_found).SendAsync(); await Response().Error(strs.not_found).SendAsync();
return; return;
} }
await ctx.SendPaginatedConfirmAsync<(string, XpShopItemType)?>(page, await Response()
current => .Paginated()
{ .Items(allItems)
var (key, item) = items.Skip(current).First(); .PageSize(1)
.CurrentPage(page)
.AddFooter(false)
.Page((items, _) =>
{
if (!items.Any())
return _sender.CreateEmbed()
.WithDescription(GetText(strs.not_found))
.WithErrorColor();
var eb = new EmbedBuilder() var (key, item) = items.FirstOrDefault();
.WithOkColor()
.WithTitle(item.Name)
.AddField(GetText(strs.price),
CurrencyHelper.N(item.Price, Culture, _gss.GetCurrencySign()),
true)
.WithImageUrl(string.IsNullOrWhiteSpace(item.Preview)
? item.Url
: item.Preview);
if (!string.IsNullOrWhiteSpace(item.Desc)) var eb = _sender.CreateEmbed()
eb.AddField(GetText(strs.desc), item.Desc); .WithOkColor()
.WithTitle(item.Name)
.AddField(GetText(strs.price),
CurrencyHelper.N(item.Price, Culture, _gss.GetCurrencySign()),
true)
.WithImageUrl(string.IsNullOrWhiteSpace(item.Preview)
? item.Url
: item.Preview);
if (key == "default") if (!string.IsNullOrWhiteSpace(item.Desc))
eb.WithDescription(GetText(strs.xpshop_website)); eb.AddField(GetText(strs.desc), item.Desc);
if (key == "default")
eb.WithDescription(GetText(strs.xpshop_website));
var tier = _service.GetXpShopTierRequirement(type); var tier = _service.GetXpShopTierRequirement(type);
if (tier != PatronTier.None) if (tier != PatronTier.None)
{ {
eb.WithFooter(GetText(strs.xp_shop_buy_required_tier(tier.ToString()))); eb.WithFooter(GetText(strs.xp_shop_buy_required_tier(tier.ToString())));
} }
return Task.FromResult(eb); return eb;
}, })
async current => .Interaction(async current =>
{ {
var (key, _) = items.Skip(current).First(); var (key, _) = allItems.Skip(current).First();
var itemType = type == XpShopInputType.Backgrounds var itemType = type == XpShopInputType.Backgrounds
? XpShopItemType.Background ? XpShopItemType.Background
: XpShopItemType.Frame; : XpShopItemType.Frame;
var ownedItem = await _service.GetUserItemAsync(ctx.User.Id, itemType, key); var ownedItem = await _service.GetUserItemAsync(ctx.User.Id, itemType, key);
if (ownedItem is not null) if (ownedItem is not null)
{ {
var button = new ButtonBuilder(ownedItem.IsUsing var button = new ButtonBuilder(ownedItem.IsUsing
? GetText(strs.in_use) ? GetText(strs.in_use)
: GetText(strs.use), : GetText(strs.use),
"xpshop:use", "xpshop:use",
emote: Emoji.Parse("👐"), emote: Emoji.Parse("👐"),
isDisabled: ownedItem.IsUsing); isDisabled: ownedItem.IsUsing);
var inter = new SimpleInteraction<(string key, XpShopItemType type)?>( var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
button, button,
OnShopUse, OnShopUse,
(key, itemType)); (key, itemType));
return inter; return inter;
} }
else else
{ {
var button = new ButtonBuilder(GetText(strs.buy), var button = new ButtonBuilder(GetText(strs.buy),
"xpshop:buy", "xpshop:buy",
emote: Emoji.Parse("💰")); emote: Emoji.Parse("💰"));
var inter = new SimpleInteraction<(string key, XpShopItemType type)?>( var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
button, button,
OnShopBuy, OnShopBuy,
(key, itemType)); (key, itemType));
return inter; return inter;
} }
}, })
items.Count, .SendAsync();
1,
addPaginatedFooter: false);
} }
[Cmd] [Cmd]

View File

@@ -16,9 +16,9 @@ public partial class Xp
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task XpRewsReset() public async Task XpRewsReset()
{ {
var promptEmbed = new EmbedBuilder() var promptEmbed = _sender.CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(GetText(strs.xprewsreset_confirm)); .WithDescription(GetText(strs.xprewsreset_confirm));
var reply = await PromptUserConfirmAsync(promptEmbed); var reply = await PromptUserConfirmAsync(promptEmbed);
@@ -66,24 +66,25 @@ public partial class Xp
.OrderBy(x => x.Key) .OrderBy(x => x.Key)
.ToList(); .ToList();
return Context.SendPaginatedConfirmAsync(page, return Response()
cur => .Paginated()
{ .Items(allRewards)
var embed = new EmbedBuilder().WithTitle(GetText(strs.level_up_rewards)).WithOkColor(); .PageSize(9)
.CurrentPage(page)
.Page((items, _) =>
{
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.level_up_rewards)).WithOkColor();
var localRewards = allRewards.Skip(cur * 9).Take(9).ToList(); if (!items.Any())
return embed.WithDescription(GetText(strs.no_level_up_rewards));
if (!localRewards.Any()) foreach (var reward in items)
return embed.WithDescription(GetText(strs.no_level_up_rewards)); embed.AddField(GetText(strs.level_x(reward.Key)),
string.Join("\n", reward.Select(y => y.Item2)));
foreach (var reward in localRewards) return embed;
embed.AddField(GetText(strs.level_x(reward.Key)), })
string.Join("\n", reward.Select(y => y.Item2))); .SendAsync();
return embed;
},
allRewards.Count,
9);
} }
[Cmd] [Cmd]
@@ -112,8 +113,10 @@ public partial class Xp
await Response().Confirm(strs.xp_role_reward_add_role(level, Format.Bold(role.ToString()))).SendAsync(); await Response().Confirm(strs.xp_role_reward_add_role(level, Format.Bold(role.ToString()))).SendAsync();
else else
{ {
await Response().Confirm(strs.xp_role_reward_remove_role(Format.Bold(level.ToString()), await Response()
Format.Bold(role.ToString()))).SendAsync(); .Confirm(strs.xp_role_reward_remove_role(Format.Bold(level.ToString()),
Format.Bold(role.ToString())))
.SendAsync();
} }
} }
@@ -129,8 +132,10 @@ public partial class Xp
if (amount == 0) if (amount == 0)
await Response().Confirm(strs.cur_reward_cleared(level, _cp.GetCurrencySign())).SendAsync(); await Response().Confirm(strs.cur_reward_cleared(level, _cp.GetCurrencySign())).SendAsync();
else else
await Response().Confirm(strs.cur_reward_added(level, await Response()
Format.Bold(amount + _cp.GetCurrencySign()))).SendAsync(); .Confirm(strs.cur_reward_added(level,
Format.Bold(amount + _cp.GetCurrencySign())))
.SendAsync();
} }
} }
} }

View File

@@ -7,7 +7,7 @@ public abstract class CleanupModuleBase : NadekoModule
{ {
try try
{ {
var embed = new EmbedBuilder() var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.sql_confirm_exec)) .WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(name); .WithDescription(name);

View File

@@ -1,8 +1,20 @@
namespace NadekoBot; namespace NadekoBot;
public class SimpleInteraction<T> public static class InteractionHelpers
{ {
public ButtonBuilder Button { get; } public static readonly IEmote ArrowLeft = Emote.Parse("<:x:1232256519844790302>");
public static readonly IEmote ArrowRight = Emote.Parse("<:x:1232256515298295838>");
}
public abstract class SimpleInteractionBase
{
public abstract Task TriggerAsync(SocketMessageComponent smc);
public abstract ButtonBuilder Button { get; }
}
public class SimpleInteraction<T> : SimpleInteractionBase
{
public override ButtonBuilder Button { get; }
private readonly Func<SocketMessageComponent, T, Task> _onClick; private readonly Func<SocketMessageComponent, T, Task> _onClick;
private readonly T? _state; private readonly T? _state;
@@ -13,7 +25,7 @@ public class SimpleInteraction<T>
_state = state; _state = state;
} }
public async Task TriggerAsync(SocketMessageComponent smc) public override async Task TriggerAsync(SocketMessageComponent smc)
{ {
await _onClick(smc, _state!); await _onClick(smc, _state!);
} }

View File

@@ -19,6 +19,7 @@ public abstract class NadekoModule : ModuleBase
public INadekoInteractionService _inter { get; set; } public INadekoInteractionService _inter { get; set; }
public IReplacementService repSvc { get; set; } public IReplacementService repSvc { get; set; }
public IMessageSenderService _sender { get; set; } public IMessageSenderService _sender { get; set; }
public BotConfigService _bcs { get; set; }
protected string prefix protected string prefix
=> _cmdHandler.GetPrefix(ctx.Guild); => _cmdHandler.GetPrefix(ctx.Guild);
@@ -27,7 +28,7 @@ public abstract class NadekoModule : ModuleBase
=> Context; => Context;
public ResponseBuilder Response() public ResponseBuilder Response()
=> new ResponseBuilder(Strings) => new ResponseBuilder(Strings, _bcs, (DiscordSocketClient)ctx.Client)
.Context(ctx); .Context(ctx);
protected override void BeforeExecute(CommandInfo command) protected override void BeforeExecute(CommandInfo command)

View File

@@ -7,4 +7,6 @@ public interface IMessageSenderService
ResponseBuilder Response(IUser user); ResponseBuilder Response(IUser user);
ResponseBuilder Response(SocketMessageComponent smc); ResponseBuilder Response(SocketMessageComponent smc);
NadekoEmbedBuilder CreateEmbed();
} }

View File

@@ -1,280 +0,0 @@
namespace NadekoBot.Extensions;
public static class MessageChannelExtensions
{
// main overload that all other send methods reduce to
public static Task<IUserMessage> SendAsync(
this IMessageChannel channel,
string? plainText,
Embed? embed = null,
IReadOnlyCollection<Embed>? embeds = null,
bool sanitizeAll = false,
MessageComponent? components = null,
IUserMessage? replyTo = null)
{
plainText = sanitizeAll
? plainText?.SanitizeAllMentions() ?? ""
: plainText?.SanitizeMentions() ?? "";
var msgReference = CreateMessageReference(channel, replyTo);
return channel.SendMessageAsync(plainText,
embed: embed,
embeds: embeds is null
? null
: embeds as Embed[] ?? embeds.ToArray(),
components: components,
messageReference: msgReference);
}
private static MessageReference? CreateMessageReference(IChannel source, IMessage? replyTo)
{
if (replyTo is null)
return null;
if (replyTo.Channel.Id != source.Id)
return null;
return new(replyTo.Id,
replyTo.Channel.Id,
(replyTo.Channel as ITextChannel)?.GuildId,
failIfNotExists: false);
}
public static async Task<IUserMessage> SendAsync(
this IMessageChannel channel,
string? plainText,
NadekoInteraction? inter,
Embed? embed = null,
IReadOnlyCollection<Embed>? embeds = null,
bool sanitizeAll = false,
IUserMessage? replyTo = null)
{
var msg = await channel.SendAsync(plainText,
embed,
embeds,
sanitizeAll,
inter?.CreateComponent(),
replyTo);
if (inter is not null)
await inter.RunAsync(msg);
return msg;
}
public static Task<IUserMessage> SendAsync(
this IMessageChannel channel,
SmartText text,
bool sanitizeAll = false,
IUserMessage? replyTo = null)
=> text switch
{
SmartEmbedText set => channel.SendAsync(set.PlainText,
set.IsValid ? set.GetEmbed().Build() : null,
sanitizeAll: sanitizeAll,
replyTo: replyTo),
SmartPlainText st => channel.SendAsync(st.Text,
default(Embed),
sanitizeAll: sanitizeAll,
replyTo: replyTo),
SmartEmbedTextArray arr => channel.SendAsync(arr.Content,
embeds: arr.GetEmbedBuilders().Map(e => e.Build()),
sanitizeAll: sanitizeAll,
replyTo: replyTo),
_ => throw new ArgumentOutOfRangeException(nameof(text))
};
public static Task<IUserMessage> EmbedAsync(
this IMessageChannel ch,
EmbedBuilder? embed,
string plainText = "",
IReadOnlyCollection<EmbedBuilder>? embeds = null,
NadekoInteraction? inter = null,
IUserMessage? replyTo = null)
=> ch.SendAsync(plainText,
inter,
embed: embed?.Build(),
embeds: embeds?.Map(x => x.Build()),
replyTo: replyTo);
// embed title and optional footer overloads
public static Task SendPaginatedConfirmAsync(
this ICommandContext ctx,
int currentPage,
Func<int, EmbedBuilder> pageFunc,
int totalElements,
int itemsPerPage,
bool addPaginatedFooter = true)
=> ctx.SendPaginatedConfirmAsync(currentPage,
x => Task.FromResult(pageFunc(x)),
totalElements,
itemsPerPage,
addPaginatedFooter);
private const string BUTTON_LEFT = "BUTTON_LEFT";
private const string BUTTON_RIGHT = "BUTTON_RIGHT";
private static readonly IEmote _arrowLeft = Emote.Parse("<:x:1232256519844790302>");
private static readonly IEmote _arrowRight = Emote.Parse("<:x:1232256515298295838>");
public static Task SendPaginatedConfirmAsync(
this ICommandContext ctx,
int currentPage,
Func<int, Task<EmbedBuilder>> pageFunc,
int totalElements,
int itemsPerPage,
bool addPaginatedFooter = true)
=> ctx.SendPaginatedConfirmAsync(currentPage,
pageFunc,
default(Func<int, ValueTask<SimpleInteraction<object>?>>),
totalElements,
itemsPerPage,
addPaginatedFooter);
public static async Task SendPaginatedConfirmAsync<T>(
this ICommandContext ctx,
int currentPage,
Func<int, Task<EmbedBuilder>> pageFunc,
Func<int, ValueTask<SimpleInteraction<T>?>>? interFactory,
int totalElements,
int itemsPerPage,
bool addPaginatedFooter = true)
{
var lastPage = (totalElements - 1) / itemsPerPage;
var embed = await pageFunc(currentPage);
if (addPaginatedFooter)
embed.AddPaginatedFooter(currentPage, lastPage);
SimpleInteraction<T>? maybeInter = null;
async Task<ComponentBuilder> GetComponentBuilder()
{
var cb = new ComponentBuilder();
cb.WithButton(new ButtonBuilder()
.WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_LEFT)
.WithDisabled(lastPage == 0)
.WithEmote(_arrowLeft)
.WithDisabled(currentPage <= 0));
if (interFactory is not null)
{
maybeInter = await interFactory(currentPage);
if (maybeInter is not null)
cb.WithButton(maybeInter.Button);
}
cb.WithButton(new ButtonBuilder()
.WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_RIGHT)
.WithDisabled(lastPage == 0 || currentPage >= lastPage)
.WithEmote(_arrowRight));
return cb;
}
async Task UpdatePageAsync(SocketMessageComponent smc)
{
var toSend = await pageFunc(currentPage);
if (addPaginatedFooter)
toSend.AddPaginatedFooter(currentPage, lastPage);
var component = (await GetComponentBuilder()).Build();
await smc.ModifyOriginalResponseAsync(x =>
{
x.Embed = toSend.Build();
x.Components = component;
});
}
var component = (await GetComponentBuilder()).Build();
var msg = await ctx.Channel.SendAsync(null, embed: embed.Build(), components: component, replyTo: ctx.Message);
async Task OnInteractionAsync(SocketInteraction si)
{
try
{
if (si is not SocketMessageComponent smc)
return;
if (smc.Message.Id != msg.Id)
return;
await si.DeferAsync();
if (smc.User.Id != ctx.User.Id)
return;
if (smc.Data.CustomId == BUTTON_LEFT)
{
if (currentPage == 0)
return;
--currentPage;
_ = UpdatePageAsync(smc);
}
else if (smc.Data.CustomId == BUTTON_RIGHT)
{
if (currentPage >= lastPage)
return;
++currentPage;
_ = UpdatePageAsync(smc);
}
else if (maybeInter is { } inter && inter.Button.CustomId == smc.Data.CustomId)
{
await inter.TriggerAsync(smc);
_ = UpdatePageAsync(smc);
}
}
catch (Exception ex)
{
Log.Error(ex, "Error in pagination: {ErrorMessage}", ex.Message);
}
}
if (lastPage == 0 && interFactory is null)
return;
var client = (DiscordSocketClient)ctx.Client;
client.InteractionCreated += OnInteractionAsync;
await Task.Delay(30_000);
client.InteractionCreated -= OnInteractionAsync;
await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build());
}
private static readonly Emoji _okEmoji = new Emoji("✅");
private static readonly Emoji _warnEmoji = new Emoji("⚠️");
private static readonly Emoji _errorEmoji = new Emoji("❌");
public static Task ReactAsync(this ICommandContext ctx, MsgType type)
{
var emoji = type switch
{
MsgType.Error => _errorEmoji,
MsgType.Pending => _warnEmoji,
MsgType.Ok => _okEmoji,
_ => throw new ArgumentOutOfRangeException(nameof(type)),
};
return ctx.Message.AddReactionAsync(emoji);
}
public static Task OkAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Ok);
public static Task ErrorAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Error);
public static Task WarningAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Pending);
}

View File

@@ -1,3 +1,4 @@
using NadekoBot.Common.Configs;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace NadekoBot.Extensions; namespace NadekoBot.Extensions;
@@ -5,27 +6,52 @@ namespace NadekoBot.Extensions;
public sealed class MessageSenderService : IMessageSenderService, INService public sealed class MessageSenderService : IMessageSenderService, INService
{ {
private readonly IBotStrings _bs; private readonly IBotStrings _bs;
private readonly BotConfigService _bcs;
private readonly DiscordSocketClient _client;
public MessageSenderService(IBotStrings bs) public MessageSenderService(IBotStrings bs, BotConfigService bcs, DiscordSocketClient client)
{ {
_bs = bs; _bs = bs;
_bcs = bcs;
_client = client;
} }
public ResponseBuilder Response(IMessageChannel channel) public ResponseBuilder Response(IMessageChannel channel)
=> new ResponseBuilder(_bs) => new ResponseBuilder(_bs, _bcs, _client)
.Channel(channel); .Channel(channel);
public ResponseBuilder Response(ICommandContext ctx) public ResponseBuilder Response(ICommandContext ctx)
=> new ResponseBuilder(_bs) => new ResponseBuilder(_bs, _bcs, _client)
.Context(ctx); .Context(ctx);
public ResponseBuilder Response(IUser user) public ResponseBuilder Response(IUser user)
=> new ResponseBuilder(_bs) => new ResponseBuilder(_bs, _bcs, _client)
.User(user); .User(user);
// todo fix interactions
public ResponseBuilder Response(SocketMessageComponent smc) public ResponseBuilder Response(SocketMessageComponent smc)
=> new ResponseBuilder(_bs) => new ResponseBuilder(_bs, _bcs, _client)
.Channel(smc.Channel); .Channel(smc.Channel);
public NadekoEmbedBuilder CreateEmbed()
=> new NadekoEmbedBuilder(_bcs);
}
public class NadekoEmbedBuilder : EmbedBuilder
{
private readonly BotConfig _bc;
public NadekoEmbedBuilder(BotConfigService bcs)
{
_bc = bcs.Data;
}
public EmbedBuilder WithOkColor()
=> WithColor(_bc.Color.Ok.ToDiscordColor());
public EmbedBuilder WithErrorColor()
=> WithColor(_bc.Color.Error.ToDiscordColor());
public EmbedBuilder WithPendingColor()
=> WithColor(_bc.Color.Pending.ToDiscordColor());
} }

View File

@@ -7,24 +7,20 @@ public partial class ResponseBuilder
private const string BUTTON_LEFT = "BUTTON_LEFT"; private const string BUTTON_LEFT = "BUTTON_LEFT";
private const string BUTTON_RIGHT = "BUTTON_RIGHT"; private const string BUTTON_RIGHT = "BUTTON_RIGHT";
private static readonly IEmote _arrowLeft = Emote.Parse("<:x:1232256519844790302>");
private static readonly IEmote _arrowRight = Emote.Parse("<:x:1232256515298295838>");
private readonly SourcedPaginatedResponseBuilder<T> _paginationBuilder; private readonly SourcedPaginatedResponseBuilder<T> _paginationBuilder;
private readonly ResponseBuilder builder; private readonly ResponseBuilder _builder;
private readonly DiscordSocketClient client; private readonly DiscordSocketClient _client;
private int currentPage; private int currentPage;
public PaginationSender( public PaginationSender(
SourcedPaginatedResponseBuilder<T> paginationBuilder, SourcedPaginatedResponseBuilder<T> paginationBuilder,
ResponseBuilder builder ResponseBuilder builder)
)
{ {
this._paginationBuilder = paginationBuilder; _paginationBuilder = paginationBuilder;
this.builder = builder; _builder = builder;
client = (DiscordSocketClient)builder.ctx.Client; _client = builder.Client;
currentPage = 0; currentPage = paginationBuilder.InitialPage;
} }
public async Task SendAsync(bool ephemeral = false) public async Task SendAsync(bool ephemeral = false)
@@ -38,7 +34,7 @@ public partial class ResponseBuilder
if (_paginationBuilder.AddPaginatedFooter) if (_paginationBuilder.AddPaginatedFooter)
embed.AddPaginatedFooter(currentPage, lastPage); embed.AddPaginatedFooter(currentPage, lastPage);
SimpleInteraction<T>? maybeInter = null; SimpleInteractionBase? maybeInter = null;
async Task<ComponentBuilder> GetComponentBuilder() async Task<ComponentBuilder> GetComponentBuilder()
{ {
@@ -48,22 +44,22 @@ public partial class ResponseBuilder
.WithStyle(ButtonStyle.Primary) .WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_LEFT) .WithCustomId(BUTTON_LEFT)
.WithDisabled(lastPage == 0) .WithDisabled(lastPage == 0)
.WithEmote(_arrowLeft) .WithEmote(InteractionHelpers.ArrowLeft)
.WithDisabled(currentPage <= 0)); .WithDisabled(currentPage <= 0));
// todo
// if (interFactory is not null) if (_paginationBuilder.InteractionFunc is not null)
// { {
// maybeInter = await interFactory(currentPage); maybeInter = await _paginationBuilder.InteractionFunc(currentPage);
//
// if (maybeInter is not null) if (maybeInter is not null)
// cb.WithButton(maybeInter.Button); cb.WithButton(maybeInter.Button);
// } }
cb.WithButton(new ButtonBuilder() cb.WithButton(new ButtonBuilder()
.WithStyle(ButtonStyle.Primary) .WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_RIGHT) .WithCustomId(BUTTON_RIGHT)
.WithDisabled(lastPage == 0 || currentPage >= lastPage) .WithDisabled(lastPage == 0 || currentPage >= lastPage)
.WithEmote(_arrowRight)); .WithEmote(InteractionHelpers.ArrowRight));
return cb; return cb;
} }
@@ -84,7 +80,7 @@ public partial class ResponseBuilder
}); });
} }
var model = builder.Build(ephemeral); var model = await _builder.BuildAsync(ephemeral);
var component = (await GetComponentBuilder()).Build(); var component = (await GetComponentBuilder()).Build();
var msg = await model.TargetChannel var msg = await model.TargetChannel
@@ -104,7 +100,8 @@ public partial class ResponseBuilder
return; return;
await si.DeferAsync(); await si.DeferAsync();
if (smc.User.Id != model.User.Id)
if (smc.User.Id != model.User?.Id)
return; return;
if (smc.Data.CustomId == BUTTON_LEFT) if (smc.Data.CustomId == BUTTON_LEFT)
@@ -134,20 +131,15 @@ public partial class ResponseBuilder
Log.Error(ex, "Error in pagination: {ErrorMessage}", ex.Message); Log.Error(ex, "Error in pagination: {ErrorMessage}", ex.Message);
} }
} }
// todo re-add
// if (lastPage == 0 && interFactory is null)
// return;
if (lastPage == 0) if (lastPage == 0 && _paginationBuilder.InteractionFunc is null)
return; return;
var client = this.client; _client.InteractionCreated += OnInteractionAsync;
client.InteractionCreated += OnInteractionAsync;
await Task.Delay(30_000); await Task.Delay(30_000);
client.InteractionCreated -= OnInteractionAsync; _client.InteractionCreated -= OnInteractionAsync;
await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build()); await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build());
} }

View File

@@ -1,29 +1,39 @@
namespace NadekoBot.Extensions; using NadekoBot.Common.Configs;
using NadekoBot.Db.Models;
namespace NadekoBot.Extensions;
public sealed partial class ResponseBuilder public sealed partial class ResponseBuilder
{ {
private ICommandContext? ctx = null; private ICommandContext? ctx;
private IMessageChannel? channel = null; private IMessageChannel? channel;
private Embed? embed = null; private string? plainText;
private string? plainText = null; private IReadOnlyCollection<EmbedBuilder>? embeds;
private IReadOnlyCollection<EmbedBuilder>? embeds = null; private IUserMessage? msg;
private IUserMessage? msg = null; private IUser? user;
private IUser? user = null;
private bool sanitizeMentions = true; private bool sanitizeMentions = true;
private LocStr? locTxt; private LocStr? locTxt;
private object[] locParams = []; private object[] locParams = [];
private bool shouldReply = true; private bool shouldReply = true;
private readonly IBotStrings _bs; private readonly IBotStrings _bs;
private EmbedBuilder? embedBuilder = null; private readonly BotConfigService _bcs;
private EmbedBuilder? embedBuilder;
private NadekoInteraction? inter; private NadekoInteraction? inter;
private Stream? fileStream = null; private Stream? fileStream;
private string? fileName = null; private string? fileName;
private EmbedColor color = EmbedColor.Ok;
private LocStr? embedLocDesc;
public ResponseBuilder(IBotStrings bs) public DiscordSocketClient Client { get; set; }
public ResponseBuilder(IBotStrings bs, BotConfigService bcs, DiscordSocketClient client)
{ {
_bs = bs; _bs = bs;
_bcs = bcs;
Client = client;
} }
private MessageReference? CreateMessageReference(IMessageChannel targetChannel) private MessageReference? CreateMessageReference(IMessageChannel targetChannel)
{ {
if (!shouldReply) if (!shouldReply)
@@ -44,72 +54,116 @@ public sealed partial class ResponseBuilder
failIfNotExists: false); failIfNotExists: false);
} }
public ResponseMessageModel Build(bool ephemeral = false) public async Task<ResponseMessageModel> BuildAsync(bool ephemeral)
{ {
// todo use ephemeral in interactions var targetChannel = await InternalResolveChannel() ?? throw new ArgumentNullException(nameof(channel));
var targetChannel = InternalResolveChannel() ?? throw new ArgumentNullException(nameof(channel));
var msgReference = CreateMessageReference(targetChannel); var msgReference = CreateMessageReference(targetChannel);
var txt = GetText(locTxt); var txt = GetText(locTxt, targetChannel);
// todo check message sanitization
if (embedLocDesc is LocStr ls)
{
InternalCreateEmbed(null, GetText(ls, targetChannel));
}
if (embedBuilder is not null)
PaintEmbedInternal(embedBuilder);
var finalEmbed = embedBuilder?.Build();
var buildModel = new ResponseMessageModel() var buildModel = new ResponseMessageModel()
{ {
TargetChannel = targetChannel, TargetChannel = targetChannel,
MessageReference = msgReference, MessageReference = msgReference,
Text = txt, Text = txt,
User = ctx?.User, User = user ?? ctx?.User,
Embed = embed ?? embedBuilder?.Build(), Embed = finalEmbed,
Embeds = embeds?.Map(x => x.Build()), Embeds = embeds?.Map(x => x.Build()),
SanitizeMentions = sanitizeMentions ? new(AllowedMentionTypes.Users) : AllowedMentions.All SanitizeMentions = sanitizeMentions ? new(AllowedMentionTypes.Users) : AllowedMentions.All,
Ephemeral = ephemeral,
Interaction = inter
}; };
return buildModel; return buildModel;
} }
public Task<IUserMessage> SendAsync(bool ephemeral = false) public async Task<IUserMessage> SendAsync(bool ephemeral = false)
{ {
var model = Build(ephemeral); var model = await BuildAsync(ephemeral);
return SendAsync(model); var sentMsg = await SendAsync(model);
return sentMsg;
} }
public async Task<IUserMessage> SendAsync(ResponseMessageModel model) public async Task<IUserMessage> SendAsync(ResponseMessageModel model)
{ {
if (this.fileStream is Stream stream) IUserMessage sentMsg;
return await model.TargetChannel.SendFileAsync(stream, if (fileStream is Stream stream)
{
sentMsg = await model.TargetChannel.SendFileAsync(stream,
filename: fileName, filename: fileName,
model.Text, model.Text,
embed: model.Embed, embed: model.Embed,
components: null, components: inter?.CreateComponent(),
allowedMentions: model.SanitizeMentions, allowedMentions: model.SanitizeMentions,
messageReference: model.MessageReference); messageReference: model.MessageReference);
}
else
{
sentMsg = await model.TargetChannel.SendMessageAsync(
model.Text,
embed: model.Embed,
embeds: model.Embeds,
components: inter?.CreateComponent(),
allowedMentions: model.SanitizeMentions,
messageReference: model.MessageReference);
}
return await model.TargetChannel.SendMessageAsync( if (model.Interaction is not null)
model.Text, {
embed: model.Embed, await model.Interaction.RunAsync(sentMsg);
embeds: model.Embeds, }
components: null,
allowedMentions: model.SanitizeMentions, return sentMsg;
messageReference: model.MessageReference);
} }
private EmbedBuilder PaintEmbedInternal(EmbedBuilder eb)
=> color switch
{
EmbedColor.Ok => eb.WithOkColor(),
EmbedColor.Pending => eb.WithPendingColor(),
EmbedColor.Error => eb.WithErrorColor(),
_ => throw new NotSupportedException()
};
private ulong? InternalResolveGuildId(IMessageChannel? targetChannel) private ulong? InternalResolveGuildId(IMessageChannel? targetChannel)
=> ctx?.Guild?.Id ?? (targetChannel as ITextChannel)?.GuildId; => ctx?.Guild?.Id ?? (targetChannel as ITextChannel)?.GuildId;
// todo not good, has to go to the user private async Task<IMessageChannel?> InternalResolveChannel()
private IMessageChannel? InternalResolveChannel() {
=> channel ?? ctx?.Channel ?? msg?.Channel; if (user is not null)
{
private string? GetText(LocStr? locStr) var ch = await user.CreateDMChannelAsync();
if (ch is not null)
{
return ch;
}
}
return channel ?? ctx?.Channel ?? msg?.Channel;
}
private string? GetText(LocStr? locStr, IMessageChannel targetChannel)
{ {
var targetChannel = InternalResolveChannel();
var guildId = InternalResolveGuildId(targetChannel); var guildId = InternalResolveGuildId(targetChannel);
return locStr is LocStr ls ? _bs.GetText(ls.Key, guildId, locParams) : plainText; return locStr is LocStr ls ? _bs.GetText(ls.Key, guildId, locParams) : plainText;
} }
private string GetText(LocStr locStr) private string GetText(LocStr locStr, IMessageChannel targetChannel)
{ {
var targetChannel = InternalResolveChannel();
var guildId = InternalResolveGuildId(targetChannel); var guildId = InternalResolveGuildId(targetChannel);
return _bs.GetText(locStr.Key, guildId, locStr.Params); return _bs.GetText(locStr.Key, guildId, locStr.Params);
} }
@@ -125,91 +179,108 @@ public sealed partial class ResponseBuilder
if (text is SmartPlainText spt) if (text is SmartPlainText spt)
plainText = spt.Text; plainText = spt.Text;
else if (text is SmartEmbedText set) else if (text is SmartEmbedText set)
embed = set.GetEmbed().Build(); embedBuilder = set.GetEmbed();
else if (text is SmartEmbedTextArray ser) else if (text is SmartEmbedTextArray ser)
embeds = ser.GetEmbedBuilders(); embeds = ser.GetEmbedBuilders();
return this; return this;
} }
private ResponseBuilder InternalColoredText(string text, EmbedColor color) private void InternalCreateEmbed(
{
embed = new EmbedBuilder()
.WithColor(color)
.WithDescription(text)
.Build();
return this;
}
private EmbedBuilder CreateEmbedInternal(
string? title, string? title,
string? text, 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? url = null,
string? footer = null) string? footer = null)
{ {
var eb = CreateEmbedInternal(title, text, url, footer); var eb = new NadekoEmbedBuilder(_bcs)
embed = PaintEmbedInternal(eb, EmbedColor.Error).Build(); .WithDescription(text);
return this;
}
if (!string.IsNullOrWhiteSpace(title))
eb.WithTitle(title);
if (!string.IsNullOrWhiteSpace(url))
eb = eb.WithUrl(url);
if (!string.IsNullOrWhiteSpace(footer))
eb = eb.WithFooter(footer);
embedBuilder = eb;
}
public ResponseBuilder Confirm( public ResponseBuilder Confirm(
string? title, string? title,
string? text, string text,
string? url = null, string? url = null,
string? footer = null) string? footer = null)
{ {
var eb = CreateEmbedInternal(title, text, url, footer); InternalCreateEmbed(title, text, url, footer);
embed = PaintEmbedInternal(eb, EmbedColor.Error).Build(); color = EmbedColor.Ok;
return this;
}
public ResponseBuilder Error(
string? title,
string text,
string? url = null,
string? footer = null)
{
InternalCreateEmbed(title, text, url, footer);
color = EmbedColor.Error;
return this;
}
public ResponseBuilder Pending(
string? title,
string text,
string? url = null,
string? footer = null)
{
InternalCreateEmbed(title, text, url, footer);
color = EmbedColor.Pending;
return this; return this;
} }
public ResponseBuilder Confirm(string text) public ResponseBuilder Confirm(string text)
=> InternalColoredText(text, EmbedColor.Ok); {
InternalCreateEmbed(null, text);
color = EmbedColor.Ok;
return this;
}
public ResponseBuilder Confirm(LocStr str) public ResponseBuilder Confirm(LocStr str)
=> Confirm(GetText(str)); {
embedLocDesc = str;
color = EmbedColor.Ok;
return this;
}
public ResponseBuilder Pending(string text) public ResponseBuilder Pending(string text)
=> InternalColoredText(text, EmbedColor.Ok); {
InternalCreateEmbed(null, text);
color = EmbedColor.Pending;
return this;
}
public ResponseBuilder Pending(LocStr str) public ResponseBuilder Pending(LocStr str)
=> Pending(GetText(str)); {
embedLocDesc = str;
color = EmbedColor.Pending;
return this;
}
public ResponseBuilder Error(string text) public ResponseBuilder Error(string text)
=> InternalColoredText(text, EmbedColor.Error); {
InternalCreateEmbed(null, text);
color = EmbedColor.Error;
return this;
}
public ResponseBuilder Error(LocStr str) public ResponseBuilder Error(LocStr str)
=> Error(GetText(str)); {
embedLocDesc = str;
color = EmbedColor.Error;
return this;
}
public ResponseBuilder UserBasedMentions() public ResponseBuilder UserBasedMentions()
{ {
@@ -220,17 +291,15 @@ public sealed partial class ResponseBuilder
private IUser? InternalResolveUser() private IUser? InternalResolveUser()
=> ctx?.User ?? user ?? msg?.Author; => ctx?.User ?? user ?? msg?.Author;
// todo embed colors
public ResponseBuilder Embed(EmbedBuilder eb) public ResponseBuilder Embed(EmbedBuilder eb)
{ {
embedBuilder = eb; embedBuilder = eb;
return this; return this;
} }
public ResponseBuilder Channel(IMessageChannel channel) public ResponseBuilder Channel(IMessageChannel ch)
{ {
this.channel = channel; channel = ch;
return this; return this;
} }
@@ -240,21 +309,21 @@ public sealed partial class ResponseBuilder
return this; return this;
} }
public ResponseBuilder Context(ICommandContext ctx) public ResponseBuilder Context(ICommandContext context)
{ {
this.ctx = ctx; ctx = context;
return this; return this;
} }
public ResponseBuilder Message(IUserMessage msg) public ResponseBuilder Message(IUserMessage message)
{ {
this.msg = msg; msg = message;
return this; return this;
} }
public ResponseBuilder User(IUser user) public ResponseBuilder User(IUser usr)
{ {
this.user = user; user = usr;
return this; return this;
} }
@@ -266,7 +335,6 @@ public sealed partial class ResponseBuilder
public ResponseBuilder Interaction(NadekoInteraction? interaction) public ResponseBuilder Interaction(NadekoInteraction? interaction)
{ {
// todo implement
inter = interaction; inter = interaction;
return this; return this;
} }
@@ -277,10 +345,10 @@ public sealed partial class ResponseBuilder
return this; return this;
} }
public ResponseBuilder FileName(Stream fileStream, string fileName) public ResponseBuilder File(Stream stream, string name)
{ {
this.fileStream = fileStream; fileStream = stream;
this.fileName = fileName; fileName = name;
return this; return this;
} }
@@ -300,27 +368,51 @@ public class PaginatedResponseBuilder
public SourcedPaginatedResponseBuilder<T> Items<T>(IReadOnlyCollection<T> items) public SourcedPaginatedResponseBuilder<T> Items<T>(IReadOnlyCollection<T> items)
=> new SourcedPaginatedResponseBuilder<T>(_builder) => new SourcedPaginatedResponseBuilder<T>(_builder)
.Items(items); .Items(items);
public SourcedPaginatedResponseBuilder<T> PageItems<T>(Func<int, Task<IEnumerable<T>>> items)
=> new SourcedPaginatedResponseBuilder<T>(_builder)
.PageItems(items);
} }
public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilder public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilder
{ {
private IReadOnlyCollection<T>? items; private IReadOnlyCollection<T>? items;
public Func<IReadOnlyList<T>, int, Task<EmbedBuilder>> PageFunc { get; private set; }
public Func<int, Task<IEnumerable<T>>> ItemsFunc { get; set; } public Func<IReadOnlyList<T>, int, Task<EmbedBuilder>> PageFunc { get; private set; } = static delegate
{
return Task.FromResult<EmbedBuilder>(new());
};
public Func<int, Task<IEnumerable<T>>> ItemsFunc { get; set; } = static delegate
{
return Task.FromResult(Enumerable.Empty<T>());
};
public Func<int, Task<SimpleInteractionBase>>? InteractionFunc { get; private set; }
public int TotalElements { get; private set; } = 1; public int TotalElements { get; private set; } = 1;
public int ItemsPerPage { get; private set; } = 9; public int ItemsPerPage { get; private set; } = 9;
public bool AddPaginatedFooter { get; private set; } = true; public bool AddPaginatedFooter { get; private set; } = true;
public bool IsEphemeral { get; private set; } public bool IsEphemeral { get; private set; }
public int InitialPage { get; set; }
public SourcedPaginatedResponseBuilder(ResponseBuilder builder) public SourcedPaginatedResponseBuilder(ResponseBuilder builder)
: base(builder) : base(builder)
{ {
} }
public SourcedPaginatedResponseBuilder<T> Items(IReadOnlyCollection<T> items) public SourcedPaginatedResponseBuilder<T> Items(IReadOnlyCollection<T> col)
{ {
this.items = items; items = col;
ItemsFunc = (i) => Task.FromResult(this.items.Skip(i * ItemsPerPage).Take(ItemsPerPage)); TotalElements = col.Count;
ItemsFunc = (i) => Task.FromResult(items.Skip(i * ItemsPerPage).Take(ItemsPerPage));
return this;
}
public SourcedPaginatedResponseBuilder<T> PageItems(Func<int, Task<IEnumerable<T>>> func)
{
ItemsFunc = func;
return this; return this;
} }
@@ -337,24 +429,22 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
return this; return this;
} }
// todo use it
public int InitialPage { get; set; }
public SourcedPaginatedResponseBuilder<T> Page(Func<IReadOnlyList<T>, int, EmbedBuilder> pageFunc) public SourcedPaginatedResponseBuilder<T> Page(Func<IReadOnlyList<T>, int, EmbedBuilder> pageFunc)
{ {
this.PageFunc = (xs, x) => Task.FromResult(pageFunc(xs, x)); PageFunc = (xs, x) => Task.FromResult(pageFunc(xs, x));
return this; return this;
} }
public SourcedPaginatedResponseBuilder<T> Page(Func<IReadOnlyList<T>, int, Task<EmbedBuilder>> pageFunc) public SourcedPaginatedResponseBuilder<T> Page(Func<IReadOnlyList<T>, int, Task<EmbedBuilder>> pageFunc)
{ {
this.PageFunc = pageFunc; PageFunc = pageFunc;
return this; return this;
} }
public SourcedPaginatedResponseBuilder<T> AddFooter() public SourcedPaginatedResponseBuilder<T> AddFooter(bool addFooter = true)
{ {
AddPaginatedFooter = true; AddPaginatedFooter = addFooter;
return this; return this;
} }
@@ -373,4 +463,10 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
return paginationSender.SendAsync(IsEphemeral); return paginationSender.SendAsync(IsEphemeral);
} }
public SourcedPaginatedResponseBuilder<T> Interaction(Func<int, Task<SimpleInteractionBase>> func)
{
InteractionFunc = async (i) => await func(i);
return this;
}
} }

View File

@@ -2,17 +2,27 @@
public static class ResponseBuilderExtensions public static class ResponseBuilderExtensions
{ {
// todo delete this
public static EmbedBuilder WithColor(this EmbedBuilder eb, EmbedColor color)
=> eb;
public static EmbedBuilder WithPendingColor(this EmbedBuilder eb) public static EmbedBuilder WithPendingColor(this EmbedBuilder eb)
=> eb.WithColor(EmbedColor.Error); {
if (eb is NadekoEmbedBuilder neb)
return neb.WithPendingColor();
return eb;
}
public static EmbedBuilder WithOkColor(this EmbedBuilder eb) public static EmbedBuilder WithOkColor(this EmbedBuilder eb)
=> eb.WithColor(EmbedColor.Ok); {
if (eb is NadekoEmbedBuilder neb)
return neb.WithOkColor();
return eb;
}
public static EmbedBuilder WithErrorColor(this EmbedBuilder eb) public static EmbedBuilder WithErrorColor(this EmbedBuilder eb)
=> eb.WithColor(EmbedColor.Error); {
if (eb is NadekoEmbedBuilder neb)
return neb.WithErrorColor();
return eb;
}
} }

View File

@@ -1,10 +1,12 @@
public class ResponseMessageModel public class ResponseMessageModel
{ {
public IMessageChannel TargetChannel { get; set; } public required IMessageChannel TargetChannel { get; set; }
public MessageReference MessageReference { get; set; } public MessageReference? MessageReference { get; set; }
public string Text { get; set; } public string? Text { get; set; }
public Embed Embed { get; set; } public Embed? Embed { get; set; }
public Embed[] Embeds { get; set; } public Embed[]? Embeds { get; set; }
public AllowedMentions SanitizeMentions { get; set; } public required AllowedMentions SanitizeMentions { get; set; }
public IUser User { get; set; } public IUser? User { get; set; }
public bool Ephemeral { get; set; }
public NadekoInteraction? Interaction { get; set; }
} }

View File

@@ -1,17 +0,0 @@
#nullable disable
namespace NadekoBot.Services;
// todo remove
public sealed class DiscordEmbedBuilderWrapper
{
// 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")
// };
}

View File

@@ -39,7 +39,7 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
var culture = _loc.GetCultureInfo(guild); var culture = _loc.GetCultureInfo(guild);
var em = new EmbedBuilder() var em = _sender.CreateEmbed()
.AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true); .AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
_dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides); _dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides);

View File

@@ -0,0 +1,30 @@
namespace NadekoBot.Extensions;
public static class CommandContextExtensions
{
private static readonly Emoji _okEmoji = new Emoji("✅");
private static readonly Emoji _warnEmoji = new Emoji("⚠️");
private static readonly Emoji _errorEmoji = new Emoji("❌");
public static Task ReactAsync(this ICommandContext ctx, MsgType type)
{
var emoji = type switch
{
MsgType.Error => _errorEmoji,
MsgType.Pending => _warnEmoji,
MsgType.Ok => _okEmoji,
_ => throw new ArgumentOutOfRangeException(nameof(type)),
};
return ctx.Message.AddReactionAsync(emoji);
}
public static Task OkAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Ok);
public static Task ErrorAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Error);
public static Task WarningAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Pending);
}

View File

@@ -9,17 +9,24 @@ namespace NadekoBot.Extensions;
public static class Extensions public static class Extensions
{ {
public static DateOnly ToDateOnly(this DateTime dateTime)
=> DateOnly.FromDateTime(dateTime);
public static bool IsBeforeToday(this DateTime date)
=> date < DateTime.UtcNow.Date;
private static readonly Regex _urlRegex = private static readonly Regex _urlRegex =
new(@"^(https?|ftp)://(?<path>[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled); new(@"^(https?|ftp)://(?<path>[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled);
// public static EmbedBuilder WithAuthor(this EmbedBuilder eb, IUser author) /// <summary>
// => eb.WithAuthor(author.ToString()!, author.RealAvatarUrl().ToString()); /// Converts <see cref="DateTime"/> to <see cref="DateOnly"/>
/// </summary>
/// <param name="dateTime"> The <see cref="DateTime"/> to convert. </param>
/// <returns> The <see cref="DateOnly"/>. </returns>
public static DateOnly ToDateOnly(this DateTime dateTime)
=> DateOnly.FromDateTime(dateTime);
/// <summary>
/// Determines if <see cref="DateTime"/> is before today
/// </summary>
/// <param name="date"> The <see cref="DateTime"/> to check. </param>
/// <returns> True if <see cref="DateTime"/> is before today. </returns>
public static bool IsBeforeToday(this DateTime date)
=> date < DateTime.UtcNow.Date;
public static Task EditAsync(this IUserMessage msg, SmartText text) public static Task EditAsync(this IUserMessage msg, SmartText text)
=> text switch => text switch

Some files were not shown because too many files have changed in this diff Show More