From fb62f132a55896e235fd4956987548bdcd0be519 Mon Sep 17 00:00:00 2001 From: Flexo Date: Tue, 15 Mar 2016 21:39:36 +0000 Subject: Honour twitter's mutes and "hide retweets from this user" settings. --- protocols/twitter/twitter.c | 2 + protocols/twitter/twitter.h | 2 + protocols/twitter/twitter_lib.c | 129 ++++++++++++++++++++++++++++++++++++++++ protocols/twitter/twitter_lib.h | 4 ++ 4 files changed, 137 insertions(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 7c96e9c3..94c88b71 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -344,6 +344,8 @@ void twitter_login_finish(struct im_connection *ic) !(td->flags & TWITTER_HAVE_FRIENDS)) { imcb_log(ic, "Getting contact list"); twitter_get_friends_ids(ic, -1); + twitter_get_mutes_ids(ic, -1); + twitter_get_noretweets_ids(ic, -1); } else { twitter_main_loop_start(ic); } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 5a1a4f63..86c88262 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -60,6 +60,8 @@ struct twitter_data { guint64 timeline_id; GSList *follow_ids; + GSList *mutes_ids; + GSList *noretweets_ids; GSList *filters; guint64 last_status_id; /* For undo */ diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 80747015..288e0207 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -239,6 +239,8 @@ 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_noretweets_ids(struct http_request *req); /** * Get the friends ids. @@ -255,6 +257,34 @@ void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor) g_free(args[1]); } +/** + * Get the muted users ids. + */ +void twitter_get_mutes_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_MUTES_IDS_URL, twitter_http_get_mutes_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) +{ + char *args[2]; + + args[0] = "cursor"; + args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor); + twitter_http(ic, TWITTER_NORETWEETS_IDS_URL, twitter_http_get_noretweets_ids, ic, 0, args, 2); + + g_free(args[1]); +} + /** * Fill a list of ids. */ @@ -336,6 +366,92 @@ static void twitter_http_get_friends_ids(struct http_request *req) txl_free(txl); } +/** + * Callback for getting the mutes ids. + */ +static void twitter_http_get_mutes_ids(struct http_request *req) +{ + struct im_connection *ic = req->data; + json_value *parsed; + struct twitter_xml_list *txl; + struct twitter_data *td; + + // Check if the connection is stil active + if (!g_slist_find(twitter_connections, ic)) { + return; + } + + td = ic->proto_data; + + // Parse the data. + if (!(parsed = twitter_parse_response(ic, req))) { + return; + } + + 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; + if (txl->next_cursor) { + /* Recurse while there are still more pages */ + twitter_get_mutes_ids(ic, txl->next_cursor); + } + + txl->list = NULL; + txl_free(txl); +} + +/** + * Callback for getting the no-retweets ids. + */ +static void twitter_http_get_noretweets_ids(struct http_request *req) +{ + struct im_connection *ic = req->data; + json_value *parsed; + struct twitter_xml_list *txl; + struct twitter_data *td; + + // Check if the connection is stil active + if (!g_slist_find(twitter_connections, ic)) { + return; + } + + td = ic->proto_data; + + // Parse the data. + if (!(parsed = twitter_parse_response(ic, req))) { + return; + } + + 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) { + unsigned int i; + for (i = 0; i < parsed->u.array.length; i++) { + json_value *c = parsed->u.array.values[i]; + if (c->type != json_integer) { + continue; + } + txl->list = g_slist_prepend(txl->list, + g_strdup_printf("%"PRIu64, c->u.integer)); + } + } + + json_value_free(parsed); + td->noretweets_ids = txl->list; + + txl->list = NULL; + txl_free(txl); +} + static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl); static void twitter_http_get_users_lookup(struct http_request *req); @@ -830,10 +946,22 @@ static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta { struct twitter_data *td = ic->proto_data; char *last_id_str; + char *uid_str; if (status->user == NULL || status->text == NULL) { return; } + + /* Check this is not a tweet that should be muted */ + uid_str = g_strdup_printf("%" PRIu64, status->user->uid); + if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) { + g_free(uid_str); + return; + } + if (status->id != status->rt_id && g_slist_find_custom(td->noretweets_ids, uid_str, (GCompareFunc)strcmp)) { + g_free(uid_str); + return; + } /* Grrrr. Would like to do this during parsing, but can't access settings from there. */ @@ -856,6 +984,7 @@ static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta last_id_str = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id); set_setstr(&ic->acc->set, "_last_tweet", last_id_str); g_free(last_id_str); + g_free(uid_str); } static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter); diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 002376b1..44f20cbc 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -62,6 +62,8 @@ /* Social graphs URLs */ #define TWITTER_FRIENDS_IDS_URL "/friends/ids.json" #define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.json" +#define TWITTER_MUTES_IDS_URL "/mutes/users/ids.json" +#define TWITTER_NORETWEETS_IDS_URL "/friendships/no_retweets/ids.json" /* Account URLs */ #define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status.json" @@ -86,6 +88,8 @@ gboolean twitter_open_stream(struct im_connection *ic); gboolean twitter_open_filter_stream(struct im_connection *ic); gboolean 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_mutes_ids(struct im_connection *ic, gint64 next_cursor); +void twitter_get_noretweets_ids(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); -- cgit v1.2.3 From 9cf63aca0ffdc0cf708c3d17ee3ae9f92fa7cbf8 Mon Sep 17 00:00:00 2001 From: Flexo Date: Thu, 31 Mar 2016 18:49:52 +0000 Subject: Add mute and unmute commands. --- protocols/twitter/twitter.c | 6 ++++++ protocols/twitter/twitter_lib.c | 13 +++++++++++++ protocols/twitter/twitter_lib.h | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 94c88b71..e543f86e 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -957,6 +957,12 @@ static void twitter_handle_command(struct im_connection *ic, char *message) } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) { twitter_remove_buddy(ic, cmd[1], NULL); goto eof; + } else if (g_strcasecmp(cmd[0], "mute") == 0 && cmd[1]) { + twitter_mute_create_destroy(ic, cmd[1], 1); + goto eof; + } else if (g_strcasecmp(cmd[0], "unmute") == 0 && cmd[1]) { + twitter_mute_create_destroy(ic, cmd[1], 0); + goto eof; } else if ((g_strcasecmp(cmd[0], "report") == 0 || g_strcasecmp(cmd[0], "spam") == 0) && cmd[1]) { char *screen_name; diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 288e0207..6b38bd7c 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -1652,6 +1652,19 @@ void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int twitter_http_post, ic, 1, args, 2); } +/** + * Mute or unmute a user + */ +void twitter_mute_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_MUTES_CREATE_URL : TWITTER_MUTES_DESTROY_URL, + twitter_http_post, ic, 1, args, 2); +} + void twitter_status_destroy(struct im_connection *ic, guint64 id) { char *url; diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 44f20cbc..6833d23d 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -77,6 +77,10 @@ #define TWITTER_BLOCKS_CREATE_URL "/blocks/create/" #define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/" +/* Mute URLs */ +#define TWITTER_MUTES_CREATE_URL "/mutes/users/create.json" +#define TWITTER_MUTES_DESTROY_URL "/mutes/users/destroy.json" + /* Report spam */ #define TWITTER_REPORT_SPAM_URL "/users/report_spam.json" @@ -95,6 +99,7 @@ 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); void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message); void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create); +void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create); void twitter_status_destroy(struct im_connection *ic, guint64 id); void twitter_status_retweet(struct im_connection *ic, guint64 id); void twitter_report_spam(struct im_connection *ic, char *screen_name); -- cgit v1.2.3 From 0d581bd04e3a42d20437886d72a13c698f0ddf6a Mon Sep 17 00:00:00 2001 From: Flexo Date: Thu, 31 Mar 2016 22:04:33 +0000 Subject: Receive mute/unmute notifications from the stream. --- protocols/twitter/twitter_lib.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 6b38bd7c..c60f159a 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -953,7 +953,7 @@ 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("%" PRIu64, status->user->uid); + uid_str = g_strdup_printf("%" G_GINT64_FORMAT, status->user->uid); if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) { g_free(uid_str); return; @@ -1121,6 +1121,8 @@ static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value json_value *source = json_o_get(o, "source"); json_value *target = json_o_get(o, "target"); const char *type = json_o_str(o, "event"); + struct twitter_xml_user *us = NULL; + struct twitter_xml_user *ut = NULL; if (!type || !source || source->type != json_object || !target || target->type != json_object) { @@ -1128,15 +1130,33 @@ static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value } if (strcmp(type, "follow") == 0) { - struct twitter_xml_user *us = twitter_xt_get_user(source); - struct twitter_xml_user *ut = twitter_xt_get_user(target); + us = twitter_xt_get_user(source); + ut = twitter_xt_get_user(target); if (g_strcasecmp(us->screen_name, td->user) == 0) { twitter_add_buddy(ic, ut->screen_name, ut->name); } - txu_free(us); - txu_free(ut); + } else if (strcmp(type, "mute") == 0) { + GSList *ids = td->mutes_ids; + ut = twitter_xt_get_user(target); + ids = g_slist_prepend(ids, + g_strdup_printf("%" G_GINT64_FORMAT, ut->uid)); + + td->mutes_ids = ids; + } else if (strcmp(type, "unmute") == 0) { + GSList *found; + char *uid_str; + ut = twitter_xt_get_user(target); + uid_str = g_strdup_printf("%" G_GINT64_FORMAT, ut->uid); + if ((found = g_slist_find_custom(td->mutes_ids, uid_str, + (GCompareFunc)strcmp))) { + td->mutes_ids = g_slist_remove(td->mutes_ids, found); + } + g_free(uid_str); } + txu_free(us); + txu_free(ut); + return TRUE; } -- cgit v1.2.3 From 166a571321826a68626eaf10a59901253237f09e Mon Sep 17 00:00:00 2001 From: Flexo Date: Fri, 1 Apr 2016 18:06:15 +0000 Subject: Avoid adding an id twice to the mutes list. Twitter doesn't error if you mute the same user multiple times. Also, correct signedness of the stringified user ids. bitlbee keeps them as unsigned even if the json library uses signed for integers... --- protocols/twitter/twitter_lib.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c60f159a..82765201 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -953,7 +953,14 @@ 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_GINT64_FORMAT, status->user->uid); + uid_str = g_strdup_printf("%" PRIu64, status->user->uid); + if (getenv("BITLBEE_DEBUG")) { + GSList *item; + fprintf(stderr, "Checking mutes; this uid=%s\n", uid_str); + for (item = td->mutes_ids; item != NULL; item = item->next) { + fprintf(stderr, " id: %s\n", (char *)item->data); + } + } if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) { g_free(uid_str); return; @@ -1136,22 +1143,34 @@ static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value twitter_add_buddy(ic, ut->screen_name, ut->name); } } else if (strcmp(type, "mute") == 0) { - GSList *ids = td->mutes_ids; + GSList *found; + char *uid_str; ut = twitter_xt_get_user(target); - ids = g_slist_prepend(ids, - g_strdup_printf("%" G_GINT64_FORMAT, ut->uid)); - - td->mutes_ids = ids; + uid_str = g_strdup_printf("%" PRIu64, 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); + if (getenv("BITLBEE_DEBUG")) { + fprintf(stderr, "New mute: %s %"PRIu64"\n", + ut->screen_name, ut->uid); + } } else if (strcmp(type, "unmute") == 0) { GSList *found; char *uid_str; ut = twitter_xt_get_user(target); - uid_str = g_strdup_printf("%" G_GINT64_FORMAT, ut->uid); + uid_str = g_strdup_printf("%" PRIu64, ut->uid); if ((found = g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp))) { td->mutes_ids = g_slist_remove(td->mutes_ids, found); } g_free(uid_str); + twitter_log(ic, "Unmuted user %s", ut->screen_name); + if (getenv("BITLBEE_DEBUG")) { + fprintf(stderr, "New unmute: %s %"PRIu64"\n", + ut->screen_name, ut->uid); + } } txu_free(us); -- cgit v1.2.3