Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Televerse v1.19.1 #261

Merged
merged 4 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 1.19.0
# 1.19.1

- 🆕 Support for Middlewares & Transformers
- Middlewares lets you attach handlers that are run before your main handler is
Expand All @@ -15,6 +15,15 @@
[#257](https://github.com/HeySreelal/televerse/pull/257) (Thanks to @devsdocs)
- ⚠️ Type of `Webhook.certificate` is changed to InputFile as described by the
official documentation.
- Added examples for
[Middleware](https://github.com/xooniverse/TeleverseExamples/blob/1c30d889d3a0b1d7bdceaad48d6cfd88208e87be/lib/middleware_example.dart)
and
[Transformer](https://github.com/xooniverse/TeleverseExamples/blob/1c30d889d3a0b1d7bdceaad48d6cfd88208e87be/lib/transformer_example.dart)
usage in Examples repo.

# 1.19.0

- [Retracted]

# 1.18.0

Expand Down
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,63 @@ bot.start((ctx) async {
Efficiently build inline query results with the InlineQueryResultBuilder,
simplifying the process of generating inline query results.

### 13. 🔌 Plugin Support

Televerse support Middlewares and Transformers in the library. These features
allow you to preprocess and manipulate API requests seamlessly.

#### Middlewares

Middlewares let you execute code before your main handler is run. This is useful
for tasks like logging, authentication, and more.

#### Example: Logging Middleware

```dart
class LoggingMiddleware implements Middleware {
@override
Future<void> handle(
Context ctx,
NextFunction next,
) async {
print('Received update: ${ctx.update}');
await next();
}
}

// Usage
bot.use(LoggingMiddleware());
```

#### Transformers

Transformers allow you to alter the request payloads directly, providing a more
flexible way to modify requests before they are sent to the API.

#### Example: Auto Replier Transformer

```dart
class AutoReplier implements Transformer {
@override
FutureOr<Map<String, dynamic>> transform(
APIMethod method,
Map<String, dynamic> payload,
Context? ctx,
) {
final isSendMethod = APIMethod.sendMethods.contains(method);
final isNotChatAction = method != APIMethod.sendChatAction;

if (isSendMethod && isNotChatAction) {
payload["reply_markup"] = ForceReply().toJson();
}
return payload;
}
}

// Usage
bot.use(AutoReplier());
```

---

## 🌟 Shoot a Star
Expand Down Expand Up @@ -380,3 +437,6 @@ clean, maintainable code that responds to messages and updates on Telegram. So,
what are you waiting for? Start building your Telegram bot with Televerse today!

[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Say%20Thanks-blue?style=flat-square&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/heysreelal)

```
```
2 changes: 1 addition & 1 deletion lib/src/televerse/bot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ class Bot {

if (index < _middlewares.length) {
try {
await _middlewares[index].fn(ctx, next);
await _middlewares[index].handle(ctx, next);
} catch (err, stack) {
final botErr = BotError(
err,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/televerse/context/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class Context {
/// Executes the middleware on the current context
Future<void> use(MiddlewareBase middleware) async {
if (middleware is Middleware) {
await middleware.fn(this, () async {});
await middleware.handle(this, () async {});
}

if (middleware is Transformer) {
Expand Down
136 changes: 92 additions & 44 deletions lib/src/televerse/middlewares/middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,119 @@ part of '../../../televerse.dart';
/// Base class for the middlewares
sealed class MiddlewareBase {}

/// The next handler to be executed.
/// A typedef for the next function used in middleware.
///
/// Invoking this function basically means, you want the next handler to also run.
/// This function should be called within a middleware to pass control to the
/// next middleware in the stack. If it's not called, the subsequent middlewares
/// and the main handler won't be executed.
typedef NextFunction = FutureOr<void> Function();

/// Represents a Middleware.
/// Represents a Middlware that can be used to process requests
/// before they reach the main handler.
///
/// Middlewares are attached to the bot with the `bot.use` method. Middlewares act as an intermediate
/// handler before the actual handler.
/// This class should be implemented by any middleware that needs to perform
/// operations on the [Context] object or decide whether to pass control to
/// the next middleware or the main handler.
///
/// These intermediate functions can be used to process or modify the Context before it reaches the main handler.
/// Example usage:
/// ```dart
/// class Logger implements Middleware {
/// @override
/// FutureOr<void> handle(Context ctx, NextFunction next) async {
/// print('Request received: ${ctx.update}');
/// await next(); // Pass control to the next middleware or handler
/// print('Response sent');
/// }
/// }
/// ```
///
/// 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 {
/// The middleware function
/// The middleware function that processes the [Context] and optionally
/// passes control to the next middleware or main handler.
///
/// The [handle] function should call the [next] function to pass control
/// to the next middleware in the stack or to the main handler if there are
/// no more middlewares. If [next] is not called, the subsequent middlewares
/// and the main handler will not be executed.
///
/// - [ctx] - The current context where the middleware is operating on
/// Example usage:
/// ```dart
/// @override
/// FutureOr<void> handle(Context ctx, NextFunction next) async {
/// // Perform some pre-processing
/// print('Before main handler');
///
/// - [next] - The next handler method. Gives you ability to decide whether to run or skip the next handler.
FutureOr<void> fn(
/// // Pass control to the next middleware or the main handler
/// await next();
///
/// // Perform some post-processing
/// print('After main handler');
/// }
/// ```
FutureOr<void> handle(
Context ctx,
NextFunction next,
);

/// Constructs a Middleware
/// Constructs a [Middleware] instance.
const Middleware();
}

/// Represents a interceptor which can alter the API requests.
/// Represents a transformer that can alter API requests before they are sent.
///
/// This class should be implemented by any transformer that needs to modify
/// the request payload based on the API method being called, the provided
/// payload, or the context.
///
/// Example usage:
/// ```dart
/// class AutoReplier implements Transformer {
/// @override
/// FutureOr<Map<String, dynamic>> transform(
/// APIMethod method,
/// Map<String, dynamic> payload,
/// Context? ctx,
/// ) {
/// final isSendMethod = APIMethod.sendMethods.contains(method);
/// final isNotChatAction = method != APIMethod.sendChatAction;
///
/// if (isSendMethod && isNotChatAction) {
/// payload["reply_markup"] = ForceReply().toJson();
/// }
/// return payload;
/// }
/// }
/// ```
///
/// By using the `Transformer` class, you can create reusable components that
/// can be attached to the bot to modify the request payload dynamically.
abstract interface class Transformer implements MiddlewareBase {
/// The API interceptor function.
///
/// ## Available Arguments
/// Here are the different arugments available to the transfomer function.
///
/// ### 1. `APIMethod method`
///
/// This parameter tells which API Method is now being called. Let's you decide whether to
/// perform or not perform the transfomer on the payload.
/// The function that processes and alters the API request payload.
///
/// ### 2. `Map<String, dynamic> payload`
/// ## Parameters
/// - `APIMethod method`: Indicates which API method is being called. This allows
/// you to decide whether or not to transform the payload based on the method.
///
/// The payload is the actual JSON body object to be posted to the current method.
/// Check the [Telegram Bot API documentation](https://core.telegram.org/bots/api) to learn about all the possible
/// parameters that can be passed. In most fo the cases, you will be altering the payload with a transformer. So,
/// it is important to alter and return the valid JSON after transformation.
/// - `Map<String, dynamic> payload`: The actual JSON body object to be posted
/// to the current method. This payload can be altered and should be returned
/// as a valid JSON after transformation. Refer to the [Telegram Bot API documentation](https://core.telegram.org/bots/api)
/// to understand the possible parameters that can be passed.
///
/// ### 3. `Context? ctx`
/// The available context object. If the user is invoking RawAPI methods through the context object, such as
/// `ctx.reply` or `ctx.answerCallbackQuery` the particular context object can be accessed with this argument.
/// Having access to the Context object primarily lets you take control of the whole incoming update. This lets you
/// further enhance your transfomrmer.
///
/// Be aware that `ctx` can be `null`, this occurs when user is invoking the method directly through the `RawAPI` instance.
/// - `Context? ctx`: The context object, if available. This is accessible when
/// the user invokes `RawAPI` methods through the context object, such as `ctx.reply`
/// or `ctx.answerCallbackQuery`. Access to the context object allows you to further
/// enhance your transformer by leveraging the incoming update.
///
/// **Note**: `ctx` can be `null` if the method is invoked directly through the `RawAPI` instance.
///
/// ## Example
/// Here's a simple implementation of the `AutoReplier` transformer. The auto-replier transformer can be used to
/// automatically require users to reply to the current message the bot sent by passing the `ForceReply` reply markup with all the possible
/// send methods.
///
/// Here's a simple implementation of the `AutoReplier` transformer:
/// ```dart
/// class AutoReplier implements Transformer {
/// @override
/// FutureOr<Map<String, dynamic>> fn(
/// FutureOr<Map<String, dynamic>> transform(
/// APIMethod method,
/// Map<String, dynamic> payload,
/// Context? ctx,
Expand All @@ -81,19 +131,17 @@ abstract interface class Transformer implements MiddlewareBase {
/// }
/// ```
///
/// The above code, simply creates a auto replier transformer. Now users can use it as:
///
/// The above code creates an `AutoReplier` transformer. Users can attach it to the bot:
/// ```dart
/// bot.use(AutoReplier());
/// ```
///
/// That's it. Now whenever user makes a send method request, the force reply markup will be added to it.
FutureOr<Map<String, dynamic>> fn(
/// Now, whenever a send method request is made, the force reply markup will be added.
FutureOr<Map<String, dynamic>> transform(
APIMethod method,
Map<String, dynamic> payload,
Context? ctx,
);

/// Constructs a API Interceptor middleware
/// Constructs a `Transformer` instance.
const Transformer();
}
2 changes: 1 addition & 1 deletion lib/src/televerse/raw_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class RawAPI {
final ts = [..._transformers, ...(_context?._transfomers ?? [])];

while (i < ts.length) {
params = await ts[i].fn(method, params!, _context);
params = await ts[i].transform(method, params!, _context);
i++;
}

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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.0
version: 1.19.1
homepage: https://github.com/HeySreelal/televerse
topics:
- telegram
Expand Down
Loading