diff --git a/Core/Attributes/AllowedSerializableTypeAttribute.cs b/Core/Attributes/AllowedSerializableTypeAttribute.cs
new file mode 100644
index 0000000..0a5943f
--- /dev/null
+++ b/Core/Attributes/AllowedSerializableTypeAttribute.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Kruzya.TelegramBot.Core.Attributes
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public class AllowedSerializableTypeAttribute : Attribute
+ {
+ public readonly string Name;
+ public AllowedSerializableTypeAttribute(string name)
+ {
+ Name = name;
+ }
+ }
+}
diff --git a/Core/Core.cs b/Core/Core.cs
index b2e7cf9..fb729a1 100644
--- a/Core/Core.cs
+++ b/Core/Core.cs
@@ -14,7 +14,7 @@ public sealed class Core
///
/// The Core API version.
///
- public UInt32 ApiVersion => 1;
+ public UInt32 ApiVersion => 2;
///
/// The array that contains all modules instances.
diff --git a/Core/Module.cs b/Core/Module.cs
index d9c422b..8c2e556 100644
--- a/Core/Module.cs
+++ b/Core/Module.cs
@@ -2,6 +2,8 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using System.Threading;
+using System.Threading.Tasks;
namespace Kruzya.TelegramBot.Core;
@@ -22,7 +24,10 @@ public abstract class Module
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
-
+
+ public virtual Task StartAsync(CancellationToken cts, IServiceScope scope) => Task.CompletedTask;
+ public virtual Task StopAsync(CancellationToken cts, IServiceScope scope) => Task.CompletedTask;
+
///
/// Ensures if module is loaded and started. Note that call can change module event chain.
///
diff --git a/Core/SerializationBinder.cs b/Core/SerializationBinder.cs
new file mode 100644
index 0000000..8457755
--- /dev/null
+++ b/Core/SerializationBinder.cs
@@ -0,0 +1,49 @@
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Kruzya.TelegramBot.Core
+{
+ public class SerializationBinder : ISerializationBinder
+ {
+ protected IDictionary _allowedTypes = new Dictionary();
+
+ public void RegisterAllowedType(string name, Type type)
+ {
+ if (_allowedTypes.ContainsKey(name))
+ {
+ throw new ArgumentException($"{name} is already registered", "name");
+ }
+
+ foreach (var knownType in _allowedTypes.Values)
+ {
+ if (type != knownType)
+ {
+ continue;
+ }
+
+ throw new ArgumentException($"{type.FullName} is already known type", "type");
+ }
+
+ _allowedTypes.Add(name, type);
+ }
+
+ public void RegisterAllowedType(string name) where T : class
+ {
+ RegisterAllowedType(name, typeof(T));
+ }
+
+ public void BindToName(Type serializedType, out string assemblyName, out string typeName)
+ {
+ assemblyName = null;
+ typeName = _allowedTypes.Where(x => x.Value == serializedType)
+ .FirstOrDefault().Key;
+ }
+
+ public Type BindToType(string assemblyName, string typeName)
+ {
+ return _allowedTypes[typeName];
+ }
+ }
+}
diff --git a/Core/Service/CoreManager.cs b/Core/Service/CoreManager.cs
new file mode 100644
index 0000000..b082b6b
--- /dev/null
+++ b/Core/Service/CoreManager.cs
@@ -0,0 +1,58 @@
+using Castle.Core.Internal;
+using Kruzya.TelegramBot.Core.Attributes;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Kruzya.TelegramBot.Core.Service
+{
+ public class CoreManager : IHostedService
+ {
+ private readonly Core _core;
+ private readonly IServiceScopeFactory _scopeFactory;
+
+ public CoreManager(Core core, IServiceScopeFactory scopeFactory)
+ {
+ _core = core;
+ _scopeFactory = scopeFactory;
+ }
+
+ public async Task StartAsync(CancellationToken cts)
+ {
+ await ManagerStartAsync(cts, _scopeFactory.CreateScope());
+ foreach (var module in _core.Modules)
+ {
+ await module.StartAsync(cts, _scopeFactory.CreateScope());
+ }
+ }
+
+ public async Task StopAsync(CancellationToken cts)
+ {
+ // await ManagerStopAsync(cts, _scopeFactory.CreateScope());
+ foreach (var module in _core.Modules)
+ {
+ await module.StopAsync(cts, _scopeFactory.CreateScope());
+ }
+ }
+
+ public Task ManagerStartAsync(CancellationToken cts, IServiceScope scope)
+ {
+ var binder = scope.ServiceProvider.GetRequiredService();
+ var allowedSerializationTypes = _core.Modules
+ .SelectMany(module => module.GetType().Assembly.ExportedTypes)
+ .Where(definedType => definedType.GetAttribute() != null)
+ .ToDictionary(type => type.GetAttribute().Name);
+
+ foreach (var pair in allowedSerializationTypes)
+ {
+ binder.RegisterAllowedType(pair.Key, pair.Value);
+ }
+
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/Core/Startup.cs b/Core/Startup.cs
index b81117e..f5d230a 100644
--- a/Core/Startup.cs
+++ b/Core/Startup.cs
@@ -14,6 +14,8 @@ using System.Collections.Concurrent;
using Kruzya.TelegramBot.Core.Options;
using Telegram.Bot.Types;
using BotFramework.Abstractions.UpdateProvider;
+using System;
+using Newtonsoft.Json.Serialization;
namespace Kruzya.TelegramBot.Core;
@@ -55,11 +57,15 @@ public class Startup
services.AddSingleton>();
services.AddHostedService();
+ services.AddHostedService();
services.AddScoped();
services.AddHttpClient();
+ services.AddTelegramBotParameterParser();
+
services.AddSingleton();
+ services.AddSingleton();
// Trigger module handlers.
_core.Modules.ConfigureServices(services);
diff --git a/Core/UriBotParser.cs b/Core/UriBotParser.cs
new file mode 100644
index 0000000..9176687
--- /dev/null
+++ b/Core/UriBotParser.cs
@@ -0,0 +1,15 @@
+using BotFramework;
+using System;
+
+namespace Kruzya.TelegramBot.Core
+{
+ public class AbsoluteUriParser : IParameterParser
+ {
+ public Uri DefaultInstance() => null;
+
+ public bool TryGetValue(string text, out Uri result)
+ {
+ return Uri.TryCreate(text, UriKind.Absolute, out result);
+ }
+ }
+}