diff options
Diffstat (limited to 'protocols/twitter/twitter_lib.c')
-rw-r--r-- | protocols/twitter/twitter_lib.c | 132 |
1 files changed, 118 insertions, 14 deletions
diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d1b65c26..f86e1f15 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -22,7 +22,10 @@ ****************************************************************************/ /* For strptime(): */ +#if(__sun) +#else #define _XOPEN_SOURCE +#endif #include "twitter_http.h" #include "twitter.h" @@ -35,6 +38,14 @@ #include <ctype.h> #include <errno.h> +/* GLib < 2.12.0 doesn't have g_ascii_strtoll(), work around using system strtoll(). */ +/* GLib < 2.12.4 can be buggy: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=488013 */ +#if !GLIB_CHECK_VERSION(2,12,5) +#include <stdlib.h> +#include <limits.h> +#define g_ascii_strtoll strtoll +#endif + #define TXL_STATUS 1 #define TXL_USER 2 #define TXL_ID 3 @@ -65,6 +76,8 @@ static void twitter_groupchat_init(struct im_connection *ic); */ 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); @@ -88,6 +101,8 @@ static void txs_free(struct twitter_xml_status *txs) 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) txs_free((struct twitter_xml_status *)l->data); @@ -104,7 +119,7 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * struct twitter_data *td = ic->proto_data; // Check if the buddy is allready in the buddy list. - if (!imcb_find_buddy( ic, name )) + if (!bee_user_by_handle( ic->bee, ic, name )) { char *mode = set_getstr(&ic->acc->set, "mode"); @@ -112,7 +127,12 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * imcb_add_buddy( ic, name, NULL ); imcb_rename_buddy( ic, name, fullname ); if (g_strcasecmp(mode, "chat") == 0) + { + /* 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 ); + } else if (g_strcasecmp(mode, "many") == 0) imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); } @@ -336,6 +356,11 @@ static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_ return XT_HANDLED; } +#ifdef __GLIBC__ +#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S %z %Y" +#else +#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y" +#endif /** * Function to fill a twitter_xml_status struct. @@ -347,7 +372,8 @@ static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_ */ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs ) { - struct xt_node *child; + struct xt_node *child, *rt = NULL; + gboolean truncated = FALSE; // Walk over the nodes children. for( child = node->children ; child ; child = child->next ) @@ -356,6 +382,14 @@ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml { 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) { struct tm parsed; @@ -363,7 +397,7 @@ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml /* Very sensitive to changes to the formatting of this field. :-( Also assumes the timezone used is UTC since C time handling functions suck. */ - if( strptime( child->text, "%a %b %d %H:%M:%S %z %Y", &parsed ) != NULL ) + if( strptime( child->text, TWITTER_TIME_FORMAT, &parsed ) != NULL ) txs->created_at = mktime_utc( &parsed ); } else if (g_strcasecmp( "user", child->name ) == 0) @@ -376,6 +410,22 @@ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml txs->id = g_ascii_strtoull (child->text, NULL, 10); } } + + /* If it's a truncated retweet, get the original because dots suck. */ + if (truncated && 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); + return XT_HANDLED; + } + + g_free(txs->text); + txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); + txs_free(rtxs); + } + return XT_HANDLED; } @@ -385,10 +435,11 @@ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml * - all <status>es within the <status> element and * - the next_cursor. */ -static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitter_xml_list *txl ) +static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt_node *node, struct twitter_xml_list *txl ) { struct twitter_xml_status *txs; struct xt_node *child; + bee_user_t *bu; // Set the type of the list. txl->type = TXL_STATUS; @@ -403,6 +454,18 @@ static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitte twitter_xt_get_status(child, txs); // Put the item in the front of the list. txl->list = g_slist_prepend (txl->list, txs); + + if (txs->user && txs->user->screen_name && + (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) + { + struct twitter_user_data *tud = bu->data; + + if (txs->id > tud->last_id) + { + tud->last_id = txs->id; + tud->last_time = txs->created_at; + } + } } else if ( g_strcasecmp( "next_cursor", child->name ) == 0) { @@ -446,7 +509,7 @@ static void twitter_groupchat_init(struct im_connection *ic) td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); - name_hint = g_strdup_printf( "Twitter_%s", ic->acc->user ); + name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user ); imcb_chat_name_hint( gc, name_hint ); g_free( name_hint ); } @@ -472,6 +535,9 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list) for ( l = list; l ; l = g_slist_next(l) ) { status = l->data; + if (status->user == NULL || status->text == NULL) + continue; + twitter_add_buddy(ic, status->user->screen_name, status->user->name); strip_html(status->text); @@ -503,7 +569,7 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) if( mode_one ) { - g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user ); + g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user ); from[MAX_STRING-1] = '\0'; } @@ -578,7 +644,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) parser = xt_new( NULL, txl ); xt_feed( parser, req->reply_body, req->body_size ); // The root <statuses> node should hold the list of statuses <status> - twitter_xt_get_status_list(parser->root, txl); + 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. @@ -687,29 +753,51 @@ void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor) static void twitter_http_post(struct http_request *req) { struct im_connection *ic = req->data; + struct twitter_data *td; // Check if the connection is still active. if( !g_slist_find( twitter_connections, ic ) ) return; + td = ic->proto_data; + td->last_status_id = 0; + // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. imcb_error(ic, "HTTP error: %s", twitter_parse_error(req)); return; } + + if (req->body_size > 0) + { + struct xt_parser *xp = NULL; + struct xt_node *node; + + xp = xt_new(NULL, NULL); + xt_feed(xp, req->reply_body, req->body_size); + + if ((node = xt_find_node(xp->root, "status")) && + (node = xt_find_node(node->children, "id")) && node->text) + td->last_status_id = g_ascii_strtoull( node->text, NULL, 10 ); + + xt_free(xp); + } } /** * Function to POST a new status to twitter. */ -void twitter_post_status(struct im_connection *ic, char* msg) +void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to) { - char* args[2]; - args[0] = "status"; - args[1] = msg; - twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1, args, 2); -// g_free(args[1]); + char* args[4] = { + "status", msg, + "in_reply_to_status_id", + g_strdup_printf("%llu", (unsigned long long) in_reply_to) + }; + twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1, + args, in_reply_to ? 4 : 2); + g_free(args[3]); } @@ -735,4 +823,20 @@ void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int args[0] = "screen_name"; args[1] = who; twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL, twitter_http_post, ic, 1, args, 2); -}
\ No newline at end of file +} + +void twitter_status_destroy(struct im_connection *ic, guint64 id) +{ + char *url; + url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id, ".xml"); + twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); + g_free(url); +} + +void twitter_status_retweet(struct im_connection *ic, guint64 id) +{ + char *url; + url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id, ".xml"); + twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); + g_free(url); +} |