Features
Explore the powerful features that make Televerse the most advanced Telegram Bot framework in the Dart ecosystem.
🎯 Revolutionary Filter System
Televerse features a powerful filter system with 80+ built-in filters that can be combined using logical operators for expressive, type-safe update handling.
Basic Filters
Use bot.on() with any built-in filter to handle specific types of updates:
// Simple filters
bot.on(bot.filters.photo, (ctx) async {
await ctx.reply('Nice photo! 📸');
});
bot.on(bot.filters.video, (ctx) async {
await ctx.reply('Cool video! 🎬');
});
bot.on(bot.filters.privateChat, (ctx) async {
await ctx.reply('This is a private chat!');
});Combining Filters with Operators
The real power comes from combining filters using logical operators:
+(OR) — Match either filter*(AND) — Match both filters-(NOT) — Match first but not second
// OR operator (+) - matches photo OR video
bot.on(
bot.filters.photo + bot.filters.video,
(ctx) async {
await ctx.reply('Media received!');
},
);
// AND operator (*) - matches text AND in group
bot.on(
bot.filters.text * bot.filters.groupChat,
(ctx) async {
await ctx.reply('Text message in group!');
},
);
// NOT operator (-) - any message except commands
bot.on(
bot.filters.anyMessage - bot.filters.command,
(ctx) async {
await ctx.reply('Non-command message!');
},
);
// Complex combinations
bot.on(
bot.filters.cmd('admin') * bot.filters.privateChat * bot.filters.user(adminId),
adminHandler,
);Available Filter Categories
🔌 Plugin Architecture
Televerse has a comprehensive plugin system with three built-in plugins and support for creating your own.
Session Plugin
Store and retrieve user-specific data across updates:
import 'package:televerse/televerse.dart';
void main() async {
final bot = Bot<Context>('YOUR_BOT_TOKEN');
// Install the session plugin
bot.plugin(SessionPlugin<Context, Map<String, dynamic>>(
initial: () => {'visits': 0, 'preferences': {}},
getSessionKey: (ctx) => 'user_${ctx.from?.id ?? 0}',
));
// Use session in handlers
bot.command('count', (ctx) async {
final session = ctx.session as Map<String, dynamic>;
session['visits'] = (session['visits'] as int) + 1;
await ctx.reply('Visit count: ${session['visits']}');
});
await bot.start();
}Conversation Plugin
Engage users in multi-step conversations with timeout handling and validation:
// Install the conversation plugin
bot.plugin(ConversationPlugin<Context>());
// Define a conversation function
Future<void> askUserInfo(Conversation<Context> conversation, Context ctx) async {
try {
await ctx.reply("What's your name?");
// Wait for text message with timeout
final nameCtx = await conversation.waitFor(
bot.filters.text.matches,
timeout: Duration(minutes: 2),
);
await nameCtx.reply("Nice to meet you, ${nameCtx.text}!");
await nameCtx.reply("How old are you?");
// Wait with validation
final ageCtx = await conversation.waitUntil(
(ctx) => int.tryParse(ctx.text ?? '') != null,
timeout: Duration(minutes: 1),
otherwise: (ctx) async {
await ctx.reply("Please send a valid number.");
},
);
final age = int.parse(ageCtx.text!);
await ageCtx.reply("Great! You are $age years old.");
} on ConversationTimeoutException {
await ctx.reply("Sorry, you took too long to respond.");
}
}
// Register and use the conversation
bot.use(createConversation('userInfo', askUserInfo));
bot.command('info', (ctx) async {
await ctx.conversation.enter('userInfo');
});Logging Plugin
Enable detailed request/response logging with a single line:
bot.plugin(LoggingPlugin<Context>());🛠️ Middleware System
The middleware system provides powerful composition capabilities for processing updates:
// Function-based middleware
bot.use((ctx, next) async {
print('📥 Processing update ${ctx.update.updateId}');
final start = DateTime.now();
await next(); // Call next middleware
final duration = DateTime.now().difference(start);
print('✅ Processed in ${duration.inMilliseconds}ms');
});
// Conditional middleware
bot.when(
(ctx) => ctx.isPrivateChat,
(ctx, next) async {
await ctx.reply('This is a private chat!');
await next();
},
);
// Forked middleware (runs concurrently)
bot.fork((ctx, next) async {
await logToDatabase(ctx.update);
});Middleware types include:
bot.use()— Standard middleware that runs for every updatebot.when()— Conditional middleware based on predicatebot.fork()— Runs concurrently without blockingbot.lazy()— Created on-demand based on context
🌐 Built-in Webhook Server
Start a production-ready webhook bot with just one method call. Perfect for serverless deployments and high-traffic bots:
final bot = Bot<Context>('YOUR_BOT_TOKEN');
// Setup handlers
bot.command('start', (ctx) async {
await ctx.reply('Hello from webhook bot! 🚀');
});
// Start webhook server - that's all you need!
await bot.startWebhook(
webhookUrl: 'https://your-domain.com/webhook',
port: 8080,
);
// For development with ngrok:
await bot.startWebhookDev('https://abc123.ngrok.io');🎨 Custom Context
Extend the Context class with your own properties and methods for cleaner, more maintainable code:
// Define your custom context
class MyContext extends Context {
MyContext(super.update, super.api, super.me);
// Add custom properties
String get userName => from?.firstName ?? 'Unknown';
bool get isAdmin => from?.id == 123456789;
// Add custom methods
Future<void> sendWelcome() async {
await reply('Welcome, $userName! 🎉');
}
}
// Use your custom context
final bot = Bot('YOUR_BOT_TOKEN', contextFactory: MyContext.new);
bot.command('start', (MyContext ctx) async {
await ctx.sendWelcome(); // Use your custom method
if (ctx.isAdmin) {
await ctx.reply('You have admin access!');
}
});⌨️ Keyboard Utilities
Create reply keyboards and inline keyboards with intuitive builder classes:
// Reply keyboard
final keyboard = Keyboard()
..text("Account")
..text("Settings")
..row()
..requestLocation("Send Location")
..resized()
..oneTime();
bot.command('menu', (ctx) async {
await ctx.reply("Choose an option:", replyMarkup: keyboard);
});
// Inline keyboard with callbacks
final inlineKeyboard = InlineKeyboard()
..addButton(
text: "Visit Website",
url: "https://televerse.dev",
)
..row()
..addButton(
text: "Help",
callbackData: "help_clicked",
);
bot.command('links', (ctx) async {
await ctx.reply("Useful links:", replyMarkup: inlineKeyboard);
});
// Handle callback queries
bot.callbackQuery('help_clicked', (ctx) async {
await ctx.answer('Help is on the way!');
await ctx.reply('Here is your help...');
});🏠 Local Bot API Support
Host your own Bot API server for increased privacy, higher file size limits, and faster response times:
// Use your own Bot API server
final bot = Bot.local(
'YOUR_BOT_TOKEN',
'http://localhost:8081',
);
// Everything works the same way!
bot.command('start', (ctx) async {
await ctx.reply('Running on local Bot API server!');
});
await bot.start();✨ And Much More
🛡️ Error Handling
Comprehensive error boundaries with context access for graceful error recovery.
📤 File Uploads
Easy file sending with InputFile class supporting files, URLs, and file IDs.
🔍 Inline Queries
Handle inline queries with the InlineQueryResultBuilder for all result types.
📋 Menu System
InlineMenu and KeyboardMenu classes with built-in handler registration.
Learn More
For complete API documentation and more examples, visit:
- API Reference — Complete documentation on pub.dev
- Examples Repository — Real-world bot examples
- Telegram Community — Get help and share your bots