BirthdayBot/UserInterface/UserCommands.cs
Noi ddcde10e09 First commit for C# rewrite
All existing VB code was 'translated' to C# as closely as possible, with
minor changes and additional notes. Currently untested and likely
broken.
Further commits will go toward making overall improvements until this
version replaces the currently existing code.
2020-04-02 11:38:26 -07:00

260 lines
9.4 KiB
C#

using Discord.WebSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace BirthdayBot.UserInterface
{
internal class UserCommands : CommandsCommon
{
public UserCommands(BirthdayBot inst, Configuration db) : base(inst, db) { }
public override IEnumerable<(string, CommandHandler)> Commands
=> new List<(string, CommandHandler)>()
{
("set", CmdSet),
("zone", CmdZone),
("remove", CmdRemove),
("when", CmdWhen)
};
/// <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)
{
// 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})$");
if (!m.Success) throw new FormatException(GenericError);
}
int day;
try
{
day = int.Parse(m.Groups["day"].Value);
}
catch (FormatException)
{
throw new Exception(GenericError);
}
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);
}
private async Task CmdSet(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
{
// Requires one parameter. Optionally two.
if (param.Length < 2 || param.Length > 3)
{
await reqChannel.SendMessageAsync(GenericError);
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.
reqChannel.SendMessageAsync(ex.Message).Wait();
return;
}
// Parsing successful. Update user information.
bool known; // Extra detail: Bot's response changes if the user was previously unknown.
try
{
var user = Instance.GuildCache[reqChannel.Guild.Id].GetUser(reqUser.Id);
known = user.IsKnown;
await user.UpdateAsync(bmonth, bday, btz, BotConfig.DatabaseSettings);
}
catch (Exception ex)
{
Program.Log("Error", ex.ToString());
reqChannel.SendMessageAsync(":x: An unknown error occurred. The bot owner has been notified.").Wait();
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.");
}
}
private async Task CmdZone(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
{
if (param.Length != 2)
{
await reqChannel.SendMessageAsync(GenericError);
return;
}
string btz = null;
var user = Instance.GuildCache[reqChannel.Guild.Id].GetUser(reqUser.Id);
if (!user.IsKnown)
{
await reqChannel.SendMessageAsync(":x: Can't set your time zone if your birth date isn't registered.");
return;
}
try
{
btz = ParseTimeZone(param[1]);
}
catch (Exception ex)
{
reqChannel.SendMessageAsync(ex.Message).Wait();
return;
}
await user.UpdateAsync(user.BirthMonth, user.BirthDay, btz, BotConfig.DatabaseSettings);
await reqChannel.SendMessageAsync($":white_check_mark: Your time zone has been updated to **{btz}**.");
}
private async Task CmdRemove(string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
{
// Parameter count check
if (param.Length != 1)
{
await reqChannel.SendMessageAsync(ExpectedNoParametersError);
return;
}
// Extra detail: Send a notification if the user isn't actually known by the bot.
bool known;
var g = Instance.GuildCache[reqChannel.Guild.Id];
known = g.GetUser(reqUser.Id).IsKnown;
// Delete database and cache entry
await g.DeleteUserAsync(reqUser.Id);
if (!known)
{
await reqChannel.SendMessageAsync(":white_check_mark: I don't have your information. Nothing to remove.");
}
else
{
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;
}
await reqChannel.SendMessageAsync(Common.FormatName(searchTarget, false) + ": "
+ $"`{ searchTargetData.BirthDay.ToString("00")}-{Common.MonthNames[searchTargetData.BirthMonth]}`"
+ searchTargetData.TimeZone == null ? "" : $" - `{ searchTargetData.TimeZone}`");
}
}
}