Interfejs Facebook Graph API v2.0 + - / ja / znajomi zwraca puste lub tylko znajomi, którzy również korzystają z mojej aplikacji

366

Usiłuję uzyskać nazwę mojego znajomego i identyfikatory za pomocą Graph API v2.0, ale dane są puste:

{
  "data": [
  ]
}

Kiedy korzystałem z wersji 1.0, wszystko było w porządku z następującym żądaniem:

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);
    }
}];

Ale teraz nie mogę znaleźć przyjaciół!

Kaslico
źródło
możliwe rozwiązanie tego problemu stackoverflow.com/a/25584013/2529087
java.quaso

Odpowiedzi:

627

W wersji 2.0 Graph API wywoływanie /me/friendszwraca znajomych osoby, która również korzysta z aplikacji.

Ponadto w wersji 2.0 należy poprosić o user_friendspozwolenie każdego użytkownika. user_friendsnie jest już domyślnie uwzględniany przy każdym logowaniu. Każdy użytkownik musi udzielić user_friendspozwolenia, aby pojawić się w odpowiedzi na adres /me/friends. Aby uzyskać bardziej szczegółowe informacje, zapoznaj się z przewodnikiem aktualizacji na Facebooku lub przejrzyj podsumowanie poniżej.

Jeśli chcesz uzyskać dostęp do listy znajomych nieużywających aplikacji, dostępne są dwie opcje:

  1. Jeśli chcesz, aby Twoi ludzie oznaczali znajomych swoimi historiami, które publikują na Facebooku za pomocą Twojej aplikacji, możesz użyć/me/taggable_friendsinterfejsu API. Korzystanie z tego punktu końcowego wymaga przeglądu przez Facebooka i powinno być stosowane tylko w przypadku, gdy renderujesz listę znajomych, aby umożliwić użytkownikowi oznaczenie ich w poście.

  2. Jeśli Twoja aplikacja jest grą ORAZ Twoja gra obsługuje Facebook Canvas , możesz użyć/me/invitable_friendspunktu końcowego, aby wyświetlić niestandardowe okno dialogowe zaproszenia , a następnie przekaż tokeny zwrócone przez ten interfejs API do standardowego okna dialogowego Żądania.

W innych przypadkach aplikacje nie są już w stanie odzyskać pełnej listy przyjaciół użytkownika (tylko ci znajomi, którzy specjalnie autoryzowali twoją aplikację za pomocą user_friendspozwolenia). Zostało to potwierdzone przez Facebooka jako „zgodnie z projektem”.

W przypadku aplikacji, które chcą umożliwić znajomym zapraszanie znajomych do korzystania z aplikacji, możesz nadal korzystać z okna dialogowego Wyślij w sieci Web lub nowego okna dialogowego wiadomości w systemie iOS i Android .

AKTUALIZACJA: Facebook opublikował FAQ na temat tych zmian tutaj: https://developers.facebook.com/docs/apps/faq, które wyjaśniają wszystkie opcje dostępne dla programistów w celu zaproszenia znajomych itp.

Simon Cross
źródło
Jakieś wiadomości od czasu ostatniej edycji lub jesteśmy na tej samej stronie?
Ilya Gazman,
Nie widzę taggable_friendselementów w celu przesłania recenzji.
AlikElzin-kilaka
7
Nie rozumiem, dlaczego musieli tak bardzo to ograniczać. Mam na myśli, że stary system był zbyt nadużywalny, ale ten jest prawie bezużyteczny. Wyświetlanie profilu publicznego wszystkich znajomych z pewnością nie stanowiłoby problemu z prywatnością. Chyba że uważasz, że związek przyjaźni ma charakter prywatny (ale dlaczego nie jest tak w przypadku tagowanych przyjaciół). Nie było to posunięcie mające na celu ograniczenie nadużywania prywatności, które jest efektem ubocznym. Chcieli oni ograniczyć zdolność normalnych (niezwiązanych z Facebookiem) programistów do rozwijania działalności przy użyciu danych z Facebooka.
Daniel Sharp,
3
Facebook opublikował niedawno poważne zmiany w dostępnych danych: patrz dziennik zmian . taggable_friendsTeraz nic nie zwraca. Przed uzyskaniem user_friendspozwolenia wymagana jest weryfikacja logowania .
Duncan Jones
Jaki jest sens udostępniania interfejsu API, jeśli nie pozwala on wykonać podstawowego zadania, takiego jak wystawienie listy znajomych?
6infinity8
17

