mirror of
https://github.com/kristoferssolo/tg-relay-rs.git
synced 2025-12-20 11:04:41 +00:00
feat: add commentary
This commit is contained in:
parent
36270c7a3f
commit
7a9ef2c48c
41
Cargo.lock
generated
41
Cargo.lock
generated
@ -1149,6 +1149,15 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error-attr2"
|
name = "proc-macro-error-attr2"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -1203,6 +1212,35 @@ version = "5.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rc-box"
|
name = "rc-box"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -1705,8 +1743,9 @@ dependencies = [
|
|||||||
"dotenv",
|
"dotenv",
|
||||||
"futures",
|
"futures",
|
||||||
"infer",
|
"infer",
|
||||||
"once_cell",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
"teloxide",
|
"teloxide",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.16",
|
||||||
|
|||||||
@ -11,8 +11,9 @@ color-eyre = "0.6"
|
|||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
infer = "0.19"
|
infer = "0.19"
|
||||||
once_cell = "1.21.3"
|
rand = "0.9"
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
teloxide = { version = "0.17", features = ["macros"] }
|
teloxide = { version = "0.17", features = ["macros"] }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
|
|||||||
141
comments.txt
Normal file
141
comments.txt
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
P-foking-18. Again. Are we even trying?
|
||||||
|
The car is a piece of shit. What do you want from me? A miracle?
|
||||||
|
He missed the apex by a foking mile. Was he looking at the birds?
|
||||||
|
We have the pace of a foking milk float. Honestly.
|
||||||
|
Did we put fuel in the car? It looks like he is pushing it.
|
||||||
|
This is not a qualifying lap, this is a foking parade lap.
|
||||||
|
Both of them! Not just one, both! Unbelievable.
|
||||||
|
Tomorrow we start from the back. At least we cannot go further back.
|
||||||
|
What was that? He braked in the wrong foking country.
|
||||||
|
The data says we should be faster. The data is a foking liar.
|
||||||
|
What the fok was that strategy? Did we let a fan decide?
|
||||||
|
Tell him to shut up and drive the foking car.
|
||||||
|
A 5-second pitstop? Are we having a picnic out there?
|
||||||
|
He’s complaining about graining? The race foking started two laps ago!
|
||||||
|
Blue flags! Just get out of the foking way! We are not racing him!
|
||||||
|
The deg is foking ridiculous on this tyre. Box. Now.
|
||||||
|
Don't tell me about the gap. I can see the gap. It's getting foking bigger.
|
||||||
|
Why is he fighting him? He will destroy the tyres for nothing!
|
||||||
|
Fok, we look like a bunch of wankers.
|
||||||
|
Is the engine on fire? No? Then tell him to keep foking pushing.
|
||||||
|
So, to summarize: the start was shit, the middle was shit, and the end was shit.
|
||||||
|
We scored zero points. Again. I am not even angry, I am just… tired.
|
||||||
|
Who is responsible for this? Everyone. Everyone is foking responsible.
|
||||||
|
Do not show me the positives. There are no foking positives today.
|
||||||
|
We got lapped. Twice. By a Sauber. A FOKING SAUBER.
|
||||||
|
Next race we try something new: we try to be less shit.
|
||||||
|
This performance was unacceptable. Go home, think about it.
|
||||||
|
I need a drink. Or maybe five drinks.
|
||||||
|
Don't talk to me. Honestly, nobody talk to me for an hour.
|
||||||
|
We are the slowest foking team on the grid. Fact.
|
||||||
|
Look at that! He foksmashed the car. Again.
|
||||||
|
That's another 200,000 dollars. Fok. Gene is going to love this.
|
||||||
|
What a foking idiot. He had the whole track to himself.
|
||||||
|
Are you okay? ... *[long pause]* ... Okay. Now, what the fok happened?
|
||||||
|
The suspension is gone, the wing is gone... he brought back half a car.
|
||||||
|
He is a foking rockstar for crashing.
|
||||||
|
Was the wall moving? I don't think so.
|
||||||
|
He blames the car. Of course he blames the foking car.
|
||||||
|
Two drivers, one corner. And now, zero foking cars.
|
||||||
|
Get the spare parts. All of them. We will need them.
|
||||||
|
You saw the race, no? Why are you asking me? It was shit.
|
||||||
|
We are not here to be last. But today, we are. So, what is your question?
|
||||||
|
Yes, it was a fantastic result. We finished. That is the highlight.
|
||||||
|
The drivers did their best. Their best was just not very good.
|
||||||
|
We need a bigger budget, a better car, and maybe some foking luck.
|
||||||
|
I am not going to stand here and say we are happy. We are foking not.
|
||||||
|
Next year will be better. It cannot be foking worse.
|
||||||
|
He is a young driver. He makes mistakes. Expensive, foking stupid mistakes.
|
||||||
|
Why did we pit then? Because the crystal ball was broken today.
|
||||||
|
Look, we are a small team. We do our best. Sometimes our best is just shit.
|
||||||
|
The gearbox is foked. Just foked.
|
||||||
|
It's not smoke, it's 'unscheduled pyrotechnics'.
|
||||||
|
We build a car for a whole year and a foking $10 sensor fails.
|
||||||
|
Engine problem. Of course. It's a Ferrari engine, what do you expect?
|
||||||
|
Hydraulics are gone. Park the foking thing before it burns.
|
||||||
|
It just went 'bang'. A very expensive 'bang'.
|
||||||
|
He says he has no power. Tell him welcome to my foking world.
|
||||||
|
The brakes failed. FOKING BRAKES. That is not a small problem.
|
||||||
|
Something is loose in the cockpit. I hope it's not his foking brain.
|
||||||
|
This part should last 5 races. It lasted 5 laps.
|
||||||
|
This is not a foking kindergarten.
|
||||||
|
We need to pull our foking finger out. Now.
|
||||||
|
Honestly, some days I think I'd be happier farming. At least the potatoes don't crash.
|
||||||
|
Everyone is a wanker until they prove otherwise.
|
||||||
|
I don't have time for this bullshit.
|
||||||
|
Focus. Just foking focus on your job.
|
||||||
|
Hope is not a strategy.
|
||||||
|
This sport will give you a foking heart attack.
|
||||||
|
If you don't want pressure, go sell ice cream.
|
||||||
|
Sometimes you just have to say 'fok it'.
|
||||||
|
I am surrounded by foking idiots.
|
||||||
|
What are we doing here? Seriously, what is the foking point?
|
||||||
|
No more excuses. I am sick of the foking excuses.
|
||||||
|
It is what it is. A foking mess.
|
||||||
|
This is racing. It's brutal.
|
||||||
|
Right. Last race was shit. This race, we will be less shit. Go.
|
||||||
|
Do not fok this up. That is all I ask.
|
||||||
|
Points. We need foking points. I don't care how.
|
||||||
|
Drive like you foking mean it for once.
|
||||||
|
Let's show them we are not a bunch of wankers.
|
||||||
|
If you crash, don't foking call me.
|
||||||
|
Be aggressive. Smart, but foking aggressive.
|
||||||
|
Forget the pressure. Just drive the damn car.
|
||||||
|
We are the underdog. Let's go bite someone in the foking ankles.
|
||||||
|
Okay guys. Let's go have some fun... and by fun, I mean don't finish last.
|
||||||
|
The catering is good today. That is the only good thing.
|
||||||
|
He wants a new contract? Show me some foking results first.
|
||||||
|
My patience is thinner than a foking razor blade right now.
|
||||||
|
I need more coffee and less stupid people.
|
||||||
|
This regulation change is a foking joke.
|
||||||
|
Don't smile at me. We have nothing to smile about.
|
||||||
|
Who designed this part? I want to have a word with him. A very loud word.
|
||||||
|
Unbelievable. Just... foking unbelievable.
|
||||||
|
For foks sake.
|
||||||
|
I'm too old for this shit.
|
||||||
|
Can we just go home now?
|
||||||
|
Another day, another foking drama.
|
||||||
|
The drivers' briefing was a waste of foking time.
|
||||||
|
He is now officially a member of the wanker club.
|
||||||
|
Everything is foked. Completely foked.
|
||||||
|
Call Gene. Tell him... actually, don't foking call Gene.
|
||||||
|
Narrator: It was not, in fact, fine.
|
||||||
|
And at that moment, they knew... they messed up.
|
||||||
|
My entire life is a series of these moments.
|
||||||
|
Task failed successfully.
|
||||||
|
I have so many questions, and all of them start with "why?"
|
||||||
|
Well, that escalated quickly.
|
||||||
|
This is fine. I'm okay with the events that are unfolding currently.
|
||||||
|
We're all just one bad decision away from being in this video.
|
||||||
|
Calculated. But man, am I bad at math.
|
||||||
|
And the Darwin Award goes to...
|
||||||
|
Me trying to get my life together.
|
||||||
|
My last two brain cells trying to function.
|
||||||
|
Monday hitting me like:
|
||||||
|
Me pretending to be productive at work.
|
||||||
|
My motivation leaving my body at 2 PM.
|
||||||
|
Another meeting that could have been an email.
|
||||||
|
Me running on 3 hours of sleep and 6 shots of espresso.
|
||||||
|
Adulting is a scam.
|
||||||
|
My bank account looking at me after I buy one (1) coffee.
|
||||||
|
Me trying to follow a recipe.
|
||||||
|
No thoughts, just vibes.
|
||||||
|
The lights are on but nobody's home.
|
||||||
|
He's a little confused, but he's got the spirit.
|
||||||
|
Someone come get their cat, it's broken.
|
||||||
|
This is what peak performance looks like.
|
||||||
|
Not a single thought behind those eyes.
|
||||||
|
The internal monologue here must be fascinating.
|
||||||
|
He understood the assignment.
|
||||||
|
My spirit animal.
|
||||||
|
What the dog doin?
|
||||||
|
The internet was a mistake.
|
||||||
|
This is my entire personality now.
|
||||||
|
Just a normal day on planet Earth.
|
||||||
|
Explain this in historical terms.
|
||||||
|
The vibe I'm trying to bring into 2025.
|
||||||
|
And I'll f*cking do it again.
|
||||||
|
Zero chaos in this video. None at all.
|
||||||
|
This has been living in my head rent-free.
|
||||||
|
POV: You gave them one (1) ounce of confidence.
|
||||||
|
I don't get paid enough for this shit.
|
||||||
94
src/comments.rs
Normal file
94
src/comments.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use crate::error::{Error, Result};
|
||||||
|
use rand::{rng, seq::IndexedRandom};
|
||||||
|
use std::{
|
||||||
|
path::Path,
|
||||||
|
sync::{Arc, OnceLock},
|
||||||
|
};
|
||||||
|
use tokio::fs::read_to_string;
|
||||||
|
static DISCLAIMER: &str = "(Roleplay — fictional messages for entertainment.)";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Comments {
|
||||||
|
pub disclaimer: String,
|
||||||
|
lines: Arc<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Comments {
|
||||||
|
/// Create a small dummy/default Comments instance (useful for tests or fallback).
|
||||||
|
#[must_use]
|
||||||
|
pub fn dummy() -> Self {
|
||||||
|
let lines = vec![
|
||||||
|
"Oh come on, that's brilliant — and slightly chaotic, like always.".into(),
|
||||||
|
"That is a proper bit of craftsmanship — then someone presses the red button.".into(),
|
||||||
|
"Nice shot — looks good on the trailer, not so good on the gearbox.".into(),
|
||||||
|
"Here you go. Judge for yourself.".into(),
|
||||||
|
];
|
||||||
|
Self {
|
||||||
|
disclaimer: DISCLAIMER.into(),
|
||||||
|
lines: lines.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load comments from a plaintext file asynchronously.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns `Error::Io` if reading the file fails (propagated from
|
||||||
|
/// `tokio::fs::read_to_string`).
|
||||||
|
/// - Returns `Error::Other` if the file contains no usable lines after
|
||||||
|
/// filtering (empty or all-comment file).
|
||||||
|
pub async fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
|
let s = read_to_string(path).await?;
|
||||||
|
|
||||||
|
let lines = s
|
||||||
|
.lines()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if lines.is_empty() {
|
||||||
|
return Err(Error::other("comments file contains no usable lines"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
disclaimer: DISCLAIMER.into(),
|
||||||
|
lines: lines.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pick a random comment as &str (no allocation). Falls back to a small static
|
||||||
|
/// string if the list is unexpectedly empty.
|
||||||
|
#[must_use]
|
||||||
|
pub fn pick(&self) -> &str {
|
||||||
|
let mut rng = rng();
|
||||||
|
self.lines
|
||||||
|
.choose(&mut rng)
|
||||||
|
.map_or("Here you go.", String::as_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub fn build_caption(&self) -> String {
|
||||||
|
self.pick().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLOBAL_COMMENTS: OnceLock<Comments> = OnceLock::new();
|
||||||
|
|
||||||
|
/// Initialize the global comments (call once at startup).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns `Error::Other` when the global is already initialized (the
|
||||||
|
/// underlying `OnceLock::set` fails).
|
||||||
|
pub fn init_global_comments(comments: Comments) -> Result<()> {
|
||||||
|
GLOBAL_COMMENTS
|
||||||
|
.set(comments)
|
||||||
|
.map_err(|_| Error::other("comments already initialized"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get global comments (if initialized). Returns Option<&'static Comments>.
|
||||||
|
pub fn global_comments() -> Option<&'static Comments> {
|
||||||
|
GLOBAL_COMMENTS.get()
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ use thiserror::Error;
|
|||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("io error: {0}")]
|
#[error("io error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] tokio::io::Error),
|
||||||
|
|
||||||
#[error("instaloader failed: {0}")]
|
#[error("instaloader failed: {0}")]
|
||||||
InstaloaderFaileled(String),
|
InstaloaderFaileled(String),
|
||||||
@ -24,4 +24,11 @@ pub enum Error {
|
|||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
#[inline]
|
||||||
|
pub fn other(text: impl Into<String>) -> Self {
|
||||||
|
Self::Other(text.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub mod comments;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
pub mod telemetry;
|
pub mod telemetry;
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@ -2,10 +2,11 @@ use dotenv::dotenv;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use teloxide::{Bot, prelude::Requester, respond, types::Message};
|
use teloxide::{Bot, prelude::Requester, respond, types::Message};
|
||||||
use tg_relay_rs::{
|
use tg_relay_rs::{
|
||||||
|
comments::{Comments, init_global_comments},
|
||||||
handlers::{InstagramHandler, SocialHandler},
|
handlers::{InstagramHandler, SocialHandler},
|
||||||
telemetry::setup_logger,
|
telemetry::setup_logger,
|
||||||
};
|
};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> color_eyre::Result<()> {
|
async fn main() -> color_eyre::Result<()> {
|
||||||
@ -13,6 +14,16 @@ async fn main() -> color_eyre::Result<()> {
|
|||||||
color_eyre::install().expect("color-eyre install");
|
color_eyre::install().expect("color-eyre install");
|
||||||
setup_logger();
|
setup_logger();
|
||||||
|
|
||||||
|
let comments = Comments::load_from_file("comments.txt")
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("failed to laod comments.txt: {}; using dummy comments", e);
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|_| Comments::dummy());
|
||||||
|
|
||||||
|
init_global_comments(comments).expect("failed to initialize global comments");
|
||||||
|
|
||||||
let bot = Bot::from_env();
|
let bot = Bot::from_env();
|
||||||
info!("bot starting");
|
info!("bot starting");
|
||||||
|
|
||||||
|
|||||||
35
src/utils.rs
35
src/utils.rs
@ -1,15 +1,23 @@
|
|||||||
use crate::error::{Error, Result};
|
use crate::{
|
||||||
|
comments::global_comments,
|
||||||
|
error::{Error, Result},
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
Bot,
|
Bot,
|
||||||
|
payloads::{SendPhotoSetters, SendVideoSetters},
|
||||||
prelude::Requester,
|
prelude::Requester,
|
||||||
types::{ChatId, InputFile},
|
types::{ChatId, InputFile},
|
||||||
};
|
};
|
||||||
use tokio::{fs::File, io::AsyncReadExt};
|
use tokio::{fs::File, io::AsyncReadExt};
|
||||||
|
|
||||||
|
const TELEGRAM_CAPTION_LIMIT: usize = 1024;
|
||||||
|
static VIDEO_EXTS: &[&str] = &["mp4", "webm", "mov", "mkv", "avi"];
|
||||||
|
static IMAGE_EXTS: &[&str] = &["jpg", "jpeg", "png", "webp"];
|
||||||
|
|
||||||
/// Simple media kind enum shared by handlers.
|
/// Simple media kind enum shared by handlers.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum MediaKind {
|
pub enum MediaKind {
|
||||||
@ -18,9 +26,6 @@ pub enum MediaKind {
|
|||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
static VIDEO_EXTS: &[&str] = &["mp4", "webm", "mov", "mkv", "avi"];
|
|
||||||
static IMAGE_EXTS: &[&str] = &["jpg", "jpeg", "png", "webp"];
|
|
||||||
|
|
||||||
/// Detect media kind first by extension, then by content/magic (sync).
|
/// Detect media kind first by extension, then by content/magic (sync).
|
||||||
/// NOTE: `infer::get_from_path` is blocking — use `detect_media_kind_async` in
|
/// NOTE: `infer::get_from_path` is blocking — use `detect_media_kind_async` in
|
||||||
/// async contexts to avoid blocking the Tokio runtime.
|
/// async contexts to avoid blocking the Tokio runtime.
|
||||||
@ -94,14 +99,32 @@ pub async fn send_media_from_path(
|
|||||||
kind: Option<MediaKind>,
|
kind: Option<MediaKind>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let kind = kind.unwrap_or_else(|| detect_media_kind(&path));
|
let kind = kind.unwrap_or_else(|| detect_media_kind(&path));
|
||||||
|
|
||||||
|
let caption_opt = global_comments().map(|c| {
|
||||||
|
let mut caption = c.build_caption();
|
||||||
|
if caption.chars().count() > TELEGRAM_CAPTION_LIMIT {
|
||||||
|
caption = caption.chars().take(TELEGRAM_CAPTION_LIMIT - 1).collect();
|
||||||
|
caption.push_str("...");
|
||||||
|
}
|
||||||
|
caption
|
||||||
|
});
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
MediaKind::Video => {
|
MediaKind::Video => {
|
||||||
let video = InputFile::file(path);
|
let video = InputFile::file(path);
|
||||||
bot.send_video(chat_id, video).await.map_err(Error::from)?;
|
let mut req = bot.send_video(chat_id, video);
|
||||||
|
if let Some(c) = caption_opt {
|
||||||
|
req = req.caption(c);
|
||||||
|
}
|
||||||
|
req.await.map_err(Error::from)?;
|
||||||
}
|
}
|
||||||
MediaKind::Image => {
|
MediaKind::Image => {
|
||||||
let photo = InputFile::file(path);
|
let photo = InputFile::file(path);
|
||||||
bot.send_photo(chat_id, photo).await.map_err(Error::from)?;
|
let mut req = bot.send_photo(chat_id, photo);
|
||||||
|
if let Some(c) = caption_opt {
|
||||||
|
req = req.caption(c);
|
||||||
|
}
|
||||||
|
req.await.map_err(Error::from)?;
|
||||||
}
|
}
|
||||||
MediaKind::Unknown => {
|
MediaKind::Unknown => {
|
||||||
bot.send_message(chat_id, "No supported media found")
|
bot.send_message(chat_id, "No supported media found")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user