Declaration of VAR

and some other stuff

C# / .NET Core, publish to Facebook

2017-11-25 19:51:23 +0100

2017-11-25 19:51:23 +0100 | Comments

I started with Twitter, and today I’ll tell you how I was struggling with publishing to Facebook from C# / .NET Core project. “Struggling” part is totally on the Facebook side. Потому что это, блядь, нечто.

Now to the details.

Getting started

So, we want to post updates from our website to a Facebook page. For that you’ll need to have a Facebook account (obviously) and create a page.

And you’ll need to become a developer and create a Facebook app. Having done that, go to your app settings and copy the following values from there:

  • App ID;
  • App Secret.

So far so good.

Torments, agony and misery

But then I got heavily misled by the Facebook documentation. It says, that to publish as a page you need to obtain access tokens first. Okay, let’s try to do that:

var rez = Task.Run(async () =>
{
    string url = "https://graph.facebook.com/YOUR-PAGE-ID?fields=access_token";
    using (var http = new HttpClient())
    {
        var httpResponse = await http.GetAsync(url);
        var httpContent = await httpResponse.Content.ReadAsStringAsync();
        
        return httpContent;
    }
});
var rezJson = JObject.Parse(rez.Result);
Console.WriteLine(rezJson);

Here’s the response:

{
  "error": {
    "message": "An access token is required to request this resource.",
    "type": "OAuthException",
    "code": 104,
    "fbtrace_id": "some/id"
  }
}

Right, so we need some other access token first? No shit, that’s why this request looked so suspiciously simple. But okay, indeed, there is some Facebook Login mentioned there, so let’s go there.

Okay, we’re here, where to next?

You think, Websites or mobile websites? Sounds logical, right? No! Because fuck you, that’s why!

Access Tokens is the one you need. Yes, another, different access tokens page. There you need App Access Tokens section. Let’s try to get this one:

// ...
string url = "https://graph.facebook.com/oauth/access_token?client_id=YOUR-APP-ID&client_secret=YOUR-APP-SECRET&grant_type=client_credentials";
// ...

Okay, this time it actually returned some token:

{
  "access_token": "GENERATED-APP-ACCESS-TOKEN",
  "token_type": "bearer"
}

Now let’s try to repeat the previous request with this token in parameters:

// ...
string url = "https://graph.facebook.com/YOUR-PAGE-ID?fields=access_token&access_token=GENERATED-APP-ACCESS-TOKEN";
// ...

Response:

{
  "id": "YOUR-PAGE-ID"
}

Thanks a lot! That’s exactly what I… see not point in getting to! Documentation promised me Page Access Token, but where is it? Here in the response there is only my Page ID, which I already knew.

How it actually works

Okay, I’ll spare you from hours of me being raped by the Facebook documentation (блядский кусок говна этот Фейсбук вместе с его пиздовыебанной документацией) and tell you how it actually works.

Those Stack Exchange answer were a great help:

After reading those, I understood the biggest confusion of the whole fucking Facebook documentation - the only way to get Page Access Token is to use Graph API Explorer! You cannot get it by performing silly HTTP requests from your HTTP client. So, it would be really helpful, if it was written in the very first sentence at the very first documentation page.

And the whole Facebook Login thing apparently is supposed to be used for users social logins on websites and applications, and not for requests from servers.

Okay, let’s get this bloody Page Access Token.

Getting Page Access Token

One more surprise for you - Facebook access tokens have expiration time, so they are valid for a particular amount of time: standard ones expire in 2 hours (or less), and extended ones expire in 2 months. Fortunately, the Page Access Token, that we will be getting, has no expiration time (is valid forever).

Open Graph API Explorer and choose your application here:

Now click on Get User Access Token:

A dialog window with lots of checkboxes will appear. Check these two: manage_pages and publish_pages.

Generated token will appear in the Access Token field. It’s a short-term one. Here you can check when it expires:

You can (probably) continue using Graph API Explorer, but I closed it and did the rest from code.

Here’s how you get an extended token:

// ...
string url = "https://graph.facebook.com/oauth/access_token?client_id=YOUR-APP-ID&client_secret=YOUR-APP-SECRET&grant_type=fb_exchange_token&fb_exchange_token=YOUR-SHORT-TERM-TOKEN";
// ...

Take it from the response:

{
  "access_token": "EXTENDED-TOKEN",
  "token_type": "bearer",
  "expires_in": 5183975
}

Check its expiration time:

And finally send this request:

// ...
string url = "https://graph.facebook.com/me/accounts?access_token=YOUR-EXTENDED-TOKEN";
// ...

Grab your Page Access Token (and also Page ID) from the response:

{
  "data": [
    {
      "access_token": "PAGE-ACCESS-TOKEN",
      "category": "Website",
      "name": "YOUR-PAGE-NAME",
      "id": "YOUR-PAGE-ID",
      "perms": [
        "ADMINISTER",
        "EDIT_PROFILE",
        "CREATE_CONTENT",
        "MODERATE_CONTENT",
        "CREATE_ADS",
        "BASIC_ADMIN"
      ]
    }
  ],
  "paging": {
    "cursors": {
      "before": "some",
      "after": "some"
    }
  }
}

Check its expiration time:

Expires: never. Hallelujah.

Publishing to Facebook Page

In order to be able to publish stuff to your Facebook page via Facebook API you need to have 2 things;

  1. Page Access Token - we got it on the previous step;
  2. Page ID - same here, but “just for fun” try to find this one on your Facebook page.

Publishing a simple text post is pretty easy, I will show this one as well, but I also needed to publish posts with pictures attached, and that’s… something.

I tried lots of things: uploading attachments, attaching links, some other stuff, but nothing worked the way I wanted. Either nothing was attached, or picture was attached in some clipped form, or it was transformed to a web-link with some sort of a preview. You know, what worked? Uploading a new photo to the page album, getting the ID of the creating post (not the photo ID) and then updating its message field! Пиздец. That way photo, that was published to the page feed, miraculously becomes a post (kinda?..).

That’s an example of what I wanted to get (and finally did):

So, here’s my class for publishing to a Facebook page:

class Facebook
{
    readonly string _accessToken;
    readonly string _pageID;
    readonly string _facebookAPI = "https://graph.facebook.com/";
    readonly string _pageEdgeFeed = "feed";
    readonly string _pageEdgePhotos = "photos";
    readonly string _postToPageURL;
    readonly string _postToPagePhotosURL;

    public Facebook(string accessToken, string pageID)
    {
        _accessToken = accessToken;
        _pageID = pageID;
        _postToPageURL = $"{_facebookAPI}{pageID}/{_pageEdgeFeed}";
        _postToPagePhotosURL = $"{_facebookAPI}{pageID}/{_pageEdgePhotos}";
    }

    /// <summary>
    /// Publish a simple text post
    /// </summary>
    /// <returns>StatusCode and JSON response</returns>
    /// <param name="postText">Text for posting</param>
    public async Task<Tuple<int, string>> PublishSimplePost(string postText)
    {
        using (var http = new HttpClient())
        {
            var postData = new Dictionary<string, string> {
                { "access_token", _accessToken },
                { "message", postText }//,
                // { "formatting", "MARKDOWN" } // doesn't work
            };

            var httpResponse = await http.PostAsync(
                _postToPageURL,
                new FormUrlEncodedContent(postData)
                );
            var httpContent = await httpResponse.Content.ReadAsStringAsync();
            
            return new Tuple<int, string>(
                (int)httpResponse.StatusCode,
                httpContent
                );
        }
    }

    /// <summary>
    /// Publish a post to Facebook page
    /// </summary>
    /// <returns>Result</returns>
    /// <param name="postText">Post to publish</param>
    /// <param name="pictureURL">Post to publish</param>
    public string PublishToFacebook(string postText, string pictureURL)
    {
        try
        {
            // upload picture first
            var rezImage = Task.Run(async () =>
            {
                using (var http = new HttpClient())
                {
                    return await UploadPhoto(pictureURL);
                }
            });
            var rezImageJson = JObject.Parse(rezImage.Result.Item2);
            
            if (rezImage.Result.Item1 != 200)
            {
                try // return error from JSON
                {
                    return $"Error uploading photo to Facebook. {rezImageJson["error"]["message"].Value<string>()}";
                }
                catch (Exception ex) // return unknown error
                {
                    // log exception somewhere
                    return $"Unknown error uploading photo to Facebook. {ex.Message}";
                }
            }
            // get post ID from the response
            string postID = rezImageJson["post_id"].Value<string>();
            
            // and update this post (which is actually a photo) with your text
            var rezText = Task.Run(async () =>
            {
                using (var http = new HttpClient())
                {
                    return await UpdatePhotoWithPost(postID, postText);
                }
            });
            var rezTextJson = JObject.Parse(rezText.Result.Item2);
            
            if (rezText.Result.Item1 != 200)
            {
                try // return error from JSON
                {
                    return $"Error posting to Facebook. {rezTextJson["error"]["message"].Value<string>()}";
                }
                catch (Exception ex) // return unknown error
                {
                    // log exception somewhere
                    return $"Unknown error posting to Facebook. {ex.Message}";
                }
            }

            return "OK";
        }
        catch (Exception ex)
        {
            // log exception somewhere
            return $"Unknown error publishing post to Facebook. {ex.Message}";
        }
    }

