.ve (verbose errors) command added. Server-based toggle.

This commit is contained in:
Master Kwoth 2017-06-12 14:26:14 +02:00
parent 21a72c182e
commit 2ff55a49fb
14 changed files with 1786 additions and 40 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
#Manually added files #Manually added files
command_errors*.txt
src/NadekoBot/Command Errors*.txt src/NadekoBot/Command Errors*.txt
src/NadekoBot/credentials.json src/NadekoBot/credentials.json

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class verboseerrors : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "VerboseErrors",
table: "GuildConfigs",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VerboseErrors",
table: "GuildConfigs");
}
}
}

View File

@ -2,7 +2,9 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Migrations namespace NadekoBot.Migrations
{ {
@ -582,6 +584,8 @@ namespace NadekoBot.Migrations
b.Property<string>("TimeZoneId"); b.Property<string>("TimeZoneId");
b.Property<bool>("VerboseErrors");
b.Property<bool>("VerbosePermissions"); b.Property<bool>("VerbosePermissions");
b.Property<bool>("VoicePlusTextEnabled"); b.Property<bool>("VoicePlusTextEnabled");

View File

@ -11,6 +11,7 @@ using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Permissions; using NadekoBot.Services.Permissions;
using NadekoBot.Services.Help;
namespace NadekoBot.Modules.Help namespace NadekoBot.Modules.Help
{ {
@ -22,16 +23,18 @@ namespace NadekoBot.Modules.Help
private readonly BotConfig _config; private readonly BotConfig _config;
private readonly CommandService _cmds; private readonly CommandService _cmds;
private readonly GlobalPermissionService _perms; private readonly GlobalPermissionService _perms;
private readonly HelpService _h;
public string HelpString => String.Format(_config.HelpString, _creds.ClientId, Prefix); public string HelpString => String.Format(_config.HelpString, _creds.ClientId, Prefix);
public string DMHelpString => _config.DMHelpString; public string DMHelpString => _config.DMHelpString;
public Help(IBotCredentials creds, GlobalPermissionService perms, BotConfig config, CommandService cmds) public Help(IBotCredentials creds, GlobalPermissionService perms, BotConfig config, CommandService cmds, HelpService h)
{ {
_creds = creds; _creds = creds;
_config = config; _config = config;
_cmds = cmds; _cmds = cmds;
_perms = perms; _perms = perms;
_h = h;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -91,37 +94,16 @@ namespace NadekoBot.Modules.Help
return; return;
} }
if (com == null) //if (com == null)
{ //{
await ReplyErrorLocalized("command_not_found").ConfigureAwait(false); // await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
return; // return;
} //}
var str = string.Format("**`{0}`**", Prefix + com.Aliases.First());
var alias = com.Aliases.Skip(1).FirstOrDefault(); var embed = _h.GetCommandHelp(com, Context.Guild);
if (alias != null)
str += string.Format(" **/ `{0}`**", Prefix + alias);
var embed = new EmbedBuilder()
.AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary(Prefix)} {GetCommandRequirements(com)}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("usage")).WithValue(com.RealRemarks(Prefix)).WithIsInline(false))
.WithColor(NadekoBot.OkColor);
await channel.EmbedAsync(embed).ConfigureAwait(false); await channel.EmbedAsync(embed).ConfigureAwait(false);
} }
private string GetCommandRequirements(CommandInfo cmd) =>
string.Join(" ", cmd.Preconditions
.Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
.Select(ca =>
{
if (ca is OwnerOnlyAttribute)
return Format.Bold(GetText("bot_owner_only"));
var cau = (RequireUserPermissionAttribute)ca;
if (cau.GuildPermission != null)
return Format.Bold(GetText("server_permission", cau.GuildPermission))
.Replace("Guild", "Server");
return Format.Bold(GetText("channel_permission", cau.ChannelPermission))
.Replace("Guild", "Server");
}));
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
@ -155,7 +137,7 @@ namespace NadekoBot.Modules.Help
lastModule = module.Name; lastModule = module.Name;
} }
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" + helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
$" {string.Format(com.Summary, Prefix)} {GetCommandRequirements(com)} |" + $" {string.Format(com.Summary, Prefix)} {_h.GetCommandRequirements(com, Context.Guild)} |" +
$" {string.Format(com.Remarks, Prefix)}"); $" {string.Format(com.Remarks, Prefix)}");
} }
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString()); File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());

