use teloxide::Bot;
use teloxide::types::ThreadId;
pub(crate) async fn send_rich_message_draft(
token: &str,
chat_id: i64,
draft_id: i32,
markdown: &str,
) -> anyhow::Result<i32> {
let url = format!("https://api.telegram.org/bot{token}/sendRichMessageDraft");
let body = serde_json::json!({
"chat_id": chat_id,
"draft_id": draft_id,
"rich_message": { "markdown": markdown },
});
let result = post_rich(&url, &body).await?;
result
.get("message_id")
.and_then(serde_json::Value::as_i64)
.map(|id| id as i32)
.ok_or_else(|| {
anyhow::anyhow!("sendRichMessageDraft ok but response carried no message_id")
})
}
pub(crate) async fn try_send_rich(
bot: &Bot,
chat_id: i64,
thread_id: Option<ThreadId>,
markdown: &str,
) -> anyhow::Result<()> {
send_rich_markdown(bot.token(), chat_id, thread_id, markdown).await
}
pub(crate) async fn send_rich_markdown(
token: &str,
chat_id: i64,
thread_id: Option<ThreadId>,
markdown: &str,
) -> anyhow::Result<()> {
let url = format!("https://api.telegram.org/bot{token}/sendRichMessage");
post_and_check(&url, &build_body(chat_id, thread_id, markdown)).await
}
pub(crate) async fn send_rich_markdown_id(
token: &str,
chat_id: i64,
thread_id: Option<ThreadId>,
markdown: &str,
) -> anyhow::Result<i32> {
let url = format!("https://api.telegram.org/bot{token}/sendRichMessage");
let result = post_rich(&url, &build_body(chat_id, thread_id, markdown)).await?;
result
.get("message_id")
.and_then(serde_json::Value::as_i64)
.map(|id| id as i32)
.ok_or_else(|| anyhow::anyhow!("sendRichMessage ok but response carried no message_id"))
}
pub(crate) async fn edit_rich_markdown(
token: &str,
chat_id: i64,
message_id: i32,
markdown: &str,
) -> anyhow::Result<()> {
let url = format!("https://api.telegram.org/bot{token}/editMessageText");
let body = serde_json::json!({
"chat_id": chat_id,
"message_id": message_id,
"rich_message": { "markdown": markdown },
});
post_and_check(&url, &body).await
}
async fn post_rich(url: &str, body: &serde_json::Value) -> anyhow::Result<serde_json::Value> {
let resp = reqwest::Client::new().post(url).json(body).send().await?;
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
let parsed: serde_json::Value = serde_json::from_str(&text).unwrap_or_default();
if status.is_success() && parsed.get("ok").and_then(serde_json::Value::as_bool) == Some(true) {
Ok(parsed
.get("result")
.cloned()
.unwrap_or(serde_json::Value::Null))
} else {
let desc = parsed
.get("description")
.and_then(serde_json::Value::as_str)
.unwrap_or(&text);
anyhow::bail!("Telegram rich API error ({status}): {desc}")
}
}
async fn post_and_check(url: &str, body: &serde_json::Value) -> anyhow::Result<()> {
post_rich(url, body).await.map(|_| ())
}
pub(crate) fn build_body(
chat_id: i64,
thread_id: Option<ThreadId>,
markdown: &str,
) -> serde_json::Value {
let mut body = serde_json::json!({
"chat_id": chat_id,
"rich_message": { "markdown": markdown },
});
if let Some(t) = thread_id {
body["message_thread_id"] = serde_json::json!(t.0.0);
}
body
}