aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/twitter
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/twitter')
-rw-r--r--protocols/twitter/twitter.c80
-rw-r--r--protocols/twitter/twitter.h12
-rw-r--r--protocols/twitter/twitter_lib.c347
-rw-r--r--protocols/twitter/twitter_lib.h2
4 files changed, 349 insertions, 92 deletions
diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c
index 57a1ed80..76ccc3eb 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 &&
@@ -89,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",
@@ -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)
@@ -464,15 +473,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,11 +498,14 @@ 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], "#") &&
+ 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;
}
@@ -513,7 +524,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 &&
@@ -524,6 +543,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]);
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..d52c29ff 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);
@@ -435,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) {
@@ -463,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);
@@ -474,6 +496,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 &lt;%s&gt;%s", txs->text, kort->text,
+ disp->text, pos + strlen(kort->text));
+
+ g_free(txs->text);
+ txs->text = new;
+ }
}
return XT_HANDLED;
@@ -521,32 +564,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 +602,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 +611,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 +624,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 +638,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 +675,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 +688,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 +706,166 @@ 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[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[4] = "since_id";
+ args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id);
+ }
+
+ 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) {
+ g_free(args[5]);
+ }
+}
+
+/**
+ * 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[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[4] = "since_id";
+ args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id);
+ }
+
+ 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) {
+ g_free(args[5]);
+ }
+}
+
/**
* Callback for getting the home timeline.
*/
@@ -712,14 +890,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 <statuses> node should hold the list of statuses <status>
+ 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 %s: %s",
+ TWITTER_MENTIONS_URL, twitter_parse_error(req));
+
+ goto end;
}
txl = g_new0(struct twitter_xml_list, 1);
@@ -732,15 +962,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);