1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#![feature(async_await)]
#[macro_use]
extern crate derive_builder;
#[macro_use]
extern crate serde_derive;
use std::{error, fmt};
use std::sync::Arc;
use futures::{Future, FutureExt, SinkExt, TryFutureExt};
use futures::channel::mpsc::Receiver;
use futures::compat::Future01CompatExt;
use reqwest::r#async::{Client, Response};
use serde::Deserialize;
use types::*;
pub mod types;
pub mod methods;
#[derive(Debug)]
pub enum BotError {
Http(reqwest::Error),
Json(serde_json::error::Error),
Api {
error_code: i64,
description: String,
parameters: Option<ResponseParameters>,
},
}
impl From<serde_json::error::Error> for BotError {
fn from(err: serde_json::error::Error) -> BotError {
BotError::Json(err)
}
}
impl From<reqwest::Error> for BotError {
fn from(err: reqwest::Error) -> BotError {
BotError::Http(err)
}
}
impl fmt::Display for BotError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BotError::Http(ref e) => write!(f, "{}", e),
BotError::Json(ref e) => write!(f, "{}", e),
BotError::Api {
error_code,
ref description,
..
} => write!(f, "Error {0}: {1}", error_code, description),
}
}
}
impl error::Error for BotError {
fn description(&self) -> &str {
"Something unexpected occured while talking to the telegram bot api."
}
}
pub trait TgMethod {
type ResponseType;
const PATH: &'static str;
}
pub trait Captures<'a> {}
impl<'a, T> Captures<'a> for T {}
#[derive(Debug, Clone)]
pub struct Bot {
token: String,
client: Arc<Client>,
}
impl Bot {
pub fn new(bot_token: impl Into<String>) -> Bot {
Bot {
token: bot_token.into(),
client: Arc::new(Client::new()),
}
}
pub fn send<'a: 'c, 'b: 'c, 'c, R: for<'de> Deserialize<'de>, M: TgMethod<ResponseType=R> + serde::Serialize>(
&'a self, m: &'b M,
) -> impl Future<Output=Result<R, BotError>> + Captures<'a> + Captures<'b> + 'c {
async move {
let url = format!("https://api.telegram.org/bot{}/{}", self.token, M::PATH);
let mut resp: Response = self.client.post(&url).json(m).send().compat().await.unwrap();
let res: ApiResult<R> = resp.json().compat().await?;
res.into()
}
}
pub fn start_polling(&self) -> Receiver<Update> {
let (mut tx, rx) = futures::channel::mpsc::channel(100);
let bot = self.clone();
tokio::spawn(async move {
let mut req = methods::GetUpdates {
offset: Some(0),
limit: None,
timeout: Some(5 * 60),
allowed_updates: None,
};
loop {
let updates = bot.send(&req).await;
for update in updates.unwrap() {
req.offset = Some(update.update_id + 1);
tx.send(update).await.unwrap();
}
}
}.boxed().unit_error().compat());
rx
}
}