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';
47 require_once dirname(dirname(__FILE__)) . '/jsonstreamreader.php';
48 require_once dirname(dirname(__FILE__)) . '/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,
69 throw new ServerException("No Twitter config for this user.");
72 $token = TwitterOAuthClient::unpackToken($flink->credentials);
74 throw new ServerException("No Twitter OAuth credentials for this user.");
77 return new TwitterOAuthClient($token->key, $token->secret);
80 function homeStreamForUser(User $user)
82 $auth = twitterAuthForUser($user);
83 return new TwitterUserStream($auth);
86 function siteStreamForOwner(User $user)
88 // The user we auth as must be the owner of the application.
89 $auth = twitterAuthForUser($user);
91 if (have_option('apiroot')) {
92 $stream = new TwitterSiteStream($auth, get_option_value('apiroot'));
94 $stream = new TwitterSiteStream($auth);
97 // Pull Twitter user IDs for all users we want to pull data for
100 $flink = new Foreign_link();
101 $flink->service = TWITTER_SERVICE;
104 while ($flink->fetch()) {
105 if (($flink->noticesync & FOREIGN_NOTICE_RECV) ==
106 FOREIGN_NOTICE_RECV) {
107 $userIds[] = $flink->foreign_id;
111 $stream->followUsers($userIds);
116 $user = User::staticGet('nickname', $nickname);
120 if (have_option('all')) {
121 $stream = siteStreamForOwner($user);
123 $stream = homeStreamForUser($user);
127 $stream->hookEvent('raw', function($data, $context) {
128 common_log(LOG_INFO, json_encode($data) . ' for ' . json_encode($context));
130 $stream->hookEvent('friends', function($data, $context) {
131 printf("Friend list: %s\n", implode(', ', $data->friends));
133 $stream->hookEvent('favorite', function($data, $context) {
134 printf("%s favorited %s's notice: %s\n",
135 $data->source->screen_name,
136 $data->target->screen_name,
137 $data->target_object->text);
139 $stream->hookEvent('unfavorite', function($data, $context) {
140 printf("%s unfavorited %s's notice: %s\n",
141 $data->source->screen_name,
142 $data->target->screen_name,
143 $data->target_object->text);
145 $stream->hookEvent('follow', function($data, $context) {
146 printf("%s friended %s\n",
147 $data->source->screen_name,
148 $data->target->screen_name);
150 $stream->hookEvent('unfollow', function($data, $context) {
151 printf("%s unfriended %s\n",
152 $data->source->screen_name,
153 $data->target->screen_name);
155 $stream->hookEvent('delete', function($data, $context) {
156 printf("Deleted status notification: %s\n",
159 $stream->hookEvent('scrub_geo', function($data, $context) {
160 printf("Req to scrub geo data for user id %s up to status ID %s\n",
162 $data->up_to_status_id);
164 $stream->hookEvent('status', function($data, $context) {
165 printf("Received status update from %s: %s\n",
166 $data->user->screen_name,
169 if (have_option('import')) {
170 $importer = new TwitterImport();
171 printf("\timporting...");
172 $notice = $importer->importStatus($data);
175 Inbox::insertNotice($myuser->id, $notice->id);
176 printf(" %s\n", $notice->id);
182 $stream->hookEvent('direct_message', function($data) {
183 printf("Direct message from %s to %s: %s\n",
184 $data->sender->screen_name,
185 $data->recipient->screen_name,
189 class TwitterManager extends IoManager
191 function __construct(TwitterStreamReader $stream)
193 $this->stream = $stream;
196 function getSockets()
198 return $this->stream->getSockets();
201 function handleInput($data)
203 $this->stream->handleInput($data);
209 $this->stream->connect();
215 $this->stream->close();
219 public static function get()
221 throw new Exception('not a singleton');
225 class TwitterStreamMaster extends IoMaster
227 function __construct($id, $ioManager)
229 parent::__construct($id);
230 $this->ioManager = $ioManager;
234 * Initialize IoManagers which are appropriate to this instance.
236 function initManagers()
238 $this->instantiate($this->ioManager);
242 $master = new TwitterStreamMaster('TwitterStream', new TwitterManager($stream));