1
Fork 0
mirror of https://gitlab.com/Kwoth/nadekobot.git synced 2024-10-02 20:13:13 +00:00

- strings rework continued

- added config/aliases.yml which contains mapping of command method names to command names + aliases
- first alias is treated as a command name, while the following ones are treated as aliases
- command strings' keys aren now using command name as keys (first entry in config/aliases.yml), and not command method name
- command strings are now .yml files in config/strings/commands/commands.LANG.yml (actual localization is not yet implemented, but is trivial to add)
- removed obsolete command strings and aliases for commands which no longer exist and added tests to check for them
- added tests which will make sure commands.yml and aliases.yml files have entries for all commands
- some commands are slightly renamed to be more consistent with other commands in their group/bot
- removed (commented out) leftover yttrackmodule which had no commands
This commit is contained in:
Kwoth 2021-04-26 13:05:43 +02:00
parent eca10671db
commit 188d957057
33 changed files with 3569 additions and 163 deletions

View file

@ -0,0 +1,127 @@
using NUnit.Framework;
using System.Globalization;
using System.Linq;
using System.Reflection;
using AngleSharp.Common;
using Discord.Commands;
using NadekoBot.Common.Attributes;
using NadekoBot.Core.Services;
using NadekoBot.Modules;
using YamlDotNet.Serialization;
namespace Nadeko.Tests
{
public class CommandStringsTests
{
[Test]
public void AllCommandNamesHaveStrings()
{
var stringsSource = new LocalFileStringsSource(
"../../../../src/NadekoBot/config/strings/responses",
"../../../../src/NadekoBot/config/strings/commands");
var strings = new LocalBotStringsProvider(stringsSource);
var culture = new CultureInfo("en-US");
var isSuccess = true;
foreach (var entry in CommandNameLoadHelper.LoadCommandNames("../../../../src/NadekoBot/config/aliases.yml"))
{
var commandName = entry.Value[0];
var cmdStrings = strings.GetCommandStrings(culture.Name, commandName);
if (cmdStrings is null)
{
isSuccess = false;
TestContext.Out.WriteLine($"{commandName} doesn't exist in commands.en-US.yml");
}
}
Assert.IsTrue(isSuccess);
}
private static string[] GetCommandMethodNames()
=> typeof(NadekoBot.NadekoBot).Assembly
.GetExportedTypes()
.Where(type => type.IsClass && !type.IsAbstract)
.Where(type => typeof(NadekoTopLevelModule).IsAssignableFrom(type) // if its a top level module
|| !(type.GetCustomAttribute<GroupAttribute>(true) is null)) // or a submodule
.SelectMany(x => x.GetMethods().Where(mi => mi.CustomAttributes.Any(ca => ca.AttributeType == typeof(NadekoCommandAttribute))))
.Select(x => x.Name.ToLowerInvariant())
.ToArray();
[Test]
public void AllCommandMethodsHaveNames()
{
var allAliases = CommandNameLoadHelper.LoadCommandNames(
"../../../../src/NadekoBot/config/aliases.yml");
var methodNames = GetCommandMethodNames();
var isSuccess = true;
foreach (var methodName in methodNames)
{
if (!allAliases.TryGetValue(methodName, out var _))
{
TestContext.Error.WriteLine($"{methodName} is missing an alias.");
isSuccess = false;
}
}
Assert.IsTrue(isSuccess);
}
[Test]
public void NoObsoleteAliases()
{
var allAliases = CommandNameLoadHelper.LoadCommandNames(
"../../../../src/NadekoBot/config/aliases.yml");
var methodNames = GetCommandMethodNames()
.ToHashSet();
var isSuccess = true;
foreach (var item in allAliases)
{
var methodName = item.Key;
if (!methodNames.Contains(methodName))
{
TestContext.WriteLine($"'{methodName}' from aliases.yml doesn't have a matching command method.");
isSuccess = false;
}
}
Assert.IsTrue(isSuccess);
}
[Test]
public void NoObsoleteCommandStrings()
{
var stringsSource = new LocalFileStringsSource(
"../../../../src/NadekoBot/config/strings/responses",
"../../../../src/NadekoBot/config/strings/commands");
var culture = new CultureInfo("en-US");
var isSuccess = true;
var allCommandNames = CommandNameLoadHelper.LoadCommandNames("../../../../src/NadekoBot/config/aliases.yml");
var enUsCommandNames = allCommandNames
.Select(x => x.Value[0]) // first alias is command name
.ToHashSet();
foreach (var entry in stringsSource.GetCommandStrings()[culture.Name])
{
// key is command name which should be specified in aliases[0] of any method name
var cmdName = entry.Key;
if (!enUsCommandNames.Contains(cmdName))
{
TestContext.Out.WriteLine($"'{cmdName}' It's either obsolete or missing an alias entry.");
isSuccess = false;
}
}
Assert.IsTrue(isSuccess);
}
}
}

