diff --git a/lib/XRay/Formats/Twitter.php b/lib/XRay/Formats/Twitter.php index d114301..c2e8e44 100644 --- a/lib/XRay/Formats/Twitter.php +++ b/lib/XRay/Formats/Twitter.php @@ -70,12 +70,6 @@ class Twitter extends Format { ); $refs = []; - // Only use the "display" segment of the text - $text = mb_substr($tweet->full_text, - $tweet->display_text_range[0], - $tweet->display_text_range[1]-$tweet->display_text_range[0], - 'UTF-8'); - if(property_exists($tweet, 'retweeted_status')) { // No content for retweets @@ -93,12 +87,7 @@ class Twitter extends Format { $refs[$repostOf] = $repostedEntry['data']; } else { - // Twitter escapes & as & in the text - $text = html_entity_decode($text); - - $text = self::expandTweetURLs($text, $tweet); - - $entry['content'] = ['text' => $text]; + $entry['content'] = self::expandTweetContent($tweet); } // Published date @@ -209,10 +198,14 @@ class Twitter extends Format { $author['name'] = $profile->screen_name; if($profile->url) { - if($profile->entities->url->urls[0]->expanded_url) - $author['url'] = $profile->entities->url->urls[0]->expanded_url; - else - $author['url'] = $profile->entities->url->urls[0]->url; + if(property_exists($profile, 'entities')) { + if($profile->entities->url->urls[0]->expanded_url) + $author['url'] = $profile->entities->url->urls[0]->expanded_url; + else + $author['url'] = $profile->entities->url->urls[0]->url; + } else { + $author['url'] = $profile->url; + } } else { $author['url'] = 'https://twitter.com/' . $profile->screen_name; @@ -223,13 +216,66 @@ class Twitter extends Format { return $author; } - private static function expandTweetURLs($text, $object) { - if(property_exists($object, 'entities') && property_exists($object->entities, 'urls')) { - foreach($object->entities->urls as $url) { + private static function expandTweetContent($tweet) { + $entities = new \StdClass; + + if(property_exists($tweet, 'truncated') && $tweet->truncated) { + if(property_exists($tweet, 'extended_tweet')) { + $text = $tweet->extended_tweet->full_text; + + if(property_exists($tweet->extended_tweet, 'entities')) { + $entities = $tweet->extended_tweet->entities; + } + } else { + $text = $tweet->text; + + if(property_exists($tweet, 'entities')) { + $entities = $tweet->entities; + } + } + } else { + // Only use the "display" segment of the text + if(property_exists($tweet, 'full_text')) { + // Only use the "display" segment of the text + $text = mb_substr($tweet->full_text, + $tweet->display_text_range[0], + $tweet->display_text_range[1]-$tweet->display_text_range[0], + 'UTF-8'); + } else { + $text = $tweet->text; + } + + if(property_exists($tweet, 'entities')) { + $entities = $tweet->entities; + } + } + + // Twitter escapes & as & in the text + $text = html_entity_decode($text); + + $html = $text; + + if(property_exists($entities, 'user_mentions')) { + foreach($entities->user_mentions as $user) { + $html = str_replace('@'.$user->screen_name, '@'.$user->screen_name.'', $html); + } + } + + if(property_exists($entities, 'urls')) { + foreach($entities->urls as $url) { $text = str_replace($url->url, $url->expanded_url, $text); + $html = str_replace($url->url, ''.$url->expanded_url.'', $html); } } - return $text; + + $content = [ + 'text' => $text, + ]; + + if($html != $text) + $content['html'] = $html; + + return $content; } private static function expandTwitterObjectURLs($text, $object, $key) { diff --git a/tests/TwitterTest.php b/tests/TwitterTest.php index dcff8b7..ba855df 100644 --- a/tests/TwitterTest.php +++ b/tests/TwitterTest.php @@ -174,4 +174,41 @@ class TwitterTest extends PHPUnit_Framework_TestCase { $this->assertEquals('Yeah that\'s me http://xkcd.com/1782/', $tweet['content']['text']); } + public function testTruncatedQuotedTweet() { + list($url, $json) = $this->loadTweet('tweet-with-truncated-quoted-tweet'); + + $data = $this->parse(['url' => $url, 'body' => $json]); + + $this->assertEquals('entry', $data['data']['type']); + $this->assertEquals('.@stream_pdx is a real treasure of our city.', $data['data']['content']['text']); + $this->assertEquals('https://twitter.com/PDXStephenG/status/964598574322339841', $data['data']['quotation-of']); + $tweet = $data['data']['refs']['https://twitter.com/PDXStephenG/status/964598574322339841']; + $this->assertEquals('Hey @OregonGovBrown @tedwheeler day 16 of #BHM is for @stream_pdx. An amazing podcast trailer run by @tyeshasnow helping to democratize story telling in #PDX. Folks can get training in the production of podcasts. @siliconflorist #SupportBlackBusiness', $tweet['content']['text']); + $this->assertEquals("Hey @OregonGovBrown @tedwheeler day 16 of #BHM is for @stream_pdx. An amazing podcast trailer run by @tyeshasnow helping to democratize story telling in #PDX. Folks can get training in the production of podcasts. @siliconflorist #SupportBlackBusiness", $tweet['content']['html']); + } + + public function testStreamingTweetWithLink() { + list($url, $json) = $this->loadTweet('streaming-tweet-with-link'); + $data = $this->parse(['url' => $url, 'body' => $json]); + + $this->assertEquals('what happens if i include a link like https://kmikeym.com', $data['data']['content']['text']); + $this->assertEquals('what happens if i include a link like https://kmikeym.com', $data['data']['content']['html']); + } + + public function testStreamingTweetWithMentions() { + list($url, $json) = $this->loadTweet('streaming-tweet-with-mentions'); + $data = $this->parse(['url' => $url, 'body' => $json]); + + $this->assertEquals('Offer accepted! @aaronpk bought 1 shares from @coledrobison at $6.73 https://kmikeym.com/trades', $data['data']['content']['text']); + $this->assertEquals('Offer accepted! @aaronpk bought 1 shares from @coledrobison at $6.73 https://kmikeym.com/trades', $data['data']['content']['html']); + } + + public function testStreamingTweetTruncated() { + list($url, $json) = $this->loadTweet('streaming-tweet-truncated'); + $data = $this->parse(['url' => $url, 'body' => $json]); + + $this->assertEquals("#indieweb community. Really would like to see a Micropub client for Gratitude logging and also a Mastodon poster similar to the twitter one.\nFeel like I could (maybe) rewrite previous open code to do some of this :)", $data['data']['content']['text']); + $this->assertArrayNotHasKey('html', $data['data']['content']); + } + } diff --git a/tests/data/api.twitter.com/streaming-tweet-truncated.json b/tests/data/api.twitter.com/streaming-tweet-truncated.json new file mode 100644 index 0000000..c9963f3 --- /dev/null +++ b/tests/data/api.twitter.com/streaming-tweet-truncated.json @@ -0,0 +1,113 @@ +{ + "created_at": "Mon Feb 19 11:13:36 +0000 2018", + "id": 965544896588406784, + "id_str": "965544896588406784", + "text": "#indieweb community. Really would like to see a Micropub client for Gratitude logging and also a Mastodon poster si\u2026 https://t.co/ElywIO64tX", + "source": "Corebird", + "truncated": true, + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 11525, + "id_str": "11525", + "name": "Ian Forrester", + "screen_name": "cubicgarden", + "location": "Manchester, UK", + "url": "http://www.cubicgarden.com", + "description": "Senior firestarter at BBC R&D, emergent technology expert and serial social geek event organiser.", + "translator_type": "none", + "protected": false, + "verified": false, + "followers_count": 5842, + "friends_count": 216, + "listed_count": 383, + "favourites_count": 535, + "statuses_count": 71775, + "created_at": "Sun Nov 05 12:18:48 +0000 2006", + "utc_offset": 0, + "time_zone": "London", + "geo_enabled": true, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "profile_background_color": "000000", + "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/824523637/0aa843688f47b92a6d887901b962e10d.jpeg", + "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/824523637/0aa843688f47b92a6d887901b962e10d.jpeg", + "profile_background_tile": false, + "profile_link_color": "009999", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "006767", + "profile_text_color": "000000", + "profile_use_background_image": true, + "profile_image_url": "http://pbs.twimg.com/profile_images/766757305413148672/f1-y-0Ng_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/766757305413148672/f1-y-0Ng_normal.jpg", + "profile_banner_url": "https://pbs.twimg.com/profile_banners/11525/1471649200", + "default_profile": false, + "default_profile_image": false, + "following": null, + "follow_request_sent": null, + "notifications": null + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "extended_tweet": { + "full_text": "#indieweb community. Really would like to see a Micropub client for Gratitude logging and also a Mastodon poster similar to the twitter one.\nFeel like I could (maybe) rewrite previous open code to do some of this :)", + "display_text_range": [ + 0, + 215 + ], + "entities": { + "hashtags": [ + { + "text": "indieweb", + "indices": [ + 0, + 9 + ] + } + ], + "urls": [], + "user_mentions": [], + "symbols": [] + } + }, + "quote_count": 0, + "reply_count": 0, + "retweet_count": 0, + "favorite_count": 0, + "entities": { + "hashtags": [ + { + "text": "indieweb", + "indices": [ + 0, + 9 + ] + } + ], + "urls": [ + { + "url": "https://t.co/ElywIO64tX", + "expanded_url": "https://twitter.com/i/web/status/965544896588406784", + "display_url": "twitter.com/i/web/status/9\u2026", + "indices": [ + 117, + 140 + ] + } + ], + "user_mentions": [], + "symbols": [] + }, + "favorited": false, + "retweeted": false, + "filter_level": "low", + "lang": "en", + "timestamp_ms": "1519038816960" +} diff --git a/tests/data/api.twitter.com/streaming-tweet-with-link.json b/tests/data/api.twitter.com/streaming-tweet-with-link.json new file mode 100644 index 0000000..0532c93 --- /dev/null +++ b/tests/data/api.twitter.com/streaming-tweet-with-link.json @@ -0,0 +1,85 @@ + +{ + "created_at": "Mon Feb 19 03:27:58 +0000 2018", + "id": 965427716135665664, + "id_str": "965427716135665664", + "text": "what happens if i include a link like https://t.co/ut9caNOtx6", + "source": "Twitter Web Client", + "truncated": false, + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 143883456, + "id_str": "143883456", + "name": "aaronpk dev", + "screen_name": "pkdev", + "location": "Portland, OR", + "url": "http://aaronpk.micro.blog/", + "description": "Dev account for testing Twitter things. Follow me here: https://twitter.com/aaronpk", + "translator_type": "none", + "protected": false, + "verified": false, + "followers_count": 1, + "friends_count": 1, + "listed_count": 0, + "favourites_count": 2, + "statuses_count": 56, + "created_at": "Fri May 14 17:47:15 +0000 2010", + "utc_offset": -28800, + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": true, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": false, + "profile_link_color": "1DA1F2", + "profile_sidebar_border_color": "C0DEED", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "profile_image_url": "http://pbs.twimg.com/profile_images/638125135904436224/qd_d94Qn_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/638125135904436224/qd_d94Qn_normal.jpg", + "default_profile": true, + "default_profile_image": false, + "following": null, + "follow_request_sent": null, + "notifications": null + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "quote_count": 0, + "reply_count": 0, + "retweet_count": 0, + "favorite_count": 0, + "entities": { + "hashtags": [], + "urls": [ + { + "url": "https://t.co/ut9caNOtx6", + "expanded_url": "https://kmikeym.com", + "display_url": "kmikeym.com", + "indices": [ + 38, + 61 + ] + } + ], + "user_mentions": [], + "symbols": [] + }, + "favorited": false, + "retweeted": false, + "possibly_sensitive": false, + "filter_level": "low", + "lang": "en", + "timestamp_ms": "1519010878963" +} diff --git a/tests/data/api.twitter.com/streaming-tweet-with-mentions.json b/tests/data/api.twitter.com/streaming-tweet-with-mentions.json new file mode 100644 index 0000000..8596c58 --- /dev/null +++ b/tests/data/api.twitter.com/streaming-tweet-with-mentions.json @@ -0,0 +1,105 @@ +{ + "created_at": "Mon Feb 19 04:35:43 +0000 2018", + "id": 965444763569635328, + "id_str": "965444763569635328", + "text": "Offer accepted! @aaronpk bought 1 shares from @coledrobison at $6.73 https://t.co/aALIOaq0Ud", + "source": "k5m", + "truncated": false, + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 1311430404, + "id_str": "1311430404", + "name": "KmikeyM Trades", + "screen_name": "k5mtrades", + "location": "The Internet", + "url": "http://www.kmikeym.com/trades", + "description": "Tweets about recent trades and other activity on http://www.kmikeym.com", + "translator_type": "none", + "protected": false, + "verified": false, + "followers_count": 58, + "friends_count": 1, + "listed_count": 2, + "favourites_count": 1, + "statuses_count": 6448, + "created_at": "Thu Mar 28 18:51:43 +0000 2013", + "utc_offset": -28800, + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": false, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": false, + "profile_link_color": "1DA1F2", + "profile_sidebar_border_color": "C0DEED", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "profile_image_url": "http://pbs.twimg.com/profile_images/3450169500/3e3afb3219d4c43187d3b8752d731170_normal.png", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/3450169500/3e3afb3219d4c43187d3b8752d731170_normal.png", + "default_profile": true, + "default_profile_image": false, + "following": null, + "follow_request_sent": null, + "notifications": null + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "quote_count": 0, + "reply_count": 0, + "retweet_count": 0, + "favorite_count": 0, + "entities": { + "hashtags": [], + "urls": [ + { + "url": "https://t.co/aALIOaq0Ud", + "expanded_url": "https://kmikeym.com/trades", + "display_url": "kmikeym.com/trades", + "indices": [ + 69, + 92 + ] + } + ], + "user_mentions": [ + { + "screen_name": "aaronpk", + "name": "Aaron Parecki", + "id": 14447132, + "id_str": "14447132", + "indices": [ + 16, + 24 + ] + }, + { + "screen_name": "coledrobison", + "name": "COL\u039e Robison", + "id": 2962255453, + "id_str": "2962255453", + "indices": [ + 46, + 59 + ] + } + ], + "symbols": [] + }, + "favorited": false, + "retweeted": false, + "possibly_sensitive": false, + "filter_level": "low", + "lang": "en", + "timestamp_ms": "1519014943388" +} diff --git a/tests/data/api.twitter.com/tweet-with-truncated-quoted-tweet.json b/tests/data/api.twitter.com/tweet-with-truncated-quoted-tweet.json new file mode 100644 index 0000000..03e9a49 --- /dev/null +++ b/tests/data/api.twitter.com/tweet-with-truncated-quoted-tweet.json @@ -0,0 +1,378 @@ +{ + "created_at": "Mon Feb 19 07:25:44 +0000 2018", + "id": 965487550512054273, + "id_str": "965487550512054273", + "full_text": ".@stream_pdx is a real treasure of our city. https://t.co/twMOevR80T", + "truncated": false, + "display_text_range": [ + 0, + 44 + ], + "entities": { + "hashtags": [ + + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "stream_pdx", + "name": "Stream PDX", + "id": 770362395579396097, + "id_str": "770362395579396097", + "indices": [ + 1, + 12 + ] + } + ], + "urls": [ + { + "url": "https://t.co/twMOevR80T", + "expanded_url": "https://twitter.com/pdxstepheng/status/964598574322339841", + "display_url": "twitter.com/pdxstepheng/st…", + "indices": [ + 45, + 68 + ] + } + ] + }, + "source": "Twitter for iPhone", + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 3138466140, + "id_str": "3138466140", + "name": "Rowan Bradley", + "screen_name": "rowbradley", + "location": "Portland, Oregon", + "description": "Photography & Digital Marketing @scoutbooks", + "url": "https://t.co/eBvYQVeMHs", + "entities": { + "url": { + "urls": [ + { + "url": "https://t.co/eBvYQVeMHs", + "expanded_url": "http://rowanbradley.com", + "display_url": "rowanbradley.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 436, + "friends_count": 2001, + "listed_count": 33, + "created_at": "Sat Apr 04 05:47:27 +0000 2015", + "favourites_count": 27442, + "utc_offset": -28800, + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": true, + "verified": false, + "statuses_count": 4833, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "000000", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": false, + "profile_image_url": "http://pbs.twimg.com/profile_images/703441818038378499/3pkWJotj_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/703441818038378499/3pkWJotj_normal.jpg", + "profile_banner_url": "https://pbs.twimg.com/profile_banners/3138466140/1444289891", + "profile_link_color": "4A913C", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "000000", + "profile_text_color": "000000", + "profile_use_background_image": false, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": true, + "quoted_status_id": 964598574322339841, + "quoted_status_id_str": "964598574322339841", + "quoted_status": { + "created_at": "Fri Feb 16 20:33:16 +0000 2018", + "id": 964598574322339841, + "id_str": "964598574322339841", + "full_text": "Hey @OregonGovBrown @tedwheeler day 16 of #BHM is for @stream_pdx. An amazing podcast trailer run by @tyeshasnow helping to democratize story telling in #PDX. Folks can get training in the production of podcasts. @siliconflorist #SupportBlackBusiness https://t.co/dvWAMrzffF", + "truncated": false, + "display_text_range": [ + 0, + 250 + ], + "entities": { + "hashtags": [ + { + "text": "BHM", + "indices": [ + 42, + 46 + ] + }, + { + "text": "PDX", + "indices": [ + 153, + 157 + ] + }, + { + "text": "SupportBlackBusiness", + "indices": [ + 229, + 250 + ] + } + ], + "symbols": [ + + ], + "user_mentions": [ + { + "screen_name": "OregonGovBrown", + "name": "Governor Kate Brown", + "id": 3023272478, + "id_str": "3023272478", + "indices": [ + 4, + 19 + ] + }, + { + "screen_name": "tedwheeler", + "name": "Ted Wheeler", + "id": 18708561, + "id_str": "18708561", + "indices": [ + 20, + 31 + ] + }, + { + "screen_name": "stream_pdx", + "name": "Stream PDX", + "id": 770362395579396097, + "id_str": "770362395579396097", + "indices": [ + 54, + 65 + ] + }, + { + "screen_name": "tyeshasnow", + "name": "tyesha snow", + "id": 21260096, + "id_str": "21260096", + "indices": [ + 101, + 112 + ] + }, + { + "screen_name": "siliconflorist", + "name": "Silicon Florist", + "id": 8072912, + "id_str": "8072912", + "indices": [ + 213, + 228 + ] + } + ], + "urls": [ + + ], + "media": [ + { + "id": 964598550842826752, + "id_str": "964598550842826752", + "indices": [ + 251, + 274 + ], + "media_url": "http://pbs.twimg.com/media/DWLxRXXX4AAgn2K.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWLxRXXX4AAgn2K.jpg", + "url": "https://t.co/dvWAMrzffF", + "display_url": "pic.twitter.com/dvWAMrzffF", + "expanded_url": "https://twitter.com/PDXStephenG/status/964598574322339841/photo/1", + "type": "photo", + "sizes": { + "large": { + "w": 1800, + "h": 530, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 1200, + "h": 353, + "resize": "fit" + }, + "small": { + "w": 680, + "h": 200, + "resize": "fit" + } + } + } + ] + }, + "extended_entities": { + "media": [ + { + "id": 964598550842826752, + "id_str": "964598550842826752", + "indices": [ + 251, + 274 + ], + "media_url": "http://pbs.twimg.com/media/DWLxRXXX4AAgn2K.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWLxRXXX4AAgn2K.jpg", + "url": "https://t.co/dvWAMrzffF", + "display_url": "pic.twitter.com/dvWAMrzffF", + "expanded_url": "https://twitter.com/PDXStephenG/status/964598574322339841/photo/1", + "type": "photo", + "sizes": { + "large": { + "w": 1800, + "h": 530, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 1200, + "h": 353, + "resize": "fit" + }, + "small": { + "w": 680, + "h": 200, + "resize": "fit" + } + } + } + ] + }, + "source": "Twitter Web Client", + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 518975229, + "id_str": "518975229", + "name": "Stephen Green", + "screen_name": "PDXStephenG", + "location": "Portland, OR", + "description": "Blatino, husband, dad, entrepreneur, economist, TEDx talker & recovering banker/vc. Day job, Community Director at @WeWork. #Startup advisor @backstage_cap", + "url": "https://t.co/DNQHGaIJWP", + "entities": { + "url": { + "urls": [ + { + "url": "https://t.co/DNQHGaIJWP", + "expanded_url": "http://www.pitchblackpdx.com", + "display_url": "pitchblackpdx.com", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [ + + ] + } + }, + "protected": false, + "followers_count": 8958, + "friends_count": 9790, + "listed_count": 404, + "created_at": "Thu Mar 08 23:12:01 +0000 2012", + "favourites_count": 12352, + "utc_offset": -28800, + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": true, + "verified": false, + "statuses_count": 7506, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "000000", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": false, + "profile_image_url": "http://pbs.twimg.com/profile_images/882333395287658497/KpZZzrI9_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/882333395287658497/KpZZzrI9_normal.jpg", + "profile_banner_url": "https://pbs.twimg.com/profile_banners/518975229/1511838336", + "profile_link_color": "19CF86", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "000000", + "profile_text_color": "000000", + "profile_use_background_image": false, + "has_extended_profile": false, + "default_profile": false, + "default_profile_image": false, + "following": false, + "follow_request_sent": false, + "notifications": false, + "translator_type": "none" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 10, + "favorite_count": 22, + "favorited": false, + "retweeted": false, + "possibly_sensitive": false, + "possibly_sensitive_appealable": false, + "lang": "en" + }, + "retweet_count": 0, + "favorite_count": 1, + "favorited": false, + "retweeted": false, + "possibly_sensitive": false, + "possibly_sensitive_appealable": false, + "lang": "en" +}