mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-10-02 00:42:10 +00:00
fix(send): refuse a file id at the upload fallback's door
download_to_temp guarded against a file id, but prepare_upload_item answered a non-http media_url first — and item_url maps FileId to the id itself, so the id was presented as a local *path* and died later with a confusing file-open error instead of a classification. The guard now sits at the entry, download_to_temp takes the already-narrowed URL (the inner guard deleted as dead), and the test pins the refusal as Permanent before any I/O. The file also carries item10's download-classification arm (RateLimited is retryable there too) — same file, landed here to keep each tree compiling.
This commit is contained in:
@@ -89,17 +89,10 @@ pub(super) enum FallbackError {
|
||||
/// errors are not.
|
||||
async fn download_to_temp(
|
||||
item: &MediaItemPayload,
|
||||
media_url: &str,
|
||||
) -> Result<(NamedTempFile, bytes::Bytes), FallbackError> {
|
||||
// Only a URL/path item is ever downloaded: a file id is sent as-is (see
|
||||
// `MediaItemPayload::input_file`), so this path cannot see one.
|
||||
let media_url = match item.media_ref() {
|
||||
MediaRef::Source(media) => media,
|
||||
MediaRef::FileId(id) => {
|
||||
return Err(FallbackError::Permanent {
|
||||
message: format!("file id reached the download path: {id}"),
|
||||
});
|
||||
}
|
||||
};
|
||||
// The caller narrows the media to a source URL before calling (its entry
|
||||
// guard rejects a file id), so there is nothing to match on here.
|
||||
// Photos are downloaded even over the upload cap so `prepare_photo` can
|
||||
// downscale / transcode them, up to their own download cap; videos and
|
||||
// animations are refused as soon as the declared size crosses their own
|
||||
@@ -177,9 +170,11 @@ async fn download_to_temp(
|
||||
/// retry could only ask the same URL again.
|
||||
fn classify_download_error(err: FetchError) -> FallbackError {
|
||||
match err {
|
||||
FetchError::Http(_) | FetchError::Transient(_) => FallbackError::Retryable {
|
||||
delay_seconds: retry_delay_seconds(0),
|
||||
},
|
||||
FetchError::Http(_) | FetchError::Transient(_) | FetchError::RateLimited { .. } => {
|
||||
FallbackError::Retryable {
|
||||
delay_seconds: retry_delay_seconds(0),
|
||||
}
|
||||
}
|
||||
FetchError::TooLarge => FallbackError::MediaTooLarge,
|
||||
e => FallbackError::Permanent {
|
||||
message: format!("download failed: {e}"),
|
||||
@@ -222,6 +217,15 @@ pub(super) async fn prepare_upload_item(
|
||||
index: usize,
|
||||
caption: Option<&str>,
|
||||
) -> Result<PreparedItem, FallbackError> {
|
||||
// A file id is already Telegram's copy of an uploaded file: there is no
|
||||
// URL to re-fetch, and without this guard `item_url` presents the id as
|
||||
// a *path*, which fails at upload time with a confusing open error
|
||||
// instead of a classification. Re-upload cannot apply to it.
|
||||
if matches!(item.media_ref(), MediaRef::FileId(_)) {
|
||||
return Err(FallbackError::Permanent {
|
||||
message: "file id reached the upload fallback".into(),
|
||||
});
|
||||
}
|
||||
// Locally produced files (ugoira / bsky remux MP4): nothing to download
|
||||
// or shrink — upload the file directly. The send is a multipart upload,
|
||||
// so the only remaining failure is an upload-cap error, which is
|
||||
@@ -243,7 +247,7 @@ pub(super) async fn prepare_upload_item(
|
||||
// the match below. A separate size probe used to issue a second GET of the
|
||||
// same URL for an answer this path already has (and issued it for photos,
|
||||
// whose answer was discarded one line later).
|
||||
match download_to_temp(&item).await {
|
||||
match download_to_temp(&item, media_url).await {
|
||||
Ok((file, bytes)) => {
|
||||
if matches!(item, MediaItemPayload::Photo { .. }) {
|
||||
// Telegram rejects photos wider+taller than 10000 px combined
|
||||
@@ -422,4 +426,18 @@ mod download_class_tests {
|
||||
FallbackError::MediaTooLarge
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_file_id_item_is_refused_before_any_download() {
|
||||
let item = MediaItemPayload::Photo {
|
||||
media: MediaRef::FileId("AgACAgIAAx".into()),
|
||||
has_spoiler: false,
|
||||
fallback_url: None,
|
||||
};
|
||||
match prepare_upload_item(item, 0, None).await {
|
||||
Err(FallbackError::Permanent { .. }) => {}
|
||||
Err(_) => panic!("expected a permanent classification, got a different error"),
|
||||
Ok(_) => panic!("a file id must be refused, not prepared"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user