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)]
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}