Attempt to improve code quality with a fearless refactor

- Completely refactor Program.cs
- Split Handlers into separate files
- Split up the command handler into multiple functions
This commit is contained in:
runebaas 2020-06-19 03:34:37 +02:00
parent f7908c2a0c
commit 83dc2c8e49
No known key found for this signature in database
GPG key ID: 2677AF508D0300D6
8 changed files with 488 additions and 381 deletions

View file

@ -1,280 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Rest;
using Discord.WebSocket;
using Geekbot.net.Database;
using Geekbot.net.Database.Models;
using Geekbot.net.Lib.Extensions;
using Geekbot.net.Lib.Logger;
using Geekbot.net.Lib.ReactionListener;
using Geekbot.net.Lib.UserRepository;
using Microsoft.EntityFrameworkCore;
namespace Geekbot.net
{
public class Handlers
{
private readonly DatabaseContext _database;
private readonly IDiscordClient _client;
private readonly IGeekbotLogger _logger;
private readonly IServiceProvider _servicesProvider;
private readonly CommandService _commands;
private readonly IUserRepository _userRepository;
private readonly IReactionListener _reactionListener;
private readonly DatabaseContext _messageCounterDatabaseContext;
private readonly RestApplication _applicationInfo;
private readonly List<ulong> _ignoredServers;
public Handlers(DatabaseInitializer databaseInitializer, IDiscordClient client, IGeekbotLogger logger,
IServiceProvider servicesProvider, CommandService commands, IUserRepository userRepository,
IReactionListener reactionListener, RestApplication applicationInfo)
{
_database = databaseInitializer.Initialize();
_messageCounterDatabaseContext = databaseInitializer.Initialize();
_client = client;
_logger = logger;
_servicesProvider = servicesProvider;
_commands = commands;
_userRepository = userRepository;
_reactionListener = reactionListener;
_applicationInfo = applicationInfo;
// ToDo: create a clean solution for this...
_ignoredServers = new List<ulong>()
{
228623803201224704, // SwitzerLAN
169844523181015040, // EEvent
248531441548263425, // MYI
110373943822540800 // Discord Bots
};
}
//
// Incoming Messages
//
public Task RunCommand(SocketMessage messageParam)
{
try
{
if (!(messageParam is SocketUserMessage message)) return Task.CompletedTask;
if (message.Author.IsBot) return Task.CompletedTask;
var argPos = 0;
var guildId = ((SocketGuildChannel) message.Channel).Guild.Id;
// Some guilds only wanted very specific functionally without any of the commands, a quick hack that solves that short term...
// ToDo: cleanup
if (_ignoredServers.Contains(guildId))
{
if (message.Author.Id != _applicationInfo.Owner.Id)
{
return Task.CompletedTask;
}
}
var lowCaseMsg = message.ToString().ToLower();
if (lowCaseMsg.StartsWith("hui"))
{
var hasPing = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId.Equals(guildId.AsLong()))?.Hui ?? false;
if (hasPing)
{
message.Channel.SendMessageAsync("hui!!!");
return Task.CompletedTask;
}
}
if (lowCaseMsg.StartsWith("ping ") || lowCaseMsg.Equals("ping"))
{
var hasPing = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId.Equals(guildId.AsLong()))?.Ping ?? false;
if (hasPing)
{
message.Channel.SendMessageAsync("pong");
return Task.CompletedTask;
}
}
if (!(message.HasCharPrefix('!', ref argPos) ||
message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return Task.CompletedTask;
var context = new CommandContext(_client, message);
_commands.ExecuteAsync(context, argPos, _servicesProvider);
_logger.Information(LogSource.Command,
context.Message.Content.Split(" ")[0].Replace("!", ""),
SimpleConextConverter.ConvertContext(context));
return Task.CompletedTask;
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to Process Message", e);
return Task.CompletedTask;
}
}
public async Task UpdateStats(SocketMessage message)
{
try
{
if (message == null) return;
if (message.Channel.Name.StartsWith('@'))
{
_logger.Information(LogSource.Message, $"[DM-Channel] {message.Content}", SimpleConextConverter.ConvertSocketMessage(message, true));
return;
}
var channel = (SocketGuildChannel) message.Channel;
var rowId = await _messageCounterDatabaseContext.Database.ExecuteSqlRawAsync(
"UPDATE \"Messages\" SET \"MessageCount\" = \"MessageCount\" + 1 WHERE \"GuildId\" = {0} AND \"UserId\" = {1}",
channel.Guild.Id.AsLong(),
message.Author.Id.AsLong()
);
if (rowId == 0)
{
_messageCounterDatabaseContext.Messages.Add(new MessagesModel
{
UserId = message.Author.Id.AsLong(),
GuildId = channel.Guild.Id.AsLong(),
MessageCount = 1
});
_messageCounterDatabaseContext.SaveChanges();
}
if (message.Author.IsBot) return;
_logger.Information(LogSource.Message, message.Content, SimpleConextConverter.ConvertSocketMessage(message));
}
catch (Exception e)
{
_logger.Error(LogSource.Message, "Could not process message stats", e);
}
}
//
// User Stuff
//
public async Task UserJoined(SocketGuildUser user)
{
try
{
var userRepoUpdate = _userRepository.Update(user);
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
if (!user.IsBot)
{
var guildSettings = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId == user.Guild.Id.AsLong());
var message = guildSettings?.WelcomeMessage;
if (string.IsNullOrEmpty(message)) return;
message = message.Replace("$user", user.Mention);
var fallbackSender = new Func<Task<RestUserMessage>>(() => user.Guild.DefaultChannel.SendMessageAsync(message));
if (guildSettings.WelcomeChannel != 0)
{
try
{
var target = await _client.GetChannelAsync(guildSettings.WelcomeChannel.AsUlong());
var channel = target as ISocketMessageChannel;
await channel.SendMessageAsync(message);
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send welcome message to user defined welcome channel", e);
await fallbackSender();
}
}
else
{
await fallbackSender();
}
}
await userRepoUpdate;
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send welcome message", e);
}
}
public async Task UserUpdated(SocketUser oldUser, SocketUser newUser)
{
await _userRepository.Update(newUser);
}
public async Task UserLeft(SocketGuildUser user)
{
try
{
var guild = _database.GuildSettings.FirstOrDefault(g =>
g.GuildId.Equals(user.Guild.Id.AsLong()));
if (guild?.ShowLeave ?? false)
{
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong());
await modChannelSocket.SendMessageAsync($"{user.Username}#{user.Discriminator} left the server");
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send leave message", e);
}
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
}
//
// Message Stuff
//
public async Task MessageDeleted(Cacheable<IMessage, ulong> message, ISocketMessageChannel channel)
{
try
{
var guildSocketData = ((IGuildChannel) channel).Guild;
var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildSocketData.Id.AsLong()));
if ((guild?.ShowDelete ?? false) && guild?.ModChannel != 0)
{
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong());
var sb = new StringBuilder();
if (message.Value != null)
{
sb.AppendLine($"The following message from {message.Value.Author.Username}#{message.Value.Author.Discriminator} was deleted in <#{channel.Id}>");
sb.AppendLine(message.Value.Content);
}
else
{
sb.AppendLine("Someone deleted a message, the message was not cached...");
}
await modChannelSocket.SendMessageAsync(sb.ToString());
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send delete message...", e);
}
}
//
// Reactions
//
public Task ReactionAdded(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.GiveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
public Task ReactionRemoved(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.RemoveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
}
}

