0
0
Fork 0
mirror of https://github.com/Sanae6/SmoOnlineServer.git synced 2024-11-13 23:25:06 +00:00
SmoOnlineServer/Server/DiscordBot.cs

167 lines
No EOL
7.5 KiB
C#

#define SEND_RESP_TO_BAD_REQ //should the bot send a message to people who attempt to run a command from an invalid location? (Comment out to disable)
#define LOG_BAD_REQ //should the bot log aformentioned invalid requests?
using DSharpPlus;
using DSharpPlus.Entities;
using Microsoft.Extensions.Logging;
using Shared;
namespace Server;
public class DiscordBot {
private DiscordClient? DiscordClient;
private string? Token;
private Settings.DiscordTable Config => Settings.Instance.Discord;
private string Prefix => Config.Prefix;
private readonly Logger Logger = new Logger("Discord");
private DiscordChannel? LogChannel;
private bool Reconnecting;
private bool warnedAboutNullLogChannel = false; //print warning message
public DiscordBot() {
Token = Config.Token;
Logger.AddLogHandler(Log);
CommandHandler.RegisterCommand("dscrestart", _ => {
// this should be async'ed but i'm lazy
Reconnecting = true;
Task.Run(Reconnect);
return "Restarting Discord bot";
});
if (Config.Token == null) return;
Settings.LoadHandler += SettingsLoadHandler;
}
private async Task Reconnect() {
warnedAboutNullLogChannel = false;
if (DiscordClient != null) // usually null prop works, not here though...`
await DiscordClient.DisconnectAsync();
await Run();
}
private async void SettingsLoadHandler() {
try {
if (DiscordClient == null || Token != Config.Token)
await Run();
if (Config.LogChannel != null)
LogChannel = await (DiscordClient?.GetChannelAsync(ulong.Parse(Config.LogChannel)) ??
throw new NullReferenceException("Discord client not setup yet!"));
} catch (Exception e) {
Logger.Error($"Failed to get log channel \"{Config.LogChannel}\"");
Logger.Error(e);
}
}
private static List<string> SplitMessage(string message, int maxSizePerElem = 2000)
{
List<string> result = new List<string>();
for (int i = 0; i < message.Length; i += maxSizePerElem)
{
result.Add(message.Substring(i, message.Length - i < maxSizePerElem ? message.Length - i : maxSizePerElem));
}
return result;
}
private async void Log(string source, string level, string text, ConsoleColor _) {
try {
if (DiscordClient != null && LogChannel != null) {
foreach (string mesg in SplitMessage(Logger.PrefixNewLines(text, $"{level} [{source}]"), 1994)) //room for 6 '`'
await DiscordClient.SendMessageAsync(LogChannel, $"```{mesg}```");
}
} catch (Exception e) {
// don't log again, it'll just stack overflow the server!
if (Reconnecting) return; // skip if reconnecting
await Console.Error.WriteLineAsync("Exception in discord logger");
await Console.Error.WriteLineAsync(e.ToString());
}
}
public async Task Run() {
Token = Config.Token;
DiscordClient?.Dispose();
if (Config.Token == null) {
DiscordClient = null;
return;
}
try {
DiscordClient = new DiscordClient(new DiscordConfiguration {
Token = Config.Token,
MinimumLogLevel = LogLevel.None
});
await DiscordClient.ConnectAsync(new DiscordActivity("Hide and Seek", ActivityType.Competing));
SettingsLoadHandler();
Logger.Info(
$"Discord bot logged in as {DiscordClient.CurrentUser.Username}#{DiscordClient.CurrentUser.Discriminator}");
Reconnecting = false;
string mentionPrefix = $"{DiscordClient.CurrentUser.Mention} ";
DiscordClient.MessageCreated += async (_, args) => {
Logger.Info($"Message recieved on channel \"{args.Channel.Id}\", accepting commands from channel \"{Config.LogChannel ?? "(Any Channel)"}\", do channels match: {args.Channel.Id.ToString() == Config.LogChannel}");
if (args.Author.IsCurrent) return; //dont respond to commands from ourselves (prevent "sql-injection" esq attacks)
//prevent commands via dm and non-public channels
if (Config.LogChannel == null) {
if (!warnedAboutNullLogChannel) {
Logger.Warn("You probably should set your LogChannel in settings.json");
warnedAboutNullLogChannel = true;
}
if (args.Channel is DiscordDmChannel) {
#if LOG_BAD_REQ
Logger.Warn("A command was sent to the bot in a direct message channel. This will not be processed. (Send commands in the specified LogChannel in settings.json or only in public channels)");
#endif
#if SEND_RESP_TO_BAD_REQ
await args.Message.RespondAsync("This channel is not valid for running commands. (Your command was not processed).");
#endif
return;
}
}
else {
ulong chId = ulong.Parse(Config.LogChannel);
if (args.Channel.Id != chId) {
#if LOG_BAD_REQ
Logger.Warn("A command was sent to the bot in a non-public channel. This will not be processed. (Send commands in the specified LogChannel in settings.json or only in public channels)");
#endif
#if SEND_RESP_TO_BAD_REQ
await args.Message.RespondAsync("This channel is not valid for running commands. (Your command was not processed).");
#endif
return;
}
}
//run command
try {
DiscordMessage msg = args.Message;
string? resp = null;
if (string.IsNullOrEmpty(Prefix)) {
await msg.Channel.TriggerTypingAsync();
resp = string.Join('\n', CommandHandler.GetResult(msg.Content).ReturnStrings);
} else if (msg.Content.StartsWith(Prefix)) {
await msg.Channel.TriggerTypingAsync();
resp = string.Join('\n', CommandHandler.GetResult(msg.Content[Prefix.Length..]).ReturnStrings);
} else if (msg.Content.StartsWith(mentionPrefix)) {
await msg.Channel.TriggerTypingAsync();
resp = string.Join('\n', CommandHandler.GetResult(msg.Content[mentionPrefix.Length..]).ReturnStrings);
}
if (resp != null)
{
foreach (string mesg in SplitMessage(resp))
await msg.RespondAsync(mesg);
}
} catch (Exception e) {
Logger.Error(e);
}
};
DiscordClient.ClientErrored += (_, args) => {
Logger.Error("Discord client caught an error in handler!");
Logger.Error(args.Exception);
return Task.CompletedTask;
};
DiscordClient.SocketErrored += (_, args) => {
Logger.Error("Discord client caught an error on socket!");
Logger.Error(args.Exception);
return Task.CompletedTask;
};
} catch (Exception e) {
Logger.Error("Exception occurred in discord runner!");
Logger.Error(e);
}
}
}