aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarius Halden <marius.h@lden.org>2017-01-08 19:58:10 +0100
committerMarius Halden <marius.h@lden.org>2017-01-08 19:58:10 +0100
commit9da4ee0315ec612e52a39aedb8a83ae4e6d51361 (patch)
treef4a9622e43da6cce19f87bcfb419a4f095f1ba88
parentdfc6d6593cad26ffef63657b2abdd63a9420e2bb (diff)
parent82e6bcf0e6f22eeca7eda7ea95aaf8378df6ddef (diff)
downloadbitlbee-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.c187
-rw-r--r--facebook/facebook-api.h24
-rw-r--r--facebook/facebook.c82
-rw-r--r--facebook/marshaller.list1
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