# HG changeset patch # User Yoshiki Yazawa # Date 1255351873 -32400 # Node ID cc41ee1f5d3ad83f8a4ba719777eb794b2226b1f # Parent 300241bd18793c3b8c99993b78428a01b476bd8d implemented reply, favorite, retweet functionalities. these are quite raw, be careful! diff -r 300241bd1879 -r cc41ee1f5d3a main.c --- a/main.c Mon Oct 12 01:45:36 2009 +0900 +++ b/main.c Mon Oct 12 21:51:13 2009 +0900 @@ -31,6 +31,8 @@ static GList *wassr_parrot_list = NULL; static GList *identica_parrot_list = NULL; static GList *ffeed_parrot_list = NULL; +guint64 reply_to_msgid = 0; +PurpleAccount *account_for_twitter = NULL; #ifdef _WIN32 gboolean blink_state = FALSE; gboolean blink_modified = FALSE; @@ -63,6 +65,7 @@ #ifndef _WIN32 extern gchar *sanitize_utf(const gchar *msg, gsize len, gsize *newlen) __attribute__ ((weak)); #endif +gboolean pt_uri_handler(const char *proto, const char *cmd, GHashTable *params); /*************/ @@ -392,7 +395,7 @@ g_free(*buffer); *buffer = m; } - } + } /* send */ /* strip all markups */ strip_markup(buffer, TRUE); @@ -405,6 +408,17 @@ playsound(buffer, RECIPIENT); } + if(service == twitter_service) { + /* escape pseudo command (to show the same result as + sending message) */ + if(purple_prefs_get_bool(OPT_ESCAPE_PSEUDO)) { + escape(buffer); + } + + /* replace ptmsgid=123 with appropriate links */ + twitter_add_links(buffer); + } + /* translate */ if(purple_prefs_get_bool(OPT_TRANSLATE_SENDER)) { if(service == ffeed_service) @@ -432,11 +446,18 @@ translate(buffer, GROUP_IDENTICA, service); } - /* escape pseudo command (to show the same result as sending message) */ - if(service == twitter_service && - purple_prefs_get_bool(OPT_ESCAPE_PSEUDO)) { - escape(buffer); +#if 0 + if(service == twitter_service) { + /* escape pseudo command (to show the same result as + sending message) */ + if(purple_prefs_get_bool(OPT_ESCAPE_PSEUDO)) { + escape(buffer); + } + + /* replace ptmsgid=123 with appropriate links */ + twitter_add_links(buffer); } +#endif if(purple_prefs_get_bool(OPT_STRIP_EXCESS_LF)) { translate(buffer, EXCESS_LF, service); @@ -528,6 +549,9 @@ break; } + if(count == 0) + reply_to_msgid = 0; + box = gtkconv->toolbar; counter = g_object_get_data(G_OBJECT(box), PLUGIN_ID "-counter"); if(counter) @@ -760,6 +784,7 @@ get_status_with_api, (gpointer)conv); source.conv = conv; attach_to_conv(conv, NULL); + account_for_twitter = conv->account; /* xxx */ break; case wassr_service: case identica_service: @@ -1160,6 +1185,22 @@ } static gboolean +pt_url_clicked_cb(GtkIMHtml *imhtml, GtkIMHtmlLink *link) +{ + const gchar * url = gtk_imhtml_link_get_url(link); + + purple_got_protocol_handler_uri(url); + + return TRUE; +} + +static gboolean +pt_url_context_menu_cb(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu) +{ + return TRUE; +} + +static gboolean load_plugin(PurplePlugin *plugin) { int i; @@ -1191,6 +1232,14 @@ "signed-on", plugin, PURPLE_CALLBACK(signed_on_cb), NULL); + gtk_imhtml_class_register_protocol("PT://", + pt_url_clicked_cb, + pt_url_context_menu_cb); + purple_signal_connect(purple_get_core(), + "uri-handler", + plugin, + PURPLE_CALLBACK(pt_uri_handler), NULL); + /* compile regex */ regp[RECIPIENT] = g_regex_new(P_RECIPIENT, 0, 0, NULL); @@ -1211,6 +1260,7 @@ regp[SIZE_128_WASSR] = g_regex_new(P_SIZE_128_WASSR, 0, 0, NULL); regp[EXCESS_LF] = g_regex_new(P_EXCESS_LF, 0, 0, NULL); regp[TRAIL_HASH] = g_regex_new(P_TRAIL_HASH, 0, 0, NULL); + regp[MESSAGE_ID] = g_regex_new(P_MESSAGE_ID, 0, 0, NULL); for(i = twitter_service; i < NUM_SERVICES; i++) { icon_hash[i] = g_hash_table_new_full(g_str_hash, g_str_equal, @@ -1289,6 +1339,13 @@ "signed-on", plugin, PURPLE_CALLBACK(signed_on_cb)); + gtk_imhtml_class_register_protocol("PT://", NULL, NULL); + + /* should do? --yaz */ + purple_signal_disconnect(purple_get_core(), + "uri-handler", + plugin, PURPLE_CALLBACK(pt_uri_handler)); + /* unreference regp */ for(i = 0; i < NUM_REGPS; i++) { g_regex_unref(regp[i]); diff -r 300241bd1879 -r cc41ee1f5d3a pidgin-twitter.h --- a/pidgin-twitter.h Mon Oct 12 01:45:36 2009 +0900 +++ b/pidgin-twitter.h Mon Oct 12 21:51:13 2009 +0900 @@ -23,6 +23,7 @@ #include #include #include +#include #include "util.h" #include "prefs.h" @@ -49,7 +50,8 @@ IMAGE_FFEED, SIZE_128_WASSR, EXCESS_LF, - TRAIL_HASH + TRAIL_HASH, + MESSAGE_ID }; /* service id */ @@ -170,6 +172,8 @@ #define TAG_FORMAT_TWITTER "%s#%s" #define TAG_FORMAT_IDENTICA "#%s" #define GROUP_FORMAT_IDENTICA "!%s" +#define LINK_FORMAT_TWITTER " R F RT" +#define TWITTER_REPLY_FORMAT "in_reply_to_status_id=%llu" #define DEFAULT_LIST "(list of users: separated with ' ,:;')" #define OOPS_MESSAGE "Oops! Your update was over 140 characters. We sent the short version to your friends (they can view the entire update on the web).
" @@ -194,6 +198,7 @@ #define P_SIZE_128_WASSR "\\.128\\." #define P_EXCESS_LF "([\\r|\\n]{2,})" #define P_TRAIL_HASH "( #\\s+$)" +#define P_MESSAGE_ID " ptmsgid=([0-9]+)$" /* twitter API specific macros */ #define TWITTER_BASE_URL "http://twitter.com" @@ -207,6 +212,13 @@ "Authorization: Basic %s\r\n" \ "Content-Length: %d\r\n" #define TWITTER_STATUS_FORMAT "status=%s&source=pidgintwitter" + +#define TWITTER_FAV_POST "POST /favorites/create/%llu.xml HTTP/1.1\r\n" \ + "Host: twitter.com\r\n" \ + "User-Agent: pidgin-twitter\r\n" \ + "Authorization: Basic %s\r\n" + + #define TWITTER_DEFAULT_INTERVAL (60) #define TWITTER_OLD_DEFAULT_ICON_URL "http://static.twitter.com/images/default_profile_bigger.png" #define TWITTER_DEFAULT_ICON_URL "http://s.twimg.com/images/default_profile_3_bigger.png" @@ -234,7 +246,7 @@ #define DEFAULT_ICON_MAX_COUNT (50) #define DEFAULT_ICON_MAX_DAYS (7) #define DAYS_TO_SECONDS(d) ((d) * 86400) -#define NUM_REGPS (18) +#define NUM_REGPS (19) #define NUM_SERVICES (5) /* twitter, wassr, identica, jisko, ffeed. */ /* debug macros */ diff -r 300241bd1879 -r cc41ee1f5d3a twitter_api.c --- a/twitter_api.c Mon Oct 12 01:45:36 2009 +0900 +++ b/twitter_api.c Mon Oct 12 21:51:13 2009 +0900 @@ -18,6 +18,9 @@ extern gboolean blink_modified; #endif +extern guint64 reply_to_msgid; +extern PurpleAccount *account_for_twitter; + /**************************/ /* API base get functions */ /**************************/ @@ -261,7 +264,9 @@ PurpleMessageFlags flag = PURPLE_MESSAGE_RECV; - msg = g_strdup_printf("%s: %s", st->screen_name, st->text); + msg = g_strdup_printf("%s: %s ptmsgid=%llu", + st->screen_name, st->text, + (long long unsigned int)st->id); /* apply filter */ if(purple_prefs_get_bool(OPT_FILTER)) { @@ -323,7 +328,6 @@ g_free(basic_auth); /* header */ - header = g_strdup_printf(TWITTER_STATUS_GET, count, basic_auth_encoded); request = g_strconcat(header, "\r\n", NULL); @@ -509,7 +513,18 @@ header = g_strdup_printf(TWITTER_STATUS_POST, basic_auth_encoded, (int)strlen(status)); - request = g_strconcat(header, "\r\n", status, "\r\n", NULL); + if(reply_to_msgid > 0) { + char *inreply = NULL; + inreply = g_strdup_printf(TWITTER_REPLY_FORMAT, + (long long unsigned int)reply_to_msgid); + request = g_strconcat(header, "\r\n", status, "\r\n", + inreply, "\r\n", NULL); + reply_to_msgid = 0; + g_free(inreply); + } + else { + request = g_strconcat(header, "\r\n", status, "\r\n", NULL); + } purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE, NULL, TRUE, request, TRUE, @@ -522,6 +537,50 @@ } +static void +fav_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *url_text, size_t len, + const gchar *error_message) +{ + /* dummy */ +} + +void +fav_with_api(guint64 id) +{ + char *header, *request; + char *basic_auth, *basic_auth_encoded; + + const char *screen_name = + purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER); + const char *password = purple_prefs_get_string(OPT_PASSWORD_TWITTER); + + if (!screen_name || !password || !screen_name[0] || !password[0]) { + twitter_debug("screen_name or password is empty\n"); + return; + } + + basic_auth = g_strdup_printf("%s:%s", screen_name, password); + basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth, + strlen(basic_auth)); + g_free(basic_auth); + + + header = g_strdup_printf(TWITTER_FAV_POST, + (long long unsigned int)id, + basic_auth_encoded); + request = g_strconcat(header, "\r\n", NULL); + + purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE, + NULL, TRUE, request, TRUE, + fav_with_api_cb, NULL); + twitter_debug("request = %s\n", request); + + g_free(header); + g_free(basic_auth_encoded); + g_free(request); +} + void signed_on_cb(PurpleConnection *gc) { @@ -547,6 +606,8 @@ return; } + account_for_twitter = account; + gconv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, name, account); if(!gconv) { diff -r 300241bd1879 -r cc41ee1f5d3a util.c --- a/util.c Mon Oct 12 01:45:36 2009 +0900 +++ b/util.c Mon Oct 12 21:51:13 2009 +0900 @@ -1,9 +1,12 @@ #include "pidgin-twitter.h" extern GRegex *regp[]; +extern guint64 reply_to_msgid; +extern PurpleAccount *account_for_twitter; /* prototypes */ static gchar *twitter_memrchr(const gchar *s, int c, size_t n); +void fav_with_api(guint64 id); /* functions */ @@ -432,3 +435,143 @@ return service; } + +gboolean +pt_uri_handler(const char *proto, const char *cmd, GHashTable *params) +{ + char *idstr = NULL; + const char *acct_id = NULL; + PurpleConversation *conv = NULL; + PidginConversation *gtkconv = NULL; + guint64 msgid = 0; + gchar *sender = NULL, *recipient = NULL, *msg = NULL; + + if(g_ascii_strcasecmp(proto, "pt")) + return FALSE; + + twitter_debug("params=%p\n", params); + + acct_id = purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER); + twitter_debug("acct_id=%s\n", acct_id); + + if(strstr(cmd, "reply-twitter")) { + sender = g_hash_table_lookup(params, "user"); + idstr = g_hash_table_lookup(params, "id"); + if(idstr) + msgid = strtoull(idstr, NULL, 10); + + /* find conv */ + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_ANY, "twitter@twitter.com", + account_for_twitter); /* xxx */ + twitter_debug("conv = %p\n", conv); + gtkconv = PIDGIN_CONVERSATION(conv); + + twitter_debug("sender = %s, id = %llu\n", sender, (unsigned long long)msgid); + + recipient = g_strdup_printf("@%s ", sender); + gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, + recipient, -1); + + gtk_widget_grab_focus(GTK_WIDGET(gtkconv->entry)); + g_free(recipient); + reply_to_msgid = msgid; /* xxx */ + + return TRUE; + } + else if(strstr(cmd, "fav-twitter")) { + idstr = g_hash_table_lookup(params, "id"); + fav_with_api(strtoull(idstr, NULL, 10)); + return TRUE; + } + else if(strstr(cmd, "retweet-twitter")) { + sender = g_hash_table_lookup(params, "user"); + idstr = g_hash_table_lookup(params, "id"); + msg = g_hash_table_lookup(params, "msg"); + if(idstr) + msgid = strtoull(idstr, NULL, 10); + + /* find conv */ + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_ANY, "twitter@twitter.com", + account_for_twitter); /* xxx */ + twitter_debug("conv = %p\n", conv); + gtkconv = PIDGIN_CONVERSATION(conv); + + twitter_debug("sender = %s, id = %llu\n", sender, (unsigned long long)msgid); + + recipient = g_strdup_printf("RT @%s: %s", sender, msg); + gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, + recipient, -1); + + GtkTextIter iter; + gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &iter); + gtk_text_buffer_place_cursor(gtkconv->entry_buffer, &iter); + + gtk_widget_grab_focus(GTK_WIDGET(gtkconv->entry)); + g_free(recipient); + + return TRUE; + } + return FALSE; +} + +void +twitter_add_links(gchar **str) +{ + GMatchInfo *match_info = NULL; + gchar *tmpstr0 = NULL, *tmpstr = NULL; + gchar *newstr = NULL, *match = NULL; + gchar *linkstr = NULL; + gchar *user = NULL; + + twitter_debug("called\n"); + + /* buffer without ptmsgid=123 */ + tmpstr0 = g_regex_replace(regp[SENDER], *str, -1, 0, "", 0, NULL); + tmpstr = g_regex_replace(regp[MESSAGE_ID], tmpstr0, -1, 0, "", 0, NULL); + g_free(tmpstr0); + tmpstr0 = NULL; + twitter_debug("tmpstr = %s\n", tmpstr); + + /* sender */ + g_regex_match(regp[SENDER], *str, 0, &match_info); + if(g_match_info_matches(match_info)) { + user = g_match_info_fetch(match_info, 2); + twitter_debug("user = %s\n", user); + g_match_info_free(match_info); + match_info = NULL; + } + + /* link string */ + g_regex_match(regp[MESSAGE_ID], *str, 0, &match_info); + if(match_info) { + match = g_match_info_fetch(match_info, 1); + linkstr = g_strdup_printf(LINK_FORMAT_TWITTER, + match, user, /* Reply */ + match, /* Favorite */ + match, user, tmpstr); /* Retweet */ + + twitter_debug("linkstr = %s\n", linkstr); + + newstr = g_regex_replace(regp[MESSAGE_ID], *str, -1, 0, + linkstr, + 0, NULL); + + twitter_debug("newstr = %s\n", newstr); + + g_free(*str); + *str = newstr; + + g_free(linkstr); + + g_free(match); + match = NULL; + + g_match_info_free(match_info); + match_info = NULL; + } + + g_free(user); + g_free(tmpstr); +} diff -r 300241bd1879 -r cc41ee1f5d3a util.h --- a/util.h Mon Oct 12 01:45:36 2009 +0900 +++ b/util.h Mon Oct 12 21:51:13 2009 +0900 @@ -5,6 +5,7 @@ void strip_markup(gchar **str, gboolean escape); gchar *strip_html_markup(const gchar *src); gboolean ensure_path_exists(const char *dir); +void twitter_add_links(gchar **str); gboolean is_twitter_conv(PurpleConversation *conv); gboolean is_wassr_account(PurpleAccount *account, const char *name);