Facebook Graph API v2.0 + - / me / friends regresa vacío, o solo amigos que también usan mi aplicación

366

Estoy tratando de obtener mi nombre de amigo y mis identificadores con Graph API v2.0, pero los datos vuelven vacíos:

{
  "data": [
  ]
}

Cuando estaba usando v1.0, todo estaba bien con la siguiente solicitud:

FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
                                              NSDictionary* result,
                                              NSError *error) {
    NSArray* friends = [result objectForKey:@"data"];
    NSLog(@"Found: %i friends", friends.count);
    for (NSDictionary<FBGraphUser>* friend in friends) {
        NSLog(@"I have a friend named %@ with id %@", friend.name, friend.id);
    }
}];

¡Pero ahora no puedo conseguir amigos!

Kaslico
fuente
una posible superación de este problema stackoverflow.com/a/25584013/2529087
java.quaso

Respuestas:

627

En la versión 2.0 de Graph API, las llamadas /me/friendsdevuelven los amigos de la persona que también usan la aplicación.

Además, en v2.0, debe solicitar el user_friendspermiso de cada usuario. user_friendsya no se incluye por defecto en cada inicio de sesión. Cada usuario debe otorgar el user_friendspermiso para aparecer en la respuesta a /me/friends. Consulte la guía de actualización de Facebook para obtener información más detallada, o revise el resumen a continuación.

Si desea acceder a una lista de amigos que no usan aplicaciones, hay dos opciones:

  1. Si desea dejar que su gente etiquete a sus amigos en historias que publican en Facebook usando su aplicación, puede usar la/me/taggable_friendsAPI. El uso de este punto final requiere una revisión por parte de Facebook y solo debe usarse para el caso en el que está representando una lista de amigos para permitir que el usuario los etiquete en una publicación.

  2. Si su aplicación es un juego y su juego es compatible con Facebook Canvas , puede usar el/me/invitable_friendspunto final para representar un cuadro de diálogo de invitación personalizado , luego pasar los tokens devueltos por esta API al cuadro de diálogo de solicitudes estándar.

En otros casos, las aplicaciones ya no pueden recuperar la lista completa de amigos de un usuario (solo aquellos amigos que han autorizado específicamente su aplicación usando el user_friendspermiso). Esto ha sido confirmado por Facebook como 'por diseño'.

Para las aplicaciones que desean permitir que las personas inviten a amigos a usar una aplicación, aún puede usar el cuadro de diálogo Enviar en la Web o el nuevo cuadro de diálogo de mensajes en iOS y Android .

ACTUALIZACIÓN: Facebook ha publicado un FAQ sobre estos cambios aquí: https://developers.facebook.com/docs/apps/faq que explican todas las opciones disponibles para los desarrolladores para invitar a amigos, etc.

Simon Cross
fuente
¿Alguna noticia desde su última edición o estamos en la misma página?
Ilya Gazman
No se puede ver taggable_friendsen los artículos para el envío de la revisión.
AlikElzin-kilaka
77
No puedo entender por qué tuvieron que restringirlo tanto. Quiero decir que el viejo sistema era demasiado abusivo, pero este es casi inútil. Ver el perfil público de todos tus amigos seguramente no sería un problema de privacidad. A menos que piense que la conexión de la amistad es privada en sí misma (pero, una vez más, ¿por qué no es así en el caso de los amigos etiquetables?). Este no fue un movimiento para restringir el abuso de privacidad que es un efecto secundario, querían cerrar la capacidad de los desarrolladores normales (no afiliados a Facebook) para hacer crecer su negocio utilizando datos de Facebook.
Daniel Sharp
3
Facebook ha publicado recientemente cambios importantes en los datos disponibles: ver el registro de cambios . El taggable_friendsahora no devuelve nada. Además, se requiere una revisión de inicio de sesión antes de que se le otorgue user_friendspermiso.
Duncan Jones el
¿Cuál es el punto de proporcionar una API si no te permite hacer una tarea básica como enumerar a los amigos?
6infinity8
17

