From 544962a1440ba881464156c8fa16922f59f9c217 Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Thu, 27 Jun 2024 20:18:43 +0530 Subject: [PATCH 1/7] So far so good :) --- lib/src/televerse/bot.dart | 99 +++++++++++++------ lib/src/televerse/context/context.dart | 20 ++-- .../televerse/conversation/conversation.dart | 8 +- lib/src/televerse/models/error.dart | 4 +- lib/src/televerse/models/handler_scope.dart | 4 +- 5 files changed, 88 insertions(+), 47 deletions(-) diff --git a/lib/src/televerse/bot.dart b/lib/src/televerse/bot.dart index c8cb1b17..9ae5b04b 100644 --- a/lib/src/televerse/bot.dart +++ b/lib/src/televerse/bot.dart @@ -1,5 +1,12 @@ part of '../../televerse.dart'; +/// Context Constructor is a function that takes two inputs and then creates a +typedef ContextConstructor = CTX Function({ + required RawAPI api, + required User me, + required Update update, +}); + /// Televerse /// This class is used to create a new bot instance. The bot instance is used to send and receive messages. /// @@ -15,7 +22,7 @@ part of '../../televerse.dart'; /// } /// ``` /// -class Bot { +class Bot { /// API Scheme final APIScheme _scheme; @@ -161,7 +168,8 @@ class Bot { onError(_defaultErrorHandler); // Perform initial /getMe request - getMe().then(_ignore).catchError(_thenHandleGetMeError); + _getMeRequest = getMe(); + _getMeRequest!.then(_ignore).catchError(_thenHandleGetMeError); // Set instance variable _instance = this; @@ -244,12 +252,18 @@ class Bot { this.fetcher.setApi(api); _instance = this; - getMe().then(_ignore).catchError(_thenHandleGetMeError); + _getMeRequest = getMe(); + _getMeRequest!.then(_ignore).catchError(_thenHandleGetMeError); } /// List of pending calls final List<_PendingCall> _pendingCalls = []; + /// (Internal) A Future that resolves to User of Bot info. + /// + /// In other words, initial getMe request :) + Future? _getMeRequest; + /// Whether _me is filled or not _GetMeStatus _getMeStatus = _GetMeStatus.notInitiated; @@ -317,7 +331,7 @@ class Bot { /// /// This method sets additional info if any has to be set. For example, this method currently sets `Context.matches` /// parameter if the [scope] is a RegExp handler. - void _preProcess(HandlerScope scope, Context context) async { + void _preProcess(HandlerScope scope, CTX context) async { if (scope.isRegExp) { final text = context.msg?.text ?? ""; if (scope.pattern != null) { @@ -327,7 +341,7 @@ class Bot { } /// Processes the Update as per the scope definition. - Future _processUpdate(HandlerScope scope, Context context) async { + Future _processUpdate(HandlerScope scope, CTX context) async { if (_isAsync(scope.handler!)) { try { await ((scope.handler!(context)) as Future); @@ -351,7 +365,7 @@ class Bot { /// Handler Scopes and proceeds to process the update. void _onUpdate(Update update) async { // Gets the sublist of Handler Scopes that is apt for the recieved Update - List sub = _handlerScopes.reversed.where((scope) { + List> sub = _handlerScopes.where((scope) { return scope.types.contains(update.type); }).toList(); @@ -362,7 +376,11 @@ class Bot { } // Creates the context instance for the update - final context = Context(this, update: update); + final context = _contextConstructor( + api: api, + me: !initialized ? await _getMeRequest! : me, + update: update, + ); // Indexes of the forked Handler Scopes final forks = sub.indexed @@ -420,7 +438,7 @@ class Bot { } /// List of Handler Scopes - final List _handlerScopes = []; + final List> _handlerScopes = []; /// List of middlewares added to the bot final List _middlewares = []; @@ -471,6 +489,23 @@ class Bot { } } + /// Use custom context + void useContext(ContextConstructor constructor) { + _usingCustomContext = true; + _contextConstructor = constructor; + } + + /// A flag that indicates whether Bot is operating with a custom context. + bool get isUsingCustomContext => _usingCustomContext; + bool _usingCustomContext = false; + ContextConstructor _contextConstructor = ({ + required api, + required me, + required update, + }) { + return Context(api: api, me: me, update: update) as CTX; + }; + /// To manually handle updates without fetcher /// /// This method is useful when you want to use a custom webhook server instead of the default one provided by Televerse, @@ -546,12 +581,12 @@ class Bot { /// This will reply "Hello!" to any message that starts with `/start`. void command( Pattern command, - Handler callback, { + Handler callback, { ScopeOptions? options, bool considerCaption = false, }) { if (initialized) { - final scope = HandlerScope( + final scope = HandlerScope( isCommand: true, options: options, handler: callback, @@ -598,7 +633,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [UpdateType.callbackQuery], @@ -622,7 +657,7 @@ class Bot { List types, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: types, @@ -683,7 +718,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -718,7 +753,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -754,7 +789,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: UpdateType.values, @@ -779,7 +814,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: UpdateType.messages(), @@ -812,7 +847,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, pattern: exp, isRegExp: true, @@ -834,7 +869,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -857,7 +892,7 @@ class Bot { /// Registers a callback for the `/settings` command. void settings( - Handler handler, { + Handler handler, { ScopeOptions? options, }) { return command( @@ -869,7 +904,7 @@ class Bot { /// Registers a callback for the `/help` command. void help( - Handler handler, { + Handler handler, { ScopeOptions? options, }) { return command( @@ -967,7 +1002,7 @@ class Bot { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: UpdateType.messages(), @@ -997,7 +1032,7 @@ class Bot { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: UpdateType.messages(), @@ -1027,7 +1062,7 @@ class Bot { ChatMemberStatus? newStatus, ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( handler: callback, options: options, types: [ @@ -1116,7 +1151,7 @@ class Bot { String? pollId, ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -1152,7 +1187,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -1170,7 +1205,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -1190,7 +1225,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -1262,7 +1297,7 @@ class Bot { ); } - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: UpdateType.messages(), @@ -1309,7 +1344,7 @@ class Bot { ); } if (username != null) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -1326,7 +1361,7 @@ class Bot { return _handlerScopes.add(scope); } - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -1573,7 +1608,7 @@ class Bot { ID? chatId, ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ @@ -2423,7 +2458,7 @@ class Bot { Handler callback, { ScopeOptions? options, }) { - final scope = HandlerScope( + final scope = HandlerScope( options: options, handler: callback, types: [ diff --git a/lib/src/televerse/context/context.dart b/lib/src/televerse/context/context.dart index 1d9f23c0..75320ea0 100644 --- a/lib/src/televerse/context/context.dart +++ b/lib/src/televerse/context/context.dart @@ -6,13 +6,10 @@ part of '../../../televerse.dart'; /// Currently Televerse support the following types of contexts: class Context { /// The RawAPI getter. - RawAPI get api => _bot.api.._addContext(this); + final RawAPI api; /// The bot that received the update's informations. - User get me => _bot.me; - - /// The RawAPI instance. - final Bot _bot; + final User me; /// The [Update] instance. /// @@ -36,10 +33,13 @@ class Context { } /// Creates a new context. - Context( - this._bot, { + Context({ + required this.api, + required this.me, required this.update, - }); + }) { + api._addContext(this); + } /// Contains the matches of the regular expression. (Internal) List? _matches; @@ -141,6 +141,6 @@ class Context { } /// Base handler -typedef Handler = FutureOr Function( - Context ctx, +typedef Handler = FutureOr Function( + CTX ctx, ); diff --git a/lib/src/televerse/conversation/conversation.dart b/lib/src/televerse/conversation/conversation.dart index 857e5895..2439f9d8 100644 --- a/lib/src/televerse/conversation/conversation.dart +++ b/lib/src/televerse/conversation/conversation.dart @@ -372,7 +372,13 @@ class Conversation { subscription = _bot.updatesStream.listen((update) { final sameChat = _isSameChat(update, chatId); if (sameChat && filter(update)) { - completer.complete(Context(_bot, update: update)); + completer.complete( + Context( + api: _bot.api, + me: _bot.me, + update: update, + ), + ); } }); diff --git a/lib/src/televerse/models/error.dart b/lib/src/televerse/models/error.dart index 87352bda..6474a0a9 100644 --- a/lib/src/televerse/models/error.dart +++ b/lib/src/televerse/models/error.dart @@ -4,7 +4,7 @@ part of 'models.dart'; /// It contains the error and the stack trace. /// /// Optionally, it can contain the context of the error. -class BotError implements Exception { +class BotError implements Exception { /// The error. /// /// This can possibly be a [TelegramException] or [TeleverseException] or [LongPollingException] or [DioException] @@ -14,7 +14,7 @@ class BotError implements Exception { final StackTrace stackTrace; /// The context of the error. - final Context? ctx; + final CTX? ctx; /// A flag that indicates whether the error occurred at a Middleware final bool sourceIsMiddleware; diff --git a/lib/src/televerse/models/handler_scope.dart b/lib/src/televerse/models/handler_scope.dart index 1927eea4..d2795aea 100644 --- a/lib/src/televerse/models/handler_scope.dart +++ b/lib/src/televerse/models/handler_scope.dart @@ -1,7 +1,7 @@ part of 'models.dart'; /// A Handler Scope is used to define the scope and related information of a handler method. -class HandlerScope { +class HandlerScope { /// If it's a command handler, we set `args` to the parameter of the command. final bool isCommand; @@ -14,7 +14,7 @@ class HandlerScope { /// Handler /// /// Required unless [isConversation] is `true`. - final Handler? handler; + final Handler? handler; /// The update type final List types; From f587d421316bc85eec689eb85c3db15045c01d03 Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Thu, 27 Jun 2024 20:20:47 +0530 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=91=80=20Handlers=20now=20use=20the?= =?UTF-8?q?=20custom=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/televerse/bot.dart | 170 ++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/lib/src/televerse/bot.dart b/lib/src/televerse/bot.dart index 9ae5b04b..10260152 100644 --- a/lib/src/televerse/bot.dart +++ b/lib/src/televerse/bot.dart @@ -630,7 +630,7 @@ class Bot { /// Registers a Handler Scope to listen to matching callback query. void _internalCallbackQueryRegister( Pattern data, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -653,7 +653,7 @@ class Bot { /// Adds Handler Scope for a Accept-All predicate void _acceptAll( - Handler callback, + Handler callback, List types, { ScopeOptions? options, }) { @@ -684,7 +684,7 @@ class Bot { /// void callbackQuery( Pattern data, - Handler callback, { + Handler callback, { @Deprecated("Use the 'data' parameter instead.") RegExp? regex, ScopeOptions? options, }) { @@ -715,7 +715,7 @@ class Bot { /// the [chatTypes] method. void chatType( ChatType type, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -750,7 +750,7 @@ class Bot { /// from a private chat or a group. void chatTypes( List types, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -786,7 +786,7 @@ class Bot { /// ``` void filter( bool Function(Context ctx) predicate, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -811,7 +811,7 @@ class Bot { /// ``` void text( String text, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -844,7 +844,7 @@ class Bot { /// matches the regular expression `Hello, (.*)!`. void hears( RegExp exp, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -866,7 +866,7 @@ class Bot { /// The callback will be called when an inline query with the specified query is received. void inlineQuery( Pattern query, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -998,7 +998,7 @@ class Bot { /// By default, this method will ONLY match entities in the message text. void entity( MessageEntityType type, - Handler callback, { + Handler callback, { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { @@ -1028,7 +1028,7 @@ class Bot { /// By default, this method will ONLY match entities in the message text. void entities( List types, - Handler callback, { + Handler callback, { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { @@ -1057,7 +1057,7 @@ class Bot { /// Registers callback for the [ChatMemberUpdated] events void _internalChatMemberUpdatedHandling({ - required Handler callback, + required Handler callback, ChatMemberStatus? oldStatus, ChatMemberStatus? newStatus, ScopeOptions? options, @@ -1100,7 +1100,7 @@ class Bot { /// You can optionally specify [ChatMemberStatus] to [oldStatus] and [newStatus] /// filter to only receive updates for a specific status. void chatMember( - Handler callback, { + Handler callback, { ChatMemberStatus? oldStatus, ChatMemberStatus? newStatus, ScopeOptions? options, @@ -1118,7 +1118,7 @@ class Bot { /// You can optionally specify [ChatMemberStatus] to [oldStatus] and [newStatus] /// filter to only receive updates for a specific status. void myChatMember({ - required Handler callback, + required Handler callback, ChatMemberStatus? oldStatus, ChatMemberStatus? newStatus, ScopeOptions? options, @@ -1133,7 +1133,7 @@ class Bot { /// Registers a callback for the [Update.poll] events. void poll( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1147,7 +1147,7 @@ class Bot { /// /// Optionally pass the [pollId] parameter to only receive updates for a specific poll. void pollAnswer( - Handler callback, { + Handler callback, { String? pollId, ScopeOptions? options, }) { @@ -1169,7 +1169,7 @@ class Bot { /// Registers a callback for the [Update.chosenInlineResult] events. /// The callback will be called when a chosen inline result is received. void chosenInlineResult( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1184,7 +1184,7 @@ class Bot { /// The callback will be called when a chat join request is received. void chatJoinRequest( ID chatId, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -1202,7 +1202,7 @@ class Bot { /// Registers a callback for Shipping Query events for the specified User. void shippingQuery( ID chatId, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -1222,7 +1222,7 @@ class Bot { /// Registers a callback for Pre Checkout Query events. void preCheckoutQuery( ChatID chatId, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -1241,7 +1241,7 @@ class Bot { /// Sets up a callback for when a message with URL is received. void onURL( - Handler callback, { + Handler callback, { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { @@ -1255,7 +1255,7 @@ class Bot { /// Sets up a callback for when a message with email is received. void onEmail( - Handler callback, { + Handler callback, { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { @@ -1269,7 +1269,7 @@ class Bot { /// Sets up a callback for when a message with phone number is received. void onPhoneNumber( - Handler callback, { + Handler callback, { bool shouldMatchCaptionEntities = false, ScopeOptions? options, }) { @@ -1283,7 +1283,7 @@ class Bot { /// Sets up a callback for when a message with hashtag is received. void onHashtag( - Handler callback, { + Handler callback, { bool shouldMatchCaptionEntities = false, String? hashtag, ScopeOptions? options, @@ -1331,7 +1331,7 @@ class Bot { /// That is you don't have to setup a different callback for [MessageEntityType.mention] /// and [MessageEntityType.textMention] entities. (Well, you can if you want to.) void onMention( - Handler callback, { + Handler callback, { String? username, int? userId, ScopeOptions? options, @@ -1386,7 +1386,7 @@ class Bot { /// This method sets up a callback to be fired when the bot is mentioned. void whenMentioned( - Handler callback, { + Handler callback, { ScopeOptions? options, }) async { if (initialized) { @@ -1411,7 +1411,7 @@ class Bot { /// Registers a callback for all Message updates void onMessage( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1423,7 +1423,7 @@ class Bot { /// Registers a callback for all Edited Message updates void onEditedMessage( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1435,7 +1435,7 @@ class Bot { /// Registers a callback for all Channel Post updates void onChannelPost( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1447,7 +1447,7 @@ class Bot { /// Registers a callback for all Edited Channel Post updates void onEditedChannelPost( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1459,7 +1459,7 @@ class Bot { /// Registers a callback for all Inline Query updates void onInlineQuery( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1471,7 +1471,7 @@ class Bot { /// Registers a callback for all Chosen Inline Result updates void onChosenInlineResult( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1483,7 +1483,7 @@ class Bot { /// Registers a callback for all Callback Query updates void onCallbackQuery( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1495,7 +1495,7 @@ class Bot { /// Registers a callback for all Shipping Query updates void onShippingQuery( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1507,7 +1507,7 @@ class Bot { /// Registers a callback for all Pre Checkout Query updates void onPreCheckoutQuery( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1519,7 +1519,7 @@ class Bot { /// Registers a callback to be fired for all successful payments void onSuccessfulPayment( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _internalSubMessageHandler( @@ -1531,7 +1531,7 @@ class Bot { /// Registers a callback for all Poll updates void onPoll( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1543,7 +1543,7 @@ class Bot { /// Registers a callback for all Poll Answer updates void onPollAnswer( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1555,7 +1555,7 @@ class Bot { /// Registers a callback for all My Chat Member updates void onMyChatMember( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1567,7 +1567,7 @@ class Bot { /// Registers a callback for all Chat Member updates void onChatMember( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1579,7 +1579,7 @@ class Bot { /// Registers a callback for all Chat Join Request updates void onChatJoinRequest( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -1601,7 +1601,7 @@ class Bot { /// Internal method to handle sub message handlers void _internalSubMessageHandler( - Handler callback, + Handler callback, bool Function(Context) predicate, { bool includeChannelPosts = false, bool onlyChannelPosts = false, @@ -1633,7 +1633,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onAudio( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1656,7 +1656,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onDocument( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1679,7 +1679,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onPhoto( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1702,7 +1702,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onSticker( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1725,7 +1725,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onVideo( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1748,7 +1748,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onVideoNote( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1771,7 +1771,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onVoice( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1789,7 +1789,7 @@ class Bot { /// Registers a callback for messages that contain a contact. void onContact( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -1808,7 +1808,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onDice( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1831,7 +1831,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onGame( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1854,7 +1854,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onPollMessage( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1877,7 +1877,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onVenue( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1900,7 +1900,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onLocation( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1918,7 +1918,7 @@ class Bot { /// Registers a callback for messages that is a live location update. void onLiveLocation( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -1938,7 +1938,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onNewChatTitle( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1961,7 +1961,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onNewChatPhoto( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -1979,7 +1979,7 @@ class Bot { /// Registers a callback for delete chat photo service messages. void onDeleteChatPhoto( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -1998,7 +1998,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onPinnedMessage( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2018,7 +2018,7 @@ class Bot { /// Registers a callback for a user is shared to the bot void onUsrShared( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2032,7 +2032,7 @@ class Bot { /// Registers a callback for a chat is shared to the bot void onChatShared( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2051,7 +2051,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void whenVideoChatScheduled( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2076,7 +2076,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void whenVideoChatStarted( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2101,7 +2101,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void whenVideoChatEnded( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2121,7 +2121,7 @@ class Bot { /// Registers a callback to be fired when new participants are invited to a video chat void whenVideoChatParticipantsInvited( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2135,7 +2135,7 @@ class Bot { /// Registers a callback to be fired when a forum topic is created void onForumTopicCreated( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2149,7 +2149,7 @@ class Bot { /// Registers a callback to be fired when a forum topic is edited void onForumTopicEdited( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2163,7 +2163,7 @@ class Bot { /// Registers a callback to be fired when a forum topic is closed void onForumTopicClosed( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2177,7 +2177,7 @@ class Bot { /// Registers a callback to be fired when a forum topic is reopened void onForumTopicReopened( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2191,7 +2191,7 @@ class Bot { /// Registers a callback to be fired when data sent from a web app is received void onWebAppData( - Handler callback, { + Handler callback, { ID? chatId, ScopeOptions? options, }) { @@ -2210,7 +2210,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onAnimation( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2235,7 +2235,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onText( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2260,7 +2260,7 @@ class Bot { /// /// Alternatively, you can pass the [onlyChannelPosts] parameter to only match messages that are sent in channels. void onCaption( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2353,7 +2353,7 @@ class Bot { /// Next step handler void setNextStep( Message msg, - Handler callback, + Handler callback, ScopeOptions? options, ) { final scopeName = "next-step-${msg.messageId}"; @@ -2378,7 +2378,7 @@ class Bot { /// /// This method will match any command that is sent to the bot. void onCommand( - Handler callback, { + Handler callback, { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, @@ -2402,7 +2402,7 @@ class Bot { /// Register a callback when a reaction to a message was changed by a user. void onMessageReaction( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2414,7 +2414,7 @@ class Bot { /// Reactions to a message with anonymous reactions were changed. void onMessageReactionCount( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2428,7 +2428,7 @@ class Bot { /// /// The bot must be an administrator in the chat for this to work. void onChatBoosted( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2442,7 +2442,7 @@ class Bot { /// /// The bot must be an administrator in the chat for this to work. void onChatBoostRemoved( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2455,7 +2455,7 @@ class Bot { /// Registers a callback to be fired when a user reacts given emoji to a message. void whenReacted( String emoji, - Handler callback, { + Handler callback, { ScopeOptions? options, }) { final scope = HandlerScope( @@ -2484,7 +2484,7 @@ class Bot { /// Registers a callback to be fired when a connection of the bot with a business account is made. void onBusinessConnection( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2496,7 +2496,7 @@ class Bot { /// Registers callback to be fired when a new message is received in a business account connected to the bot. void onBusinessMessage( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2508,7 +2508,7 @@ class Bot { /// Registers a callback to be fired when a business message is edited. void onBusinessMessageEdited( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( @@ -2520,7 +2520,7 @@ class Bot { /// Registers a callback to be fired when a business message is deleted. void onBusinessMessageDeleted( - Handler callback, { + Handler callback, { ScopeOptions? options, }) { return _acceptAll( From 1494e25e6d04517197254448465546ca0c0b1b54 Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Thu, 27 Jun 2024 22:41:23 +0530 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=A6=89=20Wow,=20a=20distant=20hoot=20?= =?UTF-8?q?can=20be=20heard=20All=20set=20to=20work=20with=20custom=20cont?= =?UTF-8?q?ext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/keyboard_button_poll_type.dart | 2 +- lib/src/televerse/bot.dart | 250 ++++++++++-------- .../televerse/conversation/conversation.dart | 201 +++++++++++--- lib/src/televerse/filters/on.dart | 6 +- lib/src/televerse/markups/inline_menu.dart | 86 +++--- lib/src/televerse/markups/keyboard_menu.dart | 140 +++++----- lib/src/televerse/markups/menu.dart | 17 +- lib/src/televerse/middlewares/middleware.dart | 5 +- lib/src/televerse/models/handler_scope.dart | 4 +- lib/src/televerse/models/scope_options.dart | 18 +- 10 files changed, 444 insertions(+), 285 deletions(-) diff --git a/lib/src/telegram/models/keyboard_button_poll_type.dart b/lib/src/telegram/models/keyboard_button_poll_type.dart index c6339531..b0aaae4a 100644 --- a/lib/src/telegram/models/keyboard_button_poll_type.dart +++ b/lib/src/telegram/models/keyboard_button_poll_type.dart @@ -11,7 +11,7 @@ class KeyboardButtonPollType { /// Creates a [KeyboardButtonPollType] object from JSON object factory KeyboardButtonPollType.fromJson(Map json) { return KeyboardButtonPollType( - type: json['type'], + type: PollType.fromJson(json['type']), ); } diff --git a/lib/src/televerse/bot.dart b/lib/src/televerse/bot.dart index 10260152..0144eb5b 100644 --- a/lib/src/televerse/bot.dart +++ b/lib/src/televerse/bot.dart @@ -47,13 +47,16 @@ class Bot { print(err.error); print(err.stackTrace); - print("---------------------"); print( "It seems you haven't set an error handler. You can do this with `bot.onError(...)` to manage most crashes.\n", ); if (fetcher.isActive) stop(); + _report(); + } + static void _report() { + print("---------------------"); print( "If you believe this issue is with the library, please raise an issue on\n" "the repository (link below). Please make sure you remove any sensitive information\n" @@ -445,7 +448,7 @@ class Bot { /// Applies middlewares over the passed context Future _applyMiddlewares( - Context ctx, + CTX ctx, Future Function() handler, ) async { int index = -1; @@ -499,11 +502,29 @@ class Bot { bool get isUsingCustomContext => _usingCustomContext; bool _usingCustomContext = false; ContextConstructor _contextConstructor = ({ - required api, - required me, - required update, - }) { - return Context(api: api, me: me, update: update) as CTX; + required RawAPI api, + required User me, + required Update update, + }) { + void handleTypeError() { + print("A `TypeError` occurred while trying to create Context.\n"); + if (CTX != Context) { + print( + "Seems like you have specified custom context type in Bot definition as " + "`Bot<$CTX>` but it seems like you haven't specified the constructor for your custom Context." + "\nYou can register the constructor method using:\n `bot.useContext($CTX.new);`\n\n", + ); + _report(); + print(""); + } + } + + try { + return Context(api: api, me: me, update: update) as CTX; + } catch (err) { + if (err is TypeError) handleTypeError(); + rethrow; + } }; /// To manually handle updates without fetcher @@ -521,7 +542,7 @@ class Bot { /// /// Optional [isServerless] flag can be passed to the method. If you set this flag to true, the bot will not start the fetcher. Future start([ - Handler? handler, + Handler? handler, bool isServerless = false, ]) async { // Registers a handler to listen for /start command @@ -582,7 +603,7 @@ class Bot { void command( Pattern command, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, bool considerCaption = false, }) { if (initialized) { @@ -631,7 +652,7 @@ class Bot { void _internalCallbackQueryRegister( Pattern data, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -655,7 +676,7 @@ class Bot { void _acceptAll( Handler callback, List types, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -686,7 +707,7 @@ class Bot { Pattern data, Handler callback, { @Deprecated("Use the 'data' parameter instead.") RegExp? regex, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalCallbackQueryRegister( data, @@ -716,7 +737,7 @@ class Bot { void chatType( ChatType type, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -751,7 +772,7 @@ class Bot { void chatTypes( List types, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -785,9 +806,9 @@ class Bot { /// }); /// ``` void filter( - bool Function(Context ctx) predicate, + bool Function(CTX ctx) predicate, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -812,7 +833,7 @@ class Bot { void text( String text, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -845,7 +866,7 @@ class Bot { void hears( RegExp exp, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -867,7 +888,7 @@ class Bot { void inlineQuery( Pattern query, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -893,7 +914,7 @@ class Bot { /// Registers a callback for the `/settings` command. void settings( Handler handler, { - ScopeOptions? options, + ScopeOptions? options, }) { return command( "settings", @@ -905,7 +926,7 @@ class Bot { /// Registers a callback for the `/help` command. void help( Handler handler, { - ScopeOptions? options, + ScopeOptions? options, }) { return command( "help", @@ -943,7 +964,7 @@ class Bot { /// /// This acts as a shortcut for the [entity] method and [onHashtag] methods. bool _internalEntityMatcher({ - required Context context, + required CTX context, required MessageEntityType type, String? content, bool shouldMatchCaptionEntities = false, @@ -1000,7 +1021,7 @@ class Bot { MessageEntityType type, Handler callback, { bool shouldMatchCaptionEntities = false, - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1030,7 +1051,7 @@ class Bot { List types, Handler callback, { bool shouldMatchCaptionEntities = false, - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1060,7 +1081,7 @@ class Bot { required Handler callback, ChatMemberStatus? oldStatus, ChatMemberStatus? newStatus, - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( handler: callback, @@ -1103,7 +1124,7 @@ class Bot { Handler callback, { ChatMemberStatus? oldStatus, ChatMemberStatus? newStatus, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalChatMemberUpdatedHandling( options: options, @@ -1121,7 +1142,7 @@ class Bot { required Handler callback, ChatMemberStatus? oldStatus, ChatMemberStatus? newStatus, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalChatMemberUpdatedHandling( callback: callback, @@ -1134,7 +1155,7 @@ class Bot { /// Registers a callback for the [Update.poll] events. void poll( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1149,7 +1170,7 @@ class Bot { void pollAnswer( Handler callback, { String? pollId, - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1170,7 +1191,7 @@ class Bot { /// The callback will be called when a chosen inline result is received. void chosenInlineResult( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1185,7 +1206,7 @@ class Bot { void chatJoinRequest( ID chatId, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1203,7 +1224,7 @@ class Bot { void shippingQuery( ID chatId, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1223,7 +1244,7 @@ class Bot { void preCheckoutQuery( ChatID chatId, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1243,7 +1264,7 @@ class Bot { void onURL( Handler callback, { bool shouldMatchCaptionEntities = false, - ScopeOptions? options, + ScopeOptions? options, }) { return entity( MessageEntityType.url, @@ -1257,7 +1278,7 @@ class Bot { void onEmail( Handler callback, { bool shouldMatchCaptionEntities = false, - ScopeOptions? options, + ScopeOptions? options, }) { return entity( MessageEntityType.email, @@ -1271,7 +1292,7 @@ class Bot { void onPhoneNumber( Handler callback, { bool shouldMatchCaptionEntities = false, - ScopeOptions? options, + ScopeOptions? options, }) { return entity( MessageEntityType.phoneNumber, @@ -1286,7 +1307,7 @@ class Bot { Handler callback, { bool shouldMatchCaptionEntities = false, String? hashtag, - ScopeOptions? options, + ScopeOptions? options, }) { if (hashtag == null) { return entity( @@ -1334,7 +1355,7 @@ class Bot { Handler callback, { String? username, int? userId, - ScopeOptions? options, + ScopeOptions? options, }) { if (username == null && userId == null) { return entity( @@ -1387,7 +1408,7 @@ class Bot { /// This method sets up a callback to be fired when the bot is mentioned. void whenMentioned( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) async { if (initialized) { return onMention( @@ -1412,7 +1433,7 @@ class Bot { /// Registers a callback for all Message updates void onMessage( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1424,7 +1445,7 @@ class Bot { /// Registers a callback for all Edited Message updates void onEditedMessage( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1436,7 +1457,7 @@ class Bot { /// Registers a callback for all Channel Post updates void onChannelPost( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1448,7 +1469,7 @@ class Bot { /// Registers a callback for all Edited Channel Post updates void onEditedChannelPost( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1460,7 +1481,7 @@ class Bot { /// Registers a callback for all Inline Query updates void onInlineQuery( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1472,7 +1493,7 @@ class Bot { /// Registers a callback for all Chosen Inline Result updates void onChosenInlineResult( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1484,7 +1505,7 @@ class Bot { /// Registers a callback for all Callback Query updates void onCallbackQuery( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1496,7 +1517,7 @@ class Bot { /// Registers a callback for all Shipping Query updates void onShippingQuery( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1508,7 +1529,7 @@ class Bot { /// Registers a callback for all Pre Checkout Query updates void onPreCheckoutQuery( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1520,7 +1541,7 @@ class Bot { /// Registers a callback to be fired for all successful payments void onSuccessfulPayment( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1532,7 +1553,7 @@ class Bot { /// Registers a callback for all Poll updates void onPoll( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1544,7 +1565,7 @@ class Bot { /// Registers a callback for all Poll Answer updates void onPollAnswer( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1556,7 +1577,7 @@ class Bot { /// Registers a callback for all My Chat Member updates void onMyChatMember( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1568,7 +1589,7 @@ class Bot { /// Registers a callback for all Chat Member updates void onChatMember( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1580,7 +1601,7 @@ class Bot { /// Registers a callback for all Chat Join Request updates void onChatJoinRequest( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -1606,7 +1627,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -1637,7 +1658,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1660,7 +1681,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1683,7 +1704,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1706,7 +1727,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1729,7 +1750,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1752,7 +1773,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1775,7 +1796,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1791,7 +1812,7 @@ class Bot { void onContact( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1812,7 +1833,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1835,7 +1856,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1858,7 +1879,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1881,7 +1902,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1904,7 +1925,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1920,7 +1941,7 @@ class Bot { void onLiveLocation( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1942,7 +1963,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1965,7 +1986,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -1981,7 +2002,7 @@ class Bot { void onDeleteChatPhoto( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2002,7 +2023,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2020,7 +2041,7 @@ class Bot { void onUsrShared( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2034,7 +2055,7 @@ class Bot { void onChatShared( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2055,7 +2076,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2080,7 +2101,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2105,7 +2126,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2123,7 +2144,7 @@ class Bot { void whenVideoChatParticipantsInvited( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2137,7 +2158,7 @@ class Bot { void onForumTopicCreated( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2151,7 +2172,7 @@ class Bot { void onForumTopicEdited( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2165,7 +2186,7 @@ class Bot { void onForumTopicClosed( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2179,7 +2200,7 @@ class Bot { void onForumTopicReopened( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2193,7 +2214,7 @@ class Bot { void onWebAppData( Handler callback, { ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2214,7 +2235,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2239,7 +2260,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2264,7 +2285,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2281,8 +2302,8 @@ class Bot { /// Attach an Inline Menu. /// /// This method will make the menu handlers to be called when the menu buttons are pressed. - void attachMenu(TeleverseMenu menu) { - if (menu is InlineMenu) { + void attachMenu(TeleverseMenu menu) { + if (menu is InlineMenu) { int rows = menu._buttons.length; for (int i = 0; i < rows; i++) { int cols = menu._buttons[i].length; @@ -2299,7 +2320,7 @@ class Bot { } } } - if (menu is KeyboardMenu) { + if (menu is KeyboardMenu) { int rows = menu._buttons.length; for (int i = 0; i < rows; i++) { int cols = menu._buttons[i].length; @@ -2308,32 +2329,41 @@ class Bot { final text = button.text; final name = "${menu.name}-$text"; - final action = button.handler as Handler; + final action = button.handler; switch (button.runtimeType) { case const (_KeyboardMenuTextButton): _internalSubMessageHandler( - action, + action!, (ctx) => ctx.message?.text == text, - options: ScopeOptions._createOrCopy(button.options, name: name), + options: ScopeOptions._createOrCopy( + button.options, + name: name, + ), ); break; case const (_KeyboardMenuRequestContactButton): _internalSubMessageHandler( - action, + action!, (ctx) => ctx.message?.contact != null, - options: ScopeOptions._createOrCopy(button.options, name: name), + options: ScopeOptions._createOrCopy( + button.options, + name: name, + ), ); break; case const (_KeyboardMenuRequestLocationButton): _internalSubMessageHandler( - action, + action!, (ctx) => ctx.message?.location != null, - options: ScopeOptions._createOrCopy(button.options, name: name), + options: ScopeOptions._createOrCopy( + button.options, + name: name, + ), ); break; case const (_KeyboardMenuRequestUsersButton): _internalSubMessageHandler( - action, + action!, (ctx) => ctx.message?.usersShared != null, options: ScopeOptions._createOrCopy(button.options, name: name), ); @@ -2344,7 +2374,7 @@ class Bot { } /// Remove an Inline Menu. - void removeMenu(TeleverseMenu menu) { + void removeMenu(TeleverseMenu menu) { _handlerScopes.removeWhere( (scope) => scope.name?.startsWith(menu.name) ?? false, ); @@ -2354,7 +2384,7 @@ class Bot { void setNextStep( Message msg, Handler callback, - ScopeOptions? options, + ScopeOptions? options, ) { final scopeName = "next-step-${msg.messageId}"; bool isNextMessage(int? messageId) { @@ -2382,7 +2412,7 @@ class Bot { bool includeChannelPosts = false, bool onlyChannelPosts = false, ID? chatId, - ScopeOptions? options, + ScopeOptions? options, }) { return _internalSubMessageHandler( callback, @@ -2403,7 +2433,7 @@ class Bot { /// Register a callback when a reaction to a message was changed by a user. void onMessageReaction( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2415,7 +2445,7 @@ class Bot { /// Reactions to a message with anonymous reactions were changed. void onMessageReactionCount( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2429,7 +2459,7 @@ class Bot { /// The bot must be an administrator in the chat for this to work. void onChatBoosted( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2443,7 +2473,7 @@ class Bot { /// The bot must be an administrator in the chat for this to work. void onChatBoostRemoved( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2456,7 +2486,7 @@ class Bot { void whenReacted( String emoji, Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { final scope = HandlerScope( options: options, @@ -2485,7 +2515,7 @@ class Bot { /// Registers a callback to be fired when a connection of the bot with a business account is made. void onBusinessConnection( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2497,7 +2527,7 @@ class Bot { /// Registers callback to be fired when a new message is received in a business account connected to the bot. void onBusinessMessage( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2509,7 +2539,7 @@ class Bot { /// Registers a callback to be fired when a business message is edited. void onBusinessMessageEdited( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, @@ -2521,7 +2551,7 @@ class Bot { /// Registers a callback to be fired when a business message is deleted. void onBusinessMessageDeleted( Handler callback, { - ScopeOptions? options, + ScopeOptions? options, }) { return _acceptAll( callback, diff --git a/lib/src/televerse/conversation/conversation.dart b/lib/src/televerse/conversation/conversation.dart index 2439f9d8..a1e9b746 100644 --- a/lib/src/televerse/conversation/conversation.dart +++ b/lib/src/televerse/conversation/conversation.dart @@ -2,44 +2,140 @@ part of '../../../televerse.dart'; /// ## Televerse Conversation /// -/// Televerse Conversation is a library that provides a simple way to create -/// a conversation between the user and the bot. +/// The `Conversation` class provides a streamlined way to manage interactions +/// between a Telegram bot and its users, simplifying the creation of conversational +/// flows. This class is part of the Televerse library, which is designed to ease the +/// development of Telegram bots by handling state management and message listening. /// -/// As a Telegram bot developer, there are times when you need to create a -/// conversation between the user and the bot. For example, when you need to -/// ask the user for some information, or when you need to ask the user to -/// choose one of the options. And this can be quite complicated. You need to -/// create a state machine, and you need to store the state of the conversation -/// somewhere. And this is where Televerse Conversation comes in. +/// ### Overview /// -/// Televerse Conversation provides a simple way to create a conversation -/// between the user and the bot. You can create a conversation with just a -/// few lines of code. And you don't need to worry about storing the state of -/// the conversation. Televerse Conversation will take care of that for you. +/// When developing a Telegram bot, it's common to require interactions that involve +/// multiple steps. For example, asking a user for their name and then responding +/// based on their input. Traditionally, this involves managing states and ensuring +/// the bot knows what to expect at each step. The `Conversation` class abstracts +/// this complexity by allowing developers to wait for specific types of messages +/// without manually handling state transitions. /// -/// Televerse Conversation is built on top of the Televerse library. So you -/// can use all the features of the Televerse library in your conversation. -class Conversation { +/// ### Features +/// +/// - **Message Filtering**: Wait for specific types of messages, such as text, photo, video, etc. +/// - **Timeouts**: Define timeouts for waiting messages to prevent hanging listeners. +/// - **Clear Unfulfilled Listeners**: Optionally clear previous unfulfilled listeners to avoid conflicts. +/// +/// ### Example Usage +/// +/// ```dart +/// // Create the Bot Instance +/// final bot = Bot(Platform.environment["BOT_TOKEN"]!); +/// +/// // Now create a conversation +/// final conv = Conversation(bot); +/// +/// void main(List args) { +/// bot.command('start', (ctx) async { +/// await ctx.reply("Hello, what's your name?"); +/// +/// // ✨ Magic happens here, you can simply wait for the user's response. +/// final nameCtx = await conv.waitForTextMessage(chatId: ctx.id); +/// await nameCtx?.reply("Well, hello ${nameCtx?.message?.text}"); +/// }); +/// +/// bot.start(); +/// } +/// ``` +/// +/// ### Creating a Conversation +/// +/// To create a conversation, instantiate the `Conversation` class with a bot instance. +/// You can optionally provide a custom name for the conversation: +/// +/// ```dart +/// final conv = Conversation(bot, name: "my_conversation"); +/// ``` +/// +/// ### Waiting for Messages +/// +/// The `Conversation` class provides several methods to wait for different types of messages: +/// +/// ```dart +/// // Wait for a text message +/// final textCtx = await conv.waitForTextMessage(chatId: ctx.id); +/// +/// // Wait for a photo message +/// final photoCtx = await conv.waitForPhotoMessage(chatId: ctx.id); +/// ``` +/// +/// Each method returns a `Future` that completes with a `Context` object containing the incoming update. +/// +/// ### Handling Timeouts +/// +/// You can specify a timeout for each wait method to prevent hanging listeners: +/// +/// ```dart +/// final textCtx = await conv.waitForTextMessage( +/// chatId: ctx.id, +/// timeout: Duration(seconds: 30), +/// ); +/// ``` +/// +/// If the timeout is reached, the `Future` completes with an error. +/// +/// ### Clearing Listeners +/// +/// You can clear all conversation listeners for a specific chat or all listeners: +/// +/// ```dart +/// // Clear all listeners for a specific chat +/// await conv.clear(ctx.id); +/// +/// // Clear all listeners for all chats +/// await conv.clearAll(); +/// ``` +/// +/// The `Conversation` class simplifies managing user interactions in Telegram bots, allowing you to create complex conversational flows with minimal effort. +class Conversation { /// A list of subscriptions. - final List<_ISubHelper> _subscriptionsList = []; + final List<_ISubHelper> _subscriptionsList = []; /// Name of the conversation. final String name; /// The bot that this conversation belongs to. - final Bot _bot; + final Bot _bot; - /// Creates a new conversation. + /// Creates a new conversation instance. + /// + /// This constructor initializes a new [Conversation] object associated with a + /// specific bot. You can optionally provide a custom name for the conversation; + /// otherwise, a default name will be generated. + /// + /// ### Parameters: + /// - [bot]: The bot instance that this conversation belongs to. This is required + /// and allows the conversation to interact with the bot. + /// - [name]: An optional parameter to specify a custom name for the conversation. + /// If not provided, a default name in the format "conv-xxxxx" will be generated + /// where "xxxxx" is a random 5-character string. + /// + /// ### Example: + /// ```dart + /// // Create a new conversation with the bot + /// final bot = Bot(Platform.environment["BOT_TOKEN"]!); + /// final conversation = Conversation(bot, name: "myCustomConversation"); + /// + /// // Create a new conversation with a default name + /// final defaultConversation = Conversation(bot); + /// ``` Conversation( - this._bot, { + Bot bot, { String? name, - }) : name = name ?? "conv-${_getRandomID(5)}"; + }) : _bot = bot, + name = name ?? "conv-${_getRandomID(5)}"; /// Wait for a text message from the user. /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, , returns a [Context] object with the incoming update. - Future waitForTextMessage({ + Future waitForTextMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -57,7 +153,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForPhotoMessage({ + Future waitForPhotoMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -75,7 +171,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForVideoMessage({ + Future waitForVideoMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -93,7 +189,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForVoiceMessage({ + Future waitForVoiceMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -111,7 +207,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForDocumentMessage({ + Future waitForDocumentMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -129,7 +225,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForContactMessage({ + Future waitForContactMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -147,7 +243,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForLocationMessage({ + Future waitForLocationMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -165,7 +261,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForVenueMessage({ + Future waitForVenueMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -183,7 +279,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForPollMessage({ + Future waitForPollMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -201,7 +297,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForDiceMessage({ + Future waitForDiceMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -219,7 +315,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForGameMessage({ + Future waitForGameMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -237,7 +333,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForStickerMessage({ + Future waitForStickerMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -255,7 +351,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForVideoNoteMessage({ + Future waitForVideoNoteMessage({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -273,7 +369,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitToStartVideoChat({ + Future waitToStartVideoChat({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -291,7 +387,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitToEndVideoChat({ + Future waitToEndVideoChat({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -309,7 +405,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForCallbackQuery({ + Future waitForCallbackQuery({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -337,7 +433,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitFor({ + Future waitFor({ required ID chatId, Duration? timeout, bool clearUnfulfilled = true, @@ -366,14 +462,14 @@ class Conversation { 'already exists. It has been removed.'); } - final completer = Completer(); + final completer = Completer(); StreamSubscription? subscription; subscription = _bot.updatesStream.listen((update) { final sameChat = _isSameChat(update, chatId); if (sameChat && filter(update)) { completer.complete( - Context( + _bot._contextConstructor( api: _bot.api, me: _bot.me, update: update, @@ -383,7 +479,7 @@ class Conversation { }); _subscriptionsList.add( - _ISubHelper(subscription, scopeName, completer), + _ISubHelper(subscription, scopeName, completer), ); _bot._handlerScopes.add( @@ -454,7 +550,7 @@ class Conversation { /// /// Possibly returns `null` if the listener has been cancelled before /// it completes. Otherwise, returns a [Context] object with the incoming update. - Future waitForPattern({ + Future waitForPattern({ required ID chatId, required Pattern pattern, Duration? timeout, @@ -478,13 +574,32 @@ class Conversation { } /// Internal helper class to store the subscription and the scope name. -class _ISubHelper { +/// +/// This class is used internally by the [Conversation] class to manage +/// the state of subscriptions and their corresponding scope names. +class _ISubHelper { + /// The subscription to the bot's update stream. final StreamSubscription subscription; + + /// The scope name associated with this subscription. final String scope; - final Completer completer; + /// A completer that will be completed when the subscription is fulfilled or cancelled. + final Completer completer; + + /// Creates a new instance of [_ISubHelper]. + /// + /// ### Parameters: + /// - [subscription]: The subscription to the bot's update stream. + /// - [scope]: The scope name associated with this subscription. + /// - [completer]: The completer that will be completed when the subscription + /// is fulfilled or cancelled. const _ISubHelper(this.subscription, this.scope, this.completer); + /// Cancels the subscription. + /// + /// If the completer has not been completed yet, it will complete with `null`. + /// This method returns a [Future] that completes when the subscription is cancelled. Future cancel() { if (!completer.isCompleted) { completer.complete(null); diff --git a/lib/src/televerse/filters/on.dart b/lib/src/televerse/filters/on.dart index a71d1147..a8bfc584 100644 --- a/lib/src/televerse/filters/on.dart +++ b/lib/src/televerse/filters/on.dart @@ -1,13 +1,13 @@ part of '../../../televerse.dart'; /// The On event. Attaches handlers for specific events. -extension On on Bot { +extension On on Bot { /// Registers a callback for particular filter types. /// /// The call back will be only be executed on specific update types. You can /// use the [TeleverseEvent] object to specify which update you want to listen to. - void on(TeleverseEvent type, Handler callback) { - final scope = HandlerScope( + void on(TeleverseEvent type, Handler callback) { + final scope = HandlerScope( handler: callback, types: type.types, predicate: (ctx) { diff --git a/lib/src/televerse/markups/inline_menu.dart b/lib/src/televerse/markups/inline_menu.dart index 7dd3b574..a25a0e00 100644 --- a/lib/src/televerse/markups/inline_menu.dart +++ b/lib/src/televerse/markups/inline_menu.dart @@ -1,13 +1,14 @@ part of '../../../televerse.dart'; /// This object represents a Keyboard with callback data. -class _InlineMenuCallbackDataButton extends _TMenuButton { +class _InlineMenuCallbackDataButton + extends _TMenuButton { final String data; const _InlineMenuCallbackDataButton( super.text, this.data, - Handler handler, { + Handler handler, { super.options, }) : super( hasHandler: true, @@ -28,7 +29,7 @@ class _InlineMenuCallbackDataButton extends _TMenuButton { } } -class _InlineMenuUrlButton extends _TMenuButton { +class _InlineMenuUrlButton extends _TMenuButton { final String url; const _InlineMenuUrlButton(super.text, this.url); @@ -41,7 +42,7 @@ class _InlineMenuUrlButton extends _TMenuButton { } } -class _InlineMenuWebAppButton extends _TMenuButton { +class _InlineMenuWebAppButton extends _TMenuButton { final String url; const _InlineMenuWebAppButton(super.text, this.url); @@ -56,7 +57,7 @@ class _InlineMenuWebAppButton extends _TMenuButton { } } -class _InlineMenuLoginButton extends _TMenuButton { +class _InlineMenuLoginButton extends _TMenuButton { final String url; final String? forwardText; final String? botUsername; @@ -84,7 +85,8 @@ class _InlineMenuLoginButton extends _TMenuButton { } } -class _InlineMenuSwitchInlineQueryButton extends _TMenuButton { +class _InlineMenuSwitchInlineQueryButton + extends _TMenuButton { final String query; const _InlineMenuSwitchInlineQueryButton(super.text, this.query); @@ -97,7 +99,8 @@ class _InlineMenuSwitchInlineQueryButton extends _TMenuButton { } } -class _InlineMenuSwitchInlineQueryCurrentChatButton extends _TMenuButton { +class _InlineMenuSwitchInlineQueryCurrentChatButton + extends _TMenuButton { final String query; const _InlineMenuSwitchInlineQueryCurrentChatButton(super.text, this.query); @@ -110,7 +113,8 @@ class _InlineMenuSwitchInlineQueryCurrentChatButton extends _TMenuButton { } } -class _InlineMenuSwitchInlineQueryChosenChatButton extends _TMenuButton { +class _InlineMenuSwitchInlineQueryChosenChatButton + extends _TMenuButton { final SwitchInlineQueryChosenChat switchInlineQueryChosenChat; const _InlineMenuSwitchInlineQueryChosenChatButton( super.text, @@ -126,7 +130,8 @@ class _InlineMenuSwitchInlineQueryChosenChatButton extends _TMenuButton { } } -class _InlineMenuCallbackGameButton extends _TMenuButton { +class _InlineMenuCallbackGameButton + extends _TMenuButton { final CallbackGame callbackGame; const _InlineMenuCallbackGameButton(super.text, this.callbackGame); @@ -139,7 +144,7 @@ class _InlineMenuCallbackGameButton extends _TMenuButton { } } -class _InlineMenuPayButton extends _TMenuButton { +class _InlineMenuPayButton extends _TMenuButton { const _InlineMenuPayButton(super.text); @override @@ -152,13 +157,14 @@ class _InlineMenuPayButton extends _TMenuButton { } /// This object represents a Inline Keyboard with the action to be done. -class InlineMenu implements InlineKeyboardMarkup, TeleverseMenu { +class InlineMenu + implements InlineKeyboardMarkup, TeleverseMenu { /// Name of the menu @override final String name; /// Map that represents the text and action to be done - final List> _buttons; + final List>> _buttons; /// Constructs a InlineMenu /// @@ -209,49 +215,49 @@ class InlineMenu implements InlineKeyboardMarkup, TeleverseMenu { } /// Add new item to the last row - InlineMenu text( + InlineMenu text( String text, - Handler handler, { + Handler handler, { required String data, - ScopeOptions? options, + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _InlineMenuCallbackDataButton( + _InlineMenuCallbackDataButton( text, data, handler, options: options, ), ); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a URL button to the last row - InlineMenu url( + InlineMenu url( String text, String url, ) { if (_buttons.isEmpty) _buttons.add([]); - _buttons.last.add(_InlineMenuUrlButton(text, url)); + _buttons.last.add(_InlineMenuUrlButton(text, url)); inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Web App button to the last row - InlineMenu webApp( + InlineMenu webApp( String text, String url, ) { if (_buttons.isEmpty) _buttons.add([]); - _buttons.last.add(_InlineMenuWebAppButton(text, url)); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + _buttons.last.add(_InlineMenuWebAppButton(text, url)); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Login button to the last row - InlineMenu login( + InlineMenu login( String text, String url, { String? forwardText, @@ -260,7 +266,7 @@ class InlineMenu implements InlineKeyboardMarkup, TeleverseMenu { }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _InlineMenuLoginButton( + _InlineMenuLoginButton( text, url, forwardText: forwardText, @@ -268,65 +274,65 @@ class InlineMenu implements InlineKeyboardMarkup, TeleverseMenu { requestWriteAccess: requestWriteAccess, ), ); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Switch Inline Query button to the last row - InlineMenu switchInlineQuery(String text, String query) { + InlineMenu switchInlineQuery(String text, String query) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _InlineMenuSwitchInlineQueryButton( + _InlineMenuSwitchInlineQueryButton( text, query, ), ); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Switch Inline Query Current Chat button to the last row - InlineMenu switchInlineQueryCurrentChat(String text, String query) { + InlineMenu switchInlineQueryCurrentChat(String text, String query) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _InlineMenuSwitchInlineQueryCurrentChatButton( + _InlineMenuSwitchInlineQueryCurrentChatButton( text, query, ), ); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Switch Inline Query Chosen Chat button to the last row - InlineMenu switchInlineQueryChosenChat( + InlineMenu switchInlineQueryChosenChat( String text, SwitchInlineQueryChosenChat switchInlineQueryChosenChat, ) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _InlineMenuSwitchInlineQueryChosenChatButton( + _InlineMenuSwitchInlineQueryChosenChatButton( text, switchInlineQueryChosenChat, ), ); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Callback Game button to the last row - InlineMenu callbackGame(String text, CallbackGame game) { + InlineMenu callbackGame(String text, CallbackGame game) { if (_buttons.isEmpty) _buttons.add([]); - _buttons.last.add(_InlineMenuCallbackGameButton(text, game)); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + _buttons.last.add(_InlineMenuCallbackGameButton(text, game)); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } /// Add a Pay button to the last row - InlineMenu pay(String text) { + InlineMenu pay(String text) { if (_buttons.isEmpty) _buttons.add([]); - _buttons.last.add(_InlineMenuPayButton(text)); - inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); + _buttons.last.add(_InlineMenuPayButton(text)); + inlineKeyboard = TeleverseMenu._makeInlineKeyboard(_buttons); return this; } diff --git a/lib/src/televerse/markups/keyboard_menu.dart b/lib/src/televerse/markups/keyboard_menu.dart index aa3b25f1..a63a7111 100644 --- a/lib/src/televerse/markups/keyboard_menu.dart +++ b/lib/src/televerse/markups/keyboard_menu.dart @@ -1,9 +1,9 @@ part of '../../../televerse.dart'; -class _KeyboardMenuTextButton extends _TMenuButton { +class _KeyboardMenuTextButton extends _TMenuButton { const _KeyboardMenuTextButton( super.text, - Handler handler, { + Handler handler, { super.options, }) : super(hasHandler: true, handler: handler); @@ -15,13 +15,14 @@ class _KeyboardMenuTextButton extends _TMenuButton { } } -class _KeyboardMenuRequestUsersButton extends _TMenuButton { +class _KeyboardMenuRequestUsersButton + extends _TMenuButton { final KeyboardButtonRequestUsers requestUsers; const _KeyboardMenuRequestUsersButton( super.text, this.requestUsers, - Handler handler, { + Handler handler, { super.options, }) : super(hasHandler: true, handler: handler); @@ -29,18 +30,19 @@ class _KeyboardMenuRequestUsersButton extends _TMenuButton { Map toJson() { return { 'text': text, - ...requestUsers.toJson(), + "request_users": requestUsers.toJson(), }; } } -class _KeyboardMenuRequestChatButton extends _TMenuButton { +class _KeyboardMenuRequestChatButton + extends _TMenuButton { final KeyboardButtonRequestChat requestChat; const _KeyboardMenuRequestChatButton( super.text, this.requestChat, - Handler handler, { + Handler handler, { super.options, }) : super(hasHandler: true, handler: handler); @@ -48,15 +50,16 @@ class _KeyboardMenuRequestChatButton extends _TMenuButton { Map toJson() { return { 'text': text, - ...requestChat.toJson(), + "request_chat": requestChat.toJson(), }; } } -class _KeyboardMenuRequestContactButton extends _TMenuButton { +class _KeyboardMenuRequestContactButton + extends _TMenuButton { const _KeyboardMenuRequestContactButton( super.text, - Handler handler, { + Handler handler, { super.options, }) : super(hasHandler: true, handler: handler); @@ -69,10 +72,11 @@ class _KeyboardMenuRequestContactButton extends _TMenuButton { } } -class _KeyboardMenuRequestLocationButton extends _TMenuButton { +class _KeyboardMenuRequestLocationButton + extends _TMenuButton { const _KeyboardMenuRequestLocationButton( super.text, - Handler handler, { + Handler handler, { super.options, }) : super(hasHandler: true, handler: handler); @@ -85,25 +89,26 @@ class _KeyboardMenuRequestLocationButton extends _TMenuButton { } } -class _KeyboardMenuRequestPollButton extends _TMenuButton { +class _KeyboardMenuRequestPollButton + extends _TMenuButton { final KeyboardButtonPollType requestPoll; const _KeyboardMenuRequestPollButton( super.text, this.requestPoll, { super.options, - }) : super(hasHandler: true); + }); @override Map toJson() { return { 'text': text, - ...requestPoll.toJson(), + "request_poll": requestPoll.toJson(), }; } } -class _KeyboardMenuWebAppButton extends _TMenuButton { +class _KeyboardMenuWebAppButton extends _TMenuButton { final String url; const _KeyboardMenuWebAppButton( @@ -115,21 +120,22 @@ class _KeyboardMenuWebAppButton extends _TMenuButton { Map toJson() { return { 'text': text, - 'web_app': WebAppInfo( - url: url, - ).toJson(), + 'web_app': { + "url": url, + }, }; } } /// This object represents a Keyboard menu with the actions to be done. -class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { +class KeyboardMenu + implements ReplyKeyboardMarkup, TeleverseMenu { /// Name of the menu @override String name; /// Map that represents the text and action to be done - final List> _buttons; + final List>> _buttons; /// Constructs a KeyboardMenu /// @@ -154,79 +160,79 @@ class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { name = name ?? _getRandomID(); /// Add a new row to the keyboard - KeyboardMenu row() { + KeyboardMenu row() { if (_buttons.last.isEmpty) return this; _buttons.add([]); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Add new item to the last row - KeyboardMenu text( + KeyboardMenu text( String text, - Handler handler, { - ScopeOptions? options, + Handler handler, { + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuTextButton( + _KeyboardMenuTextButton( text, handler, options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Request contact from the user - KeyboardMenu requestContact( + KeyboardMenu requestContact( String text, - Handler handler, { - ScopeOptions? options, + Handler handler, { + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuRequestContactButton( + _KeyboardMenuRequestContactButton( text, handler, options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Request location from the user - KeyboardMenu requestLocation( + KeyboardMenu requestLocation( String text, - Handler handler, { - ScopeOptions? options, + Handler handler, { + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuRequestLocationButton( + _KeyboardMenuRequestLocationButton( text, handler, options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Request the user to select a user from the list - KeyboardMenu requestUser({ + KeyboardMenu requestUser({ required String text, - required Handler handler, + required Handler handler, required int requestId, bool? userIsBot, bool? userIsPremium, - ScopeOptions? options, + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuRequestUsersButton( + _KeyboardMenuRequestUsersButton( text, KeyboardButtonRequestUsers( requestId: requestId, @@ -238,23 +244,23 @@ class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Request the user to select multiple users from the list - KeyboardMenu requestUsers({ + KeyboardMenu requestUsers({ required String text, - required Handler handler, + required Handler handler, required int requestId, bool? userIsBot, bool? userIsPremium, int? maxQuantity, - ScopeOptions? options, + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuRequestUsersButton( + _KeyboardMenuRequestUsersButton( text, KeyboardButtonRequestUsers( requestId: requestId, @@ -266,14 +272,14 @@ class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Requests the user to select a chat from the list. - KeyboardMenu requestChat({ + KeyboardMenu requestChat({ required String text, - required Handler handler, + required Handler handler, required int requestId, bool chatIsChannel = false, bool? chatIsForum, @@ -282,11 +288,11 @@ class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { ChatAdministratorRights? userAdministratorRights, ChatAdministratorRights? botAdministratorRights, bool? botIsMember, - ScopeOptions? options, + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuRequestChatButton( + _KeyboardMenuRequestChatButton( text, KeyboardButtonRequestChat( requestId: requestId, @@ -302,33 +308,33 @@ class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Add a web app button to the last row - KeyboardMenu webApp(String text, String url) { + KeyboardMenu webApp(String text, String url) { if (_buttons.isEmpty) _buttons.add([]); - _buttons.last.add(_KeyboardMenuWebAppButton(text, url)); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + _buttons.last.add(_KeyboardMenuWebAppButton(text, url)); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } /// Add a poll button to the last row - KeyboardMenu requestPoll( - String text, - KeyboardButtonPollType requestPoll, { - ScopeOptions? options, + KeyboardMenu requestPoll( + String text, { + PollType? type, + ScopeOptions? options, }) { if (_buttons.isEmpty) _buttons.add([]); _buttons.last.add( - _KeyboardMenuRequestPollButton( + _KeyboardMenuRequestPollButton( text, - requestPoll, + KeyboardButtonPollType(type: type), options: options, ), ); - keyboard = TeleverseMenu._makeKeyboard(_buttons); + keyboard = TeleverseMenu._makeKeyboard(_buttons); return this; } @@ -377,25 +383,25 @@ class KeyboardMenu implements ReplyKeyboardMarkup, TeleverseMenu { bool? selective; /// Makes the menu resized - KeyboardMenu resized() { + KeyboardMenu resized() { resizeKeyboard = true; return this; } /// Makes the menu persistent - KeyboardMenu persistent() { + KeyboardMenu persistent() { isPersistent = true; return this; } /// Makes the menu one time - KeyboardMenu oneTime() { + KeyboardMenu oneTime() { oneTimeKeyboard = true; return this; } /// Makes the menu selective - KeyboardMenu makeSelective() { + KeyboardMenu makeSelective() { selective = true; return this; } diff --git a/lib/src/televerse/markups/menu.dart b/lib/src/televerse/markups/menu.dart index 721b0e80..a07e9c52 100644 --- a/lib/src/televerse/markups/menu.dart +++ b/lib/src/televerse/markups/menu.dart @@ -1,11 +1,11 @@ part of '../../../televerse.dart'; /// Abstract internal class to represent a menu button -abstract class _TMenuButton { +abstract class _TMenuButton { final bool hasHandler; final String text; - final Handler? handler; - final ScopeOptions? options; + final Handler? handler; + final ScopeOptions? options; const _TMenuButton( this.text, { @@ -23,7 +23,7 @@ abstract class _TMenuButton { } /// Abstract class to represent a menu -abstract class TeleverseMenu { +abstract class TeleverseMenu { /// Name of the menu final String name; @@ -31,8 +31,9 @@ abstract class TeleverseMenu { Map toJson(); /// Converts a list of rows to a list of InlineKeyboardButton - static List> _makeInlineKeyboard( - List>? rows, + static List> + _makeInlineKeyboard( + List>>? rows, ) { if (rows == null) return []; return rows.map((row) { @@ -43,8 +44,8 @@ abstract class TeleverseMenu { } /// Converts a list of rows to a list of KeyboardButton - static List> _makeKeyboard( - List>? rows, + static List> _makeKeyboard( + List>>? rows, ) { if (rows == null) return []; return rows.map((row) { diff --git a/lib/src/televerse/middlewares/middleware.dart b/lib/src/televerse/middlewares/middleware.dart index 778d350a..85896f5e 100644 --- a/lib/src/televerse/middlewares/middleware.dart +++ b/lib/src/televerse/middlewares/middleware.dart @@ -21,7 +21,8 @@ part of '../../../televerse.dart'; /// /// By using the `Middleware` class, you can create reusable components that /// can be attached to the bot and executed in sequence. -abstract interface class Middleware implements MiddlewareBase { +abstract interface class Middleware + implements MiddlewareBase { /// The middleware function that processes the [Context] and optionally /// passes control to the next middleware or main handler. /// @@ -45,7 +46,7 @@ abstract interface class Middleware implements MiddlewareBase { /// } /// ``` FutureOr handle( - Context ctx, + CTX ctx, NextFunction next, ); diff --git a/lib/src/televerse/models/handler_scope.dart b/lib/src/televerse/models/handler_scope.dart index d2795aea..cb7a53f0 100644 --- a/lib/src/televerse/models/handler_scope.dart +++ b/lib/src/televerse/models/handler_scope.dart @@ -20,7 +20,7 @@ class HandlerScope { final List types; /// Predicate to filter updates - final bool Function(Context ctx) predicate; + final bool Function(CTX ctx) predicate; /// A flag that indicates if this is a conversation scope. final bool isConversation; @@ -29,7 +29,7 @@ class HandlerScope { final ID? chatId; /// Scope Options - Additional parameters for the scope. - final ScopeOptions? options; + final ScopeOptions? options; /// Creates a new [HandlerScope]. HandlerScope({ diff --git a/lib/src/televerse/models/scope_options.dart b/lib/src/televerse/models/scope_options.dart index 30cef5d9..772d6776 100644 --- a/lib/src/televerse/models/scope_options.dart +++ b/lib/src/televerse/models/scope_options.dart @@ -1,7 +1,7 @@ part of '../../../televerse.dart'; /// Represents additional options that can be passed to create Handler Scope -class ScopeOptions { +class ScopeOptions { /// Name of the Handler Scope. /// /// This can be used to remove a Handler later on your code. @@ -33,7 +33,7 @@ class ScopeOptions { /// /// If by any reason, this method threw any exception, it'll be caught in the passed `Bot.onError` if set. /// Otherwise, treated as `false` evaluation and skips the current handler. - final FutureOr Function(Context ctx)? customPredicate; + final FutureOr Function(CTX ctx)? customPredicate; /// Constructs a `ScopeOption`. /// @@ -68,12 +68,12 @@ class ScopeOptions { /// The update is only processed if the predicate function evaluates to `true`. /// /// Returns a new instance of [ScopeOptions] with the updated properties. - ScopeOptions copyWith({ + ScopeOptions copyWith({ String? name, bool? forked, - FutureOr Function(Context ctx)? customPredicate, + FutureOr Function(CTX ctx)? customPredicate, }) { - return ScopeOptions( + return ScopeOptions( name: name ?? this.name, forked: forked ?? this.forked, customPredicate: customPredicate ?? this.customPredicate, @@ -81,11 +81,11 @@ class ScopeOptions { } /// Internal copyWith - static ScopeOptions _createOrCopy( - ScopeOptions? options, { + static ScopeOptions _createOrCopy( + ScopeOptions? options, { String? name, bool? forked, - FutureOr Function(Context ctx)? customPredicate, + FutureOr Function(CTX ctx)? customPredicate, }) { if (options != null) { return options.copyWith( @@ -95,7 +95,7 @@ class ScopeOptions { ); } - return ScopeOptions( + return ScopeOptions( name: name, forked: forked ?? false, customPredicate: customPredicate, From 04b20753b5adb70695cbcc77d1c4485c19e11017 Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Fri, 28 Jun 2024 15:43:09 +0530 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=8C=A7=EF=B8=8F=20Classy=20finishing?= =?UTF-8?q?=20touch=20to=20Custom=20Context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/televerse/{ => api}/raw_api.dart | 2 +- lib/src/televerse/{ => bot}/bot.dart | 164 +++++++++++++++------ lib/src/televerse/context/constructor.dart | 8 + lib/src/televerse/fetch/fetch.dart | 6 +- lib/src/televerse/fetch/long_polling.dart | 6 +- lib/src/televerse/models/models.dart | 20 +-- lib/televerse.dart | 5 +- 7 files changed, 146 insertions(+), 65 deletions(-) rename lib/src/televerse/{ => api}/raw_api.dart (99%) rename lib/src/televerse/{ => bot}/bot.dart (93%) create mode 100644 lib/src/televerse/context/constructor.dart diff --git a/lib/src/televerse/raw_api.dart b/lib/src/televerse/api/raw_api.dart similarity index 99% rename from lib/src/televerse/raw_api.dart rename to lib/src/televerse/api/raw_api.dart index bb7a6022..95beaee7 100644 --- a/lib/src/televerse/raw_api.dart +++ b/lib/src/televerse/api/raw_api.dart @@ -1,4 +1,4 @@ -part of '../../televerse.dart'; +part of '../../../televerse.dart'; /// Raw API for the Telegram Bot API. class RawAPI { diff --git a/lib/src/televerse/bot.dart b/lib/src/televerse/bot/bot.dart similarity index 93% rename from lib/src/televerse/bot.dart rename to lib/src/televerse/bot/bot.dart index 0144eb5b..a5ea34ce 100644 --- a/lib/src/televerse/bot.dart +++ b/lib/src/televerse/bot/bot.dart @@ -1,11 +1,4 @@ -part of '../../televerse.dart'; - -/// Context Constructor is a function that takes two inputs and then creates a -typedef ContextConstructor = CTX Function({ - required RawAPI api, - required User me, - required Update update, -}); +part of '../../../televerse.dart'; /// Televerse /// This class is used to create a new bot instance. The bot instance is used to send and receive messages. @@ -38,7 +31,7 @@ class Bot { /// Options to configure the logger. final LoggerOptions? _loggerOptions; - void _defaultErrorHandler(BotError err) { + void _defaultErrorHandler(BotError err) { print("‼️ An error occurred while processing the update."); if (err.sourceIsMiddleware) { print("The exception is occurred at an attached middleware."); @@ -68,7 +61,7 @@ class Bot { } /// Handler for unexpected errors. - late FutureOr Function(BotError error) _onError; + late FutureOr Function(BotError error) _onError; /// The timeout duration for the requests. /// @@ -130,7 +123,7 @@ class Bot { } /// The fetcher - used to fetch updates from the Telegram servers. - late final Fetcher fetcher; + late final Fetcher fetcher; /// The bot token. final String token; @@ -154,7 +147,7 @@ class Bot { /// If you're running the Bot API server locally, you should pass the [baseURL] and the [scheme] parameters to the constructor. Bot( this.token, { - Fetcher? fetcher, + Fetcher? fetcher, String baseURL = RawAPI.defaultBase, APIScheme scheme = APIScheme.https, LoggerOptions? loggerOptions, @@ -162,20 +155,9 @@ class Bot { }) : _baseURL = _cookBaseUrlString(baseURL), isLocal = baseURL != RawAPI.defaultBase, _loggerOptions = loggerOptions, - _scheme = scheme { - // Create Fetcher and assign the RawAPI instance - this.fetcher = fetcher ?? LongPolling(); - this.fetcher.setApi(api); - - // Set the default erorr handler - onError(_defaultErrorHandler); - - // Perform initial /getMe request - _getMeRequest = getMe(); - _getMeRequest!.then(_ignore).catchError(_thenHandleGetMeError); - - // Set instance variable - _instance = this; + _scheme = scheme, + _api = RawAPI(token, loggerOptions: loggerOptions, timeout: timeout) { + _initializeBot(fetcher); } /// Function to ignore things. @@ -190,7 +172,7 @@ class Bot { err = TeleverseException.timeoutException(st, timeout!); } } - final botErr = BotError(err, st); + final botErr = BotError(err, st); await _onError(botErr); } @@ -220,7 +202,7 @@ class Bot { /// Learn to setup Local Bot API Server here: https://github.com/tdlib/telegram-bot-api factory Bot.local( String token, { - Fetcher? fetcher, + Fetcher? fetcher, String baseURL = "localhost:8081", APIScheme scheme = APIScheme.http, LoggerOptions? loggerOptions, @@ -243,20 +225,32 @@ class Bot { /// `RawAPI` instance separately. Bot.fromAPI( RawAPI api, { - Fetcher? fetcher, + Fetcher? fetcher, }) : _api = api, _baseURL = api._baseUrl, isLocal = api._baseUrl != RawAPI.defaultBase, _loggerOptions = api._httpClient.loggerOptions, _scheme = api._scheme, timeout = api.timeout, - token = api.token, - fetcher = fetcher ?? LongPolling() { - this.fetcher.setApi(api); - _instance = this; + token = api.token { + _initializeBot(fetcher); + } + + /// Initializes the bot with common setup logic. + void _initializeBot(Fetcher? fetcher) { + // Create Fetcher and assign the RawAPI instance + this.fetcher = fetcher ?? LongPolling(); + this.fetcher.setApi(_api!); + + // Set the default error handler + onError(_defaultErrorHandler); + // Perform initial /getMe request _getMeRequest = getMe(); _getMeRequest!.then(_ignore).catchError(_thenHandleGetMeError); + + // Set instance variable + _instance = this; } /// List of pending calls @@ -379,7 +373,7 @@ class Bot { } // Creates the context instance for the update - final context = _contextConstructor( + final context = await _contextConstructor( api: api, me: !initialized ? await _getMeRequest! : me, update: update, @@ -417,7 +411,7 @@ class Bot { await sub[i].options!.customPredicate!.call(context); if (!customPass) continue; } catch (err, stack) { - final botErr = BotError(err, stack); + final botErr = BotError(err, stack); await _onError(botErr); continue; } @@ -460,7 +454,7 @@ class Bot { try { await _middlewares[index].handle(ctx, next); } catch (err, stack) { - final botErr = BotError( + final botErr = BotError( err, stack, sourceIsMiddleware: true, @@ -471,7 +465,7 @@ class Bot { try { await handler(); } catch (err, stack) { - final botErr = BotError(err, stack); + final botErr = BotError(err, stack); _onError(botErr); } } @@ -492,15 +486,76 @@ class Bot { } } - /// Use custom context - void useContext(ContextConstructor constructor) { + /// Use custom context constructor to specify how custom context instances should be created. + /// + /// This method allows you to register a constructor function that creates instances + /// of a custom context type (`CTX`). The custom context must extend the base `Context` class + /// and its constructor should match the required parameters (`api`, `me`, and `update`). + /// + /// If you specify a custom context type (`Bot`), ensure you register + /// the constructor using this method (`contextBuilder`) before initializing your bot instance. + /// + /// Example usage: + /// ```dart + /// class MyCustomContext extends Context { + /// MyCustomContext({ + /// required super.api, + /// required super.me, + /// required super.update, + /// }); + /// } + /// + /// final bot = Bot(token); + /// bot.contextBuilder(MyCustomContext.new); + /// ``` + /// + /// If you encounter a `TypeError` indicating that the constructor for your custom context + /// hasn't been registered, make sure to use this method to specify the constructor. + /// + /// ```dart + /// bot.contextBuilder(MyCustomContext.new); + /// ``` + /// + /// For detailed usage instructions and examples, visit the [Custom Context Documentation](https://televerse.web.app/doc/custom-context). + void contextBuilder(ContextConstructor constructor) { _usingCustomContext = true; _contextConstructor = constructor; } - /// A flag that indicates whether Bot is operating with a custom context. + /// A flag that indicates whether the Bot is configured to use a custom context. + /// + /// When set to `true`, it means the Bot instance is using a custom context type (`CTX`), + /// and the context constructor (`_contextConstructor`) has been registered to create instances + /// of this custom context. + /// + /// To use a custom context, you must register its constructor using [contextBuilder]. + /// + /// Example usage: + /// ```dart + /// final bot = Bot(token); + /// bot.contextBuilder(MyCustomContext.new); + /// print(bot.isUsingCustomContext); // true + /// ``` bool get isUsingCustomContext => _usingCustomContext; + + /// Internal flag to track whether the Bot is configured to use a custom context. bool _usingCustomContext = false; + + /// Context constructor function used to create instances of the custom context (`CTX`). + /// + /// This function is registered using [contextBuilder] and is responsible for creating + /// instances of the custom context type (`CTX`) with the required parameters (`api`, `me`, `update`). + /// + /// Example implementation: + /// ```dart + /// ContextConstructor _contextConstructor = ({ + /// required RawAPI api, + /// required User me, + /// required Update update, + /// }) { + /// return MyCustomContext(api: api, me: me, update: update); + /// }; + /// ``` ContextConstructor _contextConstructor = ({ required RawAPI api, required User me, @@ -508,15 +563,30 @@ class Bot { }) { void handleTypeError() { print("A `TypeError` occurred while trying to create Context.\n"); + if (CTX != Context) { print( - "Seems like you have specified custom context type in Bot definition as " - "`Bot<$CTX>` but it seems like you haven't specified the constructor for your custom Context." - "\nYou can register the constructor method using:\n `bot.useContext($CTX.new);`\n\n", + "It seems like you have specified a custom context type in the Bot definition as `Bot<$CTX>`, " + "but you haven't registered the constructor for your custom Context.\n" + "Generally, you can register a constructor with the following line:\n" + " bot.useContext($CTX.new);\n\n" + "For example, if you have a custom context class `MyContext`:\n\n" + "class MyContext extends Context {\n" + " MyContext({required super.api, required super.me, required super.update});\n" + "}\n\n" + "You should register the constructor in your bot setup as follows:\n\n" + "final bot = Bot(token);\n" + "bot.useContext(MyContext.new);\n\n" + "This ensures that the bot can create instances of your custom context correctly.\n" + "If you still encounter issues, make sure that your custom context class extends `Context` " + "and its constructor matches the required parameters:\n\n" + " MyContext({required super.api, required super.me, required super.update});\n" + "📖 Check out the complete usage documentation here: https://televerse.web.app/doc/custom-context\n", ); - _report(); - print(""); } + + _report(); + print(""); } try { @@ -560,7 +630,7 @@ class Bot { return await fetcher.start(); } catch (err, stack) { fetcher.stop(); - final botErr = BotError(err, stack); + final botErr = BotError(err, stack); await _onError(botErr); return fetcher.start(); } @@ -952,7 +1022,7 @@ class Bot { /// Note: you DON'T have to manually wait for the [ResponseParameters.retryAfter] duration. /// The fetcher will automatically wait for the duration and start polling again. void onError( - void Function(BotError err) handler, + void Function(BotError err) handler, ) { _onError = handler; fetcher.onError(handler); diff --git a/lib/src/televerse/context/constructor.dart b/lib/src/televerse/context/constructor.dart new file mode 100644 index 00000000..70df5262 --- /dev/null +++ b/lib/src/televerse/context/constructor.dart @@ -0,0 +1,8 @@ +part of '../../../televerse.dart'; + +/// Context Constructor is a function that takes two inputs and then creates a +typedef ContextConstructor = FutureOr Function({ + required RawAPI api, + required User me, + required Update update, +}); diff --git a/lib/src/televerse/fetch/fetch.dart b/lib/src/televerse/fetch/fetch.dart index c0baf089..a9dc4851 100644 --- a/lib/src/televerse/fetch/fetch.dart +++ b/lib/src/televerse/fetch/fetch.dart @@ -18,7 +18,7 @@ part 'webhook.dart'; /// **Fetcher** - This is the base class for all fetchers. It is used to fetch updates from the Telegram API. /// You can use this class to create your own fetcher. Currently, there are two fetchers: [LongPolling] and [Webhook]. -abstract class Fetcher { +abstract class Fetcher { /// The stream controller that emits new updates. final StreamController _updateStreamController; @@ -56,10 +56,10 @@ abstract class Fetcher { void setApi(RawAPI api) => this.api = api; /// Error handler for long polling. - FutureOr Function(BotError err)? _onError; + FutureOr Function(BotError err)? _onError; /// Sets the error handler for long polling. - void onError(FutureOr Function(BotError err) onError) => + void onError(FutureOr Function(BotError err) onError) => _onError = onError; /// Flag to check if the fetcher is running. diff --git a/lib/src/televerse/fetch/long_polling.dart b/lib/src/televerse/fetch/long_polling.dart index 113cb362..5bffc844 100644 --- a/lib/src/televerse/fetch/long_polling.dart +++ b/lib/src/televerse/fetch/long_polling.dart @@ -2,7 +2,7 @@ part of 'fetch.dart'; /// A class that handles long polling. /// This class is used to fetch updates from the Telegram API. It uses the long polling method. -class LongPolling extends Fetcher { +class LongPolling extends Fetcher { /// Delay time between each long polling request. final Duration delayDuration; @@ -106,7 +106,7 @@ class LongPolling extends Fetcher { // If the onError handler is set, call it. if (_onError != null) { final longError = err.toLongPollingException(stackTrace); - final botErr = BotError(longError, stackTrace); + final botErr = BotError(longError, stackTrace); await _onError!(botErr); await _awaitRetryAfter(err.parameters?.retryAfter); } else if (err.parameters?.retryAfter != null) { @@ -119,7 +119,7 @@ class LongPolling extends Fetcher { } } else { if (_onError != null) { - final botErr = BotError(err, stackTrace); + final botErr = BotError(err, stackTrace); await _onError!(botErr); } else { _doubleRetryDelay(); diff --git a/lib/src/televerse/models/models.dart b/lib/src/televerse/models/models.dart index 2b2edcc4..7c98712f 100644 --- a/lib/src/televerse/models/models.dart +++ b/lib/src/televerse/models/models.dart @@ -6,19 +6,21 @@ library; import 'dart:async'; import 'dart:io' as io; import 'dart:typed_data'; -import 'package:televerse/telegram.dart'; -import 'package:televerse/televerse.dart'; + import 'package:dio/dio.dart' show DioException, MultipartFile; +import 'package:televerse/telegram.dart'; +import 'package:televerse/televerse.dart' hide HandlerScope; + part 'chat_id.dart'; -part 'input_file.dart'; -part 'webhook_exception.dart'; -part 'telegram_exception.dart'; -part 'long_polling_exception.dart'; -part 'televerse_exception.dart'; -part 'mention.dart'; part 'custom_emoji.dart'; +part 'error.dart'; part 'handler_scope.dart'; part 'inline_menu_data.dart'; -part 'error.dart'; +part 'input_file.dart'; +part 'long_polling_exception.dart'; +part 'mention.dart'; part 'payload.dart'; +part 'telegram_exception.dart'; +part 'televerse_exception.dart'; +part 'webhook_exception.dart'; diff --git a/lib/televerse.dart b/lib/televerse.dart index 5806a495..b6970f2d 100644 --- a/lib/televerse.dart +++ b/lib/televerse.dart @@ -35,12 +35,12 @@ export 'src/televerse/extensions/extensions.dart'; export 'src/televerse/links/links.dart'; part 'src/utils/http.dart'; -part 'src/televerse/bot.dart'; +part 'src/televerse/bot/bot.dart'; part 'src/utils/date.dart'; part 'src/utils/utils.dart'; part 'src/televerse/markups/keyboard.dart'; part 'src/televerse/markups/inline_keyboard.dart'; -part 'src/televerse/raw_api.dart'; +part 'src/televerse/api/raw_api.dart'; part 'src/televerse/filters/on.dart'; part 'src/televerse/filters/filters.dart'; part 'src/televerse/markups/inline_menu.dart'; @@ -51,6 +51,7 @@ part 'src/televerse/models/logger_options.dart'; part 'src/televerse/context/context.dart'; part 'src/televerse/context/methods.dart'; part 'src/televerse/context/properties.dart'; +part 'src/televerse/context/constructor.dart'; /// Conversation API part 'src/televerse/conversation/conversation.dart'; From 24c664cf545caa4a80ce0379379c9925b5bb5f81 Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Fri, 28 Jun 2024 15:43:27 +0530 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=94=83=20Sort=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/televerse.dart | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/televerse.dart b/lib/televerse.dart index b6970f2d..0e3a8582 100644 --- a/lib/televerse.dart +++ b/lib/televerse.dart @@ -24,34 +24,36 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io' as io; import 'dart:math'; + import 'package:dio/dio.dart'; + import 'package:televerse/telegram.dart'; import 'package:televerse/televerse.dart'; -export 'src/televerse/models/models.dart'; -export 'src/types/types.dart'; -export 'src/televerse/fetch/fetch.dart'; export 'src/televerse/extensions/extensions.dart'; +export 'src/televerse/fetch/fetch.dart'; export 'src/televerse/links/links.dart'; +export 'src/televerse/models/models.dart'; +export 'src/types/types.dart'; -part 'src/utils/http.dart'; -part 'src/televerse/bot/bot.dart'; -part 'src/utils/date.dart'; -part 'src/utils/utils.dart'; -part 'src/televerse/markups/keyboard.dart'; -part 'src/televerse/markups/inline_keyboard.dart'; part 'src/televerse/api/raw_api.dart'; -part 'src/televerse/filters/on.dart'; +part 'src/televerse/bot/bot.dart'; +part 'src/televerse/context/constructor.dart'; +part 'src/televerse/context/context.dart'; +part 'src/televerse/context/methods.dart'; +part 'src/televerse/context/properties.dart'; part 'src/televerse/filters/filters.dart'; +part 'src/televerse/filters/on.dart'; +part 'src/televerse/markups/inline_keyboard.dart'; part 'src/televerse/markups/inline_menu.dart'; -part 'src/televerse/markups/menu.dart'; +part 'src/televerse/markups/keyboard.dart'; part 'src/televerse/markups/keyboard_menu.dart'; -part 'src/televerse/models/multipart_helper.dart'; +part 'src/televerse/markups/menu.dart'; part 'src/televerse/models/logger_options.dart'; -part 'src/televerse/context/context.dart'; -part 'src/televerse/context/methods.dart'; -part 'src/televerse/context/properties.dart'; -part 'src/televerse/context/constructor.dart'; +part 'src/televerse/models/multipart_helper.dart'; +part 'src/utils/date.dart'; +part 'src/utils/http.dart'; +part 'src/utils/utils.dart'; /// Conversation API part 'src/televerse/conversation/conversation.dart'; From 2b94b6fccf18f7c6d9b37d2b7c9806e40b7c0020 Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Fri, 28 Jun 2024 16:10:47 +0530 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=93=9A=20Welcome=20Home?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 14 ++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 734c1cad..908ef2e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 1.20.0 + +- 🥳 Support for Custom Context +- Added detailed + [usage documentation for custom context here](https://televerse.web.app/doc/custom-context). +- The custom context feature allows you to extend the base functionality of your + bot by using your own custom context classes. +- Added `Bot.contextBuilder` method +- Refactors across the library to cope with `` + - Refactored `Bot`, `Handler`, `Fetcher`, `Menu`, `BotError`, `Conversation` + etc. +- Fixed an issue in `KeyboardMenu` that caused some features not to work. +- Added documentation on almost all new classes and typedefs. + # 1.19.5 - 🧑🏻‍🔧 Fix in `InlineQueryResultBuilder` methods `voice` and `video`. diff --git a/pubspec.yaml b/pubspec.yaml index c788816e..3bfa8b71 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: televerse description: Televerse lets you create your own efficient Telegram bots with ease in Dart. Supports latest Telegram Bot API - 7.5! -version: 1.19.5 +version: 1.20.0 homepage: https://github.com/HeySreelal/televerse topics: - telegram From c98619bc83b8ce0089602574e6ba030afae927eb Mon Sep 17 00:00:00 2001 From: Sreelal TS Date: Fri, 28 Jun 2024 17:01:46 +0530 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=8E=96=EF=B8=8F=20Updated=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 1470ca4b..61af39fa 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,22 @@ public interface, making it easy for developers to write strictly typed code. ## 🔥 What's latest? +### 🗓️ June 28, 2024 + +🎉 Support for Custom Contexts! + +Televerse now lets you build even more powerful bots with custom contexts! + +- Design custom context classes to store information specific to your bot's + needs. +- Keep your bot's logic organized with private context members and methods. +- Add features like localization by integrating mixins. + +With the new `Bot.contextBuilder` method, you can define specialized context +constructors to create context objects with personalized behaviors and +capabilities. This update allows you to tailor your bot's responses, handle +complex workflows, and integrate additional features seamlessly. + ### 🗓️ June 22, 2024 Introducing Middleware & Transformer support! You can now use the `Bot.use`