    /// <summary>
    /// Upload a picture (photo)
    /// </summary>
    /// <returns>StatusCode and JSON response</returns>
    /// <param name="photoURL">URL of the picture to upload</param>
    public async Task<Tuple<int, string>> UploadPhoto(string photoURL)
    {
        using (var http = new HttpClient())
        {
            var postData = new Dictionary<string, string> {
                { "access_token", _accessToken },
                { "url", photoURL }
            };

            var httpResponse = await http.PostAsync(
                _postToPagePhotosURL,
                new FormUrlEncodedContent(postData)
                );
            var httpContent = await httpResponse.Content.ReadAsStringAsync();
            
            return new Tuple<int, string>(
                (int)httpResponse.StatusCode,
                httpContent
                );
        }
    }

    /// <summary>
    /// Update the uploaded picture (photo) with the given text
    /// </summary>
    /// <returns>StatusCode and JSON response</returns>
    /// <param name="postID">Post ID</param>
    /// <param name="postText">Text to add tp the post</param>
    public async Task<Tuple<int, string>> UpdatePhotoWithPost(string postID, string postText)
    {
        using (var http = new HttpClient())
        {
            var postData = new Dictionary<string, string> {
                { "access_token", _accessToken },
                { "message", postText }//,
                // { "formatting", "MARKDOWN" } // doesn't work
            };

            var httpResponse = await http.PostAsync(
                $"{_facebookAPI}{postID}",
                new FormUrlEncodedContent(postData)
                );
            var httpContent = await httpResponse.Content.ReadAsStringAsync();
            
            return new Tuple<int, string>(
                (int)httpResponse.StatusCode,
                httpContent
                );
        }
    }
}

And how to use it:

Facebook facebook = new Facebook("YOUR-PAGE-ACCESS-TOKEN", "YOUR-PAGE-ID");

// 1) if you want to publish a post with attached photo (actually, the other way around)
string result = facebook.PublishToFacebook("some text", "http://your.website/images/some.png");
Console.WriteLine(result);

// 2) if you want just to publish a simple text post
// var rezText = Task.Run(async () =>
// {
//     using (var http = new HttpClient())
//     {
//         return await facebook.PublishSimplePost("some another text");
//     }
// });
// var rezTextJson = JObject.Parse(rezText.Result.Item2);
// if (rezText.Result.Item1 != 200)
// {
//     try // return error from JSON
//     {
//         Console.WriteLine($"Error posting to Facebook. {rezTextJson["error"]["message"].Value<string>()}");
//         return;
//     }
//     catch (Exception ex) // return unknown error
//     {
//         // log exception somewhere
//         Console.WriteLine($"Unknown error posting to Facebook. {ex.Message}");
//         return;
//     }
// }
// Console.WriteLine(rezTextJson);

