Ticket #663: twitter-mentions.diff
File twitter-mentions.diff, 34.0 KB (added by , at 2011-06-11T11:20:35Z) |
---|
-
twitter.c
diff -uNr twitter/twitter.c twitter-meh/twitter.c
old new 29 29 #include "url.h" 30 30 31 31 #define twitter_msg( ic, fmt... ) \ 32 do { 33 struct twitter_data *td = ic->proto_data; 34 if( td-> home_timeline_gc )\35 imcb_chat_log( td-> home_timeline_gc, fmt );\36 else 37 imcb_log( ic, fmt ); 32 do { \ 33 struct twitter_data *td = ic->proto_data; \ 34 if( td->timeline_gc ) \ 35 imcb_chat_log( td->timeline_gc, fmt ); \ 36 else \ 37 imcb_log( ic, fmt ); \ 38 38 } while( 0 ); 39 39 40 40 GSList *twitter_connections = NULL; … … 51 51 return 0; 52 52 53 53 // Do stuff.. 54 twitter_get_ home_timeline(ic, -1);54 twitter_get_timeline(ic, -1); 55 55 56 56 // If we are still logged in run this function again after timeout. 57 57 return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN; … … 68 68 69 69 // Queue the main_loop 70 70 // Save the return value, so we can remove the timeout on logout. 71 td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); 71 td->main_loop_id = 72 b_timeout_add(set_getint(&ic->acc->set, "fetch_every") * 1000, twitter_main_loop, ic); 72 73 } 73 74 74 75 static void twitter_oauth_start(struct im_connection *ic); … … 77 78 { 78 79 struct twitter_data *td = ic->proto_data; 79 80 81 td->timeline_doing = FALSE; 82 80 83 if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) 81 84 twitter_oauth_start(ic); 82 85 else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") != 0 && … … 219 222 220 223 s = set_add(&acc->set, "auto_reply_timeout", "10800", set_eval_int, acc); 221 224 225 s = set_add(&acc->set, "fetch_every", "60", set_eval_int, acc); 226 s->flags |= ACC_SET_OFFLINE_ONLY; 227 222 228 s = set_add(&acc->set, "base_url", def_url, NULL, acc); 223 229 s->flags |= ACC_SET_OFFLINE_ONLY; 224 230 … … 226 232 227 233 s = set_add(&acc->set, "message_length", "140", set_eval_int, acc); 228 234 235 s = set_add(&acc->set, "include_mentions", "true", set_eval_bool, acc); 236 229 237 s = set_add(&acc->set, "mode", "chat", set_eval_mode, acc); 230 238 s->flags |= ACC_SET_OFFLINE_ONLY; 231 239 … … 299 307 // Remove the main_loop function from the function queue. 300 308 b_event_remove(td->main_loop_id); 301 309 302 if (td-> home_timeline_gc)303 imcb_chat_free(td-> home_timeline_gc);310 if (td->timeline_gc) 311 imcb_chat_free(td->timeline_gc); 304 312 305 313 if (td) { 306 314 oauth_info_free(td->oauth_info); … … 386 394 { 387 395 struct twitter_data *td = c->ic->proto_data; 388 396 389 if (c != td-> home_timeline_gc)397 if (c != td->timeline_gc) 390 398 return; /* WTF? */ 391 399 392 400 /* If the user leaves the channel: Fine. Rejoin him/her once new 393 401 tweets come in. */ 394 imcb_chat_free(td-> home_timeline_gc);395 td-> home_timeline_gc = NULL;402 imcb_chat_free(td->timeline_gc); 403 td->timeline_gc = NULL; 396 404 } 397 405 398 406 static void twitter_keepalive(struct im_connection *ic) -
twitter.c.orig
diff -uNr twitter/twitter.c.orig twitter-meh/twitter.c.orig
old new 1 /***************************************************************************\ 2 * * 3 * BitlBee - An IRC to IM gateway * 4 * Simple module to facilitate twitter functionality. * 5 * * 6 * Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com> * 7 * * 8 * This library is free software; you can redistribute it and/or * 9 * modify it under the terms of the GNU Lesser General Public * 10 * License as published by the Free Software Foundation, version * 11 * 2.1. * 12 * * 13 * This library is distributed in the hope that it will be useful, * 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 16 * Lesser General Public License for more details. * 17 * * 18 * You should have received a copy of the GNU Lesser General Public License * 19 * along with this library; if not, write to the Free Software Foundation, * 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * 21 * * 22 ****************************************************************************/ 23 24 #include "nogaim.h" 25 #include "oauth.h" 26 #include "twitter.h" 27 #include "twitter_http.h" 28 #include "twitter_lib.h" 29 #include "url.h" 30 31 #define twitter_msg( ic, fmt... ) \ 32 do { \ 33 struct twitter_data *td = ic->proto_data; \ 34 if( td->home_timeline_gc ) \ 35 imcb_chat_log( td->home_timeline_gc, fmt ); \ 36 else \ 37 imcb_log( ic, fmt ); \ 38 } while( 0 ); 39 40 GSList *twitter_connections = NULL; 41 42 /** 43 * Main loop function 44 */ 45 gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) 46 { 47 struct im_connection *ic = data; 48 49 // Check if we are still logged in... 50 if (!g_slist_find( twitter_connections, ic )) 51 return 0; 52 53 // Do stuff.. 54 twitter_get_home_timeline(ic, -1); 55 56 // If we are still logged in run this function again after timeout. 57 return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN; 58 } 59 60 static void twitter_main_loop_start( struct im_connection *ic ) 61 { 62 struct twitter_data *td = ic->proto_data; 63 64 imcb_log( ic, "Getting initial statuses" ); 65 66 // Run this once. After this queue the main loop function. 67 twitter_main_loop(ic, -1, 0); 68 69 // Queue the main_loop 70 // Save the return value, so we can remove the timeout on logout. 71 td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); 72 } 73 74 static void twitter_oauth_start( struct im_connection *ic ); 75 76 void twitter_login_finish( struct im_connection *ic ) 77 { 78 struct twitter_data *td = ic->proto_data; 79 80 if( set_getbool( &ic->acc->set, "oauth" ) && !td->oauth_info ) 81 twitter_oauth_start( ic ); 82 else if( g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) != 0 && 83 !( td->flags & TWITTER_HAVE_FRIENDS ) ) 84 { 85 imcb_log( ic, "Getting contact list" ); 86 twitter_get_statuses_friends( ic, -1 ); 87 } 88 else 89 twitter_main_loop_start( ic ); 90 } 91 92 static const struct oauth_service twitter_oauth = 93 { 94 "http://api.twitter.com/oauth/request_token", 95 "http://api.twitter.com/oauth/access_token", 96 "https://api.twitter.com/oauth/authorize", 97 .consumer_key = "xsDNKJuNZYkZyMcu914uEA", 98 .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo", 99 }; 100 101 static const struct oauth_service identica_oauth = 102 { 103 "http://identi.ca/api/oauth/request_token", 104 "http://identi.ca/api/oauth/access_token", 105 "https://identi.ca/api/oauth/authorize", 106 .consumer_key = "e147ff789fcbd8a5a07963afbb43f9da", 107 .consumer_secret = "c596267f277457ec0ce1ab7bb788d828", 108 }; 109 110 static gboolean twitter_oauth_callback( struct oauth_info *info ); 111 112 static const struct oauth_service *get_oauth_service( struct im_connection *ic ) 113 { 114 struct twitter_data *td = ic->proto_data; 115 116 if( strstr( td->url_host, "identi.ca" ) ) 117 return &identica_oauth; 118 else 119 return &twitter_oauth; 120 121 /* Could add more services, or allow configuring your own base URL + 122 API keys. */ 123 } 124 125 static void twitter_oauth_start( struct im_connection *ic ) 126 { 127 struct twitter_data *td = ic->proto_data; 128 129 imcb_log( ic, "Requesting OAuth request token" ); 130 131 td->oauth_info = oauth_request_token( get_oauth_service( ic ), twitter_oauth_callback, ic ); 132 133 /* We need help from the user to complete OAuth login, so don't time 134 out on this login. */ 135 ic->flags |= OPT_SLOW_LOGIN; 136 } 137 138 static gboolean twitter_oauth_callback( struct oauth_info *info ) 139 { 140 struct im_connection *ic = info->data; 141 struct twitter_data *td; 142 143 if( !g_slist_find( twitter_connections, ic ) ) 144 return FALSE; 145 146 td = ic->proto_data; 147 if( info->stage == OAUTH_REQUEST_TOKEN ) 148 { 149 char name[strlen(ic->acc->user)+9], *msg; 150 151 if( info->request_token == NULL ) 152 { 153 imcb_error( ic, "OAuth error: %s", twitter_parse_error( info->http ) ); 154 imc_logout( ic, TRUE ); 155 return FALSE; 156 } 157 158 sprintf( name, "%s_%s", td->prefix, ic->acc->user ); 159 msg = g_strdup_printf( "To finish OAuth authentication, please visit " 160 "%s and respond with the resulting PIN code.", 161 info->auth_url ); 162 imcb_buddy_msg( ic, name, msg, 0, 0 ); 163 g_free( msg ); 164 } 165 else if( info->stage == OAUTH_ACCESS_TOKEN ) 166 { 167 if( info->token == NULL || info->token_secret == NULL ) 168 { 169 imcb_error( ic, "OAuth error: %s", twitter_parse_error( info->http ) ); 170 imc_logout( ic, TRUE ); 171 return FALSE; 172 } 173 else 174 { 175 const char *sn = oauth_params_get( &info->params, "screen_name" ); 176 177 if( sn != NULL && ic->acc->prpl->handle_cmp( sn, ic->acc->user ) != 0 ) 178 { 179 imcb_log( ic, "Warning: You logged in via OAuth as %s " 180 "instead of %s.", sn, ic->acc->user ); 181 } 182 } 183 184 /* IM mods didn't do this so far and it's ugly but I should 185 be able to get away with it... */ 186 g_free( ic->acc->pass ); 187 ic->acc->pass = oauth_to_string( info ); 188 189 twitter_login_finish( ic ); 190 } 191 192 return TRUE; 193 } 194 195 196 static char *set_eval_mode( set_t *set, char *value ) 197 { 198 if( g_strcasecmp( value, "one" ) == 0 || 199 g_strcasecmp( value, "many" ) == 0 || 200 g_strcasecmp( value, "chat" ) == 0 ) 201 return value; 202 else 203 return NULL; 204 } 205 206 static gboolean twitter_length_check( struct im_connection *ic, gchar *msg ) 207 { 208 int max = set_getint( &ic->acc->set, "message_length" ), len; 209 210 if( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max ) 211 return TRUE; 212 213 imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max ); 214 215 return FALSE; 216 } 217 218 static void twitter_init( account_t *acc ) 219 { 220 set_t *s; 221 char *def_url; 222 char *def_oauth; 223 224 if( strcmp( acc->prpl->name, "twitter" ) == 0 ) 225 { 226 def_url = TWITTER_API_URL; 227 def_oauth = "true"; 228 } 229 else /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ 230 { 231 def_url = IDENTICA_API_URL; 232 def_oauth = "false"; 233 } 234 235 s = set_add( &acc->set, "auto_reply_timeout", "10800", set_eval_int, acc ); 236 237 s = set_add( &acc->set, "base_url", def_url, NULL, acc ); 238 s->flags |= ACC_SET_OFFLINE_ONLY; 239 240 s = set_add( &acc->set, "commands", "true", set_eval_bool, acc ); 241 242 s = set_add( &acc->set, "message_length", "140", set_eval_int, acc ); 243 244 s = set_add( &acc->set, "mode", "chat", set_eval_mode, acc ); 245 s->flags |= ACC_SET_OFFLINE_ONLY; 246 247 s = set_add( &acc->set, "show_ids", "false", set_eval_bool, acc ); 248 s->flags |= ACC_SET_OFFLINE_ONLY; 249 250 s = set_add( &acc->set, "oauth", def_oauth, set_eval_bool, acc ); 251 } 252 253 /** 254 * Login method. Since the twitter API works with seperate HTTP request we 255 * only save the user and pass to the twitter_data object. 256 */ 257 static void twitter_login( account_t *acc ) 258 { 259 struct im_connection *ic = imcb_new( acc ); 260 struct twitter_data *td; 261 char name[strlen(acc->user)+9]; 262 url_t url; 263 264 if( !url_set( &url, set_getstr( &ic->acc->set, "base_url" ) ) || 265 ( url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS ) ) 266 { 267 imcb_error( ic, "Incorrect API base URL: %s", set_getstr( &ic->acc->set, "base_url" ) ); 268 imc_logout( ic, FALSE ); 269 return; 270 } 271 272 twitter_connections = g_slist_append( twitter_connections, ic ); 273 td = g_new0( struct twitter_data, 1 ); 274 ic->proto_data = td; 275 276 td->url_ssl = url.proto == PROTO_HTTPS; 277 td->url_port = url.port; 278 td->url_host = g_strdup( url.host ); 279 if( strcmp( url.file, "/" ) != 0 ) 280 td->url_path = g_strdup( url.file ); 281 else 282 td->url_path = g_strdup( "" ); 283 if( g_str_has_suffix( url.host, ".com" ) ) 284 td->prefix = g_strndup( url.host, strlen( url.host ) - 4 ); 285 else 286 td->prefix = g_strdup( url.host ); 287 288 td->flags |= TWITTER_HAVE_FRIENDS; 289 td->user = acc->user; 290 if( strstr( acc->pass, "oauth_token=" ) ) 291 td->oauth_info = oauth_from_string( acc->pass, get_oauth_service( ic ) ); 292 293 sprintf( name, "%s_%s", td->prefix, acc->user ); 294 imcb_add_buddy( ic, name, NULL ); 295 imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); 296 297 if( set_getbool( &acc->set, "show_ids" ) ) 298 td->log = g_new0( struct twitter_log_data, TWITTER_LOG_LENGTH ); 299 300 imcb_log( ic, "Connecting" ); 301 302 twitter_login_finish( ic ); 303 } 304 305 /** 306 * Logout method. Just free the twitter_data. 307 */ 308 static void twitter_logout( struct im_connection *ic ) 309 { 310 struct twitter_data *td = ic->proto_data; 311 312 // Set the status to logged out. 313 ic->flags &= ~ OPT_LOGGED_IN; 314 315 // Remove the main_loop function from the function queue. 316 b_event_remove(td->main_loop_id); 317 318 if(td->home_timeline_gc) 319 imcb_chat_free(td->home_timeline_gc); 320 321 if( td ) 322 { 323 oauth_info_free( td->oauth_info ); 324 g_free( td->prefix ); 325 g_free( td->url_host ); 326 g_free( td->url_path ); 327 g_free( td->pass ); 328 g_free( td->log ); 329 g_free( td ); 330 } 331 332 twitter_connections = g_slist_remove( twitter_connections, ic ); 333 } 334 335 static void twitter_handle_command( struct im_connection *ic, char *message ); 336 337 /** 338 * 339 */ 340 static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) 341 { 342 struct twitter_data *td = ic->proto_data; 343 int plen = strlen( td->prefix ); 344 345 if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' && 346 g_strcasecmp(who + plen + 1, ic->acc->user) == 0) 347 { 348 if( set_getbool( &ic->acc->set, "oauth" ) && 349 td->oauth_info && td->oauth_info->token == NULL ) 350 { 351 char pin[strlen(message)+1], *s; 352 353 strcpy( pin, message ); 354 for( s = pin + sizeof( pin ) - 2; s > pin && isspace( *s ); s -- ) 355 *s = '\0'; 356 for( s = pin; *s && isspace( *s ); s ++ ) {} 357 358 if( !oauth_access_token( s, td->oauth_info ) ) 359 { 360 imcb_error( ic, "OAuth error: %s", "Failed to send access token request" ); 361 imc_logout( ic, TRUE ); 362 return FALSE; 363 } 364 } 365 else 366 twitter_handle_command(ic, message); 367 } 368 else 369 { 370 twitter_direct_messages_new(ic, who, message); 371 } 372 return( 0 ); 373 } 374 375 /** 376 * 377 */ 378 static void twitter_set_my_name( struct im_connection *ic, char *info ) 379 { 380 } 381 382 static void twitter_get_info(struct im_connection *ic, char *who) 383 { 384 } 385 386 static void twitter_add_buddy( struct im_connection *ic, char *who, char *group ) 387 { 388 twitter_friendships_create_destroy(ic, who, 1); 389 } 390 391 static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group ) 392 { 393 twitter_friendships_create_destroy(ic, who, 0); 394 } 395 396 static void twitter_chat_msg( struct groupchat *c, char *message, int flags ) 397 { 398 if( c && message ) 399 twitter_handle_command( c->ic, message ); 400 } 401 402 static void twitter_chat_invite( struct groupchat *c, char *who, char *message ) 403 { 404 } 405 406 static void twitter_chat_leave( struct groupchat *c ) 407 { 408 struct twitter_data *td = c->ic->proto_data; 409 410 if( c != td->home_timeline_gc ) 411 return; /* WTF? */ 412 413 /* If the user leaves the channel: Fine. Rejoin him/her once new 414 tweets come in. */ 415 imcb_chat_free(td->home_timeline_gc); 416 td->home_timeline_gc = NULL; 417 } 418 419 static void twitter_keepalive( struct im_connection *ic ) 420 { 421 } 422 423 static void twitter_add_permit( struct im_connection *ic, char *who ) 424 { 425 } 426 427 static void twitter_rem_permit( struct im_connection *ic, char *who ) 428 { 429 } 430 431 static void twitter_add_deny( struct im_connection *ic, char *who ) 432 { 433 } 434 435 static void twitter_rem_deny( struct im_connection *ic, char *who ) 436 { 437 } 438 439 //static char *twitter_set_display_name( set_t *set, char *value ) 440 //{ 441 // return value; 442 //} 443 444 static void twitter_buddy_data_add( struct bee_user *bu ) 445 { 446 bu->data = g_new0( struct twitter_user_data, 1 ); 447 } 448 449 static void twitter_buddy_data_free( struct bee_user *bu ) 450 { 451 g_free( bu->data ); 452 } 453 454 static void twitter_handle_command( struct im_connection *ic, char *message ) 455 { 456 struct twitter_data *td = ic->proto_data; 457 char *cmds, **cmd, *new = NULL; 458 guint64 in_reply_to = 0; 459 460 cmds = g_strdup( message ); 461 cmd = split_command_parts( cmds ); 462 463 if( cmd[0] == NULL ) 464 { 465 g_free( cmds ); 466 return; 467 } 468 else if( !set_getbool( &ic->acc->set, "commands" ) ) 469 { 470 /* Not supporting commands. */ 471 } 472 else if( g_strcasecmp( cmd[0], "undo" ) == 0 ) 473 { 474 guint64 id; 475 476 if( cmd[1] ) 477 id = g_ascii_strtoull( cmd[1], NULL, 10 ); 478 else 479 id = td->last_status_id; 480 481 /* TODO: User feedback. */ 482 if( id ) 483 twitter_status_destroy( ic, id ); 484 else 485 twitter_msg( ic, "Could not undo last action" ); 486 487 g_free( cmds ); 488 return; 489 } 490 else if( g_strcasecmp( cmd[0], "follow" ) == 0 && cmd[1] ) 491 { 492 twitter_add_buddy( ic, cmd[1], NULL ); 493 g_free( cmds ); 494 return; 495 } 496 else if( g_strcasecmp( cmd[0], "unfollow" ) == 0 && cmd[1] ) 497 { 498 twitter_remove_buddy( ic, cmd[1], NULL ); 499 g_free( cmds ); 500 return; 501 } 502 else if( g_strcasecmp( cmd[0], "rt" ) == 0 && cmd[1] ) 503 { 504 struct twitter_user_data *tud; 505 bee_user_t *bu; 506 guint64 id; 507 508 if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[1] ) ) && 509 ( tud = bu->data ) && tud->last_id ) 510 id = tud->last_id; 511 else 512 { 513 id = g_ascii_strtoull( cmd[1], NULL, 10 ); 514 if( id < TWITTER_LOG_LENGTH && td->log ) 515 id = td->log[id].id; 516 } 517 518 td->last_status_id = 0; 519 if( id ) 520 twitter_status_retweet( ic, id ); 521 else 522 twitter_msg( ic, "User `%s' does not exist or didn't " 523 "post any statuses recently", cmd[1] ); 524 525 g_free( cmds ); 526 return; 527 } 528 else if( g_strcasecmp( cmd[0], "reply" ) == 0 && cmd[1] && cmd[2] ) 529 { 530 struct twitter_user_data *tud; 531 bee_user_t *bu = NULL; 532 guint64 id = 0; 533 534 if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[1] ) ) && 535 ( tud = bu->data ) && tud->last_id ) 536 { 537 id = tud->last_id; 538 } 539 else if( ( id = g_ascii_strtoull( cmd[1], NULL, 10 ) ) && 540 ( id < TWITTER_LOG_LENGTH ) && td->log ) 541 { 542 bu = td->log[id].bu; 543 if( g_slist_find( ic->bee->users, bu ) ) 544 id = td->log[id].id; 545 else 546 bu = NULL; 547 } 548 if( !id || !bu ) 549 { 550 twitter_msg( ic, "User `%s' does not exist or didn't " 551 "post any statuses recently", cmd[1] ); 552 return; 553 } 554 message = new = g_strdup_printf( "@%s %s", bu->handle, 555 message + ( cmd[2] - cmd[0] ) ); 556 in_reply_to = id; 557 } 558 else if( g_strcasecmp( cmd[0], "post" ) == 0 ) 559 { 560 message += 5; 561 } 562 563 { 564 char *s; 565 bee_user_t *bu; 566 567 if( !twitter_length_check( ic, message ) ) 568 { 569 g_free( new ); 570 g_free( cmds ); 571 return; 572 } 573 574 s = cmd[0] + strlen( cmd[0] ) - 1; 575 if( !new && s > cmd[0] && ( *s == ':' || *s == ',' ) ) 576 { 577 *s = '\0'; 578 579 if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[0] ) ) ) 580 { 581 struct twitter_user_data *tud = bu->data; 582 583 new = g_strdup_printf( "@%s %s", bu->handle, 584 message + ( s - cmd[0] ) + 2 ); 585 message = new; 586 587 if( time( NULL ) < tud->last_time + 588 set_getint( &ic->acc->set, "auto_reply_timeout" ) ) 589 in_reply_to = tud->last_id; 590 } 591 } 592 593 /* If the user runs undo between this request and its response 594 this would delete the second-last Tweet. Prevent that. */ 595 td->last_status_id = 0; 596 twitter_post_status( ic, message, in_reply_to ); 597 g_free( new ); 598 } 599 g_free( cmds ); 600 } 601 602 void twitter_initmodule() 603 { 604 struct prpl *ret = g_new0(struct prpl, 1); 605 606 ret->options = OPT_NOOTR; 607 ret->name = "twitter"; 608 ret->login = twitter_login; 609 ret->init = twitter_init; 610 ret->logout = twitter_logout; 611 ret->buddy_msg = twitter_buddy_msg; 612 ret->get_info = twitter_get_info; 613 ret->set_my_name = twitter_set_my_name; 614 ret->add_buddy = twitter_add_buddy; 615 ret->remove_buddy = twitter_remove_buddy; 616 ret->chat_msg = twitter_chat_msg; 617 ret->chat_invite = twitter_chat_invite; 618 ret->chat_leave = twitter_chat_leave; 619 ret->keepalive = twitter_keepalive; 620 ret->add_permit = twitter_add_permit; 621 ret->rem_permit = twitter_rem_permit; 622 ret->add_deny = twitter_add_deny; 623 ret->rem_deny = twitter_rem_deny; 624 ret->buddy_data_add = twitter_buddy_data_add; 625 ret->buddy_data_free = twitter_buddy_data_free; 626 ret->handle_cmp = g_strcasecmp; 627 628 register_protocol(ret); 629 630 /* And an identi.ca variant: */ 631 ret = g_memdup(ret, sizeof(struct prpl)); 632 ret->name = "identica"; 633 register_protocol(ret); 634 } -
twitter.h
diff -uNr twitter/twitter.h twitter-meh/twitter.h
old new 44 44 char* user; 45 45 char* pass; 46 46 struct oauth_info *oauth_info; 47 guint64 home_timeline_id; 47 48 gboolean timeline_doing; 49 gpointer home_timeline_obj; 50 gboolean home_timeline_done; 51 gpointer mentions_obj; 52 gboolean mentions_done; 53 54 guint64 timeline_id; 48 55 guint64 last_status_id; /* For undo */ 49 56 gint main_loop_id; 50 struct groupchat * home_timeline_gc;57 struct groupchat *timeline_gc; 51 58 gint http_fails; 52 59 twitter_flags_t flags; 53 60 -
twitter_lib.c
diff -uNr twitter/twitter_lib.c twitter-meh/twitter_lib.c
old new 78 78 { 79 79 if (txu == NULL) 80 80 return; 81 81 82 g_free(txu->name); 82 83 g_free(txu->screen_name); 83 84 g_free(txu); 84 85 } 85 86 86 87 87 /** 88 88 * Frees a twitter_xml_status struct. 89 89 */ 90 90 static void txs_free(struct twitter_xml_status *txs) 91 91 { 92 if (txs == NULL) 93 return; 94 92 95 g_free(txs->text); 93 96 txu_free(txs->user); 94 97 g_free(txs); … … 103 106 GSList *l; 104 107 if (txl == NULL) 105 108 return; 106 for (l = txl->list; l; l = g_slist_next(l)) 107 if (txl->type == TXL_STATUS) 109 110 for (l = txl->list; l; l = g_slist_next(l)) { 111 if (txl->type == TXL_STATUS) { 108 112 txs_free((struct twitter_xml_status *) l->data); 109 else if (txl->type == TXL_ID)113 } else if (txl->type == TXL_ID) { 110 114 g_free(l->data); 115 } 116 } 117 111 118 g_slist_free(txl->list); 112 119 g_free(txl); 113 120 } 114 121 115 122 /** 116 * Add a buddy if it is not allready added, set the status to logged in. 123 * Compare status elements 124 */ 125 static gint twitter_compare_elements(gconstpointer a, gconstpointer b) 126 { 127 struct twitter_xml_status *a_status = (struct twitter_xml_status *) a; 128 struct twitter_xml_status *b_status = (struct twitter_xml_status *) b; 129 130 if (a_status->created_at < b_status->created_at) { 131 return -1; 132 } else if (a_status->created_at > b_status->created_at) { 133 return 1; 134 } else { 135 return 0; 136 } 137 } 138 139 /** 140 * Add a buddy if it is not already added, set the status to logged in. 117 141 */ 118 142 static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname) 119 143 { 120 144 struct twitter_data *td = ic->proto_data; 121 145 122 // Check if the buddy is al lready in the buddy list.146 // Check if the buddy is already in the buddy list. 123 147 if (!bee_user_by_handle(ic->bee, ic, name)) { 124 148 char *mode = set_getstr(&ic->acc->set, "mode"); 125 149 … … 130 154 /* Necessary so that nicks always get translated to the 131 155 exact Twitter username. */ 132 156 imcb_buddy_nick_hint(ic, name, name); 133 imcb_chat_add_buddy(td-> home_timeline_gc, name);157 imcb_chat_add_buddy(td->timeline_gc, name); 134 158 } else if (g_strcasecmp(mode, "many") == 0) 135 159 imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); 136 160 } … … 438 462 return XT_HANDLED; 439 463 } 440 464 441 static void twitter_http_get_home_timeline(struct http_request *req);442 443 /**444 * Get the timeline.445 */446 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)447 {448 struct twitter_data *td = ic->proto_data;449 450 char *args[4];451 args[0] = "cursor";452 args[1] = g_strdup_printf("%lld", (long long) next_cursor);453 if (td->home_timeline_id) {454 args[2] = "since_id";455 args[3] = g_strdup_printf("%llu", (long long unsigned int) td->home_timeline_id);456 }457 458 twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,459 td->home_timeline_id ? 4 : 2);460 461 g_free(args[1]);462 if (td->home_timeline_id) {463 g_free(args[3]);464 }465 }466 467 465 static char *twitter_msg_add_id(struct im_connection *ic, 468 466 struct twitter_xml_status *txs, const char *prefix) 469 467 { … … 502 500 struct twitter_data *td = ic->proto_data; 503 501 GSList *l; 504 502 505 td-> home_timeline_gc = gc = imcb_chat_new(ic, "home/timeline");503 td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline"); 506 504 507 505 name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user); 508 506 imcb_chat_name_hint(gc, name_hint); … … 511 509 for (l = ic->bee->users; l; l = l->next) { 512 510 bee_user_t *bu = l->data; 513 511 if (bu->ic == ic) 514 imcb_chat_add_buddy(td-> home_timeline_gc, bu->handle);512 imcb_chat_add_buddy(td->timeline_gc, bu->handle); 515 513 } 516 514 } 517 515 … … 524 522 GSList *l = NULL; 525 523 struct twitter_xml_status *status; 526 524 struct groupchat *gc; 525 char *text; 526 guint64 last_id = 0; 527 size_t length; 527 528 528 529 // Create a new groupchat if it does not exsist. 529 if (!td-> home_timeline_gc)530 if (!td->timeline_gc) 530 531 twitter_groupchat_init(ic); 531 532 532 gc = td-> home_timeline_gc;533 gc = td->timeline_gc; 533 534 if (!gc->joined) 534 535 imcb_chat_add_buddy(gc, ic->acc->user); 535 536 … … 537 538 char *msg; 538 539 539 540 status = l->data; 540 if (status->user == NULL || status->text == NULL )541 if (status->user == NULL || status->text == NULL || last_id == status->id) 541 542 continue; 542 543 543 twitter_add_buddy(ic, status->user->screen_name, status->user->name);544 last_id = status->id; 544 545 545 546 strip_html(status->text); 547 546 548 msg = twitter_msg_add_id(ic, status, ""); 547 549 548 550 // Say it! 549 if (g_strcasecmp(td->user, status->user->screen_name) == 0) 551 if (g_strcasecmp(td->user, status->user->screen_name) == 0) { 550 552 imcb_chat_log(gc, "You: %s", msg ? msg : status->text); 551 else 553 } else { 554 twitter_add_buddy(ic, status->user->screen_name, status->user->name); 555 552 556 imcb_chat_msg(gc, status->user->screen_name, 553 557 msg ? msg : status->text, 0, status->created_at); 558 } 554 559 555 560 g_free(msg); 556 561 557 // Update the home_timeline_id to hold the highest id, so that by the next request562 // Update the timeline_id to hold the highest id, so that by the next request 558 563 // we won't pick up the updates already in the list. 559 td-> home_timeline_id = MAX(td->home_timeline_id, status->id);564 td->timeline_id = MAX(td->timeline_id, status->id); 560 565 } 561 566 } 562 567 … … 570 575 struct twitter_xml_status *status; 571 576 char from[MAX_STRING]; 572 577 gboolean mode_one; 578 guint64 last_id = 0; 573 579 574 580 mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0; 575 581 … … 582 588 char *prefix = NULL, *text = NULL; 583 589 584 590 status = l->data; 591 if (status->user == NULL || status->text == NULL || last_id == status->id) 592 continue; 593 594 last_id = status->id; 585 595 586 596 strip_html(status->text); 587 597 if (mode_one) … … 596 606 mode_one ? from : status->user->screen_name, 597 607 text ? text : status->text, 0, status->created_at); 598 608 599 // Update the home_timeline_id to hold the highest id, so that by the next request609 // Update the timeline_id to hold the highest id, so that by the next request 600 610 // we won't pick up the updates already in the list. 601 td-> home_timeline_id = MAX(td->home_timeline_id, status->id);611 td->timeline_id = MAX(td->timeline_id, status->id); 602 612 603 613 g_free(text); 604 614 g_free(prefix); 605 615 } 606 616 } 607 617 618 static void twitter_http_get_home_timeline(struct http_request *req); 619 static void twitter_http_get_mentions(struct http_request *req); 620 621 /** 622 * Get the timeline with optionally mentions 623 */ 624 void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) 625 { 626 struct twitter_data *td = ic->proto_data; 627 gboolean include_mentions = set_getbool(&ic->acc->set, "include_mentions"); 628 629 if (td->timeline_doing) { 630 return; 631 } 632 633 td->timeline_doing = TRUE; 634 635 twitter_get_home_timeline(ic, next_cursor); 636 637 if (include_mentions) { 638 twitter_get_mentions(ic, next_cursor); 639 } 640 } 641 642 void twitter_flush_timeline(struct im_connection *ic) 643 { 644 struct twitter_data *td = ic->proto_data; 645 gboolean include_mentions = set_getbool(&ic->acc->set, "include_mentions"); 646 struct twitter_xml_list *home_timeline = td->home_timeline_obj; 647 struct twitter_xml_list *mentions = td->mentions_obj; 648 GSList *output = NULL; 649 GSList *l; 650 651 if (!td->home_timeline_done) { 652 return; 653 } 654 655 if (include_mentions && !td->mentions_done) { 656 return; 657 } 658 659 if (home_timeline && home_timeline->list) { 660 for (l = home_timeline->list; l; l = g_slist_next(l)) { 661 output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); 662 } 663 } 664 665 if (include_mentions && mentions && mentions->list) { 666 for (l = mentions->list; l; l = g_slist_next(l)) { 667 if (output && twitter_compare_elements(l->data, output->data) < 0) { 668 continue; 669 } 670 671 output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); 672 } 673 } 674 // See if the user wants to see the messages in a groupchat window or as private messages. 675 if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 676 twitter_groupchat(ic, output); 677 else 678 twitter_private_message_chat(ic, output); 679 680 g_slist_free(output); 681 682 if (home_timeline && home_timeline->list) { 683 txl_free(home_timeline); 684 } 685 686 if (mentions && mentions->list) { 687 txl_free(mentions); 688 } 689 690 td->home_timeline_done = FALSE; 691 td->mentions_done = FALSE; 692 td->timeline_doing = FALSE; 693 } 694 695 /** 696 * Get the timeline. 697 */ 698 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) 699 { 700 struct twitter_data *td = ic->proto_data; 701 702 td->home_timeline_obj = NULL; 703 td->home_timeline_done = FALSE; 704 705 char *args[4]; 706 args[0] = "cursor"; 707 args[1] = g_strdup_printf("%lld", (long long) next_cursor); 708 if (td->timeline_id) { 709 args[2] = "since_id"; 710 args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); 711 } 712 713 twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, 714 td->timeline_id ? 4 : 2); 715 716 g_free(args[1]); 717 if (td->timeline_id) { 718 g_free(args[3]); 719 } 720 } 721 722 /** 723 * Get mentions. 724 */ 725 void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) 726 { 727 struct twitter_data *td = ic->proto_data; 728 729 td->mentions_obj = NULL; 730 td->mentions_done = FALSE; 731 732 char *args[4]; 733 args[0] = "cursor"; 734 args[1] = g_strdup_printf("%lld", (long long) next_cursor); 735 if (td->timeline_id) { 736 args[2] = "since_id"; 737 args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); 738 } 739 740 twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args, 741 td->timeline_id ? 4 : 2); 742 743 g_free(args[1]); 744 if (td->timeline_id) { 745 g_free(args[3]); 746 } 747 } 748 608 749 /** 609 750 * Callback for getting the home timeline. 610 751 */ … … 629 770 } else if (req->status_code == 401) { 630 771 imcb_error(ic, "Authentication failure"); 631 772 imc_logout(ic, FALSE); 632 return;773 goto end; 633 774 } else { 634 775 // It didn't go well, output the error and return. 635 776 if (++td->http_fails >= 5) 636 777 imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", 637 778 twitter_parse_error(req)); 638 779 780 goto end; 781 } 782 783 txl = g_new0(struct twitter_xml_list, 1); 784 txl->list = NULL; 785 786 // Parse the data. 787 parser = xt_new(NULL, txl); 788 xt_feed(parser, req->reply_body, req->body_size); 789 // The root <statuses> node should hold the list of statuses <status> 790 twitter_xt_get_status_list(ic, parser->root, txl); 791 xt_free(parser); 792 793 td->home_timeline_obj = txl; 794 795 end: 796 td->home_timeline_done = TRUE; 797 798 twitter_flush_timeline(ic); 799 } 800 801 /** 802 * Callback for getting mentions. 803 */ 804 static void twitter_http_get_mentions(struct http_request *req) 805 { 806 struct im_connection *ic = req->data; 807 struct twitter_data *td; 808 struct xt_parser *parser; 809 struct twitter_xml_list *txl; 810 811 // Check if the connection is still active. 812 if (!g_slist_find(twitter_connections, ic)) 639 813 return; 814 815 td = ic->proto_data; 816 817 // Check if the HTTP request went well. 818 if (req->status_code == 200) { 819 td->http_fails = 0; 820 if (!(ic->flags & OPT_LOGGED_IN)) 821 imcb_connected(ic); 822 } else if (req->status_code == 401) { 823 imcb_error(ic, "Authentication failure"); 824 imc_logout(ic, FALSE); 825 goto end; 826 } else { 827 // It didn't go well, output the error and return. 828 if (++td->http_fails >= 5) 829 imcb_error(ic, "Could not retrieve " TWITTER_MENTIONS_URL ": %s", 830 twitter_parse_error(req)); 831 832 goto end; 640 833 } 641 834 642 835 txl = g_new0(struct twitter_xml_list, 1); … … 649 842 twitter_xt_get_status_list(ic, parser->root, txl); 650 843 xt_free(parser); 651 844 652 // See if the user wants to see the messages in a groupchat window or as private messages. 653 if (txl->list == NULL); 654 else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 655 twitter_groupchat(ic, txl->list); 656 else 657 twitter_private_message_chat(ic, txl->list); 845 td->mentions_obj = txl; 658 846 659 // Free the structure. 660 txl_free(txl); 847 end: 848 td->mentions_done = TRUE; 849 850 twitter_flush_timeline(ic); 661 851 } 662 852 663 853 /** … … 697 887 td->http_fails = 0; 698 888 } 699 889 700 if (!td-> home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)890 if (!td->timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 701 891 twitter_groupchat_init(ic); 702 892 703 893 txl = g_new0(struct twitter_xml_list, 1); -
twitter_lib.h
diff -uNr twitter/twitter_lib.h twitter-meh/twitter_lib.h
old new 77 77 #define TWITTER_BLOCKS_CREATE_URL "/blocks/create/" 78 78 #define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/" 79 79 80 void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor); 80 81 void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor); 81 82 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); 83 void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); 82 84 void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor); 83 85 84 86 void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to);