Skip to content
This repository was archived by the owner on Sep 29, 2025. It is now read-only.

Commit cc653ef

Browse files
committed
feat(cobalt): show video after sending inline query result to chat (also removes thumbnail placeholder)
1 parent 14fe8ed commit cc653ef

3 files changed

Lines changed: 86 additions & 18 deletions

File tree

src/bot/dispatcher.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use teloxide::{
3131
update_listeners::Polling,
3232
utils::{command::BotCommands, html},
3333
};
34+
use crate::bot::inlines::cobalter::handle_inline_video;
3435

3536
async fn root_handler(
3637
update: Update,
@@ -78,7 +79,8 @@ async fn run_bot(config: Arc<Config>) -> Result<(), MyError> {
7879
)
7980
.branch(Update::filter_callback_query().endpoint(callback_query_handlers))
8081
.branch(Update::filter_my_chat_member().endpoint(handle_bot_added))
81-
.branch(Update::filter_inline_query().branch(inline_query_handler()));
82+
.branch(Update::filter_inline_query().branch(inline_query_handler()))
83+
.branch(Update::filter_chosen_inline_result().endpoint(handle_inline_video));
8284

8385
let me = bot.get_me().await?;
8486
info!("Bot name: {:?}", me.username());

src/bot/inlines/cobalter.rs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ use teloxide::{
1818
prelude::*,
1919
types::{
2020
InlineQuery, InlineQueryResult, InlineQueryResultArticle, InlineQueryResultPhoto,
21-
InlineQueryResultVideo, InputMessageContent, InputMessageContentText,
21+
InputMessageContent, InputMessageContentText, InputFile, InputMedia, InputMediaVideo
2222
},
2323
};
24+
use url::Url;
2425

2526
static URL_REGEX: Lazy<Regex> =
2627
Lazy::new(|| Regex::new(r"^(https?)://[^\s/$.?#].[^\s]*$").unwrap());
@@ -48,18 +49,18 @@ fn build_results_from_media(
4849
user_id: u64,
4950
) -> Vec<InlineQueryResult> {
5051
match media {
51-
DownloadResult::Video(video_url) => {
52-
if let Ok(url) = video_url.parse() {
52+
DownloadResult::Video { url, .. } => {
53+
if let Ok(_url) = url.parse::<Url>() {
5354
let url_kb = make_single_url_keyboard(original_url);
54-
55-
let result = InlineQueryResultVideo::new(
55+
let result = InlineQueryResultArticle::new(
5656
format!("cobalt_video:{}", url_hash),
57-
url,
58-
"video/mp4".parse().unwrap(),
59-
"https://i.imgur.com/D0A9Gxh.png".parse().unwrap(), /* preview */
60-
"Скачать видео".to_string(),
57+
"Скачать видео",
58+
InputMessageContent::Text(InputMessageContentText::new(
59+
"Нажмите, чтобы отправить видео",
60+
)),
6161
)
6262
.reply_markup(url_kb);
63+
6364
vec![result.into()]
6465
} else {
6566
vec![
@@ -164,3 +165,48 @@ pub async fn handle_cobalt_inline(
164165
bot.answer_inline_query(q.id, results).cache_time(0).await?;
165166
Ok(())
166167
}
168+
169+
pub async fn handle_inline_video(
170+
bot: Bot,
171+
chosen: ChosenInlineResult,
172+
config: Arc<Config>,
173+
) -> Result<(), MyError> {
174+
let Some(inline_message_id) = chosen.inline_message_id else {
175+
return Ok(());
176+
};
177+
178+
let Some(url_hash) = chosen.result_id.strip_prefix("cobalt_video:") else {
179+
return Ok(());
180+
};
181+
182+
bot.edit_message_text_inline(&inline_message_id, "⏳ Загружаю видео...")
183+
.await?;
184+
185+
let redis = config.get_redis_client();
186+
let cache_key = format!("cobalt_cache:{}", url_hash);
187+
188+
match redis.get::<DownloadResult>(&cache_key).await? {
189+
Some(DownloadResult::Video { url, original_url }) => {
190+
let media = InputMedia::Video(InputMediaVideo::new(InputFile::url(url.parse()?)));
191+
let url_kb = make_single_url_keyboard(&original_url);
192+
193+
if let Err(_e) = bot
194+
.edit_message_media_inline(&inline_message_id, media)
195+
.reply_markup(url_kb)
196+
.await
197+
{
198+
bot.edit_message_text_inline(
199+
inline_message_id,
200+
"❌ Ошибка: не удалось отправить видео.",
201+
)
202+
.await?;
203+
}
204+
}
205+
_ => {
206+
bot.edit_message_text_inline(inline_message_id, "❌ Ошибка: видео не найдено в кэше.")
207+
.await?;
208+
}
209+
}
210+
211+
Ok(())
212+
}

src/core/services/cobalt.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ impl VideoQuality {
3535

3636
#[derive(Serialize, Deserialize, Debug, Clone)]
3737
pub enum DownloadResult {
38-
Video(String),
38+
Video {
39+
url: String,
40+
original_url: String,
41+
},
3942
Photos {
4043
urls: Vec<String>,
4144
original_url: String,
@@ -77,26 +80,43 @@ pub async fn resolve_download_url(
7780
}));
7881
}
7982
if let Some(video_item) = picker.iter().find(|item| item.kind == "video") {
80-
return Ok(Some(DownloadResult::Video(video_item.url.clone())));
83+
return Ok(Some(DownloadResult::Video {
84+
url: video_item.url.clone(),
85+
original_url: url.to_string(),
86+
}));
8187
}
8288
Ok(None)
8389
}
84-
DownloadResponse::Tunnel { url, filename }
85-
| DownloadResponse::Redirect { url, filename } => {
90+
DownloadResponse::Tunnel {
91+
url: c_url,
92+
filename,
93+
}
94+
| DownloadResponse::Redirect {
95+
url: c_url,
96+
filename,
97+
} => {
8698
const PHOTO_EXTENSIONS: &[&str] = &[".jpg", ".jpeg", ".png", ".gif", ".webp"];
8799
let is_photo = PHOTO_EXTENSIONS
88100
.iter()
89101
.any(|ext| filename.to_lowercase().ends_with(ext));
90102

91103
if is_photo {
92104
Ok(Some(DownloadResult::Photos {
93-
urls: vec![url.clone()],
94-
original_url: url,
105+
urls: vec![c_url.clone()],
106+
original_url: url.to_string(),
95107
}))
96108
} else {
97-
Ok(Some(DownloadResult::Video(url)))
109+
Ok(Some(DownloadResult::Video {
110+
url: c_url,
111+
original_url: url.to_string(),
112+
}))
98113
}
99114
}
100-
_ => Ok(response.get_download_url().map(DownloadResult::Video)),
115+
_ => Ok(response
116+
.get_download_url()
117+
.map(|c_url| DownloadResult::Video {
118+
url: c_url,
119+
original_url: url.to_string(),
120+
})),
101121
}
102122
}

0 commit comments

Comments
 (0)