/soc/2015/jgeboski/facebook: 213498a7ab94: facebook: fetch conta...

James Geboski jgeboski at gmail.com
Sat Jul 18 09:43:11 EDT 2015


Changeset: 213498a7ab9453d1a9ae1ae6e8a91770299780b5
Author:	 James Geboski <jgeboski at gmail.com>
Date:	 2015-07-18 09:42 -0400
Branch:	 facebook
URL: https://hg.pidgin.im/soc/2015/jgeboski/facebook/rev/213498a7ab94

Description:

facebook: fetch contacts in incremental chunks

When a user has several thousand contacts, the HTTP request can fail on
Facebook's side due to the large amount of data. The data for each user
is quite large, and when trying to fetch over one thousand users in a
single request, Facebook will likely error.

In order to resolve the issue, the contacts are fetched in incremental
chunks of 200. This is an arbitrary number, but seems safe enough not
to cause any issues, while also not sending an excessive amount of HTTP
requests.

This fixes the HTTP 500 error many people were receiving when opening a
Facebook connection, and having a significant number of friends.

diffstat:

 libpurple/protocols/facebook/api.c           |  98 ++++++++++++++++++++++++++--
 libpurple/protocols/facebook/api.h           |   5 +-
 libpurple/protocols/facebook/facebook.c      |  11 ++-
 libpurple/protocols/facebook/marshaller.list |   1 +
 4 files changed, 104 insertions(+), 11 deletions(-)

diffs (247 lines):

diff --git a/libpurple/protocols/facebook/api.c b/libpurple/protocols/facebook/api.c
--- a/libpurple/protocols/facebook/api.c
+++ b/libpurple/protocols/facebook/api.c
@@ -58,6 +58,9 @@ struct _FbApiPrivate
 
 };
 
+static void
+fb_api_contacts_after(FbApi *api, const gchar *writeid);
+
 G_DEFINE_TYPE(FbApi, fb_api, G_TYPE_OBJECT);
 
 static void
@@ -217,9 +220,9 @@ fb_api_class_init(FbApiClass *klass)
 	             G_SIGNAL_ACTION,
 	             0,
 	             NULL, NULL,
-	             fb_marshal_VOID__POINTER,
+	             fb_marshal_VOID__POINTER_BOOLEAN,
 	             G_TYPE_NONE,