View File

@ -0,0 +1,34 @@
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Services.Utility;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Utility
{
public partial class Utility
{
[Group]
public class VerboseCommandErrors : NadekoSubmodule
{
private readonly VerboseErrorsService _ves;
public VerboseCommandErrors(VerboseErrorsService ves)
{
_ves = ves;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(Discord.GuildPermission.ManageMessages)]
public async Task VerboseError()
{
var state = _ves.ToggleVerboseErrors(Context.Guild.Id);
if (state)
await ReplyConfirmLocalized("verbose_errors_enabled").ConfigureAwait(false);
else
await ReplyConfirmLocalized("verbose_errors_disabled").ConfigureAwait(false);
}
}
}
}

View File

@ -111,6 +111,10 @@ namespace NadekoBot
{ {
var soundcloudApiService = new SoundCloudApiService(Credentials); var soundcloudApiService = new SoundCloudApiService(Credentials);
#region help
var helpService = new HelpService(BotConfig, CommandHandler, Strings);
#endregion
//module services //module services
//todo 90 - autodiscover, DI, and add instead of manual like this //todo 90 - autodiscover, DI, and add instead of manual like this
#region utility #region utility
@ -120,6 +124,7 @@ namespace NadekoBot
var converterService = new ConverterService(Db); var converterService = new ConverterService(Db);
var commandMapService = new CommandMapService(AllGuildConfigs); var commandMapService = new CommandMapService(AllGuildConfigs);
var patreonRewardsService = new PatreonRewardsService(Credentials, Db, Currency); var patreonRewardsService = new PatreonRewardsService(Credentials, Db, Currency);
var verboseErrorsService = new VerboseErrorsService(AllGuildConfigs, Db, CommandHandler, helpService);
#endregion #endregion
#region permissions #region permissions
@ -139,7 +144,6 @@ namespace NadekoBot
var clashService = new ClashOfClansService(Client, Db, Localization, Strings); var clashService = new ClashOfClansService(Client, Db, Localization, Strings);
var musicService = new MusicService(GoogleApi, Strings, Localization, Db, soundcloudApiService, Credentials, AllGuildConfigs); var musicService = new MusicService(GoogleApi, Strings, Localization, Db, soundcloudApiService, Credentials, AllGuildConfigs);
var crService = new CustomReactionsService(permissionsService, Db, Client, CommandHandler); var crService = new CustomReactionsService(permissionsService, Db, Client, CommandHandler);
var helpService = new HelpService(BotConfig);
#region Games #region Games
var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, Strings, Images, CommandHandler); var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, Strings, Images, CommandHandler);
@ -188,6 +192,7 @@ namespace NadekoBot
.Add(remindService) .Add(remindService)
.Add(repeaterService) .Add(repeaterService)
.Add(converterService) .Add(converterService)
.Add(verboseErrorsService)
.Add<SearchesService>(searchesService) .Add<SearchesService>(searchesService)
.Add(streamNotificationService) .Add(streamNotificationService)
.Add(animeSearchService) .Add(animeSearchService)

View File

@ -3501,4 +3501,13 @@
<data name="defprefix_desc" xml:space="preserve"> <data name="defprefix_desc" xml:space="preserve">
<value>Sets bot's default prefix for all bot commands. Provide no arguments to see the current default prefix. This will not change this server's current prefix.</value> <value>Sets bot's default prefix for all bot commands. Provide no arguments to see the current default prefix. This will not change this server's current prefix.</value>
</data> </data>
<data name="verboseerror_cmd" xml:space="preserve">
<value>verboseerror ve</value>
</data>
<data name="verboseerror_usage" xml:space="preserve">
<value>`{0}ve`</value>
</data>
<data name="verboseerror_desc" xml:space="preserve">
<value>Toggles whether the bot should print command errors when a command is incorrectly used.</value>
</data>
</root> </root>

View File