Aunque la respuesta de Simon Cross es aceptada y correcta, pensé en reforzarla un poco con un ejemplo (Android) de lo que hay que hacer. Lo mantendré lo más general posible y me centraré solo en la pregunta. Personalmente terminé almacenando cosas en una base de datos para que la carga fuera fluida, pero eso requiere un Adaptador de cursor y un Proveedor de contenido que está un poco fuera de alcance aquí.

Vine aquí yo mismo y luego pensé, ¿y ahora qué?

La cuestión

Al igual que el usuario 3594351 , noté que los datos de amigos estaban en blanco. Descubrí esto usando el FriendPickerFragment. Lo que funcionó hace tres meses, ya no funciona. Incluso los ejemplos de Facebook se rompieron. Entonces mi problema fue '¿Cómo creo FriendPickerFragment a mano?

Lo que no funcionó

La opción # 1 de Simon Cross no fue lo suficientemente fuerte como para invitar a amigos a la aplicación. Simon Cross también recomendó el Diálogo de solicitudes, pero eso solo permitiría cinco solicitudes a la vez. El cuadro de diálogo de solicitudes también mostró los mismos amigos durante cualquier sesión de Facebook iniciada sesión. Inútil.

Lo que funcionó (resumen)

Opción # 2 con algo de trabajo duro. Debes asegurarte de cumplir con las nuevas reglas de Facebook: 1.) Eres un juego 2.) Tienes una aplicación Canvas (Presencia web) 3.) Tu aplicación está registrada en Facebook. Todo se hace en el sitio web para desarrolladores de Facebook en Configuración .

Para emular el selector de amigos a mano dentro de mi aplicación, hice lo siguiente:

  1. Cree una actividad de pestaña que muestre dos fragmentos. Cada fragmento muestra una lista. Un fragmento para un amigo disponible ( / me / friends ) y otro para amigos invitables ( / me / invitable_friends ). Use el mismo código de fragmento para representar ambas pestañas.
  2. Cree una AsyncTask que obtenga los datos de amigos de Facebook. Una vez que se cargan esos datos, tírelos al adaptador que representará los valores en la pantalla.

Detalles

The AsynchTask

private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {

    private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
    GraphObject graphObject;
    ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();

    @Override
    protected Boolean doInBackground(FacebookFriend.Type... pickType) {
        //
        // Determine Type
        //
        String facebookRequest;
        if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
            facebookRequest = "/me/friends";
        } else {
            facebookRequest = "/me/invitable_friends";
        }

        //
        // Launch Facebook request and WAIT.
        //
        new Request(
            Session.getActiveSession(),
            facebookRequest,
            null,
            HttpMethod.GET,
            new Request.Callback() {
                public void onCompleted(Response response) {
                    FacebookRequestError error = response.getError();
                    if (error != null && response != null) {
                        Log.e(TAG, error.toString());
                    } else {
                        graphObject = response.getGraphObject();
                    }
                }
            }
        ).executeAndWait();

        //
        // Process Facebook response
        //
        //
        if (graphObject == null) {
            return false;
        }

        int numberOfRecords = 0;
        JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
        if (dataArray.length() > 0) {

            // Ensure the user has at least one friend ...
            for (int i = 0; i < dataArray.length(); i++) {

                JSONObject jsonObject = dataArray.optJSONObject(i);
                FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);

                if (facebookFriend.isValid()) {
                    numberOfRecords++;

                    myList.add(facebookFriend);
                }
            }
        }

        // Make sure there are records to process
        if (numberOfRecords > 0){
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Boolean... booleans) {
        // No need to update this, wait until the whole thread finishes.
    }

    @Override
    protected void onPostExecute(Boolean result) {
        if (result) {
            /*
            User the array "myList" to create the adapter which will control showing items in the list.
             */

        } else {
            Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
        }
    }
}

La clase de FacebookFriend que creé

public class FacebookFriend {

