use futures_util::future::{select, Either};
use grammers_client::session::Session;
use grammers_client::{button, reply_markup, Client, Config, InputMessage, 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 = "inline-pagination.session";
const NUMBERS_PER_PAGE: usize = 4;
const MAX_PAYLOAD_DATA_LEN: usize = 64;
fn fib_markup(mut a: u128, mut b: u128) -> reply_markup::Inline {
let mut rows = Vec::with_capacity(NUMBERS_PER_PAGE + 1);
for _ in 0..NUMBERS_PER_PAGE {
let text = a.to_string();
rows.push(vec![button::inline(&text, text.as_bytes())]);
let bb = b;
b += a;
a = bb;
}
let next = format!("{a},{b}");
if next.len() > MAX_PAYLOAD_DATA_LEN {
rows.push(vec![button::inline("I'm satisfied!!", b"done".to_vec())]);
} else {
rows.push(vec![
button::inline("Restart!", b"0,1".to_vec()),
button::inline("More!", format!("{a},{b}").into_bytes()),
]);
}
reply_markup::inline(rows)
}
async fn handle_update(_client: Client, update: Update) -> Result {
match update {
Update::NewMessage(message) if message.text() == "/start" => {
message
.respond(InputMessage::text("Here's a fibonacci").reply_markup(&fib_markup(0, 1)))
.await?;
}
Update::CallbackQuery(query) => {
let data = std::str::from_utf8(query.data()).unwrap();
println!("Got callback query for {data}");
if data == "done" {
query.answer().edit("Glad you liked it 👍").await?;
return Ok(());
}
let mut parts = data.split(',');
let a = parts.next().unwrap().parse::<u128>().unwrap();
if let Some(b) = parts.next() {
let os = (0..b.len()).map(|_| 'o').collect::<String>();
let b = b.parse::<u128>().unwrap();
query
.answer()
.edit(
InputMessage::from(format!("S{os} much fibonacci 🔢"))
.reply_markup(&fib_markup(a, b)),
)
.await?;
} else if a % 2 == 0 {
query.answer().text("Even that's a number!").send().await?;
} else {
query.answer().alert("That's odd…").send().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: 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...");
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(_) => {
println!("Exiting...");
break;
}
Either::Right((u, _)) => u?,
};
let handle = client.clone();
task::spawn(async move {
if let Err(e) = handle_update(handle, update).await {
eprintln!("Error handling updates!: {e}")
}
});
}
println!("Saving session file...");
client.session().save_to_file(SESSION_FILE)?;
Ok(())
}
fn main() -> Result {
runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async_main())
}