]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/TwitterBridge/twitter.php
Fix for regression introduced with my last update to the
[quix0rs-gnu-social.git] / plugins / TwitterBridge / twitter.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2008, 2009, StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 if (!defined('STATUSNET') && !defined('LACONICA')) {
21     exit(1);
22 }
23
24 define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1
25
26 require_once INSTALLDIR . '/plugins/TwitterBridge/twitterbasicauthclient.php';
27 require_once INSTALLDIR . '/plugins/TwitterBridge/twitteroauthclient.php';
28
29 function add_twitter_user($twitter_id, $screen_name)
30 {
31     common_debug("Add Twitter user");
32
33     $new_uri = 'http://twitter.com/' . $screen_name;
34
35     // Clear out any bad old foreign_users with the new user's legit URL
36     // This can happen when users move around or fakester accounts get
37     // repoed, and things like that.
38
39     $luser = new Foreign_user();
40     $luser->uri = $new_uri;
41     $luser->service = TWITTER_SERVICE;
42     $result = $luser->delete();
43
44     if ($result != false) {
45         common_log(LOG_WARNING,
46             "Twitter bridge - removed invalid Twitter user squatting on uri: $new_uri");
47     }
48
49     $luser->free();
50     unset($luser);
51
52     // Otherwise, create a new Twitter user
53
54     $fuser = new Foreign_user();
55
56     $fuser->nickname = $screen_name;
57     $fuser->uri = 'http://twitter.com/' . $screen_name;
58     $fuser->id = $twitter_id;
59     $fuser->service = TWITTER_SERVICE;
60     $fuser->created = common_sql_now();
61     $result = $fuser->insert();
62
63     if (empty($result)) {
64         common_log(LOG_WARNING,
65             "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
66         common_log_db_error($fuser, 'INSERT', __FILE__);
67     } else {
68         common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id).");
69     }
70
71     return $result;
72 }
73
74 // Creates or Updates a Twitter user
75 function save_twitter_user($twitter_id, $screen_name)
76 {
77     // Check to see whether the Twitter user is already in the system,
78     // and update its screen name and uri if so.
79
80     $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
81
82     if (!empty($fuser)) {
83
84         $result = true;
85
86         // Delete old record if Twitter user changed screen name
87
88         if ($fuser->nickname != $screen_name) {
89             $oldname = $fuser->nickname;
90             $fuser->delete();
91             common_log(LOG_INFO, sprintf('Twitter bridge - Updated nickname (and URI) ' .
92                                          'for Twitter user %1$d - %2$s, was %3$s.',
93                                          $fuser->id,
94                                          $screen_name,
95                                          $oldname));
96         }
97     }
98
99     return add_twitter_user($twitter_id, $screen_name);
100 }
101
102 function is_twitter_bound($notice, $flink) {
103
104     // Check to see if notice should go to Twitter
105     if (!empty($flink) && ($flink->noticesync & FOREIGN_NOTICE_SEND)) {
106
107         // If it's not a Twitter-style reply, or if the user WANTS to send replies.
108         if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) ||
109             ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) {
110             return true;
111         }
112     }
113
114     return false;
115 }
116
117 function broadcast_twitter($notice)
118 {
119     $flink = Foreign_link::getByUserID($notice->profile_id,
120                                        TWITTER_SERVICE);
121
122     if (is_twitter_bound($notice, $flink)) {
123         if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
124             return broadcast_oauth($notice, $flink);
125         } else {
126             return broadcast_basicauth($notice, $flink);
127         }
128     }
129
130     return true;
131 }
132
133 function broadcast_oauth($notice, $flink) {
134     $user = $flink->getUser();
135     $statustxt = format_status($notice);
136     $token = TwitterOAuthClient::unpackToken($flink->credentials);
137     $client = new TwitterOAuthClient($token->key, $token->secret);
138     $status = null;
139
140     try {
141         $status = $client->statusesUpdate($statustxt);
142     } catch (OAuthClientException $e) {
143         return process_error($e, $flink, $notice);
144     }
145
146     if (empty($status)) {
147
148         // This could represent a failure posting,
149         // or the Twitter API might just be behaving flakey.
150
151         $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' .
152                           'trying to post notice %d for User %s (user id %d).',
153                           $notice->id,
154                           $user->nickname,
155                           $user->id);
156
157         common_log(LOG_WARNING, $errmsg);
158
159         return false;
160     }
161
162     // Notice crossed the great divide
163
164     $msg = sprintf('Twitter bridge - posted notice %d to Twitter using ' .
165                    'OAuth for User %s (user id %d).',
166                    $notice->id,
167                    $user->nickname,
168                    $user->id);
169
170     common_log(LOG_INFO, $msg);
171
172     return true;
173 }
174
175 function broadcast_basicauth($notice, $flink)
176 {
177     $user = $flink->getUser();
178
179     $statustxt = format_status($notice);
180
181     $client = new TwitterBasicAuthClient($flink);
182     $status = null;
183
184     try {
185         $status = $client->statusesUpdate($statustxt);
186     } catch (BasicAuthException $e) {
187         return process_error($e, $flink, $notice);
188     }
189
190     if (empty($status)) {
191
192         $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' .
193                           'trying to post notice %d for %s (user id %d).',
194                           $notice->id,
195                           $user->nickname,
196                           $user->id);
197
198         common_log(LOG_WARNING, $errmsg);
199
200         $errmsg = sprintf('No data returned by Twitter API when ' .
201                           'trying to post notice %d for %s (user id %d).',
202                           $notice->id,
203                           $user->nickname,
204                           $user->id);
205         common_log(LOG_WARNING, $errmsg);
206         return false;
207     }
208
209     $msg = sprintf('Twitter bridge - posted notice %d to Twitter using ' .
210                    'HTTP basic auth for User %s (user id %d).',
211                    $notice->id,
212                    $user->nickname,
213                    $user->id);
214
215     common_log(LOG_INFO, $msg);
216
217     return true;
218 }
219
220 function process_error($e, $flink, $notice)
221 {
222     $user = $flink->getUser();
223     $code = $e->getCode();
224
225     $logmsg = sprintf('Twitter bridge - %d posting notice %d for ' .
226                       'User %s (user id: %d): %s.',
227                       $code,
228                       $notice->id,
229                       $user->nickname,
230                       $user->id,
231                       $e->getMessage());
232
233     common_log(LOG_WARNING, $logmsg);
234
235     switch($code) {
236      case 401:
237         // Probably a revoked or otherwise bad access token - nuke!
238         remove_twitter_link($flink);
239         return true;
240         break;
241      case 403:
242         // User has exceeder her rate limit -- toss the notice
243         return true;
244         break;
245      default:
246
247         // For every other case, it's probably some flakiness so try
248         // sending the notice again later (requeue).
249
250         return false;
251         break;
252     }
253 }
254
255 function format_status($notice)
256 {
257     // XXX: Hack to get around PHP cURL's use of @ being a a meta character
258     $statustxt = preg_replace('/^@/', ' @', $notice->content);
259
260     // Convert !groups to #hashes
261     $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
262
263     return $statustxt;
264 }
265
266 function remove_twitter_link($flink)
267 {
268     $user = $flink->getUser();
269
270     common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' .
271                "user $user->nickname (user id: $user->id).");
272
273     $result = $flink->delete();
274
275     if (empty($result)) {
276         common_log(LOG_ERR, 'Could not remove Twitter bridge ' .
277                    "Foreign_link for $user->nickname (user id: $user->id)!");
278         common_log_db_error($flink, 'DELETE', __FILE__);
279     }
280
281     // Notify the user that her Twitter bridge is down
282
283     if (isset($user->email)) {
284
285         $result = mail_twitter_bridge_removed($user);
286
287         if (!$result) {
288
289             $msg = 'Unable to send email to notify ' .
290               "$user->nickname (user id: $user->id) " .
291               'that their Twitter bridge link was ' .
292               'removed!';
293
294             common_log(LOG_WARNING, $msg);
295         }
296     }
297
298 }
299
300 /**
301  * Send a mail message to notify a user that her Twitter bridge link
302  * has stopped working, and therefore has been removed.  This can
303  * happen when the user changes her Twitter password, or otherwise
304  * revokes access.
305  *
306  * @param User $user   user whose Twitter bridge link has been removed
307  *
308  * @return boolean success flag
309  */
310
311 function mail_twitter_bridge_removed($user)
312 {
313     common_init_locale($user->language);
314
315     $profile = $user->getProfile();
316
317     $subject = sprintf(_m('Your Twitter bridge has been disabled.'));
318
319     $site_name = common_config('site', 'name');
320
321     $body = sprintf(_m('Hi, %1$s. We\'re sorry to inform you that your ' .
322         'link to Twitter has been disabled. We no longer seem to have ' .
323     'permission to update your Twitter status. (Did you revoke ' .
324     '%3$s\'s access?)' . "\n\n" .
325     'You can re-enable your Twitter bridge by visiting your ' .
326     "Twitter settings page:\n\n\t%2\$s\n\n" .
327         "Regards,\n%3\$s\n"),
328         $profile->getBestName(),
329         common_local_url('twittersettings'),
330         common_config('site', 'name'));
331
332     common_init_locale();
333     return mail_to_user($user, $subject, $body);
334 }
335