@ -40,6 +40,7 @@ namespace NadekoBot.Services
private ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels { get; set; } = new ImmutableArray<AsyncLazy<IDMChannel>>(); private ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels { get; set; } = new ImmutableArray<AsyncLazy<IDMChannel>>();
public event Func<IUserMessage, CommandInfo, Task> CommandExecuted = delegate { return Task.CompletedTask; }; public event Func<IUserMessage, CommandInfo, Task> CommandExecuted = delegate { return Task.CompletedTask; };
public event Func<CommandInfo, ITextChannel, string, Task> CommandErrored = delegate { return Task.CompletedTask; };
//userid/msg count //userid/msg count
public ConcurrentDictionary<ulong, uint> UserMessagesSent { get; } = new ConcurrentDictionary<ulong, uint>(); public ConcurrentDictionary<ulong, uint> UserMessagesSent { get; } = new ConcurrentDictionary<ulong, uint>();
@ -276,6 +277,8 @@ namespace NadekoBot.Services
//todo 80 should have log levels and it should return some kind of result, //todo 80 should have log levels and it should return some kind of result,
// instead of tuple with the type of thing that went wrong, like before // instead of tuple with the type of thing that went wrong, like before
LogErroredExecution(result.Error, usrMsg, channel as ITextChannel, exec2, exec3, execTime); LogErroredExecution(result.Error, usrMsg, channel as ITextChannel, exec2, exec3, execTime);
if (guild != null)
await CommandErrored(result.Info, channel as ITextChannel, result.Error);
} }
} }
@ -306,10 +309,7 @@ namespace NadekoBot.Services
var preconditionResult = await commands[i].CheckPreconditionsAsync(context, serviceProvider).ConfigureAwait(false); var preconditionResult = await commands[i].CheckPreconditionsAsync(context, serviceProvider).ConfigureAwait(false);
if (!preconditionResult.IsSuccess) if (!preconditionResult.IsSuccess)
{ {
if (commands.Count == 1) return (false, preconditionResult.ErrorReason, commands[i].Command);
return (false, null, null);
else
continue;
} }
var parseResult = await commands[i].ParseAsync(context, searchResult, preconditionResult).ConfigureAwait(false); var parseResult = await commands[i].ParseAsync(context, searchResult, preconditionResult).ConfigureAwait(false);
@ -331,7 +331,7 @@ namespace NadekoBot.Services
if (!parseResult.IsSuccess) if (!parseResult.IsSuccess)
{ {
if (commands.Count == 1) if (commands.Count == 1)
return (false, null, null); return (false, parseResult.ErrorReason, commands[i].Command);
else else
continue; continue;
} }
@ -342,7 +342,7 @@ namespace NadekoBot.Services
// Bot will ignore commands which are ran more often than what specified by // Bot will ignore commands which are ran more often than what specified by
// GlobalCommandsCooldown constant (miliseconds) // GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) if (!UsersOnShortCooldown.Add(context.Message.Author.Id))
return (false, null, null); return (false, null, commands[i].Command);
//return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.");
var commandName = cmd.Aliases.First(); var commandName = cmd.Aliases.First();
@ -352,7 +352,7 @@ namespace NadekoBot.Services
await exec.TryBlockLate(_client, context.Message, context.Guild, context.Channel, context.User, cmd.Module.GetTopLevelModule().Name, commandName).ConfigureAwait(false)) await exec.TryBlockLate(_client, context.Message, context.Guild, context.Channel, context.User, cmd.Module.GetTopLevelModule().Name, commandName).ConfigureAwait(false))
{ {
_log.Info("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, svc.GetType().Name); _log.Info("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, svc.GetType().Name);
return (false, null, null); return (false, null, commands[i].Command);
} }
} }

View File

@ -81,6 +81,7 @@ namespace NadekoBot.Services.Database.Models
public List<ShopEntry> ShopEntries { get; set; } public List<ShopEntry> ShopEntries { get; set; }
public ulong? GameVoiceChannel { get; set; } = null; public ulong? GameVoiceChannel { get; set; } = null;
public bool VerboseErrors { get; set; } = false;
//public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>(); //public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
} }

View File

