Category Archives: Programming

Syncing Comments with Facebook

I’m posting this here in case anyone else wants to pick it up and run with it. I wrote a piece of code to sync my facebook notes and blog post comments. All of my blog posts already are added to facebook via RSS feed, but I was ending up with essentially two conversations. First install Services_Facebook from the PEAR repository. Then you’ll need to create a new private application with a Facebook developer account. Set the “Connect URL” to point at this script. Then uncomment the commented out section. Visit the page and you’ll be redirected to facebook. Authorize the app, and then take the session key that is returned when your are redirected and insert it into the appropriate place in the script. Comment out the code again.

You’ll need to replace all references to “blog/Blog.php”, “blog/Post.php”, and “blog/Comment.php” as those are custom to this site, but it should be fairly easy to replace them with something like a wrapper for blogger or wordpress.

I run this as a cron job on my server every 20 minutes.

require_once 'Services/Facebook.php';
require_once 'blog/Blog.php';
require_once 'blog/Post.php';
require_once 'blog/Comment.php';

class FacebookUtils {
    private static $instance;

    public function getInstance() {
        Services_Facebook::$apiKey = '<>';
        Services_Facebook::$secret = '<>';
        if(!FacebookUtils::$instance) {
            FacebookUtils::$instance = new FacebookUtils();
        return FacebookUtils::$instance;

    public function getApi() {
        $api = new Services_Facebook();
        $api->sessionKey = '<>';
        return $api;

    public function getNoteId($user, $title) {
        $title = addslashes($title);
        $fql = "SELECT note_id
                  FROM note
                 WHERE uid = $user
                   AND title = '$title'";

        $result = $this->getApi()->fql->query($fql);

        $notes = array();
        foreach ($result->note as $note) {
            return $note->note_id;
    public function getUserName($uid) {
        $fql = "SELECT first_name, pic_small
                 FROM user
                 WHERE uid = $uid";

        $result = $this->getApi()->fql->query($fql);
        foreach($result->user as $user) {
            return array(
                "first_name" => "$user->first_name",
                "pic" => "$user->pic_small",

    public function getComments($id) {
        $fql = "SELECT post_id, fromid, time, text, id
                  FROM comment
                 WHERE object_id = $id
                 ORDER by time desc";

        $result = $this->getApi()->fql->query($fql);
        $comments = array();
        foreach($result->comment as $comment) {
            $comments[] = array(
                "id" => "$comment->id",
                "text" => "$comment->text",
                "time" => "$comment->time",
                "from" => $this->getUserName($comment->fromid),
                "post_id" => "$comment->post_id",
        return $comments;

    $api = FacebookUtils::getInstance();
    if(empty($_REQUEST["session_key"])) {
            . Services_Facebook::$apiKey 
            . "&connect_display=popup&v=1.0"
            . "&next="
            . "&cancel_url="
            . "&fbconnect=true&return_session=true"
            . "&session_key_only=true"
            . "&req_perms=read_stream,publish_stream,offline_access");
    else {
        print $_REQUEST["session_key"];
    $posts = Post::getPosts('OWNER_UID',Array($blog->UID), 
        " and visible = 'True' order by DATE desc limit 0, 5 ");

    $map = array();
    foreach($posts as $post) {
        $map[$post->UID] = $api->getNoteId("675098370", $post->TITLE);

    foreach($map as $postUID => $noteID) {
        $localComments = Comment::getComments('POST_UID',Array($postUID));
        $fbComments = $api->getComments($noteID);

        $addLocal = array();
        $addRemote = array();
        foreach($fbComments as $fbComment) {
            $foundComment = false;
            foreach($localComments as $localComment) {
                $fbText = preg_replace("/^.* on Tim's blog says.../", "", $fbComment["text"]);
                if($fbText == $localComment->COMMENT) {
                    $foundComment = true;
            if(!$foundComment) {
                $addLocal[$fbComment["id"]] = $fbComment;
        foreach($localComments as $localComment) {
           $foundComment = false;
            foreach($fbComments as $fbComment) {
                $fbText = preg_replace("/^.* on Tim's blog says.../", "", $fbComment["text"]);
                if($fbText == $localComment->COMMENT) {
                    $foundComment = true;
            if(!$foundComment) {
                $addRemote[$localComment->UID] = $localComment;
        print "Post " . $postUID . "n";
        print "Add locally:n";
        print "Add remote: n";
        foreach($addLocal as $fbComment) {
            $c = new Comment();
            $c->set('NAME', $fbComment["from"]["first_name"]);
            $c->set('ICON', $fbComment["from"]["pic"]);
            $c->set('TITLE', $fbComment["from"]["first_name"] . " on Facebook says...");
            $c->set('COMMENT', $fbComment["text"]);
            #$c->set('EMAIL', $this->director->getPostParam('commentemail'));
            #$c->set('URL', $this->director->getPostParam('commenturl'));
            #$c->set('NOTIFY', $this->director->getPostParam('commentnotify'));
            $c->set('DATE', date('Y-m-d H:i:s', $fbComment["time"]));

        foreach($addRemote as $comment) {
            $result = $api->getApi()->users->callMethod('comments.add', array(
                'session_key' => $api->getApi()->sessionKey,
                'object_id' => $noteID,
                'text' => $comment->NAME . ' on Tim's blog says...' . $comment->COMMENT,

Mediawiki Playwrighting Extension

When I worked on Empty Bowl I wrote some playwrighting extensions for MediaWiki. This lets you write scripts quite easily. You get easy revisioning and diffs. The scripts look something like this:

The girl is not the problem. This house- I won't marry.

Laurel enters

Sure you will Seth.

And when you view or print them they look like this:


The girl is not the problem. This house- I won’t marry.

(Laurel enters)


Sure you will Seth.
Act I
Scene I
Character Name
Stage Direction on its own line
Inline Stage Direction within a line

I have no plans to keep developing this, but I’m open to writing a couple more plugins if anyone wants them. I’m open to farming it out to some source control system if anyone else would like to keep developing.

Download Mediawiki Playwrighting Extension