Compare commits

...

8 Commits
5.0.3 ... 5.0.5

Author SHA1 Message Date
Kwoth
23c8dc00e8 dev: Upped version to 5.0.5 2024-05-11 08:02:59 +00:00
Kwoth
0da8190637 fix: repeat, greet, bye, boost messages can now once again mention anyone
fix: .say #channel fixed
2024-05-11 08:00:48 +00:00
Kwoth
d766295286 docs: Updated supported distros (thx cata) 2024-05-11 05:57:31 +00:00
Kwoth
21e3c64e01 Merge branch 'v5' of https://gitlab.com/kwoth/nadekobot into v5 2024-05-11 02:30:40 +00:00
Kwoth
9ddcd6d89e docs: Updated changelog 2024-05-11 02:05:56 +00:00
Kwoth
a154d5881c docs: Updated changelog 2024-05-10 15:36:12 +00:00
Kwoth
fb594e50fd fix: .h fixed, .xp fixed, pagination in .lb fixed 2024-05-10 15:29:04 +00:00
Kwoth
75c5a003bf fix: cleverbot:response should now be a valid module name 2024-05-10 09:53:20 +00:00
21 changed files with 214 additions and 167 deletions

View File

@@ -2,7 +2,15 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [5.0.3] - 10.05.2024
## [5.0.5] - 11.05.2024
### Fixed
- `%server.members%` placeholder fixed
- `.say #channel <message>` should now be working properly again
- `.repeat`, `.greet`, `.bye` and `.boost` command can now once again mention anyone
## [5.0.4] - 10.05.2024
### Added

View File

@@ -15,11 +15,13 @@
##### Compatible operating systems:
- Ubuntu: 20.04, 22.04, 22.10 +
- Debian: 11 +
- CentOS: 7
- openSUSE 15
- Fedora: 33, 34, 35
- Ubuntu: 16.04, 18.04, 20.04, 21.04, 21.10, 22.04
- Mint: 19, 20
- Debian: 10, 11, 12
- RockyLinux: 8, 9
- AlmaLinux: 8, 9
- openSUSE Leap: 15.5, 15.6 & Tumbleweed
- Fedora: 38, 39, 40, 41, 42
## Linux From Source

View File

@@ -93,8 +93,12 @@ Open PowerShell as described above and run the following commands:
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
2. Navigate to your bot's folder, example:
- `cd ~/Desktop/nadekobot`
3. Pull the new version
- `git pull`
3. Pull the new version, and make sure you're on the v5 branch
- *⚠️ the first 3 lines can be omitted if you're already on v5. If you're updating from v4, you must run them*
- `git remote set-branches origin '*'`
- `git fetch -v --depth=1`
- `git checkout v5`
- `git pull`
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
4. **Backup** old output in case your data is overwritten
- `cp -r -fo output/ output-old`

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 _sender.Response(channel).Text(newContent).SendAsync();
var toDelete = await _sender.Response(channel).Text(newContent).Sanitize(false).SendAsync();
if (conf.BoostMessageDeleteAfter > 0)
toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
@@ -217,7 +217,7 @@ public class GreetService : INService, IReadyExecutor
text = await _repSvc.ReplaceAsync(text, repCtx);
try
{
var toDelete = await _sender.Response(channel).Text(text).SendAsync();
var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
if (conf.AutoDeleteByeMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
@@ -258,7 +258,7 @@ public class GreetService : INService, IReadyExecutor
text = await _repSvc.ReplaceAsync(text, repCtx);
try
{
var toDelete = await _sender.Response(channel).Text(text).SendAsync();
var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
if (conf.AutoDeleteGreetMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
@@ -360,7 +360,7 @@ public class GreetService : INService, IReadyExecutor
}
}
await _sender.Response(user).Text(smartText).SendAsync();
await _sender.Response(user).Text(smartText).Sanitize(false).SendAsync();
}
catch
{
@@ -573,8 +573,6 @@ public class GreetService : INService, IReadyExecutor
public bool SetBoostMessage(ulong guildId, ref string message)
{
message = message.SanitizeMentions();
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
conf.BoostMessage = message;

View File

@@ -175,7 +175,7 @@ public sealed class SomethingOnlyChannelService : IExecOnMessage
// ignore owner and admin
if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator)
{
Log.Information("{Type}-Only Channel: Ignoring owner od admin ({ChannelId})", type, msg.Channel.Id);
Log.Information("{Type}-Only Channel: Ignoring owner or admin ({ChannelId})", type, msg.Channel.Id);
return false;
}

View File

@@ -776,7 +776,7 @@ public partial class Gambling : GamblingModule<GamblingService>
await using var uow = _db.GetDbContext();
var cleanRichest = await uow.Set<DiscordUser>()
.GetTopRichest(_client.CurrentUser.Id, 0, 10_000);
.GetTopRichest(_client.CurrentUser.Id, 0, 1000);
var sg = (SocketGuild)ctx.Guild!;
return cleanRichest.Where(x => sg.GetUser(x.UserId) is not null).ToList();
@@ -787,10 +787,14 @@ public partial class Gambling : GamblingModule<GamblingService>
return await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, curPage);
}
}
var res = Response()
.Paginated();
await Response()
.Paginated()
.PageItems(GetTopRichest)
.TotalElements(900)
.PageSize(9)
.CurrentPage(page)
.Page((toSend, curPage) =>

View File

@@ -67,7 +67,7 @@ public class ChatterBotService : IExecOnMessage
if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey))
return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory);
Log.Information("Cleverbot will not work as the api key is missing.");
Log.Information("Cleverbot will not work as the api key is missing");
return null;
case ChatBotImplementation.Gpt3:
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
@@ -80,7 +80,7 @@ public class ChatterBotService : IExecOnMessage
_client.CurrentUser.Username,
_httpFactory);
Log.Information("Gpt3 will not work as the api key is missing.");
Log.Information("Gpt3 will not work as the api key is missing");
return null;
default:
return null;
@@ -128,7 +128,7 @@ public class ChatterBotService : IExecOnMessage
var res = await _perms.CheckPermsAsync(sg,
usrMsg.Channel,
usrMsg.Author,
"games",
CleverBotResponseStr.CLEVERBOT_RESPONSE,
CleverBotResponseStr.CLEVERBOT_RESPONSE);
if (!res.IsAllowed)

