Facebook like - monitor! Help needed with Graph API

Hi there, I run a Creative Agency and decided it’s important for my team to know and “hear” the appreciation from our fans on FB - motivation wise.

My goal is to make a device that checks for fan count on our page and play music when the counter rises.
I’ve done a working prototype for Behance.net

// This #include statement was automatically added by the Particle IDE.
#include "SparkCorePolledTimer/SparkCorePolledTimer.h"

// This #include statement was automatically added by the Particle IDE.
#include "SparkJson/SparkJson.h"

// This #include statement was automatically added by the Particle IDE.
#include "HttpClient/HttpClient.h"
/**
* Declaring the variables.
*/
unsigned int nextTime = 0;    // Next time to contact the server
HttpClient http;

// Headers currently need to be set at init, useful for API keys etc.
http_header_t headers[] = {
    //  { "Content-Type", "application/json" },
    //  { "Accept" , "application/json" },
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

http_request_t request;
http_response_t response;

int last_appreciations= 0;

SparkCorePolledTimer updateTimer(60*1000);  //Create a timer object and set it's timeout in milliseconds
void OnTimer(void);   //Prototype for timer callback method

void setup() {
    Serial.begin(9600);
    pinMode(D7, OUTPUT);
    pinMode(D0, OUTPUT);
    pinMode(D1, OUTPUT);
    updateTimer.SetCallback(OnTimer);
    Time.zone(+2);
}

void loop() {
      updateTimer.Update();
}


// https://www.behance.net/v2/users/meggot/stats?api_key=myAPPid    

void OnTimer(void) {  
    //Handler for the timer, will be called between 10:00 and 18:00
    if (Time.hour() >= 10 && Time.hour() <= 17){
    //Serial.println();
    //Serial.println("Application>\tStart of Loop.");
    // Request path and body can be set at runtime or at setup.
    request.hostname = "www.behance.net";
    request.port = 80;
    request.path = "/v2/users/meggot/stats?api_key=<myAPPid>";

    // The library also supports sending a body with your request:
    //request.body = "{\"key\":\"value\"}";

    // Get request
    http.get(request, response, headers);
    Serial.print("BehanceIOT>\tTime now: ");
    Serial.println(Time.timeStr());
    Serial.print("BehanceIOT>\tResponse status: ");
    Serial.println(response.status);

    Serial.print("BehanceIOT>\tHTTP Response Body: ");
    StaticJsonBuffer<512> jsonBuffer;
    
    char json[500];
    response.body.toCharArray(json,500);
    
    JsonObject& root = jsonBuffer.parseObject(json);
    
    if (!root.success())
    {
      Serial.println("parseObject() failed");
      return;
    }
    
    int appreciations = root["stats"]["all_time"]["project_appreciations"];
    
    Serial.println(appreciations);
    
    if (appreciations!= last_appreciations){
        digitalWrite(D7,HIGH);
        digitalWrite(D0,HIGH);
        digitalWrite(D1,HIGH);
        Serial.println("NOWE POLUBIENIE!");
        delay(1000);
        digitalWrite(D7,LOW);
        digitalWrite(D0,LOW);
        digitalWrite(D1,LOW);
        delay(1000);
        digitalWrite(D7,HIGH);
        digitalWrite(D0,HIGH);
        digitalWrite(D1,HIGH);
        delay(1000);
        digitalWrite(D7,LOW);
        digitalWrite(D0,LOW);
        digitalWrite(D1,LOW);
        last_appreciations= appreciations;
    }
}
}

Unfortunately it doesn’t work with Facebook since FB needs it’s API calls to be made over https instead of http. I have no idea (@michaelT ) how to use the library “HTTPSCLIENT-PARTICLE” and I don’t understand the TIMEAPI-TEST.cpp - the example is not clear to me. Maybe if there was a similar example as in the normal httpclient, than I could extrapolate from that (I’m no developer - just a maker as you can probably see). The parts I don’t understand are: 1. the way in which the request is being formatted in “httpRequestContent[]”. Why not just the URL like I have below?
2. How to get the response? In normal client I get this by “Serial.println(response.body);” how is this done in httpsclient?

I have a working URL (working on my chrome browser):
https://graph.facebook.com/v2.6/815713765185385?fields=fan_count&access_token=myAPPid|myAPPsecretID

Can somebody tell me how to modify my behance API example to work with Facebook, or point me in right direction? I understand that this example will not work since the client I’m using doesn’t support HTTPS but maybe you have other solutions? Is there a way to get the like count of my page, not using https protected Graph api?

Check this out :wink:

Thanks for pointing this out. I did my research prior to posting this question. Still I didn’t understand the method it uses.

How should it work if there is no appID or https verification?

The only place where I see any FB details is in the beggining and that is:

#define HOST "graph.facebook.com" // all Facebook pages start with this domain
#define PATH "/sparkdevices" // change this to whatever page you want

but if you check for this (https://graph.facebook.com/sparkdevices) in your browser than you get:

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

Am I missing something obious in here? Does this app work? How should it work? Does it use API or does it try to parse the information from the raw html for that particular FB page?

I would do this using a web hook. Using the dashboard, create something like this:

Then you’d use code sort of like this on your Photon:

#include "Particle.h"

void fanCountHandler(const char *event, const char *data); // forward declaration

unsigned long checkPeriodMs = 60000; // every minute
unsigned long lastCheck = 0 - 45000;

void setup() {
	Serial.begin(9600);
	Particle.subscribe("hook-response/FBFanCount", fanCountHandler , MY_DEVICES);
}

void loop() {
	if (millis() - lastCheck >= checkPeriodMs) {
		Serial.println("triggering FBFanCount webhook");
		lastCheck = millis();
		Particle.publish("FBFanCount", "", PRIVATE);
	}
}


void fanCountHandler(const char *event, const char *data) {
	char *cp = strstr(data, "\"fan_count\":");
	if (cp) {
		cp += 12; // length of string above
		int fanCount = atoi(cp);
		Serial.printlnf("fanCount=%d", fanCount);
		// Put code here to do something with fanCount
	}
	else {
		Serial.println("unknown response in webhook handler:");
		Serial.println(data);
	}
}

This is what I get in the serial monitor:

triggering FBFanCount webhook
fanCount=1568
5 Likes

Hi there,

I just knew that there just has to be some smart solution to this problem. Thanks @rickkas7 a ton. I will check this ASAP and than post a full working solution in here for future:)

Yep, works like a charm. Thanks for that solution. I’m gonna build around that and post here when the device is ready.

3 Likes

Hi there,

just one more question:
I was thinking about making a small Blynk smartphone app, which would allow the user to change the page/user being monitored. Right now it is hardcoded in webhook which @rickkas7 suggested. But looking through webhook settigns I’ve found "Include Default Attributes Whether your webhook will include the data included in your Particle.publish() ".

Does this mean that I could possibly send the Facebook ID in the Particle.publish() event, and so give the device the power to decide whom it is monitoring? How should I pass this information and how should it be structured on the side of the webhook? Couldn’t find any example using it.

This should work.

The difference here is that the node ID (the big number beginning with 815) is no longer in the URL field. Instead, it’s in a new query parameter “ids” and the value is {{PARTICLE_EVENT_VALUE}}, which is the value passed in the publish call.

Then the user firmware on the Photon is pretty much the same, except the node ID is passed in the data:

Particle.publish("FBFanCount2", "815713765185385", PRIVATE);

That is the part I was lacking. Thanks

Thanks @rickkas7! FB used to not require any credentials to view this JSON blob… so I appreciate you figuring this out and replying :smile: I’ll add this to my list of things to update.

1 Like