twitter_archive/structs/
key_registry.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/key-registry.js
6//!
7//! ## Example file reader
8//!
9//! ```no_build
10//! use std::io::Read;
11//! use std::{fs, path};
12//! use zip::read::ZipArchive;
13//!
14//! use twitter_archive::structs::key_registry;
15//!
16//! fn main() {
17//!     let input_file = "~/Downloads/twitter-archive.zip";
18//!
19//!     let file_descriptor = fs::File::open(input_file).expect("Unable to read --input-file");
20//!     let mut zip_archive = ZipArchive::new(file_descriptor).unwrap();
21//!     let mut zip_file = zip_archive.by_name("data/key-registry.js").unwrap();
22//!     let mut buff = String::new();
23//!     zip_file.read_to_string(&mut buff).unwrap();
24//!
25//!     let json = buff.replacen("window.YTD.key_registry.part0 = ", "", 1);
26//!     let data: Vec<key_registry::RegisteredDevicesObject> = serde_json::from_str(&json).expect("Unable to parse");
27//!
28//!     for (index_registered_devices, object_registered_devices) in data.iter().enumerate() {
29//!         /* Do stuff with each `RegisteredDevices` entry */
30//!         println!("Registered devices index: {index_registered_devices}");
31//!         let device_metadata_list = &object_registered_devices.registered_devices.device_metadata_list;
32//!         for (index_device_metadata, object_device_metadata) in device_metadata_list.iter().enumerate() {
33//!             println!("Device Metadata index: {index_device_metadata}");
34//!             println!("User agent: {}", object_device_metadata.user_agent);
35//!             println!("Registration token: {}", object_device_metadata.registration_token);
36//!             println!("Identity key: {}", object_device_metadata.identity_key);
37//!             println!("Created at: {}", object_device_metadata.created_at);
38//!             println!("Device ID: {}", object_device_metadata.device_id);
39//!         }
40//!     }
41//! }
42//! ```
43//!
44//! ## Example `twitter-<DATE>-<UID>.zip:data/key-registry.js` content
45//!
46//! ```javascript
47//! window.YTD.key_registry.part0 = [
48//!   {
49//!     "registeredDevices" : {
50//!       "deviceMetadataList" : [
51//!         {
52//!           "userAgent" : "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0",
53//!           "registrationToken" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
54//!           "identityKey" : "DEADBEEF",
55//!           "createdAt" : "2023-05-30T13:31:42.908Z",
56//!           "deviceId" : "xxxxxxxx-111a-0000-abcd-333333333333"
57//!         }
58//!       ]
59//!     }
60//!   }
61//! ]
62//! ```
63
64use chrono::{DateTime, Utc};
65use derive_more::Display;
66use serde::{Deserialize, Serialize};
67
68use crate::convert;
69
70/// ## Example
71///
72/// ```
73/// use chrono::{DateTime, NaiveDateTime, Utc};
74///
75/// use twitter_archive::convert::date_time_iso_8601;
76///
77/// use twitter_archive::structs::key_registry::RegisteredDevicesObject;
78///
79/// let created_at_string = "2023-05-30T13:31:42.908Z";
80/// let created_at_native_time = NaiveDateTime::parse_from_str(&created_at_string, date_time_iso_8601::FORMAT).unwrap();
81/// let created_at_date_time = DateTime::<Utc>::from_naive_utc_and_offset(created_at_native_time, Utc);
82///
83/// let json = format!(r#"{{
84///   "registeredDevices": {{
85///     "deviceMetadataList": [
86///       {{
87///         "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0",
88///         "registrationToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
89///         "identityKey": "DEADBEEF",
90///         "createdAt": "{created_at_string}",
91///         "deviceId": "xxxxxxxx-111a-0000-abcd-333333333333"
92///       }}
93///     ]
94///   }}
95/// }}"#);
96///
97/// let data: RegisteredDevicesObject = serde_json::from_str(&json).unwrap();
98///
99/// // De-serialized properties
100/// assert_eq!(data.registered_devices.device_metadata_list[0].user_agent, "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0");
101/// assert_eq!(data.registered_devices.device_metadata_list[0].registration_token, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
102/// assert_eq!(data.registered_devices.device_metadata_list[0].identity_key, "DEADBEEF");
103/// assert_eq!(data.registered_devices.device_metadata_list[0].created_at, created_at_date_time);
104/// assert_eq!(data.registered_devices.device_metadata_list[0].device_id, "xxxxxxxx-111a-0000-abcd-333333333333");
105///
106/// // Re-serialize is equivalent to original data
107/// assert_eq!(serde_json::to_string_pretty(&data).unwrap(), json);
108/// ```
109#[derive(Deserialize, Serialize, Debug, Clone, Display)]
110#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
111#[serde(rename_all = "camelCase")]
112pub struct RegisteredDevicesObject {
113	/// ## Example JSON data
114	///
115	/// ```json
116	/// {
117	///   "registeredDevices": {
118	///     "deviceMetadataList": [
119	///       {
120	///         "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0",
121	///         "registrationToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
122	///         "identityKey": "DEADBEEF",
123	///         "createdAt": "2023-05-30T13:31:42.908Z",
124	///         "deviceId": "xxxxxxxx-111a-0000-abcd-333333333333"
125	///       }
126	///     ]
127	///   }
128	/// }
129	/// ```
130	pub registered_devices: RegisteredDevices,
131}
132
133/// ## Example
134///
135/// ```
136/// use chrono::{DateTime, NaiveDateTime, Utc};
137///
138/// use twitter_archive::convert::date_time_iso_8601;
139///
140/// use twitter_archive::structs::key_registry::RegisteredDevices;
141///
142/// let created_at_string = "2023-05-30T13:31:42.908Z";
143/// let created_at_native_time = NaiveDateTime::parse_from_str(&created_at_string, date_time_iso_8601::FORMAT).unwrap();
144/// let created_at_date_time = DateTime::<Utc>::from_naive_utc_and_offset(created_at_native_time, Utc);
145///
146/// let json = format!(r#"{{
147///   "deviceMetadataList": [
148///     {{
149///       "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0",
150///       "registrationToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
151///       "identityKey": "DEADBEEF",
152///       "createdAt": "{created_at_string}",
153///       "deviceId": "xxxxxxxx-111a-0000-abcd-333333333333"
154///     }}
155///   ]
156/// }}"#);
157///
158/// let data: RegisteredDevices = serde_json::from_str(&json).unwrap();
159///
160/// // De-serialized properties
161/// assert_eq!(data.device_metadata_list[0].user_agent, "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0");
162/// assert_eq!(data.device_metadata_list[0].registration_token, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
163/// assert_eq!(data.device_metadata_list[0].identity_key, "DEADBEEF");
164/// assert_eq!(data.device_metadata_list[0].created_at, created_at_date_time);
165/// assert_eq!(data.device_metadata_list[0].device_id, "xxxxxxxx-111a-0000-abcd-333333333333");
166///
167/// // Re-serialize is equivalent to original data
168/// assert_eq!(serde_json::to_string_pretty(&data).unwrap(), json);
169/// ```
170#[derive(Deserialize, Serialize, Debug, Clone, Display)]
171#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
172#[serde(rename_all = "camelCase")]
173pub struct RegisteredDevices {
174	/// ## Example JSON data
175	///
176	/// ```json
177	/// {
178	///   "deviceMetadataList": [
179	///     {
180	///       "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0",
181	///       "registrationToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
182	///       "identityKey": "DEADBEEF",
183	///       "createdAt": "2023-05-30T13:31:42.908Z",
184	///       "deviceId": "xxxxxxxx-111a-0000-abcd-333333333333"
185	///     }
186	///   ]
187	/// }
188	/// ```
189	pub device_metadata_list: Vec<DeviceMetadata>,
190}
191
192/// ## Example
193///
194/// ```
195/// use chrono::{DateTime, NaiveDateTime, Utc};
196///
197/// use twitter_archive::convert::date_time_iso_8601;
198///
199/// use twitter_archive::structs::key_registry::DeviceMetadata;
200///
201/// let created_at_string = "2023-05-30T13:31:42.908Z";
202/// let created_at_native_time = NaiveDateTime::parse_from_str(&created_at_string, date_time_iso_8601::FORMAT).unwrap();
203/// let created_at_date_time = DateTime::<Utc>::from_naive_utc_and_offset(created_at_native_time, Utc);
204///
205/// let json = format!(r#"{{
206///   "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0",
207///   "registrationToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
208///   "identityKey": "DEADBEEF",
209///   "createdAt": "{created_at_string}",
210///   "deviceId": "xxxxxxxx-111a-0000-abcd-333333333333"
211/// }}"#);
212///
213/// let data: DeviceMetadata = serde_json::from_str(&json).unwrap();
214///
215/// // De-serialized properties
216/// assert_eq!(data.user_agent, "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0");
217/// assert_eq!(data.registration_token, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
218/// assert_eq!(data.identity_key, "DEADBEEF");
219/// assert_eq!(data.created_at, created_at_date_time);
220/// assert_eq!(data.device_id, "xxxxxxxx-111a-0000-abcd-333333333333");
221///
222/// // Re-serialize is equivalent to original data
223/// assert_eq!(serde_json::to_string_pretty(&data).unwrap(), json);
224/// ```
225#[derive(Deserialize, Serialize, Debug, Clone, Display)]
226#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
227#[serde(rename_all = "camelCase")]
228pub struct DeviceMetadata {
229	/// ## Example JSON data
230	///
231	/// ```json
232	/// { "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0" }
233	/// ```
234	pub user_agent: String,
235
236	/// ## Example JSON data
237	///
238	/// ```json
239	/// { "registrationToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }
240	/// ```
241	pub registration_token: String,
242
243	/// ## Example JSON data
244	///
245	/// ```json
246	/// { "identityKey": "DEADBEEF" }
247	/// ```
248	pub identity_key: String,
249
250	/// ## Example JSON data
251	///
252	/// ```json
253	/// { "createdAt": "2023-05-30T13:31:42.908Z" }
254	/// ```
255	#[serde(with = "convert::date_time_iso_8601")]
256	pub created_at: DateTime<Utc>,
257
258	/// ## Example JSON data
259	///
260	/// ```json
261	/// { "deviceId": "xxxxxxxx-111a-0000-abcd-333333333333" }
262	/// ```
263	pub device_id: String,
264}