teloxide / teloxide

🤖 An elegant Telegram bots framework for Rust
https://docs.rs/teloxide/
MIT License
3.17k stars 233 forks source link

`operation timed out` when uploading a large video file #529

Open lorenzo9uerra opened 2 years ago

lorenzo9uerra commented 2 years ago

While I'm uploading a video of at least 50MB (I guess this depends on the speed of your connection, to reproduce the issue it has to take more than 17 seconds) the program stops after exactly 17 seconds from the start.

I tried this code:

use teloxide::prelude2::*;
use teloxide::types::InputFile;
use std::env;
use serde::{Deserialize, Serialize};
use serde_yaml::{self};

use std::{error::Error as StdError, fmt};

#[derive(Debug)]
pub enum Error {
    /// Some unspecified error.
    Any(Box<dyn StdError + Send + Sync + 'static>),
    TokioError {
        description: String,
    },
    UnknownError,
    UnreadableMessage,
    FileNotFound {
        path: String,
    },
    RequestError {
        description: String,
    },
    ParsingError {
        description: String,
    },
    MissingChatId,
    NoInput,
}

impl Error {
    /// Prints error to stderr and exits the program.
    pub fn exit(self) {
        eprintln!("{}", self);
        std::process::exit(1);
    }
}

impl<'a> fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::RequestError { ref description } | Error::TokioError { ref description } => {
                write!(f, "\nMessage failed to send due to:\n\t{}", description)
            }
            Error::FileNotFound { ref path } => {
                write!(f, "\nCould not find file in path:\n\t{}", path)
            }
            Error::MissingChatId => {
                write!(f, "\nChat ID not found in flags or TEPE_TELEGRAM_CHAT_ID")
            }
            Error::NoInput => write!(f, "\nNo input was given"),
            Error::ParsingError { ref description } => {
                write!(f, "\nError from parsing:\n\t{}", description)
            }
            Error::UnreadableMessage => write!(f, "\nIssue parsing message"),
            _ => write!(f, "\nTODO: add error description"),
        }
    }
}

use teloxide::RequestError;
impl From<RequestError> for Error {
    fn from(error: RequestError) -> Self {
        Error::RequestError {
            description: format!("{}", error),
        }
    }
}

use tokio::io::Error as TokioError;
impl From<TokioError> for Error {
    fn from(error: TokioError) -> Self {
        Error::TokioError {
            description: format!("{}", error),
        }
    }
}

impl From<std::num::ParseIntError> for Error {
    fn from(error: std::num::ParseIntError) -> Self {
        Error::ParsingError {
            description: error.to_string(),
        }
    }
}

pub trait CliExit<T> {
    fn cli_expect(self, message: &str) -> T;
}

impl<T, E> CliExit<T> for Result<T, E> {
    fn cli_expect(self, message: &str) -> T {
        match self {
            Ok(t) => t,
            Err(_e) => {
                eprintln!("{}", message);
                std::process::exit(1);
            }
        }
    }
}

impl<T> CliExit<T> for Option<T> {
    fn cli_expect(self, message: &str) -> T {
        match self {
            Some(t) => t,
            None => {
                eprintln!("{}", message);
                std::process::exit(1);
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
struct Config {
    token: String,
    chat_id: i64,
}

#[tokio::main]
async fn main() {
    run().await.unwrap_or_else(|error| error.exit());
}

async fn run() -> Result<(), Error> {
    let f = std::fs::File::open("tgconfig.yml").expect("Could not open file.");
    let scrape_config: Config = serde_yaml::from_reader(f).expect("Could not read values.");
    teloxide::enable_logging!();
    log::info!("Starting regs_bot...");
    let args: Vec<String> = env::args().collect();

    let bot = Bot::new(scrape_config.token);
    if args.len() != 3 {
        println!("Insert file path and caption");
    }
    else {
        bot.send_video(scrape_config.chat_id, InputFile::file(&args[1])).caption(&args[2]).send().await?;
    }
    Ok(())
}

I expected to see the video uploaded to the chat_id specified.

Instead, the upload stopped after exactly 17 seconds with the message "operation timed out". I tried to downgrade teloxide-core to 0.4.1 but it didn't make any difference. I also tried to downgrade teloxide version to 0.6.0, no difference.

Dependencies

WaffleLapkin commented 2 years ago

The problem is that teloxide uses 17 second network timeout by default and iirc we did this since 0.1.

You can workaround this issue by increasing the timeout:

let client = net::default_reqwest_settings().timeout(Duration::from_secs(30));
let bot = Bot::with_client(scrape_config.token, client);

Some ideas how we may fix this in the future:

Both of these are blocked on the same thing as #526 (see https://github.com/teloxide/teloxide/issues/526#issuecomment-1048754931).

hirrolot commented 2 years ago

@WaffleLapkin, is this fixed?

WaffleLapkin commented 2 years ago

@Hirrolot no, this is still blocked on https://github.com/seanmonstar/reqwest/pull/1477