View file

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Rest;
using Discord.WebSocket;
using Geekbot.net.Database;
using Geekbot.net.Database.Models;
using Geekbot.net.Lib.Extensions;
using Geekbot.net.Lib.Logger;
namespace Geekbot.net.Handlers
{
public class CommandHandler
{
private readonly IDiscordClient _client;
private readonly IGeekbotLogger _logger;
private readonly IServiceProvider _servicesProvider;
private readonly CommandService _commands;
private readonly RestApplication _applicationInfo;
private readonly List<ulong> _ignoredServers;
private readonly DatabaseContext _database;
public CommandHandler(DatabaseContext database, IDiscordClient client, IGeekbotLogger logger, IServiceProvider servicesProvider, CommandService commands, RestApplication applicationInfo)
{
_database = database;
_client = client;
_logger = logger;
_servicesProvider = servicesProvider;
_commands = commands;
_applicationInfo = applicationInfo;
// Some guilds only want very specific functionally without any of the commands, a quick hack that solves that "short term"
// ToDo: create a clean solution for this...
_ignoredServers = new List<ulong>
{
228623803201224704, // SwitzerLAN
169844523181015040, // EEvent
248531441548263425, // MYI
110373943822540800 // Discord Bots
};
}
public Task RunCommand(SocketMessage messageParam)
{
try
{
if (!(messageParam is SocketUserMessage message)) return Task.CompletedTask;
if (message.Author.IsBot) return Task.CompletedTask;
var guildId = ((SocketGuildChannel) message.Channel).Guild.Id;
if (IsIgnoredGuild(guildId, message.Author.Id)) return Task.CompletedTask;
var lowCaseMsg = message.ToString().ToLower();
if (ShouldHui(lowCaseMsg, guildId))
{
message.Channel.SendMessageAsync("hui!!!");
return Task.CompletedTask;
}
if (ShouldPong(lowCaseMsg, guildId))
{
message.Channel.SendMessageAsync("pong");
return Task.CompletedTask;
}
var argPos = 0;
if (!IsCommand(message, ref argPos)) return Task.CompletedTask;
ExecuteCommand(message, argPos);
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to Process Message", e);
}
return Task.CompletedTask;
}
private void ExecuteCommand(IUserMessage message, int argPos)
{
var context = new CommandContext(_client, message);
_commands.ExecuteAsync(context, argPos, _servicesProvider);
_logger.Information(LogSource.Command, context.Message.Content.Split(" ")[0].Replace("!", ""), SimpleConextConverter.ConvertContext(context));
}
private bool IsIgnoredGuild(ulong guildId, ulong authorId)
{
if (!_ignoredServers.Contains(guildId)) return false;
return authorId == _applicationInfo.Owner.Id;
}
private bool IsCommand(IUserMessage message, ref int argPos)
{
return message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos);
}
private bool ShouldPong(string lowerCaseMessage, ulong guildId)
{
if (!lowerCaseMessage.StartsWith("ping ") && !lowerCaseMessage.Equals("ping")) return false;
return GetGuildSettings(guildId)?.Ping ?? false;
}
private bool ShouldHui(string lowerCaseMessage, ulong guildId)
{
if (!lowerCaseMessage.StartsWith("hui")) return false;
return GetGuildSettings(guildId)?.Hui ?? false;
}
private GuildSettingsModel GetGuildSettings(ulong guildId)
{
return _database.GuildSettings.FirstOrDefault(guild => guild.GuildId.Equals(guildId.AsLong()));
}
}
}

View file

@ -0,0 +1,55 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using Geekbot.net.Database;
using Geekbot.net.Lib.Extensions;
using Geekbot.net.Lib.Logger;
namespace Geekbot.net.Handlers
{
public class MessageDeletedHandler
{
private readonly DatabaseContext _database;
private readonly IGeekbotLogger _logger;
private readonly IDiscordClient _client;
public MessageDeletedHandler(DatabaseContext database, IGeekbotLogger logger, IDiscordClient client)
{
_database = database;
_logger = logger;
_client = client;
}
public async Task HandleMessageDeleted(Cacheable<IMessage, ulong> message, ISocketMessageChannel channel)
{
try
{
var guildSocketData = ((IGuildChannel) channel).Guild;
var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildSocketData.Id.AsLong()));
if ((guild?.ShowDelete ?? false) && guild?.ModChannel != 0)
{
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong());
var sb = new StringBuilder();
if (message.Value != null)
{
sb.AppendLine($"The following message from {message.Value.Author.Username}#{message.Value.Author.Discriminator} was deleted in <#{channel.Id}>");
sb.AppendLine(message.Value.Content);
}
else
{
sb.AppendLine("Someone deleted a message, the message was not cached...");
}
await modChannelSocket.SendMessageAsync(sb.ToString());
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send delete message...", e);
}
}
}
}

View file

@ -0,0 +1,33 @@
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using Geekbot.net.Lib.ReactionListener;
namespace Geekbot.net.Handlers
{
public class ReactionHandler
{
private readonly IReactionListener _reactionListener;
public ReactionHandler(IReactionListener reactionListener)
{
_reactionListener = reactionListener;
}
public Task Added(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.GiveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
public Task Removed(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.RemoveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
}
}

View file

@ -0,0 +1,62 @@
using System;
using System.Threading.Tasks;
using Discord.WebSocket;
using Geekbot.net.Database;
using Geekbot.net.Database.Models;
using Geekbot.net.Lib.Extensions;
using Geekbot.net.Lib.Logger;
using Microsoft.EntityFrameworkCore;
namespace Geekbot.net.Handlers
{
public class StatsHandler
{
private readonly IGeekbotLogger _logger;
private readonly DatabaseContext _database;
public StatsHandler(IGeekbotLogger logger, DatabaseContext database)
{
_logger = logger;
_database = database;
}
public async Task UpdateStats(SocketMessage message)
{
try
{
if (message == null) return;
if (message.Channel.Name.StartsWith('@'))
{
_logger.Information(LogSource.Message, $"[DM-Channel] {message.Content}", SimpleConextConverter.ConvertSocketMessage(message, true));
return;
}
var channel = (SocketGuildChannel) message.Channel;
var rowId = await _database.Database.ExecuteSqlRawAsync(
"UPDATE \"Messages\" SET \"MessageCount\" = \"MessageCount\" + 1 WHERE \"GuildId\" = {0} AND \"UserId\" = {1}",
channel.Guild.Id.AsLong(),
message.Author.Id.AsLong()
);
if (rowId == 0)
{
await _database.Messages.AddAsync(new MessagesModel
{
UserId = message.Author.Id.AsLong(),
GuildId = channel.Guild.Id.AsLong(),
MessageCount = 1
});
await _database.SaveChangesAsync();
}
if (message.Author.IsBot) return;
_logger.Information(LogSource.Message, message.Content, SimpleConextConverter.ConvertSocketMessage(message));
}
catch (Exception e)
{
_logger.Error(LogSource.Message, "Could not process message stats", e);
}
}
}
}

View file

@ -0,0 +1,96 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Rest;
using Discord.WebSocket;
using Geekbot.net.Database;
using Geekbot.net.Lib.Extensions;
using Geekbot.net.Lib.Logger;
using Geekbot.net.Lib.UserRepository;
namespace Geekbot.net.Handlers
{
public class UserHandler
{
private readonly IUserRepository _userRepository;
private readonly IGeekbotLogger _logger;
private readonly DatabaseContext _database;
private readonly IDiscordClient _client;
public UserHandler(IUserRepository userRepository, IGeekbotLogger logger, DatabaseContext database, IDiscordClient client)
{
_userRepository = userRepository;
_logger = logger;
_database = database;
_client = client;
}
public async Task Joined(SocketGuildUser user)
{
try
{
var userRepoUpdate = _userRepository.Update(user);
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
if (!user.IsBot)
{
var guildSettings = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId == user.Guild.Id.AsLong());
var message = guildSettings?.WelcomeMessage;
if (string.IsNullOrEmpty(message)) return;
message = message.Replace("$user", user.Mention);
var fallbackSender = new Func<Task<RestUserMessage>>(() => user.Guild.DefaultChannel.SendMessageAsync(message));
if (guildSettings.WelcomeChannel != 0)
{
try
{
var target = await _client.GetChannelAsync(guildSettings.WelcomeChannel.AsUlong());
var channel = target as ISocketMessageChannel;
await channel.SendMessageAsync(message);
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send welcome message to user defined welcome channel", e);
await fallbackSender();
}
}
else
{
await fallbackSender();
}
}
await userRepoUpdate;
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send welcome message", e);
}
}
public async Task Updated(SocketUser oldUser, SocketUser newUser)
{
await _userRepository.Update(newUser);
}
public async Task Left(SocketGuildUser user)
{
try
{
var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(user.Guild.Id.AsLong()));
if (guild?.ShowLeave ?? false)
{
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong());
await modChannelSocket.SendMessageAsync($"{user.Username}#{user.Discriminator} left the server");
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send leave message", e);
}
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
}
}
}

