2020-03-01 22:09:14 +04:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using Microsoft.Extensions.Configuration;
|
2020-03-01 22:46:07 +04:00
|
|
|
using Microsoft.Extensions.Logging;
|
2020-03-01 22:09:14 +04:00
|
|
|
|
|
|
|
|
namespace Kruzya.TelegramBot.Core
|
|
|
|
|
{
|
|
|
|
|
public sealed class Core
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Core API version.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public UInt32 ApiVersion => 1;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The array that contains all modules instances.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Module[] Modules => _modules.ToArray();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The current bot configuration.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IConfiguration Configuration => _configuration;
|
|
|
|
|
|
|
|
|
|
#region Private properties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The array that contains all modules instances.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<Module> _modules = new List<Module>();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The current bot configuration.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private IConfiguration _configuration;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The all directories for loading our module assemblies.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<string> _directories
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
// For start, load all assemblies in required directory.
|
|
|
|
|
// And register in internal temporary array.
|
|
|
|
|
var modulesDirectory = new List<string>(new string[]
|
|
|
|
|
{
|
2020-03-01 22:46:07 +04:00
|
|
|
Path.Join(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "modules"),
|
2020-03-01 22:09:14 +04:00
|
|
|
Path.Join(Environment.CurrentDirectory, "modules")
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Also lookup load directories from config.
|
|
|
|
|
modulesDirectory.AddRange(_configuration.GetSection("ModuleDirectories").GetChildren().Select(q => q.Value)
|
|
|
|
|
.ToList());
|
|
|
|
|
|
|
|
|
|
return modulesDirectory;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-01 22:46:07 +04:00
|
|
|
|
2020-03-01 22:09:14 +04:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public Core(IConfiguration configuration)
|
|
|
|
|
{
|
|
|
|
|
_configuration = configuration;
|
|
|
|
|
|
2020-03-02 00:00:44 +04:00
|
|
|
HookAppDomain();
|
2020-03-01 22:09:14 +04:00
|
|
|
Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes all required submodules.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void Start()
|
|
|
|
|
{
|
|
|
|
|
var types = new List<Type>();
|
|
|
|
|
foreach (var directory in _directories)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(directory))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var file in Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories))
|
|
|
|
|
{
|
|
|
|
|
var assemblyTypes = LoadAssembly(file);
|
|
|
|
|
if (assemblyTypes == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
types.AddRange(assemblyTypes);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-01 22:46:07 +04:00
|
|
|
|
|
|
|
|
// Instantiate them.
|
|
|
|
|
foreach (var module in types)
|
|
|
|
|
{
|
|
|
|
|
EnsureLoaded(module);
|
|
|
|
|
}
|
2020-03-01 22:09:14 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the assembly and appends all Module types to list.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="path">The full path to assembly file</param>
|
|
|
|
|
/// <returns><see cref="IEnumerable{T}"/> if success, null otherwise.</returns>
|
|
|
|
|
private IEnumerable<Type> LoadAssembly(string path)
|
|
|
|
|
{
|
|
|
|
|
Assembly assembly;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
assembly = Assembly.LoadFile(path);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
// don't load any invalid module.
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return assembly.GetExportedTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Module)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Ensures if module is loaded and started. Note that call can change module event chain.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T">The Module type.</typeparam>
|
|
|
|
|
/// <returns>Module instance.</returns>
|
|
|
|
|
public T EnsureLoaded<T>()
|
|
|
|
|
where T : Module
|
|
|
|
|
{
|
|
|
|
|
return (T) EnsureLoaded(typeof(T));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Ensures if module is loaded and started. Note that call can change module event chain.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="moduleType">The Module type.</param>
|
|
|
|
|
/// <returns>Module instance.</returns>
|
|
|
|
|
/// <exception cref="Exception">The given type is not extends the Module class</exception>
|
|
|
|
|
public Module EnsureLoaded(Type moduleType)
|
|
|
|
|
{
|
|
|
|
|
if (!moduleType.IsSubclassOf(typeof(Module)))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("Received incorrect module type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var module = _modules.FirstOrDefault(module => module.GetType() == moduleType);
|
|
|
|
|
if (module == null)
|
|
|
|
|
{
|
|
|
|
|
module = (Module) Activator.CreateInstance(moduleType, this);
|
2020-03-02 08:49:57 +04:00
|
|
|
Console.WriteLine($"Started module {module.GetType().Name}");
|
2020-03-01 22:09:14 +04:00
|
|
|
_modules.Add(module);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return module;
|
|
|
|
|
}
|
2020-03-02 00:00:44 +04:00
|
|
|
|
|
|
|
|
private void HookAppDomain()
|
|
|
|
|
{
|
2020-03-02 18:10:19 +04:00
|
|
|
var appDomain = AppDomain.CurrentDomain;
|
2020-03-03 14:11:51 +04:00
|
|
|
|
2020-03-02 18:10:19 +04:00
|
|
|
appDomain.AssemblyResolve += delegate(object? sender, ResolveEventArgs args)
|
2020-03-02 00:00:44 +04:00
|
|
|
{
|
|
|
|
|
var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
2020-03-03 14:11:51 +04:00
|
|
|
var assemblyPath = Path.Combine(basePath, new AssemblyName(args.Name).Name + ".dll");
|
2020-03-02 00:00:44 +04:00
|
|
|
if (!File.Exists(assemblyPath))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Assembly.LoadFrom(assemblyPath);
|
|
|
|
|
};
|
|
|
|
|
}
|
2020-03-01 22:09:14 +04:00
|
|
|
}
|
|
|
|
|
}
|