From 434aa84d40016dddfd5404c8b9cb221dca8f82e5 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Mon, 9 Nov 2026 17:33:12 +0100 Subject: [PATCH] Telegram: find the token in pasted text, no password autofill, clear error Co-Authored-By: Claude Opus 5.5 --- server/src/api.rs | 3 ++- server/src/telegram.rs | 14 ++++++++++++++ web/src/components/settings/Telegram.tsx | 12 +++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/server/src/api.rs b/server/src/api.rs index f37e737..f8ce772 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -349,7 +349,8 @@ struct TelegramBody { /// Connect a bot: check the token, start polling and create a pairing code. async fn put_telegram(Extension(user): Extension, State(app): State, Json(b): Json) -> ApiResult { - let token = b.token.trim().to_string(); + let token = crate::telegram::extract_token(&b.token) + .ok_or_else(|| ApiError(StatusCode::BAD_REQUEST, "that is not a bot token – it looks like 123456789:AAH… (paste only the token from @BotFather)".into()))?; let bot = crate::telegram::check_token(&token).await.map_err(|e| ApiError(StatusCode::BAD_REQUEST, format!("token rejected by Telegram: {e:#}")))?; let s = crate::telegram::Settings { token, bot, pair_code: crate::telegram::new_pair_code(), ..Default::default() }; app.store.set_user_setting(&user.id, "telegram", &s)?; diff --git a/server/src/telegram.rs b/server/src/telegram.rs index 3ee5625..15f7c03 100644 --- a/server/src/telegram.rs +++ b/server/src/telegram.rs @@ -149,6 +149,12 @@ impl Bot { } } +/// Find the bot token in what the user pasted (also the whole BotFather message). +pub fn extract_token(input: &str) -> Option { + let re = regex::Regex::new(r"\b(\d{5,}:[A-Za-z0-9_-]{30,})\b").unwrap(); + re.captures(input).map(|c| c[1].to_string()) +} + /// Check a token and return the bot's username. pub async fn check_token(token: &str) -> Result { let me = Bot { token: token.to_string() }.call("getMe", json!({})).await?; @@ -1145,6 +1151,14 @@ mod tests { } } + #[test] + fn finds_the_token_in_pasted_text() { + let msg = "Done! Congratulations on your new bot.\nUse this token to access the HTTP API:\n7123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw4\nKeep your token secure"; + assert_eq!(extract_token(msg).as_deref(), Some("7123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw4")); + assert_eq!(extract_token(" 7123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw4 ").as_deref(), Some("7123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw4")); + assert_eq!(extract_token("my-password-123"), None); + } + #[test] fn step_lines() { assert_eq!(step_line("bash", &json!({"command": "cargo test\nmore"})), "bash: cargo test"); diff --git a/web/src/components/settings/Telegram.tsx b/web/src/components/settings/Telegram.tsx index cdefb6c..5439d4a 100644 --- a/web/src/components/settings/Telegram.tsx +++ b/web/src/components/settings/Telegram.tsx @@ -60,7 +60,17 @@ export function Telegram() {
  • {t("telegram.step2")}
  • - setToken(e.target.value)} style={{ flex: 1 }} /> + setToken(e.target.value)} + style={{ flex: 1 }} + />