mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-24 01:14:12 +00:00
Apply suggestions from code analysis
This commit is contained in:
parent
7af571205a
commit
dcb9bfdd9a
14 changed files with 74 additions and 103 deletions
|
@ -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.";
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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) { }
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}";
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue