twitter_archive/structs/lists_member.rs
1#!/usr/bin/env rust
2
3//! Tweeter archives as of 2023-08-31 have private data found under;
4//!
5//! twitter-<DATE>-<UID>.zip:data/lists-member.js
6//!
7//! ## Warnings
8//!
9//! - `.[].<KEY_NAME>.LocationHistory` data structure is subject to future changes
10//!
11//! ## Example file reader
12//!
13//! ```no_build
14//! use std::io::Read;
15//! use std::{fs, path};
16//! use zip::read::ZipArchive;
17//!
18//! use twitter_archive::structs::lists_member;
19//!
20//! fn main() {
21//! let input_file = "~/Downloads/twitter-archive.zip";
22//!
23//! let file_descriptor = fs::File::open(input_file).expect("Unable to read --input-file");
24//! let mut zip_archive = ZipArchive::new(file_descriptor).unwrap();
25//! let mut zip_file = zip_archive.by_name("data/lists-member.js").unwrap();
26//! let mut buff = String::new();
27//! zip_file.read_to_string(&mut buff).unwrap();
28//!
29//! let json = buff.replacen("window.YTD.lists_member.part0 = ", "", 1);
30//! let data: Vec<lists_member::UserListInfoObject> = serde_json::from_str(&json).expect("Unable to parse");
31//!
32//! for (index, object) in data.iter().enumerate() {
33//! /* Do stuff with each `niDeviceResponse` entry */
34//! println!("Lists member index: {index}");
35//! println!("URL: {}", object.user_list_info.url);
36//! }
37//! }
38//! ```
39//!
40//! ## Example `twitter-<DATE>-<UID>.zip:data/lists-member.js` content
41//!
42//! ```javascript
43//! window.YTD.lists_member.part0 = [
44//! {
45//! "userListInfo" : {
46//! "url" : "https://twitter.com/M16229Myers/lists/1696117177802211514"
47//! }
48//! },
49//! {
50//! "userListInfo" : {
51//! "url" : "https://twitter.com/R0oTk1t/lists/1572592337959944198"
52//! }
53//! }
54//! ]
55//! ```
56
57use derive_more::Display;
58use serde::{Deserialize, Serialize};
59
60/// ## Example
61///
62/// ```
63/// use twitter_archive::structs::lists_member::UserListInfoObject;
64///
65/// let json = r#"{
66/// "userListInfo": {
67/// "url": "https://twitter.com/R0oTk1t/lists/1572592337959944198"
68/// }
69/// }"#;
70///
71/// let data: UserListInfoObject = serde_json::from_str(&json).unwrap();
72///
73/// // De-serialized properties
74/// assert_eq!(data.user_list_info.url, "https://twitter.com/R0oTk1t/lists/1572592337959944198");
75///
76/// // Re-serialize is equivalent to original data
77/// assert_eq!(serde_json::to_string_pretty(&data).unwrap(), json);
78/// ```
79#[derive(Deserialize, Serialize, Debug, Clone, Display)]
80#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
81#[serde(rename_all = "camelCase")]
82pub struct UserListInfoObject {
83 /// ## Example JSON data
84 ///
85 /// ```json
86 /// {
87 /// "userListInfo": {
88 /// "url": "https://twitter.com/R0oTk1t/lists/1572592337959944198"
89 /// }
90 /// }
91 /// ```
92 pub user_list_info: UserListInfo,
93}
94
95/// ## Example
96///
97/// ```
98/// use twitter_archive::structs::lists_member::UserListInfo;
99///
100/// let json = r#"{
101/// "url": "https://twitter.com/R0oTk1t/lists/1572592337959944198"
102/// }"#;
103///
104/// let data: UserListInfo = serde_json::from_str(&json).unwrap();
105///
106/// // De-serialized properties
107/// assert_eq!(data.url, "https://twitter.com/R0oTk1t/lists/1572592337959944198");
108///
109/// // Re-serialize is equivalent to original data
110/// assert_eq!(serde_json::to_string_pretty(&data).unwrap(), json);
111/// ```
112#[derive(Deserialize, Serialize, Debug, Clone, Display)]
113#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
114pub struct UserListInfo {
115 /// ## Example JSON data
116 ///
117 /// ```json
118 /// { "url": "https://twitter.com/R0oTk1t/lists/1572592337959944198" }
119 /// ```
120 pub url: String,
121}