mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-24 01:14:12 +00:00
Replace test with check command
Removes some messy code as a result of the old feature's removal. Additionally, updated code style in ManagerCommands and fixed several nullable issues.
This commit is contained in:
parent
f4c7837759
commit
0c56a0859a
3 changed files with 421 additions and 552 deletions
|
@ -41,48 +41,33 @@ class BirthdayRoleUpdate : BackgroundService {
|
||||||
if (exs.Count != 0) throw new AggregateException(exs);
|
if (exs.Count != 0) throw new AggregateException(exs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Access to <see cref="ProcessGuildAsync(SocketGuild)"/> for the testing command.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Diagnostic data in string form.</returns>
|
|
||||||
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 static async Task<PGDiagnostic> ProcessGuildAsync(SocketGuild guild) {
|
private static async Task ProcessGuildAsync(SocketGuild guild) {
|
||||||
var diag = new PGDiagnostic();
|
|
||||||
|
|
||||||
// Load guild information - stop if local cache is unavailable.
|
// Load guild information - stop if local cache is unavailable.
|
||||||
if (!Common.HasMostMembersDownloaded(guild)) return diag;
|
if (!Common.HasMostMembersDownloaded(guild)) return;
|
||||||
var gc = await GuildConfiguration.LoadAsync(guild.Id, true).ConfigureAwait(false);
|
var gc = await GuildConfiguration.LoadAsync(guild.Id, true).ConfigureAwait(false);
|
||||||
if (gc == null) return diag;
|
if (gc == null) return;
|
||||||
|
|
||||||
// Check if role settings are correct before continuing with further processing
|
// Check if role settings are correct before continuing with further processing
|
||||||
SocketRole role = null;
|
SocketRole? role = guild.GetRole(gc.RoleId ?? 0);
|
||||||
if (gc.RoleId.HasValue) role = guild.GetRole(gc.RoleId.Value);
|
if (role == null || !guild.CurrentUser.GuildPermissions.ManageRoles || role.Position >= guild.CurrentUser.Hierarchy) return;
|
||||||
diag.RoleCheck = CheckCorrectRoleSettings(guild, role);
|
|
||||||
if (diag.RoleCheck != null) return diag;
|
|
||||||
|
|
||||||
// Determine who's currently having a birthday
|
// Determine who's currently having a birthday
|
||||||
var users = await GuildUserConfiguration.LoadAllAsync(guild.Id).ConfigureAwait(false);
|
var users = await GuildUserConfiguration.LoadAllAsync(guild.Id).ConfigureAwait(false);
|
||||||
var tz = gc.TimeZone;
|
var tz = gc.TimeZone;
|
||||||
var birthdays = GetGuildCurrentBirthdays(users, tz);
|
var birthdays = GetGuildCurrentBirthdays(users, tz);
|
||||||
// Note: Don't quit here if zero people are having birthdays. Roles may still need to be removed by BirthdayApply.
|
// Note: Don't quit here if zero people are having birthdays. Roles may still need to be removed by BirthdayApply.
|
||||||
diag.CurrentBirthdays = birthdays.Count.ToString();
|
|
||||||
|
|
||||||
IEnumerable<SocketGuildUser> announcementList;
|
IEnumerable<SocketGuildUser> announcementList;
|
||||||
// Update roles as appropriate
|
// Update roles as appropriate
|
||||||
try {
|
try {
|
||||||
var updateResult = await UpdateGuildBirthdayRoles(guild, role, birthdays).ConfigureAwait(false);
|
var updateResult = await UpdateGuildBirthdayRoles(guild, role, birthdays).ConfigureAwait(false);
|
||||||
announcementList = updateResult.Item1;
|
announcementList = updateResult.Item1;
|
||||||
diag.RoleApplyResult = updateResult.Item2; // statistics
|
} catch (Discord.Net.HttpException) {
|
||||||
} catch (Discord.Net.HttpException ex) {
|
return;
|
||||||
diag.RoleApply = ex.Message;
|
|
||||||
return diag;
|
|
||||||
}
|
}
|
||||||
diag.RoleApply = null;
|
|
||||||
|
|
||||||
// Birthday announcement
|
// Birthday announcement
|
||||||
var announce = gc.AnnounceMessages;
|
var announce = gc.AnnounceMessages;
|
||||||
|
@ -90,32 +75,8 @@ class BirthdayRoleUpdate : BackgroundService {
|
||||||
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.Any()) {
|
if (announcementList.Any()) {
|
||||||
var announceResult =
|
|
||||||
await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList).ConfigureAwait(false);
|
await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList).ConfigureAwait(false);
|
||||||
diag.Announcement = announceResult;
|
|
||||||
} else {
|
|
||||||
diag.Announcement = "No new role additions. Announcement not needed.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return diag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the bot may be allowed to alter roles.
|
|
||||||
/// </summary>
|
|
||||||
private static string CheckCorrectRoleSettings(SocketGuild guild, SocketRole role) {
|
|
||||||
if (role == null) return "Birthday role is not configured, or has gone missing.";
|
|
||||||
|
|
||||||
if (!guild.CurrentUser.GuildPermissions.ManageRoles) {
|
|
||||||
return "Bot does not have the 'Manage Roles' permission.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check potential role order conflict
|
|
||||||
if (role.Position >= guild.CurrentUser.Hierarchy) {
|
|
||||||
return "Can't access the birthday role. Is it above the bot's permissions?";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -229,28 +190,4 @@ class BirthdayRoleUpdate : BackgroundService {
|
||||||
return ex.Message;
|
return ex.Message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PGDiagnostic {
|
|
||||||
const string DefaultValue = "--";
|
|
||||||
|
|
||||||
public string RoleCheck = DefaultValue;
|
|
||||||
public string CurrentBirthdays = DefaultValue;
|
|
||||||
public string RoleApply = DefaultValue;
|
|
||||||
public (int, int)? RoleApplyResult;
|
|
||||||
public string Announcement = DefaultValue;
|
|
||||||
|
|
||||||
public string Export() {
|
|
||||||
var result = new StringBuilder();
|
|
||||||
result.AppendLine("Test result:");
|
|
||||||
result.AppendLine("Check role permissions: " + (RoleCheck ?? ":white_check_mark:"));
|
|
||||||
result.AppendLine("Number of known users currently with a birthday: " + CurrentBirthdays);
|
|
||||||
result.AppendLine("Role application process: " + (RoleApply ?? ":white_check_mark:"));
|
|
||||||
result.Append("Role application metrics: ");
|
|
||||||
if (RoleApplyResult.HasValue) result.AppendLine($"{RoleApplyResult.Value.Item1} additions, {RoleApplyResult.Value.Item2} removals.");
|
|
||||||
else result.AppendLine(DefaultValue);
|
|
||||||
result.AppendLine("Announcement: " + (Announcement ?? ":white_check_mark:"));
|
|
||||||
|
|
||||||
return result.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,12 +95,6 @@ namespace BirthdayBot
|
||||||
|
|
||||||
public void Log(string source, string message) => Program.Log($"Shard {ShardId:00}] [{source}", message);
|
public void Log(string source, string message) => Program.Log($"Shard {ShardId:00}] [{source}", message);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Direct access to invoke the background task of updating birthdays in a guild, for use by the testing command.
|
|
||||||
/// </summary>
|
|
||||||
public static Task<string> ForceBirthdayUpdateAsync(SocketGuild guild)
|
|
||||||
=> BirthdayRoleUpdate.SingleProcessGuildAsync(guild);
|
|
||||||
|
|
||||||
public void RequestDownloadUsers(ulong guildId) => _background.UserDownloader.RequestDownload(guildId);
|
public void RequestDownloadUsers(ulong guildId) => _background.UserDownloader.RequestDownload(guildId);
|
||||||
|
|
||||||
#region Event handling
|
#region Event handling
|
||||||
|
|
|
@ -3,13 +3,13 @@ using Discord.WebSocket;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BirthdayBot.UserInterface
|
namespace BirthdayBot.UserInterface;
|
||||||
{
|
|
||||||
internal class ManagerCommands : CommandsCommon
|
internal class ManagerCommands : CommandsCommon {
|
||||||
{
|
|
||||||
private static readonly string ConfErrorPostfix =
|
private static readonly string ConfErrorPostfix =
|
||||||
$" Refer to the `{CommandPrefix}help-config` command for information on this command's usage.";
|
$" Refer to the `{CommandPrefix}help-config` command for information on this command's usage.";
|
||||||
private delegate Task ConfigSubcommand(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel);
|
private delegate Task ConfigSubcommand(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel);
|
||||||
|
@ -17,8 +17,7 @@ namespace BirthdayBot.UserInterface
|
||||||
private readonly Dictionary<string, ConfigSubcommand> _subcommands;
|
private readonly Dictionary<string, ConfigSubcommand> _subcommands;
|
||||||
private readonly Dictionary<string, CommandHandler> _usercommands;
|
private readonly Dictionary<string, CommandHandler> _usercommands;
|
||||||
|
|
||||||
public ManagerCommands(Configuration db, IEnumerable<(string, CommandHandler)> userCommands) : base(db)
|
public ManagerCommands(Configuration db, IEnumerable<(string, CommandHandler)> userCommands) : base(db) {
|
||||||
{
|
|
||||||
_subcommands = new Dictionary<string, ConfigSubcommand>(StringComparer.OrdinalIgnoreCase)
|
_subcommands = new Dictionary<string, ConfigSubcommand>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{ "role", ScmdRole },
|
{ "role", ScmdRole },
|
||||||
|
@ -43,7 +42,7 @@ namespace BirthdayBot.UserInterface
|
||||||
{
|
{
|
||||||
("config", CmdConfigDispatch),
|
("config", CmdConfigDispatch),
|
||||||
("override", CmdOverride),
|
("override", CmdOverride),
|
||||||
("test", CmdTest)
|
("check", CmdCheck)
|
||||||
};
|
};
|
||||||
|
|
||||||
#region Documentation
|
#region Documentation
|
||||||
|
@ -53,25 +52,21 @@ namespace BirthdayBot.UserInterface
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private async Task CmdConfigDispatch(ShardInstance instance, GuildConfiguration gconf,
|
private async Task CmdConfigDispatch(ShardInstance instance, GuildConfiguration gconf,
|
||||||
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser) {
|
||||||
{
|
|
||||||
// Ignore those without the proper permissions.
|
// Ignore those without the proper permissions.
|
||||||
if (!gconf.IsBotModerator(reqUser))
|
if (!gconf.IsBotModerator(reqUser)) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: This command may only be used by bot moderators.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":x: This command may only be used by bot moderators.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.Length < 2)
|
if (param.Length < 2) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync($":x: See `{CommandPrefix}help-config` for information on how to use this command.")
|
await reqChannel.SendMessageAsync($":x: See `{CommandPrefix}help-config` for information on how to use this command.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case: Restrict 'modrole' to only guild managers, not mods
|
// Special case: Restrict 'modrole' to only guild managers, not mods
|
||||||
if (string.Equals(param[1], "modrole", StringComparison.OrdinalIgnoreCase) && !reqUser.GuildPermissions.ManageGuild)
|
if (string.Equals(param[1], "modrole", StringComparison.OrdinalIgnoreCase) && !reqUser.GuildPermissions.ManageGuild) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: This command may only be used by those with the `Manage Server` permission.")
|
await reqChannel.SendMessageAsync(":x: This command may only be used by those with the `Manage Server` permission.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
@ -81,18 +76,15 @@ namespace BirthdayBot.UserInterface
|
||||||
var confparam = new string[param.Length - 1];
|
var confparam = new string[param.Length - 1];
|
||||||
Array.Copy(param, 1, confparam, 0, param.Length - 1);
|
Array.Copy(param, 1, confparam, 0, param.Length - 1);
|
||||||
|
|
||||||
if (_subcommands.TryGetValue(confparam[0], out ConfigSubcommand h))
|
if (_subcommands.TryGetValue(confparam[0], out var h)) {
|
||||||
{
|
|
||||||
await h(confparam, gconf, reqChannel).ConfigureAwait(false);
|
await h(confparam, gconf, reqChannel).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Configuration sub-commands
|
#region Configuration sub-commands
|
||||||
// Birthday role set
|
// Birthday role set
|
||||||
private async Task ScmdRole(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdRole(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
if (param.Length != 2) {
|
||||||
if (param.Length != 2)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: A role name, role mention, or ID value must be specified.")
|
await reqChannel.SendMessageAsync(":x: A role name, role mention, or ID value must be specified.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
@ -100,16 +92,11 @@ namespace BirthdayBot.UserInterface
|
||||||
var guild = reqChannel.Guild;
|
var guild = reqChannel.Guild;
|
||||||
var role = FindUserInputRole(param[1], guild);
|
var role = FindUserInputRole(param[1], guild);
|
||||||
|
|
||||||
if (role == null)
|
if (role == null) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(RoleInputError).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(RoleInputError).ConfigureAwait(false);
|
||||||
}
|
} else if (role.Id == reqChannel.Guild.EveryoneRole.Id) {
|
||||||
else if (role.Id == reqChannel.Guild.EveryoneRole.Id)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: You cannot set that as the birthday role.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":x: You cannot set that as the birthday role.").ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
gconf.RoleId = role.Id;
|
gconf.RoleId = role.Id;
|
||||||
await gconf.UpdateAsync().ConfigureAwait(false);
|
await gconf.UpdateAsync().ConfigureAwait(false);
|
||||||
await reqChannel.SendMessageAsync($":white_check_mark: The birthday role has been set as **{role.Name}**.")
|
await reqChannel.SendMessageAsync($":white_check_mark: The birthday role has been set as **{role.Name}**.")
|
||||||
|
@ -118,11 +105,9 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ping setting
|
// Ping setting
|
||||||
private async Task ScmdPing(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdPing(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
|
||||||
const string InputErr = ":x: You must specify either `off` or `on` in this setting.";
|
const string InputErr = ":x: You must specify either `off` or `on` in this setting.";
|
||||||
if (param.Length != 2)
|
if (param.Length != 2) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(InputErr).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(InputErr).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -130,18 +115,13 @@ namespace BirthdayBot.UserInterface
|
||||||
var input = param[1].ToLower();
|
var input = param[1].ToLower();
|
||||||
bool setting;
|
bool setting;
|
||||||
string result;
|
string result;
|
||||||
if (input == "off")
|
if (input == "off") {
|
||||||
{
|
|
||||||
setting = false;
|
setting = false;
|
||||||
result = ":white_check_mark: Announcement pings are now **off**.";
|
result = ":white_check_mark: Announcement pings are now **off**.";
|
||||||
}
|
} else if (input == "on") {
|
||||||
else if (input == "on")
|
|
||||||
{
|
|
||||||
setting = true;
|
setting = true;
|
||||||
result = ":white_check_mark: Announcement pings are now **on**.";
|
result = ":white_check_mark: Announcement pings are now **on**.";
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(InputErr).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(InputErr).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -152,13 +132,11 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Announcement channel set
|
// Announcement channel set
|
||||||
private async Task ScmdChannel(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdChannel(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
|
||||||
if (param.Length == 1) // No extra parameter. Unset announcement channel.
|
if (param.Length == 1) // No extra parameter. Unset announcement channel.
|
||||||
{
|
{
|
||||||
// Extra detail: Show a unique message if a channel hadn't been set prior.
|
// Extra detail: Show a unique message if a channel hadn't been set prior.
|
||||||
if (!gconf.AnnounceChannelId.HasValue)
|
if (!gconf.AnnounceChannelId.HasValue) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: There is no announcement channel set. Nothing to unset.")
|
await reqChannel.SendMessageAsync(":x: There is no announcement channel set. Nothing to unset.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
@ -168,38 +146,29 @@ namespace BirthdayBot.UserInterface
|
||||||
await gconf.UpdateAsync();
|
await gconf.UpdateAsync();
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: The announcement channel has been unset.")
|
await reqChannel.SendMessageAsync(":white_check_mark: The announcement channel has been unset.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Determine channel from input
|
// Determine channel from input
|
||||||
ulong chId = 0;
|
ulong chId = 0;
|
||||||
|
|
||||||
// Try channel mention
|
// Try channel mention
|
||||||
var m = ChannelMention.Match(param[1]);
|
var m = ChannelMention.Match(param[1]);
|
||||||
if (m.Success)
|
if (m.Success) {
|
||||||
{
|
|
||||||
chId = ulong.Parse(m.Groups[1].Value);
|
chId = ulong.Parse(m.Groups[1].Value);
|
||||||
}
|
} else if (ulong.TryParse(param[1], out chId)) {
|
||||||
else if (ulong.TryParse(param[1], out chId))
|
|
||||||
{
|
|
||||||
// Continue...
|
// Continue...
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try text-based search
|
// Try text-based search
|
||||||
var res = reqChannel.Guild.TextChannels
|
var res = reqChannel.Guild.TextChannels
|
||||||
.FirstOrDefault(ch => string.Equals(ch.Name, param[1], StringComparison.OrdinalIgnoreCase));
|
.FirstOrDefault(ch => string.Equals(ch.Name, param[1], StringComparison.OrdinalIgnoreCase));
|
||||||
if (res != null)
|
if (res != null) {
|
||||||
{
|
|
||||||
chId = res.Id; // Yep, we're throwing the full result away only to go look for it again later...
|
chId = res.Id; // Yep, we're throwing the full result away only to go look for it again later...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to find channel in guild
|
// Attempt to find channel in guild
|
||||||
SocketTextChannel chTt = null;
|
SocketTextChannel? chTt = null;
|
||||||
if (chId != 0) chTt = reqChannel.Guild.GetTextChannel(chId);
|
if (chId != 0) chTt = reqChannel.Guild.GetTextChannel(chId);
|
||||||
if (chTt == null)
|
if (chTt == null) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: Unable to find the specified channel.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":x: Unable to find the specified channel.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -215,10 +184,8 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moderator role set
|
// Moderator role set
|
||||||
private async Task ScmdModRole(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdModRole(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
if (param.Length != 2) {
|
||||||
if (param.Length != 2)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: A role name, role mention, or ID value must be specified.")
|
await reqChannel.SendMessageAsync(":x: A role name, role mention, or ID value must be specified.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
@ -226,12 +193,9 @@ namespace BirthdayBot.UserInterface
|
||||||
var guild = reqChannel.Guild;
|
var guild = reqChannel.Guild;
|
||||||
var role = FindUserInputRole(param[1], guild);
|
var role = FindUserInputRole(param[1], guild);
|
||||||
|
|
||||||
if (role == null)
|
if (role == null) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(RoleInputError).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(RoleInputError).ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
gconf.ModeratorRole = role.Id;
|
gconf.ModeratorRole = role.Id;
|
||||||
await gconf.UpdateAsync().ConfigureAwait(false);
|
await gconf.UpdateAsync().ConfigureAwait(false);
|
||||||
await reqChannel.SendMessageAsync($":white_check_mark: The moderator role is now **{role.Name}**.")
|
await reqChannel.SendMessageAsync($":white_check_mark: The moderator role is now **{role.Name}**.")
|
||||||
|
@ -240,13 +204,11 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guild default time zone set/unset
|
// Guild default time zone set/unset
|
||||||
private async Task ScmdZone(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdZone(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
|
||||||
if (param.Length == 1) // No extra parameter. Unset guild default time zone.
|
if (param.Length == 1) // No extra parameter. Unset guild default time zone.
|
||||||
{
|
{
|
||||||
// Extra detail: Show a unique message if there is no set zone.
|
// Extra detail: Show a unique message if there is no set zone.
|
||||||
if (!gconf.AnnounceChannelId.HasValue)
|
if (!gconf.AnnounceChannelId.HasValue) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: A default zone is not set. Nothing to unset.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":x: A default zone is not set. Nothing to unset.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -255,17 +217,12 @@ namespace BirthdayBot.UserInterface
|
||||||
await gconf.UpdateAsync().ConfigureAwait(false);
|
await gconf.UpdateAsync().ConfigureAwait(false);
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: The default time zone preference has been removed.")
|
await reqChannel.SendMessageAsync(":white_check_mark: The default time zone preference has been removed.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Parameter check.
|
// Parameter check.
|
||||||
string zone;
|
string zone;
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
zone = ParseTimeZone(param[1]);
|
zone = ParseTimeZone(param[1]);
|
||||||
}
|
} catch (FormatException ex) {
|
||||||
catch (FormatException ex)
|
|
||||||
{
|
|
||||||
reqChannel.SendMessageAsync(ex.Message).Wait();
|
reqChannel.SendMessageAsync(ex.Message).Wait();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -281,54 +238,40 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block/unblock individual non-manager users from using commands.
|
// Block/unblock individual non-manager users from using commands.
|
||||||
private async Task ScmdBlock(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdBlock(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
if (param.Length != 2) {
|
||||||
if (param.Length != 2)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(ParameterError + ConfErrorPostfix).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(ParameterError + ConfErrorPostfix).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool doBan = param[0].ToLower() == "block"; // true = block, false = unblock
|
bool doBan = param[0].ToLower() == "block"; // true = block, false = unblock
|
||||||
|
|
||||||
if (!TryGetUserId(param[1], out ulong inputId))
|
if (!TryGetUserId(param[1], out ulong inputId)) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(BadUserError).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(BadUserError).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isBanned = await gconf.IsUserBlockedAsync(inputId).ConfigureAwait(false);
|
var isBanned = await gconf.IsUserBlockedAsync(inputId).ConfigureAwait(false);
|
||||||
if (doBan)
|
if (doBan) {
|
||||||
{
|
if (!isBanned) {
|
||||||
if (!isBanned)
|
|
||||||
{
|
|
||||||
await gconf.BlockUserAsync(inputId).ConfigureAwait(false);
|
await gconf.BlockUserAsync(inputId).ConfigureAwait(false);
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: User has been blocked.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":white_check_mark: User has been blocked.").ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO bug: this is incorrectly always displayed when in moderated mode
|
// TODO bug: this is incorrectly always displayed when in moderated mode
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: User is already blocked.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":white_check_mark: User is already blocked.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
if (await gconf.UnblockUserAsync(inputId).ConfigureAwait(false)) {
|
||||||
{
|
|
||||||
if (await gconf.UnblockUserAsync(inputId).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: User is now unblocked.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":white_check_mark: User is now unblocked.").ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":white_check_mark: The specified user is not blocked.").ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(":white_check_mark: The specified user is not blocked.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "moderated on/off" - Sets/unsets moderated mode.
|
// "moderated on/off" - Sets/unsets moderated mode.
|
||||||
private async Task ScmdModerated(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdModerated(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
if (param.Length != 2) {
|
||||||
if (param.Length != 2)
|
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(ParameterError + ConfErrorPostfix).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(ParameterError + ConfErrorPostfix).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -337,20 +280,16 @@ namespace BirthdayBot.UserInterface
|
||||||
bool modSet;
|
bool modSet;
|
||||||
if (parameter == "on") modSet = true;
|
if (parameter == "on") modSet = true;
|
||||||
else if (parameter == "off") modSet = false;
|
else if (parameter == "off") modSet = false;
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(":x: Expecting `on` or `off` as a parameter." + ConfErrorPostfix)
|
await reqChannel.SendMessageAsync(":x: Expecting `on` or `off` as a parameter." + ConfErrorPostfix)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gconf.IsModerated == modSet)
|
if (gconf.IsModerated == modSet) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode is already {parameter}.")
|
await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode is already {parameter}.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
gconf.IsModerated = modSet;
|
gconf.IsModerated = modSet;
|
||||||
await gconf.UpdateAsync().ConfigureAwait(false);
|
await gconf.UpdateAsync().ConfigureAwait(false);
|
||||||
await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode has been turned {parameter}.")
|
await reqChannel.SendMessageAsync($":white_check_mark: Moderated mode has been turned {parameter}.")
|
||||||
|
@ -359,24 +298,20 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets/unsets custom announcement message.
|
// Sets/unsets custom announcement message.
|
||||||
private async Task ScmdAnnounceMsg(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel)
|
private async Task ScmdAnnounceMsg(string[] param, GuildConfiguration gconf, SocketTextChannel reqChannel) {
|
||||||
{
|
|
||||||
var plural = param[0].ToLower().EndsWith("pl");
|
var plural = param[0].ToLower().EndsWith("pl");
|
||||||
|
|
||||||
string newmsg;
|
string? newmsg;
|
||||||
bool clear;
|
bool clear;
|
||||||
if (param.Length == 2)
|
if (param.Length == 2) {
|
||||||
{
|
|
||||||
newmsg = param[1];
|
newmsg = param[1];
|
||||||
clear = false;
|
clear = false;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
newmsg = null;
|
newmsg = null;
|
||||||
clear = true;
|
clear = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
(string, string) update;
|
(string?, string?) update;
|
||||||
if (!plural) update = (newmsg, gconf.AnnounceMessages.Item2);
|
if (!plural) update = (newmsg, gconf.AnnounceMessages.Item2);
|
||||||
else update = (gconf.AnnounceMessages.Item1, newmsg);
|
else update = (gconf.AnnounceMessages.Item1, newmsg);
|
||||||
gconf.AnnounceMessages = update;
|
gconf.AnnounceMessages = update;
|
||||||
|
@ -388,33 +323,28 @@ namespace BirthdayBot.UserInterface
|
||||||
|
|
||||||
// Execute command as another user
|
// Execute command as another user
|
||||||
private async Task CmdOverride(ShardInstance instance, GuildConfiguration gconf,
|
private async Task CmdOverride(ShardInstance instance, GuildConfiguration gconf,
|
||||||
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser) {
|
||||||
{
|
|
||||||
// Moderators only. As with config, silently drop if this check fails.
|
// Moderators only. As with config, silently drop if this check fails.
|
||||||
if (!gconf.IsBotModerator(reqUser)) return;
|
if (!gconf.IsBotModerator(reqUser)) return;
|
||||||
|
|
||||||
if (!Common.HasMostMembersDownloaded(reqChannel.Guild))
|
if (!Common.HasMostMembersDownloaded(reqChannel.Guild)) {
|
||||||
{
|
|
||||||
instance.RequestDownloadUsers(reqChannel.Guild.Id);
|
instance.RequestDownloadUsers(reqChannel.Guild.Id);
|
||||||
await reqChannel.SendMessageAsync(UsersNotDownloadedError);
|
await reqChannel.SendMessageAsync(UsersNotDownloadedError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.Length != 3)
|
if (param.Length != 3) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(ParameterError, embed: DocOverride.UsageEmbed).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(ParameterError, embed: DocOverride.UsageEmbed).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second parameter: determine the user to act as
|
// Second parameter: determine the user to act as
|
||||||
if (!TryGetUserId(param[1], out ulong user))
|
if (!TryGetUserId(param[1], out ulong user)) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(BadUserError, embed: DocOverride.UsageEmbed).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(BadUserError, embed: DocOverride.UsageEmbed).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var overuser = reqChannel.Guild.GetUser(user);
|
var overuser = reqChannel.Guild.GetUser(user);
|
||||||
if (overuser == null)
|
if (overuser == null) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(BadUserError, embed: DocOverride.UsageEmbed).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(BadUserError, embed: DocOverride.UsageEmbed).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -423,18 +353,14 @@ namespace BirthdayBot.UserInterface
|
||||||
// Reminder that we're only receiving a param array of size 3 at maximum. String must be split again.
|
// Reminder that we're only receiving a param array of size 3 at maximum. String must be split again.
|
||||||
var overparam = param[2].Split(" ", 3, StringSplitOptions.RemoveEmptyEntries);
|
var overparam = param[2].Split(" ", 3, StringSplitOptions.RemoveEmptyEntries);
|
||||||
var cmdsearch = overparam[0];
|
var cmdsearch = overparam[0];
|
||||||
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[CommandPrefix.Length..];
|
cmdsearch = cmdsearch[CommandPrefix.Length..];
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Add command prefix to input, just in case.
|
// Add command prefix to input, just in case.
|
||||||
overparam[0] = CommandPrefix + overparam[0].ToLower();
|
overparam[0] = CommandPrefix + overparam[0].ToLower();
|
||||||
}
|
}
|
||||||
if (!_usercommands.TryGetValue(cmdsearch, out CommandHandler action))
|
if (!_usercommands.TryGetValue(cmdsearch, out var action)) {
|
||||||
{
|
|
||||||
await reqChannel.SendMessageAsync(
|
await reqChannel.SendMessageAsync(
|
||||||
$":x: `{cmdsearch}` is not an overridable command.", embed: DocOverride.UsageEmbed)
|
$":x: `{cmdsearch}` is not an overridable command.", embed: DocOverride.UsageEmbed)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
@ -448,66 +374,79 @@ namespace BirthdayBot.UserInterface
|
||||||
await action.Invoke(instance, gconf, overparam, reqChannel, overuser).ConfigureAwait(false);
|
await action.Invoke(instance, gconf, overparam, reqChannel, overuser).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publicly available command that immediately processes the current guild,
|
// Troubleshooting tool: Check for common problems regarding typical background operation.
|
||||||
private async Task CmdTest(ShardInstance instance, GuildConfiguration gconf,
|
private async Task CmdCheck(ShardInstance instance, GuildConfiguration gconf,
|
||||||
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser)
|
string[] param, SocketTextChannel reqChannel, SocketGuildUser reqUser) {
|
||||||
{
|
|
||||||
// Moderators only. As with config, silently drop if this check fails.
|
// Moderators only. As with config, silently drop if this check fails.
|
||||||
if (!gconf.IsBotModerator(reqUser)) return;
|
if (!gconf.IsBotModerator(reqUser)) return;
|
||||||
|
|
||||||
if (param.Length != 1)
|
if (param.Length != 1) {
|
||||||
{
|
|
||||||
// Too many parameters
|
// Too many parameters
|
||||||
// Note: Non-standard error display
|
// Note: Non-standard error display
|
||||||
await reqChannel.SendMessageAsync(NoParameterError).ConfigureAwait(false);
|
await reqChannel.SendMessageAsync(NoParameterError).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// had an option to clear roles here, but application testing revealed that running the
|
static string DoTestFor(string label, Func<bool> test) => $"{label}: { (test() ? ":white_check_mark: Yes" : ":x: No") }";
|
||||||
// test at this point would make the updater assume that roles had not yet been cleared
|
var result = new StringBuilder();
|
||||||
// may revisit this later...
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var guild = reqChannel.Guild;
|
var guild = reqChannel.Guild;
|
||||||
string result = $"\nServer ID: {guild.Id} | Bot shard ID: {instance.ShardId:00}";
|
var conf = await GuildConfiguration.LoadAsync(guild.Id, true).ConfigureAwait(false);
|
||||||
result += $"\nLocally cached members: {guild.DownloadedMemberCount} out of {guild.MemberCount}";
|
result.AppendLine($"Server ID: {guild.Id} | Bot shard ID: {instance.ShardId:00}");
|
||||||
result += "\n" + await ShardInstance.ForceBirthdayUpdateAsync(guild).ConfigureAwait(false);
|
result.AppendLine();
|
||||||
await reqChannel.SendMessageAsync(result).ConfigureAwait(false);
|
|
||||||
}
|
result.AppendLine(DoTestFor("Bot has obtained the user list", () => Common.HasMostMembersDownloaded(guild)));
|
||||||
catch (Exception ex)
|
result.AppendLine(DoTestFor("Server configuration exists", delegate {
|
||||||
{
|
if (conf == null) return false;
|
||||||
Program.Log("Test command", ex.ToString());
|
return true;
|
||||||
reqChannel.SendMessageAsync(InternalError).Wait();
|
}));
|
||||||
// TODO webhook report
|
result.AppendLine();
|
||||||
}
|
result.AppendLine(DoTestFor("Birthday role set with `bb.config role`", delegate {
|
||||||
|
if (conf == null) return false;
|
||||||
|
SocketRole? role = guild.GetRole(conf.RoleId ?? 0);
|
||||||
|
return role != null;
|
||||||
|
}));
|
||||||
|
result.AppendLine(DoTestFor("Birthday role can be managed by bot", delegate {
|
||||||
|
if (conf == null) return false;
|
||||||
|
SocketRole? role = guild.GetRole(conf.RoleId ?? 0);
|
||||||
|
if (role == null) return false;
|
||||||
|
return guild.CurrentUser.GuildPermissions.ManageRoles && role.Position < guild.CurrentUser.Hierarchy;
|
||||||
|
}));
|
||||||
|
result.AppendLine();
|
||||||
|
result.AppendLine(DoTestFor("(Optional) Announcement channel set with `bb.config channel`", delegate {
|
||||||
|
if (conf == null) return false;
|
||||||
|
SocketTextChannel? channel = guild.GetTextChannel(conf.AnnounceChannelId ?? 0);
|
||||||
|
return channel != null;
|
||||||
|
}));
|
||||||
|
result.AppendLine(DoTestFor("(Optional) Bot can send messages into announcement channel", delegate {
|
||||||
|
if (conf == null) return false;
|
||||||
|
SocketTextChannel? channel = guild.GetTextChannel(conf.AnnounceChannelId ?? 0);
|
||||||
|
if (channel == null) return false;
|
||||||
|
return guild.CurrentUser.GetPermissions(channel).SendMessages;
|
||||||
|
}));
|
||||||
|
|
||||||
|
await reqChannel.SendMessageAsync(result.ToString()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#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(@"<@?&(?<snowflake>\d+)>", RegexOptions.Compiled);
|
private static readonly Regex RoleMention = new(@"<@?&(?<snowflake>\d+)>", RegexOptions.Compiled);
|
||||||
|
|
||||||
private static 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;
|
||||||
var rmatch = RoleMention.Match(input);
|
var rmatch = RoleMention.Match(input);
|
||||||
if (rmatch.Success) input = rmatch.Groups["snowflake"].Value;
|
if (rmatch.Success) input = rmatch.Groups["snowflake"].Value;
|
||||||
|
|
||||||
// Attempt to get role by ID, or null
|
// Attempt to get role by ID, or null
|
||||||
if (ulong.TryParse(input, out ulong rid))
|
if (ulong.TryParse(input, out ulong rid)) {
|
||||||
{
|
|
||||||
return guild.GetRole(rid);
|
return guild.GetRole(rid);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reset the search value on the off chance there's a role name that actually resembles a role ping.
|
// Reset the search value on the off chance there's a role name that actually resembles a role ping.
|
||||||
input = inputStr;
|
input = inputStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not already found, attempt to search role by string name
|
// If not already found, attempt to search role by string name
|
||||||
foreach (var search in guild.Roles)
|
foreach (var search in guild.Roles) {
|
||||||
{
|
|
||||||
if (string.Equals(search.Name, input, StringComparison.OrdinalIgnoreCase)) return search;
|
if (string.Equals(search.Name, input, StringComparison.OrdinalIgnoreCase)) return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,4 +454,3 @@ namespace BirthdayBot.UserInterface
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue