aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/twitter/twitter_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/twitter/twitter_lib.c')
-rw-r--r--protocols/twitter/twitter_lib.c245
1 files changed, 197 insertions, 48 deletions
diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c
index 8425c58e..4addb650 100644
--- a/protocols/twitter/twitter_lib.c
+++ b/protocols/twitter/twitter_lib.c
@@ -240,6 +240,7 @@ static json_value *twitter_parse_response(struct im_connection *ic, struct http_
static void twitter_http_get_friends_ids(struct http_request *req);
static void twitter_http_get_mutes_ids(struct http_request *req);
+static void twitter_http_get_blocks_ids(struct http_request *req);
static void twitter_http_get_noretweets_ids(struct http_request *req);
/**
@@ -272,6 +273,20 @@ void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor)
}
/**
+ * Get the blocked users ids.
+ */
+void twitter_get_blocks_ids(struct im_connection *ic, gint64 next_cursor)
+{
+ char *args[2];
+
+ args[0] = "cursor";
+ args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
+ twitter_http(ic, TWITTER_BLOCKS_IDS_URL, twitter_http_get_blocks_ids, ic, 0, args, 2);
+
+ g_free(args[1]);
+}
+
+/**
* Get the ids for users from whom we should ignore retweets.
*/
void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor)
@@ -367,14 +382,15 @@ static void twitter_http_get_friends_ids(struct http_request *req)
}
/**
- * Callback for getting the mutes ids.
+ * Callback for getting the blocks and mutes ids.
*/
-static void twitter_http_get_mutes_ids(struct http_request *req)
+static void twitter_http_get_mutes_blocks_ids(struct http_request *req, int blocks)
{
struct im_connection *ic = req->data;
json_value *parsed;
struct twitter_xml_list *txl;
struct twitter_data *td;
+ GSList *elem;
// Check if the connection is stil active
if (!g_slist_find(twitter_connections, ic)) {
@@ -394,24 +410,45 @@ static void twitter_http_get_mutes_ids(struct http_request *req)
}
txl = g_new0(struct twitter_xml_list, 1);
- txl->list = td->mutes_ids;
/* mute ids API response is similar enough to friends response
to reuse this method */
twitter_xt_get_friends_id_list(parsed, txl);
json_value_free(parsed);
- td->mutes_ids = txl->list;
+ for (elem = txl->list; elem; elem = elem->next) {
+ g_hash_table_add(blocks ? td->blocks_ids : td->mutes_ids, elem->data);
+ }
+
if (txl->next_cursor) {
/* Recurse while there are still more pages */
- twitter_get_mutes_ids(ic, txl->next_cursor);
+ if (blocks) {
+ twitter_get_blocks_ids(ic, txl->next_cursor);
+ } else {
+ twitter_get_mutes_ids(ic, txl->next_cursor);
+ }
}
- txl->list = NULL;
txl_free(txl);
}
/**
+ * Callback for getting the mutes ids.
+ */
+static void twitter_http_get_mutes_ids(struct http_request *req)
+{
+ twitter_http_get_mutes_blocks_ids(req, 0);
+}
+
+/**
+ * Callback for getting the blocks ids.
+ */
+static void twitter_http_get_blocks_ids(struct http_request *req)
+{
+ twitter_http_get_mutes_blocks_ids(req, 1);
+}
+
+/**
* Callback for getting the no-retweets ids.
*/
static void twitter_http_get_noretweets_ids(struct http_request *req)
@@ -439,8 +476,6 @@ static void twitter_http_get_noretweets_ids(struct http_request *req)
}
txl = g_new0(struct twitter_xml_list, 1);
- txl->list = td->noretweets_ids;
-
// Process the retweet ids
txl->type = TXL_ID;
if (parsed->type == json_array) {
@@ -450,15 +485,12 @@ static void twitter_http_get_noretweets_ids(struct http_request *req)
if (c->type != json_integer) {
continue;
}
- txl->list = g_slist_prepend(txl->list,
- g_strdup_printf("%" PRId64, c->u.integer));
+ g_hash_table_add(td->noretweets_ids, g_strdup_printf("%"PRId64, c->u.integer));
}
}
json_value_free(parsed);
- td->noretweets_ids = txl->list;
- txl->list = NULL;
txl_free(txl);
}
@@ -816,7 +848,7 @@ static char *twitter_msg_add_id(struct im_connection *ic,
if (txs->reply_to) {
int i;
- for (i = 0; i < TWITTER_LOG_LENGTH; i++) {
+ for (i = 0; i < td->log_length; i++) {
if (td->log[i].id == txs->reply_to) {
reply_to = i;
break;
@@ -834,26 +866,39 @@ static char *twitter_msg_add_id(struct im_connection *ic,
}
}
- td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;
- td->log[td->log_id].id = txs->id;
- td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
+ if (txs->from_filter) {
+ td->filter_log_id = (td->filter_log_id + 1) % td->log_length;
+ td->filter_log[td->filter_log_id].id = txs->id;
+ td->filter_log[td->filter_log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
+ } else {
+ td->log_id = (td->log_id + 1) % td->log_length;
+ td->log[td->log_id].id = txs->id;
+ td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
+ }
/* This is all getting hairy. :-( If we RT'ed something ourselves,
remember OUR id instead so undo will work. In other cases, the
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 (txs->from_filter) {
+ td->filter_log[td->filter_log_id].id = txs->rt_id;
+ /* More useful than NULL. */
+ td->filter_log[td->filter_log_id].bu = &twitter_log_local_user;
+ } else {
+ 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")) {
if (reply_to != -1) {
- return g_strdup_printf("\002[\002%02x->%02x\002]\002 %s%s",
- td->log_id, reply_to, prefix, txs->text);
+ return g_strdup_printf("\002[\002%0*x->%0*x\002]\002 %s%s",
+ td->id_length, td->log_id, td->id_length,
+ reply_to, prefix, txs->text);
} else {
- return g_strdup_printf("\002[\002%02x\002]\002 %s%s",
- td->log_id, prefix, txs->text);
+ return g_strdup_printf("\002[\002%0*x\002]\002 %s%s",
+ td->id_length, td->log_id, prefix, txs->text);
}
} else {
if (*prefix) {
@@ -981,11 +1026,12 @@ static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta
/* Check this is not a tweet that should be muted */
uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, status->user->uid);
- if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) {
+ if (g_hash_table_lookup(td->mutes_ids, uid_str) ||
+ g_hash_table_lookup(td->blocks_ids, uid_str)) {
g_free(uid_str);
return;
}
- if (status->id != status->rt_id && g_slist_find_custom(td->noretweets_ids, uid_str, (GCompareFunc)strcmp)) {
+ if (status->id != status->rt_id && g_hash_table_lookup(td->noretweets_ids, uid_str)) {
g_free(uid_str);
return;
}
@@ -1034,8 +1080,23 @@ static void twitter_http_stream(struct http_request *req)
if ((req->flags & HTTPC_EOF) || !req->reply_body) {
if (req == td->stream) {
td->stream = NULL;
+
+ if (req->status_code == 200 &&
+ td->stream_opentime + 3 < time(NULL)) {
+ debug("Reconnecting to twitter stream.");
+ twitter_open_stream(ic);
+ twitter_get_timeline(ic, -1);
+ return;
+ }
} else if (req == td->filter_stream) {
td->filter_stream = NULL;
+
+ if (req->status_code == 200 &&
+ td->filter_stream_opentime + 3 < time(NULL)) {
+ debug("Reconnecting to twitter filter stream.");
+ twitter_open_filter_stream(ic);
+ return;
+ }
}
imcb_error(ic, "Stream closed (%s)", req->status_string);
@@ -1094,8 +1155,12 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu
} else if ((c = json_o_get(o, "direct_message")) &&
(txs = twitter_xt_get_dm(c))) {
if (g_strcasecmp(txs->user->screen_name, td->user) != 0) {
- imcb_buddy_msg(ic, txs->user->screen_name,
- txs->text, 0, txs->created_at);
+ char *uid_str = g_strdup_printf("%" PRIu64, txs->user->uid);
+ if (!g_hash_table_lookup(td->blocks_ids, uid_str)) {
+ imcb_buddy_msg(ic, txs->user->screen_name,
+ txs->text, 0, txs->created_at);
+ }
+ g_free(uid_str);
}
txs_free(txs);
return TRUE;
@@ -1119,10 +1184,12 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu
static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs)
{
struct twitter_data *td = ic->proto_data;
+ struct twitter_log_data *tl;
int i;
- for (i = 0; i < TWITTER_LOG_LENGTH; i++) {
- if (td->log[i].id == txs->id) {
+ tl = txs->from_filter ? td->filter_log : td->log;
+ for (i = 0; i < td->log_length; i++) {
+ if (tl[i].id == txs->id) {
/* Got a duplicate (RT, probably). Drop it. */
return TRUE;
}
@@ -1165,33 +1232,25 @@ static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value
if (g_strcasecmp(us->screen_name, td->user) == 0) {
twitter_add_buddy(ic, ut->screen_name, ut->name);
}
- } else if (strcmp(type, "mute") == 0) {
- GSList *found;
+ } else if (strcmp(type, "mute") == 0 || strcmp(type, "block") == 0) {
char *uid_str;
+ int mute = strcmp(type, "mute") == 0;
ut = twitter_xt_get_user(target);
uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
- if (!(found = g_slist_find_custom(td->mutes_ids, uid_str,
- (GCompareFunc)strcmp))) {
- td->mutes_ids = g_slist_prepend(td->mutes_ids, uid_str);
- }
- twitter_log(ic, "Muted user %s", ut->screen_name);
+ g_hash_table_add(mute ? td->mutes_ids : td->blocks_ids, uid_str);
+ twitter_log(ic, "%s user %s", mute ? "Muted" : "Blocked", ut->screen_name);
if (getenv("BITLBEE_DEBUG")) {
fprintf(stderr, "New mute: %s %"G_GUINT64_FORMAT"\n",
ut->screen_name, ut->uid);
}
- } else if (strcmp(type, "unmute") == 0) {
- GSList *found;
+ } else if (strcmp(type, "unmute") == 0 || strcmp(type, "unblock") == 0) {
char *uid_str;
+ int unmute = strcmp(type, "unmute") == 0;
ut = twitter_xt_get_user(target);
uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
- if ((found = g_slist_find_custom(td->mutes_ids, uid_str,
- (GCompareFunc)strcmp))) {
- char *found_str = found->data;
- td->mutes_ids = g_slist_delete_link(td->mutes_ids, found);
- g_free(found_str);
- }
+ g_hash_table_remove(unmute ? td->mutes_ids : td->blocks_ids, uid_str);
g_free(uid_str);
- twitter_log(ic, "Unmuted user %s", ut->screen_name);
+ twitter_log(ic, "%s user %s", unmute ? "Unmuted" : "Blocked", ut->screen_name);
if (getenv("BITLBEE_DEBUG")) {
fprintf(stderr, "New unmute: %s %"G_GUINT64_FORMAT"\n",
ut->screen_name, ut->uid);
@@ -1214,6 +1273,7 @@ gboolean twitter_open_stream(struct im_connection *ic)
/* This flag must be enabled or we'll get no data until EOF
(which err, kind of, defeats the purpose of a streaming API). */
td->stream->flags |= HTTPC_STREAMING;
+ td->stream_opentime = time(NULL);
return TRUE;
}
@@ -1269,6 +1329,7 @@ static gboolean twitter_filter_stream(struct im_connection *ic)
/* This flag must be enabled or we'll get no data until EOF
(which err, kind of, defeats the purpose of a streaming API). */
td->filter_stream->flags |= HTTPC_STREAMING;
+ td->filter_stream_opentime = time(NULL);
ret = TRUE;
}
@@ -1683,10 +1744,12 @@ static void twitter_http_post(struct http_request *req)
*/
void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to)
{
- char *args[4] = {
+ char *args[6] = {
"status", msg,
"in_reply_to_status_id",
- g_strdup_printf("%" G_GUINT64_FORMAT, in_reply_to)
+ g_strdup_printf("%" G_GUINT64_FORMAT, in_reply_to),
+ "auto_populate_reply_metadata",
+ "true",
};
if (set_getbool(&ic->acc->set, "in_korea") && !in_reply_to) {
@@ -1697,7 +1760,7 @@ void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_t
}
twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1,
- args, in_reply_to ? 4 : 2);
+ args, in_reply_to ? (set_getbool(&ic->acc->set, "autofill_reply") ? 6 : 4) : 2);
g_free(args[3]);
}
@@ -1740,6 +1803,32 @@ void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create
twitter_http_post, ic, 1, args, 2);
}
+/**
+ * Block or unblock a user
+ */
+void twitter_block_create_destroy(struct im_connection *ic, char *who, int create)
+{
+ char *args[2];
+
+ args[0] = "screen_name";
+ args[1] = who;
+ twitter_http(ic, create ? TWITTER_BLOCKS_CREATE_URL : TWITTER_BLOCKS_DESTROY_URL,
+ twitter_http_post, ic, 1, args, 2);
+}
+
+/**
+ * Enable or disable retweets for user
+ */
+void twitter_retweet_enable_disable(struct im_connection *ic, char *who, int enable)
+{
+ char *args[4];
+ args[0] = "screen_name";
+ args[1] = who;
+ args[2] = "retweets";
+ args[3] = enable ? "true" : "false";
+ twitter_http(ic, TWITTER_FRIENDSHIPS_UPDATE_URL, twitter_http_post, ic, 1, args, 4);
+}
+
void twitter_status_destroy(struct im_connection *ic, guint64 id)
{
char *url;
@@ -1831,3 +1920,63 @@ void twitter_status_show_url(struct im_connection *ic, guint64 id)
twitter_http(ic, url, twitter_http_status_show_url, ic, 0, NULL, 0);
g_free(url);
}
+
+struct twitter_http_msg {
+ struct im_connection *ic;
+ char *message;
+};
+
+static void twitter_http_status_quote_post(struct http_request *req)
+{
+ struct twitter_http_msg *thm = req->data;
+ struct im_connection *ic = thm->ic;
+ struct twitter_data *td = ic->proto_data;
+ char *message;
+ const char *name;
+ json_value *parsed, *id;
+
+ if (!g_slist_find(twitter_connections, ic)) {
+ goto eof;
+ }
+
+ if (!(parsed = twitter_parse_response(ic, req))) {
+ goto eof;
+ }
+
+ name = json_o_str(json_o_get(parsed, "user"), "screen_name");
+ id = json_o_get(parsed, "id");
+
+ if (name && id && id->type == json_integer) {
+ message = g_strdup_printf("%s https://twitter.com/%s/status/%" G_GUINT64_FORMAT, thm->message, name, id->u.integer);
+
+ /*if (!twitter_length_check(ic, message)) {
+ goto eof;
+ }*/
+
+ td->last_status_id = 0;
+ twitter_post_status(ic, message, 0);
+ } else {
+ twitter_log(ic, "Error: could not fetch url for quoted tweet.");
+ }
+
+ json_value_free(parsed);
+
+eof:
+ g_free(thm->message);
+ g_free(thm);
+}
+
+void twitter_status_quote_post(struct im_connection *ic, guint64 id, char *message)
+{
+ struct twitter_http_msg *thm;
+ char *url;
+
+ thm = g_new0(struct twitter_http_msg, 1);
+
+ thm->ic = ic;
+ thm->message = g_strdup(message);
+
+ url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s", TWITTER_STATUS_SHOW_URL, id, ".json");
+ twitter_http(ic, url, twitter_http_status_quote_post, thm, 0, NULL, 0);
+ g_free(url);
+}