Mimo że odpowiedź Simona Crossa została zaakceptowana i poprawna, pomyślałem, że wzmocnię ją nieco przykładem (Androida) tego, co należy zrobić. Utrzymam to tak ogólne, jak to możliwe i skupię się tylko na pytaniu. Osobiście zakończyłem przechowywanie rzeczy w bazie danych, aby ładowanie przebiegało płynnie, ale wymaga to CursorAdapter i ContentProvider, który jest nieco poza zakresem.

Sam tu przyszedłem, a potem pomyślałem, co teraz ?!

Problem

Podobnie jak user3594351 , zauważyłem, że dane znajomego są puste. Dowiedziałem się tego za pomocą FriendPickerFragment. To, co działało trzy miesiące temu, już nie działa. Zepsuły się nawet przykłady na Facebooku. Więc mój problem brzmiał: „Jak ręcznie utworzyć FriendPickerFragment?

Co nie działa

Opcja nr 1 z Simon Cross nie była wystarczająco silna, aby zaprosić znajomych do aplikacji. Simon Cross zalecił także okno dialogowe Żądania, ale pozwoliłoby to na przyjęcie tylko pięciu żądań na raz. Okno dialogowe żądań pokazywało również tych samych znajomych podczas każdej sesji zalogowania na Facebooku. Nieprzydatne.

Co działało (Podsumowanie)

Opcja nr 2 przy odrobinie ciężkiej pracy. Musisz upewnić się, że spełniasz nowe zasady Facebooka: 1.) Jesteś grą 2.) Masz aplikację Canvas (Obecność w sieci) 3.) Twoja aplikacja jest zarejestrowana na Facebooku. Wszystko to odbywa się na stronie programisty Facebooka w Ustawieniach .

Aby ręcznie emulować próbnik wyboru znajomych w mojej aplikacji, wykonałem następujące czynności:

  1. Utwórz działanie tab, które pokazuje dwa fragmenty. Każdy fragment pokazuje listę. Jeden fragment dla dostępnego przyjaciela ( / ja / przyjaciele ), a drugi dla zaproszonych przyjaciół ( / me / invitable_friends ). Użyj tego samego kodu fragmentu, aby wyświetlić obie karty.
  2. Utwórz AsyncTask, który pobierze dane znajomych z Facebooka. Po załadowaniu danych wrzuć je do adaptera, który wyświetli wartości na ekranie.

Detale

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);
        }
    }
}

Stworzyłem klasę FacebookFriend

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());
        }
    }
}
LEW
źródło
20
Co powiesz na aplikacje inne niż do gier? Witryna randkowa ... gra, aby znaleźć ludzi, aplikacja, aby znaleźć restauracje ... gra, aby znaleźć restauracje, itp. Masz pomysł. Czy to NAPRAWDĘ zbliżone do gier, czy jest to tylko sposób zmuszenia Cię do umieszczenia aplikacji na Facebooku i wydawania pieniędzy na platformę reklamową? Kto zdecyduje, co to jest gra? Kto zdecydował, że twoja aplikacja jest grą, czy nie? Nie zrozum mnie źle, ale dla mnie nie ma sensu blokować aplikacji innych niż gry, chyba że chcesz tylko zmusić je do korzystania z ekosystemu Facebooka.
Mário
@ Mário To intencja Facebooka pod maską. Możesz umieścić wszystko i oznaczyć to jako grę, ponieważ nie obchodzi ich, czy to naprawdę gra, czy nie. jedyne, na czym im zależy, to pieniądze z kieszeni.
sandalone
12

