From d6b690624885f6bc34e5dfb9a84daa34a1adb7e6 Mon Sep 17 00:00:00 2001 From: Jasper Spaans Date: Wed, 3 Aug 2011 23:45:49 +0200 Subject: Make it easier to retweet by id. Currently, typing "rt 24" would retweet the last message from @24. This patch allows one to retweet the tweet with id 24 by typing "rt #24". --- protocols/twitter/twitter.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 57a1ed80..7cf8a239 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -490,7 +490,11 @@ static void twitter_handle_command(struct im_connection *ic, char *message) bee_user_t *bu; guint64 id; - if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && + if (g_str_has_prefix(cmd[1], "#")) { + id = g_ascii_strtoull(cmd[1] + 1, NULL, 10); + if (id < TWITTER_LOG_LENGTH && td->log) + id = td->log[id].id; + } else if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && (tud = bu->data) && tud->last_id) id = tud->last_id; else { -- cgit v1.2.3 From aa2f575d34af957eaf7fac58a54055ba74a1085f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Aug 2011 23:21:47 +0100 Subject: Better cleanup of status IDs in Twitter commands. Stop using that silly g_ascii_strtoull() everywhere. This should fix some accidental Tweet incidents. --- protocols/twitter/twitter.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index fcbc88d7..175b1d0d 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -464,15 +464,14 @@ static void twitter_handle_command(struct im_connection *ic, char *message) } else if (g_strcasecmp(cmd[0], "undo") == 0) { guint64 id; - if (cmd[1]) - id = g_ascii_strtoull(cmd[1], NULL, 10); - else - id = td->last_status_id; - - /* TODO: User feedback. */ - if (id) + if (cmd[1] == NULL) + twitter_status_destroy(ic, td->last_status_id); + else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1) { + if (id < TWITTER_LOG_LENGTH && td->log) + id = td->log[id].id; + twitter_status_destroy(ic, id); - else + } else twitter_msg(ic, "Could not undo last action"); g_free(cmds); @@ -490,15 +489,14 @@ static void twitter_handle_command(struct im_connection *ic, char *message) bee_user_t *bu; guint64 id; - if (g_str_has_prefix(cmd[1], "#")) { - id = g_ascii_strtoull(cmd[1] + 1, NULL, 10); + if (g_str_has_prefix(cmd[1], "#") && + sscanf(cmd[1] + 1, "%" G_GUINT64_FORMAT, &id) == 1) { if (id < TWITTER_LOG_LENGTH && td->log) id = td->log[id].id; } else if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && (tud = bu->data) && tud->last_id) id = tud->last_id; - else { - id = g_ascii_strtoull(cmd[1], NULL, 10); + else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1){ if (id < TWITTER_LOG_LENGTH && td->log) id = td->log[id].id; } @@ -517,7 +515,15 @@ static void twitter_handle_command(struct im_connection *ic, char *message) bee_user_t *bu = NULL; guint64 id = 0; - if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && + if (g_str_has_prefix(cmd[1], "#") && + sscanf(cmd[1] + 1, "%" G_GUINT64_FORMAT, &id) == 1 && + (id < TWITTER_LOG_LENGTH) && td->log) { + bu = td->log[id].bu; + if (g_slist_find(ic->bee->users, bu)) + id = td->log[id].id; + else + bu = NULL; + } else if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && (tud = bu->data) && tud->last_id) { id = tud->last_id; } else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1 && @@ -528,6 +534,7 @@ static void twitter_handle_command(struct im_connection *ic, char *message) else bu = NULL; } + if (!id || !bu) { twitter_msg(ic, "User `%s' does not exist or didn't " "post any statuses recently", cmd[1]); -- cgit v1.2.3 From 2322a9f38a2f6c9bf86eb62c2cd68fd3848b694f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 25 Aug 2011 20:08:07 +0200 Subject: Merging Twitter-mentions patch from meh. Bug #663. --- protocols/twitter/twitter.c | 37 +++-- protocols/twitter/twitter.h | 12 +- protocols/twitter/twitter_lib.c | 302 ++++++++++++++++++++++++++++++++-------- protocols/twitter/twitter_lib.h | 2 + 4 files changed, 282 insertions(+), 71 deletions(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 175b1d0d..ac180250 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -29,12 +29,12 @@ #include "url.h" #define twitter_msg( ic, fmt... ) \ - do { \ - struct twitter_data *td = ic->proto_data; \ - if( td->home_timeline_gc ) \ - imcb_chat_log( td->home_timeline_gc, fmt ); \ - else \ - imcb_log( ic, fmt ); \ + do { \ + struct twitter_data *td = ic->proto_data; \ + if( td->timeline_gc ) \ + imcb_chat_log( td->timeline_gc, fmt ); \ + else \ + imcb_log( ic, fmt ); \ } while( 0 ); GSList *twitter_connections = NULL; @@ -51,7 +51,7 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) return 0; // Do stuff.. - twitter_get_home_timeline(ic, -1); + twitter_get_timeline(ic, -1); // If we are still logged in run this function again after timeout. return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN; @@ -68,7 +68,8 @@ static void twitter_main_loop_start(struct im_connection *ic) // Queue the main_loop // Save the return value, so we can remove the timeout on logout. - td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); + td->main_loop_id = + b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, twitter_main_loop, ic); } static void twitter_oauth_start(struct im_connection *ic); @@ -77,6 +78,8 @@ void twitter_login_finish(struct im_connection *ic) { struct twitter_data *td = ic->proto_data; + td->flags &= ~TWITTER_DOING_TIMELINE; + if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) twitter_oauth_start(ic); else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") != 0 && @@ -215,7 +218,6 @@ static void twitter_init(account_t * acc) def_url = TWITTER_API_URL; def_oauth = "true"; } else { /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ - def_url = IDENTICA_API_URL; def_oauth = "false"; } @@ -227,6 +229,11 @@ static void twitter_init(account_t * acc) s = set_add(&acc->set, "commands", "true", set_eval_bool, acc); + s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc); + s->flags |= ACC_SET_OFFLINE_ONLY; + + s = set_add(&acc->set, "fetch_mentions", "true", set_eval_bool, acc); + s = set_add(&acc->set, "message_length", "140", set_eval_int, acc); s = set_add(&acc->set, "mode", "chat", set_eval_mode, acc); @@ -235,6 +242,8 @@ static void twitter_init(account_t * acc) s = set_add(&acc->set, "show_ids", "false", set_eval_bool, acc); s->flags |= ACC_SET_OFFLINE_ONLY; + s = set_add(&acc->set, "show_old_mentions", "true", set_eval_bool, acc); + s = set_add(&acc->set, "oauth", def_oauth, set_eval_bool, acc); } @@ -316,8 +325,8 @@ static void twitter_logout(struct im_connection *ic) // Remove the main_loop function from the function queue. b_event_remove(td->main_loop_id); - if (td->home_timeline_gc) - imcb_chat_free(td->home_timeline_gc); + if (td->timeline_gc) + imcb_chat_free(td->timeline_gc); if (td) { oauth_info_free(td->oauth_info); @@ -403,13 +412,13 @@ static void twitter_chat_leave(struct groupchat *c) { struct twitter_data *td = c->ic->proto_data; - if (c != td->home_timeline_gc) + if (c != td->timeline_gc) return; /* WTF? */ /* If the user leaves the channel: Fine. Rejoin him/her once new tweets come in. */ - imcb_chat_free(td->home_timeline_gc); - td->home_timeline_gc = NULL; + imcb_chat_free(td->timeline_gc); + td->timeline_gc = NULL; } static void twitter_keepalive(struct im_connection *ic) diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index c38d9b86..14e43824 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -35,6 +35,9 @@ typedef enum { TWITTER_HAVE_FRIENDS = 1, + TWITTER_DOING_TIMELINE = 0x10000, + TWITTER_GOT_TIMELINE = 0x20000, + TWITTER_GOT_MENTIONS = 0x40000, } twitter_flags_t; struct twitter_log_data; @@ -43,12 +46,17 @@ struct twitter_data { char* user; struct oauth_info *oauth_info; + + gpointer home_timeline_obj; + gpointer mentions_obj; + + guint64 timeline_id; + GSList *follow_ids; - guint64 home_timeline_id; guint64 last_status_id; /* For undo */ gint main_loop_id; - struct groupchat *home_timeline_gc; + struct groupchat *timeline_gc; gint http_fails; twitter_flags_t flags; diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 14e98c53..805ff5aa 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -77,17 +77,20 @@ static void txu_free(struct twitter_xml_user *txu) { if (txu == NULL) return; + g_free(txu->name); g_free(txu->screen_name); g_free(txu); } - /** * Frees a twitter_xml_status struct. */ static void txs_free(struct twitter_xml_status *txs) { + if (txs == NULL) + return; + g_free(txs->text); txu_free(txs->user); g_free(txs); @@ -102,19 +105,40 @@ static void txl_free(struct twitter_xml_list *txl) GSList *l; if (txl == NULL) return; - for (l = txl->list; l; l = g_slist_next(l)) - if (txl->type == TXL_STATUS) + + for (l = txl->list; l; l = g_slist_next(l)) { + if (txl->type == TXL_STATUS) { txs_free((struct twitter_xml_status *) l->data); - else if (txl->type == TXL_ID) + } else if (txl->type == TXL_ID) { g_free(l->data); - else if (txl->type == TXL_USER) + } else if (txl->type == TXL_USER) { txu_free(l->data); + } + } + g_slist_free(txl->list); g_free(txl); } /** - * Add a buddy if it is not allready added, set the status to logged in. + * Compare status elements + */ +static gint twitter_compare_elements(gconstpointer a, gconstpointer b) +{ + struct twitter_xml_status *a_status = (struct twitter_xml_status *) a; + struct twitter_xml_status *b_status = (struct twitter_xml_status *) b; + + if (a_status->created_at < b_status->created_at) { + return -1; + } else if (a_status->created_at > b_status->created_at) { + return 1; + } else { + return 0; + } +} + +/** + * Add a buddy if it is not already added, set the status to logged in. */ static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname) { @@ -131,7 +155,7 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * /* Necessary so that nicks always get translated to the exact Twitter username. */ imcb_buddy_nick_hint(ic, name, name); - imcb_chat_add_buddy(td->home_timeline_gc, name); + imcb_chat_add_buddy(td->timeline_gc, name); } else if (g_strcasecmp(mode, "many") == 0) imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); } @@ -259,7 +283,7 @@ static void twitter_http_get_friends_ids(struct http_request *req) } /* Create the room now that we "logged in". */ - if (!td->home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) + if (!td->timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) twitter_groupchat_init(ic); txl = g_new0(struct twitter_xml_list, 1); @@ -521,32 +545,6 @@ static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_ return XT_HANDLED; } -static void twitter_http_get_home_timeline(struct http_request *req); - -/** - * Get the timeline. - */ -void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) -{ - struct twitter_data *td = ic->proto_data; - - char *args[4]; - args[0] = "cursor"; - args[1] = g_strdup_printf("%lld", (long long) next_cursor); - if (td->home_timeline_id) { - args[2] = "since_id"; - args[3] = g_strdup_printf("%llu", (long long unsigned int) td->home_timeline_id); - } - - twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, - td->home_timeline_id ? 4 : 2); - - g_free(args[1]); - if (td->home_timeline_id) { - g_free(args[3]); - } -} - static char *twitter_msg_add_id(struct im_connection *ic, struct twitter_xml_status *txs, const char *prefix) { @@ -585,7 +583,7 @@ static void twitter_groupchat_init(struct im_connection *ic) struct twitter_data *td = ic->proto_data; GSList *l; - td->home_timeline_gc = gc = imcb_chat_new(ic, "home/timeline"); + td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline"); name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user); imcb_chat_name_hint(gc, name_hint); @@ -594,7 +592,7 @@ static void twitter_groupchat_init(struct im_connection *ic) for (l = ic->bee->users; l; l = l->next) { bee_user_t *bu = l->data; if (bu->ic == ic) - imcb_chat_add_buddy(td->home_timeline_gc, bu->handle); + imcb_chat_add_buddy(td->timeline_gc, bu->handle); } } @@ -607,12 +605,13 @@ static void twitter_groupchat(struct im_connection *ic, GSList * list) GSList *l = NULL; struct twitter_xml_status *status; struct groupchat *gc; + guint64 last_id = 0; // Create a new groupchat if it does not exsist. - if (!td->home_timeline_gc) + if (!td->timeline_gc) twitter_groupchat_init(ic); - gc = td->home_timeline_gc; + gc = td->timeline_gc; if (!gc->joined) imcb_chat_add_buddy(gc, ic->acc->user); @@ -620,26 +619,30 @@ static void twitter_groupchat(struct im_connection *ic, GSList * list) char *msg; status = l->data; - if (status->user == NULL || status->text == NULL) + if (status->user == NULL || status->text == NULL || last_id == status->id) continue; - twitter_add_buddy(ic, status->user->screen_name, status->user->name); + last_id = status->id; strip_html(status->text); + msg = twitter_msg_add_id(ic, status, ""); // Say it! - if (g_strcasecmp(td->user, status->user->screen_name) == 0) + if (g_strcasecmp(td->user, status->user->screen_name) == 0) { imcb_chat_log(gc, "You: %s", msg ? msg : status->text); - else + } else { + twitter_add_buddy(ic, status->user->screen_name, status->user->name); + imcb_chat_msg(gc, status->user->screen_name, msg ? msg : status->text, 0, status->created_at); + } g_free(msg); - // Update the home_timeline_id to hold the highest id, so that by the next request + // Update the timeline_id to hold the highest id, so that by the next request // we won't pick up the updates already in the list. - td->home_timeline_id = MAX(td->home_timeline_id, status->id); + td->timeline_id = MAX(td->timeline_id, status->id); } } @@ -653,6 +656,7 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list struct twitter_xml_status *status; char from[MAX_STRING]; gboolean mode_one; + guint64 last_id = 0; mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0; @@ -665,6 +669,10 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list char *prefix = NULL, *text = NULL; status = l->data; + if (status->user == NULL || status->text == NULL || last_id == status->id) + continue; + + last_id = status->id; strip_html(status->text); if (mode_one) @@ -679,15 +687,150 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list mode_one ? from : status->user->screen_name, text ? text : status->text, 0, status->created_at); - // Update the home_timeline_id to hold the highest id, so that by the next request + // Update the timeline_id to hold the highest id, so that by the next request // we won't pick up the updates already in the list. - td->home_timeline_id = MAX(td->home_timeline_id, status->id); + td->timeline_id = MAX(td->timeline_id, status->id); g_free(text); g_free(prefix); } } +static void twitter_http_get_home_timeline(struct http_request *req); +static void twitter_http_get_mentions(struct http_request *req); + +/** + * Get the timeline with optionally mentions + */ +void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) +{ + struct twitter_data *td = ic->proto_data; + gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions"); + + if (td->flags & TWITTER_DOING_TIMELINE) { + return; + } + + td->flags |= TWITTER_DOING_TIMELINE; + + twitter_get_home_timeline(ic, next_cursor); + + if (include_mentions) { + twitter_get_mentions(ic, next_cursor); + } +} + +/** + * Call this one after receiving timeline/mentions. Show to user once we have + * both. + */ +void twitter_flush_timeline(struct im_connection *ic) +{ + struct twitter_data *td = ic->proto_data; + gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions"); + gboolean show_old_mentions = set_getbool(&ic->acc->set, "show_old_mentions"); + struct twitter_xml_list *home_timeline = td->home_timeline_obj; + struct twitter_xml_list *mentions = td->mentions_obj; + GSList *output = NULL; + GSList *l; + + if (!(td->flags & TWITTER_GOT_TIMELINE)) { + return; + } + + if (include_mentions && !(td->flags & TWITTER_GOT_MENTIONS)) { + return; + } + + if (home_timeline && home_timeline->list) { + for (l = home_timeline->list; l; l = g_slist_next(l)) { + output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); + } + } + + if (include_mentions && mentions && mentions->list) { + for (l = mentions->list; l; l = g_slist_next(l)) { + if (!show_old_mentions && output && twitter_compare_elements(l->data, output->data) < 0) { + continue; + } + + output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); + } + } + + // See if the user wants to see the messages in a groupchat window or as private messages. + if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) + twitter_groupchat(ic, output); + else + twitter_private_message_chat(ic, output); + + g_slist_free(output); + + if (home_timeline && home_timeline->list) { + txl_free(home_timeline); + } + + if (mentions && mentions->list) { + txl_free(mentions); + } + + td->flags &= ~(TWITTER_DOING_TIMELINE | TWITTER_GOT_TIMELINE | TWITTER_GOT_MENTIONS); +} + +/** + * Get the timeline. + */ +void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) +{ + struct twitter_data *td = ic->proto_data; + + td->home_timeline_obj = NULL; + td->flags &= ~TWITTER_GOT_TIMELINE; + + char *args[4]; + args[0] = "cursor"; + args[1] = g_strdup_printf("%lld", (long long) next_cursor); + if (td->timeline_id) { + args[2] = "since_id"; + args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); + } + + twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, + td->timeline_id ? 4 : 2); + + g_free(args[1]); + if (td->timeline_id) { + g_free(args[3]); + } +} + +/** + * Get mentions. + */ +void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) +{ + struct twitter_data *td = ic->proto_data; + + td->mentions_obj = NULL; + td->flags &= ~TWITTER_GOT_MENTIONS; + + char *args[4]; + args[0] = "cursor"; + args[1] = g_strdup_printf("%lld", (long long) next_cursor); + if (td->timeline_id) { + args[2] = "since_id"; + args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); + } + + twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args, + td->timeline_id ? 4 : 2); + + g_free(args[1]); + if (td->timeline_id) { + g_free(args[3]); + } +} + /** * Callback for getting the home timeline. */ @@ -712,14 +855,66 @@ static void twitter_http_get_home_timeline(struct http_request *req) } else if (req->status_code == 401) { imcb_error(ic, "Authentication failure"); imc_logout(ic, FALSE); - return; + goto end; } else { // It didn't go well, output the error and return. if (++td->http_fails >= 5) imcb_error(ic, "Could not retrieve %s: %s", TWITTER_HOME_TIMELINE_URL, twitter_parse_error(req)); + goto end; + } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + + // Parse the data. + parser = xt_new(NULL, txl); + xt_feed(parser, req->reply_body, req->body_size); + // The root node should hold the list of statuses + twitter_xt_get_status_list(ic, parser->root, txl); + xt_free(parser); + + td->home_timeline_obj = txl; + + end: + td->flags |= TWITTER_GOT_TIMELINE; + + twitter_flush_timeline(ic); +} + +/** + * Callback for getting mentions. + */ +static void twitter_http_get_mentions(struct http_request *req) +{ + struct im_connection *ic = req->data; + struct twitter_data *td; + struct xt_parser *parser; + struct twitter_xml_list *txl; + + // Check if the connection is still active. + if (!g_slist_find(twitter_connections, ic)) return; + + td = ic->proto_data; + + // Check if the HTTP request went well. + if (req->status_code == 200) { + td->http_fails = 0; + if (!(ic->flags & OPT_LOGGED_IN)) + imcb_connected(ic); + } else if (req->status_code == 401) { + imcb_error(ic, "Authentication failure"); + imc_logout(ic, FALSE); + goto end; + } else { + // It didn't go well, output the error and return. + if (++td->http_fails >= 5) + imcb_error(ic, "Could not retrieve " TWITTER_MENTIONS_URL ": %s", + twitter_parse_error(req)); + + goto end; } txl = g_new0(struct twitter_xml_list, 1); @@ -732,15 +927,12 @@ static void twitter_http_get_home_timeline(struct http_request *req) twitter_xt_get_status_list(ic, parser->root, txl); xt_free(parser); - // See if the user wants to see the messages in a groupchat window or as private messages. - if (txl->list == NULL); - else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) - twitter_groupchat(ic, txl->list); - else - twitter_private_message_chat(ic, txl->list); + td->mentions_obj = txl; - // Free the structure. - txl_free(txl); + end: + td->flags |= TWITTER_GOT_MENTIONS; + + twitter_flush_timeline(ic); } /** diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index c33b2dfc..b06f5055 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -75,8 +75,10 @@ #define TWITTER_BLOCKS_CREATE_URL "/blocks/create/" #define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/" +void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor); void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor); void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); +void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor); void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to); -- cgit v1.2.3 From 429a9b17fb256e5fc750104f4c5ffb2dc7fba74b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Aug 2011 22:16:17 +0200 Subject: Since t.co is all over Twitter now, start parsing and showing entity information. I'm going for this way of rendering even though full URLs are also available to me because A) it puts me on the safe side wrt Twitter ToS and B) some full URLS may really be very long. Noticed some problem with truncated RT statuses not getting fixed but this seems to be unrelated. (This is a truncated RT status without the "truncated" property set.) Bug #821. --- protocols/twitter/twitter_lib.c | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 805ff5aa..7f2fb811 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -498,6 +498,27 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ g_free(txs->text); txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); txs_free(rtxs); + } else { + struct xt_node *urls, *url; + + urls = xt_find_path(node, "entities/urls"); + for (url = urls ? urls->children : NULL; url; url = url->next) { + /* "short" is a reserved word. :-P */ + struct xt_node *kort = xt_find_node(url->children, "url"); + struct xt_node *disp = xt_find_node(url->children, "display_url"); + char *pos, *new; + + if (!kort || !kort->text || !disp || !disp->text || + !(pos = strstr(txs->text, kort->text))) + continue; + + *pos = '\0'; + new = g_strdup_printf("%s%s <%s>%s", txs->text, kort->text, + disp->text, pos + strlen(kort->text)); + + g_free(txs->text); + txs->text = new; + } } return XT_HANDLED; @@ -787,20 +808,22 @@ void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) td->home_timeline_obj = NULL; td->flags &= ~TWITTER_GOT_TIMELINE; - char *args[4]; + char *args[6]; args[0] = "cursor"; args[1] = g_strdup_printf("%lld", (long long) next_cursor); + args[2] = "include_entities"; + args[3] = "true"; if (td->timeline_id) { - args[2] = "since_id"; - args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); + args[4] = "since_id"; + args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); } twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, - td->timeline_id ? 4 : 2); + td->timeline_id ? 6 : 4); g_free(args[1]); if (td->timeline_id) { - g_free(args[3]); + g_free(args[5]); } } @@ -814,20 +837,22 @@ void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) td->mentions_obj = NULL; td->flags &= ~TWITTER_GOT_MENTIONS; - char *args[4]; + char *args[6]; args[0] = "cursor"; args[1] = g_strdup_printf("%lld", (long long) next_cursor); + args[2] = "include_entities"; + args[3] = "true"; if (td->timeline_id) { - args[2] = "since_id"; - args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); + args[4] = "since_id"; + args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); } twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args, - td->timeline_id ? 4 : 2); + td->timeline_id ? 6 : 4); g_free(args[1]); if (td->timeline_id) { - g_free(args[3]); + g_free(args[5]); } } -- cgit v1.2.3 From 0fff0b22db8860d3e6165f381028b441f26a31fe Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Aug 2011 12:18:45 +0200 Subject: Just reconstruct all retweets instead of just the ones marked as truncated. --- protocols/twitter/twitter_lib.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 7f2fb811..4c680930 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -459,14 +459,11 @@ static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_l static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_status *txs) { struct xt_node *child, *rt = NULL; - gboolean truncated = FALSE; // Walk over the nodes children. for (child = node->children; child; child = child->next) { if (g_strcasecmp("text", child->name) == 0) { txs->text = g_memdup(child->text, child->text_len + 1); - } else if (g_strcasecmp("truncated", child->name) == 0 && child->text) { - truncated = bool2int(child->text); } else if (g_strcasecmp("retweeted_status", child->name) == 0) { rt = child; } else if (g_strcasecmp("created_at", child->name) == 0) { @@ -487,8 +484,9 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ } } - /* If it's a truncated retweet, get the original because dots suck. */ - if (truncated && rt) { + /* If it's a (truncated) retweet, get the original. Even if the API claims it + wasn't truncated because it may be lying. */ + if (rt) { struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1); if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) { txs_free(rtxs); -- cgit v1.2.3 From 733f607bfaa34aa949df2602aec3b46aa2c3a46f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 17 Oct 2011 23:58:14 -0400 Subject: Stupid work-around for bug #838. The troublesome condition is known, I just don't know exactly how BitlBee gets into it. Any more info from people who see this problem often would be useful. --- protocols/twitter/twitter.h | 1 + protocols/twitter/twitter_lib.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 14e43824..47dbeb29 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -38,6 +38,7 @@ typedef enum TWITTER_DOING_TIMELINE = 0x10000, TWITTER_GOT_TIMELINE = 0x20000, TWITTER_GOT_MENTIONS = 0x40000, + TWITTER_DOING_TIMELINE_SLOW = 0x80000, } twitter_flags_t; struct twitter_log_data; diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 4c680930..dd9c3ac6 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -726,6 +726,13 @@ void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) struct twitter_data *td = ic->proto_data; gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions"); + if ((td->flags & 0xf0000) == (TWITTER_DOING_TIMELINE | TWITTER_DOING_TIMELINE_SLOW)) { + imcb_log(ic, "Connection seems to have stalled again.\n" + "This is a known bug, if you see this happen a lot " + "please generate some traffic dumps."); + td->flags &= ~0xf0000; + } + if (td->flags & TWITTER_DOING_TIMELINE) { return; } -- cgit v1.2.3 From 32bea82d26872395634862204f551644233454ae Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 19 Oct 2011 22:59:57 -0400 Subject: changeset:devel,814 was silly and incomplete. This should fix that. Full workaround for Twitter "hang" problem. --- protocols/twitter/twitter_lib.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index dd9c3ac6..00e3a2ee 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -734,6 +734,10 @@ void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) } if (td->flags & TWITTER_DOING_TIMELINE) { + /* This shouldn't normally happen at all but I'm currently hunting a bug + where it does. Instead of having users suffer under it, have a work- + around with a warning. */ + td->flags |= TWITTER_DOING_TIMELINE_SLOW; return; } -- cgit v1.2.3 From 90fc864b79ba673623ce96e3e89866e48bc32d7f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 24 Nov 2011 08:18:04 +0000 Subject: Hopefully this fixed #838. Patch from Artem Savkov, thanks! The problem was HTTP failures caused by (most likely) DNS resolution problems. Yes, ignoring return values is still bad, kids! --- protocols/twitter/twitter.h | 1 - protocols/twitter/twitter_lib.c | 35 ++++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 47dbeb29..14e43824 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -38,7 +38,6 @@ typedef enum TWITTER_DOING_TIMELINE = 0x10000, TWITTER_GOT_TIMELINE = 0x20000, TWITTER_GOT_MENTIONS = 0x40000, - TWITTER_DOING_TIMELINE_SLOW = 0x80000, } twitter_flags_t; struct twitter_log_data; diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 00e3a2ee..d52c29ff 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -726,18 +726,7 @@ void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) struct twitter_data *td = ic->proto_data; gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions"); - if ((td->flags & 0xf0000) == (TWITTER_DOING_TIMELINE | TWITTER_DOING_TIMELINE_SLOW)) { - imcb_log(ic, "Connection seems to have stalled again.\n" - "This is a known bug, if you see this happen a lot " - "please generate some traffic dumps."); - td->flags &= ~0xf0000; - } - if (td->flags & TWITTER_DOING_TIMELINE) { - /* This shouldn't normally happen at all but I'm currently hunting a bug - where it does. Instead of having users suffer under it, have a work- - around with a warning. */ - td->flags |= TWITTER_DOING_TIMELINE_SLOW; return; } @@ -827,8 +816,14 @@ void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); } - twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, - td->timeline_id ? 6 : 4); + if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, + td->timeline_id ? 6 : 4) == NULL) { + if (++td->http_fails >= 5) + imcb_error(ic, "Could not retrieve %s: %s", + TWITTER_HOME_TIMELINE_URL, "connection failed"); + td->flags |= TWITTER_GOT_TIMELINE; + twitter_flush_timeline(ic); + } g_free(args[1]); if (td->timeline_id) { @@ -856,8 +851,14 @@ void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id); } - twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args, - td->timeline_id ? 6 : 4); + if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args, + td->timeline_id ? 6 : 4) == NULL) { + if (++td->http_fails >= 5) + imcb_error(ic, "Could not retrieve %s: %s", + TWITTER_MENTIONS_URL, "connection failed"); + td->flags |= TWITTER_GOT_MENTIONS; + twitter_flush_timeline(ic); + } g_free(args[1]); if (td->timeline_id) { @@ -945,8 +946,8 @@ static void twitter_http_get_mentions(struct http_request *req) } else { // It didn't go well, output the error and return. if (++td->http_fails >= 5) - imcb_error(ic, "Could not retrieve " TWITTER_MENTIONS_URL ": %s", - twitter_parse_error(req)); + imcb_error(ic, "Could not retrieve %s: %s", + TWITTER_MENTIONS_URL, twitter_parse_error(req)); goto end; } -- cgit v1.2.3 From 3f808ca58e45f2a305e01471cc514957039a865d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Dec 2011 16:38:02 +0000 Subject: Support HTTP/1.1 redirect status codes and use HTTPS for OAuth setup. This is required for identi.ca and really should be done for Twitter as well. Twitter OAuth is still broken though, it seems to disagree about signatures. --- protocols/twitter/twitter.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'protocols/twitter') diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index ac180250..76ccc3eb 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -92,16 +92,16 @@ void twitter_login_finish(struct im_connection *ic) } static const struct oauth_service twitter_oauth = { - "http://api.twitter.com/oauth/request_token", - "http://api.twitter.com/oauth/access_token", + "https://api.twitter.com/oauth/request_token", + "https://api.twitter.com/oauth/access_token", "https://api.twitter.com/oauth/authorize", .consumer_key = "xsDNKJuNZYkZyMcu914uEA", .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo", }; static const struct oauth_service identica_oauth = { - "http://identi.ca/api/oauth/request_token", - "http://identi.ca/api/oauth/access_token", + "https://identi.ca/api/oauth/request_token", + "https://identi.ca/api/oauth/access_token", "https://identi.ca/api/oauth/authorize", .consumer_key = "e147ff789fcbd8a5a07963afbb43f9da", .consumer_secret = "c596267f277457ec0ce1ab7bb788d828", -- cgit v1.2.3