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}