SupportChild/SupportChild.cs

255 lines
No EOL
8.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Linq;
using DSharpPlus;
using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Enums;
using DSharpPlus.Interactivity.Extensions;
using DSharpPlus.Commands;
using Microsoft.Extensions.Logging;
using SupportChild.Commands;
using CommandLine;
using DSharpPlus.Commands.Processors.SlashCommands;
using DSharpPlus.Exceptions;
using Microsoft.Extensions.DependencyInjection;
namespace SupportChild;
internal static class SupportChild
{
internal static DiscordClient client = null;
public class CommandLineArguments
{
[Option('c',
"config",
Required = false,
HelpText = "Select a config file to use.",
Default = "config.yml",
MetaValue = "PATH")]
public string configPath { get; set; }
[Option('t',
"transcripts",
Required = false,
HelpText = "Select directory to store transcripts in.",
Default = "./transcripts",
MetaValue = "PATH")]
public string transcriptDir { get; set; }
[Option("leave",
Required = false,
HelpText = "Leaves one or more Discord servers. " +
"You can check which servers your bot is in when it starts up.",
MetaValue = "ID,ID,ID...",
Separator = ','
)]
public IEnumerable<ulong> serversToLeave { get; set; }
}
internal static CommandLineArguments commandLineArgs;
private static void Main(string[] args)
{
StringWriter sw = new StringWriter();
commandLineArgs = new Parser(settings =>
{
settings.AutoHelp = true;
settings.HelpWriter = sw;
settings.AutoVersion = false;
}).ParseArguments<CommandLineArguments>(args).Value;
// CommandLineParser has some bugs related to the built-in version option, ignore the output if it isn't found.
if (!sw.ToString().Contains("Option 'version' is unknown."))
{
Console.Write(sw);
}
if (args.Contains("--help"))
{
return;
}
if (args.Contains("--version"))
{
Console.WriteLine(Assembly.GetEntryAssembly()?.GetName().Name + ' ' + GetVersion());
Console.WriteLine("Build time: " + BuildInfo.BuildTimeUTC.ToString("yyyy-MM-dd HH:mm:ss") + " UTC");
return;
}
MainAsync().GetAwaiter().GetResult();
}
private static async Task MainAsync()
{
Logger.Log("Starting " + Assembly.GetEntryAssembly()?.GetName().Name + " version " + GetVersion() + "...");
try
{
if (!await Reload())
{
Logger.Fatal("Aborting startup due to a fatal error...");
return;
}
// Block this task until the program is closed.
await Task.Delay(-1);
}
catch (Exception e)
{
Logger.Fatal("Fatal error:\n" + e);
Console.ReadLine();
}
}
public static string GetVersion()
{
Version version = Assembly.GetEntryAssembly()?.GetName().Version;
return version?.Major + "."
+ version?.Minor + "."
+ version?.Build
+ (version?.Revision == 0 ? "" : "-" + (char)(64 + version?.Revision ?? 0))
+ " (" + ThisAssembly.Git.Commit + ")";
}
public static async Task<bool> Reload()
{
if (client != null)
{
await client.DisconnectAsync();
client.Dispose();
}
Config.LoadConfig();
// Check if token is unset
if (Config.token is "<add-token-here>" or "")
{
Logger.Fatal("You need to set your bot token in the config and start the bot again.");
throw new ArgumentException("Invalid Discord bot token");
}
// Database connection and setup
try
{
Logger.Log("Connecting to database... (" + Config.hostName + ":" + Config.port + ")");
Database.SetConnectionString(Config.hostName, Config.port, Config.database, Config.username, Config.password);
Database.SetupTables();
}
catch (Exception e)
{
Logger.Fatal("Could not set up database tables, please confirm connection settings, status of the server and permissions of MySQL user. Error: ", e);
return false;
}
Logger.Log("Setting up Discord client...");
DiscordClientBuilder clientBuilder = DiscordClientBuilder.CreateDefault(Config.token, DiscordIntents.All).SetReconnectOnFatalGatewayErrors();
clientBuilder.ConfigureServices(configure =>
{
configure.AddSingleton<IClientErrorHandler>(new ErrorHandler());
});
clientBuilder.ConfigureEventHandlers(builder =>
{
builder.HandleGuildDownloadCompleted(EventHandler.OnReady);
builder.HandleGuildAvailable(EventHandler.OnGuildAvailable);
builder.HandleMessageCreated(EventHandler.OnMessageCreated);
builder.HandleGuildMemberAdded(EventHandler.OnMemberAdded);
builder.HandleGuildMemberRemoved(EventHandler.OnMemberRemoved);
builder.HandleComponentInteractionCreated(EventHandler.OnComponentInteractionCreated);
});
clientBuilder.UseInteractivity(new InteractivityConfiguration
{
PaginationBehaviour = PaginationBehaviour.Ignore,
PaginationDeletion = PaginationDeletion.DeleteMessage,
Timeout = TimeSpan.FromMinutes(15)
});
clientBuilder.UseCommands((_, extension) =>
{
extension.AddCommands(
[
typeof(AddCategoryCommand),
typeof(AddCommand),
typeof(AddMessageCommand),
typeof(AddStaffCommand),
typeof(AdminCommands),
typeof(AssignCommand),
typeof(BlacklistCommand),
typeof(CloseCommand),
typeof(CreateButtonPanelCommand),
typeof(CreateSelectionBoxPanelCommand),
typeof(InterviewTemplateCommands),
typeof(ListAssignedCommand),
typeof(ListCommand),
typeof(ListInvalidCommand),
typeof(ListOpen),
typeof(ListUnassignedCommand),
typeof(MoveCommand),
typeof(NewCommand),
typeof(RandomAssignCommand),
typeof(RemoveCategoryCommand),
typeof(RemoveMessageCommand),
typeof(RemoveStaffCommand),
typeof(RestartInterviewCommand),
typeof(SayCommand),
typeof(SetSummaryCommand),
typeof(StatusCommand),
typeof(SummaryCommand),
typeof(ToggleActiveCommand),
typeof(TranscriptCommand),
typeof(UnassignCommand),
typeof(UnblacklistCommand),
]);
extension.AddProcessor(new SlashCommandProcessor());
extension.CommandErrored += EventHandler.OnCommandError;
}, new CommandsConfiguration()
{
RegisterDefaultCommandProcessors = false,
UseDefaultCommandErrorHandler = false
});
clientBuilder.ConfigureExtraFeatures(clientConfig =>
{
clientConfig.LogUnknownEvents = false;
clientConfig.LogUnknownAuditlogs = false;
});
clientBuilder.ConfigureLogging(config =>
{
config.AddProvider(new LogTestFactory());
});
client = clientBuilder.Build();
Logger.Log("Connecting to Discord...");
await client.ConnectAsync();
return true;
}
}
internal class ErrorHandler : IClientErrorHandler
{
public ValueTask HandleEventHandlerError(string name, Exception exception, Delegate invokedDelegate, object sender, object args)
{
Logger.Error("Client exception occured:\n" + exception);
switch (exception)
{
case BadRequestException ex:
Logger.Error("JSON Message: " + ex.JsonMessage);
break;
default:
break;
}
return ValueTask.FromException(exception);
}
public ValueTask HandleGatewayError(Exception exception)
{
Logger.Error("A gateway error occured:\n" + exception);
return ValueTask.FromException(exception);
}
}