Inline Mode
Inline mode lets users interact with your bot directly from the message input field of any chat. When a user types @yourbotname query, Telegram sends an inline query to your bot, and you respond with a list of results the user can pick from.
Prerequisites
Before your bot can receive inline queries, you must enable inline mode through @BotFather:
- Send
/mybotsand select your bot. - Choose “Bot Settings” then “Inline Mode”.
- Turn it on.
Handling Inline Queries
Register an inline query handler with FnHandler::on_inline_query:
use rust_tg_bot::ext::prelude::{
ApplicationBuilder, Arc, CommandHandler, Context, FnHandler,
HandlerResult, Update,
};
use rust_tg_bot::raw::types::inline::inline_query_result_article::InlineQueryResultArticle;
use rust_tg_bot::raw::types::inline::input_message_content::InputMessageContent;
use rust_tg_bot::raw::types::inline::input_text_message_content::InputTextMessageContent;
async fn inline_query_handler(update: Arc<Update>, context: Context) -> HandlerResult {
let iq = match update.inline_query() {
Some(q) => q,
None => return Ok(()),
};
if iq.query.is_empty() {
return Ok(());
}
let content = InputTextMessageContent::new(iq.query.to_uppercase());
let article = InlineQueryResultArticle::new(
format!("caps-{}", iq.id),
"CAPS",
InputMessageContent::Text(content),
);
let results = vec![
serde_json::to_value(article).expect("article serialization"),
];
context
.bot()
.answer_inline_query(&iq.id, results)
.await?;
Ok(())
}
#[tokio::main]
async fn main() {
let token = std::env::var("TELEGRAM_BOT_TOKEN").unwrap();
let app = ApplicationBuilder::new().token(token).build();
app.add_handler(FnHandler::on_inline_query(inline_query_handler), 0)
.await;
app.run_polling().await.unwrap();
}
Building Results
InlineQueryResultArticle
The most common result type. It shows a title and optional description, and sends a message when the user taps it.
#![allow(unused)]
fn main() {
use rust_tg_bot::raw::types::inline::inline_query_result_article::InlineQueryResultArticle;
use rust_tg_bot::raw::types::inline::input_message_content::InputMessageContent;
use rust_tg_bot::raw::types::inline::input_text_message_content::InputTextMessageContent;
let content = InputTextMessageContent::new("Hello, world!");
let article = InlineQueryResultArticle::new(
"unique-id-1", // Unique result ID
"Say Hello", // Title shown to the user
InputMessageContent::Text(content),
);
}
InputTextMessageContent
Controls the message that gets sent when the user selects a result.
#![allow(unused)]
fn main() {
// Plain text
let plain = InputTextMessageContent::new("Just plain text");
// HTML formatted
let html = InputTextMessageContent::new("<b>Bold</b> and <i>italic</i>")
.parse_mode("HTML");
// MarkdownV2 formatted
let markdown = InputTextMessageContent::new("*Bold* and _italic_")
.parse_mode("MarkdownV2");
}
Multiple Results
Most inline bots offer several options. Each result needs a unique ID within the response:
#![allow(unused)]
fn main() {
async fn inline_query_handler(update: Arc<Update>, context: Context) -> HandlerResult {
let iq = match update.inline_query() {
Some(q) => q,
None => return Ok(()),
};
let query = &iq.query;
if query.is_empty() {
return Ok(());
}
// Escape HTML special characters
let escaped = query
.replace('&', "&")
.replace('<', "<")
.replace('>', ">");
let caps_content = InputTextMessageContent::new(query.to_uppercase());
let bold_content = InputTextMessageContent::new(format!("<b>{escaped}</b>"))
.parse_mode("HTML");
let italic_content = InputTextMessageContent::new(format!("<i>{escaped}</i>"))
.parse_mode("HTML");
let results = vec![
serde_json::to_value(InlineQueryResultArticle::new(
format!("caps-{}", iq.id),
"Caps",
InputMessageContent::Text(caps_content),
)).expect("serialization"),
serde_json::to_value(InlineQueryResultArticle::new(
format!("bold-{}", iq.id),
"Bold",
InputMessageContent::Text(bold_content),
)).expect("serialization"),
serde_json::to_value(InlineQueryResultArticle::new(
format!("italic-{}", iq.id),
"Italic",
InputMessageContent::Text(italic_content),
)).expect("serialization"),
];
context
.bot()
.answer_inline_query(&iq.id, results)
.await?;
Ok(())
}
}
Combining with Commands
Inline bots typically also respond to direct messages. Register both command handlers and the inline query handler:
async fn start(update: Arc<Update>, context: Context) -> HandlerResult {
context
.reply_text(
&update,
"Hi! Use me inline by typing @botusername <query> in any chat.",
)
.await?;
Ok(())
}
async fn help_command(update: Arc<Update>, context: Context) -> HandlerResult {
context
.reply_text(
&update,
"Type @botusername <text> in any chat. I will offer \
CAPS, Bold, and Italic transformations.",
)
.await?;
Ok(())
}
#[tokio::main]
async fn main() {
let token = std::env::var("TELEGRAM_BOT_TOKEN").unwrap();
let app = ApplicationBuilder::new().token(token).build();
app.add_handler(CommandHandler::new("start", start), 0).await;
app.add_handler(CommandHandler::new("help", help_command), 0).await;
app.add_handler(FnHandler::on_inline_query(inline_query_handler), 0).await;
println!("Inline bot is running. Press Ctrl+C to stop.");
println!("Remember to enable inline mode with @BotFather!");
app.run_polling().await.unwrap();
}
Tips
- Empty queries. Always check if the query is empty. Users often trigger inline mode accidentally by typing
@botnamewithout a query. - Result limits. Telegram allows up to 50 results per
answer_inline_querycall. - Caching. Telegram caches results for 300 seconds by default. Use the
cache_timebuilder method onanswer_inline_queryto adjust. - Unique IDs. Every result in a single response must have a unique
id. Using the inline query’s ownidas a prefix (e.g.,format!("caps-{}", iq.id)) is a reliable pattern. - HTML escaping. Always escape user input before embedding it in HTML-formatted content to prevent injection.
- Serialization. Each result must be serialized to
serde_json::Valueviaserde_json::to_value()before passing toanswer_inline_query. - 10-second deadline. You must call
answer_inline_querywithin 10 seconds of receiving the query, or Telegram will show an error to the user.
Next Steps
- Payments – accept payments directly through Telegram.
- Inline Keyboards – the related feature for buttons within messages.