3 * StatusNet, the distributed open-source microblogging tool
7 * LICENCE: This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 * @author Brion Vibber <brion@status.net>
23 * @copyright 2010 StatusNet, Inc.
24 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
25 * @link http://status.net/
28 define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
31 $longoptions = array('nick=','import','all','apiroot=');
33 $helptext = <<<ENDOFHELP
34 USAGE: streamtest.php -n <username>
36 -n --nick=<username> Local user whose Twitter timeline to watch
37 --import Experimental: run incoming messages through import
38 --all Experimental: run multiuser; requires nick be the app owner
39 --apiroot=<url> Provide alternate streaming API root URL
41 Attempts a User Stream connection to Twitter as the given user, dumping
46 require_once INSTALLDIR.'/scripts/commandline.inc.php';
47 require_once dirname(dirname(__FILE__)) . '/lib/jsonstreamreader.php';
48 require_once dirname(dirname(__FILE__)) . '/lib/twitterstreamreader.php';
50 if (have_option('n')) {
51 $nickname = get_option_value('n');
52 } else if (have_option('nick')) {
53 $nickname = get_option_value('nickname');
62 * @return TwitterOAuthClient
64 function twitterAuthForUser(User $user)
66 $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
67 $token = TwitterOAuthClient::unpackToken($flink->credentials);
69 throw new ServerException("No Twitter OAuth credentials for this user.");
72 return new TwitterOAuthClient($token->key, $token->secret);
75 function homeStreamForUser(User $user)
77 $auth = twitterAuthForUser($user);
78 return new TwitterUserStream($auth);
81 function siteStreamForOwner(User $user)
83 // The user we auth as must be the owner of the application.
84 $auth = twitterAuthForUser($user);
86 if (have_option('apiroot')) {
87 $stream = new TwitterSiteStream($auth, get_option_value('apiroot'));
89 $stream = new TwitterSiteStream($auth);
92 // Pull Twitter user IDs for all users we want to pull data for
95 $flink = new Foreign_link();
96 $flink->service = TWITTER_SERVICE;
99 while ($flink->fetch()) {
100 if (($flink->noticesync & FOREIGN_NOTICE_RECV) ==
101 FOREIGN_NOTICE_RECV) {
102 $userIds[] = $flink->foreign_id;
106 $stream->followUsers($userIds);
111 $user = User::getKV('nickname', $nickname);
115 if (have_option('all')) {
116 $stream = siteStreamForOwner($user);
118 $stream = homeStreamForUser($user);
122 $stream->hookEvent('raw', function($data, $context) {
123 common_log(LOG_INFO, json_encode($data) . ' for ' . json_encode($context));
125 $stream->hookEvent('friends', function($data, $context) {
126 printf("Friend list: %s\n", implode(', ', $data->friends));
128 $stream->hookEvent('favorite', function($data, $context) {
129 printf("%s favorited %s's notice: %s\n",
130 $data->source->screen_name,
131 $data->target->screen_name,
132 $data->target_object->text);
134 $stream->hookEvent('unfavorite', function($data, $context) {
135 printf("%s unfavorited %s's notice: %s\n",
136 $data->source->screen_name,
137 $data->target->screen_name,
138 $data->target_object->text);
140 $stream->hookEvent('follow', function($data, $context) {
141 printf("%s friended %s\n",
142 $data->source->screen_name,
143 $data->target->screen_name);
145 $stream->hookEvent('unfollow', function($data, $context) {
146 printf("%s unfriended %s\n",
147 $data->source->screen_name,
148 $data->target->screen_name);
150 $stream->hookEvent('delete', function($data, $context) {
151 printf("Deleted status notification: %s\n",
154 $stream->hookEvent('scrub_geo', function($data, $context) {
155 printf("Req to scrub geo data for user id %s up to status ID %s\n",
157 $data->up_to_status_id);
159 $stream->hookEvent('status', function($data, $context) {
160 printf("Received status update from %s: %s\n",
161 $data->user->screen_name,
164 if (have_option('import')) {
165 $importer = new TwitterImport();
166 printf("\timporting...");
167 $notice = $importer->importStatus($data);
168 if (!$notice instanceof Notice) {
173 $stream->hookEvent('direct_message', function($data) {
174 printf("Direct message from %s to %s: %s\n",
175 $data->sender->screen_name,
176 $data->recipient->screen_name,
180 class TwitterManager extends IoManager
182 function __construct(TwitterStreamReader $stream)
184 $this->stream = $stream;
187 function getSockets()
189 return $this->stream->getSockets();
192 function handleInput($data)
194 $this->stream->handleInput($data);
200 $this->stream->connect();
206 $this->stream->close();
210 public static function get()
212 throw new Exception('not a singleton');
216 class TwitterStreamMaster extends IoMaster
218 function __construct($id, $ioManager)
220 parent::__construct($id);
221 $this->ioManager = $ioManager;
225 * Initialize IoManagers which are appropriate to this instance.
227 function initManagers()
229 $this->instantiate($this->ioManager);
233 $master = new TwitterStreamMaster('TwitterStream', new TwitterManager($stream));