shrimple_telegram/
lib.rs

1//! A library for running a Telegram bot.
2//!
3//! Work in progress.
4
5pub mod methods;
6pub mod types;
7
8use {
9    reqwest::{
10        header::{HeaderValue, CONTENT_TYPE},
11        multipart::{Form, Part},
12        RequestBuilder, Url,
13    },
14    serde::{
15        de::{DeserializeOwned, Error as _},
16        ser::{Impossible, SerializeStruct},
17        Deserialize, Serialize, Serializer,
18    },
19    std::{
20        borrow::Cow,
21        fmt::{Display, Formatter},
22        future::{Future, IntoFuture},
23        pin::Pin,
24        sync::Arc,
25    },
26    types::ReplyMarkup,
27};
28
29pub const MAX_MSG_LEN: usize = 4096;
30pub type Result<T = (), E = Error> = std::result::Result<T, E>;
31
32trait IsDefault {
33    fn is_default(&self) -> bool;
34}
35
36impl<T> IsDefault for Option<T> {
37    fn is_default(&self) -> bool {
38        self.is_none()
39    }
40}
41
42impl IsDefault for bool {
43    fn is_default(&self) -> bool {
44        !*self
45    }
46}
47
48impl IsDefault for Cow<'_, str> {
49    fn is_default(&self) -> bool {
50        self.is_empty()
51    }
52}
53
54impl<T> IsDefault for Cow<'_, [T]>
55where
56    [T]: ToOwned,
57{
58    fn is_default(&self) -> bool {
59        self.is_empty()
60    }
61}
62
63impl IsDefault for ReplyMarkup<'_> {
64    fn is_default(&self) -> bool {
65        matches!(self, Self::None)
66    }
67}
68
69/// An error that can occur while sending a request to Telegram Bot API
70#[derive(Debug)]
71pub enum Error {
72    Io(std::io::Error),
73    Request { method_name: &'static str, inner: reqwest::Error },
74    Response { method_name: &'static str, description: Box<str> },
75}
76
77impl From<std::io::Error> for Error {
78    fn from(e: std::io::Error) -> Self {
79        Self::Io(e)
80    }
81}
82
83impl Display for Error {
84    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85        match self {
86            Self::Io(e) => write!(f, "IO error: {e}"),
87            Self::Request { method_name, inner } => {
88                write!(f, "Error while sending `{method_name}`: {inner}")
89            }
90            Self::Response { method_name, description } => {
91                writeln!(f, "Telegram API error from method {method_name}: {description}")
92            }
93        }
94    }
95}
96
97impl std::error::Error for Error {}
98
99pub trait Request: Serialize + IntoFuture<Output = Result<Self::Response>> {
100    const NAME: &str;
101    type Response: DeserializeOwned;
102
103    /// Analogous to [`IntoFuture::into_future`], but creates a future without consuming the
104    /// request.
105    ///
106    /// # Warning
107    ///
108    /// Implementors may panic upon calling this method if the request body may not be shared
109    /// without additional async processing; for an example,
110    /// see [`InputFile`](types::InputFile) and its [`reusable`](types::InputFile::reusable) method.
111    fn to_future(&self) -> impl Future<Output = Result<Self::Response>>;
112}
113
114#[derive(Deserialize)]
115struct TelegramResponse<T> {
116    ok: bool,
117    #[serde(default)]
118    description: String,
119    result: Option<T>,
120}
121
122fn form_filler_err(s: impl Display) -> serde::de::value::Error {
123    serde::de::value::Error::custom(format_args!("FormFiller::{s}"))
124}
125
126struct StringExtractor;
127
128impl Serializer for StringExtractor {
129    type Ok = String;
130    type Error = std::fmt::Error;
131    type SerializeSeq = Impossible<Self::Ok, std::fmt::Error>;
132    type SerializeTuple = Impossible<Self::Ok, std::fmt::Error>;
133    type SerializeTupleStruct = Impossible<Self::Ok, std::fmt::Error>;
134    type SerializeTupleVariant = Impossible<Self::Ok, std::fmt::Error>;
135    type SerializeMap = Impossible<Self::Ok, std::fmt::Error>;
136    type SerializeStruct = Impossible<Self::Ok, std::fmt::Error>;
137    type SerializeStructVariant = Impossible<Self::Ok, std::fmt::Error>;
138
139    fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
140        Err(std::fmt::Error)
141    }
142
143    fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
144        Err(std::fmt::Error)
145    }
146
147    fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
148        Err(std::fmt::Error)
149    }
150
151    fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> {
152        Err(std::fmt::Error)
153    }
154
155    fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> {
156        Err(std::fmt::Error)
157    }
158
159    fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
160        Err(std::fmt::Error)
161    }
162
163    fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
164        Err(std::fmt::Error)
165    }
166
167    fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
168        Err(std::fmt::Error)
169    }
170
171    fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
172        Err(std::fmt::Error)
173    }
174
175    fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
176        Err(std::fmt::Error)
177    }
178
179    fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
180        Err(std::fmt::Error)
181    }
182
183    fn serialize_char(self, c: char) -> Result<Self::Ok, Self::Error> {
184        Ok(c.to_string())
185    }
186
187    fn serialize_str(self, s: &str) -> Result<Self::Ok, Self::Error> {
188        Ok(s.to_owned())
189    }
190
191    fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
192        Err(std::fmt::Error)
193    }
194
195    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
196        Err(std::fmt::Error)
197    }
198
199    fn serialize_some<T: ?Sized + Serialize>(self, x: &T) -> Result<Self::Ok, Self::Error> {
200        x.serialize(self)
201    }
202
203    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
204        Err(std::fmt::Error)
205    }
206
207    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
208        Ok(name.to_owned())
209    }
210
211    fn serialize_unit_variant(
212        self,
213        _: &'static str,
214        _: u32,
215        name: &'static str,
216    ) -> Result<Self::Ok, Self::Error> {
217        Ok(name.to_owned())
218    }
219
220    fn serialize_newtype_struct<T: ?Sized + Serialize>(
221        self,
222        _: &'static str,
223        x: &T,
224    ) -> Result<Self::Ok, Self::Error> {
225        x.serialize(self)
226    }
227
228    fn serialize_newtype_variant<T: ?Sized + Serialize>(
229        self,
230        _: &'static str,
231        _: u32,
232        _: &'static str,
233        x: &T,
234    ) -> Result<Self::Ok, Self::Error> {
235        x.serialize(self)
236    }
237
238    fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
239        Err(std::fmt::Error)
240    }
241
242    fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
243        Err(std::fmt::Error)
244    }
245
246    fn serialize_tuple_struct(
247        self,
248        _: &'static str,
249        _: usize,
250    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
251        Err(std::fmt::Error)
252    }
253
254    fn serialize_tuple_variant(
255        self,
256        _: &'static str,
257        _: u32,
258        _: &'static str,
259        _: usize,
260    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
261        Err(std::fmt::Error)
262    }
263
264    fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
265        Err(std::fmt::Error)
266    }
267
268    fn serialize_struct(
269        self,
270        _: &'static str,
271        _: usize,
272    ) -> Result<Self::SerializeStruct, Self::Error> {
273        Err(std::fmt::Error)
274    }
275
276    fn serialize_struct_variant(
277        self,
278        _: &'static str,
279        _: u32,
280        _: &'static str,
281        _: usize,
282    ) -> Result<Self::SerializeStructVariant, Self::Error> {
283        Err(std::fmt::Error)
284    }
285}
286
287fn get_string<T: ?Sized + Serialize>(value: &T) -> Result<String, &T> {
288    value.serialize(StringExtractor).map_err(|_| value)
289}
290
291struct FormFiller(Option<Form>);
292
293impl SerializeStruct for FormFiller {
294    type Ok = Form;
295    type Error = serde::de::value::Error;
296
297    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
298    where
299        T: ?Sized + Serialize,
300    {
301        let json = get_string(value) // strings must be passed verbatim, not formatted as JSON
302            .map_or_else(serde_json::to_string, Ok)
303            .map_err(|e| {
304                form_filler_err(format_args!("serialize_field: JSON serializer failed: {e}"))
305            })?;
306        self.0 = self
307            .0
308            .take()
309            .ok_or(form_filler_err("serialize_field: form lost"))?
310            .text(key, json)
311            .into();
312        Ok(())
313    }
314
315    fn end(self) -> Result<Self::Ok, Self::Error> {
316        self.0.ok_or(form_filler_err("end: form lost"))
317    }
318}
319
320fn e<T>() -> Result<T, serde::de::value::Error> {
321    Err(serde::de::value::Error::custom("FormFiller failed: Request must be a struct"))
322}
323
324impl Serializer for FormFiller {
325    type Ok = Form;
326    type Error = serde::de::value::Error;
327    type SerializeSeq = Impossible<Self::Ok, Self::Error>;
328    type SerializeTuple = Impossible<Self::Ok, Self::Error>;
329    type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
330    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
331    type SerializeMap = Impossible<Self::Ok, Self::Error>;
332    type SerializeStruct = Self;
333    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
334
335    fn serialize_struct(self, _: &str, _: usize) -> Result<Self::SerializeStruct, Self::Error> {
336        Ok(self)
337    }
338
339    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
340        e()
341    }
342    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
343        e()
344    }
345    fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
346        e()
347    }
348    fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
349        e()
350    }
351    fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
352        e()
353    }
354    fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> {
355        e()
356    }
357    fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> {
358        e()
359    }
360    fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
361        e()
362    }
363    fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
364        e()
365    }
366    fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
367        e()
368    }
369    fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
370        e()
371    }
372    fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
373        e()
374    }
375    fn serialize_str(self, _: &str) -> Result<Self::Ok, Self::Error> {
376        e()
377    }
378    fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
379        e()
380    }
381    fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
382        e()
383    }
384    fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
385        e()
386    }
387    fn serialize_unit_struct(self, _: &str) -> Result<Self::Ok, Self::Error> {
388        e()
389    }
390    fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
391        e()
392    }
393    fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
394        e()
395    }
396    fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
397        e()
398    }
399    fn serialize_some<T: ?Sized + Serialize>(self, _: &T) -> Result<Self::Ok, Self::Error> {
400        e()
401    }
402    fn serialize_unit_variant(self, _: &str, _: u32, _: &str) -> Result<Self::Ok, Self::Error> {
403        e()
404    }
405    fn serialize_tuple_struct(
406        self,
407        _: &str,
408        _: usize,
409    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
410        e()
411    }
412    fn serialize_newtype_struct<T: ?Sized + Serialize>(
413        self,
414        _: &str,
415        _: &T,
416    ) -> Result<Self::Ok, Self::Error> {
417        e()
418    }
419    fn serialize_tuple_variant(
420        self,
421        _: &str,
422        _: u32,
423        _: &str,
424        _: usize,
425    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
426        e()
427    }
428    fn serialize_struct_variant(
429        self,
430        _: &str,
431        _: u32,
432        _: &str,
433        _: usize,
434    ) -> Result<Self::SerializeStructVariant, Self::Error> {
435        e()
436    }
437    fn serialize_newtype_variant<T: ?Sized + Serialize>(
438        self,
439        _: &str,
440        _: u32,
441        _: &str,
442        _: &T,
443    ) -> Result<Self::Ok, Self::Error> {
444        e()
445    }
446}
447
448pub(crate) const PAYLOAD_NAME: &str = "__PAYLOAD";
449pub(crate) const PAYLOAD_LINK: &str = "attach://__PAYLOAD";
450
451pub(crate) fn serialise_into_form<R: Serialize>(req: &R, payload: Part) -> Form {
452    req.serialize(FormFiller(Some(Form::new())))
453        .expect("request to be a struct")
454        .part(PAYLOAD_NAME, payload)
455}
456
457pub(crate) trait Multipart {
458    fn get_payload(&self) -> Option<Part>;
459    /// Exclusive access allows for more performant access.
460    ///
461    /// Implementation may assume that the functions of this trait will never be called again on
462    /// this same value.
463    fn get_payload_mut(&mut self) -> Option<Part>;
464}
465
466/// A builder for the [`Bot`] that allows for customising the API URL & the HTTPS client config used
467/// for connecting to the API endpoint.
468pub struct BotBuilder<'src> {
469    token: &'src str,
470    api_url: Option<Url>,
471    client: Option<reqwest::Client>,
472}
473
474impl<'src> BotBuilder<'src> {
475    /// Creates a new builder for the [`Bot`].
476    pub fn new(token: &'src str) -> Self {
477        Self { token, api_url: None, client: None }
478    }
479
480    /// Set a different URL as the endpoint for requests to the Telegram Bot API.
481    ///
482    /// The default one is `https://api.telegram.org`.
483    pub fn api_url(mut self, base: impl reqwest::IntoUrl) -> reqwest::Result<Self> {
484        self.api_url = Some(base.into_url()?);
485        Ok(self)
486    }
487
488    /// Set a different URL as the endpoint for requests to the Telegram Bot API.
489    ///
490    /// The default one is `https://api.telegram.org`.
491    pub fn set_api_url(&mut self, base: impl reqwest::IntoUrl) -> reqwest::Result<()> {
492        self.api_url = Some(base.into_url()?);
493        Ok(())
494    }
495
496    /// Set a different HTTPS client for sending requests to the Telegram Bot API.
497    ///
498    /// Use this if the default config of [`reqwest::Client`] doesn't suffice for your case.
499    pub fn client(mut self, client: reqwest::Client) -> Self {
500        self.client = Some(client);
501        self
502    }
503
504    /// Set a different HTTPS client for sending requests to the Telegram Bot API.
505    ///
506    /// Use this if the default config of [`reqwest::Client`] doesn't suffice for your case.
507    pub fn set_client(&mut self, client: reqwest::Client) {
508        self.client = Some(client);
509    }
510
511    /// Set a different Telegram Bot API token.
512    pub fn token(mut self, token: &'src str) -> Self {
513        self.token = token;
514        self
515    }
516
517    /// Set a different Telegram Bot API token.
518    pub fn set_token(&mut self, token: &'src str) {
519        self.token = token;
520    }
521
522    /// Build the [`Bot`] instance.
523    pub fn build(self) -> Bot {
524        let mut base =
525            self.api_url.unwrap_or_else(|| Url::parse("https://api.telegram.org").unwrap());
526        base.set_path(&format!("bot{}", self.token));
527        Bot(Arc::new((self.client.unwrap_or_default(), base)))
528    }
529}
530
531/// A high-level representation of the connection to Telegram Bot API.
532#[derive(Clone)]
533pub struct Bot(Arc<(reqwest::Client, Url)>);
534
535impl Bot {
536    /// Create a [`Bot`] instance.
537    ///
538    /// if you need more customisation for the API connection, use [`BotBuilder`] instead.
539    pub fn new(token: &str) -> Self {
540        BotBuilder::new(token).build()
541    }
542
543    fn client(&self) -> &reqwest::Client {
544        &self.0 .0
545    }
546
547    fn base(&self) -> &Url {
548        &self.0 .1
549    }
550
551    /// Make a manual request to Telegram Bot API using `multipart/form-data`.
552    ///
553    /// The `payload` will be referred to in the request as "payload"
554    ///
555    /// # Warning
556    /// This is a low-level method, only use this if the Telegram method is not yet adapted to
557    /// `shrimple-telegram`.
558    /// Consider [opening an issue](https://github.com/its-the-shrimp/shrimple-telegram/issues/new)
559    /// if that's the case.
560    pub fn multipart_request<R: DeserializeOwned + 'static>(
561        &self,
562        method_name: &'static str,
563        form: Form,
564    ) -> Pin<Box<dyn Future<Output = Result<R>> + Send + Sync + 'static>> {
565        let mut url = self.base().clone();
566        if let Ok(mut segments) = url.path_segments_mut() {
567            segments.push(method_name);
568        }
569        let req = self.client().get(url).multipart(form);
570
571        Box::pin(Self::prepared_request(method_name, req))
572    }
573
574    /// Make a manual request to Telegram Bot API.
575    ///
576    /// # Warning
577    /// This is a low-level method, only use this if the Telegram method is not yet adapted to
578    /// `shrimple-telegram`.
579    /// Consider [opening an issue](https://github.com/its-the-shrimp/shrimple-telegram/issues/new)
580    /// if that's the case.
581    pub fn request<R: DeserializeOwned + 'static>(
582        &self,
583        method_name: &'static str,
584        json: String,
585    ) -> Pin<Box<dyn Future<Output = Result<R>> + Send + Sync + 'static>> {
586        let mut url = self.base().clone();
587        if let Ok(mut segments) = url.path_segments_mut() {
588            segments.push(method_name);
589        }
590        let req = self
591            .client()
592            .get(url)
593            .body(json)
594            .header(CONTENT_TYPE, const { HeaderValue::from_static("application/json") });
595
596        Box::pin(Self::prepared_request(method_name, req))
597    }
598
599    async fn prepared_request<R: DeserializeOwned>(
600        method_name: &'static str,
601        req: RequestBuilder,
602    ) -> Result<R> {
603        let (client, req) = req.build_split();
604        let req = req.map_err(|inner| Error::Request { method_name, inner })?;
605
606        match client
607            .execute(req)
608            .await
609            .map_err(|inner| Error::Request { method_name, inner })?
610            .json()
611            .await
612            .map_err(|inner| Error::Request { method_name, inner })?
613        {
614            TelegramResponse { ok: true, result: Some(result), .. } => Ok(result),
615            TelegramResponse { mut description, .. } => Err({
616                description.insert_str(0, ": Telegram API error: ");
617                description.insert_str(0, method_name);
618                crate::Error::Response { method_name, description: description.into() }
619            }),
620        }
621    }
622}