mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-25 01:44:12 +00:00
Improved user-facing error messages
Also moved bb.when handler to a different class
This commit is contained in:
parent
fdb8e2bbb9
commit
1137777092
4 changed files with 112 additions and 90 deletions
|
@ -20,7 +20,10 @@ namespace BirthdayBot.UserInterface
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
public const string GenericError = ":x: Invalid usage. Consult the help command.";
|
public const string GenericError = ":x: Invalid usage. Consult the help command.";
|
||||||
public const string BadUserError = ":x: Unable to find user. Specify their `@` mention or their ID.";
|
public const string BadUserError = ":x: Unable to find user. Specify their `@` mention or their ID.";
|
||||||
public const string ExpectedNoParametersError = ":x: This command does not take parameters. Did you mean to use another?";
|
public const string ParameterError = ":x: Incorrect number of parameters. Be sure you have not added spaces"
|
||||||
|
+ " where the bot is not expecting them or that all required information has been provided.";
|
||||||
|
public const string NoParameterError = ":x: This command does not accept parameters.";
|
||||||
|
public const string InternalError = ":x: An internal bot error occurred. The bot maintainer has been notified of the issue.";
|
||||||
|
|
||||||
public delegate Task CommandHandler(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser);
|
public delegate Task CommandHandler(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser);
|
||||||
|
|
||||||
|
@ -68,7 +71,8 @@ namespace BirthdayBot.UserInterface
|
||||||
// Just check if the input exists in the map. Get the "true" value, or reject it altogether.
|
// Just check if the input exists in the map. Get the "true" value, or reject it altogether.
|
||||||
if (!TzNameMap.TryGetValue(tzinput, out tz))
|
if (!TzNameMap.TryGetValue(tzinput, out tz))
|
||||||
{
|
{
|
||||||
throw new FormatException(":x: Unknown or invalid time zone name.");
|
throw new FormatException(":x: Unexpected time zone name."
|
||||||
|
+ $" Refer to `{CommandPrefix}help-tzdata` to help determine the correct value.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tz;
|
return tz;
|
||||||
|
|
|
@ -37,25 +37,18 @@ namespace BirthdayBot.UserInterface
|
||||||
Name = "Commands",
|
Name = "Commands",
|
||||||
Value = $"{cpfx}help`, `{CommandPrefix}info`, `{CommandPrefix}help-tzdata`\n"
|
Value = $"{cpfx}help`, `{CommandPrefix}info`, `{CommandPrefix}help-tzdata`\n"
|
||||||
+ $" » Help and informational messages.\n"
|
+ $" » Help and informational messages.\n"
|
||||||
+ $"{cpfx}recent` and `{CommandPrefix}upcoming`\n"
|
+ ListingCommands.DocUpcoming.Export() + "\n"
|
||||||
+ $" » Lists recent and upcoming birthdays.\n"
|
+ UserCommands.DocSet.Export() + "\n"
|
||||||
+ $"{cpfx}set (date) [zone]`\n"
|
+ UserCommands.DocZone.Export() + "\n"
|
||||||
+ $" » Registers your birth date. Time zone is optional.\n"
|
+ UserCommands.DocRemove.Export() + "\n"
|
||||||
+ $" »» Examples: `{CommandPrefix}set jan-31`, `{CommandPrefix}set 15-aug America/Los_Angeles`.\n"
|
+ ListingCommands.DocWhen.Export()
|
||||||
+ $"{cpfx}zone (zone)`\n"
|
|
||||||
+ $" » Sets your local time zone. See `{CommandPrefix}help-tzdata`.\n"
|
|
||||||
+ $"{cpfx}remove`\n"
|
|
||||||
+ $" » Removes your birthday information from this bot.\n"
|
|
||||||
+ $"{cpfx}when (user)`\n"
|
|
||||||
+ $" » Displays birthday information of the given user."
|
|
||||||
};
|
};
|
||||||
var cmdModField = new EmbedFieldBuilder()
|
var cmdModField = new EmbedFieldBuilder()
|
||||||
{
|
{
|
||||||
Name = "Commands",
|
Name = "Moderator actions",
|
||||||
Value = $"{cpfx}config`\n"
|
Value = $"{cpfx}config`\n"
|
||||||
+ $" » Edit bot configuration. See `{CommandPrefix}help-config`.\n"
|
+ $" » Edit bot configuration. See `{CommandPrefix}help-config`.\n"
|
||||||
+ $"{cpfx}list`\n"
|
+ ListingCommands.DocList.Export() + "\n"
|
||||||
+ $" » Exports all birthdays to file. Accepts `csv` as a parameter.\n"
|
|
||||||
+ $"{cpfx}override (user ping or ID) (command w/ parameters)`\n"
|
+ $"{cpfx}override (user ping or ID) (command w/ parameters)`\n"
|
||||||
+ " » Perform certain commands on behalf of another user."
|
+ " » Perform certain commands on behalf of another user."
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,15 +21,84 @@ namespace BirthdayBot.UserInterface
|
||||||
{
|
{
|
||||||
("list", CmdList),
|
("list", CmdList),
|
||||||
("upcoming", CmdUpcoming),
|
("upcoming", CmdUpcoming),
|
||||||
("recent", CmdUpcoming)
|
("recent", CmdUpcoming),
|
||||||
|
("when", CmdWhen)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#region Documentation
|
||||||
|
public static readonly CommandDocumentation DocList =
|
||||||
|
new CommandDocumentation(new string[] { "list" }, "Exports all birthdays to a file."
|
||||||
|
+ " Accepts `csv` as an optional parameter.", null);
|
||||||
|
public static readonly CommandDocumentation DocUpcoming =
|
||||||
|
new CommandDocumentation(new string[] { "recent", "upcoming" }, "Lists recent and upcoming birthdays.", null);
|
||||||
|
public static readonly CommandDocumentation DocWhen =
|
||||||
|
new CommandDocumentation(new string[] { "when" }, "Displays the given user's birthday information.", null);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task CmdWhen(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
||||||
|
{
|
||||||
|
// Requires a parameter
|
||||||
|
if (param.Length == 1)
|
||||||
|
{
|
||||||
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocWhen.UsageEmbed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var search = param[1];
|
||||||
|
if (param.Length == 3)
|
||||||
|
{
|
||||||
|
// param maxes out at 3 values. param[2] might contain part of the search string (if name has a space)
|
||||||
|
search += " " + param[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketGuildUser searchTarget = null;
|
||||||
|
|
||||||
|
ulong searchId = 0;
|
||||||
|
if (!TryGetUserId(search, out searchId)) // ID lookup
|
||||||
|
{
|
||||||
|
// name lookup without discriminator
|
||||||
|
foreach (var searchuser in reqChannel.Guild.Users)
|
||||||
|
{
|
||||||
|
if (string.Equals(search, searchuser.Username, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
searchTarget = searchuser;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchTarget = reqChannel.Guild.GetUser(searchId);
|
||||||
|
}
|
||||||
|
if (searchTarget == null)
|
||||||
|
{
|
||||||
|
await reqChannel.SendMessageAsync(BadUserError, embed: DocWhen.UsageEmbed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = Instance.GuildCache[reqChannel.Guild.Id].Users;
|
||||||
|
var searchTargetData = users.FirstOrDefault(u => u.UserId == searchTarget.Id);
|
||||||
|
if (searchTargetData == null)
|
||||||
|
{
|
||||||
|
await reqChannel.SendMessageAsync("I do not have birthday information for that user.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string result = Common.FormatName(searchTarget, false);
|
||||||
|
result += ": ";
|
||||||
|
result += $"`{searchTargetData.BirthDay:00}-{Common.MonthNames[searchTargetData.BirthMonth]}`";
|
||||||
|
result += searchTargetData.TimeZone == null ? "" : $" - `{searchTargetData.TimeZone}`";
|
||||||
|
|
||||||
|
await reqChannel.SendMessageAsync(result);
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a file with all birthdays.
|
// Creates a file with all birthdays.
|
||||||
private async Task CmdList(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
private async Task CmdList(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
||||||
{
|
{
|
||||||
// For now, we're restricting this command to moderators only. This may turn into an option later.
|
// For now, we're restricting this command to moderators only. This may turn into an option later.
|
||||||
if (!Instance.GuildCache[reqChannel.Guild.Id].IsUserModerator(reqUser))
|
if (!Instance.GuildCache[reqChannel.Guild.Id].IsUserModerator(reqUser))
|
||||||
{
|
{
|
||||||
|
// Do not add detailed usage information to this error message.
|
||||||
await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.");
|
await reqChannel.SendMessageAsync(":x: Only bot moderators may use this command.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -41,13 +110,13 @@ namespace BirthdayBot.UserInterface
|
||||||
if (param[1].ToLower() == "csv") useCsv = true;
|
if (param[1].ToLower() == "csv") useCsv = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(":x: That is not available as an export format.");
|
await reqChannel.SendMessageAsync(":x: That is not available as an export format.", embed: DocList.UsageEmbed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (param.Length > 2)
|
else if (param.Length > 2)
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(GenericError);
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocList.UsageEmbed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +147,7 @@ namespace BirthdayBot.UserInterface
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Program.Log("Listing", ex.ToString());
|
Program.Log("Listing", ex.ToString());
|
||||||
reqChannel.SendMessageAsync(":x: An internal error occurred. It has been reported to the bot owner.").Wait();
|
reqChannel.SendMessageAsync(InternalError).Wait();
|
||||||
// TODO webhook report
|
// TODO webhook report
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -16,8 +16,7 @@ namespace BirthdayBot.UserInterface
|
||||||
{
|
{
|
||||||
("set", CmdSet),
|
("set", CmdSet),
|
||||||
("zone", CmdZone),
|
("zone", CmdZone),
|
||||||
("remove", CmdRemove),
|
("remove", CmdRemove)
|
||||||
("when", CmdWhen)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -27,6 +26,8 @@ namespace BirthdayBot.UserInterface
|
||||||
/// <exception cref="FormatException">Thrown for any parsing issue. Reason is expected to be sent to Discord as-is.</exception>
|
/// <exception cref="FormatException">Thrown for any parsing issue. Reason is expected to be sent to Discord as-is.</exception>
|
||||||
private (int, int) ParseDate(string dateInput)
|
private (int, int) ParseDate(string dateInput)
|
||||||
{
|
{
|
||||||
|
const string FormatError = ":x: Incorrect date format. Use a three-letter abbreviation and a number separated by "
|
||||||
|
+ "hyphen to specify a date. Examples: `jan-15` `23-aug` `may-12` `5-jun`";
|
||||||
// Not doing DateTime.Parse. Setting it up is rather complicated, and it's probably case sensitive.
|
// Not doing DateTime.Parse. Setting it up is rather complicated, and it's probably case sensitive.
|
||||||
// Admittedly, doing it the way it's being done here probably isn't any better.
|
// Admittedly, doing it the way it's being done here probably isn't any better.
|
||||||
var m = Regex.Match(dateInput, @"^(?<day>\d{1,2})-(?<month>[A-Za-z]{3})$");
|
var m = Regex.Match(dateInput, @"^(?<day>\d{1,2})-(?<month>[A-Za-z]{3})$");
|
||||||
|
@ -34,7 +35,7 @@ namespace BirthdayBot.UserInterface
|
||||||
{
|
{
|
||||||
// Flip the fields around, try again
|
// Flip the fields around, try again
|
||||||
m = Regex.Match(dateInput, @"^(?<month>[A-Za-z]{3})-(?<day>\d{1,2})$");
|
m = Regex.Match(dateInput, @"^(?<month>[A-Za-z]{3})-(?<day>\d{1,2})$");
|
||||||
if (!m.Success) throw new FormatException(GenericError);
|
if (!m.Success) throw new FormatException(FormatError);
|
||||||
}
|
}
|
||||||
int day;
|
int day;
|
||||||
try
|
try
|
||||||
|
@ -43,7 +44,7 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
catch (FormatException)
|
catch (FormatException)
|
||||||
{
|
{
|
||||||
throw new Exception(GenericError);
|
throw new Exception(FormatError);
|
||||||
}
|
}
|
||||||
var monthVal = m.Groups["month"].Value;
|
var monthVal = m.Groups["month"].Value;
|
||||||
int month;
|
int month;
|
||||||
|
@ -99,12 +100,23 @@ namespace BirthdayBot.UserInterface
|
||||||
return (month, day);
|
return (month, day);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Documentation
|
||||||
|
public static readonly CommandDocumentation DocSet =
|
||||||
|
new CommandDocumentation(new string[] { "set (date) [zone]" }, "Registers your birth date. Time zone is optional.",
|
||||||
|
$"`{CommandPrefix}set jan-31`, `{CommandPrefix}set 15-aug America/Los_Angeles`.");
|
||||||
|
public static readonly CommandDocumentation DocZone =
|
||||||
|
new CommandDocumentation(new string[] { "zone (zone)" }, "Sets your local time zone. "
|
||||||
|
+ $"See also `{CommandPrefix}help-tzdata`.", null);
|
||||||
|
public static readonly CommandDocumentation DocRemove =
|
||||||
|
new CommandDocumentation(new string[] { "remove" }, "Removes your birthday information from this bot.", null);
|
||||||
|
#endregion
|
||||||
|
|
||||||
private async Task CmdSet(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
private async Task CmdSet(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
||||||
{
|
{
|
||||||
// Requires one parameter. Optionally two.
|
// Requires one parameter. Optionally two.
|
||||||
if (param.Length < 2 || param.Length > 3)
|
if (param.Length < 2 || param.Length > 3)
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(GenericError);
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocSet.UsageEmbed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +132,7 @@ namespace BirthdayBot.UserInterface
|
||||||
catch (FormatException ex)
|
catch (FormatException ex)
|
||||||
{
|
{
|
||||||
// Our parse methods' FormatException has its message to send out to Discord.
|
// Our parse methods' FormatException has its message to send out to Discord.
|
||||||
reqChannel.SendMessageAsync(ex.Message).Wait();
|
reqChannel.SendMessageAsync(ex.Message, embed: DocSet.UsageEmbed).Wait();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +147,8 @@ namespace BirthdayBot.UserInterface
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Program.Log("Error", ex.ToString());
|
Program.Log("Error", ex.ToString());
|
||||||
reqChannel.SendMessageAsync(":x: An unknown error occurred. The bot owner has been notified.").Wait();
|
|
||||||
// TODO webhook report
|
// TODO webhook report
|
||||||
|
reqChannel.SendMessageAsync(InternalError).Wait();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (known)
|
if (known)
|
||||||
|
@ -153,25 +165,26 @@ namespace BirthdayBot.UserInterface
|
||||||
{
|
{
|
||||||
if (param.Length != 2)
|
if (param.Length != 2)
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(GenericError);
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocZone.UsageEmbed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string btz = null;
|
|
||||||
var user = Instance.GuildCache[reqChannel.Guild.Id].GetUser(reqUser.Id);
|
var user = Instance.GuildCache[reqChannel.Guild.Id].GetUser(reqUser.Id);
|
||||||
if (!user.IsKnown)
|
if (!user.IsKnown)
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(":x: Can't set your time zone if your birth date isn't registered.");
|
await reqChannel.SendMessageAsync(":x: You may only update your time zone when you have a birthday registered."
|
||||||
|
+ $" Refer to the `{CommandPrefix}set` command.", embed: DocZone.UsageEmbed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string btz;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
btz = ParseTimeZone(param[1]);
|
btz = ParseTimeZone(param[1]);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
reqChannel.SendMessageAsync(ex.Message).Wait();
|
reqChannel.SendMessageAsync(ex.Message, embed: DocZone.UsageEmbed).Wait();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings);
|
await user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings);
|
||||||
|
@ -184,7 +197,7 @@ namespace BirthdayBot.UserInterface
|
||||||
// Parameter count check
|
// Parameter count check
|
||||||
if (param.Length != 1)
|
if (param.Length != 1)
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(ExpectedNoParametersError);
|
await reqChannel.SendMessageAsync(NoParameterError, embed: DocRemove.UsageEmbed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,69 +209,12 @@ namespace BirthdayBot.UserInterface
|
||||||
await g.DeleteUserAsync(reqUser.Id);
|
await g.DeleteUserAsync(reqUser.Id);
|
||||||
if (!known)
|
if (!known)
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: I don't have your information. Nothing to remove.");
|
await reqChannel.SendMessageAsync(":white_check_mark: This bot already does not contain your information.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: Your information has been removed.");
|
await reqChannel.SendMessageAsync(":white_check_mark: Your information has been removed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CmdWhen(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
|
||||||
{
|
|
||||||
// Requires a parameter
|
|
||||||
if (param.Length == 1)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(GenericError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var search = param[1];
|
|
||||||
if (param.Length == 3)
|
|
||||||
{
|
|
||||||
// param maxes out at 3 values. param[2] might contain part of the search string (if name has a space)
|
|
||||||
search += " " + param[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketGuildUser searchTarget = null;
|
|
||||||
|
|
||||||
ulong searchId = 0;
|
|
||||||
if (!TryGetUserId(search, out searchId)) // ID lookup
|
|
||||||
{
|
|
||||||
// name lookup without discriminator
|
|
||||||
foreach (var searchuser in reqChannel.Guild.Users)
|
|
||||||
{
|
|
||||||
if (string.Equals(search, searchuser.Username, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
searchTarget = searchuser;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
searchTarget = reqChannel.Guild.GetUser(searchId);
|
|
||||||
}
|
|
||||||
if (searchTarget == null)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(BadUserError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var users = Instance.GuildCache[reqChannel.Guild.Id].Users;
|
|
||||||
var searchTargetData = users.FirstOrDefault(u => u.UserId == searchTarget.Id);
|
|
||||||
if (searchTargetData == null)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync("The given user does not exist or has not set a birthday.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string result = Common.FormatName(searchTarget, false);
|
|
||||||
result += ": ";
|
|
||||||
result += $"`{searchTargetData.BirthDay:00}-{Common.MonthNames[searchTargetData.BirthMonth]}`";
|
|
||||||
result += searchTargetData.TimeZone == null ? "" : $" - `{searchTargetData.TimeZone}`";
|
|
||||||
|
|
||||||
await reqChannel.SendMessageAsync(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue