diff --git a/Cargo.toml b/Cargo.toml index d5d760a8..b3fcc9b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,6 @@ reqwest = "0.9.20" serde_json = "1.0.39" serde = {version = "1.0.92", features = ["derive"] } lazy_static = "1.3" -apply = "0.2.2" \ No newline at end of file +apply = "0.2.2" +typed-builder = "0.3.0" +derive_more = "0.15.0" \ No newline at end of file diff --git a/src/core/mod.rs b/src/core/mod.rs index f268fe11..bb06ff46 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,3 +1,6 @@ +mod network; +mod requests; + mod games; mod getting_updates; mod inline_mode; @@ -5,5 +8,4 @@ mod other; mod payments; mod stickers; mod telegram_passport; -mod network; -mod updating_messages; \ No newline at end of file +mod updating_messages; diff --git a/src/core/network/mod.rs b/src/core/network/mod.rs index 883b6274..fafcdf1b 100644 --- a/src/core/network/mod.rs +++ b/src/core/network/mod.rs @@ -2,9 +2,9 @@ use reqwest::StatusCode; use reqwest::r#async::Client; use serde_json::Value; use futures::compat::Future01CompatExt; -use reqwest::r#async::multipart::Form; use apply::Apply; - +use serde::de::DeserializeOwned; +use super::requests::Request; const TELEGRAM_URL_START: &str = "https://api.telegram.org/bot"; @@ -20,20 +20,18 @@ pub enum Error { pub type Response = Result; -pub async fn request( +pub async fn request>( client: &Client, - token: &str, - method_name: &str, - params: Option
, + request: Req, ) -> Response { let mut response = client .post(&format!( "{}{token}/{method}", TELEGRAM_URL_START, - token = token, - method = method_name, + token = request.token(), + method = request.name(), )) - .apply(|req| if let Some(params) = params { + .apply(|req| if let Some(params) = request.params() { req.multipart(params) } else { req }) .send() diff --git a/src/core/requests/get_me.rs b/src/core/requests/get_me.rs new file mode 100644 index 00000000..f732a1e3 --- /dev/null +++ b/src/core/requests/get_me.rs @@ -0,0 +1,16 @@ +use crate::core::other::User; +use super::Request; + +use reqwest::r#async::multipart::Form; + + +#[derive(Debug, Constructor)] +pub struct GetMe<'a> { + token: &'a str, +} + +impl Request for GetMe<'_> { + fn name(&self) -> &str { "getMe" } + fn params(self) -> Option { None } + fn token(&self) -> &str { self.token } +} \ No newline at end of file diff --git a/src/core/requests/mod.rs b/src/core/requests/mod.rs new file mode 100644 index 00000000..8e4a4eef --- /dev/null +++ b/src/core/requests/mod.rs @@ -0,0 +1,27 @@ +use reqwest::r#async::multipart::Form; + + +/// Request that can be sended to telegram. +/// `R` - return type. +pub trait Request { + /// Get name of the request (e.g. "getMe" or "sendMessage") + fn name(&self) -> &str; + + /// Form with params + fn params(self) -> Option; + + /// Bot token + fn token(&self) -> &str; +} + +/// Unique identifier for the target chat or username of the target channel (in the format @channelusername) +#[derive(Debug, Serialize, From)] +pub enum ChatId { + /// chat identifier + Id(i32), // 32? + /// _channel_ username (in the format @channelusername) + ChannelUsername(String) +} + +pub mod get_me; +pub mod send_message; \ No newline at end of file diff --git a/src/core/requests/send_message.rs b/src/core/requests/send_message.rs new file mode 100644 index 00000000..44ea3649 --- /dev/null +++ b/src/core/requests/send_message.rs @@ -0,0 +1,81 @@ +use crate::core::other::Message; +use super::{ChatId, Request}; + +use reqwest::r#async::multipart::Form; + + +#[derive(Debug, TypedBuilder)] +pub struct SendMessage { + token: String, + chat_id: ChatId, + text: String, + + #[builder(default)] + parse_mode: Option, // TODO: enum + #[builder(default)] + disable_web_page_preview: Option, + #[builder(default)] + disable_notification: Option, + #[builder(default)] + reply_to_message_id: Option, + #[builder(default)] + reply_markup: Option<()>, // TODO: ReplyMarkup enum +} + +impl Request for SendMessage { + fn name(&self) -> &str { "getMe" } + fn params(self) -> Option { + use apply::Apply; + + // TODO: we need better serialization + let params = Form::new() + .text("chat_id", format!("{:?}", self.chat_id)) + .text("text", self.text) + .apply(|f| if let Some(parse_mode) = self.parse_mode { + f.text("parse_mode", parse_mode); + f + } else { f }) + .apply(|f| if let Some(disable_web_page_preview) = self.disable_web_page_preview { + f.text("disable_web_page_preview", format!("{:?}", disable_web_page_preview)); + f + } else { f }) + .apply(|f| if let Some(disable_notification) = self.disable_notification { + f.text("disable_notification", format!("{:?}", disable_notification)); + f + } else { f }) + .apply(|f| if let Some(reply_to_message_id) = self.reply_to_message_id { + f.text("reply_to_message_id", format!("{:?}", reply_to_message_id)); + f + } else { f }) + .apply(|f| if let Some(reply_markup) = self.reply_markup { + unimplemented!(); + //f.text("reply_markup", ); + f + } else { f }); + + Some(params) + } + fn token(&self) -> &str { &self.token } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn default() { + let sm = SendMessage::builder().token("TOKEN").chat_id(123456.into()).text("text"); + let r = SendMessage { + token: String::from("TOKEN"), + chat_id: ChatId::Id(123456), + text: String::from("text"), + parse_mode: None, + disable_web_page_preview: None, + disable_notification: None, + reply_to_message_id: None, + reply_markup: None + }; + + assert_eq!(sm, r); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0c41560d..188ef957 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,12 @@ #![feature(async_await)] +#[macro_use] +extern crate typed_builder; + +#[macro_use] +extern crate derive_more; + +#[macro_use] +extern crate serde; + mod core;