aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/user-guide/commands.xml3
-rw-r--r--protocols/twitter/twitter.c29
-rw-r--r--protocols/twitter/twitter.h12
-rw-r--r--protocols/twitter/twitter_lib.c56
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 &lt;screenname&gt;</term><listitem><para>Stop following a person</para></listitem></varlistentry>
<varlistentry><term>favourite &lt;screenname|#id&gt;</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 &lt;message&gt;</term><listitem><para>Post a tweet</para></listitem></varlistentry>
+ <varlistentry><term>url &lt;screenname|#id&gt;</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")) {