twitter_archive/structs/
connected_application.rs

1#!/usr/bin/env rust
2
3//! Tweeter archives as of 2023-08-31 have public connected_application found under;
4//!
5//!   twitter-<DATE>-<UID>.zip:data/connected-application.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::connected_application;
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/connected-application.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.connected_application.part0 = ", "", 1);
26//!     let data: Vec<connected_application::ConnectedApplicationObject> = serde_json::from_str(&json).expect("Unable to parse");
27//!
28//!     for (index, object) in data.iter().enumerate() {
29//!         /* Do stuff with each deleted Tweet */
30//!         println!("Connected application index: {index}");
31//!         println!("Organization name: {}", object.connected_application.organization.name);
32//!         println!("Organization URL: {}", object.connected_application.organization.url);
33//!         println!("Organization privacy policy: {}", object.connected_application.organization.privacy_policy_url);
34//!         println!("Organization terms and conditions: {}", object.connected_application.organization.terms_and_conditions_url);
35//!         println!("Description: {}", object.connected_application.description);
36//!         println!("Permissions: {:?}", object.connected_application.permissions);
37//!         println!("Approved at: {}", object.connected_application.approved_at);
38//!         println!("ID: {}", object.connected_application.id);
39//!     }
40//! }
41//! ```
42//!
43//! ## Example `twitter-<DATE>-<UID>.zip:data/connected-application.js` content
44//!
45//! ```javascript
46//! window.YTD.connected_application.part0 = [
47//!   {
48//!     "connectedApplication" : {
49//!       "organization" : {
50//!         "name" : "Medium",
51//!         "url" : "https://medium.com",
52//!         "privacyPolicyUrl" : "https://medium.com/policy/medium-privacy-policy-f03bf92035c9",
53//!         "termsAndConditionsUrl" : "https://medium.com/policy/medium-terms-of-service-9db0094a1e0f"
54//!       },
55//!       "name" : "Medium",
56//!       "description" : "Evolving publishing",
57//!       "permissions" : [
58//!         "read",
59//!         "write",
60//!         "emailaddress"
61//!       ],
62//!       "approvedAt" : "2020-01-20T21:42:09.068Z",
63//!       "id" : "1111111"
64//!     }
65//!   }
66//! ]
67//! ```
68
69use chrono::{DateTime, Utc};
70use derive_more::Display;
71use serde::{Deserialize, Serialize};
72
73use crate::convert;
74
75/// ## Example
76///
77/// ```
78/// use chrono::{DateTime, NaiveDateTime, Utc};
79///
80/// use twitter_archive::convert::date_time_iso_8601::FORMAT;
81///
82/// use twitter_archive::structs::connected_application::ConnectedApplicationObject;
83///
84/// let approved_at_string = "2020-01-20T21:42:09.068Z";
85/// let approved_at_native_time = NaiveDateTime::parse_from_str(&approved_at_string, FORMAT).unwrap();
86/// let approved_at_date_time = DateTime::<Utc>::from_naive_utc_and_offset(approved_at_native_time, Utc);
87///
88/// let json = format!(r#"{{
89///   "connectedApplication" : {{
90///     "organization" : {{
91///       "name" : "Example",
92///       "url" : "https://example.com",
93///       "privacyPolicyUrl" : "https://example.com/policy/example-privacy-policy",
94///       "termsAndConditionsUrl" : "https://example.com/policy/medium-terms-of-service"
95///     }},
96///     "name" : "Example",
97///     "description" : "Example-description",
98///     "permissions" : [
99///       "read",
100///       "write",
101///       "emailaddress"
102///     ],
103///     "approvedAt" : "{approved_at_string}",
104///     "id" : "1111111"
105///   }}
106/// }}"#);
107///
108/// let data: ConnectedApplicationObject = serde_json::from_str(&json).unwrap();
109///
110/// // De-serialized properties
111/// assert_eq!(data.connected_application.organization.name, "Example");
112/// assert_eq!(data.connected_application.organization.url, "https://example.com");
113/// assert_eq!(data.connected_application.organization.privacy_policy_url, "https://example.com/policy/example-privacy-policy");
114/// assert_eq!(data.connected_application.organization.terms_and_conditions_url, "https://example.com/policy/medium-terms-of-service");
115///
116/// assert_eq!(data.connected_application.name, "Example");
117/// assert_eq!(data.connected_application.description, "Example-description");
118///
119/// assert_eq!(data.connected_application.permissions.len(), 3);
120/// assert_eq!(data.connected_application.permissions[0], "read");
121/// assert_eq!(data.connected_application.permissions[1], "write");
122/// assert_eq!(data.connected_application.permissions[2], "emailaddress");
123///
124/// assert_eq!(data.connected_application.approved_at, approved_at_date_time);
125/// assert_eq!(data.connected_application.id, "1111111");
126///
127/// // Re-serialize is equivalent to original data without pretty printing
128/// // assert_eq!(serde_json::to_string(&data).unwrap(), json.replace("\n", "").replace(" ", ""));
129/// ```
130#[derive(Deserialize, Serialize, Debug, Clone, Display)]
131#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
132#[serde(rename_all = "camelCase")]
133pub struct ConnectedApplicationObject {
134	/// ## Example JSON data
135	///
136	/// ```json
137	/// {
138	///   "connectedApplication" : {
139	///     "organization" : {
140	///       "name" : "Example",
141	///       "url" : "https://example.com",
142	///       "privacyPolicyUrl" : "https://example.com/policy/example-privacy-policy",
143	///       "termsAndConditionsUrl" : "https://example.com/policy/medium-terms-of-service"
144	///     },
145	///     "name" : "Example",
146	///     "description" : "Example-description",
147	///     "permissions" : [
148	///       "read",
149	///       "write",
150	///       "emailaddress"
151	///     ],
152	///     "approvedAt" : "2020-01-20T21:42:09.068Z",
153	///     "id" : "1111111"
154	///   }
155	/// }
156	/// ```
157	pub connected_application: ConnectedApplication,
158}
159
160/// ## Example
161///
162/// ```
163/// use chrono::{DateTime, NaiveDateTime, Utc};
164///
165/// use twitter_archive::convert::date_time_iso_8601::FORMAT;
166///
167/// use twitter_archive::structs::connected_application::ConnectedApplication;
168///
169/// let approved_at_string = "2020-01-20T21:42:09.068Z";
170/// let approved_at_native_time = NaiveDateTime::parse_from_str(&approved_at_string, FORMAT).unwrap();
171/// let approved_at_date_time = DateTime::<Utc>::from_naive_utc_and_offset(approved_at_native_time, Utc);
172///
173/// let json = format!(r#"{{
174///   "organization" : {{
175///     "name" : "Example",
176///     "url" : "https://example.com",
177///     "privacyPolicyUrl" : "https://example.com/policy/example-privacy-policy",
178///     "termsAndConditionsUrl" : "https://example.com/policy/medium-terms-of-service"
179///   }},
180///   "name" : "Example",
181///   "description" : "Example-description",
182///   "permissions" : [
183///     "read",
184///     "write",
185///     "emailaddress"
186///   ],
187///   "approvedAt" : "{approved_at_string}",
188///   "id" : "1111111"
189/// }}"#);
190///
191/// let data: ConnectedApplication = serde_json::from_str(&json).unwrap();
192///
193/// // De-serialized properties
194/// assert_eq!(data.organization.name, "Example");
195/// assert_eq!(data.organization.url, "https://example.com");
196/// assert_eq!(data.organization.privacy_policy_url, "https://example.com/policy/example-privacy-policy");
197/// assert_eq!(data.organization.terms_and_conditions_url, "https://example.com/policy/medium-terms-of-service");
198///
199/// assert_eq!(data.name, "Example");
200/// assert_eq!(data.description, "Example-description");
201///
202/// assert_eq!(data.permissions.len(), 3);
203/// assert_eq!(data.permissions[0], "read");
204/// assert_eq!(data.permissions[1], "write");
205/// assert_eq!(data.permissions[2], "emailaddress");
206///
207/// assert_eq!(data.approved_at, approved_at_date_time);
208/// assert_eq!(data.id, "1111111");
209///
210/// // Re-serialize is equivalent to original data without pretty printing
211/// // assert_eq!(serde_json::to_string(&data).unwrap(), json.replace("\n", "").replace(" ", ""));
212/// ```
213#[derive(Deserialize, Serialize, Debug, Clone, Display)]
214#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
215#[serde(rename_all = "camelCase")]
216pub struct ConnectedApplication {
217	/// ## Example JSON data
218	///
219	/// ```json
220	/// {
221	///   "organization" : {
222	///     "name" : "Example",
223	///     "url" : "https://example.com",
224	///     "privacyPolicyUrl" : "https://example.com/policy/example-privacy-policy",
225	///     "termsAndConditionsUrl" : "https://example.com/policy/medium-terms-of-service"
226	///   }
227	/// }
228	/// ```
229	pub organization: Organization,
230
231	/// Human readable name of application
232	///
233	/// ## Example JSON data
234	///
235	/// ```json
236	/// { "name" : "Example" }
237	/// ```
238	pub name: String,
239
240	/// Human readable description of application
241	///
242	/// ## Example JSON data
243	///
244	/// ```json
245	/// { "description" : "Example-description" }
246	/// ```
247	pub description: String,
248
249	/// List of permissions provided to application
250	///
251	/// ## Example JSON data
252	///
253	/// ```json
254	/// {
255	///   "permissions" : [
256	///     "read",
257	///     "write",
258	///     "emailaddress"
259	///   ]
260	/// }
261	/// ```
262	pub permissions: Vec<String>,
263
264	/// ## Example JSON data
265	///
266	/// ```json
267	/// { "approvedAt" : "2020-01-20T21:42:09.068Z" }
268	/// ```
269	#[serde(with = "convert::date_time_iso_8601")]
270	pub approved_at: DateTime<Utc>,
271
272	/// ID of application or maybe ID of account permitting application?
273	///
274	/// ## Example JSON data
275	///
276	/// ```json
277	/// { "id" : "1111111" }
278	/// ```
279	pub id: String,
280}
281
282/// ## Example
283///
284/// ```
285/// use twitter_archive::structs::connected_application::Organization;
286///
287/// let json = r#"{
288///   "name" : "Example",
289///   "url" : "https://example.com",
290///   "privacyPolicyUrl" : "https://example.com/policy/example-privacy-policy",
291///   "termsAndConditionsUrl" : "https://example.com/policy/medium-terms-of-service"
292/// }"#;
293///
294/// let data: Organization = serde_json::from_str(&json).unwrap();
295///
296/// // De-serialized properties
297/// assert_eq!(data.name, "Example");
298/// assert_eq!(data.url, "https://example.com");
299/// assert_eq!(data.privacy_policy_url, "https://example.com/policy/example-privacy-policy");
300/// assert_eq!(data.terms_and_conditions_url, "https://example.com/policy/medium-terms-of-service");
301///
302/// // Re-serialize is equivalent to original data without pretty printing
303/// // assert_eq!(serde_json::to_string(&data).unwrap(), json.replace("\n", "").replace(" ", ""));
304/// ```
305#[derive(Deserialize, Serialize, Debug, Clone, Display)]
306#[display(fmt = "{}", "serde_json::to_value(self).unwrap()")]
307#[serde(rename_all = "camelCase")]
308pub struct Organization {
309	/// Human readable name of application
310	///
311	/// ## Example JSON data
312	///
313	/// ```json
314	/// { "name" : "Example" }
315	/// ```
316	pub name: String,
317
318	/// Web address of application, usually the "home" page
319	///
320	/// ## Example JSON data
321	///
322	/// ```json
323	/// { "url" : "https://example.com" }
324	/// ```
325	pub url: String,
326
327	/// Web address of privacy policy for application
328	///
329	/// ## Example JSON data
330	///
331	/// ```json
332	/// { "privacyPolicyUrl" : "https://example.com/policy/example-privacy-policy" }
333	/// ```
334	pub privacy_policy_url: String,
335
336	/// Web address of terms and conditions policy for application
337	///
338	/// ## Example JSON data
339	///
340	/// ```json
341	/// { "termsAndConditionsUrl" : "https://example.com/policy/medium-terms-of-service" }
342	/// ```
343	pub terms_and_conditions_url: String,
344}