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