-
Notifications
You must be signed in to change notification settings - Fork 124
/
Copy pathecho.rs
110 lines (95 loc) · 3.38 KB
/
echo.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Example to echo user text messages. Updates are handled concurrently.
//!
//! The `TG_ID` and `TG_HASH` environment variables must be set (learn how to do it for
//! [Windows](https://ss64.com/nt/set.html) or [Linux](https://ss64.com/bash/export.html))
//! to Telegram's API ID and API hash respectively.
//!
//! Then, run it as:
//!
//! ```sh
//! cargo run --example echo -- BOT_TOKEN
//! ```
use futures_util::future::{select, Either};
use grammers_client::session::Session;
use grammers_client::{Client, Config, InitParams, Update};
use simple_logger::SimpleLogger;
use std::env;
use std::pin::pin;
use tokio::{runtime, task};
type Result = std::result::Result<(), Box<dyn std::error::Error>>;
const SESSION_FILE: &str = "echo.session";
async fn handle_update(client: Client, update: Update) -> Result {
match update {
Update::NewMessage(message) if !message.outgoing() => {
let chat = message.chat();
println!(
"Responding to {}",
chat.name().unwrap_or(&format!("id {}", chat.id()))
);
client.send_message(&chat, message.text()).await?;
}
_ => {}
}
Ok(())
}
async fn async_main() -> Result {
SimpleLogger::new()
.with_level(log::LevelFilter::Debug)
.init()
.unwrap();
let api_id = env!("TG_ID").parse().expect("TG_ID invalid");
let api_hash = env!("TG_HASH").to_string();
let token = env::args().nth(1).expect("token missing");
println!("Connecting to Telegram...");
let client = Client::connect(Config {
session: Session::load_file_or_create(SESSION_FILE)?,
api_id,
api_hash: api_hash.clone(),
params: InitParams {
// Fetch the updates we missed while we were offline
catch_up: true,
..Default::default()
},
})
.await?;
println!("Connected!");
if !client.is_authorized().await? {
println!("Signing in...");
client.bot_sign_in(&token).await?;
client.session().save_to_file(SESSION_FILE)?;
println!("Signed in!");
}
println!("Waiting for messages...");
// This code uses `select` on Ctrl+C to gracefully stop the client and have a chance to
// save the session. You could have fancier logic to save the session if you wanted to
// (or even save it on every update). Or you could also ignore Ctrl+C and just use
// `let update = client.next_update().await?`.
//
// Using `tokio::select!` would be a lot cleaner but add a heavy dependency,
// so a manual `select` is used instead by pinning async blocks by hand.
loop {
let exit = pin!(async { tokio::signal::ctrl_c().await });
let upd = pin!(async { client.next_update().await });
let update = match select(exit, upd).await {
Either::Left(_) => break,
Either::Right((u, _)) => u?,
};
let handle = client.clone();
task::spawn(async move {
match handle_update(handle, update).await {
Ok(_) => {}
Err(e) => eprintln!("Error handling updates!: {e}"),
}
});
}
println!("Saving session file and exiting...");
client.session().save_to_file(SESSION_FILE)?;
Ok(())
}
fn main() -> Result {
runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async_main())
}