Nachdem Facebook im Mai die Permanenten Tokens abgeschafft hat, musste ich meine RSS-Ping-Applikation gezwungenermaßen aktualisieren.

Der neue Authentication-Flow sieht es leider nicht mehr vor, dass eine Applikation ohne eingeloggten Benutzer agiert.

Daher ein kleines Update. Das neue Skript funktioniert nur noch mit einem Server im Hintergrund, während es bei dem alten Skript noch möglich war lokal zu testen ist man nun leider dazu gezwungen scharf zu schießen. (Macht es halt etwas umständlicher, imho.)

Zum Anmelden der neuen Applikation benötigt man nun nur noch folgende Rechte:

https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=manage_pages,create_note&response_type=token

# manage_pages für Seitenverwaltung
# create_note um Notizen posten zu dürfen

Und der zugehörige Code der Applikation in Perl:

#!/usr/bin/perl
use strict;
use LWP::Simple;            # Für Get-Anfragen an Facebook
use CGI;                    # Zum Auswerten der Parameter
use JSON;                   # Zum Auswerten der Antworten
use URI::Escape;            # Zum Escapen der Nachrichten

my $q = CGI->new();	    # Neuer Query

# Anwendungs-ID
my $client_id = '123456789012345';

# Anwendungs-Secret
my $client_secret = '1234a56b789012cde345678a90123b45';

# Fan-Page ID
my $page_id = '123456789012';

# Ort des Skripts 
# Muss mit Redirect_URI übereinstimmen und im
# App-Domain Raum liegen.
my $script_location = $q->url();

# Solange, wie der Parameter "Code" nicht angegeben ist.
unless($q->param('code')) {
	my $code_query = "https://www.facebook.com/dialog/oauth?" .
    "client_id=" . $client_id .
   "&redirect_uri=" . $script_location .
   "&response_type=code";

	print $q->redirect($code_query);
	exit;
}
else {
	if($q->param('code') eq "") {
		print $q->header(-status=>406),
		$q->start_html('Problems'),
		$q->h2('No Coffee for you.'),
		$q->strong('Code was empty... User not logged in?');
		exit 0;
	}
	
	my $token_query = "https://graph.facebook.com/oauth/access_token?" .
    "client_id=" . $client_id . 
   "&redirect_uri=" . $script_location .
   "&client_secret=" . $client_secret .
   "&code=" . $q->param('code');

	my $app_token = get($token_query);
	&post_news($app_token);
	exit;
}
exit; 

   
sub post_news($) {   
	# Application Token
	my $token = shift;
	
	# Betreff
	# Achtung! Lazy auf UTF8 konvertieren, sonst
	# werden Umlaute nicht richtig dargestellt.
	my $subject = uri_escape_utf8('Nur ein Test');

	# Nachricht
	my $message = uri_escape_utf8('Testnachricht...<br>Hallo Welt!');

	# Liste der Accounts (der verwalteten Seiten / Apps ...) holen
	# und Token für die Seite suchen.
	my $accounts = get('https://graph.facebook.com/me/accounts?' . $token);

	unless($accounts)
	{
		print $q->header(-status=>407),
		$q->start_html('Problems'),
		$q->h2('This should (hopefully) never happen...'),
		$q->strong('Accounts was undefined - Maybe wrong token?');
		exit 0;
	}

	my %data = %{decode_json($accounts)};
	my $page_access_token = "";

	foreach my $access_page (@{$data{'data'}})
	{
		my %item = %{$access_page};
		if($item{'id'} eq $page_id) {
			$page_access_token = $item{'access_token'};
		}
	}

	if(!defined $page_access_token || $page_access_token eq "")
	{
		print $q->header(-status=>408),
		$q->start_html('Problems'),
		$q->h2('No access token...'),
		$q->strong('manage_pages privilege removed? Try granting it again!');
		exit 0;
	}

	# Nachricht posten
	my $news_id = get('https://graph.facebook.com/' . $page_id .
               '/notes?message='. $message .
               '&subject=' . $subject .
               '&method=post&access_token=' . $page_access_token);

	print $q->header(-status=>200),
	$q->start_html('Great Success'),
	$q->h2($news_id . " posted!");
	exit;
}

Sollte man ausgeloggt werden, reicht ein einfaches:

https://graph.facebook.com/oauth/access_token?client_id=APP_ID&client_secret=APP_SECRET&grant_type=fb_exchange_token&fb_exchange_token=ALTES_TOKEN

für den Relogin. (natürlich muss dann das alte pseudo perma Token durch das Neue ersetzt werden.)