View File

@@ -10,7 +10,7 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
namespace NadekoBot.Modules.Help;
public sealed class Help : NadekoModule<HelpService>
public sealed partial class Help : NadekoModule<HelpService>
{
public const string PATREON_URL = "https://patreon.com/nadekobot";
public const string PAYPAL_URL = "https://paypal.me/Kwoth";
@@ -72,7 +72,7 @@ public sealed class Help : NadekoModule<HelpService>
return;
var topLevelModules = new List<ModuleInfo>();
foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).Select(x => x.Key))
foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).OrderBy(x => x.Key.Name).Select(x => x.Key))
{
var result = await _perms.CheckPermsAsync(ctx.Guild,
ctx.Channel,
@@ -80,6 +80,11 @@ public sealed class Help : NadekoModule<HelpService>
m.Name,
null);
#if GLOBAL_NADEKO
if (m.Preconditions.Any(x => x is NoPublicBotAttribute))
continue;
#endif
if (result.IsAllowed)
topLevelModules.Add(m);
}
@@ -87,6 +92,7 @@ public sealed class Help : NadekoModule<HelpService>
await Response()
.Paginated()
.Items(topLevelModules)
.PageSize(12)
.CurrentPage(page)
.AddFooter(false)
.Page((items, _) =>
@@ -99,13 +105,13 @@ public sealed class Help : NadekoModule<HelpService>
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));
items
.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;
})
@@ -310,7 +316,7 @@ public sealed class Help : NadekoModule<HelpService>
{
string cmdName;
if (cmd.Aliases.Count > 1)
cmdName = Format.Code(prefix +cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]);
cmdName = Format.Code(prefix + cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]);
else
cmdName = Format.Code(prefix + cmd.Aliases.First());
@@ -354,17 +360,16 @@ public sealed class Help : NadekoModule<HelpService>
public async Task H([Leftover] CommandInfo com = null)
{
var channel = ctx.Channel;
if (com is null)
{
var ch = channel is ITextChannel ? await ctx.User.CreateDMChannelAsync() : channel;
try
{
var ch = channel is ITextChannel ? await ctx.User.CreateDMChannelAsync() : channel;
var data = await GetHelpString();
if (data == default)
return;
await Response().Text(data).SendAsync();
await Response().Channel(ch).Text(data).SendAsync();
try
{
await ctx.OkAsync();

View File

@@ -3,6 +3,7 @@
namespace NadekoBot.Modules;
[OwnerOnly]
[NoPublicBot]
public partial class Medusa : NadekoModule<IMedusaLoaderService>
{
private readonly IMedusaeRepositoryService _repo;

View File

@@ -1,149 +1,156 @@
namespace NadekoBot.Modules.Patronage;
using NadekoBot.Modules.Patronage;
[OnlyPublicBot]
public partial class Patronage : NadekoModule
namespace NadekoBot.Modules.Help;
public partial class Help
{
private readonly PatronageService _service;
private readonly PatronageConfig _pConf;
public Patronage(PatronageService service, PatronageConfig pConf)
[OnlyPublicBot]
public partial class Patronage : NadekoModule
{
_service = service;
_pConf = pConf;
}
private readonly PatronageService _service;
private readonly PatronageConfig _pConf;
[Cmd]
[Priority(2)]
public Task Patron()
=> InternalPatron(ctx.User);
[Cmd]
[Priority(0)]
[OwnerOnly]
public Task Patron(IUser user)
=> InternalPatron(user);
[Cmd]
[Priority(0)]
[OwnerOnly]
public async Task PatronMessage(PatronTier tierAndHigher, string message)
{
_ = ctx.Channel.TriggerTypingAsync();
var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message);
await Response()
.Confirm(strs.patron_msg_sent(
Format.Code(tierAndHigher.ToString()),
Format.Bold(result.Success.ToString()),
Format.Bold(result.Failed.ToString())))
.SendAsync();
}
// [OwnerOnly]
// public async Task PatronGift(IUser user, int amount)
// {
// // i can't figure out a good way to gift more than one month at the moment.
//
// if (amount < 1)
// return;
//
// var patron = _service.GiftPatronAsync(user, amount);
//
// 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)
// .AddField("Amount", $"**{patron.Amount / 100.0f:N1}$**", true)
// .AddField("Until", TimestampTag.FromDateTime(patron.ValidThru.AddDays(1)))).SendAsync();
//
//
// }
private async Task InternalPatron(IUser user)
{
if (!_pConf.Data.IsEnabled)
public Patronage(PatronageService service, PatronageConfig pConf)
{
await Response().Error(strs.patron_not_enabled).SendAsync();
return;
_service = service;
_pConf = pConf;
}
var patron = await _service.GetPatronAsync(user.Id);
var quotaStats = await _service.GetUserQuotaStatistic(user.Id);
[Cmd]
[Priority(2)]
public Task Patron()
=> InternalPatron(ctx.User);
var eb = _sender.CreateEmbed()
.WithAuthor(user)
.WithTitle(GetText(strs.patron_info))
.WithOkColor();
[Cmd]
[Priority(0)]
[OwnerOnly]
public Task Patron(IUser user)
=> InternalPatron(user);
if (quotaStats.Commands.Count == 0
&& quotaStats.Groups.Count == 0
&& quotaStats.Modules.Count == 0)
[Cmd]
[Priority(0)]
[OwnerOnly]
public async Task PatronMessage(PatronTier tierAndHigher, string message)
{
eb.WithDescription(GetText(strs.no_quota_found));
_ = ctx.Channel.TriggerTypingAsync();
var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message);
await Response()
.Confirm(strs.patron_msg_sent(
Format.Code(tierAndHigher.ToString()),
Format.Bold(result.Success.ToString()),
Format.Bold(result.Failed.ToString())))
.SendAsync();
}
else
// [OwnerOnly]
// public async Task PatronGift(IUser user, int amount)
// {
// // i can't figure out a good way to gift more than one month at the moment.
//
// if (amount < 1)
// return;
//
// var patron = _service.GiftPatronAsync(user, amount);
//
// 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)
// .AddField("Amount", $"**{patron.Amount / 100.0f:N1}$**", true)
// .AddField("Until", TimestampTag.FromDateTime(patron.ValidThru.AddDays(1)))).SendAsync();
//
//
// }
private async Task InternalPatron(IUser user)
{
eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true)
.AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true);
if (patron.Tier != PatronTier.None)
eb.AddField(GetText(strs.expires), patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(), true);
eb.AddField(GetText(strs.quotas), "", false);
if (quotaStats.Commands.Count > 0)
if (!_pConf.Data.IsEnabled)
{
var text = GetQuotaList(quotaStats.Commands);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.commands), text, true);
await Response().Error(strs.patron_not_enabled).SendAsync();
return;
}
if (quotaStats.Groups.Count > 0)
var patron = await _service.GetPatronAsync(user.Id);
var quotaStats = await _service.GetUserQuotaStatistic(user.Id);
var eb = _sender.CreateEmbed()
.WithAuthor(user)
.WithTitle(GetText(strs.patron_info))
.WithOkColor();
if (quotaStats.Commands.Count == 0
&& quotaStats.Groups.Count == 0
&& quotaStats.Modules.Count == 0)
{
var text = GetQuotaList(quotaStats.Groups);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.groups), text, true);
eb.WithDescription(GetText(strs.no_quota_found));
}
else
{
eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true)
.AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true);
if (patron.Tier != PatronTier.None)
eb.AddField(GetText(strs.expires),
patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(),
true);
eb.AddField(GetText(strs.quotas), "", false);
if (quotaStats.Commands.Count > 0)
{
var text = GetQuotaList(quotaStats.Commands);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.commands), text, true);
}
if (quotaStats.Groups.Count > 0)
{
var text = GetQuotaList(quotaStats.Groups);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.groups), text, true);
}
if (quotaStats.Modules.Count > 0)
{
var text = GetQuotaList(quotaStats.Modules);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.modules), text, true);
}
}
if (quotaStats.Modules.Count > 0)
try
{
var text = GetQuotaList(quotaStats.Modules);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.modules), text, true);
await Response().User(ctx.User).Embed(eb).SendAsync();
_ = ctx.OkAsync();
}
catch
{
await Response().Error(strs.cant_dm).SendAsync();
}
}
private string GetQuotaList(IReadOnlyDictionary<string, FeatureQuotaStats> featureQuotaStats)
{
var text = string.Empty;
foreach (var (key, q) in featureQuotaStats)
{
text += $"\n\t`{key}`\n";
if (q.Hourly != default)
text += $" {GetEmoji(q.Hourly)} {q.Hourly.Cur}/{q.Hourly.Max} per hour\n";
if (q.Daily != default)
text += $" {GetEmoji(q.Daily)} {q.Daily.Cur}/{q.Daily.Max} per day\n";
if (q.Monthly != default)
text += $" {GetEmoji(q.Monthly)} {q.Monthly.Cur}/{q.Monthly.Max} per month\n";
}
try
{
await Response().User(ctx.User).Embed(eb).SendAsync();
_ = ctx.OkAsync();
}
catch
{
await Response().Error(strs.cant_dm).SendAsync();
return text;
}
private string GetEmoji((uint Cur, uint Max) limit)
=> limit.Cur < limit.Max
? "✅"
: "⚠️";
}
private string GetQuotaList(IReadOnlyDictionary<string, FeatureQuotaStats> featureQuotaStats)
{
var text = string.Empty;
foreach (var (key, q) in featureQuotaStats)
{
text += $"\n\t`{key}`\n";
if (q.Hourly != default)
text += $" {GetEmoji(q.Hourly)} {q.Hourly.Cur}/{q.Hourly.Max} per hour\n";
if (q.Daily != default)
text += $" {GetEmoji(q.Daily)} {q.Daily.Cur}/{q.Daily.Max} per day\n";
if (q.Monthly != default)
text += $" {GetEmoji(q.Monthly)} {q.Monthly.Cur}/{q.Monthly.Max} per month\n";
}
return text;
}
private string GetEmoji((uint Cur, uint Max) limit)
=> limit.Cur < limit.Max
? "✅"
: "⚠️";
}

