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"
+}