diff --git a/tests/mocked-data/_issue_28.2.json b/tests/mocked-data/_issue_28.2.json
new file mode 100644
index 0000000..89ac9b0
--- /dev/null
+++ b/tests/mocked-data/_issue_28.2.json
@@ -0,0 +1,906 @@
+{
+ "data": {
+ "threaded_conversation_with_injections_v2": {
+ "instructions": [
+ {
+ "type": "TimelineAddEntries",
+ "entries": [
+ {
+ "entryId": "tweet-1658421690001502208",
+ "sortIndex": "7564950346853273599",
+ "content": {
+ "entryType": "TimelineTimelineItem",
+ "__typename": "TimelineTimelineItem",
+ "itemContent": {
+ "itemType": "TimelineTweet",
+ "__typename": "TimelineTweet",
+ "tweet_results": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1658421690001502208",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjo0MzYxNDk1OTg=",
+ "rest_id": "436149598",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": false,
+ "can_media_tag": true,
+ "created_at": "Tue Dec 13 21:10:47 +0000 2011",
+ "default_profile": false,
+ "default_profile_image": false,
+ "description": "De Salamanca,",
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 18457,
+ "followers_count": 3177,
+ "friends_count": 3040,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 5,
+ "location": "españa",
+ "media_count": 1613,
+ "name": "Moreno",
+ "normal_followers_count": 3177,
+ "pinned_tweet_ids_str": [],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/436149598/1368225000",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1664391740881510400/TMMIzBow_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "morenoagosto",
+ "statuses_count": 24367,
+ "translator_type": "none",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1658421690001502208"
+ ],
+ "editable_until_msecs": "1684235317000",
+ "is_edit_eligible": true,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": false,
+ "views": {
+ "count": "67",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter for Android",
+ "quoted_status_result": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1658407192859492355",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjoyOTQ5MDY1NzIz",
+ "rest_id": "2949065723",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": true,
+ "can_media_tag": false,
+ "created_at": "Mon Dec 29 11:54:26 +0000 2014",
+ "default_profile": true,
+ "default_profile_image": false,
+ "description": "MI LUCHA ESTÁ EN TODAS PARTES. POR LOS DERECHOS DE LOS ANIMALES. POR UN MUNDO MEJOR.ACTIVISTA",
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 108032,
+ "followers_count": 15455,
+ "friends_count": 13034,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 34,
+ "location": "En donde me den amor ❤️😊🥰",
+ "media_count": 12987,
+ "name": "SisuMisu",
+ "normal_followers_count": 15455,
+ "pinned_tweet_ids_str": [
+ "1087440689368043520"
+ ],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2949065723/1513372062",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1528038627317538817/wvoi8QLf_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "SisuMisu3",
+ "statuses_count": 164349,
+ "translator_type": "none",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "professional": {
+ "rest_id": "1490288411835523072",
+ "professional_type": "Creator",
+ "category": [
+ {
+ "id": 934,
+ "name": "Social Media Influencer",
+ "icon_name": "IconBriefcaseStroke"
+ }
+ ]
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1658407192859492355"
+ ],
+ "editable_until_msecs": "1684231861000",
+ "is_edit_eligible": true,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": true,
+ "views": {
+ "count": "1110",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter for Android",
+ "legacy": {
+ "created_at": "Tue May 16 09:41:01 +0000 2023",
+ "conversation_id_str": "1658407192859492355",
+ "display_text_range": [
+ 0,
+ 121
+ ],
+ "entities": {
+ "media": [
+ {
+ "display_url": "pic.twitter.com/apswi6Wwiw",
+ "expanded_url": "https://twitter.com/SisuMisu3/status/1658407192859492355/photo/1",
+ "id_str": "1658407190594453505",
+ "indices": [
+ 122,
+ 145
+ ],
+ "media_url_https": "https://pbs.twimg.com/media/FwPYjaBWAAETpRI.jpg",
+ "type": "photo",
+ "url": "https://t.co/apswi6Wwiw",
+ "features": {
+ "large": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ },
+ "medium": {
+ "faces": [
+ {
+ "x": 255,
+ "y": 237,
+ "h": 329,
+ "w": 329
+ }
+ ]
+ },
+ "small": {
+ "faces": [
+ {
+ "x": 145,
+ "y": 134,
+ "h": 186,
+ "w": 186
+ }
+ ]
+ },
+ "orig": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ }
+ },
+ "sizes": {
+ "large": {
+ "h": 1280,
+ "w": 1024,
+ "resize": "fit"
+ },
+ "medium": {
+ "h": 1200,
+ "w": 960,
+ "resize": "fit"
+ },
+ "small": {
+ "h": 680,
+ "w": 544,
+ "resize": "fit"
+ },
+ "thumb": {
+ "h": 150,
+ "w": 150,
+ "resize": "crop"
+ }
+ },
+ "original_info": {
+ "height": 1280,
+ "width": 1024,
+ "focus_rects": [
+ {
+ "x": 0,
+ "y": 642,
+ "w": 1024,
+ "h": 573
+ },
+ {
+ "x": 0,
+ "y": 256,
+ "w": 1024,
+ "h": 1024
+ },
+ {
+ "x": 0,
+ "y": 113,
+ "w": 1024,
+ "h": 1167
+ },
+ {
+ "x": 32,
+ "y": 0,
+ "w": 640,
+ "h": 1280
+ },
+ {
+ "x": 0,
+ "y": 0,
+ "w": 1024,
+ "h": 1280
+ }
+ ]
+ }
+ }
+ ],
+ "user_mentions": [
+ {
+ "id_str": "1860300192",
+ "name": "Alejandra Jacinto",
+ "screen_name": "AleJacintoUrang",
+ "indices": [
+ 81,
+ 97
+ ]
+ }
+ ],
+ "urls": [],
+ "hashtags": [
+ {
+ "indices": [
+ 116,
+ 121
+ ],
+ "text": "Sisu"
+ }
+ ],
+ "symbols": []
+ },
+ "extended_entities": {
+ "media": [
+ {
+ "display_url": "pic.twitter.com/apswi6Wwiw",
+ "expanded_url": "https://twitter.com/SisuMisu3/status/1658407192859492355/photo/1",
+ "id_str": "1658407190594453505",
+ "indices": [
+ 122,
+ 145
+ ],
+ "media_key": "3_1658407190594453505",
+ "media_url_https": "https://pbs.twimg.com/media/FwPYjaBWAAETpRI.jpg",
+ "type": "photo",
+ "url": "https://t.co/apswi6Wwiw",
+ "ext_media_availability": {
+ "status": "Available"
+ },
+ "features": {
+ "large": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ },
+ "medium": {
+ "faces": [
+ {
+ "x": 255,
+ "y": 237,
+ "h": 329,
+ "w": 329
+ }
+ ]
+ },
+ "small": {
+ "faces": [
+ {
+ "x": 145,
+ "y": 134,
+ "h": 186,
+ "w": 186
+ }
+ ]
+ },
+ "orig": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ }
+ },
+ "sizes": {
+ "large": {
+ "h": 1280,
+ "w": 1024,
+ "resize": "fit"
+ },
+ "medium": {
+ "h": 1200,
+ "w": 960,
+ "resize": "fit"
+ },
+ "small": {
+ "h": 680,
+ "w": 544,
+ "resize": "fit"
+ },
+ "thumb": {
+ "h": 150,
+ "w": 150,
+ "resize": "crop"
+ }
+ },
+ "original_info": {
+ "height": 1280,
+ "width": 1024,
+ "focus_rects": [
+ {
+ "x": 0,
+ "y": 642,
+ "w": 1024,
+ "h": 573
+ },
+ {
+ "x": 0,
+ "y": 256,
+ "w": 1024,
+ "h": 1024
+ },
+ {
+ "x": 0,
+ "y": 113,
+ "w": 1024,
+ "h": 1167
+ },
+ {
+ "x": 32,
+ "y": 0,
+ "w": 640,
+ "h": 1280
+ },
+ {
+ "x": 0,
+ "y": 0,
+ "w": 1024,
+ "h": 1280
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "favorite_count": 60,
+ "favorited": false,
+ "full_text": "Esta noche el circo está asegurado 😂😂😂\nAyuso se presenta al único debate.\nVamos \n@AleJacintoUrang \nEstamos contigo.\n#Sisu https://t.co/apswi6Wwiw",
+ "is_quote_status": false,
+ "lang": "es",
+ "possibly_sensitive": false,
+ "possibly_sensitive_editable": true,
+ "quote_count": 2,
+ "reply_count": 5,
+ "retweet_count": 42,
+ "retweeted": false,
+ "user_id_str": "2949065723",
+ "id_str": "1658407192859492355"
+ }
+ }
+ },
+ "legacy": {
+ "created_at": "Tue May 16 10:38:37 +0000 2023",
+ "conversation_id_str": "1658421690001502208",
+ "display_text_range": [
+ 0,
+ 35
+ ],
+ "entities": {
+ "user_mentions": [],
+ "urls": [],
+ "hashtags": [],
+ "symbols": []
+ },
+ "favorite_count": 2,
+ "favorited": false,
+ "full_text": "Se sabe si le permiten pinganillo??",
+ "is_quote_status": true,
+ "lang": "es",
+ "quote_count": 1,
+ "quoted_status_id_str": "1658407192859492355",
+ "quoted_status_permalink": {
+ "url": "https://t.co/03JmJW6BYc",
+ "expanded": "https://twitter.com/SisuMisu3/status/1658407192859492355",
+ "display": "twitter.com/SisuMisu3/stat…"
+ },
+ "reply_count": 0,
+ "retweet_count": 0,
+ "retweeted": false,
+ "user_id_str": "436149598",
+ "id_str": "1658421690001502208"
+ },
+ "quick_promote_eligibility": {
+ "eligibility": "IneligibleNotProfessional"
+ }
+ }
+ },
+ "tweetDisplayType": "Tweet",
+ "hasModeratedReplies": false
+ }
+ }
+ },
+ {
+ "entryId": "tweetdetailrelatedtweets-1676889656225411072",
+ "sortIndex": "1",
+ "content": {
+ "entryType": "TimelineTimelineModule",
+ "__typename": "TimelineTimelineModule",
+ "items": [
+ {
+ "entryId": "tweetdetailrelatedtweets-1676889656225411072-tweet-1676889656225411072",
+ "item": {
+ "itemContent": {
+ "itemType": "TimelineTweet",
+ "__typename": "TimelineTweet",
+ "tweet_results": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1676889656225411072",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjo0MzYxNDk1OTg=",
+ "rest_id": "436149598",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": false,
+ "can_media_tag": true,
+ "created_at": "Tue Dec 13 21:10:47 +0000 2011",
+ "default_profile": false,
+ "default_profile_image": false,
+ "description": "De Salamanca,",
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 18457,
+ "followers_count": 3177,
+ "friends_count": 3040,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 5,
+ "location": "españa",
+ "media_count": 1613,
+ "name": "Moreno",
+ "normal_followers_count": 3177,
+ "pinned_tweet_ids_str": [],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/436149598/1368225000",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1664391740881510400/TMMIzBow_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "morenoagosto",
+ "statuses_count": 24367,
+ "translator_type": "none",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1676889656225411072"
+ ],
+ "editable_until_msecs": "1688640223000",
+ "is_edit_eligible": true,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": true,
+ "views": {
+ "count": "40",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter for Android",
+ "quoted_status_result": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1676697529461743622",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjozMjg0OTg3MTA=",
+ "rest_id": "328498710",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": false,
+ "can_media_tag": true,
+ "created_at": "Sun Jul 03 13:57:45 +0000 2011",
+ "default_profile": true,
+ "default_profile_image": false,
+ "description": "** Erik Urano ** // *** Flat Erik *** / Astronauta a tiempo parcial // Extraterrestre a tiempo completo// Neovalladolor // MNGMNT & BKNG luis@sonidomuchacho.com",
+ "entities": {
+ "description": {
+ "urls": []
+ },
+ "url": {
+ "urls": [
+ {
+ "display_url": "linktr.ee/erikurano",
+ "expanded_url": "https://linktr.ee/erikurano",
+ "url": "https://t.co/qrNiieuzZw",
+ "indices": [
+ 0,
+ 23
+ ]
+ }
+ ]
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 35402,
+ "followers_count": 15175,
+ "friends_count": 1602,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 59,
+ "location": "",
+ "media_count": 1344,
+ "name": "DON DATA®️",
+ "normal_followers_count": 15175,
+ "pinned_tweet_ids_str": [
+ "1674336677597159424"
+ ],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/328498710/1687971178",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1674098569953902592/5tisrAy__normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "Erik_Urano",
+ "statuses_count": 12901,
+ "translator_type": "none",
+ "url": "https://t.co/qrNiieuzZw",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "professional": {
+ "rest_id": "1554576748209229824",
+ "professional_type": "Creator",
+ "category": [
+ {
+ "id": 1006,
+ "name": "Musical Artist",
+ "icon_name": "IconBriefcaseStroke"
+ }
+ ]
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1676697529461743622"
+ ],
+ "editable_until_msecs": "1688594417000",
+ "is_edit_eligible": true,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": false,
+ "views": {
+ "count": "973266",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter for iPhone",
+ "legacy": {
+ "created_at": "Wed Jul 05 21:00:17 +0000 2023",
+ "conversation_id_str": "1676697529461743622",
+ "display_text_range": [
+ 0,
+ 71
+ ],
+ "entities": {
+ "media": [
+ {
+ "display_url": "pic.twitter.com/chX4MREd5H",
+ "expanded_url": "https://twitter.com/Erik_Urano/status/1676697529461743622/video/1",
+ "id_str": "1676697491226468353",
+ "indices": [
+ 72,
+ 95
+ ],
+ "media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/1676697491226468353/pu/img/7VGLjWMYpFsXWvq1.jpg",
+ "type": "photo",
+ "url": "https://t.co/chX4MREd5H",
+ "features": {},
+ "sizes": {
+ "large": {
+ "h": 716,
+ "w": 1280,
+ "resize": "fit"
+ },
+ "medium": {
+ "h": 671,
+ "w": 1200,
+ "resize": "fit"
+ },
+ "small": {
+ "h": 380,
+ "w": 680,
+ "resize": "fit"
+ },
+ "thumb": {
+ "h": 150,
+ "w": 150,
+ "resize": "crop"
+ }
+ },
+ "original_info": {
+ "height": 716,
+ "width": 1280
+ }
+ }
+ ],
+ "user_mentions": [],
+ "urls": [],
+ "hashtags": [],
+ "symbols": []
+ },
+ "extended_entities": {
+ "media": [
+ {
+ "display_url": "pic.twitter.com/chX4MREd5H",
+ "expanded_url": "https://twitter.com/Erik_Urano/status/1676697529461743622/video/1",
+ "id_str": "1676697491226468353",
+ "indices": [
+ 72,
+ 95
+ ],
+ "media_key": "7_1676697491226468353",
+ "media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/1676697491226468353/pu/img/7VGLjWMYpFsXWvq1.jpg",
+ "type": "video",
+ "url": "https://t.co/chX4MREd5H",
+ "additional_media_info": {
+ "monetizable": false
+ },
+ "mediaStats": {
+ "viewCount": 622775
+ },
+ "ext_media_availability": {
+ "status": "Available"
+ },
+ "features": {},
+ "sizes": {
+ "large": {
+ "h": 716,
+ "w": 1280,
+ "resize": "fit"
+ },
+ "medium": {
+ "h": 671,
+ "w": 1200,
+ "resize": "fit"
+ },
+ "small": {
+ "h": 380,
+ "w": 680,
+ "resize": "fit"
+ },
+ "thumb": {
+ "h": 150,
+ "w": 150,
+ "resize": "crop"
+ }
+ },
+ "original_info": {
+ "height": 716,
+ "width": 1280
+ },
+ "video_info": {
+ "aspect_ratio": [
+ 320,
+ 179
+ ],
+ "duration_millis": 8536,
+ "variants": [
+ {
+ "content_type": "application/x-mpegURL",
+ "url": "https://video.twimg.com/ext_tw_video/1676697491226468353/pu/pl/hmKfq_gU8h5Pj_bi.m3u8?tag=12&container=fmp4"
+ },
+ {
+ "bitrate": 256000,
+ "content_type": "video/mp4",
+ "url": "https://video.twimg.com/ext_tw_video/1676697491226468353/pu/vid/482x270/CxOsBliV2KQSDySg.mp4?tag=12"
+ },
+ {
+ "bitrate": 2176000,
+ "content_type": "video/mp4",
+ "url": "https://video.twimg.com/ext_tw_video/1676697491226468353/pu/vid/1280x716/ir6MOu-zeqHCOMDZ.mp4?tag=12"
+ },
+ {
+ "bitrate": 832000,
+ "content_type": "video/mp4",
+ "url": "https://video.twimg.com/ext_tw_video/1676697491226468353/pu/vid/642x360/vyOPi9cH5pjAcgSS.mp4?tag=12"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "favorite_count": 9694,
+ "favorited": false,
+ "full_text": "Frase a empezar a utilizar cada vez que me pregunten “¿Que tal estás?”: https://t.co/chX4MREd5H",
+ "is_quote_status": false,
+ "lang": "es",
+ "possibly_sensitive": false,
+ "possibly_sensitive_editable": true,
+ "quote_count": 401,
+ "reply_count": 123,
+ "retweet_count": 2289,
+ "retweeted": false,
+ "user_id_str": "328498710",
+ "id_str": "1676697529461743622"
+ }
+ }
+ },
+ "legacy": {
+ "created_at": "Thu Jul 06 09:43:43 +0000 2023",
+ "conversation_id_str": "1676889656225411072",
+ "display_text_range": [
+ 0,
+ 66
+ ],
+ "entities": {
+ "user_mentions": [],
+ "urls": [],
+ "hashtags": [],
+ "symbols": []
+ },
+ "favorite_count": 0,
+ "favorited": false,
+ "full_text": "No, solo quiere decir que aún no ha empezado a darle al \" morapio\"",
+ "is_quote_status": true,
+ "lang": "es",
+ "quote_count": 0,
+ "quoted_status_id_str": "1676697529461743622",
+ "quoted_status_permalink": {
+ "url": "https://t.co/SIZUcNQRrS",
+ "expanded": "https://twitter.com/Erik_Urano/status/1676697529461743622",
+ "display": "twitter.com/Erik_Urano/sta…"
+ },
+ "reply_count": 0,
+ "retweet_count": 0,
+ "retweeted": false,
+ "user_id_str": "436149598",
+ "id_str": "1676889656225411072"
+ },
+ "quick_promote_eligibility": {
+ "eligibility": "IneligibleNotProfessional"
+ }
+ }
+ },
+ "tweetDisplayType": "Tweet"
+ },
+ "clientEventInfo": {
+ "component": "related_tweet",
+ "details": {
+ "conversationDetails": {
+ "conversationSection": "RelatedTweet"
+ }
+ }
+ }
+ }
+ }
+ ],
+ "displayType": "Vertical",
+ "header": {
+ "displayType": "Classic",
+ "text": "Discover more",
+ "socialContext": {
+ "type": "TimelineGeneralContext",
+ "contextType": "TextOnly",
+ "text": "Sourced from across Twitter"
+ },
+ "sticky": true
+ },
+ "clientEventInfo": {
+ "component": "related_tweet",
+ "details": {
+ "conversationDetails": {
+ "conversationSection": "RelatedTweet"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "TimelineTerminateTimeline",
+ "direction": "Top"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/mocked-data/_issue_28.json b/tests/mocked-data/_issue_28.json
new file mode 100644
index 0000000..a14b540
--- /dev/null
+++ b/tests/mocked-data/_issue_28.json
@@ -0,0 +1,839 @@
+{
+ "data": {
+ "threaded_conversation_with_injections_v2": {
+ "instructions": [
+ {
+ "type": "TimelineAddEntries",
+ "entries": [
+ {
+ "entryId": "tweet-1658409412799737856",
+ "sortIndex": "7564962624055037951",
+ "content": {
+ "entryType": "TimelineTimelineItem",
+ "__typename": "TimelineTimelineItem",
+ "itemContent": {
+ "itemType": "TimelineTweet",
+ "__typename": "TimelineTweet",
+ "tweet_results": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1658409412799737856",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjoxMjk2ODAxOTcwNzc1MzY3Njgw",
+ "rest_id": "1296801970775367680",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": true,
+ "can_media_tag": true,
+ "created_at": "Fri Aug 21 13:31:26 +0000 2020",
+ "default_profile": true,
+ "default_profile_image": false,
+ "description": "Por una unidad de izquierdas real .Bienvenidos a la #RedRoja. Sigue a quien te siga . Red de seguridad: @RedRoj4 @REDROJA@mastodon.world",
+ "entities": {
+ "description": {
+ "urls": []
+ },
+ "url": {
+ "urls": [
+ {
+ "display_url": "instagram.com/redroja4?igshi…",
+ "expanded_url": "https://instagram.com/redroja4?igshid=1d8cfo0z5wf2t",
+ "url": "https://t.co/yLqbCaFwck",
+ "indices": [
+ 0,
+ 23
+ ]
+ }
+ ]
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 60839,
+ "followers_count": 43398,
+ "friends_count": 31537,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 100,
+ "location": "Izquierda ",
+ "media_count": 1469,
+ "name": "RED ROJA 🔻",
+ "normal_followers_count": 43398,
+ "pinned_tweet_ids_str": [
+ "1592141363050860546"
+ ],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1296801970775367680/1618465956",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1541282511816376322/4UQWhvwj_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "REDROJA4",
+ "statuses_count": 30422,
+ "translator_type": "none",
+ "url": "https://t.co/yLqbCaFwck",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1658409412799737856"
+ ],
+ "editable_until_msecs": "1684232390425",
+ "is_edit_eligible": false,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": true,
+ "views": {
+ "state": "Enabled"
+ },
+ "source": "Twitter for iPhone",
+ "legacy": {
+ "created_at": "Tue May 16 09:49:50 +0000 2023",
+ "conversation_id_str": "1658409412799737856",
+ "display_text_range": [
+ 0,
+ 140
+ ],
+ "entities": {
+ "user_mentions": [
+ {
+ "id_str": "2949065723",
+ "name": "SisuMisu",
+ "screen_name": "SisuMisu3",
+ "indices": [
+ 3,
+ 13
+ ]
+ },
+ {
+ "id_str": "1860300192",
+ "name": "Alejandra Jacinto",
+ "screen_name": "AleJacintoUrang",
+ "indices": [
+ 96,
+ 112
+ ]
+ }
+ ],
+ "urls": [],
+ "hashtags": [
+ {
+ "indices": [
+ 131,
+ 136
+ ],
+ "text": "Sisu"
+ }
+ ],
+ "symbols": []
+ },
+ "favorite_count": 0,
+ "favorited": false,
+ "full_text": "RT @SisuMisu3: Esta noche el circo está asegurado 😂😂😂\nAyuso se presenta al único debate.\nVamos \n@AleJacintoUrang \nEstamos contigo.\n#Sisu ht…",
+ "is_quote_status": false,
+ "lang": "es",
+ "quote_count": 0,
+ "reply_count": 0,
+ "retweet_count": 42,
+ "retweeted": false,
+ "user_id_str": "1296801970775367680",
+ "id_str": "1658409412799737856",
+ "retweeted_status_result": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1658407192859492355",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjoyOTQ5MDY1NzIz",
+ "rest_id": "2949065723",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": true,
+ "can_media_tag": false,
+ "created_at": "Mon Dec 29 11:54:26 +0000 2014",
+ "default_profile": true,
+ "default_profile_image": false,
+ "description": "MI LUCHA ESTÁ EN TODAS PARTES. POR LOS DERECHOS DE LOS ANIMALES. POR UN MUNDO MEJOR.ACTIVISTA",
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 108021,
+ "followers_count": 15454,
+ "friends_count": 13032,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 34,
+ "location": "En donde me den amor ❤️😊🥰",
+ "media_count": 12985,
+ "name": "SisuMisu",
+ "normal_followers_count": 15454,
+ "pinned_tweet_ids_str": [
+ "1087440689368043520"
+ ],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2949065723/1513372062",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1528038627317538817/wvoi8QLf_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "SisuMisu3",
+ "statuses_count": 164333,
+ "translator_type": "none",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "professional": {
+ "rest_id": "1490288411835523072",
+ "professional_type": "Creator",
+ "category": [
+ {
+ "id": 934,
+ "name": "Social Media Influencer",
+ "icon_name": "IconBriefcaseStroke"
+ }
+ ]
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1658407192859492355"
+ ],
+ "editable_until_msecs": "1684231861000",
+ "is_edit_eligible": true,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": true,
+ "views": {
+ "count": "1110",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter for Android",
+ "legacy": {
+ "created_at": "Tue May 16 09:41:01 +0000 2023",
+ "conversation_id_str": "1658407192859492355",
+ "display_text_range": [
+ 0,
+ 121
+ ],
+ "entities": {
+ "media": [
+ {
+ "display_url": "pic.twitter.com/apswi6Wwiw",
+ "expanded_url": "https://twitter.com/SisuMisu3/status/1658407192859492355/photo/1",
+ "id_str": "1658407190594453505",
+ "indices": [
+ 122,
+ 145
+ ],
+ "media_url_https": "https://pbs.twimg.com/media/FwPYjaBWAAETpRI.jpg",
+ "type": "photo",
+ "url": "https://t.co/apswi6Wwiw",
+ "features": {
+ "large": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ },
+ "medium": {
+ "faces": [
+ {
+ "x": 255,
+ "y": 237,
+ "h": 329,
+ "w": 329
+ }
+ ]
+ },
+ "small": {
+ "faces": [
+ {
+ "x": 145,
+ "y": 134,
+ "h": 186,
+ "w": 186
+ }
+ ]
+ },
+ "orig": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ }
+ },
+ "sizes": {
+ "large": {
+ "h": 1280,
+ "w": 1024,
+ "resize": "fit"
+ },
+ "medium": {
+ "h": 1200,
+ "w": 960,
+ "resize": "fit"
+ },
+ "small": {
+ "h": 680,
+ "w": 544,
+ "resize": "fit"
+ },
+ "thumb": {
+ "h": 150,
+ "w": 150,
+ "resize": "crop"
+ }
+ },
+ "original_info": {
+ "height": 1280,
+ "width": 1024,
+ "focus_rects": [
+ {
+ "x": 0,
+ "y": 642,
+ "w": 1024,
+ "h": 573
+ },
+ {
+ "x": 0,
+ "y": 256,
+ "w": 1024,
+ "h": 1024
+ },
+ {
+ "x": 0,
+ "y": 113,
+ "w": 1024,
+ "h": 1167
+ },
+ {
+ "x": 32,
+ "y": 0,
+ "w": 640,
+ "h": 1280
+ },
+ {
+ "x": 0,
+ "y": 0,
+ "w": 1024,
+ "h": 1280
+ }
+ ]
+ }
+ }
+ ],
+ "user_mentions": [
+ {
+ "id_str": "1860300192",
+ "name": "Alejandra Jacinto",
+ "screen_name": "AleJacintoUrang",
+ "indices": [
+ 81,
+ 97
+ ]
+ }
+ ],
+ "urls": [],
+ "hashtags": [
+ {
+ "indices": [
+ 116,
+ 121
+ ],
+ "text": "Sisu"
+ }
+ ],
+ "symbols": []
+ },
+ "extended_entities": {
+ "media": [
+ {
+ "display_url": "pic.twitter.com/apswi6Wwiw",
+ "expanded_url": "https://twitter.com/SisuMisu3/status/1658407192859492355/photo/1",
+ "id_str": "1658407190594453505",
+ "indices": [
+ 122,
+ 145
+ ],
+ "media_key": "3_1658407190594453505",
+ "media_url_https": "https://pbs.twimg.com/media/FwPYjaBWAAETpRI.jpg",
+ "type": "photo",
+ "url": "https://t.co/apswi6Wwiw",
+ "ext_media_availability": {
+ "status": "Available"
+ },
+ "features": {
+ "large": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ },
+ "medium": {
+ "faces": [
+ {
+ "x": 255,
+ "y": 237,
+ "h": 329,
+ "w": 329
+ }
+ ]
+ },
+ "small": {
+ "faces": [
+ {
+ "x": 145,
+ "y": 134,
+ "h": 186,
+ "w": 186
+ }
+ ]
+ },
+ "orig": {
+ "faces": [
+ {
+ "x": 273,
+ "y": 253,
+ "h": 351,
+ "w": 351
+ }
+ ]
+ }
+ },
+ "sizes": {
+ "large": {
+ "h": 1280,
+ "w": 1024,
+ "resize": "fit"
+ },
+ "medium": {
+ "h": 1200,
+ "w": 960,
+ "resize": "fit"
+ },
+ "small": {
+ "h": 680,
+ "w": 544,
+ "resize": "fit"
+ },
+ "thumb": {
+ "h": 150,
+ "w": 150,
+ "resize": "crop"
+ }
+ },
+ "original_info": {
+ "height": 1280,
+ "width": 1024,
+ "focus_rects": [
+ {
+ "x": 0,
+ "y": 642,
+ "w": 1024,
+ "h": 573
+ },
+ {
+ "x": 0,
+ "y": 256,
+ "w": 1024,
+ "h": 1024
+ },
+ {
+ "x": 0,
+ "y": 113,
+ "w": 1024,
+ "h": 1167
+ },
+ {
+ "x": 32,
+ "y": 0,
+ "w": 640,
+ "h": 1280
+ },
+ {
+ "x": 0,
+ "y": 0,
+ "w": 1024,
+ "h": 1280
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "favorite_count": 60,
+ "favorited": false,
+ "full_text": "Esta noche el circo está asegurado 😂😂😂\nAyuso se presenta al único debate.\nVamos \n@AleJacintoUrang \nEstamos contigo.\n#Sisu https://t.co/apswi6Wwiw",
+ "is_quote_status": false,
+ "lang": "es",
+ "possibly_sensitive": false,
+ "possibly_sensitive_editable": true,
+ "quote_count": 2,
+ "reply_count": 5,
+ "retweet_count": 42,
+ "retweeted": false,
+ "user_id_str": "2949065723",
+ "id_str": "1658407192859492355"
+ }
+ }
+ }
+ },
+ "quick_promote_eligibility": {
+ "eligibility": "IneligibleNotProfessional"
+ }
+ }
+ },
+ "tweetDisplayType": "Tweet",
+ "hasModeratedReplies": false
+ }
+ }
+ },
+ {
+ "entryId": "tweetdetailrelatedtweets-1677037919880507392",
+ "sortIndex": "1",
+ "content": {
+ "entryType": "TimelineTimelineModule",
+ "__typename": "TimelineTimelineModule",
+ "items": [
+ {
+ "entryId": "tweetdetailrelatedtweets-1677037919880507392-tweet-1677037919880507392",
+ "item": {
+ "itemContent": {
+ "itemType": "TimelineTweet",
+ "__typename": "TimelineTweet",
+ "tweet_results": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1677037919880507392",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjoyOTQ5MDY1NzIz",
+ "rest_id": "2949065723",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": true,
+ "can_media_tag": false,
+ "created_at": "Mon Dec 29 11:54:26 +0000 2014",
+ "default_profile": true,
+ "default_profile_image": false,
+ "description": "MI LUCHA ESTÁ EN TODAS PARTES. POR LOS DERECHOS DE LOS ANIMALES. POR UN MUNDO MEJOR.ACTIVISTA",
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 108021,
+ "followers_count": 15454,
+ "friends_count": 13032,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 34,
+ "location": "En donde me den amor ❤️😊🥰",
+ "media_count": 12985,
+ "name": "SisuMisu",
+ "normal_followers_count": 15454,
+ "pinned_tweet_ids_str": [
+ "1087440689368043520"
+ ],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2949065723/1513372062",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1528038627317538817/wvoi8QLf_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "SisuMisu3",
+ "statuses_count": 164333,
+ "translator_type": "none",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "professional": {
+ "rest_id": "1490288411835523072",
+ "professional_type": "Creator",
+ "category": [
+ {
+ "id": 934,
+ "name": "Social Media Influencer",
+ "icon_name": "IconBriefcaseStroke"
+ }
+ ]
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1677037919880507392"
+ ],
+ "editable_until_msecs": "1688675572000",
+ "is_edit_eligible": true,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": true,
+ "views": {
+ "count": "75",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter for Android",
+ "quoted_status_result": {
+ "result": {
+ "__typename": "Tweet",
+ "rest_id": "1677035434214412289",
+ "has_birdwatch_notes": false,
+ "core": {
+ "user_results": {
+ "result": {
+ "__typename": "User",
+ "id": "VXNlcjoxMzA1NDYwNDg5",
+ "rest_id": "1305460489",
+ "affiliates_highlighted_label": {},
+ "has_graduated_access": true,
+ "is_blue_verified": false,
+ "legacy": {
+ "can_dm": false,
+ "can_media_tag": false,
+ "created_at": "Tue Mar 26 19:56:20 +0000 2013",
+ "default_profile": false,
+ "default_profile_image": false,
+ "description": "Apartidario\n\"No puedo decir que no estoy en desacuerdo contigo\"\nMi humilde opinión . . .",
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "fast_followers_count": 0,
+ "favourites_count": 24976,
+ "followers_count": 3394,
+ "friends_count": 3386,
+ "has_custom_timelines": true,
+ "is_translator": false,
+ "listed_count": 35,
+ "location": "Madrid, Comunidad de Madrid",
+ "media_count": 5191,
+ "name": "P.S.V.",
+ "normal_followers_count": 3394,
+ "pinned_tweet_ids_str": [
+ "1075291506767003648"
+ ],
+ "possibly_sensitive": false,
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1305460489/1437468007",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/869271246885793794/6YZVDC_r_normal.jpg",
+ "profile_interstitial_type": "",
+ "screen_name": "PShermanV",
+ "statuses_count": 175081,
+ "translator_type": "none",
+ "verified": false,
+ "want_retweets": false,
+ "withheld_in_countries": []
+ },
+ "professional": {
+ "rest_id": "1580505200997138433",
+ "professional_type": "Business",
+ "category": [
+ {
+ "id": 15,
+ "name": "Entertainment & Recreation",
+ "icon_name": "IconBriefcaseStroke"
+ }
+ ]
+ },
+ "business_account": {}
+ }
+ }
+ },
+ "edit_control": {
+ "edit_tweet_ids": [
+ "1677035434214412289"
+ ],
+ "editable_until_msecs": "1688674979000",
+ "is_edit_eligible": false,
+ "edits_remaining": "5"
+ },
+ "edit_perspective": {
+ "favorited": false,
+ "retweeted": false
+ },
+ "is_translatable": false,
+ "views": {
+ "count": "83",
+ "state": "EnabledWithCount"
+ },
+ "source": "Twitter Web App",
+ "legacy": {
+ "created_at": "Thu Jul 06 19:22:59 +0000 2023",
+ "conversation_id_str": "1676995076461821958",
+ "display_text_range": [
+ 28,
+ 99
+ ],
+ "entities": {
+ "user_mentions": [
+ {
+ "id_str": "2949065723",
+ "name": "SisuMisu",
+ "screen_name": "SisuMisu3",
+ "indices": [
+ 0,
+ 10
+ ]
+ },
+ {
+ "id_str": "888853157912227840",
+ "name": "Rosa Isabel",
+ "screen_name": "RosaIsa38309247",
+ "indices": [
+ 11,
+ 27
+ ]
+ }
+ ],
+ "urls": [],
+ "hashtags": [],
+ "symbols": []
+ },
+ "favorite_count": 1,
+ "favorited": false,
+ "full_text": "@SisuMisu3 @RosaIsa38309247 O ESPABILAMOS O A LIMPIAR LAS ESCOPETAS\nANTE AQUELLO DE LOS 26 MILLONES",
+ "in_reply_to_screen_name": "SisuMisu3",
+ "in_reply_to_status_id_str": "1676995076461821958",
+ "in_reply_to_user_id_str": "2949065723",
+ "is_quote_status": false,
+ "lang": "es",
+ "quote_count": 1,
+ "reply_count": 0,
+ "retweet_count": 0,
+ "retweeted": false,
+ "user_id_str": "1305460489",
+ "id_str": "1677035434214412289"
+ }
+ }
+ },
+ "legacy": {
+ "created_at": "Thu Jul 06 19:32:52 +0000 2023",
+ "conversation_id_str": "1677037919880507392",
+ "display_text_range": [
+ 0,
+ 90
+ ],
+ "entities": {
+ "user_mentions": [],
+ "urls": [],
+ "hashtags": [],
+ "symbols": []
+ },
+ "favorite_count": 2,
+ "favorited": false,
+ "full_text": "Tengo fé en que todo saldrá bien.\nNi como esperamos pero nos librará de este sin sentido .",
+ "is_quote_status": true,
+ "lang": "es",
+ "quote_count": 0,
+ "quoted_status_id_str": "1677035434214412289",
+ "quoted_status_permalink": {
+ "url": "https://t.co/X6fr18ZfNN",
+ "expanded": "https://twitter.com/PShermanV/status/1677035434214412289",
+ "display": "twitter.com/PShermanV/stat…"
+ },
+ "reply_count": 0,
+ "retweet_count": 0,
+ "retweeted": false,
+ "user_id_str": "2949065723",
+ "id_str": "1677037919880507392"
+ },
+ "quick_promote_eligibility": {
+ "eligibility": "IneligibleNotProfessional"
+ }
+ }
+ },
+ "tweetDisplayType": "Tweet"
+ },
+ "clientEventInfo": {
+ "component": "related_tweet",
+ "details": {
+ "conversationDetails": {
+ "conversationSection": "RelatedTweet"
+ }
+ }
+ }
+ }
+ }
+ ],
+ "displayType": "Vertical",
+ "header": {
+ "displayType": "Classic",
+ "text": "Discover more",
+ "socialContext": {
+ "type": "TimelineGeneralContext",
+ "contextType": "TextOnly",
+ "text": "Sourced from across Twitter"
+ },
+ "sticky": true
+ },
+ "clientEventInfo": {
+ "component": "related_tweet",
+ "details": {
+ "conversationDetails": {
+ "conversationSection": "RelatedTweet"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "TimelineTerminateTimeline",
+ "direction": "Top"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 8a8f5af..05e4119 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -53,7 +53,8 @@ def mock_gen(obj, fn: str):
setattr(obj, fn, cb_gen)
-def check_tweet(doc: Tweet):
+def check_tweet(doc: Tweet | None):
+ assert doc is not None
assert doc.id is not None
assert doc.id_str is not None
assert isinstance(doc.id, int)
@@ -122,7 +123,7 @@ def check_user(doc: User):
async def test_search():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "search_raw")
items = await gather(api.search("elon musk lang:en", limit=20))
@@ -133,7 +134,7 @@ async def test_search():
async def test_user_by_id():
- api = API(AccountsPool())
+ api = API()
mock_rep(api, "user_by_id_raw")
doc = await api.user_by_id(2244994945)
@@ -150,7 +151,7 @@ async def test_user_by_id():
async def test_user_by_login():
- api = API(AccountsPool())
+ api = API()
mock_rep(api, "user_by_login_raw")
doc = await api.user_by_login("twitterdev")
@@ -167,17 +168,19 @@ async def test_user_by_login():
async def test_tweet_details():
- api = API(AccountsPool())
+ api = API()
mock_rep(api, "tweet_details_raw")
doc = await api.tweet_details(1649191520250245121)
+ assert doc is not None
check_tweet(doc)
+
assert doc.id == 1649191520250245121
assert doc.user is not None
async def test_followers():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "followers_raw")
users = await gather(api.followers(2244994945))
@@ -188,7 +191,7 @@ async def test_followers():
async def test_following():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "following_raw")
users = await gather(api.following(2244994945))
@@ -199,7 +202,7 @@ async def test_following():
async def test_retweters():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "retweeters_raw")
users = await gather(api.retweeters(1649191520250245121))
@@ -210,7 +213,7 @@ async def test_retweters():
async def test_favoriters():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "favoriters_raw")
users = await gather(api.favoriters(1649191520250245121))
@@ -221,7 +224,7 @@ async def test_favoriters():
async def test_user_tweets():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "user_tweets_raw")
tweets = await gather(api.user_tweets(2244994945))
@@ -232,7 +235,7 @@ async def test_user_tweets():
async def test_user_tweets_and_replies():
- api = API(AccountsPool())
+ api = API()
mock_gen(api, "user_tweets_and_replies_raw")
tweets = await gather(api.user_tweets_and_replies(2244994945))
@@ -243,7 +246,7 @@ async def test_user_tweets_and_replies():
async def test_tweet_with_video():
- api = API(AccountsPool())
+ api = API()
files = [
("manual_tweet_with_video_1.json", 1671508600538161153),
@@ -253,9 +256,40 @@ async def test_tweet_with_video():
for file, twid in files:
mock_rep(api, "tweet_details_raw", os.path.join(DATA_DIR, file))
doc = await api.tweet_details(twid)
+ assert doc is not None
check_tweet(doc)
+async def test_issue_28():
+ api = API()
+ mock_rep(api, "issue_28")
+
+ doc = await api.tweet_details(1658409412799737856)
+ assert doc is not None
+ check_tweet(doc)
+
+ assert doc.id == 1658409412799737856
+ assert doc.user is not None
+
+ assert doc.retweetedTweet is not None
+ assert doc.retweetedTweet.viewCount is not None
+ assert doc.viewCount is not None # views should come from retweetedTweet
+ assert doc.viewCount == doc.retweetedTweet.viewCount
+ check_tweet(doc.retweetedTweet)
+
+ mock_rep(api, "issue_28.2")
+ doc = await api.tweet_details(1658421690001502208)
+ assert doc is not None
+ check_tweet(doc)
+ assert doc.id == 1658421690001502208
+ assert doc.viewCount is not None
+
+ assert doc.quotedTweet is not None
+ assert doc.quotedTweet.id != doc.id
+ check_tweet(doc.quotedTweet)
+ assert doc.quotedTweet.viewCount is not None
+
+
async def main():
# prepare mock files from real twitter replies
# you need to have some account to perform this
diff --git a/twscrape/api.py b/twscrape/api.py
index d237021..7989495 100644
--- a/twscrape/api.py
+++ b/twscrape/api.py
@@ -18,7 +18,7 @@ SEARCH_FEATURES = {
class API:
- def __init__(self, pool: AccountsPool | None, debug=False):
+ def __init__(self, pool: AccountsPool | None = None, debug=False):
self.pool = pool if pool is not None else AccountsPool()
self.debug = debug
diff --git a/twscrape/models.py b/twscrape/models.py
index 3122b75..4b6183a 100644
--- a/twscrape/models.py
+++ b/twscrape/models.py
@@ -178,8 +178,19 @@ class Tweet(JSONTrait):
@staticmethod
def parse(obj: dict, res: dict):
tw_usr = User.parse(res["users"][obj["user_id_str"]])
- rt_obj = get_or(res, f"tweets.{obj.get('retweeted_status_id_str')}")
- qt_obj = get_or(res, f"tweets.{obj.get('quoted_status_id_str')}")
+
+ rt_id = _first(obj, ["retweeted_status_id_str", "retweeted_status_result.result.rest_id"])
+ rt_obj = get_or(res, f"tweets.{rt_id}")
+
+ qt_id = _first(obj, ["quoted_status_id_str", "quoted_status_result.result.rest_id"])
+ qt_obj = get_or(res, f"tweets.{qt_id}")
+
+ # for development
+ # print()
+ # print("-" * 80)
+ # print(res["tweets"].keys())
+ # print(rt_id, rt_obj is not None)
+ # print(qt_id, qt_obj is not None)
return Tweet(
id=int(obj["id_str"]),
@@ -198,7 +209,7 @@ class Tweet(JSONTrait):
cashtags=[x["text"] for x in get_or(obj, "entities.symbols", [])],
mentionedUsers=[UserRef.parse(x) for x in get_or(obj, "entities.user_mentions", [])],
links=_parse_links(obj, ["entities.urls"]),
- viewCount=int_or_none(obj, "ext_views.count"),
+ viewCount=_get_views(obj, rt_obj or {}),
retweetedTweet=Tweet.parse(rt_obj, res) if rt_obj else None,
quotedTweet=Tweet.parse(qt_obj, res) if qt_obj else None,
place=Place.parse(obj["place"]) if obj.get("place") else None,
@@ -345,3 +356,20 @@ def _parse_links(obj: dict, paths: list[str]):
links = [TextLink.parse(x) for x in links]
links = [x for x in links if x is not None]
return links
+
+
+def _first(obj: dict, paths: list[str]):
+ for x in paths:
+ cid = get_or(obj, x, None)
+ if cid is not None:
+ return cid
+ return None
+
+
+def _get_views(obj: dict, rt_obj: dict):
+ for x in [obj, rt_obj]:
+ for y in ["ext_views.count", "views.count"]:
+ k = int_or_none(x, y)
+ if k is not None:
+ return k
+ return None
diff --git a/twscrape/queue_client.py b/twscrape/queue_client.py
index 6aaf07b..70aec63 100644
--- a/twscrape/queue_client.py
+++ b/twscrape/queue_client.py
@@ -114,7 +114,7 @@ class QueueClient:
logger.debug(f"Rate limit for {log_id}")
elif rep.status_code == 400:
- # twitter can return different types of cursors that not transfers between accounts
+ # api can return different types of cursors that not transfers between accounts
# just take the next account, the current cursor can work in it
logger.debug(f"Cursor not valid for {log_id}")
diff --git a/twscrape/utils.py b/twscrape/utils.py
index 4ea72ff..405ca21 100644
--- a/twscrape/utils.py
+++ b/twscrape/utils.py
@@ -121,7 +121,13 @@ def get_typed_object(obj: dict, res: defaultdict[str, list]):
def to_old_obj(obj: dict):
- return {**obj, **obj["legacy"], "id_str": str(obj["rest_id"]), "id": int(obj["rest_id"])}
+ return {
+ **obj,
+ **obj["legacy"],
+ "id_str": str(obj["rest_id"]),
+ "id": int(obj["rest_id"]),
+ "legacy": None,
+ }
def to_old_rep(obj: dict):