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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use crate::{models::MessageResponse, GatewayClient, REST_URL};
use anyhow::Result;
use reqwest::Client;
use std::{fmt::Display, time::Duration};
use todel::models::{ErrorResponse, InstanceInfo, Message};
use tokio::time;
/// Simple Http client
#[derive(Debug, Clone)]
pub struct HttpClient {
client: Client,
instance_info: Option<InstanceInfo>,
pub rest_url: String,
pub user_name: Option<String>,
}
impl Default for HttpClient {
fn default() -> Self {
HttpClient {
client: Client::new(),
instance_info: None,
rest_url: REST_URL.to_string(),
user_name: None,
}
}
}
impl HttpClient {
/// Create a new HttpClient
pub fn new() -> Self {
HttpClient::default()
}
/// Change the [`HttpClient::user_name`] of the HttpClient
///
/// # Example:
/// ```rust
/// use eludrs::HttpClient;
///
/// let client = HttpClient::new().name("Uwuki".to_string());
///
/// assert_eq!(client.user_name, Some("Uwuki".to_string()))
/// ```
pub fn name(mut self, name: String) -> Self {
self.user_name = Some(name);
self
}
/// Change the url of the HttpClient
///
/// # Example:
/// ```rust
/// use eludrs::HttpClient;
///
/// let client = HttpClient::new().rest_url("http://0.0.0.0:7159".to_string());
///
/// assert_eq!(client.rest_url, "http://0.0.0.0:7159".to_string())
/// ```
pub fn rest_url(mut self, url: String) -> Self {
self.rest_url = url;
self
}
/// Fetch the info payload of an instance
pub async fn fetch_instance_info(&self) -> Result<InstanceInfo> {
Ok(self.client.get(&self.rest_url).send().await?.json().await?)
}
/// Try to get the client's internal InstanceInfo or fetch it if it does not already exist
pub async fn get_instance_info(&mut self) -> Result<&InstanceInfo> {
if self.instance_info.is_some() {
Ok(self.instance_info.as_ref().unwrap())
} else {
self.instance_info = Some(self.fetch_instance_info().await?);
Ok(self.instance_info.as_ref().unwrap())
}
}
/// Send a message supplying both an author name and content
pub async fn send_message<T: Display, C: Display>(
&self,
author: T,
content: C,
) -> Result<Message> {
loop {
match self
.client
.post(format!("{}/messages", self.rest_url))
.json(&Message {
author: author.to_string(),
content: content.to_string(),
})
.send()
.await?
.json::<MessageResponse>()
.await
{
Ok(MessageResponse::Message(msg)) => {
break Ok(msg);
}
Ok(MessageResponse::Error(err)) => match err {
ErrorResponse::RateLimited { try_after, .. } => {
log::info!(
"Client got ratelimited at /messages, retrying in {}ms",
try_after
);
time::sleep(Duration::from_millis(try_after)).await;
}
ErrorResponse::Validation {
value_name, info, ..
} => {
Err(anyhow::anyhow!(
"Ran into a validation error with field {}: {}",
value_name,
info,
))?;
}
err => Err(anyhow::anyhow!("Could not send message: {:?}", err))?,
},
Err(err) => {
break Err(err)?;
}
}
}
}
/// Send a message using the client's [`HttpClient::user_name`]
///
/// # Panics
///
/// This function can panic if there is no name set by the [`HttpClient::name`] function
pub async fn send<T: Display>(&self, content: T) -> Result<Message> {
self.send_message(
&self
.user_name
.as_ref()
.expect("You have to specifiy a name to run this function"),
content,
)
.await
}
/// Create a [`GatewayClient`] using the connected instance's instance info
/// pandemonium url if any.
pub async fn create_gateway(&mut self) -> Result<GatewayClient> {
let info = self.get_instance_info().await?;
Ok(GatewayClient::new().gateway_url(info.pandemonium_url.clone()))
}
}