ddapi_rs/scheme/
ddnet.rs

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