1use crate::scheme::{deserialize_datetime_timestamp, serialize_datetime_timestamp};
2use crate::util::encoding::{encode, slugify2};
3use chrono::NaiveDateTime;
4use serde_derive::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6
7fn default_location() -> String {
8 "unknown".to_string()
9}
10
11pub enum MasterServer {
12 One,
13 Two,
14 Three,
15 Four,
16}
17
18impl MasterServer {
19 fn get_index(&self) -> i32 {
20 match &self {
21 Self::One => 1,
22 Self::Two => 2,
23 Self::Three => 3,
24 Self::Four => 4,
25 }
26 }
27}
28
29#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct Query {
32 pub points: i64,
33 pub name: String,
34}
35
36impl Query {
37 pub fn api(player: &str) -> String {
38 format!("https://ddnet.org/players/?query={}", encode(player))
39 }
40}
41
42#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct ReleasesMaps {
44 pub name: String,
45 pub website: String,
46 pub thumbnail: String,
47 pub web_preview: String,
48 pub r#type: String,
49 pub points: u8,
50 pub difficulty: u8,
51 pub mapper: String,
52 pub release: String,
53 pub width: u64,
54 pub height: u64,
55 pub tiles: Vec<String>,
56}
57
58impl ReleasesMaps {
59 pub fn api() -> String {
60 "https://ddnet.org/releases/maps.json".to_string()
61 }
62}
63
64#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
65pub struct StatusData {
66 pub name: String,
67 pub r#type: String,
68 pub host: String,
69 pub location: String,
70 pub online4: bool,
71 pub online6: bool,
72 pub uptime: String,
73 pub load: f32,
74 pub network_rx: u64,
75 pub network_tx: u64,
76 pub packets_rx: u64,
77 pub packets_tx: u64,
78 pub cpu: u32,
79 pub memory_total: u64,
80 pub memory_used: u64,
81 pub swap_total: u64,
82 pub swap_used: u64,
83 pub hdd_total: u64,
84 pub hdd_used: u64,
85}
86
87#[derive(Serialize, Deserialize, Debug)]
88pub struct Status {
89 pub servers: Vec<StatusData>,
90 pub updated: String,
91}
92
93impl Status {
94 pub fn api() -> String {
95 "https://ddnet.org/status/json/stats.json".to_string()
96 }
97}
98
99#[derive(Default, Debug, Clone)]
100pub struct ClansCount {
101 pub name: String,
102 pub count: usize,
103}
104
105#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct Master {
108 pub servers: Vec<Server>,
109}
110
111impl Master {
112 pub fn api(master: MasterServer) -> String {
113 format!(
114 "https://master{}.ddnet.org/ddnet/15/servers.json",
115 master.get_index()
116 )
117 }
118
119 pub fn count_clients(&self) -> usize {
120 self.servers.iter().map(Server::count_client).sum()
121 }
122
123 pub fn get_clans(&self) -> Vec<ClansCount> {
124 self.get_clans_with_custom_rm(None)
125 }
126
127 pub fn get_clans_with_custom_rm(&self, rm: Option<Vec<&str>>) -> Vec<ClansCount> {
128 let remove_list: HashSet<&str> = rm
129 .unwrap_or_else(|| vec!["DD-Persian", "/vDQMHSss8W"])
130 .into_iter()
131 .collect();
132
133 if self.servers.is_empty() {
134 return vec![];
135 }
136
137 let mut dat: HashMap<String, usize> = HashMap::new();
138
139 self.servers.iter().for_each(|server| {
140 server
141 .info
142 .clients
143 .iter()
144 .filter(|client| !client.clan.is_empty())
145 .for_each(|client| {
146 *dat.entry(client.clan.clone()).or_insert(0) += 1;
147 });
148 });
149
150 for clan in remove_list {
151 dat.remove(clan);
152 }
153
154 let mut sorted_dat: Vec<ClansCount> = dat
155 .into_iter()
156 .map(|(name, count)| ClansCount { name, count })
157 .collect();
158
159 sorted_dat.sort_by(|a, b| b.count.cmp(&a.count));
160 sorted_dat
161 }
162}
163
164#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
165pub struct Server {
166 pub addresses: Vec<String>,
167 #[serde(default = "default_location")]
168 pub location: String,
169 pub info: Info,
170}
171
172impl Server {
173 #[allow(dead_code)]
174 pub fn count_client(&self) -> usize {
175 self.info.clients.len()
176 }
177}
178
179#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
180pub struct Info {
181 pub max_clients: i64,
182 pub max_players: i64,
183 #[serde(default)]
184 pub passworded: bool,
185 #[serde(rename = "game_type")]
186 pub gametype: String,
187 pub name: String,
188 pub map: IMap,
189 pub version: String,
190 #[serde(default)]
191 pub clients: Vec<Client>,
192 #[serde(default)]
193 pub requires_login: bool,
194 pub community: Option<Community>,
195}
196
197#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
198pub struct IMap {
199 pub name: String,
200 pub sha256: Option<String>,
201 pub size: Option<i64>,
202}
203
204#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct Client {
206 pub name: String,
207 pub clan: String,
208 pub country: i32,
209 pub score: i64,
210 #[serde(default)]
211 pub is_player: bool,
212 pub skin: Option<Skin>,
213 #[serde(default)]
214 pub afk: bool,
215 #[serde(default)]
216 pub team: i64,
217}
218
219#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
220pub struct Skin {
221 pub name: Option<String>,
222 pub color_body: Option<i64>,
223 pub color_feet: Option<i64>,
224}
225
226#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
227pub struct Community {
228 pub id: String,
229 pub icon: String,
230 pub admin: Vec<String>,
231 pub public_key: Option<String>,
232 pub signature: Option<String>,
233}
234
235#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
236pub struct Player {
237 pub player: String,
238 pub points: Points,
239 pub team_rank: Option<Rank>,
240 pub rank: Option<Rank>,
241 pub points_last_year: Option<Rank>,
242 pub points_last_month: Option<Rank>,
243 pub points_last_week: Option<Rank>,
244 pub favorite_server: FavoriteServer,
245 pub first_finish: FirstFinish,
246 pub last_finishes: Vec<LastFinish>,
247 pub favorite_partners: Option<Vec<FavoritePartner>>,
248 pub types: Types,
249 pub activity: Vec<Activity>,
250 pub hours_played_past_365_days: i64,
251}
252
253impl Player {
254 pub fn url(&self) -> String {
255 format!(
256 "https://ddnet.org/players/{}",
257 encode(&slugify2(&self.player))
258 )
259 }
260
261 pub fn url_with_name(player: &str) -> String {
262 format!("https://ddnet.org/players/{}", encode(&slugify2(player)))
263 }
264
265 pub fn api(player: &str) -> String {
266 format!("https://ddnet.org/players/?json2={}", encode(player))
267 }
268}
269
270#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
271pub struct Map {
272 pub name: String,
273 pub website: String,
274 pub thumbnail: String,
275 pub web_preview: String,
276 pub r#type: String,
277 pub points: i64,
278 pub difficulty: i64,
279 pub mapper: String,
280 pub release: Option<f64>,
281 pub median_time: f64,
282 pub first_finish: f64,
283 pub last_finish: f64,
284 pub finishes: i64,
285 pub finishers: i64,
286 pub biggest_team: i64,
287 pub width: i64,
288 pub height: i64,
289 pub tiles: Vec<String>,
290 pub team_ranks: Vec<DTeamRank>,
291 pub ranks: Vec<DRank>,
292 pub max_finishes: Vec<MaxFinish>,
293}
294
295impl Map {
296 pub fn url(&self) -> String {
297 format!("https://ddnet.org/maps/{}", encode(&slugify2(&self.name)))
298 }
299
300 pub fn url_with_name(map: &str) -> String {
301 format!("https://ddnet.org/maps/{}", encode(&slugify2(map)))
302 }
303
304 pub fn api(map: &str) -> String {
305 format!("https://ddnet.org/maps/?json={}", encode(map))
306 }
307}
308
309#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
310pub struct DTeamRank {
311 pub rank: i64,
312 pub players: Vec<String>,
313 pub time: f64,
314 #[serde(
315 serialize_with = "serialize_datetime_timestamp",
316 deserialize_with = "deserialize_datetime_timestamp"
317 )]
318 pub timestamp: NaiveDateTime,
319 pub country: String,
320}
321
322#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
323pub struct DRank {
324 pub rank: i64,
325 pub player: String,
326 pub time: f64,
327 #[serde(
328 serialize_with = "serialize_datetime_timestamp",
329 deserialize_with = "deserialize_datetime_timestamp"
330 )]
331 pub timestamp: NaiveDateTime,
332 pub country: String,
333}
334
335#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
336pub struct MaxFinish {
337 pub rank: i64,
338 pub player: String,
339 pub num: i64,
340 pub time: f64,
341 #[serde(
342 serialize_with = "serialize_datetime_timestamp",
343 deserialize_with = "deserialize_datetime_timestamp"
344 )]
345 pub min_timestamp: NaiveDateTime,
346 #[serde(
347 serialize_with = "serialize_datetime_timestamp",
348 deserialize_with = "deserialize_datetime_timestamp"
349 )]
350 pub max_timestamp: NaiveDateTime,
351}
352
353#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
354pub struct Points {
355 pub total: u64,
356 pub points: Option<u64>,
357 pub rank: Option<u64>,
358}
359
360#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
361pub struct Rank {
362 pub points: Option<u64>,
363 pub rank: Option<u64>,
364}
365
366#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
367pub struct FavoriteServer {
368 pub server: String,
369}
370
371#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
372pub struct FirstFinish {
373 #[serde(
374 serialize_with = "serialize_datetime_timestamp",
375 deserialize_with = "deserialize_datetime_timestamp"
376 )]
377 pub timestamp: NaiveDateTime,
378 pub map: String,
379 pub time: f64,
380}
381
382#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
383pub struct LastFinish {
384 #[serde(
385 serialize_with = "serialize_datetime_timestamp",
386 deserialize_with = "deserialize_datetime_timestamp"
387 )]
388 pub timestamp: NaiveDateTime,
389 pub map: String,
390 pub time: f64,
391 pub country: String,
392 #[serde(rename = "type")]
393 pub type_map: Option<String>,
394}
395
396#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
397pub struct FavoritePartner {
398 pub name: String,
399 pub finishes: i64,
400}
401
402#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
403#[serde(rename_all = "PascalCase")]
404pub struct Types {
405 pub novice: Type,
406 pub moderate: Type,
407 pub brutal: Type,
408 pub insane: Type,
409 pub dummy: Type,
410 #[serde(rename = "DDmaX.Easy")]
411 pub ddma_x_easy: Type,
412 #[serde(rename = "DDmaX.Next")]
413 pub ddma_x_next: Type,
414 #[serde(rename = "DDmaX.Pro")]
415 pub ddma_x_pro: Type,
416 #[serde(rename = "DDmaX.Nut")]
417 pub ddma_x_nut: Type,
418 pub oldschool: Type,
419 pub solo: Type,
420 pub race: Type,
421 pub fun: Type,
422}
423
424#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
425pub struct Type {
426 pub points: Points,
427 pub team_rank: Option<Rank>,
428 pub rank: Option<Rank>,
429 pub maps: HashMap<String, DDMap>,
430}
431
432#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
433pub struct DDMap {
434 pub points: i64,
435 pub total_finishes: i64,
436 pub finishes: i64,
437 pub team_rank: Option<i64>,
438 pub rank: Option<i64>,
439 pub time: Option<f64>,
440 pub first_finish: Option<f64>,
441}
442
443#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
444pub struct Activity {
445 pub date: String,
446 pub hours_played: i64,
447}