deep_linking/
deep_linking.rs

1//! This example demonstrates how to use deep linking in Telegram
2//! by making a simple anonymous message bot.
3//!
4//! Deep linking (links like https://t.me/some_bot?start=123456789)
5//! is handled by telegram in the same way as just sending /start {argument}.
6//! So, in the StartCommand enum we need to write Start(String)
7//! to get the argument, just like in command.rs example.
8//!
9//! Also, deep linking is only supported with /start command!
10//! "https://t.me/some_bot?argument=123456789" will not work
11//!
12//! https://core.telegram.org/bots/features#deep-linking
13use dptree::{case, deps};
14use teloxide::{
15    dispatching::dialogue::{self, InMemStorage},
16    macros::BotCommands,
17    prelude::*,
18    types::{Me, ParseMode},
19};
20
21pub type MyDialogue = Dialogue<State, InMemStorage<State>>;
22pub type HandlerResult = Result<(), Box<dyn std::error::Error + Send + Sync>>;
23
24#[derive(Clone, PartialEq, Debug, Default)]
25pub enum State {
26    #[default]
27    Start,
28    WriteToSomeone {
29        id: ChatId,
30    },
31}
32
33#[derive(BotCommands, Clone, Debug)]
34#[command(rename_rule = "lowercase")]
35pub enum StartCommand {
36    Start(String),
37}
38
39#[tokio::main]
40async fn main() {
41    pretty_env_logger::init();
42    log::info!("Starting deep linking bot...");
43
44    let bot = Bot::from_env();
45
46    let handler = dialogue::enter::<Update, InMemStorage<State>, State, _>()
47        .branch(
48            Update::filter_message()
49                .filter_command::<StartCommand>()
50                .branch(case![StartCommand::Start(start)].endpoint(start)),
51        )
52        .branch(
53            Update::filter_message()
54                .branch(case![State::WriteToSomeone { id }].endpoint(send_message)),
55        );
56
57    Dispatcher::builder(bot, handler)
58        .dependencies(deps![InMemStorage::<State>::new()])
59        .enable_ctrlc_handler()
60        .build()
61        .dispatch()
62        .await;
63}
64
65pub async fn start(
66    bot: Bot,
67    dialogue: MyDialogue,
68    msg: Message,
69    start: String, // Available from `case![StartCommand::Start(start)]`
70    me: Me,
71) -> HandlerResult {
72    if start.is_empty() {
73        // This means that it is just a regular link like https://t.me/some_bot, or a /start command
74        bot.send_message(
75            msg.chat.id,
76            format!(
77                "Hello!\n\nThis link allows anyone to message you secretly: {}?start={}",
78                me.tme_url(),
79                msg.chat.id
80            ),
81        )
82        .await?;
83        dialogue.exit().await?;
84    } else {
85        // And this means that the link is like this: https://t.me/some_bot?start=123456789,
86        // or a /start 123456789 command
87        match start.parse::<i64>() {
88            Ok(id) => {
89                bot.send_message(msg.chat.id, "Send your message:").await?;
90                dialogue.update(State::WriteToSomeone { id: ChatId(id) }).await?;
91            }
92            Err(_) => {
93                bot.send_message(msg.chat.id, "Bad link!").await?;
94                dialogue.exit().await?;
95            }
96        }
97    }
98    Ok(())
99}
100
101pub async fn send_message(
102    bot: Bot,
103    id: ChatId, // Available from `State::WriteToSomeone`
104    msg: Message,
105    dialogue: MyDialogue,
106    me: Me,
107) -> HandlerResult {
108    match msg.text() {
109        Some(text) => {
110            // Trying to send a message to the user
111            let sent_result = bot
112                .send_message(id, format!("You have a new message!\n\n<i>{text}</i>"))
113                .parse_mode(ParseMode::Html)
114                .await;
115
116            // And if no error is returned, success!
117            if sent_result.is_ok() {
118                bot.send_message(
119                    msg.chat.id,
120                    format!(
121                        "Message sent!\n\nYour link is: {}?start={}",
122                        me.tme_url(),
123                        msg.chat.id
124                    ),
125                )
126                .await?;
127            } else {
128                bot.send_message(msg.chat.id, "Error sending message! Maybe user blocked the bot?")
129                    .await?;
130            }
131            dialogue.exit().await?;
132        }
133        None => {
134            bot.send_message(msg.chat.id, "This bot can send only text.").await?;
135        }
136    };
137    Ok(())
138}