One more thing: right now your Facebook app is in development mode and is not publicly available. That means, all the posts you publish with it won’t be visible to anyone, except those whom you added into your app Roles (https://developers.facebook.com/apps/YOUR-APP/roles/). At first I didn’t know that, so I was totally freaking out, because I clearly saw my posts on the page under my account, while other users didn’t, for them it was an empty wall/timeline.

So, go to App Review (https://developers.facebook.com/apps/YOUR-APP/review-status/) section and switch “make it public” button. You don’t need to submit your app for approval, just make it public:

After that all your posts you made while your app was in development mode will become visible to everyone (and all the future posts too, of course).



Having started to work on integration with Instagram API, I suddenly understood Facebook Login mechanism better (thanks to the Instagram documentation).

Turns out, you can actually make Facebook Login work. But you anyway cannot achieve it by using only HTTP requests from your server - for the initial step you will anyway need a browser, where user (you) will click on buttons in the Facebook login dialog.

And for that you first need to register a redirect URI in your app settings. Don’t let this redirect URI scare you, it’s nothing biggie - you can specify any address there, even http://example.org, like I did:

After that, according to the information from this page, you need to open the following URL in browser:

https://www.facebook.com/dialog/oauth?client_id=YOUR-ADD-ID&redirect_uri=http://example.org/

Pay attention to the trailing slash (/), because redirect URI should be exactly the same as you registered it in your app settings.

Okay, you opened the URL, it showed you Facebook login dialog, and after that redirected you to the specified http://example.org/ and put a code parameter in the URL - check the address bar in your browser:

Now documentation says to send this request (this one you can send from code):

// ...
https://graph.facebook.com/oauth/access_token?client_id=YOUR-APP-ID&redirect_uri=http://example.org/&client_secret=YOUR-APP-SECRET&code=THE-CODE-YOU-JUST-GOT-FROM-BROWSER
// ...

Again, pay attention to the trailing slash (/)! If you’ll miss it, response will be the following:

{
  "error": {
    "message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": "some"
  }
}

And if you formed request right, you’ll get this response:

{
  "access_token": "USER-ACCESS-TOKEN",
  "token_type": "bearer"
}

And that’s the User Access Token (already extended, I believe) you need for this step (if it’s not an extended token, then for this step).

BUT.

Documentation has fucked you up again Default login URL only gives you default permissions (such as viewing public profile). So, trying to proceed and get Page Access Token will give you this response:

{
  "data": []
}

Meaning, that your app has no permissions to do anything on any page.

So, if you need more permissions (publishing as page, for instance), you need to add scope parameter to the login URL, like this:

https://www.facebook.com/v2.11/dialog/oauth?client_id=YOUR-ADD-ID&redirect_uri=http://example.org/&scope=manage_pages,publish_pages

Remember, this is an URL you need to open in browser (to get a Facebook login dialog).

And here’s the final surprise - you cannot ask for permissions such as manage_pages and publish_pages for an un-reviewed app using login dialog. Of course, if you were anyway going to submit your app for reviewing, then it should not be a problem for you. Meanwhile, that’s what you get:

…So, I’ll stick to the Graph API Explorer method.



So, from today Facebook requires all the apps to go through the App Review.

I knew it was coming, but I wasn’t worrying about that, because all 3 warnings I received were saying the following:

If your app is not submitted for review, you will lose access to any permissions or features requiring review, including these:
  • user_friends
  • user_link
  • user_gender
  • user_age_range

I am not using any of those (neither did I request any of those), so I thought I’m fine.

But today I got the following error in my requests:

The permissions manage_pages,publish_pages are not available. It could because either they are deprecated or need to be approved by App Review

Nice surprise, thank you, Facebook.

I went to the application page, and suddenly discovered that my app does have user_friends permission, because it was granted automatically:

Okay, let’s try to submit our app for review then:

Oh, so manage_pages and publish_pages are also the kind of permissions that require passing App Review.

Okay, let’s check what sort of “additional information” in needed there:

Ha-ha, 2nd and 3rd options (let’s not count other) say that “Apps won’t be approved for this use of publish_page”, so you can only go through the 1st one, which has some ridiculous requirements, considering the actual use-case - automatic content posting from a website to a public Facebook page. I mean, my app isn’t even supposed to be used by anyone but me, and I used it only once myself, when I was getting the API key.

Perhaps, such usage of the Facebook API was not allowed from the beginning, I don’t know (and I didn’t read the rules). But if Facebook doesn’t want me to deliver new content to their wretched network, so be it.



Today I suddenly discovered that my requests to Facebook API are working again, even though I haven’t done anything and most definitely did not submit my app for review.

I went to Facebook Developer Community to see if it’s not only me, and discovered that Facebook got a lot of angry posts from developers towards the whole revoking access and mandatory App Review thing.

These guys, for instance, almost got their business destroyed (and they still might):

And this one is fairly loosing his shit over the fact that he has to demonstrate his server-to-server use-case somehow:

Another example:

Seriously, how does one suppose to show a reviewer the “improvement of user experience”? I am that user, it’s for me only! But as I said, looks like such usage of API was not really allowed in the first place.

But anyway, my guess is that Facebook realized how big it did fuck up (way too many unhappy developers) and rollbacked the access revocation. I saw others saying the same thing - that they just got the access back without doing anything.

Well, it’s good that requests are working again, but who knows what happens next. I won’t be surprised if they will revoke the access again later on.

А этот скрин просто поугарать:

Какая продвинутая святая обитель, общается с паствой прямо в бездуховных соцсетях. Ну так звонили бы уж тогда сразу наверх, что проку в этих мирских склоках, гугугу.