View file

@ -11,7 +11,7 @@ namespace Geekbot.net.Lib.Logger
{
Message = new MessageDto.MessageContent
{
Content = context.Message.Content,
Content = context.Message.Content, // Only when an error occurs, including for diagnostic reason
Id = context.Message.Id.ToString(),
Attachments = context.Message.Attachments.Count,
ChannelMentions = context.Message.MentionedChannelIds.Count,
@ -42,7 +42,6 @@ namespace Geekbot.net.Lib.Logger
{
Message = new MessageDto.MessageContent
{
Content = message.Content,
Id = message.Id.ToString(),
Attachments = message.Attachments.Count,
ChannelMentions = message.MentionedChannels.Count,

View file

@ -7,6 +7,7 @@ using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Database;
using Geekbot.net.Handlers;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Clients;
using Geekbot.net.Lib.Converters;
@ -34,12 +35,11 @@ namespace Geekbot.net
private CommandService _commands;
private DatabaseInitializer _databaseInitializer;
private IGlobalSettings _globalSettings;
private IServiceCollection _services;
private IServiceProvider _servicesProvider;
private string _token;
private GeekbotLogger _logger;
private IUserRepository _userRepository;
private RunParameters _runParameters;
private IReactionListener _reactionListener;
private static void Main(string[] args)
{
@ -73,66 +73,25 @@ namespace Geekbot.net
{
_logger = logger;
_runParameters = runParameters;
logger.Information(LogSource.Geekbot, "Initing Stuff");
var discordLogger = new DiscordLogger(logger);
_client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Verbose,
MessageCacheSize = 1000,
ExclusiveBulkDelete = true
});
_client.Log += discordLogger.Log;
_commands = new CommandService();
_databaseInitializer = new DatabaseInitializer(runParameters, logger);
var database = _databaseInitializer.Initialize();
database.Database.EnsureCreated();
if(!_runParameters.InMemory) database.Database.Migrate();
logger.Information(LogSource.Geekbot, "Connecting to Database");
var database = ConnectToDatabase();
_globalSettings = new GlobalSettings(database);
_token = runParameters.Token ?? _globalSettings.GetKey("DiscordToken");
if (string.IsNullOrEmpty(_token))
{
Console.Write("Your bot Token: ");
var newToken = Console.ReadLine();
await _globalSettings.SetKey("DiscordToken", newToken);
await _globalSettings.SetKey("Game", "Ping Pong");
_token = newToken;
}
_services = new ServiceCollection();
_userRepository = new UserRepository(_databaseInitializer.Initialize(), logger);
var fortunes = new FortunesProvider(logger);
var mediaProvider = new MediaProvider(logger);
var malClient = new MalClient(_globalSettings, logger);
var levelCalc = new LevelCalc();
var emojiConverter = new EmojiConverter();
var mtgManaConverter = new MtgManaConverter();
var wikipediaClient = new WikipediaClient();
var randomNumberGenerator = new RandomNumberGenerator();
var kvMemoryStore = new KvInInMemoryStore();
_services.AddSingleton(_userRepository);
_services.AddSingleton<IGeekbotLogger>(logger);
_services.AddSingleton<ILevelCalc>(levelCalc);
_services.AddSingleton<IEmojiConverter>(emojiConverter);
_services.AddSingleton<IFortunesProvider>(fortunes);
_services.AddSingleton<IMediaProvider>(mediaProvider);
_services.AddSingleton<IMalClient>(malClient);
_services.AddSingleton<IMtgManaConverter>(mtgManaConverter);
_services.AddSingleton<IWikipediaClient>(wikipediaClient);
_services.AddSingleton<IRandomNumberGenerator>(randomNumberGenerator);
_services.AddSingleton<IKvInMemoryStore>(kvMemoryStore);
_services.AddSingleton(_globalSettings);
_services.AddTransient<IHighscoreManager>(e => new HighscoreManager(_databaseInitializer.Initialize(), _userRepository));
_services.AddTransient(e => _databaseInitializer.Initialize());
logger.Information(LogSource.Geekbot, "Connecting to Discord");
SetupDiscordClient();
await Login();
_logger.Information(LogSource.Geekbot, $"Now Connected as {_client.CurrentUser.Username} to {_client.Guilds.Count} Servers");
await _client.SetGameAsync(_globalSettings.GetKey("Game"));
_logger.Information(LogSource.Geekbot, "Loading Dependencies and Handlers");
RegisterDependencies();
await RegisterHandlers();
_logger.Information(LogSource.Api, "Starting Web API");
StartWebApi();
_logger.Information(LogSource.Geekbot, "Done and ready for use");
await Task.Delay(-1);
}
@ -141,42 +100,10 @@ namespace Geekbot.net
{
try
{
await _client.LoginAsync(TokenType.Bot, _token);
var token = await GetToken();
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
var isConneted = await IsConnected();
if (isConneted)
{
var applicationInfo = await _client.GetApplicationInfoAsync();
await _client.SetGameAsync(_globalSettings.GetKey("Game"));
_logger.Information(LogSource.Geekbot, $"Now Connected as {_client.CurrentUser.Username} to {_client.Guilds.Count} Servers");
_logger.Information(LogSource.Geekbot, "Registering Stuff");
var translationHandler = new TranslationHandler(_databaseInitializer.Initialize(), _logger);
var errorHandler = new ErrorHandler(_logger, translationHandler, _runParameters.ExposeErrors);
var reactionListener = new ReactionListener(_databaseInitializer.Initialize());
_services.AddSingleton<IErrorHandler>(errorHandler);
_services.AddSingleton<ITranslationHandler>(translationHandler);
_services.AddSingleton(_client);
_services.AddSingleton<IReactionListener>(reactionListener);
_servicesProvider = _services.BuildServiceProvider();
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _servicesProvider);
var handlers = new Handlers(_databaseInitializer, _client, _logger, _servicesProvider, _commands, _userRepository, reactionListener, applicationInfo);
_client.MessageReceived += handlers.RunCommand;
_client.MessageDeleted += handlers.MessageDeleted;
_client.UserJoined += handlers.UserJoined;
_client.UserUpdated += handlers.UserUpdated;
_client.UserLeft += handlers.UserLeft;
_client.ReactionAdded += handlers.ReactionAdded;
_client.ReactionRemoved += handlers.ReactionRemoved;
if (!_runParameters.InMemory) _client.MessageReceived += handlers.UpdateStats;
var webserver = _runParameters.DisableApi ? Task.Delay(10) : StartWebApi();
_logger.Information(LogSource.Geekbot, "Done and ready for use");
await webserver;
}
while (!_client.ConnectionState.Equals(ConnectionState.Connected)) await Task.Delay(25);
}
catch (Exception e)
{
@ -185,19 +112,117 @@ namespace Geekbot.net
}
}
private async Task<bool> IsConnected()
private DatabaseContext ConnectToDatabase()
{
while (!_client.ConnectionState.Equals(ConnectionState.Connected))
await Task.Delay(25);
return true;
_databaseInitializer = new DatabaseInitializer(_runParameters, _logger);
var database = _databaseInitializer.Initialize();
database.Database.EnsureCreated();
if(!_runParameters.InMemory) database.Database.Migrate();
return database;
}
private Task StartWebApi()
private async Task<string> GetToken()
{
_logger.Information(LogSource.Api, "Starting Webserver");
var token = _runParameters.Token ?? _globalSettings.GetKey("DiscordToken");
if (string.IsNullOrEmpty(token))
{
Console.Write("Your bot Token: ");
var newToken = Console.ReadLine();
await _globalSettings.SetKey("DiscordToken", newToken);
await _globalSettings.SetKey("Game", "Ping Pong");
token = newToken;
}
return token;
}
private void SetupDiscordClient()
{
_client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Verbose,
MessageCacheSize = 1000,
ExclusiveBulkDelete = true
});
var discordLogger = new DiscordLogger(_logger);
_client.Log += discordLogger.Log;
}
private void RegisterDependencies()
{
var services = new ServiceCollection();
_userRepository = new UserRepository(_databaseInitializer.Initialize(), _logger);
_reactionListener = new ReactionListener(_databaseInitializer.Initialize());
var fortunes = new FortunesProvider(_logger);
var mediaProvider = new MediaProvider(_logger);
var malClient = new MalClient(_globalSettings, _logger);
var levelCalc = new LevelCalc();
var emojiConverter = new EmojiConverter();
var mtgManaConverter = new MtgManaConverter();
var wikipediaClient = new WikipediaClient();
var randomNumberGenerator = new RandomNumberGenerator();
var kvMemoryStore = new KvInInMemoryStore();
var translationHandler = new TranslationHandler(_databaseInitializer.Initialize(), _logger);
var errorHandler = new ErrorHandler(_logger, translationHandler, _runParameters.ExposeErrors);
services.AddSingleton(_userRepository);
services.AddSingleton<IGeekbotLogger>(_logger);
services.AddSingleton<ILevelCalc>(levelCalc);
services.AddSingleton<IEmojiConverter>(emojiConverter);
services.AddSingleton<IFortunesProvider>(fortunes);
services.AddSingleton<IMediaProvider>(mediaProvider);
services.AddSingleton<IMalClient>(malClient);
services.AddSingleton<IMtgManaConverter>(mtgManaConverter);
services.AddSingleton<IWikipediaClient>(wikipediaClient);
services.AddSingleton<IRandomNumberGenerator>(randomNumberGenerator);
services.AddSingleton<IKvInMemoryStore>(kvMemoryStore);
services.AddSingleton<IGlobalSettings>(_globalSettings);
services.AddSingleton<IErrorHandler>(errorHandler);
services.AddSingleton<ITranslationHandler>(translationHandler);
services.AddSingleton<IReactionListener>(_reactionListener);
services.AddSingleton(_client);
services.AddTransient<IHighscoreManager>(e => new HighscoreManager(_databaseInitializer.Initialize(), _userRepository));
services.AddTransient(e => _databaseInitializer.Initialize());
_servicesProvider = services.BuildServiceProvider();
}
private async Task RegisterHandlers()
{
var applicationInfo = await _client.GetApplicationInfoAsync();
_commands = new CommandService();
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _servicesProvider);
var commandHandler = new CommandHandler(_databaseInitializer.Initialize(), _client, _logger, _servicesProvider, _commands, applicationInfo);
var userHandler = new UserHandler(_userRepository, _logger, _databaseInitializer.Initialize(), _client);
var reactionHandler = new ReactionHandler(_reactionListener);
var statsHandler = new StatsHandler(_logger, _databaseInitializer.Initialize());
var messageDeletedHandler = new MessageDeletedHandler(_databaseInitializer.Initialize(), _logger, _client);
_client.MessageReceived += commandHandler.RunCommand;
_client.MessageDeleted += messageDeletedHandler.HandleMessageDeleted;
_client.UserJoined += userHandler.Joined;
_client.UserUpdated += userHandler.Updated;
_client.UserLeft += userHandler.Left;
_client.ReactionAdded += reactionHandler.Added;
_client.ReactionRemoved += reactionHandler.Removed;
if (!_runParameters.InMemory) _client.MessageReceived += statsHandler.UpdateStats;
}
private void StartWebApi()
{
if (_runParameters.DisableApi)
{
_logger.Warning(LogSource.Api, "Web API is disabled");
return;
}
var highscoreManager = new HighscoreManager(_databaseInitializer.Initialize(), _userRepository);
WebApiStartup.StartWebApi(_logger, _runParameters, _commands, _databaseInitializer.Initialize(), _client, _globalSettings, highscoreManager);
return Task.CompletedTask;
}
}
}