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