2022-08-22 22:35:06 +00:00
#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?
2022-08-22 22:46:39 +00:00
#define LOG_CHANNELS_ON_COMMAND_ATTEMPT_VERBOSE //print a message describing relevant channel ids whenever a command is attempted to be sent?
2022-08-22 22:27:12 +00:00
using DSharpPlus ;
2022-06-13 00:48:24 +00:00
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 ;
2022-06-20 18:55:01 +00:00
private bool Reconnecting ;
2022-06-13 00:48:24 +00:00
2022-08-22 22:27:12 +00:00
private bool warnedAboutNullLogChannel = false ; //print warning message
2022-06-13 00:48:24 +00:00
public DiscordBot ( ) {
Token = Config . Token ;
Logger . AddLogHandler ( Log ) ;
2022-06-20 18:55:01 +00:00
CommandHandler . RegisterCommand ( "dscrestart" , _ = > {
// this should be async'ed but i'm lazy
Reconnecting = true ;
Task . Run ( Reconnect ) ;
return "Restarting Discord bot" ;
} ) ;
2022-06-13 00:48:24 +00:00
if ( Config . Token = = null ) return ;
Settings . LoadHandler + = SettingsLoadHandler ;
}
2022-06-20 18:55:01 +00:00
private async Task Reconnect ( ) {
2022-08-22 22:27:12 +00:00
warnedAboutNullLogChannel = false ;
2022-06-20 18:55:01 +00:00
if ( DiscordClient ! = null ) // usually null prop works, not here though...`
await DiscordClient . DisconnectAsync ( ) ;
await Run ( ) ;
}
2022-06-13 00:48:24 +00:00
private async void SettingsLoadHandler ( ) {
try {
if ( DiscordClient = = null | | Token ! = Config . Token )
2022-06-20 18:55:01 +00:00
await Run ( ) ;
2022-06-13 00:48:24 +00:00
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 ) ;
}
}
2022-07-29 01:16:24 +00:00
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 ;
}
2022-06-13 00:48:24 +00:00
private async void Log ( string source , string level , string text , ConsoleColor _ ) {
try {
if ( DiscordClient ! = null & & LogChannel ! = null ) {
2022-07-29 01:16:24 +00:00
foreach ( string mesg in SplitMessage ( Logger . PrefixNewLines ( text , $"{level} [{source}]" ) , 1994 ) ) //room for 6 '`'
await DiscordClient . SendMessageAsync ( LogChannel , $"```{mesg}```" ) ;
2022-06-13 00:48:24 +00:00
}
} catch ( Exception e ) {
2022-06-18 03:18:57 +00:00
// don't log again, it'll just stack overflow the server!
2022-06-20 18:55:01 +00:00
if ( Reconnecting ) return ; // skip if reconnecting
2022-06-13 00:48:24 +00:00
await Console . Error . WriteLineAsync ( "Exception in discord logger" ) ;
await Console . Error . WriteLineAsync ( e . ToString ( ) ) ;
}
}
2022-06-20 18:55:01 +00:00
public async Task Run ( ) {
2022-06-13 00:48:24 +00:00
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 ( ) ;
2022-06-20 18:55:01 +00:00
Logger . Info (
$"Discord bot logged in as {DiscordClient.CurrentUser.Username}#{DiscordClient.CurrentUser.Discriminator}" ) ;
Reconnecting = false ;
2022-06-13 00:48:24 +00:00
string mentionPrefix = $"{DiscordClient.CurrentUser.Mention} " ;
DiscordClient . MessageCreated + = async ( _ , args ) = > {
2022-08-22 22:27:12 +00:00
if ( args . Author . IsCurrent ) return ; //dont respond to commands from ourselves (prevent "sql-injection" esq attacks)
2022-08-22 22:46:39 +00:00
#if LOG_CHANNELS_ON_COMMAND_ATTEMPT_VERBOSE
2022-08-22 22:49:13 +00:00
//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) || (Config.LogChannel == null && !(args.Channel is DiscordDmChannel))}");
Logger . Info ( $"cmdchannel == channel id exec ({args.Channel.Id.ToString() == Config.LogChannel})" ) ;
2022-08-22 22:46:39 +00:00
#endif
2022-08-22 22:27:12 +00:00
//prevent commands via dm and non-public channels
2022-08-22 22:01:58 +00:00
if ( Config . LogChannel = = null ) {
2022-08-22 22:27:12 +00:00
if ( ! warnedAboutNullLogChannel ) {
Logger . Warn ( "You probably should set your LogChannel in settings.json" ) ;
warnedAboutNullLogChannel = true ;
}
2022-08-22 22:35:06 +00:00
if ( args . Channel is DiscordDmChannel ) {
2022-08-22 22:27:12 +00:00
#if LOG_BAD_REQ
2022-08-22 22:43:00 +00:00
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)" ) ;
2022-08-22 22:27:12 +00:00
#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 ;
}
2022-08-22 22:01:58 +00:00
}
2022-08-22 22:27:12 +00:00
else {
ulong chId = ulong . Parse ( Config . LogChannel ) ;
if ( args . Channel . Id ! = chId ) {
#if LOG_BAD_REQ
2022-08-22 22:46:39 +00:00
Logger . Warn ( "A command was sent to the bot in some non-public channel. This will not be processed. (Send commands in the specified LogChannel in settings.json or only in public channels)" ) ;
2022-08-22 22:27:12 +00:00
#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 ;
}
2022-08-22 22:01:58 +00:00
}
//run command
2022-06-20 01:33:45 +00:00
try {
DiscordMessage msg = args . Message ;
2022-07-29 01:16:24 +00:00
string? resp = null ;
2022-06-20 18:55:01 +00:00
if ( string . IsNullOrEmpty ( Prefix ) ) {
await msg . Channel . TriggerTypingAsync ( ) ;
2022-07-29 01:16:24 +00:00
resp = string . Join ( '\n' , CommandHandler . GetResult ( msg . Content ) . ReturnStrings ) ;
2022-06-20 18:55:01 +00:00
} else if ( msg . Content . StartsWith ( Prefix ) ) {
2022-06-20 01:33:45 +00:00
await msg . Channel . TriggerTypingAsync ( ) ;
2022-07-29 01:16:24 +00:00
resp = string . Join ( '\n' , CommandHandler . GetResult ( msg . Content [ Prefix . Length . . ] ) . ReturnStrings ) ;
2022-06-20 01:33:45 +00:00
} else if ( msg . Content . StartsWith ( mentionPrefix ) ) {
await msg . Channel . TriggerTypingAsync ( ) ;
2022-07-29 01:16:24 +00:00
resp = string . Join ( '\n' , CommandHandler . GetResult ( msg . Content [ mentionPrefix . Length . . ] ) . ReturnStrings ) ;
}
if ( resp ! = null )
{
foreach ( string mesg in SplitMessage ( resp ) )
await msg . RespondAsync ( mesg ) ;
}
2022-06-20 01:33:45 +00:00
} catch ( Exception e ) {
Logger . Error ( e ) ;
2022-06-13 00:48:24 +00:00
}
} ;
2022-06-20 01:33:45 +00:00
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 ;
} ;
2022-06-13 00:48:24 +00:00
} catch ( Exception e ) {
Logger . Error ( "Exception occurred in discord runner!" ) ;
Logger . Error ( e ) ;
}
}
}