View file

@ -7,7 +7,7 @@ using NUnit.Framework;
namespace Nadeko.Tests
{
public class Tests
public class GroupGreetTests
{
private GreetGrouper<int> _grouper;

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using Discord.Commands;
@ -8,7 +10,8 @@ namespace NadekoBot.Common.Attributes
[AttributeUsage(AttributeTargets.Method)]
public sealed class AliasesAttribute : AliasAttribute
{
public AliasesAttribute([CallerMemberName] string memberName = "") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Cmd.Split(' ').Skip(1).ToArray())
public AliasesAttribute([CallerMemberName] string memberName = "")
: base(CommandNameLoadHelper.GetAliasesFor(memberName))
{
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace NadekoBot.Common.Attributes
{
public static class CommandNameLoadHelper
{
private static YamlDotNet.Serialization.IDeserializer _deserializer
= new YamlDotNet.Serialization.Deserializer();
public static Lazy<Dictionary<string, string[]>> LazyCommandAliases
= new Lazy<Dictionary<string, string[]>>(() => LoadCommandNames());
public static Dictionary<string, string[]> LoadCommandNames(string aliasesFilePath = "config/aliases.yml")
{
var text = File.ReadAllText(aliasesFilePath);
return _deserializer.Deserialize<Dictionary<string, string[]>>(text);
}
public static string[] GetAliasesFor(string methodName)
=> LazyCommandAliases.Value.TryGetValue(methodName.ToLowerInvariant(), out var aliases) && aliases.Length > 1
? aliases.Skip(1).ToArray()
: Array.Empty<string>();
public static string GetCommandNameFor(string methodName)
{
methodName = methodName.ToLowerInvariant();
var toReturn = LazyCommandAliases.Value.TryGetValue(methodName, out var aliases) && aliases.Length > 0
? aliases[0]
: methodName;
return toReturn;
}
}
}

View file

@ -8,9 +8,9 @@ namespace NadekoBot.Common.Attributes
[AttributeUsage(AttributeTargets.Method)]
public sealed class DescriptionAttribute : SummaryAttribute
{
public DescriptionAttribute([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Desc)
// Localization.LoadCommand(memberName.ToLowerInvariant()).Desc
public DescriptionAttribute(string text = "") : base(text)
{
}
}
}

View file

@ -8,9 +8,12 @@ namespace NadekoBot.Common.Attributes
[AttributeUsage(AttributeTargets.Method)]
public sealed class NadekoCommandAttribute : CommandAttribute
{
public NadekoCommandAttribute([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Cmd.Split(' ')[0])
public NadekoCommandAttribute([CallerMemberName] string memberName="")
: base(CommandNameLoadHelper.GetCommandNameFor(memberName))
{
this.MethodName = memberName.ToLowerInvariant();
}
public string MethodName { get; }
}
}

View file

@ -9,15 +9,13 @@ namespace NadekoBot.Common.Attributes
[AttributeUsage(AttributeTargets.Method)]
public sealed class UsageAttribute : RemarksAttribute
{
public UsageAttribute([CallerMemberName] string memberName="") : base(UsageAttribute.GetUsage(memberName))
// public static string GetUsage(string memberName)
// {
// var usage = Localization.LoadCommand(memberName.ToLowerInvariant()).Usage;
// return JsonConvert.SerializeObject(usage);
// }
public UsageAttribute(string text = "") : base(text)
{
}
public static string GetUsage(string memberName)
{
var usage = Localization.LoadCommand(memberName.ToLowerInvariant()).Usage;
return JsonConvert.SerializeObject(usage);
}
}
}

View file

@ -167,7 +167,7 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task Tesar()
public async Task Togglexclsar()
{
bool areExclusive = _service.ToggleEsar(ctx.Guild.Id);
if (areExclusive)

View file

@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Administration
if (cmdText.StartsWith(Prefix + "die", StringComparison.InvariantCulture))
return;
var guser = ((IGuildUser)ctx.User);
var guser = (IGuildUser)ctx.User;
var cmd = new StartupCommand()
{
CommandText = cmdText,
@ -85,7 +85,7 @@ namespace NadekoBot.Modules.Administration
if (interval < 5)
return;
var guser = ((IGuildUser)ctx.User);
var guser = (IGuildUser)ctx.User;
var cmd = new StartupCommand()
{
CommandText = cmdText,
@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task StartupCommands(int page = 1)
public async Task StartupCommandsList(int page = 1)
{
if (page-- < 1)
return;

View file

@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WaifuClaimerAffinity([Leftover]IGuildUser u = null)
public async Task Affinity([Leftover]IGuildUser u = null)
{
if (u?.Id == ctx.User.Id)
{
@ -184,7 +184,7 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WaifuLeaderboard(int page = 1)
public async Task WaifuLb(int page = 1)
{
page--;

View file

@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Games
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[NadekoOptions(typeof(AcrophobiaGame.Options))]
public async Task Acro(params string[] args)
public async Task Acrophobia(params string[] args)
{
var (options, _) = OptionsParser.ParseFrom(new AcrophobiaGame.Options(), args);
var channel = (ITextChannel)ctx.Channel;

View file

@ -26,23 +26,23 @@ namespace NadekoBot.Modules.Help
{
public const string PatreonUrl = "https://patreon.com/nadekobot";
public const string PaypalUrl = "https://paypal.me/Kwoth";
private readonly IBotCredentials _creds;
private readonly CommandService _cmds;
private readonly GlobalPermissionService _perms;
private readonly IServiceProvider _services;
private readonly DiscordSocketClient _client;
private readonly IBotStrings _strings;
private readonly AsyncLazy<ulong> _lazyClientId;
public Help(IBotCredentials creds, GlobalPermissionService perms, CommandService cmds,
IServiceProvider services, DiscordSocketClient client)
public Help(GlobalPermissionService perms, CommandService cmds,
IServiceProvider services, DiscordSocketClient client, IBotStrings strings)
{
_creds = creds;
_cmds = cmds;
_perms = perms;
_services = services;
_client = client;
_strings = strings;
_lazyClientId = new AsyncLazy<ulong>(async () => (await _client.GetApplicationInfoAsync()).Id);
}
@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Help
public async Task Modules()
{
var embed = new EmbedBuilder().WithOkColor()
.WithFooter(efb => efb.WithText("" + GetText("modules_footer", Prefix)))
.WithFooter(efb => efb.WithText(" " + GetText("modules_footer", Prefix)))
.WithTitle(GetText("list_of_modules"))
.WithDescription(string.Join("\n",
_cmds.Modules.GroupBy(m => m.GetTopLevelModule())
@ -220,46 +220,6 @@ namespace NadekoBot.Modules.Help
var embed = _service.GetCommandHelp(com, ctx.Guild);
await channel.EmbedAsync(embed).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Hgit()
{
Dictionary<string, List<object>> cmdData = new Dictionary<string, List<object>>();
foreach (var com in _cmds.Commands
.OrderBy(com => com.Module.GetTopLevelModule().Name)
.GroupBy(c => c.Aliases.First())
.Select(g => g.First()))
{
var module = com.Module.GetTopLevelModule();
List<string> optHelpStr = null;
var opt = ((NadekoOptionsAttribute)com.Attributes.FirstOrDefault(x => x is NadekoOptionsAttribute))?.OptionType;
if (opt != null)
{
optHelpStr = HelpService.GetCommandOptionHelpList(opt);
}
var obj = new
{
Aliases = com.Aliases.Select(x => Prefix + x).ToArray(),
Description = string.Format(com.Summary, Prefix),
Usage = JsonConvert.DeserializeObject<string[]>(com.Remarks).Select(x => string.Format(x, Prefix)).ToArray(),
Submodule = com.Module.Name,
Module = com.Module.GetTopLevelModule().Name,
Options = optHelpStr,
Requirements = HelpService.GetCommandRequirements(com),
};
if (cmdData.TryGetValue(module.Name, out var cmds))
cmds.Add(obj);
else
cmdData.Add(module.Name, new List<object>
{
obj
});
}
File.WriteAllText("../../docs/cmds_new.json", JsonConvert.SerializeObject(cmdData, Formatting.Indented));
await ReplyConfirmLocalizedAsync("commandlist_regen").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
@ -288,10 +248,9 @@ namespace NadekoBot.Modules.Help
return new CommandJsonObject
{
Aliases = com.Aliases.Select(x => Prefix + x).ToArray(),
Description = string.Format(com.Summary, Prefix),
Usage = JsonConvert.DeserializeObject<string[]>(com.Remarks)
.Select(x => string.Format(x, Prefix)).ToArray(),
Aliases = com.Aliases.Select(alias => Prefix + alias).ToArray(),
Description = com.RealSummary(_strings, Prefix),
Usage = com.RealRemarksArr(_strings, Prefix),
Submodule = com.Module.Name,
Module = com.Module.GetTopLevelModule().Name,
Options = optHelpStr,

View file

@ -66,7 +66,7 @@ namespace NadekoBot.Modules.Help.Services
str += string.Format(" **/ `{0}`**", prefix + alias);
var em = new EmbedBuilder()
.AddField(fb => fb.WithName(str)
.WithValue($"{com.RealSummary(prefix)}")
.WithValue($"{com.RealSummary(_strings, prefix)}")
.WithIsInline(true));
_dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides);
@ -79,7 +79,8 @@ namespace NadekoBot.Modules.Help.Services
em
.AddField(fb => fb.WithName(GetText("usage", guild))
.WithValue(com.RealRemarks(prefix))
.WithValue(string.Join("\n", Array.ConvertAll(com.RealRemarksArr(_strings, prefix),
arg => Format.Code(arg))))
.WithIsInline(false))
.WithFooter(efb => efb.WithText(GetText("module", guild, com.Module.GetTopLevelModule().Name)))
.WithColor(NadekoBot.OkColor);

View file

@ -706,7 +706,7 @@ namespace NadekoBot.Modules.Music
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ShufflePlaylist()
public async Task PlaylistShuffle()
{
var mp = await _service.GetOrCreatePlayer(Context).ConfigureAwait(false);
var val = mp.ToggleShuffle();
@ -788,7 +788,7 @@ namespace NadekoBot.Modules.Music
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task LocalPl([Leftover] string dirPath)
public async Task LocalPlaylist([Leftover] string dirPath)
{
if (string.IsNullOrWhiteSpace(dirPath))
return;
@ -925,7 +925,7 @@ namespace NadekoBot.Modules.Music
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task RepeatPl()
public async Task PlaylistRepeat()
{
var mp = await _service.GetOrCreatePlayer(Context).ConfigureAwait(false);
var currentValue = mp.ToggleRepeatPlaylist();

View file

@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Permissions
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Lgp()
public async Task GlobalPermList()
{
if (!_service.BlockedModules.Any() && !_service.BlockedCommands.Any())
{
@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Permissions
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Gmod(ModuleOrCrInfo module)
public async Task GlobalModule(ModuleOrCrInfo module)
{
var moduleName = module.Name.ToLowerInvariant();
if (_service.BlockedModules.Add(moduleName))
@ -83,7 +83,7 @@ namespace NadekoBot.Modules.Permissions
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Gcmd(CommandOrCrInfo cmd)
public async Task GlobalCommand(CommandOrCrInfo cmd)
{
var commandName = cmd.Name.ToLowerInvariant();
if (_service.BlockedCommands.Add(commandName))

View file

@ -21,15 +21,15 @@ namespace NadekoBot.Modules.Permissions
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task ResetPermissions()
public async Task ResetPerms()
{
await _service.ResetPermissions(ctx.Guild.Id).ConfigureAwait(false);
await _service.ResetPerms(ctx.Guild.Id).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("perms_reset").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ResetGlobalPermissions()
public async Task ResetGlobalPerms()
{
await _service.ResetGlobalPermissions().ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("global_perms_reset").ConfigureAwait(false);

View file

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions.Services
_db = db;
}
public async Task ResetPermissions(ulong guildId)
public async Task ResetPerms(ulong guildId)
{
using (var uow = _db.GetDbContext())
{

View file

@ -2,8 +2,8 @@
namespace NadekoBot.Core.Modules.Searches.Services
{
public class YtTrackService : INService
{
// public class YtTrackService : INService
// {
// private readonly IGoogleApiService _google;
// private readonly IHttpClientFactory httpClientFactory;
// private readonly DiscordSocketClient _client;
@ -133,5 +133,5 @@ namespace NadekoBot.Core.Modules.Searches.Services
//
// return true;
// }
}
// }
}

View file

@ -6,53 +6,53 @@ namespace NadekoBot.Core.Modules.Searches
{
public partial class Searches
{
[Group]
public class YtTrackCommands : NadekoSubmodule<YtTrackService>
{
// [NadekoCommand, Usage, Description, Aliases]
// [RequireContext(ContextType.Guild)]
// public async Task YtFollow(string ytChannelId, [Leftover] string uploadMessage = null)
// {
// var succ = await _service.ToggleChannelFollowAsync(Context.Guild.Id, Context.Channel.Id, ytChannelId, uploadMessage);
// if(succ)
// {
// await ReplyConfirmLocalizedAsync("yt_follow_added").ConfigureAwait(false);
// }
// else
// {
// await ReplyConfirmLocalizedAsync("yt_follow_fail").ConfigureAwait(false);
// }
// }
//
// [NadekoCommand, Usage, Description, Aliases]
// [RequireContext(ContextType.Guild)]
// public async Task YtTrackRm(int index)
// {
// //var succ = await _service.ToggleChannelTrackingAsync(Context.Guild.Id, Context.Channel.Id, ytChannelId, uploadMessage);
// //if (succ)
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_added").ConfigureAwait(false);
// //}
// //else
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_fail").ConfigureAwait(false);
// //}
// }
//
// [NadekoCommand, Usage, Description, Aliases]
// [RequireContext(ContextType.Guild)]
// public async Task YtTrackList()
// {
// //var succ = await _service.ToggleChannelTrackingAsync(Context.Guild.Id, Context.Channel.Id, ytChannelId, uploadMessage);
// //if (succ)
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_added").ConfigureAwait(false);
// //}
// //else
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_fail").ConfigureAwait(false);
// //}
// }
}
// [Group]
// public class YtTrackCommands : NadekoSubmodule<YtTrackService>
// {
// [NadekoCommand, Usage, Description, Aliases]
// [RequireContext(ContextType.Guild)]
// public async Task YtFollow(string ytChannelId, [Leftover] string uploadMessage = null)
// {
// var succ = await _service.ToggleChannelFollowAsync(Context.Guild.Id, Context.Channel.Id, ytChannelId, uploadMessage);
// if(succ)
// {
// await ReplyConfirmLocalizedAsync("yt_follow_added").ConfigureAwait(false);
// }
// else
// {
// await ReplyConfirmLocalizedAsync("yt_follow_fail").ConfigureAwait(false);
// }
// }
//
// [NadekoCommand, Usage, Description, Aliases]
// [RequireContext(ContextType.Guild)]
// public async Task YtTrackRm(int index)
// {
// //var succ = await _service.ToggleChannelTrackingAsync(Context.Guild.Id, Context.Channel.Id, ytChannelId, uploadMessage);
// //if (succ)
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_added").ConfigureAwait(false);
// //}
// //else
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_fail").ConfigureAwait(false);
// //}
// }
//
// [NadekoCommand, Usage, Description, Aliases]
// [RequireContext(ContextType.Guild)]
// public async Task YtTrackList()
// {
// //var succ = await _service.ToggleChannelTrackingAsync(Context.Guild.Id, Context.Channel.Id, ytChannelId, uploadMessage);
// //if (succ)
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_added").ConfigureAwait(false);
// //}
// //else
// //{
// // await ReplyConfirmLocalizedAsync("yt_track_fail").ConfigureAwait(false);
// //}
// }
// }
}
}

View file

@ -20,18 +20,6 @@ namespace NadekoBot.Modules.Utility
{
_creds = creds;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.DM)]
[OwnerOnly]
public async Task PatreonRewardsReload()
{
if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
return;
await _service.RefreshPledges().ConfigureAwait(false);
await ctx.OkAsync();
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.DM)]

View file

@ -188,7 +188,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task AddQuote(string keyword, [Leftover] string text)
public async Task QuoteAdd(string keyword, [Leftover] string text)
{
if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text))
return;

View file

@ -14,6 +14,7 @@ namespace NadekoBot.Core.Services.Database.Repositories.Impl
public List<CurrencyTransaction> GetPageFor(ulong userId, int page)
{
return _set.AsQueryable()
.AsNoTracking()
.Where(x => x.UserId == userId)
.OrderByDescending(x => x.DateAdded)
.Skip(15 * page)

View file

@ -7,8 +7,10 @@ namespace NadekoBot.Core.Services
/// </summary>
public interface IBotStrings
{
public string GetText(string key, ulong? guildId = null, params object[] data);
public string GetText(string key, CultureInfo locale, params object[] data);
string GetText(string key, ulong? guildId = null, params object[] data);
string GetText(string key, CultureInfo locale, params object[] data);
void Reload();
CommandStrings GetCommandStrings(string commandName, ulong? guildId = null);
CommandStrings GetCommandStrings(string commandName, CultureInfo cultureInfo);
}
}

View file

@ -17,5 +17,12 @@
/// Reloads string cache
/// </summary>
void Reload();
/// <summary>
/// Gets command arg examples and description
/// </summary>
/// <param name="localeName">Language name</param>
/// <param name="commandName">Command name</param>
CommandStrings GetCommandStrings(string localeName, string commandName);
}
}

View file

@ -11,6 +11,8 @@ namespace NadekoBot.Core.Services
/// Gets all response strings
/// </summary>
/// <returns>Dictionary(localename, Dictionary(key, response))</returns>
public Dictionary<string, Dictionary<string, string>> GetResponseStrings();
Dictionary<string, Dictionary<string, string>> GetResponseStrings();
Dictionary<string, Dictionary<string, CommandStrings>> GetCommandStrings();
}
}

View file

@ -1,6 +1,7 @@
using System;
using System.Globalization;
using NLog;
using YamlDotNet.Serialization;
namespace NadekoBot.Core.Services
{
@ -58,13 +59,46 @@ namespace NadekoBot.Core.Services
$" Key '{key}' is not properly formatted. Please report this.";
}
}
public CommandStrings GetCommandStrings(string commandName, ulong? guildId = null)
=> GetCommandStrings(commandName, _localization.GetCultureInfo(guildId));
// public string[] GetUsages(string commandName, ulong guildId)
// => GetUsages(commandName, _localization.GetCultureInfo(guildId));
public CommandStrings GetCommandStrings(string commandName, CultureInfo cultureInfo)
{
var cmdStrings = _stringsProvider.GetCommandStrings(cultureInfo.Name, commandName);
if (cmdStrings is null)
{
if (cultureInfo.Name == _usCultureInfo.Name
|| (cmdStrings = _stringsProvider.GetCommandStrings(_usCultureInfo.Name, commandName)) == null)
{
_log.Warn($"'{commandName}' doesn't exist in 'en-US' command strings. Please report this.");
return new CommandStrings()
{
Args = new[] {""},
Desc = "?"
};
}
// _log.Warn($"'{commandName}' command strings don't exist in {cultureInfo.Name} culture." +
// $"This message is safe to ignore, however you can ask in Nadeko support server how you can" +
// $" contribute command translations");
return cmdStrings;
}
return cmdStrings;
}
public void Reload()
{
_stringsProvider.Reload();
}
}
public class CommandStrings
{
[YamlMember(Alias = "desc")]
public string Desc { get; set; }
[YamlMember(Alias = "args")]
public string[] Args { get; set; }
}
}

View file

@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace NadekoBot.Core.Services
{
@ -8,6 +6,7 @@ namespace NadekoBot.Core.Services
{
private readonly IStringsSource _source;
private IReadOnlyDictionary<string, Dictionary<string, string>> responseStrings;
private IReadOnlyDictionary<string, Dictionary<string, CommandStrings>> commandStrings;
public LocalBotStringsProvider(IStringsSource source)
{
@ -29,6 +28,18 @@ namespace NadekoBot.Core.Services
public void Reload()
{
responseStrings = _source.GetResponseStrings();
commandStrings = _source.GetCommandStrings();
}
public CommandStrings GetCommandStrings(string localeName, string commandName)
{
if (commandStrings.TryGetValue(localeName, out var langStrings)
&& langStrings.TryGetValue(commandName, out var strings))
{
return strings;
}
return null;
}
}
}

View file

@ -1,20 +1,29 @@
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using YamlDotNet.Serialization;
namespace NadekoBot.Core.Services
{
/// <summary>
/// Loads strings from the local default filepath <see cref="StringsPath"/>
/// Loads strings from the local default filepath <see cref="_responsesPath"/>
/// </summary>
public class LocalFileStringsSource : IStringsSource
{
private const string StringsPath = "config/strings/responses";
private readonly string _responsesPath = "config/strings/responses";
private readonly string _commandsPath = "config/strings/commands";
public LocalFileStringsSource(string responsesPath = "config/strings/responses",
string commandsPath = "config/strings/commands")
{
_responsesPath = responsesPath;
_commandsPath = commandsPath;
}
public Dictionary<string, Dictionary<string, string>> GetResponseStrings()
{
var outputDict = new Dictionary<string, Dictionary<string, string>>();
foreach (var file in Directory.GetFiles(StringsPath))
foreach (var file in Directory.GetFiles(_responsesPath))
{
var langDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(file));
var localeName = GetLocaleName(file);
@ -23,9 +32,27 @@ namespace NadekoBot.Core.Services
return outputDict;
}
public Dictionary<string, Dictionary<string, CommandStrings>> GetCommandStrings()
{
var deserializer = new DeserializerBuilder()
.Build();
var outputDict = new Dictionary<string, Dictionary<string, CommandStrings>>();
foreach (var file in Directory.GetFiles(_commandsPath))
{
var text = File.ReadAllText(file);
var langDict = deserializer.Deserialize<Dictionary<string, CommandStrings>>(text);
var localeName = GetLocaleName(file);
outputDict[localeName] = langDict;
}
return outputDict;
}
private static string GetLocaleName(string fileName)
{
fileName = Path.GetFileName(fileName);
var dotIndex = fileName.IndexOf('.') + 1;
var secondDotIndex = fileName.LastIndexOf('.');
return fileName.Substring(dotIndex, secondDotIndex - dotIndex);

View file

@ -1,4 +1,6 @@
using System.Linq;
using System;
using System.Linq;
using System.Web;
using Discord.WebSocket;
using StackExchange.Redis;
@ -28,6 +30,24 @@ namespace NadekoBot.Core.Services
return value;
}
public CommandStrings GetCommandStrings(string localeName, string commandName)
{
string argsStr = _redis.GetDatabase().HashGet($"commands:{localeName}", $"{commandName}::args");
if (argsStr == default)
return null;
var descStr = _redis.GetDatabase().HashGet($"commands:{localeName}", $"{commandName}::desc");
if (descStr == default)
return null;
var args = Array.ConvertAll(argsStr.Split('&'), HttpUtility.UrlDecode);
return new CommandStrings()
{
Args = args,
Desc = descStr
};
}
public void Reload()
{
var redisDb = _redis.GetDatabase();
@ -39,6 +59,18 @@ namespace NadekoBot.Core.Services
redisDb.HashSet($"responses:{localeName}", hashFields);
}
foreach (var (localeName, localeStrings) in _source.GetCommandStrings())
{
var hashFields = localeStrings
.Select(x => new HashEntry($"{x.Key}::args",
string.Join('&', Array.ConvertAll(x.Value.Args, HttpUtility.UrlEncode))))
.Concat(localeStrings
.Select(x => new HashEntry($"{x.Key}::desc", x.Value.Desc)))
.ToArray();
redisDb.HashSet($"commands:{localeName}", hashFields);
}
}
}
}

View file

@ -30,6 +30,8 @@ using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AngleSharp.Attributes;
using NadekoBot.Common.Attributes;
namespace NadekoBot.Extensions
{
@ -160,11 +162,21 @@ namespace NadekoBot.Extensions
public static bool IsAuthor(this IMessage msg, IDiscordClient client)
=> msg.Author?.Id == client.CurrentUser.Id;
public static string RealSummary(this CommandInfo cmd, string prefix)
=> string.Format(cmd.Summary, prefix);
public static string RealRemarks(this CommandInfo cmd, string prefix)
=> string.Join("\n", JsonConvert.DeserializeObject<string[]>(cmd.Remarks)
.Select(x => Format.Code(string.Format(x, prefix))));
public static string RealSummary(this CommandInfo cmd, IBotStrings strings, string prefix)
=> string.Format(strings.GetCommandStrings(cmd.Name).Desc, prefix);
public static string[] RealRemarksArr(this CommandInfo cmd, IBotStrings strings, string prefix)
=> Array.ConvertAll(strings.GetCommandStrings(cmd.MethodName()).Args,
arg => GetFullUsage(cmd.Name, arg, prefix));
public static string MethodName(this CommandInfo cmd)
=> ((NadekoCommandAttribute) cmd.Attributes.FirstOrDefault(x => x is NadekoCommandAttribute))?.MethodName
?? cmd.Name;
// public static string RealRemarks(this CommandInfo cmd, IBotStrings strings, string prefix)
// => string.Join('\n', cmd.RealRemarksArr(strings, prefix));
public static string GetFullUsage(string commandName, string args, string prefix)
=> $"{prefix}{commandName} {args}";
public static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage)
{

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff