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
- For self-hosters:
- Added `.sqlselectcsv` which will return results in a csv file instead of an embed.
- Added a page parameter to `.feedlist`
### 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
- `.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

View File

@@ -58,7 +58,7 @@ namespace NadekoBot.Tests
aliasesPath);
var methodNames = GetCommandMethodNames();
var isSuccess = true;
foreach (var methodName in methodNames)
{

View File

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

View File

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

View File

@@ -97,7 +97,7 @@ public class GreetService : INService, IReadyExecutor
{
var newContent = await _repSvc.ReplaceAsync(toSend,
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)
toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
@@ -217,12 +217,12 @@ public class GreetService : INService, IReadyExecutor
text = await _repSvc.ReplaceAsync(text, repCtx);
try
{
var toDelete = await channel.SendAsync(text);
var toDelete = await _sender.Response(channel).Text(text).SendAsync();
if (conf.AutoDeleteByeMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions ||
ex.DiscordCode == DiscordErrorCode.UnknownChannel)
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{
Log.Warning(ex,
"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);
try
{
var toDelete = await channel.SendAsync(text);
var toDelete = await _sender.Response(channel).Text(text).SendAsync();
if (conf.AutoDeleteGreetMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions ||
ex.DiscordCode == DiscordErrorCode.UnknownChannel)
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{
Log.Warning(ex,
"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
seta.Embeds = seta.Embeds.Append(new SmartEmbedArrayElementText()
{
Footer = CreateFooterSource(user)
}).ToArray();
{
Footer = CreateFooterSource(user)
})
.ToArray();
}
}
}

View File

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

View File

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

View File

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

View File

@@ -241,7 +241,7 @@ public partial class Administration
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)
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);

View File

@@ -12,7 +12,7 @@ public partial class Administration
{
_rero = rero;
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
@@ -29,15 +29,16 @@ public partial class Administration
if (levelReq < 0)
return;
var msg = await ctx.Channel.GetMessageAsync(messageId);
if (msg is null)
{
await Response().Error(strs.not_found).SendAsync();
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();
return;
@@ -71,48 +72,52 @@ public partial class Administration
{
if (--page < 0)
return;
var reros = await _rero.GetReactionRolesAsync(ctx.Guild.Id);
await ctx.SendPaginatedConfirmAsync(page, curPage =>
{
var embed = new EmbedBuilder()
.WithOkColor();
var allReros = await _rero.GetReactionRolesAsync(ctx.Guild.Id);
var content = string.Empty;
foreach (var g in reros.OrderBy(x => x.Group)
.Skip(curPage * 10)
.GroupBy(x => x.MessageId)
.OrderBy(x => x.Key))
{
var messageId = g.Key;
content +=
$"[{messageId}](https://discord.com/channels/{ctx.Guild.Id}/{g.First().ChannelId}/{g.Key})\n";
await Response()
.Paginated()
.Items(allReros.OrderBy(x => x.Group).ToList())
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var embed = _sender.CreateEmbed()
.WithOkColor();
var groupGroups = g.GroupBy(x => x.Group);
var content = string.Empty;
foreach (var g in items
.GroupBy(x => x.MessageId)
.OrderBy(x => x.Key))
{
var messageId = g.Key;
content +=
$"[{messageId}](https://discord.com/channels/{ctx.Guild.Id}/{g.First().ChannelId}/{g.Key})\n";
foreach (var ggs in groupGroups)
{
content += $"`< {(g.Key == 0 ? ("Not Exclusive (Group 0)") : ($"Group {ggs.Key}"))} >`\n";
var groupGroups = g.GroupBy(x => x.Group);
foreach (var rero in ggs)
{
content +=
$"\t{rero.Emote} -> {(ctx.Guild.GetRole(rero.RoleId)?.Mention ?? "<missing role>")}";
if (rero.LevelReq > 0)
content += $" (lvl {rero.LevelReq}+)";
content += '\n';
}
}
foreach (var ggs in groupGroups)
{
content += $"`< {(g.Key == 0 ? ("Not Exclusive (Group 0)") : ($"Group {ggs.Key}"))} >`\n";
}
foreach (var rero in ggs)
{
content +=
$"\t{rero.Emote} -> {(ctx.Guild.GetRole(rero.RoleId)?.Mention ?? "<missing role>")}";
if (rero.LevelReq > 0)
content += $" (lvl {rero.LevelReq}+)";
content += '\n';
}
}
}
embed.WithDescription(string.IsNullOrWhiteSpace(content)
? "There are no reaction roles on this server"
: content);
embed.WithDescription(string.IsNullOrWhiteSpace(content)
? "There are no reaction roles on this server"
: content);
return embed;
}, reros.Count, 10);
return embed;
})
.SendAsync();
}
[Cmd]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ public static class NadekoExpressionExtensions
IUserMessage ctx,
IReplacementService repSvc,
DiscordSocketClient client,
bool sanitize)
IMessageSenderService sender)
{
var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel;
@@ -46,7 +46,7 @@ public static class NadekoExpressionExtensions
var text = SmartText.CreateFrom(cr.Response);
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)]

View File

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

View File

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

View File

@@ -59,7 +59,7 @@ public partial class Gambling
{
var bal = await _bank.GetBalanceAsync(ctx.User.Id);
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithOkColor()
.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] + " "));
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithTitle("BlackJack")
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);

View File

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

View File

@@ -38,7 +38,7 @@ public partial class Gambling
var fileName = $"dice.{format.FileExtensions.First()}";
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithAuthor(ctx.User)
.AddField(GetText(strs.roll2), gen)
@@ -115,7 +115,7 @@ public partial class Gambling
d.Dispose();
var imageName = $"dice.{format.FileExtensions.First()}";
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithAuthor(ctx.User)
.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++)
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithAuthor(ctx.User)
.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);
var sum = arr.Sum();
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithAuthor(ctx.User)
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))

View File

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

View File

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

View File

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

View File

@@ -73,7 +73,7 @@ public partial class Gambling : GamblingModule<GamblingService>
{
var stats = await _gamblingTxTracker.GetAllAsync();
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithOkColor();
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
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.economy_state))
.AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
@@ -310,7 +310,7 @@ public partial class Gambling : GamblingModule<GamblingService>
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()
?? $"{userId}")))
.WithOkColor();
@@ -360,7 +360,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return;
}
var eb = new EmbedBuilder().WithOkColor();
var eb = _sender.CreateEmbed().WithOkColor();
eb.WithAuthor(ctx.User);
eb.WithTitle(GetText(strs.transaction));
@@ -624,7 +624,7 @@ public partial class Gambling : GamblingModule<GamblingService>
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;
@@ -731,7 +731,7 @@ public partial class Gambling : GamblingModule<GamblingService>
str = GetText(strs.better_luck);
}
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User)
.WithDescription(Format.Bold(str))
.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);
List<DiscordUser> cleanRichest;
// List<DiscordUser> cleanRichest;
// it's pointless to have clean on dm context
if (ctx.Guild is null)
{
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();
}
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 =>
else
{
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;
if (!opts.Clean)
{
await using var uow = _db.GetDbContext();
toSend = await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, curPage);
}
else
{
toSend = cleanRichest.Skip(curPage * 9).Take(9).ToList();
}
await Response()
.Paginated()
.PageItems(GetTopRichest)
.PageSize(9)
.CurrentPage(page)
.Page((toSend, curPage) =>
{
var embed = _sender.CreateEmbed().WithOkColor()
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
if (!toSend.Any())
{
embed.WithDescription(GetText(strs.no_user_on_this_page));
return embed;
}
if (!toSend.Any())
{
embed.WithDescription(GetText(strs.no_user_on_this_page));
return Task.FromResult(embed);
}
for (var i = 0; i < toSend.Count; i++)
{
var x = toSend[i];
var usrStr = x.ToString().TrimTo(20, true);
for (var i = 0; i < toSend.Count; i++)
{
var x = toSend[i];
var usrStr = x.ToString().TrimTo(20, true);
var j = i;
embed.AddField("#" + ((9 * curPage) + j + 1) + " " + usrStr, N(x.CurrencyAmount), true);
}
var j = i;
embed.AddField("#" + ((9 * curPage) + j + 1) + " " + usrStr, N(x.CurrencyAmount), true);
}
return embed;
},
opts.Clean ? cleanRichest.Count() : 9000,
9,
opts.Clean);
return Task.FromResult(embed);
})
.SendAsync();
}
public enum InputRpsPick : byte
@@ -861,7 +857,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return;
}
var embed = new EmbedBuilder();
var embed = _sender.CreateEmbed();
string msg;
if (result.Result == RpsResultType.Draw)
@@ -922,7 +918,7 @@ public partial class Gambling : GamblingModule<GamblingService>
sb.AppendLine();
}
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithDescription(sb.ToString())
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,7 @@ public partial class Games : NadekoModule<GamesService>
return;
var res = _service.GetEightballResponse(ctx.User.Id, question);
await Response().Embed(new EmbedBuilder()
await Response().Embed(_sender.CreateEmbed()
.WithOkColor()
.WithDescription(ctx.User.ToString())
.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)
{
return new EmbedBuilder()
return sender.CreateEmbed()
.WithOkColor()
.AddField("Hangman", Draw(state))
.AddField("Guess", Format.Code(state.Word))
@@ -36,14 +36,14 @@ public partial class Games
if (state.Phase == HangmanGame.Phase.Ended && state.Failed)
{
return new EmbedBuilder()
return sender.CreateEmbed()
.WithErrorColor()
.AddField("Hangman", Draw(state))
.AddField("Guess", Format.Code(state.Word))
.WithFooter(state.MissedLetters.Join(' '));
}
return new EmbedBuilder()
return sender.CreateEmbed()
.WithOkColor()
.AddField("Hangman", Draw(state))
.AddField("Guess", Format.Code(state.Word))
@@ -60,7 +60,7 @@ public partial class Games
return;
}
var eb = GetEmbed(hangman);
var eb = GetEmbed(_sender, hangman);
eb.WithDescription(GetText(strs.hangman_game_started));
await Response().Embed(eb).SendAsync();
}

View File

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

View File

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

View File

@@ -82,7 +82,6 @@ public class TypingGame
do
{
// todo fix all modifies
await Task.Delay(2000);
time -= 2;
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;
_finishedUserIds.Add(msg.Author.Id);
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithTitle($"{msg.Author} finished the race!")
.AddField("Place", $"#{_finishedUserIds.Count}", true)

View File

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

View File

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

View File

@@ -84,32 +84,32 @@ public sealed class Help : NadekoModule<HelpService>
topLevelModules.Add(m);
}
await ctx.SendPaginatedConfirmAsync(page,
cur =>
{
var embed = new EmbedBuilder().WithOkColor().WithTitle(GetText(strs.list_of_modules));
await Response()
.Paginated()
.Items(topLevelModules)
.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())
{
embed = embed.WithOkColor().WithDescription(GetText(strs.module_page_empty));
return embed;
}
items.OrderBy(module => module.Name)
.ToList()
.ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}",
GetModuleDescription(module.Name)
+ "\n"
+ Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))),
true));
localModules.OrderBy(module => module.Name)
.ToList()
.ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}",
GetModuleDescription(module.Name)
+ "\n"
+ Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))),
true));
return embed;
},
topLevelModules.Count(),
12,
false);
return embed;
})
.SendAsync();
}
private string GetModuleDescription(string moduleName)
@@ -271,7 +271,7 @@ public sealed class Help : NadekoModule<HelpService>
var cnt = 0;
var groups = cmdsWithGroup.GroupBy(_ => cnt++ / 48).ToArray();
var embed = new EmbedBuilder().WithOkColor();
var embed = _sender.CreateEmbed().WithOkColor();
foreach (var g in groups)
{
var last = g.Count();
@@ -303,9 +303,9 @@ public sealed class Help : NadekoModule<HelpService>
private async Task Group(ModuleInfo group)
{
var eb = new EmbedBuilder()
.WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor();
var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor();
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();
if (data == default)
return;
await ch.SendAsync(data);
await Response().Text(data).SendAsync();
try
{
await ctx.OkAsync();
@@ -534,9 +535,9 @@ public sealed class Help : NadekoModule<HelpService>
label: "Selfhosting"),
SelfhostAction));
var eb = new EmbedBuilder()
.WithOkColor()
.WithTitle("Thank you for considering to donate to the NadekoBot project!");
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithTitle("Thank you for considering to donate to the NadekoBot project!");
eb
.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
{
await (await ctx.User.CreateDMChannelAsync()).EmbedAsync(eb, inter: selfhostInter);
await Response()
.Channel(await ctx.User.CreateDMChannelAsync())
.Embed(eb)
.Interaction(selfhostInter)
.SendAsync();
_ = ctx.OkAsync();
}
catch

View File

@@ -2,11 +2,22 @@ using NadekoBot.Common.ModuleBehaviors;
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)
{
var settings = bss.Data;
var settings = _bss.Data;
if (guild is null)
{
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
// if they're not, then reply to every DM
if (settings.DmHelpTextKeywords is not null &&
!settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
if (settings.DmHelpTextKeywords is not null
&& !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
{
return;
}
var repCtx = new ReplacementContext(guild: guild, channel: msg.Channel, users: msg.Author)
.WithOverride("%prefix%", () => bss.Data.Prefix)
.WithOverride("%bot.prefix%", () => bss.Data.Prefix);
.WithOverride("%prefix%", () => _bss.Data.Prefix)
.WithOverride("%bot.prefix%", () => _bss.Data.Prefix);
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

@@ -11,7 +11,7 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
{
_repo = repo;
}
[Cmd]
[OwnerOnly]
public async Task MedusaLoad(string? name = null)
@@ -21,11 +21,11 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
var loaded = _service.GetLoadedMedusae()
.Select(x => x.Name)
.ToHashSet();
var unloaded = _service.GetAllMedusae()
.Where(x => !loaded.Contains(x))
.Select(x => Format.Code(x.ToString()))
.ToArray();
.Where(x => !loaded.Contains(x))
.Select(x => Format.Code(x.ToString()))
.ToArray();
if (unloaded.Length == 0)
{
@@ -33,16 +33,18 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
return;
}
await ctx.SendPaginatedConfirmAsync(0,
page =>
{
return new EmbedBuilder()
.WithOkColor()
.WithTitle(GetText(strs.list_of_unloaded))
.WithDescription(unloaded.Skip(10 * page).Take(10).Join('\n'));
},
unloaded.Length,
10);
await Response()
.Paginated()
.Items(unloaded)
.PageSize(10)
.Page((items, _) =>
{
return _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.list_of_unloaded))
.WithDescription(items.Join('\n'));
})
.SendAsync();
return;
}
@@ -63,7 +65,7 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
await Response().Error(locStr).SendAsync();
}
}
[Cmd]
[OwnerOnly]
public async Task MedusaUnload(string? name = null)
@@ -77,15 +79,17 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
return;
}
await Response().Embed(new EmbedBuilder()
.WithOkColor()
.WithTitle(GetText(strs.loaded_medusae))
.WithDescription(loaded.Select(x => x.Name)
.Join("\n"))).SendAsync();
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.loaded_medusae))
.WithDescription(loaded.Select(x => x.Name)
.Join("\n")))
.SendAsync();
return;
}
var res = await _service.UnloadMedusaAsync(name);
if (res == MedusaUnloadResult.Success)
await Response().Confirm(strs.medusa_unloaded(Format.Code(name))).SendAsync();
@@ -113,27 +117,29 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
await Response().Pending(strs.no_medusa_available).SendAsync();
return;
}
var loaded = _service.GetLoadedMedusae()
.Select(x => x.Name)
.ToHashSet();
var output = all
.Select(m =>
{
var emoji = loaded.Contains(m) ? "`✅`" : "`🔴`";
return $"{emoji} `{m}`";
})
.ToArray();
.Select(m =>
{
var emoji = loaded.Contains(m) ? "`✅`" : "`🔴`";
return $"{emoji} `{m}`";
})
.ToArray();
await ctx.SendPaginatedConfirmAsync(0,
page => new EmbedBuilder()
.WithOkColor()
.WithTitle(GetText(strs.list_of_medusae))
.WithDescription(output.Skip(page * 10).Take(10).Join('\n')),
output.Length,
10);
await Response()
.Paginated()
.Items(output)
.PageSize(10)
.Page((items, _) => _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.list_of_medusae))
.WithDescription(items.Join('\n')))
.SendAsync();
}
[Cmd]
@@ -147,7 +153,7 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
var found = medusae.FirstOrDefault(x => string.Equals(x.Name,
name,
StringComparison.InvariantCultureIgnoreCase));
if (found is null)
{
await Response().Error(strs.medusa_name_not_found).SendAsync();
@@ -156,26 +162,26 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
var cmdCount = found.Sneks.Sum(x => x.Commands.Count);
var cmdNames = found.Sneks
.SelectMany(x => Format.Code(string.IsNullOrWhiteSpace(x.Prefix)
? x.Name
: $"{x.Prefix} {x.Name}"))
.Join("\n");
.SelectMany(x => Format.Code(string.IsNullOrWhiteSpace(x.Prefix)
? x.Name
: $"{x.Prefix} {x.Name}"))
.Join("\n");
var eb = new EmbedBuilder()
.WithOkColor()
.WithAuthor(GetText(strs.medusa_info))
.WithTitle(found.Name)
.WithDescription(found.Description)
.AddField(GetText(strs.sneks_count(found.Sneks.Count)),
found.Sneks.Count == 0
? "-"
: found.Sneks.Select(x => x.Name).Join('\n'),
true)
.AddField(GetText(strs.commands_count(cmdCount)),
string.IsNullOrWhiteSpace(cmdNames)
? "-"
: cmdNames,
true);
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithAuthor(GetText(strs.medusa_info))
.WithTitle(found.Name)
.WithDescription(found.Description)
.AddField(GetText(strs.sneks_count(found.Sneks.Count)),
found.Sneks.Count == 0
? "-"
: found.Sneks.Select(x => x.Name).Join('\n'),
true)
.AddField(GetText(strs.commands_count(cmdCount)),
string.IsNullOrWhiteSpace(cmdNames)
? "-"
: cmdNames,
true);
await Response().Embed(eb).SendAsync();
return;
@@ -186,42 +192,49 @@ public partial class Medusa : NadekoModule<IMedusaLoaderService>
await Response().Pending(strs.no_medusa_loaded).SendAsync();
return;
}
await ctx.SendPaginatedConfirmAsync(0,
page =>
{
var eb = new EmbedBuilder()
.WithOkColor();
foreach (var medusa in medusae.Skip(page * 9).Take(9))
{
eb.AddField(medusa.Name,
$"""
`Sneks:` {medusa.Sneks.Count}
`Commands:` {medusa.Sneks.Sum(x => x.Commands.Count)}
--
{medusa.Description}
""");
}
await Response()
.Paginated()
.Items(medusae)
.PageSize(9)
.CurrentPage(0)
.Page((items, _) =>
{
var eb = _sender.CreateEmbed()
.WithOkColor();
return eb;
}, medusae.Count, 9);
foreach (var medusa in items)
{
eb.AddField(medusa.Name,
$"""
`Sneks:` {medusa.Sneks.Count}
`Commands:` {medusa.Sneks.Sum(x => x.Commands.Count)}
--
{medusa.Description}
""");
}
return eb;
})
.SendAsync();
}
[Cmd]
[OwnerOnly]
public async Task MedusaSearch()
{
var eb = new EmbedBuilder()
.WithTitle(GetText(strs.list_of_medusae))
.WithOkColor();
var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.list_of_medusae))
.WithOkColor();
foreach (var item in await _repo.GetModuleItemsAsync())
{
eb.AddField(item.Name, $"""
{item.Description}
`{item.Command}`
""", true);
eb.AddField(item.Name,
$"""
{item.Description}
`{item.Command}`
""",
true);
}
await Response().Embed(eb).SendAsync();

View File

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

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,7 @@ public partial class Patronage : NadekoModule
//
// 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}!")
// .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 quotaStats = await _service.GetUserQuotaStatistic(user.Id);
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithAuthor(user)
.WithTitle(GetText(strs.patron_info))
.WithOkColor();

View File

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

View File

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

View File

@@ -61,7 +61,7 @@ public partial class Permissions
else
await Response().Confirm(strs.cmdcd_add(Format.Bold(name), Format.Bold(secs.ToString()))).SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(0)]
@@ -80,7 +80,7 @@ public partial class Permissions
{
if (--page < 0)
return;
var channel = (ITextChannel)ctx.Channel;
var localSet = _service.GetCommandCooldowns(ctx.Guild.Id);
@@ -88,17 +88,21 @@ public partial class Permissions
await Response().Confirm(strs.cmdcd_none).SendAsync();
else
{
await ctx.SendPaginatedConfirmAsync(page, curPage =>
{
var items = localSet.Skip(curPage * 15)
.Take(15)
.Select(x => $"{Format.Code(x.CommandName)}: {x.Seconds.Seconds().Humanize(maxUnit: TimeUnit.Second, culture: Culture)}");
await Response()
.Paginated()
.Items(localSet)
.PageSize(15)
.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()
.WithOkColor()
.WithDescription(items.Join("\n"));
}, localSet.Count, 15);
return _sender.CreateEmbed()
.WithOkColor()
.WithDescription(output.Join("\n"));
})
.SendAsync();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -111,28 +111,36 @@ public partial class Searches
[Cmd]
[RequireContext(ContextType.Guild)]
[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);
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;
}
await ctx.SendPaginatedConfirmAsync(0,
cur =>
{
var embed = new EmbedBuilder().WithOkColor();
var i = 0;
var fs = string.Join("\n",
feeds.Skip(cur * 10).Take(10).Select(x => $"`{(cur * 10) + ++i}.` <#{x.ChannelId}> {x.Url}"));
return embed.WithDescription(fs);
},
feeds.Count,
10);
await Response()
.Paginated()
.Items(feeds)
.PageSize(10)
.CurrentPage(page)
.Page((items, cur) =>
{
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);
})
.SendAsync();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -95,9 +95,9 @@ public partial class Searches
.Page((elements, cur) =>
{
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++)
{
var elem = elements[index];

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ public partial class Searches
await ctx.Channel.TriggerTypingAsync();
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();
}
@@ -80,7 +80,7 @@ public partial class Searches
{
var langs = _service.GetLanguages().ToList();
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.supported_languages))
.WithOkColor();

View File

@@ -25,7 +25,7 @@ public partial class Searches
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync($"{XKCD_URL}/info.0.json");
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithImageUrl(comic.ImageLink)
.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 comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithImageUrl(comic.ImageLink)
.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();
await ctx.SendPaginatedConfirmAsync(page,
curPage =>
{
return new EmbedBuilder()
.WithOkColor()
.WithTitle(GetText(strs.alias_list))
.WithDescription(string.Join("\n",
arr.Skip(curPage * 10).Take(10).Select(x => $"`{x.Key}` => `{x.Value}`")));
},
arr.Length,
10);
await Response()
.Paginated()
.Items(arr)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
return _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.alias_list))
.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)
{
var configNames = _settingServices.Select(x => x.Name);
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithErrorColor()
.WithDescription(GetText(strs.config_not_found(Format.Code(name))))
.AddField(GetText(strs.config_list), string.Join("\n", configNames));
@@ -43,7 +43,7 @@ public partial class Utility
name = name?.ToLowerInvariant();
if (string.IsNullOrWhiteSpace(name))
{
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.config_list))
.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 (setting is null)
{
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithErrorColor()
.WithDescription(GetText(strs.config_not_found(Format.Code(name))))
.AddField(GetText(strs.config_list), string.Join("\n", configNames));
@@ -75,7 +75,7 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(prop))
{
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();
@@ -88,7 +88,7 @@ public partial class Utility
if (!exists)
{
var propStrings = GetPropsAndValuesString(setting, propNames);
var propErrorEmbed = new EmbedBuilder()
var propErrorEmbed = _sender.CreateEmbed()
.WithErrorColor()
.WithDescription(GetText(
strs.config_prop_not_found(Format.Code(prop), Format.Code(name))))
@@ -110,7 +110,7 @@ public partial class Utility
if (prop != "currency.sign")
value = Format.Code(Format.Sanitize(value.TrimTo(1000)), "json");
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.AddField("Config", Format.Code(setting.Name), true)
.AddField("Prop", Format.Code(prop), true)

View File

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

View File

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

View File

@@ -54,7 +54,7 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(features))
features = "-";
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithAuthor(GetText(strs.server_info))
.WithTitle(guild.Name)
.AddField(GetText(strs.id), guild.Id.ToString(), true)
@@ -87,7 +87,7 @@ public partial class Utility
return;
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 embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions(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)
.AddMilliseconds(role.Id >> 22);
var usercount = role.Members.LongCount();
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithTitle(role.Name.TrimTo(128))
.WithDescription(role.Permissions.ToList().Join(" | "))
.AddField(GetText(strs.id), role.Id.ToString(), true)
@@ -133,7 +133,7 @@ public partial class Utility
if (user is null)
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))
embed.AddField(GetText(strs.nickname), user.Nickname, true);
@@ -204,7 +204,7 @@ public partial class Utility
kvp.Value)));
}
await Response().Embed(new EmbedBuilder()
await Response().Embed(_sender.CreateEmbed()
.WithTitle(GetText(strs.activity_page(page + 1)))
.WithOkColor()
.WithFooter(GetText(

View File

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

View File

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

View File

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

View File

@@ -64,7 +64,7 @@ public partial class Utility
}
var description = GetRepeaterInfoString(removed);
await Response().Embed(new EmbedBuilder()
await Response().Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.repeater_removed(index + 1)))
.WithDescription(description)).SendAsync();
@@ -187,7 +187,7 @@ public partial class Utility
}
var description = GetRepeaterInfoString(runner);
await Response().Embed(new EmbedBuilder()
await Response().Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.repeater_created))
.WithDescription(description)).SendAsync();
@@ -205,7 +205,7 @@ public partial class Utility
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;
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 object _queueLocker = new();
private readonly IMessageSenderService _sender;
public RepeaterService(
DiscordSocketClient client,
DbService db,
IReplacementService repSvc,
IBotCredentials creds)
IBotCredentials creds,
IMessageSenderService sender)
{
_db = db;
_repSvc = repSvc;
_creds = creds;
_client = client;
_sender = sender;
using var uow = _db.GetDbContext();
var shardRepeaters = uow.Set<Repeater>()
.Where(x => (int)(x.GuildId / Math.Pow(2, 22)) % _creds.TotalShards
== _client.ShardId)
.AsNoTracking()
.ToList();
.Where(x => (int)(x.GuildId / Math.Pow(2, 22)) % _creds.TotalShards
== _client.ShardId)
.AsNoTracking()
.ToList();
_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();
var toTrigger = await uow.Set<Repeater>().AsNoTracking()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
var toTrigger = await uow.Set<Repeater>()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
if (toTrigger is null)
return false;
@@ -265,7 +269,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
var text = SmartText.CreateFrom(repeater.Message);
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("🔄"));
if (_noRedundant.Contains(repeater.Id))
@@ -308,12 +312,13 @@ public sealed class RepeaterService : IReadyExecutor, INService
private async Task SetRepeaterLastMessageInternal(int repeaterId, ulong lastMsgId)
{
await using var uow = _db.GetDbContext();
await uow.Set<Repeater>().AsQueryable()
.Where(x => x.Id == repeaterId)
.UpdateAsync(rep => new()
{
LastMessageId = lastMsgId
});
await uow.Set<Repeater>()
.AsQueryable()
.Where(x => x.Id == repeaterId)
.UpdateAsync(rep => new()
{
LastMessageId = lastMsgId
});
}
public async Task<RunningRepeater?> AddRepeaterAsync(
@@ -358,10 +363,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
throw new ArgumentOutOfRangeException(nameof(index));
await using var uow = _db.GetDbContext();
var toRemove = await uow.Set<Repeater>().AsNoTracking()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
var toRemove = await uow.Set<Repeater>()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
if (toRemove is null)
return null;
@@ -389,10 +395,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
public async Task<bool?> ToggleRedundantAsync(ulong guildId, int index)
{
await using var uow = _db.GetDbContext();
var toToggle = await uow.Set<Repeater>().AsQueryable()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
var toToggle = await uow.Set<Repeater>()
.AsQueryable()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
if (toToggle is null)
return null;
@@ -411,9 +418,9 @@ public sealed class RepeaterService : IReadyExecutor, INService
{
await using var ctx = _db.GetDbContext();
var toSkip = await ctx.Set<Repeater>()
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
.Where(x => x.GuildId == guildId)
.Skip(index)
.FirstOrDefaultAsyncEF();
if (toSkip is null)
return null;

View File

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

View File

@@ -13,7 +13,7 @@ public partial class Utility
{
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))

View File

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

View File

@@ -127,10 +127,9 @@ public partial class Xp
.Paginated()
.Items(allUsers)
.PageSize(10)
.CurrentPage(0)
.Page((users, _) =>
{
var embed = new EmbedBuilder()
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithTitle($"{club}")
.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();
return ctx.SendPaginatedConfirmAsync(page,
_ =>
{
var toShow = string.Join("\n",
bans
.Skip(page * 10)
.Take(10)
.Select(x => x.ToString()));
return Response()
.Paginated()
.Items(bans)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
return new EmbedBuilder()
.WithTitle(GetText(strs.club_bans_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
},
bans.Length,
10);
return _sender.CreateEmbed()
.WithTitle(GetText(strs.club_bans_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
})
.SendAsync();
}
[Cmd]
@@ -236,18 +234,21 @@ public partial class Xp
var apps = club.Applicants.Select(x => x.User).ToArray();
return ctx.SendPaginatedConfirmAsync(page,
_ =>
{
var toShow = string.Join("\n", apps.Skip(page * 10).Take(10).Select(x => x.ToString()));
return Response()
.Paginated()
.Items(apps)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
return new EmbedBuilder()
.WithTitle(GetText(strs.club_apps_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
},
apps.Length,
10);
return _sender.CreateEmbed()
.WithTitle(GetText(strs.club_apps_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
})
.SendAsync();
}
[Cmd]
@@ -412,7 +413,7 @@ public partial class Xp
? "-"
: desc;
var eb = new EmbedBuilder()
var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User)
.WithTitle(GetText(strs.club_desc_update))
.WithOkColor()
@@ -443,7 +444,7 @@ public partial class Xp
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;
foreach (var club in clubs)
@@ -464,7 +465,7 @@ public partial class Xp
return;
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();
return;
}

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,20 @@
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 T? _state;
@@ -13,7 +25,7 @@ public class SimpleInteraction<T>
_state = state;
}
public async Task TriggerAsync(SocketMessageComponent smc)
public override async Task TriggerAsync(SocketMessageComponent smc)
{
await _onClick(smc, _state!);
}

View File

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

View File

@@ -7,4 +7,6 @@ public interface IMessageSenderService
ResponseBuilder Response(IUser user);
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;
namespace NadekoBot.Extensions;
@@ -5,27 +6,52 @@ namespace NadekoBot.Extensions;
public sealed class MessageSenderService : IMessageSenderService, INService
{
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;
_bcs = bcs;
_client = client;
}
public ResponseBuilder Response(IMessageChannel channel)
=> new ResponseBuilder(_bs)
=> new ResponseBuilder(_bs, _bcs, _client)
.Channel(channel);
public ResponseBuilder Response(ICommandContext ctx)
=> new ResponseBuilder(_bs)
=> new ResponseBuilder(_bs, _bcs, _client)
.Context(ctx);
public ResponseBuilder Response(IUser user)
=> new ResponseBuilder(_bs)
=> new ResponseBuilder(_bs, _bcs, _client)
.User(user);
// todo fix interactions
public ResponseBuilder Response(SocketMessageComponent smc)
=> new ResponseBuilder(_bs)
=> new ResponseBuilder(_bs, _bcs, _client)
.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_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 ResponseBuilder builder;
private readonly DiscordSocketClient client;
private readonly ResponseBuilder _builder;
private readonly DiscordSocketClient _client;
private int currentPage;
public PaginationSender(
SourcedPaginatedResponseBuilder<T> paginationBuilder,
ResponseBuilder builder
)
ResponseBuilder builder)
{
this._paginationBuilder = paginationBuilder;
this.builder = builder;
_paginationBuilder = paginationBuilder;
_builder = builder;
client = (DiscordSocketClient)builder.ctx.Client;
currentPage = 0;
_client = builder.Client;
currentPage = paginationBuilder.InitialPage;
}
public async Task SendAsync(bool ephemeral = false)
@@ -38,7 +34,7 @@ public partial class ResponseBuilder
if (_paginationBuilder.AddPaginatedFooter)
embed.AddPaginatedFooter(currentPage, lastPage);
SimpleInteraction<T>? maybeInter = null;
SimpleInteractionBase? maybeInter = null;
async Task<ComponentBuilder> GetComponentBuilder()
{
@@ -48,22 +44,22 @@ public partial class ResponseBuilder
.WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_LEFT)
.WithDisabled(lastPage == 0)
.WithEmote(_arrowLeft)
.WithEmote(InteractionHelpers.ArrowLeft)
.WithDisabled(currentPage <= 0));
// todo
// if (interFactory is not null)
// {
// maybeInter = await interFactory(currentPage);
//
// if (maybeInter is not null)
// cb.WithButton(maybeInter.Button);
// }
if (_paginationBuilder.InteractionFunc is not null)
{
maybeInter = await _paginationBuilder.InteractionFunc(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));
.WithEmote(InteractionHelpers.ArrowRight));
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 msg = await model.TargetChannel
@@ -104,7 +100,8 @@ public partial class ResponseBuilder
return;
await si.DeferAsync();
if (smc.User.Id != model.User.Id)
if (smc.User.Id != model.User?.Id)
return;
if (smc.Data.CustomId == BUTTON_LEFT)
@@ -134,20 +131,15 @@ public partial class ResponseBuilder
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;
var client = this.client;
client.InteractionCreated += OnInteractionAsync;
_client.InteractionCreated += OnInteractionAsync;
await Task.Delay(30_000);
client.InteractionCreated -= OnInteractionAsync;
_client.InteractionCreated -= OnInteractionAsync;
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
{
private ICommandContext? ctx = null;
private IMessageChannel? channel = null;
private Embed? embed = null;
private string? plainText = null;
private IReadOnlyCollection<EmbedBuilder>? embeds = null;
private IUserMessage? msg = null;
private IUser? user = null;
private ICommandContext? ctx;
private IMessageChannel? channel;
private string? plainText;
private IReadOnlyCollection<EmbedBuilder>? embeds;
private IUserMessage? msg;
private IUser? user;
private bool sanitizeMentions = true;
private LocStr? locTxt;
private object[] locParams = [];
private bool shouldReply = true;
private readonly IBotStrings _bs;
private EmbedBuilder? embedBuilder = null;
private readonly BotConfigService _bcs;
private EmbedBuilder? embedBuilder;
private NadekoInteraction? inter;
private Stream? fileStream = null;
private string? fileName = null;
private Stream? fileStream;
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;
_bcs = bcs;
Client = client;
}
private MessageReference? CreateMessageReference(IMessageChannel targetChannel)
{
if (!shouldReply)
@@ -44,72 +54,116 @@ public sealed partial class ResponseBuilder
failIfNotExists: false);
}
public ResponseMessageModel Build(bool ephemeral = false)
public async Task<ResponseMessageModel> BuildAsync(bool ephemeral)
{
// todo use ephemeral in interactions
var targetChannel = InternalResolveChannel() ?? throw new ArgumentNullException(nameof(channel));
var targetChannel = await InternalResolveChannel() ?? throw new ArgumentNullException(nameof(channel));
var msgReference = CreateMessageReference(targetChannel);
var txt = GetText(locTxt);
// todo check message sanitization
var txt = GetText(locTxt, targetChannel);
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()
{
TargetChannel = targetChannel,
MessageReference = msgReference,
Text = txt,
User = ctx?.User,
Embed = embed ?? embedBuilder?.Build(),
User = user ?? ctx?.User,
Embed = finalEmbed,
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;
}
public Task<IUserMessage> SendAsync(bool ephemeral = false)
public async Task<IUserMessage> SendAsync(bool ephemeral = false)
{
var model = Build(ephemeral);
return SendAsync(model);
var model = await BuildAsync(ephemeral);
var sentMsg = await SendAsync(model);
return sentMsg;
}
public async Task<IUserMessage> SendAsync(ResponseMessageModel model)
{
if (this.fileStream is Stream stream)
return await model.TargetChannel.SendFileAsync(stream,
IUserMessage sentMsg;
if (fileStream is Stream stream)
{
sentMsg = await model.TargetChannel.SendFileAsync(stream,
filename: fileName,
model.Text,
embed: model.Embed,
components: null,
components: inter?.CreateComponent(),
allowedMentions: model.SanitizeMentions,
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(
model.Text,
embed: model.Embed,
embeds: model.Embeds,
components: null,
allowedMentions: model.SanitizeMentions,
messageReference: model.MessageReference);
if (model.Interaction is not null)
{
await model.Interaction.RunAsync(sentMsg);
}
return sentMsg;
}
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)
=> ctx?.Guild?.Id ?? (targetChannel as ITextChannel)?.GuildId;
// todo not good, has to go to the user
private IMessageChannel? InternalResolveChannel()
=> channel ?? ctx?.Channel ?? msg?.Channel;
private string? GetText(LocStr? locStr)
private async Task<IMessageChannel?> InternalResolveChannel()
{
if (user is not null)
{
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);
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);
return _bs.GetText(locStr.Key, guildId, locStr.Params);
}
@@ -125,91 +179,108 @@ public sealed partial class ResponseBuilder
if (text is SmartPlainText spt)
plainText = spt.Text;
else if (text is SmartEmbedText set)
embed = set.GetEmbed().Build();
embedBuilder = set.GetEmbed();
else if (text is SmartEmbedTextArray ser)
embeds = ser.GetEmbedBuilders();
return this;
}
private ResponseBuilder InternalColoredText(string text, EmbedColor color)
{
embed = new EmbedBuilder()
.WithColor(color)
.WithDescription(text)
.Build();
return this;
}
private EmbedBuilder CreateEmbedInternal(
private void InternalCreateEmbed(
string? title,
string? text,
string? url,
string? footer = null)
{
var embed = new EmbedBuilder()
.WithTitle(title)
.WithDescription(text);
if (!string.IsNullOrWhiteSpace(url))
embed = embed.WithUrl(url);
if (!string.IsNullOrWhiteSpace(footer))
embed = embed.WithFooter(footer);
return embed;
}
private EmbedBuilder PaintEmbedInternal(EmbedBuilder eb, EmbedColor color)
=> color switch
{
EmbedColor.Ok => eb.WithOkColor(),
EmbedColor.Pending => eb.WithPendingColor(),
EmbedColor.Error => eb.WithErrorColor(),
};
public ResponseBuilder Error(
string? title,
string? text,
string text,
string? url = null,
string? footer = null)
{
var eb = CreateEmbedInternal(title, text, url, footer);
embed = PaintEmbedInternal(eb, EmbedColor.Error).Build();
return this;
}
var eb = new NadekoEmbedBuilder(_bcs)
.WithDescription(text);
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(
string? title,
string? text,
string text,
string? url = null,
string? footer = null)
{
var eb = CreateEmbedInternal(title, text, url, footer);
embed = PaintEmbedInternal(eb, EmbedColor.Error).Build();
InternalCreateEmbed(title, text, url, footer);
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;
}
public ResponseBuilder Confirm(string text)
=> InternalColoredText(text, EmbedColor.Ok);
{
InternalCreateEmbed(null, text);
color = EmbedColor.Ok;
return this;
}
public ResponseBuilder Confirm(LocStr str)
=> Confirm(GetText(str));
{
embedLocDesc = str;
color = EmbedColor.Ok;
return this;
}
public ResponseBuilder Pending(string text)
=> InternalColoredText(text, EmbedColor.Ok);
{
InternalCreateEmbed(null, text);
color = EmbedColor.Pending;
return this;
}
public ResponseBuilder Pending(LocStr str)
=> Pending(GetText(str));
{
embedLocDesc = str;
color = EmbedColor.Pending;
return this;
}
public ResponseBuilder Error(string text)
=> InternalColoredText(text, EmbedColor.Error);
{
InternalCreateEmbed(null, text);
color = EmbedColor.Error;
return this;
}
public ResponseBuilder Error(LocStr str)
=> Error(GetText(str));
{
embedLocDesc = str;
color = EmbedColor.Error;
return this;
}
public ResponseBuilder UserBasedMentions()
{
@@ -220,17 +291,15 @@ public sealed partial class ResponseBuilder
private IUser? InternalResolveUser()
=> ctx?.User ?? user ?? msg?.Author;
// todo embed colors
public ResponseBuilder Embed(EmbedBuilder eb)
{
embedBuilder = eb;
return this;
}
public ResponseBuilder Channel(IMessageChannel channel)
public ResponseBuilder Channel(IMessageChannel ch)
{
this.channel = channel;
channel = ch;
return this;
}
@@ -240,21 +309,21 @@ public sealed partial class ResponseBuilder
return this;
}
public ResponseBuilder Context(ICommandContext ctx)
public ResponseBuilder Context(ICommandContext context)
{
this.ctx = ctx;
ctx = context;
return this;
}
public ResponseBuilder Message(IUserMessage msg)
public ResponseBuilder Message(IUserMessage message)
{
this.msg = msg;
msg = message;
return this;
}
public ResponseBuilder User(IUser user)
public ResponseBuilder User(IUser usr)
{
this.user = user;
user = usr;
return this;
}
@@ -266,7 +335,6 @@ public sealed partial class ResponseBuilder
public ResponseBuilder Interaction(NadekoInteraction? interaction)
{
// todo implement
inter = interaction;
return this;
}
@@ -277,10 +345,10 @@ public sealed partial class ResponseBuilder
return this;
}
public ResponseBuilder FileName(Stream fileStream, string fileName)
public ResponseBuilder File(Stream stream, string name)
{
this.fileStream = fileStream;
this.fileName = fileName;
fileStream = stream;
fileName = name;
return this;
}
@@ -300,27 +368,51 @@ public class PaginatedResponseBuilder
public SourcedPaginatedResponseBuilder<T> Items<T>(IReadOnlyCollection<T> items)
=> new SourcedPaginatedResponseBuilder<T>(_builder)
.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
{
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 ItemsPerPage { get; private set; } = 9;
public bool AddPaginatedFooter { get; private set; } = true;
public bool IsEphemeral { get; private set; }
public int InitialPage { get; set; }
public SourcedPaginatedResponseBuilder(ResponseBuilder builder)
: base(builder)
{
}
public SourcedPaginatedResponseBuilder<T> Items(IReadOnlyCollection<T> items)
public SourcedPaginatedResponseBuilder<T> Items(IReadOnlyCollection<T> col)
{
this.items = items;
ItemsFunc = (i) => Task.FromResult(this.items.Skip(i * ItemsPerPage).Take(ItemsPerPage));
items = col;
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;
}
@@ -337,24 +429,22 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
return this;
}
// todo use it
public int InitialPage { get; set; }
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;
}
public SourcedPaginatedResponseBuilder<T> Page(Func<IReadOnlyList<T>, int, Task<EmbedBuilder>> pageFunc)
{
this.PageFunc = pageFunc;
PageFunc = pageFunc;
return this;
}
public SourcedPaginatedResponseBuilder<T> AddFooter()
public SourcedPaginatedResponseBuilder<T> AddFooter(bool addFooter = true)
{
AddPaginatedFooter = true;
AddPaginatedFooter = addFooter;
return this;
}
@@ -373,4 +463,10 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
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
{
// todo delete this
public static EmbedBuilder WithColor(this EmbedBuilder eb, EmbedColor color)
=> 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)
=> eb.WithColor(EmbedColor.Ok);
{
if (eb is NadekoEmbedBuilder neb)
return neb.WithOkColor();
return 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 IMessageChannel TargetChannel { get; set; }
public MessageReference MessageReference { get; set; }
public string Text { get; set; }
public Embed Embed { get; set; }
public Embed[] Embeds { get; set; }
public AllowedMentions SanitizeMentions { get; set; }
public IUser User { get; set; }
public required IMessageChannel TargetChannel { get; set; }
public MessageReference? MessageReference { get; set; }
public string? Text { get; set; }
public Embed? Embed { get; set; }
public Embed[]? Embeds { get; set; }
public required AllowedMentions SanitizeMentions { 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 em = new EmbedBuilder()
var em = _sender.CreateEmbed()
.AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
_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);
}

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