Apply suggestions from code analysis

This commit is contained in:
Noi 2021-10-13 20:38:52 -07:00
parent 7af571205a
commit dcb9bfdd9a
14 changed files with 74 additions and 103 deletions

View file

@ -19,8 +19,7 @@ namespace BirthdayBot.BackgroundServices
public BirthdayRoleUpdate(ShardInstance instance) : base(instance) { } public BirthdayRoleUpdate(ShardInstance instance) : base(instance) { }
/// <summary> /// <summary>
/// Processes birthday updates for all available guilds synchronously /// Processes birthday updates for all available guilds synchronously.
/// (to avoid database connection pool bottlenecks and rate limiting).
/// </summary> /// </summary>
public override async Task OnTick(CancellationToken token) public override async Task OnTick(CancellationToken token)
{ {
@ -41,25 +40,24 @@ namespace BirthdayBot.BackgroundServices
} }
catch (Exception ex) catch (Exception ex)
{ {
// Catch all exceptions per-guild but continue processing, throw at end // Catch all exceptions per-guild but continue processing, throw at end.
exs.Add(ex); exs.Add(ex);
} }
} }
if (exs.Count != 0) throw new AggregateException(exs); if (exs.Count != 0) throw new AggregateException(exs);
// TODO metrics for role sets, unsets, announcements - and how to do that for singles too?
} }
/// <summary> /// <summary>
/// Access to <see cref="ProcessGuildAsync(SocketGuild)"/> for the testing command. /// Access to <see cref="ProcessGuildAsync(SocketGuild)"/> for the testing command.
/// </summary> /// </summary>
/// <returns>Diagnostic data in string form.</returns> /// <returns>Diagnostic data in string form.</returns>
public async Task<string> SingleProcessGuildAsync(SocketGuild guild) => (await ProcessGuildAsync(guild).ConfigureAwait(false)).Export(); public static async Task<string> SingleProcessGuildAsync(SocketGuild guild)
=> (await ProcessGuildAsync(guild).ConfigureAwait(false)).Export();
/// <summary> /// <summary>
/// Main method where actual guild processing occurs. /// Main method where actual guild processing occurs.
/// </summary> /// </summary>
private async Task<PGDiagnostic> ProcessGuildAsync(SocketGuild guild) private static async Task<PGDiagnostic> ProcessGuildAsync(SocketGuild guild)
{ {
var diag = new PGDiagnostic(); var diag = new PGDiagnostic();
@ -100,7 +98,7 @@ namespace BirthdayBot.BackgroundServices
var announceping = gc.AnnouncePing; var announceping = gc.AnnouncePing;
SocketTextChannel channel = null; SocketTextChannel channel = null;
if (gc.AnnounceChannelId.HasValue) channel = guild.GetTextChannel(gc.AnnounceChannelId.Value); if (gc.AnnounceChannelId.HasValue) channel = guild.GetTextChannel(gc.AnnounceChannelId.Value);
if (announcementList.Count() != 0) if (announcementList.Any())
{ {
var announceResult = var announceResult =
await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList).ConfigureAwait(false); await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList).ConfigureAwait(false);
@ -117,7 +115,7 @@ namespace BirthdayBot.BackgroundServices
/// <summary> /// <summary>
/// Checks if the bot may be allowed to alter roles. /// Checks if the bot may be allowed to alter roles.
/// </summary> /// </summary>
private string CheckCorrectRoleSettings(SocketGuild guild, SocketRole role) private static string CheckCorrectRoleSettings(SocketGuild guild, SocketRole role)
{ {
if (role == null) return "Designated role is not set, or target role cannot be found."; if (role == null) return "Designated role is not set, or target role cannot be found.";
@ -139,7 +137,7 @@ namespace BirthdayBot.BackgroundServices
/// Gets all known users from the given guild and returns a list including only those who are /// Gets all known users from the given guild and returns a list including only those who are
/// currently experiencing a birthday in the respective time zone. /// currently experiencing a birthday in the respective time zone.
/// </summary> /// </summary>
private HashSet<ulong> GetGuildCurrentBirthdays(IEnumerable<GuildUserConfiguration> guildUsers, string defaultTzStr) private static HashSet<ulong> GetGuildCurrentBirthdays(IEnumerable<GuildUserConfiguration> guildUsers, string defaultTzStr)
{ {
var birthdayUsers = new HashSet<ulong>(); var birthdayUsers = new HashSet<ulong>();
@ -184,7 +182,7 @@ namespace BirthdayBot.BackgroundServices
/// First item: List of users who had the birthday role applied, used to announce. /// First item: List of users who had the birthday role applied, used to announce.
/// Second item: Counts of users who have had roles added/removed, used for operation reporting. /// Second item: Counts of users who have had roles added/removed, used for operation reporting.
/// </returns> /// </returns>
private async Task<(IEnumerable<SocketGuildUser>, (int, int))> UpdateGuildBirthdayRoles( private static async Task<(IEnumerable<SocketGuildUser>, (int, int))> UpdateGuildBirthdayRoles(
SocketGuild g, SocketRole r, HashSet<ulong> names) SocketGuild g, SocketRole r, HashSet<ulong> names)
{ {
// Check members currently with the role. Figure out which users to remove it from. // Check members currently with the role. Figure out which users to remove it from.
@ -224,7 +222,7 @@ namespace BirthdayBot.BackgroundServices
/// who have just had their birthday role added. /// who have just had their birthday role added.
/// </summary> /// </summary>
/// <returns>The message to place into operation status log.</returns> /// <returns>The message to place into operation status log.</returns>
private async Task<string> AnnounceBirthdaysAsync( private static async Task<string> AnnounceBirthdaysAsync(
(string, string) announce, bool announcePing, SocketTextChannel c, IEnumerable<SocketGuildUser> names) (string, string) announce, bool announcePing, SocketTextChannel c, IEnumerable<SocketGuildUser> names)
{ {
if (c == null) return "Announcement channel is not set, or previous announcement channel has been deleted."; if (c == null) return "Announcement channel is not set, or previous announcement channel has been deleted.";

View file

@ -15,7 +15,7 @@ namespace BirthdayBot.BackgroundServices
/// </summary> /// </summary>
class DataRetention : BackgroundService class DataRetention : BackgroundService
{ {
private static readonly SemaphoreSlim _updateLock = new SemaphoreSlim(ShardManager.MaxConcurrentOperations); private static readonly SemaphoreSlim _updateLock = new(ShardManager.MaxConcurrentOperations);
const int ProcessInterval = 3600 / ShardBackgroundWorker.Interval; // Process about once per hour const int ProcessInterval = 3600 / ShardBackgroundWorker.Interval; // Process about once per hour
private int _tickCount = -1; private int _tickCount = -1;
@ -33,10 +33,11 @@ namespace BirthdayBot.BackgroundServices
try try
{ {
// A semaphore is used to restrict this work being done concurrently on other shards // A semaphore is used to restrict this work being done concurrently on other shards
// to avoid putting pressure on the SQL connection pool. Updating this is a low priority. // to avoid putting pressure on the SQL connection pool. Clearing old database information
// ultimately is a low priority among other tasks.
await _updateLock.WaitAsync(token).ConfigureAwait(false); await _updateLock.WaitAsync(token).ConfigureAwait(false);
} }
catch (Exception ex) when (ex is OperationCanceledException || ex is ObjectDisposedException) catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException)
{ {
// Calling thread does not expect the exception that SemaphoreSlim throws... // Calling thread does not expect the exception that SemaphoreSlim throws...
throw new TaskCanceledException(); throw new TaskCanceledException();
@ -81,13 +82,13 @@ namespace BirthdayBot.BackgroundServices
var userlist = item.Value; var userlist = item.Value;
pUpdateG.Value = (long)guild; pUpdateG.Value = (long)guild;
updatedGuilds += await cUpdateGuild.ExecuteNonQueryAsync().ConfigureAwait(false); updatedGuilds += await cUpdateGuild.ExecuteNonQueryAsync(CancellationToken.None).ConfigureAwait(false);
pUpdateGU_g.Value = (long)guild; pUpdateGU_g.Value = (long)guild;
foreach (var userid in userlist) foreach (var userid in userlist)
{ {
pUpdateGU_u.Value = (long)userid; pUpdateGU_u.Value = (long)userid;
updatedUsers += await cUpdateGuildUser.ExecuteNonQueryAsync().ConfigureAwait(false); updatedUsers += await cUpdateGuildUser.ExecuteNonQueryAsync(CancellationToken.None).ConfigureAwait(false);
} }
} }
var resultText = new StringBuilder(); var resultText = new StringBuilder();
@ -100,13 +101,13 @@ namespace BirthdayBot.BackgroundServices
{ {
// Delete data for guilds not seen in 4 weeks // Delete data for guilds not seen in 4 weeks
c.CommandText = $"delete from {GuildConfiguration.BackingTable} where (now() - interval '28 days') > last_seen"; c.CommandText = $"delete from {GuildConfiguration.BackingTable} where (now() - interval '28 days') > last_seen";
staleGuilds = await c.ExecuteNonQueryAsync().ConfigureAwait(false); staleGuilds = await c.ExecuteNonQueryAsync(CancellationToken.None).ConfigureAwait(false);
} }
using (var c = db.CreateCommand()) using (var c = db.CreateCommand())
{ {
// Delete data for users not seen in 8 weeks // Delete data for users not seen in 8 weeks
c.CommandText = $"delete from {GuildUserConfiguration.BackingTable} where (now() - interval '56 days') > last_seen"; c.CommandText = $"delete from {GuildUserConfiguration.BackingTable} where (now() - interval '56 days') > last_seen";
staleUsers = await c.ExecuteNonQueryAsync().ConfigureAwait(false); staleUsers = await c.ExecuteNonQueryAsync(CancellationToken.None).ConfigureAwait(false);
} }
if (staleGuilds != 0 || staleUsers != 0) if (staleGuilds != 0 || staleUsers != 0)
{ {

View file

@ -14,7 +14,7 @@ namespace BirthdayBot.BackgroundServices
const int ProcessInterval = 600 / ShardBackgroundWorker.Interval; // Process every ~5 minutes const int ProcessInterval = 600 / ShardBackgroundWorker.Interval; // Process every ~5 minutes
private int _tickCount = 0; private int _tickCount = 0;
private static readonly HttpClient _httpClient = new HttpClient(); private static readonly HttpClient _httpClient = new();
public ExternalStatisticsReporting(ShardInstance instance) : base(instance) { } public ExternalStatisticsReporting(ShardInstance instance) : base(instance) { }

View file

@ -14,9 +14,9 @@ namespace BirthdayBot.BackgroundServices
/// </summary> /// </summary>
class SelectiveAutoUserDownload : BackgroundService class SelectiveAutoUserDownload : BackgroundService
{ {
private static readonly SemaphoreSlim _updateLock = new SemaphoreSlim(2); private static readonly SemaphoreSlim _updateLock = new(2);
private readonly HashSet<ulong> _fetchRequests = new HashSet<ulong>(); private readonly HashSet<ulong> _fetchRequests = new();
public SelectiveAutoUserDownload(ShardInstance instance) : base(instance) { } public SelectiveAutoUserDownload(ShardInstance instance) : base(instance) { }
@ -42,7 +42,7 @@ namespace BirthdayBot.BackgroundServices
if (requests.Contains(guild.Id) || await GuildUserAnyAsync(guild.Id, token).ConfigureAwait(false)) if (requests.Contains(guild.Id) || await GuildUserAnyAsync(guild.Id, token).ConfigureAwait(false))
{ {
await guild.DownloadUsersAsync().ConfigureAwait(false); await guild.DownloadUsersAsync().ConfigureAwait(false);
await Task.Delay(500).ConfigureAwait(false); await Task.Delay(500, CancellationToken.None).ConfigureAwait(false);
} }
} }
} }
@ -51,13 +51,13 @@ namespace BirthdayBot.BackgroundServices
/// Determines if the user database contains any entries corresponding to this guild. /// Determines if the user database contains any entries corresponding to this guild.
/// </summary> /// </summary>
/// <returns>True if any entries exist.</returns> /// <returns>True if any entries exist.</returns>
private async Task<bool> GuildUserAnyAsync(ulong guildId, CancellationToken token) private static async Task<bool> GuildUserAnyAsync(ulong guildId, CancellationToken token)
{ {
try try
{ {
await _updateLock.WaitAsync(token).ConfigureAwait(false); await _updateLock.WaitAsync(token).ConfigureAwait(false);
} }
catch (Exception ex) when (ex is OperationCanceledException || ex is ObjectDisposedException) catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException)
{ {
// Calling thread does not expect the exception that SemaphoreSlim throws... // Calling thread does not expect the exception that SemaphoreSlim throws...
throw new TaskCanceledException(); throw new TaskCanceledException();
@ -68,7 +68,7 @@ namespace BirthdayBot.BackgroundServices
using var c = db.CreateCommand(); using var c = db.CreateCommand();
c.CommandText = $"select count(*) from {GuildUserConfiguration.BackingTable} where guild_id = @Gid"; c.CommandText = $"select count(*) from {GuildUserConfiguration.BackingTable} where guild_id = @Gid";
c.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint).Value = (long)guildId; c.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint).Value = (long)guildId;
await c.PrepareAsync().ConfigureAwait(false); await c.PrepareAsync(CancellationToken.None).ConfigureAwait(false);
var r = (long)await c.ExecuteScalarAsync(token).ConfigureAwait(false); var r = (long)await c.ExecuteScalarAsync(token).ConfigureAwait(false);
return r != 0; return r != 0;
} }

View file

@ -14,12 +14,12 @@ namespace BirthdayBot
{ {
if (ping) return member.Mention; if (ping) return member.Mention;
string escapeFormattingCharacters(string input) static string escapeFormattingCharacters(string input)
{ {
var result = new StringBuilder(); var result = new StringBuilder();
foreach (var c in input) foreach (var c in input)
{ {
if (c == '\\' || c == '_' || c == '~' || c == '*' || c == '@') if (c is '\\' or '_' or '~' or '*' or '@')
{ {
result.Append('\\'); result.Append('\\');
} }
@ -36,7 +36,7 @@ namespace BirthdayBot
return $"**{username}**#{member.Discriminator}"; return $"**{username}**#{member.Discriminator}";
} }
public static readonly Dictionary<int, string> MonthNames = new Dictionary<int, string>() public static readonly Dictionary<int, string> MonthNames = new()
{ {
{1, "Jan"}, {2, "Feb"}, {3, "Mar"}, {4, "Apr"}, {5, "May"}, {6, "Jun"}, {1, "Jan"}, {2, "Feb"}, {3, "Mar"}, {4, "Apr"}, {5, "May"}, {6, "Jun"},
{7, "Jul"}, {8, "Aug"}, {9, "Sep"}, {10, "Oct"}, {11, "Nov"}, {12, "Dec"} {7, "Jul"}, {8, "Aug"}, {9, "Sep"}, {10, "Oct"}, {11, "Nov"}, {12, "Dec"}

View file

@ -86,7 +86,7 @@ namespace BirthdayBot
string srVal = jc[ShardLenConfKey]?.Value<string>(); string srVal = jc[ShardLenConfKey]?.Value<string>();
if (!string.IsNullOrWhiteSpace(srVal)) if (!string.IsNullOrWhiteSpace(srVal))
{ {
Regex srPicker = new Regex(@"(?<low>\d{1,2})[-,]{1}(?<high>\d{1,2})"); Regex srPicker = new(@"(?<low>\d{1,2})[-,]{1}(?<high>\d{1,2})");
var m = srPicker.Match(srVal); var m = srPicker.Match(srVal);
if (m.Success) if (m.Success)
{ {

View file

@ -108,8 +108,8 @@ namespace BirthdayBot
/// <summary> /// <summary>
/// Direct access to invoke the background task of updating birthdays in a guild, for use by the testing command. /// Direct access to invoke the background task of updating birthdays in a guild, for use by the testing command.
/// </summary> /// </summary>
public Task<string> ForceBirthdayUpdateAsync(SocketGuild guild) public static Task<string> ForceBirthdayUpdateAsync(SocketGuild guild)
=> _background.BirthdayUpdater.SingleProcessGuildAsync(guild); => BirthdayRoleUpdate.SingleProcessGuildAsync(guild);
public void RequestDownloadUsers(ulong guildId) => _background.UserDownloader.RequestDownload(guildId); public void RequestDownloadUsers(ulong guildId) => _background.UserDownloader.RequestDownload(guildId);
@ -166,7 +166,7 @@ namespace BirthdayBot
/// </summary> /// </summary>
private async Task Client_MessageReceived(SocketMessage msg) private async Task Client_MessageReceived(SocketMessage msg)
{ {
if (!(msg.Channel is SocketTextChannel channel)) return; if (msg.Channel is not SocketTextChannel channel) return;
if (msg.Author.IsBot || msg.Author.IsWebhook) return; if (msg.Author.IsBot || msg.Author.IsWebhook) return;
if (((IMessage)msg).Type != MessageType.Default) return; if (((IMessage)msg).Type != MessageType.Default) return;
var author = (SocketGuildUser)msg.Author; var author = (SocketGuildUser)msg.Author;
@ -178,7 +178,7 @@ namespace BirthdayBot
if (csplit.Length > 0 && csplit[0].StartsWith(CommandPrefix, StringComparison.OrdinalIgnoreCase)) if (csplit.Length > 0 && csplit[0].StartsWith(CommandPrefix, StringComparison.OrdinalIgnoreCase))
{ {
// Determine if it's something we're listening for. // Determine if it's something we're listening for.
if (!_dispatchCommands.TryGetValue(csplit[0].Substring(CommandPrefix.Length), out CommandHandler command)) return; if (!_dispatchCommands.TryGetValue(csplit[0][CommandPrefix.Length..], out CommandHandler command)) return;
// Load guild information here // Load guild information here
var gconf = await GuildConfiguration.LoadAsync(channel.Guild.Id, false); var gconf = await GuildConfiguration.LoadAsync(channel.Guild.Id, false);

View file

@ -39,7 +39,7 @@ namespace BirthdayBot
/// Amount of time without a completed background service run before a shard instance /// Amount of time without a completed background service run before a shard instance
/// is considered "dead" and tasked to be removed. /// is considered "dead" and tasked to be removed.
/// </summary> /// </summary>
private static readonly TimeSpan DeadShardThreshold = new TimeSpan(0, 20, 0); private static readonly TimeSpan DeadShardThreshold = new(0, 20, 0);
/// <summary> /// <summary>
/// A dictionary with shard IDs as its keys and shard instances as its values. /// A dictionary with shard IDs as its keys and shard instances as its values.
@ -132,7 +132,6 @@ namespace BirthdayBot
LogLevel = LogSeverity.Info, LogLevel = LogSeverity.Info,
DefaultRetryMode = RetryMode.RetryRatelimit, DefaultRetryMode = RetryMode.RetryRatelimit,
MessageCacheSize = 0, // not needed at all MessageCacheSize = 0, // not needed at all
ExclusiveBulkDelete = true, // not relevant, but this is configured to skip the warning
GatewayIntents = GatewayIntents.Guilds | GatewayIntents.GuildMembers | GatewayIntents.GuildMessages GatewayIntents = GatewayIntents.Guilds | GatewayIntents.GuildMembers | GatewayIntents.GuildMessages
}; };
var newClient = new DiscordSocketClient(clientConf); var newClient = new DiscordSocketClient(clientConf);

View file

@ -15,9 +15,9 @@ namespace BirthdayBot.UserInterface
{ {
var cmds = new List<string>(); var cmds = new List<string>();
foreach (var item in commands) cmds.Add(CommandsCommon.CommandPrefix + item); foreach (var item in commands) cmds.Add(CommandsCommon.CommandPrefix + item);
if (cmds.Count == 0) throw new ArgumentException(nameof(commands)); if (cmds.Count == 0) throw new ArgumentException(null, nameof(commands));
Commands = cmds.ToArray(); Commands = cmds.ToArray();
Usage = usage ?? throw new ArgumentException(nameof(usage)); Usage = usage ?? throw new ArgumentException(null, nameof(usage));
Examples = examples; Examples = examples;
} }

View file

@ -59,7 +59,7 @@ namespace BirthdayBot.UserInterface
/// <summary> /// <summary>
/// Checks given time zone input. Returns a valid string for use with NodaTime. /// Checks given time zone input. Returns a valid string for use with NodaTime.
/// </summary> /// </summary>
protected string ParseTimeZone(string tzinput) protected static string ParseTimeZone(string tzinput)
{ {
string tz = null; string tz = null;
if (tzinput != null) if (tzinput != null)
@ -80,16 +80,14 @@ namespace BirthdayBot.UserInterface
/// Given user input where a user-like parameter is expected, attempts to resolve to an ID value. /// Given user input where a user-like parameter is expected, attempts to resolve to an ID value.
/// Input must be a mention or explicit ID. No name resolution is done here. /// Input must be a mention or explicit ID. No name resolution is done here.
/// </summary> /// </summary>
protected bool TryGetUserId(string input, out ulong result) protected static bool TryGetUserId(string input, out ulong result)
{ {
string doParse; string doParse;
var m = UserMention.Match(input); var m = UserMention.Match(input);
if (m.Success) doParse = m.Groups[1].Value; if (m.Success) doParse = m.Groups[1].Value;
else doParse = input; else doParse = input;
ulong resultVal; if (ulong.TryParse(doParse, out ulong resultVal)) {
if (ulong.TryParse(doParse, out resultVal))
{
result = resultVal; result = resultVal;
return true; return true;
} }

View file

@ -33,7 +33,7 @@ namespace BirthdayBot.UserInterface
("invite", CmdInfo) ("invite", CmdInfo)
}; };
private (Embed, Embed) BuildHelpEmbeds() private static (Embed, Embed) BuildHelpEmbeds()
{ {
var cpfx = $"●`{CommandPrefix}"; var cpfx = $"●`{CommandPrefix}";

View file

@ -27,12 +27,12 @@ namespace BirthdayBot.UserInterface
#region Documentation #region Documentation
public static readonly CommandDocumentation DocList = public static readonly CommandDocumentation DocList =
new CommandDocumentation(new string[] { "list" }, "Exports all birthdays to a file." new(new string[] { "list" }, "Exports all birthdays to a file."
+ " Accepts `csv` as an optional parameter.", null); + " Accepts `csv` as an optional parameter.", null);
public static readonly CommandDocumentation DocUpcoming = public static readonly CommandDocumentation DocUpcoming =
new CommandDocumentation(new string[] { "recent", "upcoming" }, "Lists recent and upcoming birthdays.", null); new(new string[] { "recent", "upcoming" }, "Lists recent and upcoming birthdays.", null);
public static readonly CommandDocumentation DocWhen = public static readonly CommandDocumentation DocWhen =
new CommandDocumentation(new string[] { "when" }, "Displays the given user's birthday information.", null); new(new string[] { "when" }, "Displays the given user's birthday information.", null);
#endregion #endregion
private async Task CmdWhen(ShardInstance instance, GuildConfiguration gconf, private async Task CmdWhen(ShardInstance instance, GuildConfiguration gconf,
@ -202,7 +202,7 @@ namespace BirthdayBot.UserInterface
search += 1; search += 1;
if (search > 366) search = 1; // wrap to beginning of year if (search > 366) search = 1; // wrap to beginning of year
if (results.Count() == 0) continue; // back out early if (!results.Any()) continue; // back out early
resultCount += results.Count(); resultCount += results.Count();
// Build sorted name list // Build sorted name list
@ -245,7 +245,7 @@ namespace BirthdayBot.UserInterface
/// Fetches all guild birthdays and places them into an easily usable structure. /// Fetches all guild birthdays and places them into an easily usable structure.
/// Users currently not in the guild are not included in the result. /// Users currently not in the guild are not included in the result.
/// </summary> /// </summary>
private async Task<List<ListItem>> GetSortedUsersAsync(SocketGuild guild) private static async Task<List<ListItem>> GetSortedUsersAsync(SocketGuild guild)
{ {
using var db = await Database.OpenConnectionAsync(); using var db = await Database.OpenConnectionAsync();
using var c = db.CreateCommand(); using var c = db.CreateCommand();
@ -323,7 +323,7 @@ namespace BirthdayBot.UserInterface
return result.ToString(); return result.ToString();
} }
private string CsvEscape(string input) private static string CsvEscape(string input)
{ {
var result = new StringBuilder(); var result = new StringBuilder();
result.Append('"'); result.Append('"');
@ -336,7 +336,7 @@ namespace BirthdayBot.UserInterface
return result.ToString(); return result.ToString();
} }
private int DateIndex(int month, int day) private static int DateIndex(int month, int day)
{ {
var dateindex = 0; var dateindex = 0;
// Add month offsets // Add month offsets

View file

@ -48,7 +48,7 @@ namespace BirthdayBot.UserInterface
#region Documentation #region Documentation
public static readonly CommandDocumentation DocOverride = public static readonly CommandDocumentation DocOverride =
new CommandDocumentation(new string[] { "override (user ping or ID) (command w/ parameters)" }, new(new string[] { "override (user ping or ID) (command w/ parameters)" },
"Perform certain commands on behalf of another user.", null); "Perform certain commands on behalf of another user.", null);
#endregion #endregion
@ -426,7 +426,7 @@ namespace BirthdayBot.UserInterface
if (cmdsearch.StartsWith(CommandPrefix)) if (cmdsearch.StartsWith(CommandPrefix))
{ {
// Strip command prefix to search for the given command. // Strip command prefix to search for the given command.
cmdsearch = cmdsearch.Substring(CommandPrefix.Length); cmdsearch = cmdsearch[CommandPrefix.Length..];
} }
else else
{ {
@ -472,7 +472,7 @@ namespace BirthdayBot.UserInterface
var guild = reqChannel.Guild; var guild = reqChannel.Guild;
string result = $"\nServer ID: {guild.Id} | Bot shard ID: {instance.ShardId:00}"; string result = $"\nServer ID: {guild.Id} | Bot shard ID: {instance.ShardId:00}";
result += $"\nLocally cached members: {guild.DownloadedMemberCount} out of {guild.MemberCount}"; result += $"\nLocally cached members: {guild.DownloadedMemberCount} out of {guild.MemberCount}";
result += "\n" + await instance.ForceBirthdayUpdateAsync(guild).ConfigureAwait(false); result += "\n" + await ShardInstance.ForceBirthdayUpdateAsync(guild).ConfigureAwait(false);
await reqChannel.SendMessageAsync(result).ConfigureAwait(false); await reqChannel.SendMessageAsync(result).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
@ -485,9 +485,9 @@ namespace BirthdayBot.UserInterface
#region Common/helper methods #region Common/helper methods
private const string RoleInputError = ":x: Unable to determine the given role."; private const string RoleInputError = ":x: Unable to determine the given role.";
private static readonly Regex RoleMention = new Regex(@"<@?&(?<snowflake>\d+)>", RegexOptions.Compiled); private static readonly Regex RoleMention = new(@"<@?&(?<snowflake>\d+)>", RegexOptions.Compiled);
private SocketRole FindUserInputRole(string inputStr, SocketGuild guild) private static SocketRole FindUserInputRole(string inputStr, SocketGuild guild)
{ {
// Resembles a role mention? Strip it to the pure number // Resembles a role mention? Strip it to the pure number
var input = inputStr; var input = inputStr;

View file

@ -23,8 +23,8 @@ namespace BirthdayBot.UserInterface
const string FormatError = ":x: Unrecognized date format. The following formats are accepted, as examples: " const string FormatError = ":x: Unrecognized date format. The following formats are accepted, as examples: "
+ "`15-jan`, `jan-15`, `15 jan`, `jan 15`, `15 January`, `January 15`."; + "`15-jan`, `jan-15`, `15 jan`, `jan 15`, `15 January`, `January 15`.";
private static readonly Regex DateParse1 = new Regex(@"^(?<day>\d{1,2})[ -](?<month>[A-Za-z]+)$", RegexOptions.Compiled); private static readonly Regex DateParse1 = new(@"^(?<day>\d{1,2})[ -](?<month>[A-Za-z]+)$", RegexOptions.Compiled);
private static readonly Regex DateParse2 = new Regex(@"^(?<month>[A-Za-z]+)[ -](?<day>\d{1,2})$", RegexOptions.Compiled); private static readonly Regex DateParse2 = new(@"^(?<month>[A-Za-z]+)[ -](?<day>\d{1,2})$", RegexOptions.Compiled);
/// <summary> /// <summary>
/// Parses a date input. /// Parses a date input.
@ -33,7 +33,7 @@ namespace BirthdayBot.UserInterface
/// <exception cref="FormatException"> /// <exception cref="FormatException">
/// Thrown for any parsing issue. Reason is expected to be sent to Discord as-is. /// Thrown for any parsing issue. Reason is expected to be sent to Discord as-is.
/// </exception> /// </exception>
private (int, int) ParseDate(string dateInput) private static (int, int) ParseDate(string dateInput)
{ {
var m = DateParse1.Match(dateInput); var m = DateParse1.Match(dateInput);
if (!m.Success) if (!m.Success)
@ -71,60 +71,35 @@ namespace BirthdayBot.UserInterface
/// <exception cref="FormatException"> /// <exception cref="FormatException">
/// Thrown on error. Send out to Discord as-is. /// Thrown on error. Send out to Discord as-is.
/// </exception> /// </exception>
private (int, int) GetMonth(string input) private static (int, int) GetMonth(string input)
{ {
switch (input.ToLower()) return input.ToLower() switch {
{ "jan" or "january" => (1, 31),
case "jan": "feb" or "february" => (2, 29),
case "january": "mar" or "march" => (3, 31),
return (1, 31); "apr" or "april" => (4, 30),
case "feb": "may" => (5, 31),
case "february": "jun" or "june" => (6, 30),
return (2, 29); "jul" or "july" => (7, 31),
case "mar": "aug" or "august" => (8, 31),
case "march": "sep" or "september" => (9, 30),
return (3, 31); "oct" or "october" => (10, 31),
case "apr": "nov" or "november" => (11, 30),
case "april": "dec" or "december" => (12, 31),
return (4, 30); _ => throw new FormatException($":x: Can't determine month name `{input}`. Check your spelling and try again."),
case "may": };
return (5, 31);
case "jun":
case "june":
return (6, 30);
case "jul":
case "july":
return (7, 31);
case "aug":
case "august":
return (8, 31);
case "sep":
case "september":
return (9, 30);
case "oct":
case "october":
return (10, 31);
case "nov":
case "november":
return (11, 30);
case "dec":
case "december":
return (12, 31);
default:
throw new FormatException($":x: Can't determine month name `{input}`. Check your spelling and try again.");
}
} }
#endregion #endregion
#region Documentation #region Documentation
public static readonly CommandDocumentation DocSet = public static readonly CommandDocumentation DocSet =
new CommandDocumentation(new string[] { "set (date)" }, "Registers your birth month and day.", new(new string[] { "set (date)" }, "Registers your birth month and day.",
$"`{CommandPrefix}set jan-31`, `{CommandPrefix}set 15 may`."); $"`{CommandPrefix}set jan-31`, `{CommandPrefix}set 15 may`.");
public static readonly CommandDocumentation DocZone = public static readonly CommandDocumentation DocZone =
new CommandDocumentation(new string[] { "zone (zone)" }, "Sets your local time zone. " new(new string[] { "zone (zone)" }, "Sets your local time zone. "
+ $"See also `{CommandPrefix}help-tzdata`.", null); + $"See also `{CommandPrefix}help-tzdata`.", null);
public static readonly CommandDocumentation DocRemove = public static readonly CommandDocumentation DocRemove =
new CommandDocumentation(new string[] { "remove" }, "Removes your birthday information from this bot.", null); new(new string[] { "remove" }, "Removes your birthday information from this bot.", null);
#endregion #endregion
private async Task CmdSet(ShardInstance instance, GuildConfiguration gconf, private async Task CmdSet(ShardInstance instance, GuildConfiguration gconf,