feat: update tweets parser (viewsCount, retweetedTweet, quotedTweet) #28

Этот коммит содержится в:
Vlad Pronsky 2023-07-07 12:25:50 +03:00
родитель 364c0ddfd0
Коммит 9238398b3c
7 изменённых файлов: 1831 добавлений и 18 удалений

906
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": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
"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": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
"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": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
"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": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
"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"
}
]
}
}
}

839
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": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
"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": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
"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": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
"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": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>",
"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"
}
]
}
}
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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}")

Просмотреть файл

@ -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):