    String facebookId;
    String name;
    String pictureUrl;
    boolean invitable;
    boolean available;
    boolean isValid;
    public enum Type {AVAILABLE, INVITABLE};

    public FacebookFriend(JSONObject jsonObject, Type type) {
        //
        //Parse the Facebook Data from the JSON object.
        //
        try {
            if (type == Type.INVITABLE) {
                //parse /me/invitable_friend
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");

                // Handle the picture data.
                JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
                boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
                if (!isSilhouette) {
                    this.pictureUrl = pictureJsonObject.getString("url");

                } else {
                    this.pictureUrl = "";
                }

                this.invitable = true;
            } else {
                // Parse /me/friends
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");
                this.available = true;
                this.pictureUrl = "";
            }

            isValid = true;
        } catch (JSONException e) {
            Log.w("#", "Warnings - unable to process Facebook JSON: " + e.getLocalizedMessage());
        }
    }
}
LEÓN
fuente
20
¿Qué tal para aplicaciones que no son de juego? Un sitio web de citas ... un juego para encontrar personas, una aplicación para encontrar restaurantes ... un juego para encontrar restaurantes, etc. Entonces, ¿está REALMENTE cerca de los juegos o es solo una forma de obligarlo a colocar una aplicación dentro de Facebook y gastar dinero en su plataforma de publicidad? ¿Quién va a decidir qué es un juego? ¿Quién decidió que tu aplicación es realmente un juego o no? No me malinterpretes en esto, pero para mí no tiene sentido bloquear aplicaciones que no sean de juego a menos que solo quieras obligarlas a usar el ecosistema de Facebook.
Mário
@ Mário Esa es la intención de Facebook bajo el capó. Puedes poner cualquier cosa y marcarlo como un juego, ya que no les importa si realmente es un juego o no. todo lo que les importa es sacar dinero de su bolsillo.
sandalone
12

Facebook ha revisado sus políticas ahora. De todos modos, no puede obtener toda la lista de amigos si su aplicación no tiene una implementación de Canvas y si su aplicación no es un juego. Por supuesto, también hay taggable_friends, pero ese es solo para etiquetar.

Podrá extraer la lista de amigos que solo han autorizado la aplicación.

Las aplicaciones que usan Graph API 1.0 estarán funcionando hasta el 30 de abril de 2015 y después de eso quedarán en desuso.

Consulte lo siguiente para obtener más detalles sobre esto:

Jobins John
fuente
3

En Swift 4.2 y Xcode 10.1:

Si desea obtener la lista de amigos de Facebook, debe enviar su aplicación para su revisión en Facebook. Vea algunos de los permisos de inicio de sesión:

Permisos de inicio de sesión

Aquí están los dos pasos:

1) Primero, el estado de su aplicación debe estar en Live

2) Obtenga los permisos requeridos de Facebook.

1) Habilite el estado de nuestra aplicación en vivo:

  1. Vaya a la página de aplicaciones y seleccione su aplicación

    https://developers.facebook.com/apps/

  2. Seleccione el estado en la esquina superior derecha en el Tablero .

    Ingrese la descripción de la imagen aquí

  3. Enviar URL de política de privacidad

    Ingrese la descripción de la imagen aquí

  4. Seleccione categoría

    Ingrese la descripción de la imagen aquí

  5. Ahora nuestra aplicación está en estado Live .

    Ingrese la descripción de la imagen aquí

Se completa un paso.

2) Envíe nuestra aplicación para su revisión:

  1. Primero envíe las solicitudes requeridas.

    Ejemplo: user_friends, user_videos, user_posts, etc.

    Ingrese la descripción de la imagen aquí

  2. En segundo lugar, vaya a la página Solicitud actual

    Ingrese la descripción de la imagen aquí

    Ejemplo: user_events

  3. Enviar todos los detalles

    Ingrese la descripción de la imagen aquí

  4. Al igual que este envío para todas las solicitudes (user_friends, user_events, user_videos, user_posts, etc.).

  5. Finalmente envíe su aplicación para su revisión.

    Si su opinión es aceptada por parte de Facebook, ahora es elegible para leer contactos, etc.