Facebook zmienił teraz swoje zasady. Tak czy inaczej, nie możesz uzyskać całej listy znajomych, jeśli twoja aplikacja nie ma implementacji Canvas i jeśli nie jest to gra. Oczywiście są też taggable_friends, ale ten służy wyłącznie do tagowania.

Będziesz mógł wyciągnąć listę przyjaciół, którzy autoryzowali tylko aplikację.

Aplikacje korzystające z Graph API 1.0 będą działać do 30 kwietnia 2015 r., A następnie będą przestarzałe.

Zobacz następujące informacje, aby uzyskać więcej informacji na ten temat:

Jobins John
źródło
3

W Swift 4.2 i Xcode 10.1:

Jeśli chcesz pobrać listę znajomych z Facebooka, musisz przesłać swoją aplikację do sprawdzenia na Facebooku. Zobacz niektóre uprawnienia do logowania:

Uprawnienia do logowania

Oto dwa kroki:

1) Najpierw musisz mieć status aplikacji na żywo

2) Uzyskaj wymagane uprawnienia z Facebooka.

1) Włącz status naszej aplikacji na żywo:

  1. Przejdź do strony aplikacji i wybierz aplikację

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

  2. Wybierz status w prawym górnym rogu pulpitu nawigacyjnego .

    Wpisz opis zdjęcia tutaj

  3. Prześlij adres URL polityki prywatności

    Wpisz opis zdjęcia tutaj

  4. Wybierz kategorię

    Wpisz opis zdjęcia tutaj

  5. Teraz nasza aplikacja ma status Live .

    Wpisz opis zdjęcia tutaj

Jeden krok został zakończony.

2) Prześlij naszą aplikację do sprawdzenia:

  1. Najpierw wyślij wymagane wnioski.

    Przykład: user_friends, user_videos, user_posts itp.

    Wpisz opis zdjęcia tutaj

  2. Po drugie, przejdź do strony Bieżące żądanie

    Wpisz opis zdjęcia tutaj

    Przykład: user_events

  3. Prześlij wszystkie szczegóły

    Wpisz opis zdjęcia tutaj

  4. Podobnie jak to w przypadku wszystkich żądań (user_friends, user_events, user_videos, user_posts itp.).

  5. Na koniec prześlij swoją aplikację do sprawdzenia.

    Jeśli Twoja opinia zostanie zaakceptowana ze strony Facebooka, możesz teraz czytać kontakty itp.

iOS
źródło
3
user_friendsUprawnienie jest bardzo ograniczona. Z dokumentacji Facebooka (kwiecień 2019 r.): [To tylko] przyznaje aplikacji zezwolenie na dostęp do listy znajomych, którzy również korzystają z tej aplikacji. Zezwolenie jest ograniczone do ograniczonego zestawu partnerów, a korzystanie z niego wymaga uprzedniej zgody Facebooka. Aby osoba mogła pojawić się na liście znajomych innej osoby, obie osoby muszą udostępnić swoją listę znajomych aplikacji i nie mogą wyłączać tego uprawnienia podczas logowania.
dearsina
2

Jak wspomniał Simon, nie jest to możliwe w nowym interfejsie API Facebooka. Z technicznego punktu widzenia można to zrobić za pomocą automatyzacji przeglądarki.

  • jest to sprzeczne z polityką Facebooka, więc w zależności od kraju, w którym mieszkasz, może to nie być legalne
  • będziesz musiał użyć swoich poświadczeń / poprosić użytkownika o poświadczenia i ewentualnie je zapisać (przechowywanie haseł nawet zaszyfrowanych symetrycznie nie jest dobrym pomysłem)
  • gdy Facebook zmienia interfejs API, musisz także zaktualizować kod automatyzacji przeglądarki (jeśli nie możesz wymusić aktualizacji aplikacji, powinieneś opublikować element automatyzacji przeglądarki jako usługę internetową)
  • omija to koncepcję OAuth
  • z drugiej strony mam wrażenie, że jestem właścicielem moich danych, w tym listy moich znajomych, a Facebook nie powinien mnie ograniczać przed dostępem do nich za pośrednictwem interfejsu API