View File

@@ -269,7 +269,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
var text = SmartText.CreateFrom(repeater.Message);
text = await _repSvc.ReplaceAsync(text, repCtx);
var newMsg = await _sender.Response(channel).Text(text).SendAsync();
var newMsg = await _sender.Response(channel)
.Text(text)
.Sanitize(false)
.SendAsync();
_ = newMsg.AddReactionAsync(new Emoji("🔄"));
if (_noRedundant.Contains(repeater.Id))

View File

@@ -85,6 +85,7 @@ public partial class Utility : NadekoModule
await Response()
.Text(message)
.Channel(channel)
.UserBasedMentions()
.SendAsync();
}

View File

@@ -1194,7 +1194,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
}
//avatar
if (stats.User.AvatarId is not null && template.User.Icon.Show)
if (template.User.Icon.Show)
{
try
{

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>5.0.3</Version>
<Version>5.0.5</Version>
<!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>

View File

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
namespace NadekoBot.Common;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
[SuppressMessage("Style", "IDE0022:Use expression body for methods")]
public sealed class NoPublicBotAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(

View File

@@ -40,7 +40,7 @@ public sealed partial class ReplacementPatternStore
Register("%server.id%", static (IGuild g) => g.Id.ToString());
Register("%server.name%", static (IGuild g) => g.Name);
Register("%server.icon%", static (IGuild g) => g.IconUrl);
Register("%server.members%", static (IGuild g) => g.ApproximateMemberCount?.ToString() ?? "?");
Register("%server.members%", static async (IGuild g) => (g as SocketGuild)?.MemberCount.ToString() ?? "?");
Register("%server.boosters%", static (IGuild g) => g.PremiumSubscriptionCount.ToString());
Register("%server.boost_level%", static (IGuild g) => ((int)g.PremiumTier).ToString());
}

View File

@@ -25,7 +25,7 @@ public partial class ResponseBuilder
public async Task SendAsync(bool ephemeral = false)
{
var lastPage = (_paginationBuilder.TotalElements - 1)
var lastPage = (_paginationBuilder.Elems - 1)
/ _paginationBuilder.ItemsPerPage;
var items = (await _paginationBuilder.ItemsFunc(currentPage)).ToArray();

View File

@@ -357,9 +357,10 @@ public sealed partial class ResponseBuilder
fileName = name;
return this;
}
public PaginatedResponseBuilder Paginated()
=> new(this);
}
public class PaginatedResponseBuilder
@@ -396,7 +397,7 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
public Func<int, Task<SimpleInteractionBase>>? InteractionFunc { get; private set; }
public int TotalElements { get; private set; } = 1;
public int Elems { get; private set; } = 1;
public int ItemsPerPage { get; private set; } = 9;
public bool AddPaginatedFooter { get; private set; } = true;
public bool IsEphemeral { get; private set; }
@@ -411,10 +412,16 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
public SourcedPaginatedResponseBuilder<T> Items(IReadOnlyCollection<T> col)
{
items = col;
TotalElements = col.Count;
Elems = col.Count;
ItemsFunc = (i) => Task.FromResult(items.Skip(i * ItemsPerPage).Take(ItemsPerPage));
return this;
}
public SourcedPaginatedResponseBuilder<T> TotalElements(int i)
{
Elems = i;
return this;
}
public SourcedPaginatedResponseBuilder<T> PageItems(Func<int, Task<IEnumerable<T>>> func)
{

View File

@@ -113,7 +113,7 @@ public static class ServiceCollectionExtensions
typeof(IInputTransformer),
typeof(INService)
];
foreach (var svc in a.GetTypes()
.Where(type => type.IsClass && types.Any(t => type.IsAssignableTo(t)) && !type.HasAttribute<DIIgnoreAttribute>()
#if GLOBAL_NADEKO

View File

@@ -1,4 +1,6 @@
#nullable disable
using NadekoBot.Modules.Permissions;
namespace NadekoBot.Common.TypeReaders;
public sealed class ModuleTypeReader : NadekoTypeReader<ModuleInfo>
@@ -34,7 +36,7 @@ public sealed class ModuleOrExprTypeReader : NadekoTypeReader<ModuleOrExpr>
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule())
.FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)
?.Key;
if (module is null && input != "ACTUALEXPRESSIONS")
if (module is null && input != "ACTUALEXPRESSIONS" && input != CleverBotResponseStr.CLEVERBOT_RESPONSE)
return new(TypeReaderResult.FromError<ModuleOrExpr>(CommandError.ParseFailed, "No such module found."));
return new(TypeReaderResult.FromSuccess(new ModuleOrExpr

View File

@@ -10,7 +10,12 @@ public static class UserExtensions
// This method is only used for the xp card
public static Uri? RealAvatarUrl(this DiscordUser usr)
=> Uri.TryCreate(CDN.GetDefaultUserAvatarUrl(usr.UserId), UriKind.Absolute, out var uri)
{
if (!string.IsNullOrWhiteSpace(usr.AvatarId))
return new Uri(CDN.GetUserAvatarUrl(usr.UserId, usr.AvatarId, 128, ImageFormat.Png));
return Uri.TryCreate(CDN.GetDefaultUserAvatarUrl(usr.UserId), UriKind.Absolute, out var uri)
? uri
: null;
}
}