iOS
fuente
3
El user_friendspermiso es muy limitado. De la documentación de Facebook (abril de 2019): [Esto solo] otorga permiso a una aplicación para acceder a una lista de amigos que también usan dicha aplicación. Este permiso está restringido a un conjunto limitado de socios y su uso requiere la aprobación previa de Facebook. Para que una persona aparezca en la lista de amigos de otra persona, ambas deben haber compartido su lista de amigos con la aplicación y no haber deshabilitado este permiso durante el inicio de sesión.
dearsina
2

Como Simon mencionó, esto no es posible en la nueva API de Facebook. Técnicamente hablando , puede hacerlo a través de la automatización del navegador.

  • esto va en contra de la política de Facebook, por lo que, dependiendo del país donde viva, esto puede no ser legal
  • tendrá que usar sus credenciales / solicitar credenciales al usuario y posiblemente almacenarlas (almacenar contraseñas incluso cifradas simétricamente no es una buena idea)
  • cuando Facebook cambie su API, también deberá actualizar el código de automatización del navegador (si no puede forzar las actualizaciones de su aplicación, debe colocar la pieza de automatización del navegador como un servicio web)
  • esto pasa por alto el concepto OAuth
  • Por otro lado, creo que soy dueño de mis datos, incluida la lista de mis amigos, y Facebook no debería restringirme para acceder a ellos a través de la API

Implementación de muestra usando WatiN :

class FacebookUser
{
  public string Name { get; set; }
  public long Id { get; set; }
}

public IList<FacebookUser> GetFacebookFriends(string email, string password, int? maxTimeoutInMilliseconds)
{
  var users = new List<FacebookUser>();
  Settings.Instance.MakeNewIeInstanceVisible = false;
  using (var browser = new IE("https://www.facebook.com"))
  {
    try
    {
      browser.TextField(Find.ByName("email")).Value = email;
      browser.TextField(Find.ByName("pass")).Value = password;
      browser.Form(Find.ById("login_form")).Submit();
      browser.WaitForComplete();
    }
    catch (ElementNotFoundException)
    {
      // We're already logged in
    }
    browser.GoTo("https://www.facebook.com/friends");
    var watch = new Stopwatch();
    watch.Start();

    Link previousLastLink = null;
    while (maxTimeoutInMilliseconds.HasValue && watch.Elapsed.TotalMilliseconds < maxTimeoutInMilliseconds.Value)
    {
      var lastLink = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
                       && l.GetAttributeValue("data-hovercard").Contains("user.php")
                       && l.Text != null
                     ).LastOrDefault();

      if (lastLink == null || previousLastLink == lastLink)
      {
        break;
      }

      var ieElement = lastLink.NativeElement as IEElement;
      if (ieElement != null)
      {
        var htmlElement = ieElement.AsHtmlElement;
        htmlElement.scrollIntoView();
        browser.WaitForComplete();
      }

      previousLastLink = lastLink;
    }

    var links = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
      && l.GetAttributeValue("data-hovercard").Contains("user.php")
      && l.Text != null
    ).ToList();

    var idRegex = new Regex("id=(?<id>([0-9]+))");
    foreach (var link in links)
    {
      string hovercard = link.GetAttributeValue("data-hovercard");
      var match = idRegex.Match(hovercard);
      long id = 0;
      if (match.Success)
      {
        id = long.Parse(match.Groups["id"].Value);
      }
      users.Add(new FacebookUser
      {
        Name = link.Text,
        Id = id
      });
    }
  }
  return users;
}

