diff --git a/README.md b/README.md
index d3565e12..6e0c581c 100644
--- a/README.md
+++ b/README.md
@@ -5,3 +5,8 @@
+
+## Dependency graph
+
+
+
diff --git a/graph.png b/graph.png
new file mode 100644
index 00000000..8c66df6f
Binary files /dev/null and b/graph.png differ
diff --git a/src/bot/mod.rs b/src/bot/mod.rs
index d87df13f..74ff89d4 100644
--- a/src/bot/mod.rs
+++ b/src/bot/mod.rs
@@ -1,7 +1,24 @@
use reqwest::r#async::Client;
-use crate::core::requests::{
- get_me::GetMe, send_message::SendMessage, ChatId, RequestContext,
+use crate::core::{
+ types::{
+ InputFile,
+ InputMedia,
+ },
+ requests::{
+ ChatId,
+ RequestContext,
+
+ get_me::GetMe,
+ send_message::SendMessage,
+ edit_message_live_location::EditMessageLiveLocation,
+ forward_message::ForwardMessage,
+ send_audio::SendAudio,
+ send_location::SendLocation,
+ send_media_group::SendMediaGroup,
+ send_photo::SendPhoto,
+ stop_message_live_location::StopMessageLiveLocation,
+ }
};
pub struct Bot {
@@ -23,15 +40,19 @@ impl Bot {
client,
}
}
+
+ fn ctx(&self) -> RequestContext {
+ RequestContext {
+ token: &self.token,
+ client: &self.client,
+ }
+ }
}
/// Telegram functions
impl Bot {
pub fn get_me(&self) -> GetMe {
- GetMe::new(RequestContext {
- token: &self.token,
- client: &self.client,
- })
+ GetMe::new(self.ctx())
}
pub fn send_message(&self, chat_id: C, text: T) -> SendMessage
@@ -40,12 +61,105 @@ impl Bot {
T: Into,
{
SendMessage::new(
- RequestContext {
- token: &self.token,
- client: &self.client,
- },
+ self.ctx(),
chat_id.into(),
text.into(),
)
}
+
+ pub fn edit_message_live_location(
+ &self,
+ latitude: Lt,
+ longitude: Lg,
+ ) -> EditMessageLiveLocation
+ where
+ Lt: Into,
+ Lg: Into,
+ {
+ EditMessageLiveLocation::new(
+ self.ctx(),
+ latitude.into(),
+ longitude.into(),
+ )
+ }
+
+ pub fn forward_message(
+ &self,
+ chat_id: C,
+ from_chat_id: F,
+ message_id: M
+ ) -> ForwardMessage
+ where
+ C: Into,
+ F: Into,
+ M: Into,
+ {
+ ForwardMessage::new(
+ self.ctx(),
+ chat_id.into(),
+ from_chat_id.into(),
+ message_id.into(),
+ )
+ }
+
+ pub fn send_audio(&self, chat_id: C, audio: A) -> SendAudio
+ where
+ C: Into,
+ A: Into,
+ {
+ SendAudio::new(
+ self.ctx(),
+ chat_id.into(),
+ audio.into()
+ )
+ }
+
+ pub fn send_location(
+ &self,
+ chat_id: C,
+ latitude: Lt,
+ longitude: Lg,
+ ) -> SendLocation
+ where
+ C: Into,
+ Lt: Into,
+ Lg: Into,
+ {
+ SendLocation::new(
+ self.ctx(),
+ chat_id.into(),
+ latitude.into(),
+ longitude.into(),
+ )
+ }
+
+ pub fn send_media_group(&self, chat_id: C, media: M) -> SendMediaGroup
+ where
+ C: Into,
+ M: Into>
+ {
+ SendMediaGroup::new(
+ self.ctx(),
+ chat_id.into(),
+ media.into(),
+ )
+ }
+
+ pub fn send_photo(&self, chat_id: C, photo: P) -> SendPhoto
+ where
+ C: Into,
+ P: Into
+ {
+ SendPhoto::new(
+ self.ctx(),
+ chat_id.into(),
+ photo.into(),
+ )
+ }
+
+ pub fn stop_message_live_location(&self) -> StopMessageLiveLocation {
+ StopMessageLiveLocation::new(
+ self.ctx()
+ )
+ }
}
diff --git a/src/core/network/mod.rs b/src/core/network/mod.rs
index 8fdde960..244c6d63 100644
--- a/src/core/network/mod.rs
+++ b/src/core/network/mod.rs
@@ -9,7 +9,6 @@ use reqwest::{
StatusCode,
};
use serde::{de::DeserializeOwned, Serialize};
-use serde_json::Value;
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
@@ -63,7 +62,7 @@ pub async fn request_multipart(
TelegramResponse::Err {
description,
error_code,
- response_parameters,
+ response_parameters: _,
..
} => Err(RequestError::ApiError {
description,
@@ -95,7 +94,7 @@ pub async fn request_json(
TelegramResponse::Err {
description,
error_code,
- response_parameters,
+ response_parameters: _,
..
} => Err(RequestError::ApiError {
description,
@@ -108,11 +107,17 @@ pub async fn request_json(
#[serde(untagged)]
enum TelegramResponse {
Ok {
- ok: bool, // true
+ /// Dummy field. Used for deserialization.
+ #[allow(dead_code)]
+ ok: bool, // TODO: True type
+
result: R,
},
Err {
- ok: bool, // false
+ /// Dummy field. Used for deserialization.
+ #[allow(dead_code)]
+ ok: bool, // TODO: False type
+
description: String,
error_code: u16,
response_parameters: Option,
diff --git a/src/core/requests/answer_pre_checkout_query.rs b/src/core/requests/answer_pre_checkout_query.rs
new file mode 100644
index 00000000..fb995ae2
--- /dev/null
+++ b/src/core/requests/answer_pre_checkout_query.rs
@@ -0,0 +1,83 @@
+use crate::core::{
+ requests::{RequestContext, Request, RequestFuture, ResponseResult},
+ network
+};
+
+#[derive(Debug, Serialize, Clone)]
+/// Once the user has confirmed their payment and shipping details, the Bot API
+/// sends the final confirmation in the form of an [`Update`] with the field
+/// pre_checkout_query. Use this method to respond to such pre-checkout queries.
+/// On success, True is returned. Note: The Bot API must receive an answer
+/// within 10 seconds after the pre-checkout query was sent.
+pub struct AnswerPreCheckoutQuery<'a> {
+ #[serde(skip_serializing)]
+ ctx: RequestContext<'a>,
+
+ /// Unique identifier for the query to be answered
+ pub pre_checkout_query_id: String,
+
+ /// Specify True if everything is alright (goods are available, etc.) and
+ /// the bot is ready to proceed with the order. Use False if there are any
+ /// problems.
+ pub ok: bool,
+
+ #[serde(skip_serializing_if = "Option::is_none")]
+ /// Required if ok is False. Error message in human readable form that
+ /// explains the reason for failure to proceed with the checkout (e.g.
+ /// "Sorry, somebody just bought the last of our amazing black T-shirts
+ /// while you were busy filling out your payment details. Please choose a
+ /// different color or garment!"). Telegram will display this message to
+ /// the user.
+ pub error_message: Option,
+}
+
+impl<'a> Request<'a> for AnswerPreCheckoutQuery<'a> {
+ type ReturnValue = bool;
+
+ fn send(self) -> RequestFuture<'a, ResponseResult> {
+ Box::pin(async move {
+ network::request_json(
+ &self.ctx.client,
+ &self.ctx.token,
+ "answerPreCheckoutQuery",
+ &self
+ ).await
+ })
+ }
+}
+
+impl<'a> AnswerPreCheckoutQuery<'a> {
+ pub(crate) fn new(
+ ctx: RequestContext<'a>,
+ pre_checkout_query_id: String,
+ ok: bool
+ ) -> Self {
+ Self {
+ ctx,
+ pre_checkout_query_id,
+ ok,
+ error_message: None
+ }
+ }
+
+ pub fn pre_checkout_query_id(mut self, pre_checkout_query_id: T) -> Self
+ where T: Into
+ {
+ self.pre_checkout_query_id = pre_checkout_query_id.into();
+ self
+ }
+
+ pub fn ok(mut self, ok: T) -> Self
+ where T: Into
+ {
+ self.ok = ok.into();
+ self
+ }
+
+ pub fn error_message(mut self, error_message: T) -> Self
+ where T: Into
+ {
+ self.error_message = Some(error_message.into());
+ self
+ }
+}
diff --git a/src/core/requests/answer_shipping_query.rs b/src/core/requests/answer_shipping_query.rs
new file mode 100644
index 00000000..74728d92
--- /dev/null
+++ b/src/core/requests/answer_shipping_query.rs
@@ -0,0 +1,91 @@
+use crate::core::types::ShippingOption;
+use crate::core::requests::{RequestContext, Request, RequestFuture, ResponseResult};
+use crate::core::network;
+
+#[derive(Debug, Clone, Serialize)]
+/// If you sent an invoice requesting a shipping address and the parameter
+/// is_flexible was specified, the Bot API will send an [`Update`] with a
+/// shipping_query field to the bot. Use this method to reply to shipping
+/// queries. On success, True is returned.
+pub struct AnswerShippingQuery<'a> {
+ #[serde(skip_serializing)]
+ ctx: RequestContext<'a>,
+
+ /// Unique identifier for the query to be answered
+ pub shipping_query_id: String,
+ /// Specify True if delivery to the specified address is possible and False
+ /// if there are any problems (for example, if delivery to the specified
+ /// address is not possible)
+ pub ok: bool,
+
+ #[serde(skip_serializing_if = "Option::is_none")]
+ /// Required if ok is True. A JSON-serialized array of available shipping
+ /// options.
+ pub shipping_options: Option>,
+
+ #[serde(skip_serializing_if = "Option::is_none")]
+ /// Required if ok is False. Error message in human readable form that
+ /// explains why it is impossible to complete the order (e.g. "Sorry,
+ /// delivery to your desired address is unavailable'). Telegram will
+ /// display this message to the user.
+ pub error_message: Option,
+}
+
+impl<'a> Request<'a> for AnswerShippingQuery<'a> {
+ type ReturnValue = bool;
+
+ fn send(self) -> RequestFuture<'a, ResponseResult> {
+ Box::pin(async move {
+ network::request_json(
+ &self.ctx.client,
+ &self.ctx.token,
+ "answerShippingQuery",
+ &self
+ ).await
+ })
+ }
+}
+
+impl<'a> AnswerShippingQuery<'a> {
+ pub(crate) fn new(
+ ctx: RequestContext<'a>,
+ shipping_query_id: String,
+ ok: bool
+ ) -> Self {
+ Self {
+ ctx,
+ shipping_query_id,
+ ok,
+ shipping_options: None,
+ error_message: None
+ }
+ }
+
+ pub fn shipping_query_id(mut self, shipping_query_id: T) -> Self
+ where T: Into
+ {
+ self.shipping_query_id = shipping_query_id.into();
+ self
+ }
+
+ pub fn ok(mut self, ok: T) -> Self
+ where T: Into
+ {
+ self.ok = ok.into();
+ self
+ }
+
+ pub fn shipping_options(mut self, shipping_options: T) -> Self
+ where T: Into>
+ {
+ self.shipping_options = Some(shipping_options.into());
+ self
+ }
+
+ pub fn error_message(mut self, error_message: T) -> Self
+ where T: Into
+ {
+ self.error_message = Some(error_message.into());
+ self
+ }
+}
diff --git a/src/core/requests/form_builder.rs b/src/core/requests/form_builder.rs
index e266c837..d9bd8b69 100644
--- a/src/core/requests/form_builder.rs
+++ b/src/core/requests/form_builder.rs
@@ -6,7 +6,6 @@ use crate::core::{
};
use reqwest::r#async::multipart::Form;
-use serde::Serialize;
/// This is a convenient struct that builds `reqwest::r#async::multipart::Form`
/// from scratch.
diff --git a/src/core/requests/forward_message.rs b/src/core/requests/forward_message.rs
index 8e61bce6..dc751bfd 100644
--- a/src/core/requests/forward_message.rs
+++ b/src/core/requests/forward_message.rs
@@ -1,8 +1,11 @@
use crate::core::{
network,
requests::{
- form_builder::FormBuilder, ChatId, Request, RequestContext,
- RequestFuture, ResponseResult,
+ ChatId,
+ Request,
+ RequestFuture,
+ RequestContext,
+ ResponseResult,
},
types::Message,
};
diff --git a/src/core/requests/mod.rs b/src/core/requests/mod.rs
index 05e69f7f..a1dfa82f 100644
--- a/src/core/requests/mod.rs
+++ b/src/core/requests/mod.rs
@@ -89,6 +89,8 @@ mod tests {
}
}
+pub mod answer_pre_checkout_query;
+pub mod answer_shipping_query;
pub mod edit_message_live_location;
pub mod forward_message;
pub mod get_file;
diff --git a/src/core/requests/send_chat_action.rs b/src/core/requests/send_chat_action.rs
index ad686836..f8a1142c 100644
--- a/src/core/requests/send_chat_action.rs
+++ b/src/core/requests/send_chat_action.rs
@@ -1,8 +1,83 @@
-use crate::core::requests::RequestContext;
-//TODO:: need implementation
+use crate::core::network;
+use crate::core::requests::{ChatId, Request, RequestContext, RequestFuture, ResponseResult};
+use crate::core::types::Message;
+///Use this method when you need to tell the user that something is happening on the bot's side.
+///The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status).
+///Returns True on success.
#[derive(Debug, Clone, Serialize)]
struct SendChatAction<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
+ /// Unique identifier for the target chat or
+ /// username of the target channel (in the format @channelusername)
+ pub chat_id: ChatId,
+ /// Type of action to broadcast. Choose one, depending on what the user is
+ /// about to receive: typing for text messages, upload_photo for photos,
+ /// record_video or upload_video for videos, record_audio or upload_audio
+ /// for audio files, upload_document for general files, find_location for
+ /// location data, record_video_note or upload_video_note for video notes.
+ pub action: ChatAction,
}
+
+#[derive(Debug, Serialize, From, Clone)]
+#[serde(rename_all = "snake_case")]
+enum ChatAction {
+ Typing,
+ UploadPhoto,
+ RecordVideo,
+ UploadVideo,
+ RecordAudio,
+ UploadAudio,
+ UploadDocument,
+ FindLocation,
+ RecordVideoNote,
+ UploadVideoNote,
+}
+
+impl<'a> Request<'a> for SendChatAction<'a> {
+ type ReturnValue = bool;
+
+ fn send(self) -> RequestFuture<'a, ResponseResult> {
+ Box::pin(async move {
+ network::request_json(
+ &self.ctx.client,
+ &self.ctx.token,
+ "sendChatAction",
+ &self,
+ )
+ .await
+ })
+ }
+}
+
+impl<'a> SendChatAction<'a> {
+ pub(crate) fn new(
+ ctx: RequestContext<'a>,
+ chat_id: ChatId,
+ action: ChatAction,
+ ) -> Self {
+ Self {
+ ctx,
+ chat_id,
+ action,
+ }
+ }
+
+ pub fn chat_id(mut self, chat_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.chat_id = chat_id.into();
+ self
+ }
+
+
+ pub fn action(mut self, action: T) -> Self
+ where
+ T: Into,
+ {
+ self.action = action.into();
+ self
+ }
+}
\ No newline at end of file
diff --git a/src/core/requests/send_contact.rs b/src/core/requests/send_contact.rs
index 6ffd1f2a..16cbec0e 100644
--- a/src/core/requests/send_contact.rs
+++ b/src/core/requests/send_contact.rs
@@ -1,7 +1,142 @@
-use crate::core::requests::RequestContext;
-//TODO:: need implementation
+use crate::core::network;
+use crate::core::requests::{
+ ChatId, Request, RequestContext, RequestFuture, ResponseResult,
+};
+use crate::core::types::{Message, ReplyMarkup};
+
+/// Use this method to send phone contacts.
+/// returned.
#[derive(Debug, Clone, Serialize)]
struct SendContact<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
+ /// Unique identifier for the target chat or
+ /// username of the target channel (in the format @channelusername)
+ pub chat_id: ChatId,
+ /// Contact's phone number
+ pub phone_number: String,
+ /// Contact's first name
+ pub first_name: String,
+ /// Contact's last name
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub last_name: Option,
+ /// Additional data about the contact in the form of a
+ /// vCard, 0-2048 bytes
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub vcard: Option,
+ /// Sends the message silently. Users will receive a
+ /// notification with no sound.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub disable_notification: Option,
+ /// If the message is a reply, ID of the original
+ /// message
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reply_to_message_id: Option,
+ /// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove
+ /// or ForceReply Optional Additional interface options. A JSON-serialized
+ /// object for an inline keyboard, custom reply keyboard, instructions to
+ /// remove keyboard or to force a reply from the user.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reply_markup: Option,
+}
+
+impl<'a> Request<'a> for SendContact<'a> {
+ type ReturnValue = Message;
+
+ fn send(self) -> RequestFuture<'a, ResponseResult> {
+ Box::pin(async move {
+ network::request_json(
+ &self.ctx.client,
+ &self.ctx.token,
+ "sendContact",
+ &self,
+ )
+ .await
+ })
+ }
+}
+
+impl<'a> SendContact<'a> {
+ pub(crate) fn new(
+ ctx: RequestContext<'a>,
+ chat_id: ChatId,
+ phone_number: String,
+ first_name: String,
+ ) -> Self {
+ Self {
+ ctx,
+ chat_id,
+ phone_number,
+ first_name,
+ last_name: None,
+ vcard: None,
+ disable_notification: None,
+ reply_to_message_id: None,
+ reply_markup: None,
+ }
+ }
+
+ pub fn chat_id(mut self, chat_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.chat_id = chat_id.into();
+ self
+ }
+
+ pub fn phone_number(mut self, phone_number: T) -> Self
+ where
+ T: Into,
+ {
+ self.phone_number = phone_number.into();
+ self
+ }
+
+ pub fn first_name(mut self, first_name: T) -> Self
+ where
+ T: Into,
+ {
+ self.first_name = first_name.into();
+ self
+ }
+
+ pub fn last_name(mut self, last_name: T) -> Self
+ where
+ T: Into,
+ {
+ self.last_name = Some(last_name.into());
+ self
+ }
+
+ pub fn vcard(mut self, vcard: T) -> Self
+ where
+ T: Into,
+ {
+ self.vcard = Some(vcard.into());
+ self
+ }
+
+ pub fn disable_notification(mut self, disable_notification: T) -> Self
+ where
+ T: Into,
+ {
+ self.disable_notification = Some(disable_notification.into());
+ self
+ }
+
+ pub fn reply_to_message_id(mut self, reply_to_message_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.reply_to_message_id = Some(reply_to_message_id.into());
+ self
+ }
+
+ pub fn reply_markup(mut self, reply_markup: T) -> Self
+ where
+ T: Into,
+ {
+ self.reply_markup = Some(reply_markup.into());
+ self
+ }
}
diff --git a/src/core/requests/send_message.rs b/src/core/requests/send_message.rs
index 889bcb14..d3bf2421 100644
--- a/src/core/requests/send_message.rs
+++ b/src/core/requests/send_message.rs
@@ -1,8 +1,11 @@
use crate::core::{
network,
requests::{
- form_builder::FormBuilder, ChatId, Request, RequestContext,
- RequestFuture, ResponseResult,
+ ChatId,
+ Request,
+ RequestFuture,
+ RequestContext,
+ ResponseResult,
},
types::{Message, ParseMode, ReplyMarkup},
};
diff --git a/src/core/requests/send_photo.rs b/src/core/requests/send_photo.rs
index 0d4e40e3..9d2a07b5 100644
--- a/src/core/requests/send_photo.rs
+++ b/src/core/requests/send_photo.rs
@@ -1,5 +1,3 @@
-use std::path::Path;
-
use crate::core::{
network,
requests::{
diff --git a/src/core/requests/send_poll.rs b/src/core/requests/send_poll.rs
index 8341bb39..861c98e7 100644
--- a/src/core/requests/send_poll.rs
+++ b/src/core/requests/send_poll.rs
@@ -1,8 +1,114 @@
-use crate::core::requests::RequestContext;
-//TODO:: need implementation
+use crate::core::network;
+use crate::core::requests::{
+ ChatId, Request, RequestContext, RequestFuture, ResponseResult,
+};
+use crate::core::types::{Message, ReplyMarkup};
+/// Use this method to send a native poll. A native poll can't be sent to a
+/// private chat. On success, the sent Message is returned.
#[derive(Debug, Clone, Serialize)]
struct SendPoll<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
+ /// identifier for the target chat or username of the target channel (in
+ /// the format @channelusername). A native poll can't be sent to a private
+ /// chat.
+ chat_id: ChatId,
+ /// Poll question, 1-255 characters
+ question: String,
+ /// List of answer options, 2-10 strings 1-100 characters each
+ options: Vec,
+ /// Sends the message silently. Users will receive a notification with no
+ /// sound.
+ disable_notification: Option,
+ /// If the message is a reply, ID of the original message
+ reply_to_message_id: Option,
+ /// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove
+ /// or ForceReply Optional Additional interface options. A JSON-serialized
+ /// object for an inline keyboard, custom reply keyboard, instructions to
+ /// remove reply keyboard or to force a reply from the user.
+ reply_markup: Option,
+}
+
+impl<'a> Request<'a> for SendPoll<'a> {
+ type ReturnValue = Message;
+
+ fn send(self) -> RequestFuture<'a, ResponseResult> {
+ Box::pin(async move {
+ network::request_json(
+ &self.ctx.client,
+ &self.ctx.token,
+ "sendPoll",
+ &self,
+ )
+ .await
+ })
+ }
+}
+
+impl<'a> SendPoll<'a> {
+ pub(crate) fn new(
+ ctx: RequestContext<'a>,
+ chat_id: ChatId,
+ question: String,
+ options: Vec,
+ ) -> Self {
+ Self {
+ ctx,
+ chat_id,
+ question,
+ options,
+ disable_notification: None,
+ reply_to_message_id: None,
+ reply_markup: None,
+ }
+ }
+
+ pub fn chat_id(mut self, chat_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.chat_id = chat_id.into();
+ self
+ }
+
+ pub fn question(mut self, question: T) -> Self
+ where
+ T: Into,
+ {
+ self.question = question.into();
+ self
+ }
+
+ pub fn options(mut self, options: T) -> Self
+ where
+ T: Into>,
+ {
+ self.options = options.into();
+ self
+ }
+
+ pub fn disable_notification(mut self, disable_notification: T) -> Self
+ where
+ T: Into,
+ {
+ self.disable_notification = Some(disable_notification.into());
+ self
+ }
+
+ pub fn reply_to_message_id(mut self, reply_to_message_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.reply_to_message_id = Some(reply_to_message_id.into());
+ self
+ }
+
+ pub fn reply_markup(mut self, reply_markup: T) -> Self
+ where
+ T: Into,
+ {
+ self.reply_markup = Some(reply_markup.into());
+ self
+ }
}
diff --git a/src/core/requests/send_venue.rs b/src/core/requests/send_venue.rs
index deac9b61..85fee6d6 100644
--- a/src/core/requests/send_venue.rs
+++ b/src/core/requests/send_venue.rs
@@ -1,43 +1,158 @@
-use crate::core::requests::{ChatId, RequestContext};
+use crate::core::network;
+use crate::core::requests::{
+ ChatId, Request, RequestContext, RequestFuture, ResponseResult,
+};
+use crate::core::types::{Message, ReplyMarkup};
-//TODO:: need implementation
-///Use this method to send information about a venue. On success, the sent
+/// Use this method to send information about a venue.
/// Message is returned.
#[derive(Debug, Clone, Serialize)]
struct SendVenue<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
- /// Integer or String Yes Unique identifier for the target chat or
+ /// Unique identifier for the target chat or
/// username of the target channel (in the format @channelusername)
- chat_id: ChatId,
- /// Float number Yes Latitude of the venue
- latitude: f64,
- ///Float number Yes Longitude of the venue
- longitude: f64,
- /// Yes Name of the venue
- title: String,
- ///String Yes Address of the venue
- address: String,
- /// String Optional Foursquare identifier of the venue
+ pub chat_id: ChatId,
+ /// Latitude of the venue
+ pub latitude: f64,
+ /// Longitude of the venue
+ pub longitude: f64,
+ /// Name of the venue
+ pub title: String,
+ /// Address of the venue
+ pub address: String,
+ /// Foursquare identifier of the venue
#[serde(skip_serializing_if = "Option::is_none")]
- foursquare_id: Option,
- /// String Optional Foursquare type of the venue, if known. (For
+ pub foursquare_id: Option,
+ /// Foursquare type of the venue, if known. (For
/// example, “arts_entertainment/default”, “arts_entertainment/aquarium” or
/// “food/icecream”.)
#[serde(skip_serializing_if = "Option::is_none")]
- foursquare_type: Option,
- /// Boolean Optional Sends the message silently. Users will receive a
+ pub foursquare_type: Option,
+ /// Sends the message silently. Users will receive a
/// notification with no sound.
#[serde(skip_serializing_if = "Option::is_none")]
- disable_notification: Option,
- /// Integer Optional If the message is a reply, ID of the original
+ pub disable_notification: Option,
+ /// If the message is a reply, ID of the original
/// message
#[serde(skip_serializing_if = "Option::is_none")]
- reply_to_message_id: Option,
+ pub reply_to_message_id: Option,
/// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or
/// ForceReply Optional Additional interface options. A JSON-serialized
/// object for an inline keyboard, custom reply keyboard, instructions to
/// remove reply keyboard or to force a reply from the user.
#[serde(skip_serializing_if = "Option::is_none")]
- reply_markup: Option<()>, //TODO: need concrete type
+ pub reply_markup: Option,
+}
+
+impl<'a> Request<'a> for SendVenue<'a> {
+ type ReturnValue = Message;
+
+ fn send(self) -> RequestFuture<'a, ResponseResult> {
+ Box::pin(async move {
+ network::request_json(
+ &self.ctx.client,
+ &self.ctx.token,
+ "sendVenue",
+ &self,
+ )
+ .await
+ })
+ }
+}
+
+impl<'a> SendVenue<'a> {
+ pub fn new(
+ ctx: RequestContext<'a>,
+ chat_id: ChatId,
+ latitude: f64,
+ longitude: f64,
+ title: String,
+ address: String,
+ ) -> Self {
+ Self {
+ ctx,
+ chat_id,
+ latitude,
+ longitude,
+ title,
+ address,
+ foursquare_id: None,
+ foursquare_type: None,
+ disable_notification: None,
+ reply_to_message_id: None,
+ reply_markup: None,
+ }
+ }
+ pub fn chat_id(mut self, chat_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.chat_id = chat_id.into();
+ self
+ }
+
+ pub fn longitude(mut self, longitude: T) -> Self
+ where
+ T: Into,
+ {
+ self.longitude = longitude.into();
+ self
+ }
+
+ pub fn latitude(mut self, latitude: T) -> Self
+ where
+ T: Into,
+ {
+ self.latitude = latitude.into();
+ self
+ }
+
+ pub fn title(mut self, title: T) -> Self
+ where
+ T: Into,
+ {
+ self.title = title.into();
+ self
+ }
+
+ pub fn address(mut self, address: T) -> Self
+ where
+ T: Into,
+ {
+ self.address = address.into();
+ self
+ }
+
+ pub fn foursquare_id(mut self, foursquare_id: T) -> Self
+ where
+ T: Into,
+ {
+ self.foursquare_id = Some(foursquare_id.into());
+ self
+ }
+
+ pub fn disable_notification(mut self, disable_notification: T) -> Self
+ where
+ T: Into,
+ {
+ self.disable_notification = Some(disable_notification.into());
+ self
+ }
+
+ pub fn foursquare_type(mut self, foursquare_type: T) -> Self
+ where
+ T: Into,
+ {
+ self.foursquare_type = Some(foursquare_type.into());
+ self
+ }
+
+ pub fn reply_markup(mut self, reply_markup: T) -> Self
+ where
+ T: Into,
+ {
+ self.reply_markup = Some(reply_markup.into());
+ self
+ }
}
diff --git a/src/core/requests/stop_message_live_location.rs b/src/core/requests/stop_message_live_location.rs
index 2598f4bf..c86ee1ed 100644
--- a/src/core/requests/stop_message_live_location.rs
+++ b/src/core/requests/stop_message_live_location.rs
@@ -1,19 +1,20 @@
-use std::path::Path;
-
use crate::core::{
network,
requests::{
- form_builder::FormBuilder, ChatId, Request, RequestContext,
- RequestFuture, ResponseResult,
+ ChatId,
+ Request,
+ RequestFuture,
+ RequestContext,
+ ResponseResult,
},
- types::{InlineKeyboardMarkup, Message, ParseMode},
+ types::{InlineKeyboardMarkup, Message},
};
/// Use this method to stop updating a live location message before live_period
/// expires. On success, if the message was sent by the bot, the sent Message is
/// returned, otherwise True is returned.
#[derive(Debug, Clone, Serialize)]
-struct StopMessageLiveLocation<'a> {
+pub struct StopMessageLiveLocation<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
/// Required if inline_message_id is not specified. Unique identifier for
@@ -52,7 +53,7 @@ impl<'a> Request<'a> for StopMessageLiveLocation<'a> {
}
impl<'a> StopMessageLiveLocation<'a> {
- fn new(ctx: RequestContext<'a>) -> Self {
+ pub(crate) fn new(ctx: RequestContext<'a>) -> Self {
Self {
ctx,
chat_id: None,
diff --git a/src/core/requests/utils.rs b/src/core/requests/utils.rs
index 44231ef2..193bad0c 100644
--- a/src/core/requests/utils.rs
+++ b/src/core/requests/utils.rs
@@ -1,9 +1,12 @@
+use std::path::PathBuf;
+
use bytes::{Bytes, BytesMut};
use reqwest::r#async::multipart::Part;
-use std::fs::File;
-use std::path::PathBuf;
-use tokio::codec::FramedRead;
-use tokio::prelude::*;
+use tokio::{
+ prelude::*,
+ codec::FramedRead,
+};
+
struct FileDecoder;
diff --git a/src/core/types/animation.rs b/src/core/types/animation.rs
index 09fccf40..4a60e4c5 100644
--- a/src/core/types/animation.rs
+++ b/src/core/types/animation.rs
@@ -1,13 +1,63 @@
use crate::core::types::PhotoSize;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+/// This object represents an animation file (GIF or H.264/MPEG-4 AVC video
+/// without sound).
pub struct Animation {
+ /// Identifier for this file
pub file_id: String,
+ /// Video width as defined by sender
pub width: u32,
+ /// Video height as defined by sender
pub height: u32,
+ /// Duration of the video in seconds as defined by sender
pub duration: u32,
- pub thumb: PhotoSize,
+ /// Optional. Animation thumbnail as defined by sender
+ pub thumb: Option,
+ /// Optional. Original animation filename as defined by sender
pub file_name: Option,
+ /// Optional. MIME type of the file as defined by sender
pub mime_type: Option,
+ /// Optional. File size
pub file_size: Option
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn deserialize() {
+ let json = r#"{
+ "file_id":"id",
+ "width":320,
+ "height":320,
+ "duration":59,
+ "thumb":{
+ "file_id":"id",
+ "width":320,
+ "height":320,
+ "file_size":3452
+ },
+ "file_name":"some",
+ "mime_type":"gif",
+ "file_size":6500}"#;
+ let expected = Animation {
+ file_id: "id".to_string(),
+ width: 320,
+ height: 320,
+ duration: 59,
+ thumb: Some(PhotoSize {
+ file_id: "id".to_string(),
+ width: 320,
+ height: 320,
+ file_size: Some(3452)
+ }),
+ file_name: Some("some".to_string()),
+ mime_type: Some("gif".to_string()),
+ file_size: Some(6500)
+ };
+ let actual = serde_json::from_str::(json).unwrap();
+ assert_eq!(actual, expected)
+ }
+}
diff --git a/src/core/types/answer_pre_checkout_query.rs b/src/core/types/answer_pre_checkout_query.rs
deleted file mode 100644
index 07d1c758..00000000
--- a/src/core/types/answer_pre_checkout_query.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
-pub struct AnswerPreCheckoutQuery {
- pub pre_checkout_query_id: String,
- pub ok: bool,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub error_message: Option,
-}
diff --git a/src/core/types/answer_shipping_query.rs b/src/core/types/answer_shipping_query.rs
deleted file mode 100644
index c04c016b..00000000
--- a/src/core/types/answer_shipping_query.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use crate::core::types::ShippingOption;
-
-#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
-pub struct AnswerShippingQuery {
- pub shipping_query_id: String,
- pub ok: bool,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub shipping_options: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub error_message: Option,
-}
diff --git a/src/core/types/inline_keyboard_button.rs b/src/core/types/inline_keyboard_button.rs
index 73389ae6..ba2bcc4f 100644
--- a/src/core/types/inline_keyboard_button.rs
+++ b/src/core/types/inline_keyboard_button.rs
@@ -40,3 +40,54 @@ pub enum InlineKeyboardButtonKind {
/* CallbackGame(CallbackGame), TODO: разобраться, что с этим делать
* TODO: add LoginUrl, pay */
}
+
+/// Build buttons
+///
+/// Example:
+/// ```edition2018
+/// use async_telegram_bot::core::types::InlineKeyboardButton;
+///
+/// fn main() {
+/// let url_button = InlineKeyboardButton::url(
+/// "Text".to_string(),
+/// "http://url.com".to_string(),
+/// );
+/// }
+/// ```
+impl InlineKeyboardButton {
+ pub fn url(text: String, url: String) -> InlineKeyboardButton {
+ InlineKeyboardButton {
+ text,
+ kind: InlineKeyboardButtonKind::Url(url),
+ }
+ }
+
+ pub fn callback(text: String, callback_data: String)
+ -> InlineKeyboardButton {
+ InlineKeyboardButton {
+ text,
+ kind: InlineKeyboardButtonKind::CallbackData(callback_data),
+ }
+ }
+
+ pub fn switch_inline_query(text: String, switch_inline_query: String)
+ -> InlineKeyboardButton {
+ InlineKeyboardButton {
+ text,
+ kind: InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query)
+ }
+ }
+
+ pub fn switch_inline_query_current_chat(
+ text: String,
+ switch_inline_query_current_chat: String
+ ) -> InlineKeyboardButton {
+
+ InlineKeyboardButton {
+ text,
+ kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
+ switch_inline_query_current_chat
+ )
+ }
+ }
+}
diff --git a/src/core/types/inline_keyboard_markup.rs b/src/core/types/inline_keyboard_markup.rs
index 77f8eee7..5dd68af3 100644
--- a/src/core/types/inline_keyboard_markup.rs
+++ b/src/core/types/inline_keyboard_markup.rs
@@ -11,3 +11,111 @@ pub struct InlineKeyboardMarkup {
/// [`InlineKeyboardButton`] objects
pub inline_keyboard: Vec>,
}
+
+/// Build Markup
+///
+/// Example:
+/// ```edition2018
+/// use async_telegram_bot::core::types::{
+/// InlineKeyboardMarkup,
+/// InlineKeyboardButton
+/// };
+///
+/// fn main() {
+/// let url_button = InlineKeyboardButton::url(
+/// "text".to_string(),
+/// "http://url.com".to_string()
+/// );
+/// let keyboard = InlineKeyboardMarkup::new()
+/// .row(vec![url_button]);
+/// }
+/// ```
+impl InlineKeyboardMarkup {
+ pub fn new() -> Self {
+ Self {
+ inline_keyboard: vec![]
+ }
+ }
+
+ pub fn append_row(mut self, buttons: Vec) -> Self {
+ self.inline_keyboard.push(buttons);
+ self
+ }
+
+ pub fn append_to_row(mut self, button: InlineKeyboardButton, index: usize)
+ -> Self {
+ match self.inline_keyboard.get_mut(index) {
+ Some(buttons) => buttons.push(button),
+ None => self.inline_keyboard.push(vec![button])
+ };
+ self
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn append_row() {
+ let button1 = InlineKeyboardButton::url(
+ "text 1".to_string(),
+ "url 1".to_string(),
+ );
+ let button2 = InlineKeyboardButton::url(
+ "text 2".to_string(),
+ "url 2".to_string(),
+ );
+ let markup = InlineKeyboardMarkup::new()
+ .append_row(vec![button1.clone(), button2.clone()]);
+ let expected = InlineKeyboardMarkup {
+ inline_keyboard: vec![
+ vec![button1.clone(), button2.clone()]
+ ]
+ };
+ assert_eq!(markup, expected);
+ }
+
+ #[test]
+ fn append_to_row__existent_row() {
+ let button1 = InlineKeyboardButton::url(
+ "text 1".to_string(),
+ "url 1".to_string(),
+ );
+ let button2 = InlineKeyboardButton::url(
+ "text 2".to_string(),
+ "url 2".to_string(),
+ );
+ let markup = InlineKeyboardMarkup::new()
+ .append_row(vec![button1.clone()])
+ .append_to_row(button2.clone(), 0);
+ let expected = InlineKeyboardMarkup {
+ inline_keyboard: vec![
+ vec![button1.clone(), button2.clone()]
+ ]
+ };
+ assert_eq!(markup, expected);
+ }
+
+ #[test]
+ fn append_to_row__nonexistent_row() {
+ let button1 = InlineKeyboardButton::url(
+ "text 1".to_string(),
+ "url 1".to_string(),
+ );
+ let button2 = InlineKeyboardButton::url(
+ "text 2".to_string(),
+ "url 2".to_string(),
+ );
+ let markup = InlineKeyboardMarkup::new()
+ .append_row(vec![button1.clone()])
+ .append_to_row(button2.clone(), 1);
+ let expected = InlineKeyboardMarkup {
+ inline_keyboard: vec![
+ vec![button1.clone()],
+ vec![button2.clone()]
+ ]
+ };
+ assert_eq!(markup, expected);
+ }
+}
diff --git a/src/core/types/label_price.rs b/src/core/types/label_price.rs
index a9ced54e..d7816ef3 100644
--- a/src/core/types/label_price.rs
+++ b/src/core/types/label_price.rs
@@ -1,5 +1,29 @@
-#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
+#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize)]
+/// This object represents a portion of the price for goods or services.
pub struct LabeledPrice {
+ /// Portion label
pub label: String,
+ /// Price of the product in the smallest units of the
+ /// [currency](https://core.telegram.org/bots/payments#supported-currencies)
+ /// (integer, not float/double). For example, for a price of US$ 1.45 pass
+ /// amount = 145. See the exp parameter in [`currencies.json`](https://core.telegram.org/bots/payments/currencies.json),
+ /// it shows the number of digits past the decimal point for each currency
+ /// (2 for the majority of currencies).
pub amount: i64,
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn serialize() {
+ let labeled_price = LabeledPrice {
+ label: "Label".to_string(),
+ amount: 60
+ };
+ let expected = r#"{"label":"Label","amount":60}"#;
+ let actual = serde_json::to_string(&labeled_price).unwrap();
+ assert_eq!(actual, expected);
+ }
+}
diff --git a/src/core/types/mod.rs b/src/core/types/mod.rs
index 7e6d7e08..3c1cca89 100644
--- a/src/core/types/mod.rs
+++ b/src/core/types/mod.rs
@@ -1,7 +1,5 @@
use self::not_implemented_types::*;
pub use self::{
- answer_pre_checkout_query::AnswerPreCheckoutQuery,
- answer_shipping_query::AnswerShippingQuery,
animation::Animation,
audio::Audio,
callback_query::CallbackQuery,
@@ -51,8 +49,6 @@ pub use self::{
};
mod animation;
-mod answer_pre_checkout_query;
-mod answer_shipping_query;
mod audio;
mod callback_query;
mod chat;
diff --git a/src/core/types/photo_size.rs b/src/core/types/photo_size.rs
index 9f8d6cf3..8df34596 100644
--- a/src/core/types/photo_size.rs
+++ b/src/core/types/photo_size.rs
@@ -1,7 +1,32 @@
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
+/// This object represents one size of a photo or a [`Document`] /
+/// [`Sticker`] thumbnail.
pub struct PhotoSize {
+ /// Identifier for this file
pub file_id: String,
+ /// Photo width
pub width: i32,
+ /// Photo height
pub height: i32,
+ /// Optional. File size
pub file_size: Option,
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn deserialize() {
+ let json = r#"{"file_id":"id","width":320,"height":320,
+ "file_size":3452}"#;
+ let expected = PhotoSize {
+ file_id: "id".to_string(),
+ width: 320,
+ height: 320,
+ file_size: Some(3452)
+ };
+ let actual = serde_json::from_str::(json).unwrap();
+ assert_eq!(actual, expected);
+ }
+}
diff --git a/src/core/types/send_invoice.rs b/src/core/types/send_invoice.rs
index 87c26524..07698819 100644
--- a/src/core/types/send_invoice.rs
+++ b/src/core/types/send_invoice.rs
@@ -1,6 +1,6 @@
use crate::core::types::{InlineKeyboardMarkup, LabeledPrice};
-#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct SendInvoice {
pub chat_id: i64,
pub title: String,
diff --git a/src/core/types/shipping_option.rs b/src/core/types/shipping_option.rs
index 4dc1835c..9a6e4e8e 100644
--- a/src/core/types/shipping_option.rs
+++ b/src/core/types/shipping_option.rs
@@ -1,8 +1,31 @@
use crate::core::types::LabeledPrice;
-#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
+#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize)]
+/// This object represents one shipping option.
pub struct ShippingOption {
- pub id: i64,
+ /// Shipping option identifier
+ pub id: String,
+ /// Option title
pub title: String,
+ /// List of price portions
pub prices: Vec,
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn serialize() {
+ let shipping_option = ShippingOption {
+ id: "0".to_string(),
+ title: "Option".to_string(),
+ prices: vec![
+ LabeledPrice { label: "Label".to_string(), amount: 60 }
+ ]
+ };
+ let expected = r#"{"id":"0","title":"Option","prices":[{"label":"Label","amount":60}]}"#;
+ let actual = serde_json::to_string(&shipping_option).unwrap();
+ assert_eq!(actual, expected);
+ }
+}
diff --git a/src/keyboards/inline_keyboard_button.rs b/src/keyboards/inline_keyboard_button.rs
deleted file mode 100644
index 9cc43188..00000000
--- a/src/keyboards/inline_keyboard_button.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use crate::core::types::{InlineKeyboardButton, InlineKeyboardButtonKind};
-
-pub struct InlineKeyboardButtonBuilder;
-
-/// Build buttons
-///
-/// Example:
-/// ```edition2018
-/// use async_telegram_bot::keyboards::InlineKeyboardButtonBuilder;
-///
-/// fn main() {
-/// let url_button = InlineKeyboardButtonBuilder::url(
-/// "Text".to_string(),
-/// "http://url.com".to_string(),
-/// );
-/// }
-/// ```
-impl InlineKeyboardButtonBuilder {
- pub fn url(text: String, url: String) -> InlineKeyboardButton {
- InlineKeyboardButton {
- text,
- kind: InlineKeyboardButtonKind::Url(url),
- }
- }
-
- pub fn callback(text: String, callback_data: String)
- -> InlineKeyboardButton {
- InlineKeyboardButton {
- text,
- kind: InlineKeyboardButtonKind::CallbackData(callback_data),
- }
- }
-
- pub fn switch_inline_query(text: String, switch_inline_query: String)
- -> InlineKeyboardButton {
- InlineKeyboardButton {
- text,
- kind: InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query)
- }
- }
-
- pub fn switch_inline_query_current_chat(
- text: String,
- switch_inline_query_current_chat: String
- ) -> InlineKeyboardButton {
-
- InlineKeyboardButton {
- text,
- kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
- switch_inline_query_current_chat
- )
- }
- }
-}
-
diff --git a/src/keyboards/inline_keyboard_markup.rs b/src/keyboards/inline_keyboard_markup.rs
deleted file mode 100644
index e0ff0663..00000000
--- a/src/keyboards/inline_keyboard_markup.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use crate::core::types::{InlineKeyboardMarkup, InlineKeyboardButton};
-
-pub struct InlineKeyboardMarkupBuilder {
- keyboard: InlineKeyboardMarkup,
-}
-
-/// Builder for [`InlineKeyboardMarkup`]
-///
-/// Example:
-/// ```edition2018
-/// use async_telegram_bot::keyboards;
-///
-/// fn main() {
-/// let url_button = keyboards::InlineKeyboardButtonBuilder::url(
-/// "text".to_string(),
-/// "http://url.com".to_string()
-/// );
-/// let keyboard = keyboards::InlineKeyboardMarkupBuilder::new()
-/// .row(vec![url_button])
-/// .build();
-/// }
-/// ```
-impl InlineKeyboardMarkupBuilder {
- pub fn new() -> Self {
- Self {
- keyboard: InlineKeyboardMarkup {
- inline_keyboard: vec![]
- }
- }
- }
-
- pub fn row(mut self, buttons: Vec) -> Self {
- self.keyboard.inline_keyboard.push(buttons);
- self
- }
-
- pub fn append_to_row(mut self, button: InlineKeyboardButton, index: usize)
- -> Self {
- match self.keyboard.inline_keyboard.get_mut(index) {
- Some(buttons) => buttons.push(button),
- None => self.keyboard.inline_keyboard.push(vec![button])
- };
- self
- }
-
- pub fn build(self) -> InlineKeyboardMarkup {
- self.keyboard
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::keyboards::InlineKeyboardButtonBuilder;
-
- #[test]
- fn test_row() {
- let btn = InlineKeyboardButtonBuilder::url(
- "text".to_string(),
- "http://url".to_string(),
- );
- let kb = InlineKeyboardMarkupBuilder::new()
- .row(vec![btn.clone()])
- .build();
- let expected = InlineKeyboardMarkup {
- inline_keyboard: vec![vec![btn.clone()]],
- };
- assert_eq!(kb, expected);
- }
-}
diff --git a/src/keyboards/mod.rs b/src/keyboards/mod.rs
deleted file mode 100644
index 63fac65a..00000000
--- a/src/keyboards/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-mod inline_keyboard_button;
-mod inline_keyboard_markup;
-
-pub use self::{
- inline_keyboard_button::InlineKeyboardButtonBuilder,
- inline_keyboard_markup::InlineKeyboardMarkupBuilder,
-};
diff --git a/src/lib.rs b/src/lib.rs
index 081b06a5..4f8dd9a7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,5 +5,4 @@ extern crate serde;
pub mod bot;
pub mod core;
-pub mod keyboards;
pub mod dispatcher;