diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2015-05-04 23:21:57 +0200 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2015-05-04 23:21:57 +0200 |
commit | 5ca141685d78cb372acd0e9aded36175cba957a3 (patch) | |
tree | e18b13d32ecc35ea1bac07a2ca794a4cfb378ef8 | |
parent | 70ec7ab33202b933a88e04122140a815a7d2081e (diff) | |
parent | c43146d9308aea2025c0a0520c9e2d40758d9e8d (diff) |
Merge branch 'wilmer-twitter'
-rw-r--r-- | doc/user-guide/commands.xml | 3 | ||||
-rw-r--r-- | protocols/twitter/twitter.c | 29 | ||||
-rw-r--r-- | protocols/twitter/twitter.h | 12 | ||||
-rw-r--r-- | protocols/twitter/twitter_lib.c | 56 |
4 files changed, 77 insertions, 23 deletions
diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 6ce19774..4d6e7659 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -120,7 +120,7 @@ <description> <para> - This commands deletes an account from your account list. You should signoff the account before deleting it. + This command deletes an account from your account list. You should signoff the account before deleting it. </para> @@ -852,6 +852,7 @@ <varlistentry><term>unfollow <screenname></term><listitem><para>Stop following a person</para></listitem></varlistentry> <varlistentry><term>favourite <screenname|#id></term><listitem><para>Favo<emphasis>u</emphasis>rite the given user's most recent tweet, or the given tweet ID.</para></listitem></varlistentry> <varlistentry><term>post <message></term><listitem><para>Post a tweet</para></listitem></varlistentry> + <varlistentry><term>url <screenname|#id></term><listitem><para>Show URL for a tweet to open it in a browser (and see context)</para></listitem></varlistentry> </variablelist> <para> diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 95384573..421a0552 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -866,6 +866,8 @@ static gboolean twitter_parse_id(char *string, int base, guint64 *id) return TRUE; } +bee_user_t twitter_log_local_user; + /** Convert the given bitlbee tweet ID, bitlbee username, or twitter tweet ID * into a twitter tweet ID. * @@ -896,10 +898,6 @@ static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, cha if (twitter_parse_id(arg, 16, &id) && id < TWITTER_LOG_LENGTH) { bu = td->log[id].bu; id = td->log[id].id; - /* Beware of dangling pointers! */ - if (!g_slist_find(ic->bee->users, bu)) { - bu = NULL; - } } else if (twitter_parse_id(arg, 10, &id)) { /* Allow normal tweet IDs as well; not a very useful feature but it's always been there. Just ignore @@ -910,6 +908,16 @@ static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, cha } } if (bu_) { + if (bu == &twitter_log_local_user) { + /* HACK alert. There's no bee_user object for the local + * user so just fake one for the few cmds that need it. */ + twitter_log_local_user.handle = td->user; + } else { + /* Beware of dangling pointers! */ + if (!g_slist_find(ic->bee->users, bu)) { + bu = NULL; + } + } *bu_ = bu; } return id; @@ -1002,6 +1010,19 @@ static void twitter_handle_command(struct im_connection *ic, char *message) message = cmd[2]; in_reply_to = id; allow_post = TRUE; + } else if (g_strcasecmp(cmd[0], "url") == 0) { + id = twitter_message_id_from_command_arg(ic, cmd[1], &bu); + if (!id) { + twitter_log(ic, "Tweet `%s' does not exist", cmd[1]); + } else { + /* More common link is twitter.com/$UID/status/$ID (and that's + * what this will 302 to) but can't generate that since for RTs, + * bu here points at the retweeter while id contains the id of + * the original message. */ + twitter_log(ic, "https://twitter.com/statuses/%lld", id); + } + goto eof; + } else if (g_strcasecmp(cmd[0], "post") == 0) { message += 5; allow_post = TRUE; diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 7cfd9148..5a1a4f63 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -100,7 +100,9 @@ struct twitter_user_data { #define TWITTER_LOG_LENGTH 256 struct twitter_log_data { guint64 id; - struct bee_user *bu; /* DANGER: can be a dead pointer. Check it first. */ + /* DANGER: bu can be a dead pointer. Check it first. + * twitter_message_id_from_command_arg() will do this. */ + struct bee_user *bu; }; /** @@ -110,6 +112,14 @@ struct twitter_log_data { */ extern GSList *twitter_connections; +/** + * Evil hack: Fake bee_user which will always point at the local user. + * Sometimes used as a return value by twitter_message_id_from_command_arg. + * NOT thread safe but don't you dare to even think of ever making BitlBee + * threaded. :-) + */ +extern bee_user_t twitter_log_local_user; + void twitter_login_finish(struct im_connection *ic); struct http_request; diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c20f8295..17bceb9f 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -459,7 +459,7 @@ static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list * #define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y" #endif -static char* expand_entities(char* text, const json_value *entities); +static void expand_entities(char **text, const json_value *node); /** * Function to fill a twitter_xml_status struct. @@ -472,7 +472,7 @@ static char* expand_entities(char* text, const json_value *entities); static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) { struct twitter_xml_status *txs; - const json_value *rt = NULL, *entities = NULL; + const json_value *rt = NULL; if (node->type != json_object) { return FALSE; @@ -500,8 +500,6 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) txs->rt_id = txs->id = v->u.integer; } else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) { txs->reply_to = v->u.integer; - } else if (strcmp("entities", k) == 0 && v->type == json_object) { - entities = v; } } @@ -515,8 +513,8 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) txs->id = rtxs->id; txs_free(rtxs); } - } else if (entities) { - txs->text = expand_entities(txs->text, entities); + } else { + expand_entities(&txs->text, node); } if (txs->text && txs->user && txs->id) { @@ -533,7 +531,6 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node) { struct twitter_xml_status *txs; - const json_value *entities = NULL; if (node->type != json_object) { return FALSE; @@ -560,9 +557,7 @@ static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node) } } - if (entities) { - txs->text = expand_entities(txs->text, entities); - } + expand_entities(&txs->text, node); if (txs->text && txs->user && txs->id) { return txs; @@ -572,8 +567,26 @@ static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node) return NULL; } -static char* expand_entities(char* text, const json_value *entities) +static void expand_entities(char **text, const json_value *node) { + json_value *entities, *quoted; + char *quote_url = NULL, *quote_text = NULL; + + if (!((entities = json_o_get(node, "entities")) && entities->type == json_object)) + return; + if ((quoted = json_o_get(node, "quoted_status")) && quoted->type == json_object) { + /* New "retweets with comments" feature. Note that this info + * seems to be included in the streaming API only! Grab the + * full message and try to insert it when we run into the + * Tweet entity. */ + struct twitter_xml_status *txs = twitter_xt_get_status(quoted); + quote_text = g_strdup_printf("@%s: %s", txs->user->screen_name, txs->text); + quote_url = g_strdup_printf("%s/status/%" G_GUINT64_FORMAT, txs->user->screen_name, txs->id); + txs_free(txs); + } else { + quoted = NULL; + } + JSON_O_FOREACH(entities, k, v) { int i; @@ -585,28 +598,35 @@ static char* expand_entities(char* text, const json_value *entities) } for (i = 0; i < v->u.array.length; i++) { + const char *format = "%s%s <%s>%s"; + if (v->u.array.values[i]->type != json_object) { continue; } const char *kort = json_o_str(v->u.array.values[i], "url"); const char *disp = json_o_str(v->u.array.values[i], "display_url"); + const char *full = json_o_str(v->u.array.values[i], "expanded_url"); char *pos, *new; - if (!kort || !disp || !(pos = strstr(text, kort))) { + if (!kort || !disp || !(pos = strstr(*text, kort))) { continue; } + if (quote_url && strstr(full, quote_url)) { + format = "%s<%s> [%s]%s"; + disp = quote_text; + } *pos = '\0'; - new = g_strdup_printf("%s%s <%s>%s", text, kort, + new = g_strdup_printf(format, *text, kort, disp, pos + strlen(kort)); - g_free(text); - text = new; + g_free(*text); + *text = new; } } - - return text; + g_free(quote_text); + g_free(quote_url); } /** @@ -680,6 +700,8 @@ static char *twitter_msg_add_id(struct im_connection *ic, original tweet's id should be remembered for deduplicating. */ if (g_strcasecmp(txs->user->screen_name, td->user) == 0) { td->log[td->log_id].id = txs->rt_id; + /* More useful than NULL. */ + td->log[td->log_id].bu = &twitter_log_local_user; } if (set_getbool(&ic->acc->set, "show_ids")) { |