Przykładowa implementacja za pomocą 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;
}

Prototyp z implementacją tego podejścia (przy użyciu C # / WatiN) patrz https://github.com/svejdo1/ShadowApi . Umożliwia także dynamiczną aktualizację łącznika Facebooka, który pobiera listę twoich kontaktów.

Ondrej Svejdar
źródło
2
Kod zakłada, że ​​aplikacja ma dostęp do nazwy użytkownika i hasła użytkownika. Nie tak działa OAuth, a większość użytkowników nie poda swojej nazwy użytkownika i hasła FB do aplikacji. To byłoby ogromne ryzyko bezpieczeństwa.
jbustamovej
32
Jest to niezgodne z polityką Facebooka. Nie należy stosować się do tej rady.
Simon Cross,
3
Z drugiej strony, nie jesteś nawet bezpieczny, chroniąc poświadczenia użytkownika podczas włamania. Na wypadek, gdyby ktoś zdecydował się użyć Twojego kodu, upewnij się, że używa protokołu SSL, zmieniając uri na https.
Rogala,
8
Plus jeden za wepchnięcie go w twarz!
Skynet,
12
Wygląda na to, że polityka Facebooka jest rodzajem prawa międzynarodowego i łamanie go jest przestępcą. Uważasz także, że Facebook ma na myśli tylko to, co najlepsze dla użytkownika, co moim zdaniem jest błędne, jak stwierdził @OndrejSvejdar, ponieważ wtedy pozwolą ci tylko wybrać, czy chcesz być widoczny w innych aplikacjach. Myślę, że to tylko ruch Facebooka, aby za darmo zatrzymać rozwój innych poprzez ich platformę. Zamiast tego ludzie powinni zacząć rozwijać swoje aplikacje, usługi, gry, wiadomości itp. Bezpośrednio na Facebooku. I zapłać za reklamy na Facebooku, jeśli nie będą.
JustGoscha
2

Spróbuj /me/taggable_friends?limit=5000użyć kodu JavaScript

Lub

wypróbuj Graph API :

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

Abhishek Sharma
źródło
2
Moim zdaniem - tutaj są głosy negatywne, ponieważ „Korzystanie z tego punktu końcowego wymaga przeglądu przez Facebooka i powinno być stosowane tylko w przypadku, gdy renderujesz listę znajomych, aby umożliwić użytkownikowi oznaczenie ich postem”. Możesz przeczytać więcej na ten temat w wybranej odpowiedzi.
moonvader
{"data": [], "summary": {"total_count": 414}} Mam blok danych JSON, nie mam przyjaciół. Co mogę zrobić?
Makvin,
-1

W API SDK v2.0 Facebook Graph lub powyżej, należy zwrócić się do user_friends pozwolenie od każdego użytkownika w momencie Facebook login od user_friends nie jest już włączone domyślnie w każdym logowaniu; musimy to dodać.

Każdy użytkownik musi udzielić user_friends pozwolenie w celu umieszczenia w odpowiedzi na / Me / przyjaciół .

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 {
        }
    }
}

W momencie logowania na Facebooku wyświetla się monit z ekranem zawierającym wszystkie uprawnienia:

Wpisz opis zdjęcia tutaj

Jeśli użytkownik naciśnie przycisk Kontynuuj , uprawnienia zostaną ustawione. Gdy uzyskasz dostęp do listy znajomych za pomocą Graph API, wyświetlą się Twoi znajomi, którzy zalogowali się do aplikacji jak wyżej

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

            print(result!)
        }
    })
}

Dane wyjściowe będą zawierać użytkowników, którzy udzielili uprawnienia użytkownik_znajomi w momencie logowania do aplikacji za pośrednictwem Facebooka.

{
    data = (
             {
                 id = xxxxxxxxxx;
                 name = "xxxxxxxx";
             }
           );
    paging = {
        cursors = {
            after = xxxxxx;
            before = xxxxxxx;
        };
    };
    summary = {
        "total_count" = 8;
    };
}
Lineesh K Mohan
źródło