Ticket #663: twitter-mentions.patch
File twitter-mentions.patch, 18.6 KB (added by , at 2011-05-07T23:21:03Z) |
---|
-
protocols/twitter/twitter.c
diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index dbe9b98..293cfa1 100644
a b 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; … … gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) 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; … … static void twitter_main_loop_start( struct im_connection *ic ) 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 = b_timeout_add(set_getint(&ic->acc->set, "fetch_every") * 1000, twitter_main_loop, ic); 72 72 } 73 73 74 74 static void twitter_oauth_start( struct im_connection *ic ); … … static void twitter_oauth_start( struct im_connection *ic ); 76 76 void twitter_login_finish( struct im_connection *ic ) 77 77 { 78 78 struct twitter_data *td = ic->proto_data; 79 79 80 td->timeline_doing = FALSE; 81 80 82 if( set_getbool( &ic->acc->set, "oauth" ) && !td->oauth_info ) 81 83 twitter_oauth_start( ic ); 82 84 else if( g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) != 0 && … … static void twitter_init( account_t *acc ) 211 213 } 212 214 213 215 s = set_add( &acc->set, "auto_reply_timeout", "10800", set_eval_int, acc ); 216 217 s = set_add( &acc->set, "fetch_every", "60", set_eval_int, acc ); 218 s->flags |= ACC_SET_OFFLINE_ONLY; 214 219 215 220 s = set_add( &acc->set, "base_url", def_url, NULL, acc ); 216 221 s->flags |= ACC_SET_OFFLINE_ONLY; … … static void twitter_init( account_t *acc ) 218 223 s = set_add( &acc->set, "commands", "true", set_eval_bool, acc ); 219 224 220 225 s = set_add( &acc->set, "message_length", "140", set_eval_int, acc ); 226 227 s = set_add( &acc->set, "include_mentions", "true", set_eval_bool, acc ); 221 228 222 229 s = set_add( &acc->set, "mode", "chat", set_eval_mode, acc ); 223 230 s->flags |= ACC_SET_OFFLINE_ONLY; … … static void twitter_logout( struct im_connection *ic ) 286 293 // Remove the main_loop function from the function queue. 287 294 b_event_remove(td->main_loop_id); 288 295 289 if(td-> home_timeline_gc)290 imcb_chat_free(td-> home_timeline_gc);296 if(td->timeline_gc) 297 imcb_chat_free(td->timeline_gc); 291 298 292 299 if( td ) 293 300 { … … static void twitter_chat_leave( struct groupchat *c ) 377 384 { 378 385 struct twitter_data *td = c->ic->proto_data; 379 386 380 if( c != td-> home_timeline_gc )387 if( c != td->timeline_gc ) 381 388 return; /* WTF? */ 382 389 383 390 /* If the user leaves the channel: Fine. Rejoin him/her once new 384 391 tweets come in. */ 385 imcb_chat_free(td-> home_timeline_gc);386 td-> home_timeline_gc = NULL;392 imcb_chat_free(td->timeline_gc); 393 td->timeline_gc = NULL; 387 394 } 388 395 389 396 static void twitter_keepalive( struct im_connection *ic ) -
protocols/twitter/twitter.h
diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 98f1683..0ebf996 100644
a b struct twitter_data 42 42 char* user; 43 43 char* pass; 44 44 struct oauth_info *oauth_info; 45 guint64 home_timeline_id; 45 46 gboolean timeline_doing; 47 gpointer home_timeline_obj; 48 gboolean home_timeline_done; 49 gpointer mentions_obj; 50 gboolean mentions_done; 51 52 guint64 timeline_id; 46 53 guint64 last_status_id; /* For undo */ 47 54 gint main_loop_id; 48 struct groupchat * home_timeline_gc;55 struct groupchat *timeline_gc; 49 56 gint http_fails; 50 57 twitter_flags_t flags; 51 58 -
protocols/twitter/twitter_lib.c
diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c8e4e76..7e1e21a 100644
a b static void txu_free(struct twitter_xml_user *txu) 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); … … static void txl_free(struct twitter_xml_list *txl) 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 } 114 else if (txl->type == TXL_ID) { 110 115 g_free(l->data); 116 } 117 } 118 111 119 g_slist_free(txl->list); 112 120 g_free(txl); 113 121 } 114 122 115 123 /** 116 * Add a buddy if it is not allready added, set the status to logged in. 124 * Compare status elements 125 */ 126 static gint twitter_compare_elements (gconstpointer a, gconstpointer b) 127 { 128 struct twitter_xml_status *a_status = (struct twitter_xml_status *) a; 129 struct twitter_xml_status *b_status = (struct twitter_xml_status *) b; 130 131 if (a_status->created_at < b_status->created_at) { 132 return -1; 133 } 134 else if (a_status->created_at > b_status->created_at) { 135 return 1; 136 } 137 else { 138 return 0; 139 } 140 } 141 142 /** 143 * Add a buddy if it is not already added, set the status to logged in. 117 144 */ 118 145 static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname) 119 146 { 120 147 struct twitter_data *td = ic->proto_data; 121 148 122 // Check if the buddy is al lready in the buddy list.149 // Check if the buddy is already in the buddy list. 123 150 if (!bee_user_by_handle( ic->bee, ic, name )) 124 151 { 125 152 char *mode = set_getstr(&ic->acc->set, "mode"); … … static void twitter_add_buddy(struct im_connection *ic, char *name, const char * 132 159 /* Necessary so that nicks always get translated to the 133 160 exact Twitter username. */ 134 161 imcb_buddy_nick_hint( ic, name, name ); 135 imcb_chat_add_buddy( td-> home_timeline_gc, name );162 imcb_chat_add_buddy( td->timeline_gc, name ); 136 163 } 137 164 else if (g_strcasecmp(mode, "many") == 0) 138 165 imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); … … static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt 476 503 return XT_HANDLED; 477 504 } 478 505 479 static void twitter_http_get_home_timeline(struct http_request *req);480 481 /**482 * Get the timeline.483 */484 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)485 {486 struct twitter_data *td = ic->proto_data;487 488 char* args[4];489 args[0] = "cursor";490 args[1] = g_strdup_printf ("%lld", (long long) next_cursor);491 if (td->home_timeline_id) {492 args[2] = "since_id";493 args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);494 }495 496 twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->home_timeline_id ? 4 : 2);497 498 g_free(args[1]);499 if (td->home_timeline_id) {500 g_free(args[3]);501 }502 }503 504 506 static void twitter_groupchat_init(struct im_connection *ic) 505 507 { 506 508 char *name_hint; … … static void twitter_groupchat_init(struct im_connection *ic) 508 510 struct twitter_data *td = ic->proto_data; 509 511 GSList *l; 510 512 511 td-> home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );513 td->timeline_gc = gc = imcb_chat_new( ic, "twitter/timeline" ); 512 514 513 515 name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user ); 514 516 imcb_chat_name_hint( gc, name_hint ); … … static void twitter_groupchat_init(struct im_connection *ic) 518 520 { 519 521 bee_user_t *bu = l->data; 520 522 if( bu->ic == ic ) 521 imcb_chat_add_buddy( td-> home_timeline_gc, bu->handle );523 imcb_chat_add_buddy( td->timeline_gc, bu->handle ); 522 524 } 523 525 } 524 526 … … static void twitter_groupchat(struct im_connection *ic, GSList *list) 531 533 GSList *l = NULL; 532 534 struct twitter_xml_status *status; 533 535 struct groupchat *gc; 536 char* text; 537 guint64 last_id = 0; 538 size_t length; 534 539 535 540 // Create a new groupchat if it does not exsist. 536 if (!td-> home_timeline_gc)541 if (!td->timeline_gc) 537 542 twitter_groupchat_init(ic); 538 543 539 gc = td-> home_timeline_gc;544 gc = td->timeline_gc; 540 545 if (!gc->joined) 541 546 imcb_chat_add_buddy( gc, ic->acc->user ); 542 547 543 548 for ( l = list; l ; l = g_slist_next(l) ) 544 549 { 545 550 status = l->data; 546 if (status->user == NULL || status->text == NULL )551 if (status->user == NULL || status->text == NULL || last_id == status->id) 547 552 continue; 548 553 549 twitter_add_buddy(ic, status->user->screen_name, status->user->name);550 554 last_id = status->id; 555 551 556 strip_html(status->text); 552 557 553 558 // Say it! 554 if (g_strcasecmp(td->user, status->user->screen_name) == 0) 559 if (g_strcasecmp(td->user, status->user->screen_name) == 0) { 555 560 imcb_chat_log (gc, "You: %s", status->text); 556 else 557 imcb_chat_msg (gc, status->user->screen_name, status->text, 0, status->created_at ); 561 } 562 else { 563 twitter_add_buddy(ic, status->user->screen_name, status->user->name); 564 565 text = g_strdup_printf("@%s", td->user); 566 567 if (g_str_has_prefix(status->text, text)) { 568 length = strlen(text); g_free(text); 569 570 text = g_strdup_printf("%s%s", ic->acc->user, &status->text[length]); 571 } 572 else { 573 text = g_strdup(status->text); 574 } 575 576 imcb_chat_msg (gc, status->user->screen_name, text, 0, status->created_at ); 577 578 g_free(text); 579 } 558 580 559 // Update the home_timeline_id to hold the highest id, so that by the next request560 // we won't pick up the updates al lready in the list.561 td-> home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;581 // Update the timeline_id to hold the highest id, so that by the next request 582 // we won't pick up the updates already in the list. 583 td->timeline_id = td->timeline_id < status->id ? status->id : td->timeline_id; 562 584 } 563 585 } 564 586 … … static void twitter_private_message_chat(struct im_connection *ic, GSList *list) 572 594 struct twitter_xml_status *status; 573 595 char from[MAX_STRING]; 574 596 gboolean mode_one; 597 guint64 last_id = 0; 575 598 576 599 mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0; 577 600 … … static void twitter_private_message_chat(struct im_connection *ic, GSList *list) 586 609 char *text = NULL; 587 610 588 611 status = l->data; 612 if (status->user == NULL || status->text == NULL || last_id == status->id) 613 continue; 614 615 last_id = status->id; 589 616 590 617 strip_html( status->text ); 591 618 if( mode_one ) … … static void twitter_private_message_chat(struct im_connection *ic, GSList *list) 599 626 mode_one ? text : status->text, 600 627 0, status->created_at ); 601 628 602 // Update the home_timeline_id to hold the highest id, so that by the next request603 // we won't pick up the updates al lready in the list.604 td-> home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;629 // Update the timeline_id to hold the highest id, so that by the next request 630 // we won't pick up the updates already in the list. 631 td->timeline_id = td->timeline_id < status->id ? status->id : td->timeline_id; 605 632 606 633 g_free( text ); 607 634 } 608 635 } 609 636 637 static void twitter_http_get_home_timeline(struct http_request *req); 638 static void twitter_http_get_mentions(struct http_request *req); 639 640 /** 641 * Get the timeline with optionally mentions 642 */ 643 void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) 644 { 645 struct twitter_data *td = ic->proto_data; 646 gboolean include_mentions = set_getbool(&ic->acc->set, "include_mentions"); 647 648 if (td->timeline_doing) { 649 return; 650 } 651 652 td->timeline_doing = TRUE; 653 654 twitter_get_home_timeline(ic, next_cursor); 655 656 if (include_mentions) { 657 twitter_get_mentions(ic, next_cursor); 658 } 659 } 660 661 void twitter_flush_timeline(struct im_connection *ic) 662 { 663 struct twitter_data *td = ic->proto_data; 664 gboolean include_mentions = set_getbool(&ic->acc->set, "include_mentions"); 665 struct twitter_xml_list *home_timeline = td->home_timeline_obj; 666 struct twitter_xml_list *mentions = td->mentions_obj; 667 GSList *output = NULL; 668 GSList *l; 669 670 if (!td->home_timeline_done) { 671 return; 672 } 673 674 if (include_mentions && !td->mentions_done) { 675 return; 676 } 677 678 if (home_timeline && home_timeline->list) { 679 for (l = home_timeline->list; l ; l = g_slist_next(l)) { 680 output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); 681 } 682 } 683 684 if (include_mentions && mentions && mentions->list) { 685 for (l = mentions->list; l ; l = g_slist_next(l)) { 686 if (output && twitter_compare_elements(l->data, output->data) < 0) { 687 continue; 688 } 689 690 output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); 691 } 692 } 693 694 // See if the user wants to see the messages in a groupchat window or as private messages. 695 if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 696 twitter_groupchat(ic, output); 697 else 698 twitter_private_message_chat(ic, output); 699 700 g_slist_free(output); 701 702 if (home_timeline && home_timeline->list) { 703 txl_free(home_timeline); 704 } 705 706 if (mentions && mentions->list) { 707 txl_free(mentions); 708 } 709 710 td->home_timeline_done = FALSE; 711 td->mentions_done = FALSE; 712 td->timeline_doing = FALSE; 713 } 714 715 /** 716 * Get the timeline. 717 */ 718 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) 719 { 720 struct twitter_data *td = ic->proto_data; 721 722 td->home_timeline_obj = NULL; 723 td->home_timeline_done = FALSE; 724 725 char* args[4]; 726 args[0] = "cursor"; 727 args[1] = g_strdup_printf ("%lld", (long long) next_cursor); 728 if (td->timeline_id) { 729 args[2] = "since_id"; 730 args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->timeline_id); 731 } 732 733 twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->timeline_id ? 4 : 2); 734 735 g_free(args[1]); 736 if (td->timeline_id) { 737 g_free(args[3]); 738 } 739 } 740 741 /** 742 * Get mentions. 743 */ 744 void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) 745 { 746 struct twitter_data *td = ic->proto_data; 747 748 td->mentions_obj = NULL; 749 td->mentions_done = FALSE; 750 751 char* args[4]; 752 args[0] = "cursor"; 753 args[1] = g_strdup_printf ("%lld", (long long) next_cursor); 754 if (td->timeline_id) { 755 args[2] = "since_id"; 756 args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->timeline_id); 757 } 758 759 twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args, td->timeline_id ? 4 : 2); 760 761 g_free(args[1]); 762 if (td->timeline_id) { 763 g_free(args[3]); 764 } 765 } 766 610 767 /** 611 768 * Callback for getting the home timeline. 612 769 */ … … static void twitter_http_get_home_timeline(struct http_request *req) 634 791 { 635 792 imcb_error( ic, "Authentication failure" ); 636 793 imc_logout( ic, FALSE ); 637 return;794 goto end; 638 795 } 639 796 else 640 797 { … … static void twitter_http_get_home_timeline(struct http_request *req) 642 799 if (++td->http_fails >= 5) 643 800 imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", twitter_parse_error(req)); 644 801 645 return;802 goto end; 646 803 } 647 804 648 805 txl = g_new0(struct twitter_xml_list, 1); … … static void twitter_http_get_home_timeline(struct http_request *req) 655 812 twitter_xt_get_status_list(ic, parser->root, txl); 656 813 xt_free( parser ); 657 814 658 // See if the user wants to see the messages in a groupchat window or as private messages. 659 if (txl->list == NULL) 660 ; 661 else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 662 twitter_groupchat(ic, txl->list); 815 td->home_timeline_obj = txl; 816 817 end: 818 td->home_timeline_done = TRUE; 819 820 twitter_flush_timeline(ic); 821 } 822 823 /** 824 * Callback for getting mentions. 825 */ 826 static void twitter_http_get_mentions(struct http_request *req) 827 { 828 struct im_connection *ic = req->data; 829 struct twitter_data *td; 830 struct xt_parser *parser; 831 struct twitter_xml_list *txl; 832 833 // Check if the connection is still active. 834 if( !g_slist_find( twitter_connections, ic ) ) 835 return; 836 837 td = ic->proto_data; 838 839 // Check if the HTTP request went well. 840 if (req->status_code == 200) 841 { 842 td->http_fails = 0; 843 if (!(ic->flags & OPT_LOGGED_IN)) 844 imcb_connected(ic); 845 } 846 else if (req->status_code == 401) 847 { 848 imcb_error( ic, "Authentication failure" ); 849 imc_logout( ic, FALSE ); 850 goto end; 851 } 663 852 else 664 twitter_private_message_chat(ic, txl->list); 853 { 854 // It didn't go well, output the error and return. 855 if (++td->http_fails >= 5) 856 imcb_error(ic, "Could not retrieve " TWITTER_MENTIONS_URL ": %s", twitter_parse_error(req)); 857 858 goto end; 859 } 665 860 666 // Free the structure. 667 txl_free(txl); 861 txl = g_new0(struct twitter_xml_list, 1); 862 txl->list = NULL; 863 864 // Parse the data. 865 parser = xt_new( NULL, txl ); 866 xt_feed( parser, req->reply_body, req->body_size ); 867 // The root <statuses> node should hold the list of statuses <status> 868 twitter_xt_get_status_list(ic, parser->root, txl); 869 xt_free( parser ); 870 871 td->mentions_obj = txl; 872 873 end: 874 td->mentions_done = TRUE; 875 876 twitter_flush_timeline(ic); 668 877 } 669 878 670 879 /** … … static void twitter_http_get_statuses_friends(struct http_request *req) 704 913 td->http_fails = 0; 705 914 } 706 915 707 if( !td-> home_timeline_gc &&916 if( !td->timeline_gc && 708 917 g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "chat" ) == 0 ) 709 918 twitter_groupchat_init( ic ); 710 919 -
protocols/twitter/twitter_lib.h
diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 24b4a08..6ebbdfe 100644
a b 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);