Changeset cc6fdf8 for protocols/twitter/twitter.c
- Timestamp:
- 2012-12-22T00:14:26Z (12 years ago)
- Branches:
- master
- Children:
- 7d5afa6
- Parents:
- 92d3044 (diff), 573e274 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
protocols/twitter/twitter.c
r92d3044 rcc6fdf8 4 4 * Simple module to facilitate twitter functionality. * 5 5 * * 6 * Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com> * 6 * Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com> * 7 * Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net> * 7 8 * * 8 9 * This library is free software; you can redistribute it and/or * … … 29 30 #include "url.h" 30 31 31 #define twitter_msg( 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 } while( 0 );39 40 32 GSList *twitter_connections = NULL; 41 42 33 /** 43 34 * Main loop function … … 62 53 struct twitter_data *td = ic->proto_data; 63 54 55 /* Create the room now that we "logged in". */ 56 if (td->flags & TWITTER_MODE_CHAT) 57 twitter_groupchat_init(ic); 58 64 59 imcb_log(ic, "Getting initial statuses"); 65 60 66 // Run this once. After this queue the main loop function. 61 // Run this once. After this queue the main loop function (or open the 62 // stream if available). 67 63 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 = 72 b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, twitter_main_loop, ic); 64 65 if (set_getbool(&ic->acc->set, "stream")) { 66 /* That fetch was just to get backlog, the stream will give 67 us the rest. \o/ */ 68 twitter_open_stream(ic); 69 70 /* Stream sends keepalives (empty lines) or actual data at 71 least twice a minute. Disconnect if this stops. */ 72 ic->flags |= OPT_PONGS; 73 } else { 74 /* Not using the streaming API, so keep polling the old- 75 fashioned way. :-( */ 76 td->main_loop_id = 77 b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, 78 twitter_main_loop, ic); 79 } 80 } 81 82 struct groupchat *twitter_groupchat_init(struct im_connection *ic) 83 { 84 char *name_hint; 85 struct groupchat *gc; 86 struct twitter_data *td = ic->proto_data; 87 GSList *l; 88 89 if (td->timeline_gc) 90 return td->timeline_gc; 91 92 td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline"); 93 94 name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user); 95 imcb_chat_name_hint(gc, name_hint); 96 g_free(name_hint); 97 98 for (l = ic->bee->users; l; l = l->next) { 99 bee_user_t *bu = l->data; 100 if (bu->ic == ic) 101 imcb_chat_add_buddy(gc, bu->handle); 102 } 103 imcb_chat_add_buddy(gc, ic->acc->user); 104 105 return gc; 73 106 } 74 107 … … 83 116 if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) 84 117 twitter_oauth_start(ic); 85 else if ( g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") != 0&&86 118 else if (!(td->flags & TWITTER_MODE_ONE) && 119 !(td->flags & TWITTER_HAVE_FRIENDS)) { 87 120 imcb_log(ic, "Getting contact list"); 88 121 twitter_get_friends_ids(ic, -1); 89 //twitter_get_statuses_friends(ic, -1);90 122 } else 91 123 twitter_main_loop_start(ic); … … 187 219 } 188 220 189 190 static char *set_eval_mode(set_t * set, char *value)191 {192 if (g_strcasecmp(value, "one") == 0 ||193 g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0)194 return value;195 else196 return NULL;197 }198 199 221 int twitter_url_len_diff(gchar *msg, unsigned int target_len) 200 222 { … … 233 255 return TRUE; 234 256 235 imcb_error(ic, "Maximum message length exceeded: %d > %d", len, max);257 twitter_log(ic, "Maximum message length exceeded: %d > %d", len, max); 236 258 237 259 return FALSE; 260 } 261 262 static char *set_eval_commands(set_t * set, char *value) 263 { 264 if (g_strcasecmp(value, "strict") == 0 ) 265 return value; 266 else 267 return set_eval_bool(set, value); 268 } 269 270 static char *set_eval_mode(set_t * set, char *value) 271 { 272 if (g_strcasecmp(value, "one") == 0 || 273 g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0) 274 return value; 275 else 276 return NULL; 238 277 } 239 278 … … 257 296 s->flags |= ACC_SET_OFFLINE_ONLY; 258 297 259 s = set_add(&acc->set, "commands", "true", set_eval_ bool, acc);298 s = set_add(&acc->set, "commands", "true", set_eval_commands, acc); 260 299 261 300 s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc); … … 274 313 275 314 s = set_add(&acc->set, "show_ids", "true", set_eval_bool, acc); 276 s->flags |= ACC_SET_OFFLINE_ONLY;277 315 278 316 s = set_add(&acc->set, "show_old_mentions", "20", set_eval_int, acc); 279 317 280 318 s = set_add(&acc->set, "strip_newlines", "false", set_eval_bool, acc); 319 320 if (strcmp(acc->prpl->name, "twitter") == 0) { 321 s = set_add(&acc->set, "stream", "true", set_eval_bool, acc); 322 s->flags |= ACC_SET_OFFLINE_ONLY; 323 } 281 324 } 282 325 283 326 /** 284 * Login method. Since the twitter API works with sep erate HTTP request we327 * Login method. Since the twitter API works with separate HTTP request we 285 328 * only save the user and pass to the twitter_data object. 286 329 */ … … 298 341 imc_logout(ic, FALSE); 299 342 return; 343 } 344 345 if (!strstr(url.host, "twitter.com") && 346 set_getbool(&ic->acc->set, "stream")) { 347 imcb_error(ic, "Warning: The streaming API is only supported by Twitter, " 348 "and you seem to be connecting to a different service."); 300 349 } 301 350 … … 340 389 imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); 341 390 342 if (set_getbool(&acc->set, "show_ids")) 343 td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH); 391 td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH); 392 td->log_id = -1; 393 394 s = set_getstr(&ic->acc->set, "mode"); 395 if (g_strcasecmp(s, "one") == 0) 396 td->flags |= TWITTER_MODE_ONE; 397 else if (g_strcasecmp(s, "many") == 0) 398 td->flags |= TWITTER_MODE_MANY; 399 else 400 td->flags |= TWITTER_MODE_CHAT; 344 401 345 402 twitter_login_finish(ic); … … 363 420 364 421 if (td) { 422 http_close(td->stream); 365 423 oauth_info_free(td->oauth_info); 366 424 g_free(td->user); … … 495 553 * Returns 0 if the user provides garbage. 496 554 */ 497 static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, struct twitter_data *td, char *arg) { 555 static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, char *arg, bee_user_t **bu_) { 556 struct twitter_data *td = ic->proto_data; 498 557 struct twitter_user_data *tud; 499 bee_user_t *bu ;558 bee_user_t *bu = NULL; 500 559 guint64 id = 0; 501 if (g_str_has_prefix(arg, "#") && 502 sscanf(arg + 1, "%" G_GUINT64_FORMAT, &id) == 1) { 503 if (id < TWITTER_LOG_LENGTH && td->log) 560 561 if (bu_) 562 *bu_ = NULL; 563 if (!arg || !arg[0]) 564 return 0; 565 566 if (arg[0] != '#' && (bu = bee_user_by_handle(ic->bee, ic, arg))) { 567 if ((tud = bu->data)) 568 id = tud->last_id; 569 } else { 570 if (arg[0] == '#') 571 arg++; 572 if (sscanf(arg, "%" G_GINT64_MODIFIER "x", &id) == 1 && 573 id < TWITTER_LOG_LENGTH) { 574 bu = td->log[id].bu; 504 575 id = td->log[id].id; 505 } else if ((bu = bee_user_by_handle(ic->bee, ic, arg)) && 506 (tud = bu->data) && tud->last_id) 507 id = tud->last_id; 508 else if (sscanf(arg, "%" G_GUINT64_FORMAT, &id) == 1){ 509 if (id < TWITTER_LOG_LENGTH && td->log) 510 id = td->log[id].id; 511 } 576 /* Beware of dangling pointers! */ 577 if (!g_slist_find(ic->bee->users, bu)) 578 bu = NULL; 579 } else if (sscanf(arg, "%" G_GINT64_MODIFIER "d", &id) == 1) { 580 /* Allow normal tweet IDs as well; not a very useful 581 feature but it's always been there. Just ignore 582 very low IDs to avoid accidents. */ 583 if (id < 1000000) 584 id = 0; 585 } 586 } 587 if (bu_) 588 *bu_ = bu; 512 589 return id; 513 590 } … … 517 594 struct twitter_data *td = ic->proto_data; 518 595 char *cmds, **cmd, *new = NULL; 519 guint64 in_reply_to = 0; 596 guint64 in_reply_to = 0, id; 597 gboolean allow_post = 598 g_strcasecmp(set_getstr(&ic->acc->set, "commands"), "strict") != 0; 599 bee_user_t *bu = NULL; 520 600 521 601 cmds = g_strdup(message); … … 523 603 524 604 if (cmd[0] == NULL) { 525 g_free(cmds); 526 return; 527 } else if (!set_getbool(&ic->acc->set, "commands")) { 528 /* Not supporting commands. */ 605 goto eof; 606 } else if (!set_getbool(&ic->acc->set, "commands") && allow_post) { 607 /* Not supporting commands if "commands" is set to true/strict. */ 529 608 } else if (g_strcasecmp(cmd[0], "undo") == 0) { 530 guint64 id;531 532 609 if (cmd[1] == NULL) 533 610 twitter_status_destroy(ic, td->last_status_id); 534 else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1) { 535 if (id < TWITTER_LOG_LENGTH && td->log) 536 id = td->log[id].id; 537 611 else if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) 538 612 twitter_status_destroy(ic, id); 539 } else 540 twitter_msg(ic, "Could not undo last action"); 541 542 g_free(cmds); 543 return; 613 else 614 twitter_log(ic, "Could not undo last action"); 615 616 goto eof; 544 617 } else if (g_strcasecmp(cmd[0], "favourite") == 0 && cmd[1]) { 545 guint64 id; 546 if ((id = twitter_message_id_from_command_arg(ic, td, cmd[1]))) { 618 if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) { 547 619 twitter_favourite_tweet(ic, id); 548 620 } else { 549 twitter_ msg(ic, "Please provide a message ID or username.");621 twitter_log(ic, "Please provide a message ID or username."); 550 622 } 551 g_free(cmds); 552 return; 623 goto eof; 553 624 } else if (g_strcasecmp(cmd[0], "follow") == 0 && cmd[1]) { 554 625 twitter_add_buddy(ic, cmd[1], NULL); 555 g_free(cmds); 556 return; 626 goto eof; 557 627 } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) { 558 628 twitter_remove_buddy(ic, cmd[1], NULL); 559 g_free(cmds); 560 return; 629 goto eof; 561 630 } else if ((g_strcasecmp(cmd[0], "report") == 0 || 562 631 g_strcasecmp(cmd[0], "spam") == 0) && cmd[1]) { 563 char * screen_name; 564 guint64 id; 565 screen_name = cmd[1]; 632 char *screen_name; 633 566 634 /* Report nominally works on users but look up the user who 567 635 posted the given ID if the user wants to do it that way */ 568 if (g_str_has_prefix(cmd[1], "#") && 569 sscanf(cmd[1] + 1, "%" G_GUINT64_FORMAT, &id) == 1) { 570 if (id < TWITTER_LOG_LENGTH && td->log) { 571 if (g_slist_find(ic->bee->users, td->log[id].bu)) { 572 screen_name = td->log[id].bu->handle; 573 } 574 } 575 } 636 twitter_message_id_from_command_arg(ic, cmd[1], &bu); 637 if (bu) 638 screen_name = bu->handle; 639 else 640 screen_name = cmd[1]; 641 576 642 twitter_report_spam(ic, screen_name); 577 g_free(cmds); 578 return; 643 goto eof; 579 644 } else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) { 580 guint64 id = twitter_message_id_from_command_arg(ic, td, cmd[1]);645 id = twitter_message_id_from_command_arg(ic, cmd[1], NULL); 581 646 582 647 td->last_status_id = 0; … … 584 649 twitter_status_retweet(ic, id); 585 650 else 586 twitter_ msg(ic, "User `%s' does not exist or didn't "651 twitter_log(ic, "User `%s' does not exist or didn't " 587 652 "post any statuses recently", cmd[1]); 588 653 589 g_free(cmds); 590 return; 654 goto eof; 591 655 } else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) { 592 struct twitter_user_data *tud; 593 bee_user_t *bu = NULL; 594 guint64 id = 0; 595 596 if (g_str_has_prefix(cmd[1], "#") && 597 sscanf(cmd[1] + 1, "%" G_GUINT64_FORMAT, &id) == 1 && 598 (id < TWITTER_LOG_LENGTH) && td->log) { 599 bu = td->log[id].bu; 600 if (g_slist_find(ic->bee->users, bu)) 601 id = td->log[id].id; 602 else 603 bu = NULL; 604 } else if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && 605 (tud = bu->data) && tud->last_id) { 606 id = tud->last_id; 607 } else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1 && 608 (id < TWITTER_LOG_LENGTH) && td->log) { 609 bu = td->log[id].bu; 610 if (g_slist_find(ic->bee->users, bu)) 611 id = td->log[id].id; 612 else 613 bu = NULL; 614 } 615 656 id = twitter_message_id_from_command_arg(ic, cmd[1], &bu); 616 657 if (!id || !bu) { 617 twitter_ msg(ic, "User `%s' does not exist or didn't "658 twitter_log(ic, "User `%s' does not exist or didn't " 618 659 "post any statuses recently", cmd[1]); 619 g_free(cmds); 620 return; 660 goto eof; 621 661 } 622 662 message = new = g_strdup_printf("@%s %s", bu->handle, message + (cmd[2] - cmd[0])); 623 663 in_reply_to = id; 664 allow_post = TRUE; 624 665 } else if (g_strcasecmp(cmd[0], "post") == 0) { 625 666 message += 5; 626 } 627 628 { 667 allow_post = TRUE; 668 } 669 670 if (allow_post) { 629 671 char *s; 630 bee_user_t *bu; 631 632 if (!twitter_length_check(ic, message)) { 633 g_free(new); 634 g_free(cmds); 635 return; 636 } 672 673 if (!twitter_length_check(ic, message)) 674 goto eof; 637 675 638 676 s = cmd[0] + strlen(cmd[0]) - 1; … … 657 695 td->last_status_id = 0; 658 696 twitter_post_status(ic, message, in_reply_to); 659 g_free(new); 660 } 697 } else { 698 twitter_log(ic, "Unknown command: %s", cmd[0]); 699 } 700 eof: 701 g_free(new); 661 702 g_free(cmds); 662 703 } 704 705 void twitter_log(struct im_connection *ic, char *format, ... ) 706 { 707 struct twitter_data *td = ic->proto_data; 708 va_list params; 709 char *text; 710 711 va_start(params, format); 712 text = g_strdup_vprintf(format, params); 713 va_end(params); 714 715 if (td->timeline_gc) 716 imcb_chat_log(td->timeline_gc, "%s", text); 717 else 718 imcb_log(ic, "%s", text); 719 720 g_free(text); 721 } 722 663 723 664 724 void twitter_initmodule()
Note: See TracChangeset
for help on using the changeset viewer.