diff options
author | Marius Halden <marius.h@lden.org> | 2017-01-08 19:58:10 +0100 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2017-01-08 19:58:10 +0100 |
commit | 9da4ee0315ec612e52a39aedb8a83ae4e6d51361 (patch) | |
tree | f4a9622e43da6cce19f87bcfb419a4f095f1ba88 | |
parent | dfc6d6593cad26ffef63657b2abdd63a9420e2bb (diff) | |
parent | 82e6bcf0e6f22eeca7eda7ea95aaf8378df6ddef (diff) | |
download | bitlbee-facebook-9da4ee0315ec612e52a39aedb8a83ae4e6d51361.tar.gz bitlbee-facebook-9da4ee0315ec612e52a39aedb8a83ae4e6d51361.tar.bz2 bitlbee-facebook-9da4ee0315ec612e52a39aedb8a83ae4e6d51361.tar.xz |
Merge branch 'master' into track-messages
-rw-r--r-- | facebook/facebook-api.c | 187 | ||||
-rw-r--r-- | facebook/facebook-api.h | 24 | ||||
-rw-r--r-- | facebook/facebook.c | 82 | ||||
-rw-r--r-- | facebook/marshaller.list | 1 |
4 files changed, 242 insertions, 52 deletions
diff --git a/facebook/facebook-api.c b/facebook/facebook-api.c index 184cb89..1237861 100644 --- a/facebook/facebook-api.c +++ b/facebook/facebook-api.c @@ -61,6 +61,7 @@ struct _FbApiPrivate gboolean invisible; guint unread; FbId lastmid; + gchar *contacts_delta; }; struct _FbApiData @@ -81,6 +82,9 @@ fb_api_message_send(FbApi *api, FbApiMessage *msg); static void fb_api_sticker(FbApi *api, FbId sid, FbApiMessage *msg); +void +fb_api_contacts_delta(FbApi *api, const gchar *delta_cursor); + G_DEFINE_TYPE(FbApi, fb_api, G_TYPE_OBJECT); static void @@ -174,6 +178,7 @@ fb_api_dispose(GObject *obj) g_free(priv->did); g_free(priv->stoken); g_free(priv->token); + g_free(priv->contacts_delta); } static void @@ -336,6 +341,23 @@ fb_api_class_init(FbApiClass *klass) 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); /** + * FbApi::contacts-delta: + * @api: The #FbApi. + * @added: The #GSList of added #FbApiUser's. + * @removed: The #GSList of strings with removed user ids. + * + * Like 'contacts', but only the deltas. + */ + g_signal_new("contacts-delta", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_ACTION, + 0, + NULL, NULL, + fb_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, + 2, G_TYPE_POINTER, G_TYPE_POINTER); + + /** * FbApi::error: * @api: The #FbApi. * @error: The #GError. @@ -762,6 +784,9 @@ fb_api_http_query(FbApi *api, gint64 query, JsonBuilder *builder, case FB_API_QUERY_CONTACTS_AFTER: name = "FetchContactsFullWithAfterQuery"; break; + case FB_API_QUERY_CONTACTS_DELTA: + name = "FetchContactsDeltaQuery"; + break; case FB_API_QUERY_STICKER: name = "FetchStickersWithPreviewsQuery"; break; @@ -2001,24 +2026,15 @@ fb_api_contact(FbApi *api, FbId uid) fb_api_http_query(api, FB_API_QUERY_CONTACT, bldr, fb_api_cb_contact); } -static void -fb_api_cb_contacts(FbHttpRequest *req, gpointer data) +static GSList * +fb_api_cb_contacts_nodes(FbApi *api, JsonNode *root, GSList *users) { - const gchar *cursor; const gchar *str; - FbApi *api = data; FbApiPrivate *priv = api->priv; FbApiUser *user; FbId uid; FbJsonValues *values; - gboolean complete; GError *err = NULL; - GSList *users = NULL; - JsonNode *root; - - if (!fb_api_http_chk(api, req, &root)) { - return; - } values = fb_json_values_new(root); fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, @@ -2029,8 +2045,10 @@ fb_api_cb_contacts(FbHttpRequest *req, gpointer data) "$.structured_name.text"); fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, "$.hugePictureUrl.uri"); - fb_json_values_set_array(values, FALSE, "$.viewer.messenger_contacts" - ".nodes"); + + if (JSON_NODE_TYPE(root) == JSON_NODE_ARRAY) { + fb_json_values_set_array(values, FALSE, "$"); + } while (fb_json_values_update(values, &err)) { str = fb_json_values_next_str(values, "0"); @@ -2051,20 +2069,126 @@ fb_api_cb_contacts(FbHttpRequest *req, gpointer data) user->csum = fb_api_user_icon_checksum(user->icon); users = g_slist_prepend(users, user); + + if (JSON_NODE_TYPE(root) != JSON_NODE_ARRAY) { + break; + } } g_object_unref(values); - values = fb_json_values_new(root); + return users; +} + +/* base64(contact:<our id>:<their id>:<whatever>) */ +static GSList * +fb_api_cb_contacts_parse_removed(FbApi *api, JsonNode *node, GSList *users) +{ + gsize len; + char **split; + guchar *decoded = g_base64_decode(json_node_get_string(node), &len); + + g_return_val_if_fail(decoded[len] == '\0', users); + g_return_val_if_fail(len == strlen(decoded), users); + g_return_val_if_fail(g_str_has_prefix(decoded, "contact:"), users); + + split = g_strsplit_set(decoded, ":", 4); + + g_return_val_if_fail(g_strv_length(split) == 4, users); + + users = g_slist_prepend(users, g_strdup(split[2])); + + g_strfreev(split); + g_free(decoded); + + return users; +} + +static void +fb_api_cb_contacts(FbHttpRequest *req, gpointer data) +{ + const gchar *cursor; + const gchar *delta_cursor; + const gchar *str; + FbApi *api = data; + FbApiPrivate *priv = api->priv; + FbApiUser *user; + FbId uid; + FbJsonValues *values; + gboolean complete; + gboolean is_delta; + GError *err = NULL; + GList *l; + GSList *users = NULL; + JsonNode *root; + JsonNode *croot; + JsonNode *node; + + if (!fb_api_http_chk(api, req, &root)) { + return; + } + + croot = fb_json_node_get(root, "$.viewer.messenger_contacts.deltas", NULL); + is_delta = (croot != NULL); + + if (!is_delta) { + croot = fb_json_node_get(root, "$.viewer.messenger_contacts", NULL); + node = fb_json_node_get(croot, "$.nodes", NULL); + users = fb_api_cb_contacts_nodes(api, node, users); + json_node_free(node); + + } else { + GSList *added = NULL; + GSList *removed = NULL; + JsonArray *arr = fb_json_node_get_arr(croot, "$.nodes", NULL); + GList *elms = json_array_get_elements(arr); + + for (l = elms; l != NULL; l = l->next) { + if (node = fb_json_node_get(l->data, "$.added", NULL)) { + added = fb_api_cb_contacts_nodes(api, node, added); + json_node_free(node); + } + + if (node = fb_json_node_get(l->data, "$.removed", NULL)) { + removed = fb_api_cb_contacts_parse_removed(api, node, removed); + json_node_free(node); + } + } + + g_signal_emit_by_name(api, "contacts-delta", added, removed); + + g_slist_free_full(added, (GDestroyNotify) fb_api_user_free); + g_slist_free_full(removed, (GDestroyNotify) g_free); + + g_list_free(elms); + json_array_unref(arr); + } + + values = fb_json_values_new(croot); + fb_json_values_add(values, FB_JSON_TYPE_BOOL, FALSE, + "$.page_info.has_next_page"); fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, - "$.viewer.messenger_contacts.page_info.end_cursor"); + "$.page_info.delta_cursor"); + fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, + "$.page_info.end_cursor"); fb_json_values_update(values, NULL); + complete = !fb_json_values_next_bool(values, FALSE); + + delta_cursor = fb_json_values_next_str(values, NULL); + cursor = fb_json_values_next_str(values, NULL); if (G_UNLIKELY(err == NULL)) { - complete = (cursor == NULL); - g_signal_emit_by_name(api, "contacts", users, complete); + + if (is_delta || complete) { + g_free(priv->contacts_delta); + priv->contacts_delta = g_strdup(is_delta ? cursor : delta_cursor); + } + + if (users) { + g_signal_emit_by_name(api, "contacts", users, complete); + } if (!complete) { fb_api_contacts_after(api, cursor); @@ -2075,14 +2199,25 @@ fb_api_cb_contacts(FbHttpRequest *req, gpointer data) g_slist_free_full(users, (GDestroyNotify) fb_api_user_free); g_object_unref(values); + + json_node_free(croot); json_node_free(root); } void fb_api_contacts(FbApi *api) { + FbApiPrivate *priv; JsonBuilder *bldr; + g_return_if_fail(FB_IS_API(api)); + priv = api->priv; + + if (priv->contacts_delta) { + fb_api_contacts_delta(api, priv->contacts_delta); + return; + } + bldr = fb_json_bldr_new(JSON_NODE_OBJECT); fb_json_bldr_arr_begin(bldr, "0"); fb_json_bldr_add_str(bldr, NULL, "user"); @@ -2110,6 +2245,24 @@ fb_api_contacts_after(FbApi *api, const gchar *cursor) } void +fb_api_contacts_delta(FbApi *api, const gchar *delta_cursor) +{ + JsonBuilder *bldr; + + bldr = fb_json_bldr_new(JSON_NODE_OBJECT); + + fb_json_bldr_add_str(bldr, "0", delta_cursor); + + fb_json_bldr_arr_begin(bldr, "1"); + fb_json_bldr_add_str(bldr, NULL, "user"); + fb_json_bldr_arr_end(bldr); + + fb_json_bldr_add_str(bldr, "2", G_STRINGIFY(FB_API_CONTACTS_COUNT)); + fb_api_http_query(api, FB_API_QUERY_CONTACTS_DELTA, bldr, + fb_api_cb_contacts); +} + +void fb_api_connect(FbApi *api, gboolean invisible) { FbApiPrivate *priv; diff --git a/facebook/facebook-api.h b/facebook/facebook-api.h index ac4cc59..cb7467f 100644 --- a/facebook/facebook-api.h +++ b/facebook/facebook-api.h @@ -163,10 +163,8 @@ * 2: big_img_size * 3: huge_img_size * 4: small_img_size - * 5: low_res_cover_size - * 6: media_type */ -#define FB_API_QUERY_CONTACTS 10153856456271729 +#define FB_API_QUERY_CONTACTS 10154444360806729 /** * FB_API_QUERY_CONTACTS_AFTER: @@ -180,10 +178,24 @@ * 3: big_img_size * 4: huge_img_size * 5: small_img_size - * 6: low_res_cover_size - * 7: media_type */ -#define FB_API_QUERY_CONTACTS_AFTER 10153856456281729 +#define FB_API_QUERY_CONTACTS_AFTER 10154444360816729 + + +/** + * FB_API_QUERY_CONTACTS_DELTA: + * + * The query hash for the `FetchContactsDeltaQuery`. + * + * Key mapping: + * 0: after + * 1: profile_types + * 2: limit + * 3: big_img_size + * 4: huge_img_size + * 5: small_img_size + */ +#define FB_API_QUERY_CONTACTS_DELTA 10154444360801729 /** * FB_API_QUERY_STICKER: diff --git a/facebook/facebook.c b/facebook/facebook.c index 15dd6c6..fb7e2b6 100644 --- a/facebook/facebook.c +++ b/facebook/facebook.c @@ -202,16 +202,32 @@ fb_cb_sync_contacts(gpointer data, gint fd, b_input_condition cond) } static void +fb_sync_contacts_add_timeout(FbData *fata) +{ + gint sync; + struct im_connection *ic = fb_data_get_connection(fata); + account_t *acct = ic->acc; + + sync = set_getint(&acct->set, "sync_interval"); + + if (sync < 1) { + set_setint(&acct->set, "sync_interval", 1); + sync = 1; + } + + sync *= 60 * 1000; + fb_data_add_timeout(fata, "sync-contacts", sync, fb_cb_sync_contacts, + fata); +} + +static void fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data) { - account_t *acct; - bee_user_t *bu; FbApiUser *user; FbData *fata = data; FbId muid; FbBuddyData *fbd; gchar uid[FB_ID_STRMAX]; - gint sync; GSList *l; GValue val = G_VALUE_INIT; struct im_connection *ic; @@ -244,40 +260,44 @@ fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data) return; } - l = ic->bee->users; + if (!(ic->flags & OPT_LOGGED_IN)) { + imcb_log(ic, "Connecting"); + fb_api_connect(api, FALSE); + } - while (l != NULL) { - bu = l->data; - l = l->next; + fb_sync_contacts_add_timeout(fata); +} - if (bu->ic != ic) { - continue; - } +static void +fb_cb_api_contacts_delta(FbApi *api, GSList *added, GSList *removed, gpointer data) +{ + bee_user_t *bu; + FbApiUser *user; + FbData *fata = data; + gchar uid[FB_ID_STRMAX]; + GSList *l; + struct im_connection *ic; - fbd = bu->data; - if (fbd->flags.new_buddy) { - fbd->flags.new_buddy = FALSE; - } else { - imcb_remove_buddy(ic, bu->handle, NULL); - } - } + ic = fb_data_get_connection(fata); - if (!(ic->flags & OPT_LOGGED_IN)) { - imcb_log(ic, "Connecting"); - fb_api_connect(api, FALSE); + for (l = added; l != NULL; l = l->next) { + user = l->data; + FB_ID_TO_STR(user->uid, uid); + + imcb_add_buddy(ic, uid, NULL); + imcb_buddy_nick_hint(ic, uid, user->name); + imcb_rename_buddy(ic, uid, user->name); } - acct = ic->acc; - sync = set_getint(&acct->set, "sync_interval"); + for (l = removed; l != NULL; l = l->next) { + bu = imcb_buddy_by_handle(ic, l->data); - if (sync < 5) { - set_setint(&acct->set, "sync_interval", 5); - sync = 5; + if (bu) { + imcb_remove_buddy(ic, bu->handle, NULL); + } } - sync *= 60 * 1000; - fb_data_add_timeout(fata, "sync-contacts", sync, fb_cb_sync_contacts, - fata); + fb_sync_contacts_add_timeout(fata); } static void @@ -745,7 +765,7 @@ fb_init(account_t *acct) set_add(&acct->set, "mark_read", "false", fb_eval_mark_read, acct); set_add(&acct->set, "mark_read_reply", "false", set_eval_bool, acct); set_add(&acct->set, "show_unread", "false", set_eval_bool, acct); - set_add(&acct->set, "sync_interval", "30", set_eval_int, acct); + set_add(&acct->set, "sync_interval", "5", set_eval_int, acct); } static void @@ -777,6 +797,10 @@ fb_login(account_t *acc) G_CALLBACK(fb_cb_api_contacts), fata); g_signal_connect(api, + "contacts-delta", + G_CALLBACK(fb_cb_api_contacts_delta), + fata); + g_signal_connect(api, "error", G_CALLBACK(fb_cb_api_error), fata); diff --git a/facebook/marshaller.list b/facebook/marshaller.list index ab96190..ef5d4c9 100644 --- a/facebook/marshaller.list +++ b/facebook/marshaller.list @@ -4,3 +4,4 @@ VOID:POINTER VOID:POINTER,BOOLEAN VOID:STRING,BOXED VOID:VOID +VOID:POINTER,POINTER |