-	             1, G_TYPE_POINTER);
+	             2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
 	g_signal_new("error",
 	             G_TYPE_FROM_CLASS(klass),
 	             G_SIGNAL_ACTION,
@@ -321,7 +324,9 @@ fb_api_json_chk(FbApi *api, gconstpointe
 	root = fb_json_node_new(data, size, &err);
 	FB_API_ERROR_CHK(api, err, return FALSE);
 
-	if (fb_json_node_chk_str(root, "$.failedSend.errorMessage", &msg)) {
+	if (fb_json_node_chk_str(root, "$.error.summary", &msg) ||
+	    fb_json_node_chk_str(root, "$.failedSend.errorMessage", &msg))
+	{
 		fb_api_error(api, FB_API_ERROR_GENERAL, "%s", msg);
 	} else if (fb_json_node_chk_int(root, "$.error_code", &code)) {
 		if (!fb_json_node_chk_str(root, "$.error_msg", &msg)) {
@@ -1007,6 +1012,7 @@ fb_api_cb_contacts(PurpleHttpConnection 
 	FbApiUser user;
 	FbHttpParams *params;
 	gchar *str;
+	gchar *writeid = NULL;
 	GError *err = NULL;
 	GList *elms = NULL;
 	GList *l;
@@ -1031,6 +1037,11 @@ fb_api_cb_contacts(PurpleHttpConnection 
 		node = l->data;
 		fb_api_user_reset(&user, FALSE);
 
+		g_free(writeid);
+		writeid = fb_json_node_get_str(node, "$.graph_api_write_id",
+		                               &err);
+		FB_API_ERROR_CHK(api, err, goto finish);
+
 		str = fb_json_node_get_str(node, "$.represented_profile.id",
 		                           NULL);
 
@@ -1045,7 +1056,7 @@ fb_api_cb_contacts(PurpleHttpConnection 
 		                                 "$.structured_name.text",
 		                                 NULL);
 		user.icon = fb_json_node_get_str(node,
-		                                 "$.huge_picture_url.uri",
+		                                 "$.hugePictureUrl.uri",
 		                                 NULL);
 
 		params = fb_http_params_new_parse(user.icon, TRUE);
@@ -1057,13 +1068,18 @@ fb_api_cb_contacts(PurpleHttpConnection 
 		users = g_slist_prepend(users, mptr);
 	}
 
-	g_signal_emit_by_name(api, "contacts", users);
+	g_signal_emit_by_name(api, "contacts", users, writeid == NULL);
+
+	if (writeid != NULL) {
+		fb_api_contacts_after(api, writeid);
+	}
 
 finish:
 	if (G_LIKELY(arr != NULL)) {
 		json_array_unref(arr);
 	}
 
+	g_free(writeid);
 	g_list_free(elms);
 	g_slist_free_full(users, (GDestroyNotify) fb_api_user_free);
 	json_node_free(root);
@@ -1073,18 +1089,86 @@ void
 fb_api_contacts(FbApi *api)
 {
 	FbHttpParams *prms;
+	gchar *json;
+	JsonBuilder *bldr;
 
 	static const FbApiHttpInfo info = {
 		fb_api_cb_contacts,
-		"com.facebook.contacts.service.d",
+		"com.facebook.contacts.service.c",
 		"FetchContactsFullQuery",
 		"get"
 	};
 
+	/* Object key mapping:
+	 *   0 = profile_types
+	 *   1 = limit
+	 *   2 = small_img_size
+	 *   3 = big_img_size
+	 *   4 = huge_img_size
+	 *   5 = low_res_cover_size
+	 *   6 = media_type
+	 *   7 = high_res_cover_size
+	 */
+
+	bldr = fb_json_bldr_new(JSON_NODE_OBJECT);
+	fb_json_bldr_arr_begin(bldr, "0");
+	fb_json_bldr_add_str(bldr, NULL, "user");
+	fb_json_bldr_arr_end(bldr);
+
+	fb_json_bldr_add_str(bldr, "1", FB_API_CONTACTS_COUNT);
+
+	json = fb_json_bldr_close(bldr, JSON_NODE_OBJECT, NULL);
 	prms = fb_http_params_new();
 	fb_http_params_set_str(prms, "query_id", FB_API_QRYID_CONTACTS);
-	fb_http_params_set_str(prms, "query_params", "{}");
+	fb_http_params_set_str(prms, "query_params", json);
 	fb_api_http_req(api, &info, prms, FB_API_URL_GQL);
+	g_free(json);
+}
+
+static void
+fb_api_contacts_after(FbApi *api, const gchar *writeid)
+{
+	FbHttpParams *prms;
+	gchar *json;
+	JsonBuilder *bldr;
+
+	static const FbApiHttpInfo info = {
+		fb_api_cb_contacts,
+		"com.facebook.contacts.service.c",
+		"FetchContactsFullWithAfterQuery",
+		"get"
+	};
+
+	/* Object key mapping:
+	 *   0 = profile_types
+	 *   1 = after
+	 *   2 = limit
+	 *   3 = small_img_size
+	 *   4 = big_img_size
+	 *   5 = huge_img_size
+	 *   6 = low_res_cover_size
+	 *   7 = media_type
+	 *   8 = high_res_cover_size
+	 */
+
+	if (g_str_has_prefix(writeid, "contact_")) {
+		writeid += 8;
+	}
+
+	bldr = fb_json_bldr_new(JSON_NODE_OBJECT);
+	fb_json_bldr_arr_begin(bldr, "0");
+	fb_json_bldr_add_str(bldr, NULL, "user");
+	fb_json_bldr_arr_end(bldr);
+
+	fb_json_bldr_add_str(bldr, "1", writeid);
+	fb_json_bldr_add_str(bldr, "2", FB_API_CONTACTS_COUNT);
+
+	json = fb_json_bldr_close(bldr, JSON_NODE_OBJECT, NULL);
+	prms = fb_http_params_new();
+	fb_http_params_set_str(prms, "query_id", FB_API_QRYID_CONTACTS_AFTER);
+	fb_http_params_set_str(prms, "query_params", json);
+	fb_api_http_req(api, &info, prms, FB_API_URL_GQL);
+	g_free(json);
 }
 
 void
diff --git a/libpurple/protocols/facebook/api.h b/libpurple/protocols/facebook/api.h
--- a/libpurple/protocols/facebook/api.h
+++ b/libpurple/protocols/facebook/api.h
@@ -36,6 +36,8 @@
 #define FB_API_KEY     "256002347743983"
 #define FB_API_SECRET  "374e60f8b9bb6b8cbb30f78030438895"
 
+#define FB_API_CONTACTS_COUNT  "200"
+
 #define FB_API_URL_AUTH   FB_API_BHOST "/method/auth.login"
 #define FB_API_URL_FQL    FB_API_GHOST "/fql"
 #define FB_API_URL_GQL    FB_API_GHOST "/graphql"
@@ -43,7 +45,8 @@
 #define FB_API_URL_THRDS  FB_API_GHOST "/me/threads"
 #define FB_API_URL_TOPIC  FB_API_HOST  "/method/messaging.setthreadname"
 
-#define FB_API_QRYID_CONTACTS  "10153122424521729"
+#define FB_API_QRYID_CONTACTS        "10153746900696729"
+#define FB_API_QRYID_CONTACTS_AFTER  "10153746900731729"
 
 #define FB_TYPE_API             (fb_api_get_type())
 #define FB_API(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), FB_TYPE_API, FbApi))
diff --git a/libpurple/protocols/facebook/facebook.c b/libpurple/protocols/facebook/facebook.c
--- a/libpurple/protocols/facebook/facebook.c
+++ b/libpurple/protocols/facebook/facebook.c
@@ -100,7 +100,7 @@ fb_cb_data_icon(PurpleHttpConnection *co
 }
 
 static void
-fb_cb_api_contacts(FbApi *api, GSList *users, gpointer data)
+fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data)
 {
 	const gchar *alias;
 	const gchar *csum;
@@ -113,12 +113,14 @@ fb_cb_api_contacts(FbApi *api, GSList *u
 	PurpleAccount *acct;
 	PurpleBuddy *bdy;
 	PurpleConnection *gc;
+	PurpleConnectionState state;
 	PurpleGroup *grp;
 
 	gc = fb_data_get_connection(fata);
 	acct = purple_connection_get_account(gc);
 	grp = purple_blist_get_default_group();
 	alias = purple_account_get_private_alias(acct);
+	state = purple_connection_get_state(gc);
 
 	g_value_init(&val, FB_TYPE_ID);
 	g_object_get_property(G_OBJECT(api), "uid", &val);
@@ -157,8 +159,11 @@ fb_cb_api_contacts(FbApi *api, GSList *u
 	}
 
 	fb_data_icon_queue(fata);
-	purple_connection_update_progress(gc, _("Connecting"), 3, 4);
-	fb_api_connect(api);
+
+	if (complete && (state != PURPLE_CONNECTION_CONNECTED)) {
+		purple_connection_update_progress(gc, _("Connecting"), 3, 4);
+		fb_api_connect(api);
+	}
 }
 
 static void
diff --git a/libpurple/protocols/facebook/marshaller.list b/libpurple/protocols/facebook/marshaller.list
--- a/libpurple/protocols/facebook/marshaller.list
+++ b/libpurple/protocols/facebook/marshaller.list
@@ -1,5 +1,6 @@
 VOID:INT64
 VOID:OBJECT
 VOID:POINTER
+VOID:POINTER,BOOLEAN
 VOID:STRING,BOXED
 VOID:VOID



More information about the Commits mailing list