teloxide_core/
errors.rs

1//! Possible error types.
2
3use std::{io, sync::Arc};
4
5use thiserror::Error;
6
7use crate::types::{ChatId, ResponseParameters, Seconds};
8
9/// An error caused by sending a request to Telegram.
10#[derive(Debug, Error, Clone)]
11pub enum RequestError {
12    /// A Telegram API error.
13    #[error("A Telegram's error: {0}")]
14    Api(#[from] ApiError),
15
16    /// The group has been migrated to a supergroup with the specified
17    /// identifier.
18    #[error("The group has been migrated to a supergroup with ID #{0}")]
19    MigrateToChatId(ChatId),
20
21    /// In case of exceeding flood control, the number of seconds left to wait
22    /// before the request can be repeated.
23    #[error("Retry after {0}")]
24    RetryAfter(Seconds),
25
26    /// Network error while sending a request to Telegram.
27    #[error("A network error: {0}")]
28    // NOTE: this variant must not be created by anything except the explicit From impl
29    Network(#[source] Arc<reqwest::Error>),
30
31    /// Error while parsing a response from Telegram.
32    ///
33    /// If you've received this error, please, [open an issue] with the
34    /// description of the error.
35    ///
36    /// [open an issue]: https://github.com/teloxide/teloxide/issues/new
37    #[error("An error while parsing JSON: {source} (raw: {raw:?})")]
38    InvalidJson {
39        #[source]
40        source: Arc<serde_json::Error>,
41        /// The raw string JSON that couldn't been parsed
42        raw: Box<str>,
43    },
44
45    /// Occurs when trying to send a file to Telegram.
46    #[error("An I/O error: {0}")]
47    Io(#[from] Arc<io::Error>),
48}
49
50/// An error caused by downloading a file.
51#[derive(Debug, Error, Clone)]
52pub enum DownloadError {
53    /// A network error while downloading a file from Telegram.
54    #[error("A network error: {0}")]
55    // NOTE: this variant must not be created by anything except the explicit From impl
56    Network(#[source] Arc<reqwest::Error>),
57
58    /// An I/O error while writing a file to destination.
59    #[error("An I/O error: {0}")]
60    Io(#[from] Arc<std::io::Error>),
61}
62
63pub trait AsResponseParameters {
64    fn response_parameters(&self) -> Option<ResponseParameters>;
65
66    fn retry_after(&self) -> Option<Seconds> {
67        self.response_parameters().and_then(|rp| match rp {
68            ResponseParameters::RetryAfter(n) => Some(n),
69            _ => None,
70        })
71    }
72
73    fn migrate_to_chat_id(&self) -> Option<ChatId> {
74        self.response_parameters().and_then(|rp| match rp {
75            ResponseParameters::MigrateToChatId(id) => Some(id),
76            _ => None,
77        })
78    }
79}
80
81impl AsResponseParameters for crate::RequestError {
82    fn response_parameters(&self) -> Option<ResponseParameters> {
83        match *self {
84            Self::RetryAfter(n) => Some(ResponseParameters::RetryAfter(n)),
85            Self::MigrateToChatId(id) => Some(ResponseParameters::MigrateToChatId(id)),
86            _ => None,
87        }
88    }
89}
90
91macro_rules! impl_api_error {
92    (
93        $( #[$meta:meta] )*
94        $vis:vis enum $ident:ident {
95            $(
96                $( #[$var_meta:meta] )*
97                $var_name:ident $( ($var_inner:ty) )? = $var_string:literal $(with $var_parser:expr)?
98             ),*
99         }
100    ) => {
101
102        $(#[$meta])*
103        #[derive(Error)]
104        $vis enum $ident {
105            $(
106            $(#[$var_meta])*
107            #[error($var_string)]
108            $var_name $(($var_inner))*,
109            )*
110        }
111
112        const _: () = {
113            struct Visitor;
114
115            impl<'de> ::serde::de::Visitor<'de> for Visitor {
116                type Value = $ident;
117
118                fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
119                    formatter.write_str("telegram api error string")
120                }
121
122                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
123                where
124                    E: ::serde::de::Error,
125                {
126                    $(impl_api_error!(@de v, $var_name $( ($var_inner) )?, $var_string $(, $var_parser)*);)*
127                    Err(E::unknown_variant(v, &[]))
128                }
129            }
130
131            impl<'de> ::serde::de::Deserialize<'de> for $ident {
132                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133                where
134                    D: ::serde::de::Deserializer<'de>,
135                {
136                    deserializer.deserialize_str(Visitor)
137                }
138            }
139        };
140    };
141    (@de $value:ident, $variant:ident, $val:literal) => {
142        if $value == $val {
143            return Ok(Self::Value::$variant)
144        }
145    };
146    (@de $value:ident, $variant:ident ($var_inner:ty), $val:literal, $block:expr) => {
147        #[allow(clippy::redundant_closure_call)]
148        match $block($value) {
149            Some(data) => return Ok(Self::Value::$variant(data)),
150            _ => {}
151        }
152    };
153    (@de $value:ident, $variant:ident, $val:literal, $block:expr) => {
154        #[allow(clippy::redundant_closure_call)]
155        if $block($value) {
156            return Ok(Self::Value::$variant);
157        }
158    };
159}
160
161impl_api_error! {
162    /// A kind of an API error.
163    #[derive(Debug, PartialEq, Hash, Eq, Clone)]
164    #[non_exhaustive]
165    pub enum ApiError {
166        /// Occurs when the bot tries to send message to user who blocked the bot.
167        BotBlocked = "Forbidden: bot was blocked by the user",
168
169        /// Occurs when the bot token is invalid.
170        // N.B. These errors are actually slightly different, "Unauthorized" is when the bot token
171        //      is formatted mostly right, but is incorrect, whereas "Not Found" is when the url is
172        //      not handled by TBA at all. From user POV both of those are "token is invalid", but
173        //      there might be some cases where this is not right...
174        InvalidToken = "Invalid bot token" with |text: &str| text == "Unauthorized" || text == "Not Found",
175
176        /// Occurs when bot tries to modify a message without modification content.
177        ///
178        /// May happen in methods:
179        /// 1. [`EditMessageText`]
180        ///
181        /// [`EditMessageText`]: crate::payloads::EditMessageText
182        MessageNotModified = "Bad Request: message is not modified: specified new message content and reply markup are \
183            exactly the same as a current content and reply markup of the message",
184
185        /// Occurs when bot tries to forward or delete a message which was deleted.
186        ///
187        /// May happen in methods:
188        /// 1. [`ForwardMessage`]
189        /// 2. [`DeleteMessage`]
190        ///
191        /// [`ForwardMessage`]: crate::payloads::ForwardMessage
192        /// [`DeleteMessage`]: crate::payloads::DeleteMessage
193        MessageIdInvalid = "Bad Request: MESSAGE_ID_INVALID",
194
195        /// Occurs when bot tries to forward a message which does not exists.
196        ///
197        /// May happen in methods:
198        /// 1. [`ForwardMessage`]
199        ///
200        /// [`ForwardMessage`]: crate::payloads::ForwardMessage
201        MessageToForwardNotFound = "Bad Request: message to forward not found",
202
203        /// Occurs when bot tries to delete a message which does not exists.
204        ///
205        /// May happen in methods:
206        /// 1. [`DeleteMessage`]
207        ///
208        /// [`DeleteMessage`]: crate::payloads::DeleteMessage
209        MessageToDeleteNotFound = "Bad Request: message to delete not found",
210
211        /// Occurs when bot tries to copy a message which does not exists.
212        /// May happen in methods:
213        /// 1. [`CopyMessage`]
214        ///
215        /// [`CopyMessage`]: crate::payloads::CopyMessage
216        MessageToCopyNotFound = "Bad Request: message to copy not found",
217
218        /// Occurs when bot tries to send a text message without text.
219        ///
220        /// May happen in methods:
221        /// 1. [`SendMessage`]
222        ///
223        /// [`SendMessage`]: crate::payloads::SendMessage
224        MessageTextIsEmpty = "Bad Request: message text is empty",
225
226        /// Occurs when bot tries to edit a message after long time.
227        ///
228        /// May happen in methods:
229        /// 1. [`EditMessageText`]
230        ///
231        /// [`EditMessageText`]: crate::payloads::EditMessageText
232        MessageCantBeEdited = "Bad Request: message can't be edited",
233
234        /// Occurs when bot tries to delete a someone else's message in group where
235        /// it does not have enough rights.
236        ///
237        /// May happen in methods:
238        /// 1. [`DeleteMessage`]
239        ///
240        /// [`DeleteMessage`]: crate::payloads::DeleteMessage
241        MessageCantBeDeleted = "Bad Request: message can't be deleted",
242
243        /// Occurs when bot tries to edit a message which does not exists.
244        ///
245        /// May happen in methods:
246        /// 1. [`EditMessageText`]
247        ///
248        /// [`EditMessageText`]: crate::payloads::EditMessageText
249        MessageToEditNotFound = "Bad Request: message to edit not found",
250
251        /// Occurs when bot tries to reply to a message which does not exists.
252        ///
253        /// May happen in methods:
254        /// 1. [`SendMessage`]
255        ///
256        /// [`SendMessage`]: crate::payloads::SendMessage
257        MessageToReplyNotFound = "Bad Request: message to be replied not found",
258
259        /// Occurs when bot tries to
260        MessageIdentifierNotSpecified = "Bad Request: message identifier is not specified",
261
262        /// Occurs when bot tries to send a message with text size greater then
263        /// 4096 symbols.
264        ///
265        /// May happen in methods:
266        /// 1. [`SendMessage`]
267        ///
268        /// [`SendMessage`]: crate::payloads::SendMessage
269        MessageIsTooLong = "Bad Request: message is too long",
270
271        /// Occurs when bot tries to edit a message with text size greater then
272        /// 4096 symbols.
273        ///
274        /// May happen in methods:
275        /// 1. [`EditMessageText`]
276        /// 2. [`EditMessageTextInline`]
277        /// 3. [`EditMessageCaption`]
278        /// 4. [`EditMessageCaptionInline`]
279        ///
280        /// [`EditMessageText`]: crate::payloads::EditMessageText
281        /// [`EditMessageTextInline`]: crate::payloads::EditMessageTextInline
282        /// [`EditMessageCaption`]: crate::payloads::EditMessageCaption
283        /// [`EditMessageCaptionInline`]: crate::payloads::EditMessageCaptionInline
284        EditedMessageIsTooLong = "Bad Request: MESSAGE_TOO_LONG",
285
286        /// Occurs when bot tries to send media group with more than 10 items.
287        ///
288        /// May happen in methods:
289        /// 1. [`SendMediaGroup`]
290        ///
291        /// [`SendMediaGroup`]: crate::payloads::SendMediaGroup
292        TooMuchMessages = "Bad Request: Too much messages to send as an album",
293
294        /// Occurs when bot tries to answer an inline query with more than 50
295        /// results.
296        ///
297        /// Consider using offsets to paginate results.
298        ///
299        /// May happen in methods:
300        /// 1. [`AnswerInlineQuery`]
301        ///
302        /// [`AnswerInlineQuery`]: crate::payloads::AnswerInlineQuery
303        TooMuchInlineQueryResults = "Bad Request: RESULTS_TOO_MUCH",
304
305        /// Occurs when bot tries to stop poll that has already been stopped.
306        ///
307        /// May happen in methods:
308        /// 1. [`SendPoll`]
309        ///
310        /// [`SendPoll`]: crate::payloads::SendPoll
311        PollHasAlreadyClosed = "Bad Request: poll has already been closed",
312
313        /// Occurs when bot tries to send poll with less than 2 options.
314        ///
315        /// May happen in methods:
316        /// 1. [`SendPoll`]
317        ///
318        /// [`SendPoll`]: crate::payloads::SendPoll
319        PollMustHaveMoreOptions = "Bad Request: poll must have at least 2 option",
320
321        /// Occurs when bot tries to send poll with more than 12 options.
322        ///
323        /// May happen in methods:
324        /// 1. [`SendPoll`]
325        ///
326        /// [`SendPoll`]: crate::payloads::SendPoll
327        PollCantHaveMoreOptions = "Bad Request: poll can't have more than 12 options",
328
329        /// Occurs when bot tries to send poll with empty option (without text).
330        ///
331        /// May happen in methods:
332        /// 1. [`SendPoll`]
333        ///
334        /// [`SendPoll`]: crate::payloads::SendPoll
335        PollOptionsMustBeNonEmpty = "Bad Request: poll options must be non-empty",
336
337        /// Occurs when bot tries to send poll with empty question (without text).
338        ///
339        /// May happen in methods:
340        /// 1. [`SendPoll`]
341        ///
342        /// [`SendPoll`]: crate::payloads::SendPoll
343        PollQuestionMustBeNonEmpty = "Bad Request: poll question must be non-empty",
344
345        /// Occurs when bot tries to send poll with total size of options more than
346        /// 100 symbols.
347        ///
348        /// May happen in methods:
349        /// 1. [`SendPoll`]
350        ///
351        /// [`SendPoll`]: crate::payloads::SendPoll
352        PollOptionsLengthTooLong = "Bad Request: poll options length must not exceed 100",
353
354        /// Occurs when bot tries to send poll with question size more than 255
355        /// symbols.
356        ///
357        /// May happen in methods:
358        /// 1. [`SendPoll`]
359        ///
360        /// [`SendPoll`]: crate::payloads::SendPoll
361        PollQuestionLengthTooLong = "Bad Request: poll question length must not exceed 255",
362
363        /// Occurs when bot tries to stop poll with message without poll.
364        ///
365        /// May happen in methods:
366        /// 1. [`StopPoll`]
367        ///
368        /// [`StopPoll`]: crate::payloads::StopPoll
369        MessageWithPollNotFound = "Bad Request: message with poll to stop not found",
370
371        /// Occurs when bot tries to stop poll with message without poll.
372        ///
373        /// May happen in methods:
374        /// 1. [`StopPoll`]
375        ///
376        /// [`StopPoll`]: crate::payloads::StopPoll
377        MessageIsNotAPoll = "Bad Request: message is not a poll",
378
379        /// Occurs when bot tries to send a message to chat in which it is not a
380        /// member.
381        ///
382        /// May happen in methods:
383        /// 1. [`SendMessage`]
384        ///
385        /// [`SendMessage`]: crate::payloads::SendMessage
386        ChatNotFound = "Bad Request: chat not found",
387
388        /// Occurs when bot tries to send method with unknown user_id.
389        ///
390        /// May happen in methods:
391        /// 1. [`getUserProfilePhotos`]
392        ///
393        /// [`getUserProfilePhotos`]:
394        /// crate::payloads::GetUserProfilePhotos
395        UserNotFound = "Bad Request: user not found",
396
397        /// Occurs when bot tries to send [`SetChatDescription`] with same text as
398        /// in the current description.
399        ///
400        /// May happen in methods:
401        /// 1. [`SetChatDescription`]
402        ///
403        /// [`SetChatDescription`]: crate::payloads::SetChatDescription
404        ChatDescriptionIsNotModified = "Bad Request: chat description is not modified",
405
406        /// Occurs when bot tries to answer to query after timeout expire.
407        ///
408        /// May happen in methods:
409        /// 1. [`AnswerCallbackQuery`]
410        ///
411        /// [`AnswerCallbackQuery`]: crate::payloads::AnswerCallbackQuery
412        InvalidQueryId = "Bad Request: query is too old and response timeout expired or query id is invalid",
413
414        /// Occurs when bot tries to send InlineKeyboardMarkup with invalid button
415        /// url.
416        ///
417        /// May happen in methods:
418        /// 1. [`SendMessage`]
419        ///
420        /// [`SendMessage`]: crate::payloads::SendMessage
421        ButtonUrlInvalid = "Bad Request: BUTTON_URL_INVALID",
422
423        /// Occurs when bot tries to send button with data size more than 64 bytes.
424        ///
425        /// May happen in methods:
426        /// 1. [`SendMessage`]
427        ///
428        /// [`SendMessage`]: crate::payloads::SendMessage
429        ButtonDataInvalid = "Bad Request: BUTTON_DATA_INVALID",
430
431        /// Occurs when bot tries to send button with data size == 0.
432        ///
433        /// May happen in methods:
434        /// 1. [`SendMessage`]
435        ///
436        /// [`SendMessage`]: crate::payloads::SendMessage
437        TextButtonsAreUnallowed = "Bad Request: can't parse inline keyboard button: Text buttons are unallowed in the \
438            inline keyboard",
439
440        /// Occurs when bot tries to get file by wrong file id.
441        ///
442        /// May happen in methods:
443        /// 1. [`GetFile`]
444        ///
445        /// [`GetFile`]: crate::payloads::GetFile
446        WrongFileId = "Bad Request: wrong file id",
447
448        /// Occurs when bot tries to send files with wrong file identifier or HTTP
449        /// url
450        WrongFileIdOrUrl = "Bad Request: wrong file identifier/HTTP URL specified",
451
452        /// Occurs when When sending files with an url to a site that doesn't
453        /// respond.
454        FailedToGetUrlContent = "Bad Request: failed to get HTTP URL content",
455
456        /// Occurs when bot tries to do some with group which was deactivated.
457        GroupDeactivated = "Bad Request: group is deactivated",
458
459        /// Occurs when image processing fails on telegram's side.
460        ///
461        /// This is likely caused by an incorrectly encoded image, make sure that
462        /// the image is correctly encoded in a format telegram accepts.
463        ImageProcessFailed = "Bad Request: IMAGE_PROCESS_FAILED",
464
465        /// Occurs when bot tries to set chat photo from file ID
466        ///
467        /// May happen in methods:
468        /// 1. [`SetChatPhoto`]
469        ///
470        /// [`SetChatPhoto`]: crate::payloads::SetChatPhoto
471        PhotoAsInputFileRequired = "Bad Request: Photo should be uploaded as an InputFile",
472
473        /// Occurs when bot tries to add sticker to stickerset by invalid name.
474        ///
475        /// May happen in methods:
476        /// 1. [`AddStickerToSet`]
477        ///
478        /// [`AddStickerToSet`]: crate::payloads::AddStickerToSet
479        InvalidStickersSet = "Bad Request: STICKERSET_INVALID",
480
481        /// Occurs when bot tries to create a sticker set with a name that is
482        /// already used by another sticker set.
483        ///
484        /// May happen in methods:
485        /// 1. [`CreateNewStickerSet`]
486        ///
487        /// [`CreateNewStickerSet`]: crate::payloads::CreateNewStickerSet
488        StickerSetNameOccupied = "Bad Request: sticker set name is already occupied",
489
490        /// Occurs when bot tries to create a sticker set with user id of a bot.
491        ///
492        /// May happen in methods:
493        /// 1. [`CreateNewStickerSet`]
494        ///
495        /// [`CreateNewStickerSet`]: crate::payloads::CreateNewStickerSet
496        StickerSetOwnerIsBot = "Bad Request: USER_IS_BOT",
497
498        /// Occurs when bot tries to create a sticker set with invalid name.
499        ///
500        /// From documentation of [`CreateNewStickerSet`]:
501        /// > Short name of sticker set, to be used in `t.me/addstickers/` URLs
502        /// (e.g., _animals_). Can contain only english letters, digits and
503        /// underscores. Must begin with a letter, can't contain consecutive
504        /// underscores and must end in “\_by\_<bot\_username>”. <bot\_username>
505        /// is case insensitive. 1-64 characters.
506        ///
507        /// May happen in methods:
508        /// 1. [`CreateNewStickerSet`]
509        ///
510        /// [`CreateNewStickerSet`]: crate::payloads::CreateNewStickerSet
511        InvalidStickerName = "Bad Request: invalid sticker set name is specified",
512
513        /// Occurs when bot tries to pin a message without rights to pin in this
514        /// chat.
515        ///
516        /// May happen in methods:
517        /// 1. [`PinChatMessage`]
518        ///
519        /// [`PinChatMessage`]: crate::payloads::PinChatMessage
520        NotEnoughRightsToPinMessage = "Bad Request: not enough rights to pin a message",
521
522        /// Occurs when bot tries to pin or unpin a message without rights to pin
523        /// in this chat.
524        ///
525        /// May happen in methods:
526        /// 1. [`PinChatMessage`]
527        /// 2. [`UnpinChatMessage`]
528        ///
529        /// [`PinChatMessage`]: crate::payloads::PinChatMessage
530        /// [`UnpinChatMessage`]: crate::payloads::UnpinChatMessage
531        NotEnoughRightsToManagePins = "Bad Request: not enough rights to manage pinned messages in the chat",
532
533        /// Occurs when bot tries change default chat permissions without "Ban
534        /// Users" permission in this chat.
535        ///
536        /// May happen in methods:
537        /// 1. [`SetChatPermissions`]
538        ///
539        /// [`SetChatPermissions`]: crate::payloads::SetChatPermissions
540        NotEnoughRightsToChangeChatPermissions = "Bad Request: not enough rights to change chat permissions",
541
542        /// Occurs when bot tries to use method in group which is allowed only in a
543        /// supergroup or channel.
544        MethodNotAvailableInPrivateChats = "Bad Request: method is available only for supergroups and channel",
545
546        /// Occurs when bot tries to demote chat creator.
547        ///
548        /// May happen in methods:
549        /// 1. [`PromoteChatMember`]
550        ///
551        /// [`PromoteChatMember`]: crate::payloads::PromoteChatMember
552        CantDemoteChatCreator = "Bad Request: can't demote chat creator",
553
554        /// Occurs when bot tries to restrict self in group chats.
555        ///
556        /// May happen in methods:
557        /// 1. [`RestrictChatMember`]
558        ///
559        /// [`RestrictChatMember`]: crate::payloads::RestrictChatMember
560        CantRestrictSelf = "Bad Request: can't restrict self",
561
562        /// Occurs when bot tries to restrict chat member without rights to
563        /// restrict in this chat.
564        ///
565        /// May happen in methods:
566        /// 1. [`RestrictChatMember`]
567        ///
568        /// [`RestrictChatMember`]: crate::payloads::RestrictChatMember
569        NotEnoughRightsToRestrict = "Bad Request: not enough rights to restrict/unrestrict chat member",
570
571        /// Occurs when bot tries to post a message in a channel without "Post
572        /// Messages" admin right.
573        NotEnoughRightsToPostMessages = "Bad Request: need administrator rights in the channel chat",
574
575        /// Occurs when bot tries set webhook to protocol other than HTTPS.
576        ///
577        /// May happen in methods:
578        /// 1. [`SetWebhook`]
579        ///
580        /// [`SetWebhook`]: crate::payloads::SetWebhook
581        WebhookRequireHttps = "Bad Request: bad webhook: HTTPS url must be provided for webhook",
582
583        /// Occurs when bot tries to set webhook to port other than 80, 88, 443 or
584        /// 8443.
585        ///
586        /// May happen in methods:
587        /// 1. [`SetWebhook`]
588        ///
589        /// [`SetWebhook`]: crate::payloads::SetWebhook
590        BadWebhookPort = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or 8443",
591
592        /// Occurs when bot tries to set webhook to unknown host.
593        ///
594        /// May happen in methods:
595        /// 1. [`SetWebhook`]
596        ///
597        /// [`SetWebhook`]: crate::payloads::SetWebhook
598        UnknownHost = "Bad Request: bad webhook: Failed to resolve host: Name or service not known",
599
600        /// Occurs when bot tries to set webhook to invalid URL.
601        ///
602        /// May happen in methods:
603        /// 1. [`SetWebhook`]
604        ///
605        /// [`SetWebhook`]: crate::payloads::SetWebhook
606        CantParseUrl = "Bad Request: can't parse URL",
607
608        /// Occurs when bot tries to send message with unfinished entities.
609        ///
610        /// May happen in methods:
611        /// 1. [`SendMessage`]
612        ///
613        /// [`SendMessage`]: crate::payloads::SendMessage
614        CantParseEntities(String) = "{0}" with |text: &str| {
615            if text.starts_with("Bad Request: can't parse entities") {
616                Some(text.to_owned())
617            } else {
618                None
619            }
620        },
621
622        /// Occurs when bot tries to use getUpdates while webhook is active.
623        ///
624        /// May happen in methods:
625        /// 1. [`GetUpdates`]
626        ///
627        /// [`GetUpdates`]: crate::payloads::GetUpdates
628        CantGetUpdates = "can't use getUpdates method while webhook is active",
629
630        /// Occurs when bot tries to do some in group where bot was kicked.
631        ///
632        /// May happen in methods:
633        /// 1. [`SendMessage`]
634        ///
635        /// [`SendMessage`]: crate::payloads::SendMessage
636        BotKicked = "Unauthorized: bot was kicked from a chat",
637
638        /// Occurs when bot tries to do something in a supergroup the bot was
639        /// kicked from.
640        ///
641        /// May happen in methods:
642        /// 1. [`SendMessage`]
643        ///
644        /// [`SendMessage`]: crate::payloads::SendMessage
645        BotKickedFromSupergroup = "Forbidden: bot was kicked from the supergroup chat",
646
647        /// Occurs when bot tries to do something in a channel the bot was
648        /// kicked from.
649        ///
650        /// May happen in methods:
651        /// 1. [`SendMessage`]
652        ///
653        /// [`SendMessage`]: crate::payloads::SendMessage
654        BotKickedFromChannel = "Forbidden: bot was kicked from the channel chat",
655
656        /// Occurs when bot tries to send a message to a deactivated user (i.e. a
657        /// user that was banned by telegram).
658        ///
659        /// May happen in methods:
660        /// 1. [`SendMessage`]
661        ///
662        /// [`SendMessage`]: crate::payloads::SendMessage
663        UserDeactivated = "Forbidden: user is deactivated",
664
665        /// Occurs when you tries to initiate conversation with a user.
666        ///
667        /// May happen in methods:
668        /// 1. [`SendMessage`]
669        ///
670        /// [`SendMessage`]: crate::payloads::SendMessage
671        CantInitiateConversation = "Unauthorized: bot can't initiate conversation with a user",
672
673        /// Occurs when you tries to send message to bot.
674        ///
675        /// May happen in methods:
676        /// 1. [`SendMessage`]
677        ///
678        /// [`SendMessage`]: crate::payloads::SendMessage
679        CantTalkWithBots = "Unauthorized: bot can't send messages to bots",
680
681        /// Occurs when bot tries to send button with invalid http url.
682        ///
683        /// May happen in methods:
684        /// 1. [`SendMessage`]
685        ///
686        /// [`SendMessage`]: crate::payloads::SendMessage
687        WrongHttpUrl = "Bad Request: wrong HTTP URL",
688
689        /// Occurs when multiple [`GetUpdates`] calls happen at the same time.
690        ///
691        /// This can happen if
692        /// 1. You are running multiple bot instances (including on different servers/hosting platforms)
693        /// 2. You are running multiple update consumers (like `Dispatcher` or `repl`)
694        /// 3. You are calling [`GetUpdates`] yourself and the second call is done before the first one finishes
695        ///
696        /// May happen in methods:
697        /// 1. [`GetUpdates`]
698        ///
699        /// [`GetUpdates`]: crate::payloads::GetUpdates
700        TerminatedByOtherGetUpdates = "Conflict: terminated by other getUpdates request; make sure that only one bot instance \
701            is running",
702
703        /// Occurs when bot tries to get file by invalid file id.
704        ///
705        /// May happen in methods:
706        /// 1. [`GetFile`]
707        ///
708        /// [`GetFile`]: crate::payloads::GetFile
709        FileIdInvalid = "Bad Request: invalid file id",
710
711        /// Occurs when bot tries to upload a file which is larger than 50 MB using
712        /// multipart/form-data.
713        ///
714        /// May happen in methods:
715        /// 1. [`SendVideo`]
716        /// 2. [`SendDocument`]
717        ///
718        /// [`SendVideo`]: crate::payloads::SendVideo
719        /// [`SendDocument`]: crate::payloads::SendDocument
720        RequestEntityTooLarge = "Request Entity Too Large",
721
722
723        /// Error which is not known to `teloxide`.
724        ///
725        /// If you've received this error, please [open an issue] with the
726        /// description of the error.
727        ///
728        /// [open an issue]: https://github.com/teloxide/teloxide/issues/new
729        Unknown(String) = "Unknown error: {0:?}" with |text: &str| Some(text.to_owned())
730    }
731}
732
733/// This impl allows to use `?` to propagate [`DownloadError`]s in function
734/// returning [`RequestError`]s. For example:
735///
736/// ```rust
737/// # use teloxide_core::errors::{DownloadError, RequestError};
738///
739/// async fn handler() -> Result<(), RequestError> {
740///     download_file().await?; // `?` just works
741///
742///     Ok(())
743/// }
744///
745/// async fn download_file() -> Result<(), DownloadError> {
746///     /* download file here */
747///     Ok(())
748/// }
749/// ```
750impl From<DownloadError> for RequestError {
751    fn from(download_err: DownloadError) -> Self {
752        match download_err {
753            DownloadError::Network(err) => RequestError::Network(err),
754            DownloadError::Io(err) => RequestError::Io(err),
755        }
756    }
757}
758
759impl From<reqwest::Error> for DownloadError {
760    fn from(error: reqwest::Error) -> Self {
761        DownloadError::Network(Arc::new(hide_token(error)))
762    }
763}
764
765impl From<reqwest::Error> for RequestError {
766    fn from(error: reqwest::Error) -> Self {
767        RequestError::Network(Arc::new(hide_token(error)))
768    }
769}
770
771/// Replaces token in the url in the error with `token:redacted` string.
772pub(crate) fn hide_token(mut error: reqwest::Error) -> reqwest::Error {
773    let url = match error.url_mut() {
774        Some(url) => url,
775        None => return error,
776    };
777
778    if let Some(mut segments) = url.path_segments() {
779        // Usually the url looks like "bot<token>/..." or "file/bot<token>/...".
780        let (beginning, segment) = match segments.next() {
781            Some("file") => ("file/", segments.next()),
782            segment => ("", segment),
783        };
784
785        if let Some(token) = segment.and_then(|s| s.strip_prefix("bot")) {
786            // make sure that what we are about to delete looks like a bot token
787            if let Some((id, secret)) = token.split_once(':') {
788                // The part before the : in the token is the id of the bot.
789                let id_character = |c: char| c.is_ascii_digit();
790
791                // The part after the : in the token is the secret.
792                //
793                // In all bot tokens we could find the secret is 35 characters long and is
794                // 0-9a-zA-Z_- only.
795                //
796                // It would be nice to research if TBA always has 35 character secrets or if it
797                // is just a coincidence.
798                const SECRET_LENGTH: usize = 35;
799                let secret_character = |c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_';
800
801                if secret.len() >= SECRET_LENGTH
802                    && id.chars().all(id_character)
803                    && secret.chars().all(secret_character)
804                {
805                    // found token, hide only the token
806                    let without_token =
807                        &url.path()[(beginning.len() + "/bot".len() + token.len())..];
808                    let redacted = format!("{beginning}token:redacted{without_token}");
809
810                    url.set_path(&redacted);
811                    return error;
812                }
813            }
814        }
815    }
816
817    // couldn't find token in the url, hide the whole url
818    error.without_url()
819}
820
821#[cfg(test)]
822mod tests {
823    #[test]
824    fn custom_result() {
825        use super::ApiError;
826        use serde::Deserialize;
827
828        let cases = &[
829            ("{\"data\": \"Forbidden: bot was blocked by the user\"}", ApiError::BotBlocked),
830            ("{\"data\": \"Unauthorized\"}", ApiError::InvalidToken),
831            ("{\"data\": \"Not Found\"}", ApiError::InvalidToken),
832            (
833                "{\"data\": \"Bad Request: message is not modified: specified new message content \
834                 and reply markup are exactly the same as a current content and reply markup of \
835                 the message\"}",
836                ApiError::MessageNotModified,
837            ),
838            ("{\"data\": \"Bad Request: MESSAGE_ID_INVALID\"}", ApiError::MessageIdInvalid),
839            (
840                "{\"data\": \"Bad Request: message to forward not found\"}",
841                ApiError::MessageToForwardNotFound,
842            ),
843            (
844                "{\"data\": \"Bad Request: message to delete not found\"}",
845                ApiError::MessageToDeleteNotFound,
846            ),
847            (
848                "{\"data\": \"Bad Request: message to copy not found\"}",
849                ApiError::MessageToCopyNotFound,
850            ),
851            ("{\"data\": \"Bad Request: message text is empty\"}", ApiError::MessageTextIsEmpty),
852            ("{\"data\": \"Bad Request: message can't be edited\"}", ApiError::MessageCantBeEdited),
853            (
854                "{\"data\": \"Bad Request: message can't be deleted\"}",
855                ApiError::MessageCantBeDeleted,
856            ),
857            (
858                "{\"data\": \"Bad Request: message to edit not found\"}",
859                ApiError::MessageToEditNotFound,
860            ),
861            (
862                "{\"data\": \"Bad Request: message to be replied not found\"}",
863                ApiError::MessageToReplyNotFound,
864            ),
865            (
866                "{\"data\": \"Bad Request: message identifier is not specified\"}",
867                ApiError::MessageIdentifierNotSpecified,
868            ),
869            ("{\"data\": \"Bad Request: message is too long\"}", ApiError::MessageIsTooLong),
870            ("{\"data\": \"Bad Request: MESSAGE_TOO_LONG\"}", ApiError::EditedMessageIsTooLong),
871            (
872                "{\"data\": \"Bad Request: Too much messages to send as an album\"}",
873                ApiError::TooMuchMessages,
874            ),
875            ("{\"data\": \"Bad Request: RESULTS_TOO_MUCH\"}", ApiError::TooMuchInlineQueryResults),
876            (
877                "{\"data\": \"Bad Request: poll has already been closed\"}",
878                ApiError::PollHasAlreadyClosed,
879            ),
880            (
881                "{\"data\": \"Bad Request: poll must have at least 2 option\"}",
882                ApiError::PollMustHaveMoreOptions,
883            ),
884            (
885                "{\"data\": \"Bad Request: poll can't have more than 12 options\"}",
886                ApiError::PollCantHaveMoreOptions,
887            ),
888            (
889                "{\"data\": \"Bad Request: poll options must be non-empty\"}",
890                ApiError::PollOptionsMustBeNonEmpty,
891            ),
892            (
893                "{\"data\": \"Bad Request: poll question must be non-empty\"}",
894                ApiError::PollQuestionMustBeNonEmpty,
895            ),
896            (
897                "{\"data\": \"Bad Request: poll options length must not exceed 100\"}",
898                ApiError::PollOptionsLengthTooLong,
899            ),
900            (
901                "{\"data\": \"Bad Request: poll question length must not exceed 255\"}",
902                ApiError::PollQuestionLengthTooLong,
903            ),
904            (
905                "{\"data\": \"Bad Request: message with poll to stop not found\"}",
906                ApiError::MessageWithPollNotFound,
907            ),
908            ("{\"data\": \"Bad Request: message is not a poll\"}", ApiError::MessageIsNotAPoll),
909            ("{\"data\": \"Bad Request: chat not found\"}", ApiError::ChatNotFound),
910            ("{\"data\": \"Bad Request: user not found\"}", ApiError::UserNotFound),
911            (
912                "{\"data\": \"Bad Request: chat description is not modified\"}",
913                ApiError::ChatDescriptionIsNotModified,
914            ),
915            (
916                "{\"data\": \"Bad Request: query is too old and response timeout expired or query \
917                 id is invalid\"}",
918                ApiError::InvalidQueryId,
919            ),
920            ("{\"data\": \"Bad Request: BUTTON_URL_INVALID\"}", ApiError::ButtonUrlInvalid),
921            ("{\"data\": \"Bad Request: BUTTON_DATA_INVALID\"}", ApiError::ButtonDataInvalid),
922            (
923                "{\"data\": \"Bad Request: can't parse inline keyboard button: Text buttons are \
924                 unallowed in the inline keyboard\"}",
925                ApiError::TextButtonsAreUnallowed,
926            ),
927            ("{\"data\": \"Bad Request: wrong file id\"}", ApiError::WrongFileId),
928            (
929                "{\"data\": \"Bad Request: wrong file identifier/HTTP URL specified\"}",
930                ApiError::WrongFileIdOrUrl,
931            ),
932            (
933                "{\"data\": \"Bad Request: failed to get HTTP URL content\"}",
934                ApiError::FailedToGetUrlContent,
935            ),
936            ("{\"data\": \"Bad Request: group is deactivated\"}", ApiError::GroupDeactivated),
937            ("{\"data\": \"Bad Request: IMAGE_PROCESS_FAILED\"}", ApiError::ImageProcessFailed),
938            (
939                "{\"data\": \"Bad Request: Photo should be uploaded as an InputFile\"}",
940                ApiError::PhotoAsInputFileRequired,
941            ),
942            ("{\"data\": \"Bad Request: STICKERSET_INVALID\"}", ApiError::InvalidStickersSet),
943            (
944                "{\"data\": \"Bad Request: sticker set name is already occupied\"}",
945                ApiError::StickerSetNameOccupied,
946            ),
947            ("{\"data\": \"Bad Request: USER_IS_BOT\"}", ApiError::StickerSetOwnerIsBot),
948            (
949                "{\"data\": \"Bad Request: invalid sticker set name is specified\"}",
950                ApiError::InvalidStickerName,
951            ),
952            (
953                "{\"data\": \"Bad Request: not enough rights to pin a message\"}",
954                ApiError::NotEnoughRightsToPinMessage,
955            ),
956            (
957                "{\"data\": \"Bad Request: not enough rights to manage pinned messages in the \
958                 chat\"}",
959                ApiError::NotEnoughRightsToManagePins,
960            ),
961            (
962                "{\"data\": \"Bad Request: not enough rights to change chat permissions\"}",
963                ApiError::NotEnoughRightsToChangeChatPermissions,
964            ),
965            (
966                "{\"data\": \"Bad Request: method is available only for supergroups and channel\"}",
967                ApiError::MethodNotAvailableInPrivateChats,
968            ),
969            (
970                "{\"data\": \"Bad Request: can't demote chat creator\"}",
971                ApiError::CantDemoteChatCreator,
972            ),
973            ("{\"data\": \"Bad Request: can't restrict self\"}", ApiError::CantRestrictSelf),
974            (
975                "{\"data\": \"Bad Request: not enough rights to restrict/unrestrict chat member\"}",
976                ApiError::NotEnoughRightsToRestrict,
977            ),
978            (
979                "{\"data\": \"Bad Request: need administrator rights in the channel chat\"}",
980                ApiError::NotEnoughRightsToPostMessages,
981            ),
982            (
983                "{\"data\": \"Bad Request: bad webhook: HTTPS url must be provided for webhook\"}",
984                ApiError::WebhookRequireHttps,
985            ),
986            (
987                "{\"data\": \"Bad Request: bad webhook: Webhook can be set up only on ports 80, \
988                 88, 443 or 8443\"}",
989                ApiError::BadWebhookPort,
990            ),
991            (
992                "{\"data\": \"Bad Request: bad webhook: Failed to resolve host: Name or service \
993                 not known\"}",
994                ApiError::UnknownHost,
995            ),
996            ("{\"data\": \"Bad Request: can't parse URL\"}", ApiError::CantParseUrl),
997            (
998                "{\"data\": \"Bad Request: can't parse entities: SomeRandomString\"}",
999                ApiError::CantParseEntities(
1000                    "Bad Request: can't parse entities: SomeRandomString".to_owned(),
1001                ),
1002            ),
1003            (
1004                "{\"data\": \"can't use getUpdates method while webhook is active\"}",
1005                ApiError::CantGetUpdates,
1006            ),
1007            ("{\"data\": \"Unauthorized: bot was kicked from a chat\"}", ApiError::BotKicked),
1008            (
1009                "{\"data\": \"Forbidden: bot was kicked from the supergroup chat\"}",
1010                ApiError::BotKickedFromSupergroup,
1011            ),
1012            ("{\"data\": \"Forbidden: user is deactivated\"}", ApiError::UserDeactivated),
1013            (
1014                "{\"data\": \"Unauthorized: bot can't initiate conversation with a user\"}",
1015                ApiError::CantInitiateConversation,
1016            ),
1017            (
1018                "{\"data\": \"Unauthorized: bot can't send messages to bots\"}",
1019                ApiError::CantTalkWithBots,
1020            ),
1021            ("{\"data\": \"Bad Request: wrong HTTP URL\"}", ApiError::WrongHttpUrl),
1022            (
1023                "{\"data\": \"Conflict: terminated by other getUpdates request; make sure that \
1024                 only one bot instance is running\"}",
1025                ApiError::TerminatedByOtherGetUpdates,
1026            ),
1027            ("{\"data\": \"Bad Request: invalid file id\"}", ApiError::FileIdInvalid),
1028            ("{\"data\": \"Request Entity Too Large\"}", ApiError::RequestEntityTooLarge),
1029            ("{\"data\": \"RandomError\"}", ApiError::Unknown("RandomError".to_string())),
1030        ];
1031
1032        #[derive(Deserialize, Debug)]
1033        struct Res<T> {
1034            data: T,
1035        }
1036
1037        for (data, expected) in cases {
1038            let raw = serde_json::from_str::<Res<String>>(data).unwrap().data;
1039            let parsed = serde_json::from_str::<Res<ApiError>>(data).unwrap().data;
1040            assert_eq!(&parsed, expected);
1041
1042            let expected_error_message = match parsed {
1043                ApiError::Unknown(_) => {
1044                    format!("Unknown error: \"{raw}\"")
1045                }
1046                ApiError::InvalidToken => "Invalid bot token".to_owned(),
1047                _ => raw,
1048            };
1049            assert_eq!(parsed.to_string(), expected_error_message);
1050        }
1051    }
1052}