11using System ;
22using System . Linq ;
3- using Telegram . Bot ;
43using System . Threading ;
54using System . Reflection ;
6- using Telegram . Bot . Types ;
7- using TelegramBot . Handlers ;
85using TelegramBot . Services ;
9- using TelegramBot . Builders ;
10- using TelegramBot . Attributes ;
116using System . Threading . Tasks ;
12- using TelegramBot . Extensions ;
7+ using TelegramBot . Containers ;
138using TelegramBot . Controllers ;
14- using TelegramBot . Abstractions ;
159using System . Collections . Generic ;
1610using Microsoft . Extensions . Hosting ;
1711using Microsoft . Extensions . Logging ;
@@ -26,10 +20,7 @@ public class BotApp : IBot
2620 {
2721 private bool _disposed = false ;
2822 private readonly ILogger < BotApp > _logger ;
29- private readonly TelegramBotClient _client ;
3023 private readonly ServiceProvider _serviceProvider ;
31- private readonly BotConfiguration _botConfiguration ;
32- private IReadOnlyCollection < MethodInfo > _controllerMethods ;
3324 private readonly CancellationTokenSource _cancellationTokenSource ;
3425
3526 /// <summary>
@@ -40,15 +31,10 @@ public class BotApp : IBot
4031 /// <summary>
4132 /// Creates a new instance of <see cref="BotApp"/>.
4233 /// </summary>
43- /// <param name="client">Telegram bot client.</param>
4434 /// <param name="serviceProvider">Service provider.</param>
45- /// <param name="botConfiguration">Bot configuration.</param>
46- public BotApp ( TelegramBotClient client , ServiceProvider serviceProvider , BotConfiguration botConfiguration )
35+ public BotApp ( ServiceProvider serviceProvider )
4736 {
48- _client = client ;
4937 _serviceProvider = serviceProvider ;
50- _botConfiguration = botConfiguration ;
51- _controllerMethods = new List < MethodInfo > ( ) ;
5238 _cancellationTokenSource = new CancellationTokenSource ( ) ;
5339 _logger = serviceProvider . GetRequiredService < ILogger < BotApp > > ( ) ;
5440 }
@@ -68,9 +54,15 @@ public IBot MapControllers()
6854 result . Add ( type ) ;
6955 }
7056 }
71- _controllerMethods = result
57+ var controllerMethods = result
7258 . SelectMany ( t => t . GetMethods ( BindingFlags . Public | BindingFlags . Instance | BindingFlags . DeclaredOnly ) )
7359 . ToList ( ) ;
60+ BotControllerMethodsContainer container = _serviceProvider . GetService < BotControllerMethodsContainer > ( )
61+ ?? throw new InvalidOperationException ( "Bot controller methods container is not registered." ) ;
62+ foreach ( var method in controllerMethods )
63+ {
64+ container . AddMethod ( method ) ;
65+ }
7466 return this ;
7567 }
7668
@@ -104,28 +96,6 @@ public async Task StartAsync(CancellationToken cancellationToken = default)
10496 var mergedToken = MergeTokens ( cancellationToken ) ;
10597 AppDomain . CurrentDomain . ProcessExit += new EventHandler ( OnProcessExit ) ;
10698 CheckDisposed ( ) ;
107- try
108- {
109- var botUser = _client . GetMe ( ) . Result ;
110- if ( _botConfiguration . ReceiveUpdates )
111- {
112- _client . StartReceiving ( UpdateHandler , ErrorHandler , cancellationToken : mergedToken ) ;
113- _logger . LogInformation ( "Bot '{botUser}' started - receiving updates." , botUser . Username ) ;
114- }
115- else
116- {
117- _logger . LogInformation ( "Bot '{botUser}' started - not receiving updates." , botUser . Username ) ;
118- }
119- }
120- catch ( Exception ex )
121- {
122- if ( ex is AggregateException aggregateException )
123- {
124- ex = aggregateException . InnerException ;
125- }
126- _logger . LogError ( ex , "Error occurred while starting the bot. Probably the bot token is invalid or the network is not available." ) ;
127- throw ex ;
128- }
12999
130100 var hostApplicationLifetime = _serviceProvider . GetService < IHostApplicationLifetime > ( ) as HostApplicationLifetime
131101 ?? throw new InvalidOperationException ( "Host application lifetime is not registered." ) ;
@@ -135,21 +105,7 @@ public async Task StartAsync(CancellationToken cancellationToken = default)
135105 foreach ( var hostedService in hostedServices )
136106 {
137107 await hostedService . StartAsync ( mergedToken ) ;
138- _logger . LogInformation ( "Started '{hostedService}'." , hostedService . GetType ( ) . Name ) ;
139- }
140-
141- var commandRegistrationBuilders = _serviceProvider . GetServices < CommandRegistrationBuilder > ( ) ;
142- if ( commandRegistrationBuilders != null && commandRegistrationBuilders . Any ( ) )
143- {
144- foreach ( var builder in commandRegistrationBuilders )
145- {
146- var commands = builder . Build ( ) ;
147- await _client . SetMyCommands ( commands ,
148- languageCode : builder . Language ,
149- cancellationToken : mergedToken ) ;
150- _logger . LogInformation ( "Registered {count} commands for language '{language}'." ,
151- commands . Count ( ) , builder . Language ) ;
152- }
108+ _logger . LogInformation ( "Started hosted service: '{hostedService}'." , hostedService . GetType ( ) . Name ) ;
153109 }
154110 }
155111
@@ -208,134 +164,6 @@ private CancellationToken MergeTokens(CancellationToken token)
208164 return CancellationTokenSource . CreateLinkedTokenSource ( _cancellationTokenSource . Token , token ) . Token ;
209165 }
210166
211- private Task ErrorHandler ( ITelegramBotClient client , Exception exception , CancellationToken token )
212- {
213- CheckDisposed ( ) ;
214- _logger . LogError ( exception , "Error occurred while receiving updates." ) ;
215- return Task . CompletedTask ;
216- }
217-
218- private async Task UpdateHandler ( ITelegramBotClient client , Update update , CancellationToken token )
219- {
220- CheckDisposed ( ) ;
221- if ( update . Message != null && ! string . IsNullOrWhiteSpace ( update . Message . Text ) && ! update . Message . Text . StartsWith ( '/' ) )
222- {
223- _logger . LogInformation ( "Received text message: {Text}." , update . Message . Text ) ;
224- var handler = new TextMessageHandler ( _controllerMethods , update ) ;
225- await HandleRequestAsync ( handler , update ) ;
226- }
227- else if ( update . Message != null && ! string . IsNullOrWhiteSpace ( update . Message . Text ) && update . Message . Text . StartsWith ( '/' ) )
228- {
229- _logger . LogInformation ( "Received text command: {Text}." , update . Message . Text ) ;
230- var handler = new TextCommandHandler ( _controllerMethods , update ) ;
231- await HandleRequestAsync ( handler , update ) ;
232- }
233- else if ( update . CallbackQuery != null && update . CallbackQuery . Data != null )
234- {
235- _logger . LogInformation ( "Received inline query: {Data}." , update . CallbackQuery . Data ) ;
236- var handler = new InlineQueryHandler ( _controllerMethods , update ) ;
237- await HandleRequestAsync ( handler , update ) ;
238- }
239- else
240- {
241- _logger . LogWarning ( "Unsupported update type: {UpdateType}." , update . Type ) ;
242- }
243- }
244-
245- private async Task HandleRequestAsync ( ITelegramUpdateHandler handler , Update update )
246- {
247- CheckDisposed ( ) ;
248- bool hasUser = update . TryGetUser ( out User user ) ;
249- if ( ! hasUser )
250- {
251- return ;
252- }
253- var args = handler . GetArguments ( ) ;
254- MethodInfo ? method = handler . GetMethodInfo ( ) ;
255- if ( method == null )
256- {
257- _logger . LogWarning ( "Method not found for message: {Text}." , update . Message ? . Text ) ;
258- return ;
259- }
260- bool isAuthorized = await AuthorizeAsync ( method , user ) ;
261- if ( ! isAuthorized )
262- {
263- return ;
264- }
265- CheckMethodMatching ( method , args ) ;
266- BotControllerBase controller = ( BotControllerBase ) ActivatorUtilities . CreateInstance ( _serviceProvider , method . DeclaringType ! ) ;
267- controller . Update = update ;
268- controller . User = user ;
269- controller . Client = _client ;
270- if ( _serviceProvider . GetService < IKeyValueProvider > ( ) is IKeyValueProvider keyValueProvider )
271- {
272- controller . KeyValueProvider = keyValueProvider ;
273- }
274- var result = method . Invoke ( controller , args ) ;
275- await ExecuteResultAsync ( result , user . Id ) ;
276- await DisposeAsync ( controller ) ;
277- await DisposeAsync ( result ) ;
278- }
279-
280- private void CheckMethodMatching ( MethodInfo method , object [ ] ? args )
281- {
282- if ( method . ReturnType != typeof ( Task < IActionResult > ) && method . ReturnType != typeof ( IActionResult ) )
283- {
284- throw new InvalidOperationException ( "Invalid return type: " + method . ReturnType . Name ) ;
285- }
286- if ( args != null && method . GetParameters ( ) . Length != args ? . Length )
287- {
288- throw new InvalidOperationException ( "Invalid arguments count: " + args ? . Length ) ;
289- }
290- }
291-
292- private async Task < bool > AuthorizeAsync ( MethodInfo method , User user )
293- {
294- if ( method . GetCustomAttribute < AuthorizeAttribute > ( ) != null
295- || method . DeclaringType ? . GetCustomAttribute < AuthorizeAttribute > ( ) != null )
296- {
297- if ( _serviceProvider . GetService < IBotAuthorizationHandler > ( ) is IBotAuthorizationHandler authorizationHandler )
298- {
299- if ( ! authorizationHandler . Authorize ( user ) )
300- {
301- await authorizationHandler
302- . HandleUnauthorized ( user )
303- . ExecuteResultAsync ( new ActionContext ( _client , user . Id ) ) ;
304- return false ;
305- }
306- }
307- }
308- return true ;
309- }
310-
311- private async Task DisposeAsync ( object obj )
312- {
313- if ( obj is IAsyncDisposable asyncDisposable )
314- {
315- await asyncDisposable . DisposeAsync ( ) ;
316- }
317- else if ( obj is IDisposable disposable )
318- {
319- disposable . Dispose ( ) ;
320- }
321- }
322-
323- private async Task ExecuteResultAsync ( object result , long userId )
324- {
325- if ( result is Task < IActionResult > taskResult )
326- {
327- await ( await taskResult ) . ExecuteResultAsync ( new ActionContext ( _client , userId ) ) ;
328- }
329- else if ( result is IActionResult actionResult )
330- {
331- await actionResult . ExecuteResultAsync ( new ActionContext ( _client , userId ) ) ;
332- }
333- else
334- {
335- throw new InvalidOperationException ( "Invalid result type: " + result . GetType ( ) . Name ) ;
336- }
337- }
338-
339167 private void CheckDisposed ( )
340168 {
341169 if ( _disposed )
0 commit comments