2020-07-22 23:58:38 +00:00
|
|
|
|
using BirthdayBot.Data;
|
|
|
|
|
using Discord.WebSocket;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace BirthdayBot.UserInterface
|
|
|
|
|
{
|
|
|
|
|
internal class UserCommands : CommandsCommon
|
|
|
|
|
{
|
2020-10-05 04:40:38 +00:00
|
|
|
|
public UserCommands(Configuration db) : base(db) { }
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
|
|
|
|
public override IEnumerable<(string, CommandHandler)> Commands
|
|
|
|
|
=> new List<(string, CommandHandler)>()
|
|
|
|
|
{
|
|
|
|
|
("set", CmdSet),
|
|
|
|
|
("zone", CmdZone),
|
2020-06-04 01:58:03 +00:00
|
|
|
|
("remove", CmdRemove)
|
2020-04-02 18:27:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parses date parameter. Strictly takes dd-MMM or MMM-dd only. Eliminates ambiguity over dd/mm vs mm/dd.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Tuple: month, day</returns>
|
|
|
|
|
/// <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)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
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`";
|
2020-04-02 18:27:55 +00:00
|
|
|
|
// 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.
|
|
|
|
|
var m = Regex.Match(dateInput, @"^(?<day>\d{1,2})-(?<month>[A-Za-z]{3})$");
|
|
|
|
|
if (!m.Success)
|
|
|
|
|
{
|
|
|
|
|
// Flip the fields around, try again
|
|
|
|
|
m = Regex.Match(dateInput, @"^(?<month>[A-Za-z]{3})-(?<day>\d{1,2})$");
|
2020-06-04 01:58:03 +00:00
|
|
|
|
if (!m.Success) throw new FormatException(FormatError);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|
|
|
|
|
int day;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
day = int.Parse(m.Groups["day"].Value);
|
|
|
|
|
}
|
|
|
|
|
catch (FormatException)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
throw new Exception(FormatError);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|
|
|
|
|
var monthVal = m.Groups["month"].Value;
|
|
|
|
|
int month;
|
|
|
|
|
var dayUpper = 31; // upper day of month check
|
|
|
|
|
switch (monthVal.ToLower())
|
|
|
|
|
{
|
|
|
|
|
case "jan":
|
|
|
|
|
month = 1;
|
|
|
|
|
break;
|
|
|
|
|
case "feb":
|
|
|
|
|
month = 2;
|
|
|
|
|
dayUpper = 29;
|
|
|
|
|
break;
|
|
|
|
|
case "mar":
|
|
|
|
|
month = 3;
|
|
|
|
|
break;
|
|
|
|
|
case "apr":
|
|
|
|
|
month = 4;
|
|
|
|
|
dayUpper = 30;
|
|
|
|
|
break;
|
|
|
|
|
case "may":
|
|
|
|
|
month = 5;
|
|
|
|
|
break;
|
|
|
|
|
case "jun":
|
|
|
|
|
month = 6;
|
|
|
|
|
dayUpper = 30;
|
|
|
|
|
break;
|
|
|
|
|
case "jul":
|
|
|
|
|
month = 7;
|
|
|
|
|
break;
|
|
|
|
|
case "aug":
|
|
|
|
|
month = 8;
|
|
|
|
|
break;
|
|
|
|
|
case "sep":
|
|
|
|
|
month = 9;
|
|
|
|
|
dayUpper = 30;
|
|
|
|
|
break;
|
|
|
|
|
case "oct":
|
|
|
|
|
month = 10;
|
|
|
|
|
break;
|
|
|
|
|
case "nov":
|
|
|
|
|
month = 11;
|
|
|
|
|
dayUpper = 30;
|
|
|
|
|
break;
|
|
|
|
|
case "dec":
|
|
|
|
|
month = 12;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new FormatException(":x: Invalid month name. Use a three-letter month abbreviation.");
|
|
|
|
|
}
|
|
|
|
|
if (day == 0 || day > dayUpper) throw new FormatException(":x: The date you specified is not a valid calendar date.");
|
|
|
|
|
|
|
|
|
|
return (month, day);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 01:58:03 +00:00
|
|
|
|
#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
|
|
|
|
|
|
2020-10-05 04:40:38 +00:00
|
|
|
|
private async Task CmdSet(ShardInstance instance, GuildConfiguration gconf,
|
|
|
|
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
2020-04-02 18:27:55 +00:00
|
|
|
|
{
|
|
|
|
|
// Requires one parameter. Optionally two.
|
|
|
|
|
if (param.Length < 2 || param.Length > 3)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocSet.UsageEmbed);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bmonth, bday;
|
|
|
|
|
string btz = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var res = ParseDate(param[1]);
|
|
|
|
|
bmonth = res.Item1;
|
|
|
|
|
bday = res.Item2;
|
|
|
|
|
if (param.Length == 3) btz = ParseTimeZone(param[2]);
|
|
|
|
|
}
|
|
|
|
|
catch (FormatException ex)
|
|
|
|
|
{
|
|
|
|
|
// Our parse methods' FormatException has its message to send out to Discord.
|
2020-06-04 01:58:03 +00:00
|
|
|
|
reqChannel.SendMessageAsync(ex.Message, embed: DocSet.UsageEmbed).Wait();
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parsing successful. Update user information.
|
|
|
|
|
bool known; // Extra detail: Bot's response changes if the user was previously unknown.
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-07-22 23:58:38 +00:00
|
|
|
|
var user = await GuildUserConfiguration.LoadAsync(gconf.GuildId, reqUser.Id);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
known = user.IsKnown;
|
2020-07-22 23:58:38 +00:00
|
|
|
|
await user.UpdateAsync(bmonth, bday, btz);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Program.Log("Error", ex.ToString());
|
2020-05-22 07:27:31 +00:00
|
|
|
|
// TODO webhook report
|
2020-06-04 01:58:03 +00:00
|
|
|
|
reqChannel.SendMessageAsync(InternalError).Wait();
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (known)
|
|
|
|
|
{
|
|
|
|
|
await reqChannel.SendMessageAsync(":white_check_mark: Your information has been updated.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
await reqChannel.SendMessageAsync(":white_check_mark: Your birthday has been recorded.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 04:40:38 +00:00
|
|
|
|
private async Task CmdZone(ShardInstance instance, GuildConfiguration gconf,
|
|
|
|
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
2020-04-02 18:27:55 +00:00
|
|
|
|
{
|
|
|
|
|
if (param.Length != 2)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocZone.UsageEmbed);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 23:58:38 +00:00
|
|
|
|
var user = await GuildUserConfiguration.LoadAsync(gconf.GuildId, reqUser.Id);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
if (!user.IsKnown)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
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);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 01:58:03 +00:00
|
|
|
|
string btz;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
btz = ParseTimeZone(param[1]);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
reqChannel.SendMessageAsync(ex.Message, embed: DocZone.UsageEmbed).Wait();
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-07-22 23:58:38 +00:00
|
|
|
|
await user.UpdateAsync(user.BirthMonth, user.BirthDay, btz);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
|
|
|
|
await reqChannel.SendMessageAsync($":white_check_mark: Your time zone has been updated to **{btz}**.");
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 04:40:38 +00:00
|
|
|
|
private async Task CmdRemove(ShardInstance instance, GuildConfiguration gconf,
|
|
|
|
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
2020-04-02 18:27:55 +00:00
|
|
|
|
{
|
|
|
|
|
// Parameter count check
|
|
|
|
|
if (param.Length != 1)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
await reqChannel.SendMessageAsync(NoParameterError, embed: DocRemove.UsageEmbed);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extra detail: Send a notification if the user isn't actually known by the bot.
|
|
|
|
|
bool known;
|
2020-07-22 23:58:38 +00:00
|
|
|
|
var u = await GuildUserConfiguration.LoadAsync(gconf.GuildId, reqUser.Id);
|
|
|
|
|
known = u.IsKnown;
|
|
|
|
|
await u.DeleteAsync();
|
2020-04-02 18:27:55 +00:00
|
|
|
|
if (!known)
|
|
|
|
|
{
|
2020-06-04 01:58:03 +00:00
|
|
|
|
await reqChannel.SendMessageAsync(":white_check_mark: This bot already does not contain your information.");
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
await reqChannel.SendMessageAsync(":white_check_mark: Your information has been removed.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|