Prototipo con implementación de este enfoque (usando C # / WatiN) ver https://github.com/svejdo1/ShadowApi . También está permitiendo la actualización dinámica del conector de Facebook que está recuperando una lista de sus contactos.

Ondrej Svejdar
fuente
2
Su código asume que la aplicación tiene acceso al nombre de usuario y contraseña del usuario. Así no es como funciona OAuth, y la mayoría de los usuarios no van a proporcionar su nombre de usuario y contraseña de FB a una aplicación. Eso sería un gran riesgo de seguridad.
jbustamovej
32
Esto va en contra de la política de Facebook. Este consejo no debe seguirse.
Simon Cross
3
En otra nota, ni siquiera es seguro proteger las credenciales de su usuario en su pirateo. En caso de que alguien decida usar su código, asegúrese de que use SSL cambiando la uri a https.
Rogala
8
¡Más uno por meterse la cara!
Skynet
12
Todos parece que las políticas de Facebook son algún tipo de derecho internacional y, al romperlo, eres un criminal. También crees que Facebook solo tiene en mente lo mejor para el usuario, lo que creo que está mal, como dijo @OndrejSvejdar, porque entonces simplemente te dejarían elegir si quieres que te vean en otras aplicaciones. Creo que esto es solo un movimiento de Facebook para interrumpir el crecimiento de otros a través de su plataforma GRATIS. En cambio, las personas deberían comenzar a desarrollar sus aplicaciones, servicios, juegos, noticias, etc. directamente en Facebook. Y pague por la publicidad de Facebook cuando no lo hagan.
JustGoscha
2

Intenta /me/taggable_friends?limit=5000usar tu código JavaScript

O

prueba la API Graph :

https://graph.facebook.com/v2.3/user_id_here/taggable_friends?access_token=

Abhishek Sharma
fuente
2
Como creo, las votaciones negativas aquí se deben a que "el uso de este punto final requiere una revisión por parte de Facebook y solo debe usarse para el caso en el que se presenta una lista de amigos para permitir que el usuario los etiquete en una publicación". Puede leer más sobre esto en la respuesta seleccionada.
moonvader
{"data": [], "summary": {"total_count": 414}} obtuve este bloque de datos json nulo, no tengo amigos. ¿Que puedo hacer?
Makvin
-1

En Facebook SDK Graph API v2.0 o superior, debe solicitar el permiso user_friends a cada usuario en el momento del inicio de sesión en Facebook, ya que user_friends ya no se incluye de forma predeterminada en cada inicio de sesión; Tenemos que agregar eso.

Cada usuario debe otorgar el permiso user_friends para aparecer en la respuesta a / me / friends .

let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
fbLoginManager.logIn(withReadPermissions: ["email","user_friends","public_profile"], from: self) { (result, error) in
    if (error == nil) {

        let fbloginresult : FBSDKLoginManagerLoginResult = result!
        if fbloginresult.grantedPermissions != nil {
            if (fbloginresult.grantedPermissions.contains("email")) {
                // Do the stuff
            }
            else {
            }
        }
        else {
        }
    }
}

Entonces, en el momento del inicio de sesión en Facebook, aparece una pantalla que contiene todos los permisos:

Ingrese la descripción de la imagen aquí

Si el usuario presiona el botón Continuar , se establecerán los permisos. Cuando acceda a la lista de amigos con Graph API, se enumerarán sus amigos que iniciaron sesión en la aplicación como se indicó anteriormente.

if ((FBSDKAccessToken.current()) != nil) {
    FBSDKGraphRequest(graphPath: "/me/friends", parameters: ["fields" : "id,name"]).start(completionHandler: { (connection, result, error) -> Void in
        if (error == nil) {

            print(result!)
        }
    })
}

La salida contendrá los usuarios que otorgaron el permiso user_friends en el momento de iniciar sesión en su aplicación a través de Facebook.

{
    data = (
             {
                 id = xxxxxxxxxx;
                 name = "xxxxxxxx";
             }
           );
    paging = {
        cursors = {
            after = xxxxxx;
            before = xxxxxxx;
        };
    };
    summary = {
        "total_count" = 8;
    };
}
Lineesh K Mohan
fuente