@ -4,16 +4,24 @@ using System.Threading.Tasks;
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using System; using System;
using Discord.Commands;
using NadekoBot.Extensions;
using System.Linq;
using NadekoBot.Attributes;
namespace NadekoBot.Services.Help namespace NadekoBot.Services.Help
{ {
public class HelpService : ILateExecutor public class HelpService : ILateExecutor
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CommandHandler _ch;
private readonly NadekoStrings _strings;
public HelpService(BotConfig bc) public HelpService(BotConfig bc, CommandHandler ch, NadekoStrings strings)
{ {
_bc = bc; _bc = bc;
_ch = ch;
_strings = strings;
} }
public async Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg) public async Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg)
@ -28,5 +36,37 @@ namespace NadekoBot.Services.Help
//ignore //ignore
} }
} }
public EmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild)
{
var prefix = _ch.GetPrefix(guild);
var str = string.Format("**`{0}`**", prefix + com.Aliases.First());
var alias = com.Aliases.Skip(1).FirstOrDefault();
if (alias != null)
str += string.Format(" **/ `{0}`**", prefix + alias);
return new EmbedBuilder()
.AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary(prefix)} {GetCommandRequirements(com, guild)}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("usage", guild)).WithValue(com.RealRemarks(prefix)).WithIsInline(false))
.WithColor(NadekoBot.OkColor);
}
public string GetCommandRequirements(CommandInfo cmd, IGuild guild) =>
string.Join(" ", cmd.Preconditions
.Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
.Select(ca =>
{
if (ca is OwnerOnlyAttribute)
return Format.Bold(GetText("bot_owner_only", guild));
var cau = (RequireUserPermissionAttribute)ca;
if (cau.GuildPermission != null)
return Format.Bold(GetText("server_permission", guild, cau.GuildPermission))
.Replace("Guild", "Server");
return Format.Bold(GetText("channel_permission", guild, cau.ChannelPermission))
.Replace("Guild", "Server");
}));
private string GetText(string text, IGuild guild, params object[] replacements) =>
_strings.GetText(text, guild?.Id, "Help".ToLowerInvariant(), replacements);
} }
} }

View File

@ -90,7 +90,7 @@ namespace NadekoBot.Services
return val; return val;
} }
public string GetText(string key, ulong guildId, string lowerModuleTypeName, params object[] replacements) => public string GetText(string key, ulong? guildId, string lowerModuleTypeName, params object[] replacements) =>
GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName, replacements); GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName, replacements);
public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName) public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName)

View File

@ -0,0 +1,80 @@
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord;
using NadekoBot.Extensions;
using NadekoBot.Services.Help;
using Discord.Commands;
using System.Linq;
namespace NadekoBot.Services.Utility
{
public class VerboseErrorsService
{
private readonly ConcurrentHashSet<ulong> guildsEnabled;
private readonly DbService _db;
private readonly CommandHandler _ch;
private readonly HelpService _hs;
public VerboseErrorsService(IEnumerable<GuildConfig> gcs, DbService db, CommandHandler ch, HelpService hs)
{
_db = db;
_ch = ch;
_hs = hs;
ch.CommandErrored += LogVerboseError;
guildsEnabled = new ConcurrentHashSet<ulong>(gcs.Where(x => x.VerboseErrors).Select(x => x.GuildId));
}
private async Task LogVerboseError(CommandInfo cmd, ITextChannel channel, string reason)
{
if (channel == null || !guildsEnabled.Contains(channel.GuildId))
return;
try
{
var embed = _hs.GetCommandHelp(cmd, channel.Guild)
.WithTitle("Command Error")
.WithDescription(reason)
.WithErrorColor();
await channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{
//ignore
}
}
public bool ToggleVerboseErrors(ulong guildId)
{
using (var uow = _db.UnitOfWork)
{
var gc = uow.GuildConfigs.For(guildId, set => set);
gc.VerboseErrors = !gc.VerboseErrors;
uow.Complete();
if (gc.VerboseErrors)
guildsEnabled.Add(guildId);
else
guildsEnabled.TryRemove(guildId);
}
if (guildsEnabled.Add(guildId))
{
return true;
}
else
{
guildsEnabled.TryRemove(guildId);
return false;
}
}
}
}

View File

@ -674,6 +674,8 @@
"utility_uptime": "Uptime", "utility_uptime": "Uptime",
"utility_userid": "{0} of the user {1} is {2}", "utility_userid": "{0} of the user {1} is {2}",
"utility_users": "Users", "utility_users": "Users",
"utility_verbose_errors_enabled": "Incorrectly used commands will now show errors.",
"utility_verbose_errors_disabled": "Incorrectly used commands will no longer show errors.",
"utility_voice_channels": "Voice channels", "utility_voice_channels": "Voice channels",
"gambling_animal_race_already_in": "You've already joined this race!", "gambling_animal_race_already_in": "You've already joined this race!",
"games_current_poll_results": "Current poll results", "games_current_poll_results": "Current poll results",