a collection of problems and solutions for random combinations of technology

Sep 25, 2009

facebook push to iphone

the problem:
Facebook sends a lot of emails. I generally don't mind the default setting which sends an email everytime your friend sneezes, but I wanted certain kinds of notifications to "bubble up to the top". I recently stumbled on a new iPhone app called Notifications which offers a REST API to send push alerts to your iPhone. So, I started working on a solution...

ingredients used: Postfix 2.6.2, Dovecot 1.2.4, Dovecot Sieve 0.1.12, Ruby 1.8/1.9
prerequisites: a working Postfix and Dovecot installation
instructions for windows here

The basic flow is: mail comes in from facebook, a sieve script determines if the mail is high priority, then the mail is forwarded to a sub-address, a specific .forward file intercepts it and executes a script which utilizes the Notifications REST API

First, go get the Notifications app and register at their site. After creating your account, go to their REST API examples page. You'll see your API key embedded in the examples. You'll need it later...

I use dovecot as an IMAP server and as a MDA. This allows me to utilize the awesomeness that is the sieve plugin. In order for this recipe to work, you don't necessarily need Postfix or Dovecot. You will need a decent sieve interpreter. If you're currently using Postfix and Dovecot but are not using deliver as your MDA, here's a quick and dirty setup:

  1. modify postfix's main.cf mailbox_command = /usr/local/libexec/dovecot/deliver

  2. modify postfix's master.cf dovecot unix - n n - - pipe
    flags=DRhu user=vmail:vmail argv=/usr/local/libexec/dovecot/deliver -c /usr/local/etc/dovecot/dovecot.conf -f ${sender} -d ${user}@${nexthop} -a ${recipient}

  3. install the sieve plugin FreeBSD ports FTW $ cd /usr/ports/mail/dovecot-sieve/
    $ make install clean

  4. modify dovecot.conf protocol lda {
      mail_plugins = sieve
more info here

Now we can configure a sieve script. The default location for user sieve scripts is ~/.dovecot.sieve. Here's what mine looks like require ["envelope", "subaddress", "fileinto", "copy"];
if allof(address :domain :is "from" "facebookmail.com",
         not envelope :detail "to" "notify-facebook") {
  fileinto "auto_filed.facebook";
  if allof(exists "X-Facebook-Notify",
           header :contains "X-Facebook-Notify" ["share_comment;", "feed_comment;", "friend;", "msg;", "photo_tag;", "photo_comment;", "photo_album_comment;"]) {
    redirect :copy "your_username+notify-facebook@example.com";
in prose, form
If the mail is from facebook and does not have the sub-address "notify-facebook", file the mail into the sub-folder "auto_filed/facebook". If the mail contains specific facebook headers which we consider high priority, forward a copy to my email address with the sub-address of "notify-facebook".
In this example, the list of "high priority" notifications are when someone comments on one of your links, status, photos, albums or you receive a friend request, private message, or tags you in a photo.
I never found a list of all the message types embedded in the "X-Facebook-Notify" MIME header, if you stumble on such a list, leave a comment.

Postfix (and probably other MTAs) support different .forward files for sub-address. Let's make sure yours is configured properly by adding this to postfix's main.cf forward_path = $home/.forward${recipient_delimiter}${extension}, $home/.forward
recipient_delimiter = +

Now for our sub-address specific forward file: ~/.forward+notify-facebook. Point to a script which should take one argument of API Key and read mail from STDIN. "| /usr/local/share/mobile_notification_scripts/facebook.rb <YOUR-NOTIFICATIONS-API-KEY-GOES-HERE>"

Here's the ruby script I use... #!/usr/local/bin/ruby
require 'rubygems'
require 'tmail'
require 'mechanize'
  VERSIONLIB = '0.1'
  POST_URL = "https://www.appnotifications.com/account/notifications.xml"

  exit if SINGLE_TOKEN.blank?

  # parse mail
  email = TMail::Mail.parse(STDIN.read)
  body = (email.multipart?) ? parts.detect { |part| part.content_type == "text/plain" } : email.body
  exit if body.blank?
  link = (match = body.match(/(http:\/\/www.facebook.com\/n\/.+?)\n/)) ? match[1] : nil
  message_details = email.body.gsub(/\n/, "<br/>")
  unless link.nil?
    message_details = message_details.sub(/#{Regexp.escape(link)}.*/m,'') + <<-HTML
    <a href='#{link}'>http://www.facebook.com/...</a><br/><br>
    facebook app links:<br/>
    <table style="width:100%;">
        <td style="padding-top:16px;text-align:left;"><a href="fb://feed">News Feed</a></td>
        <td style="padding-top:16px;text-align:center;"><a href="fb://requests">Requests</a></td>
        <td style="padding-top:16px;text-align:right;"><a href="fb://profile">Profile</a></td>
        <td style="padding-top:30px;text-align:left;"><a href="fb://albums">Albums</a></td>
        <td style="padding-top:30px;text-align:center;"><a href="fb://notes">Notes</a></td>
  a = WWW::Mechanize.new { |agent|
    agent.user_agent = "AppNotifications Ruby #{VERSIONLIB}"

  # Send a notification
    { :user_credentials => SINGLE_TOKEN,
      'notification[title]'                => 'facebook',
      'notification[message_level]'        => -1,
      'notification[silent]'               => 0,
      'notification[action_loc_key]'       => 'View',
      'notification[run_command]'          => 'notifications://',
      'notification[message]'              => email.subject,
      'notification[long_message_preview]' => email.subject,
      'notification[long_message]'         => message_details
Did you know iPhone apps can register URL scheme handlers? here's a list. Notably, facebook supports fb://profile, fb://requests, etc...
This script configures the push notification badge to automatically open the Notifications app when unlocking your phone. Once in the app, you can click on the latest notification and view the full details which includes a shortened version of the original mail. I also added some facebook app links to the bottom of the notification details.

It seems like a lot of work to get a bullshit feature. True. But it was a fun detour...


  1. I have гead a few good ѕtuff here.
    Definitelу value booκmaгκing wongaliga fοr reviѕiting.
    І wonder hοω ѕο much attempt yоu ѕеt to create this
    sort οf excellent infoгmative

  2. You should be shrewd about phone repair and you have to settle on your choice as cautiously as you can. You have to focus on the parts quality. Handy reparatur

  3. Mobile phones when all is said in done are not bio-degradable.Display Iphone reparatur

  4. Casino de Gama Casino - DrmCD
    Casino de 파주 출장샵 Gama Casino was established in 1998 by the Government 공주 출장마사지 of the 공주 출장마사지 Federations of the Central African Republic. We 안양 출장샵 are 정읍 출장마사지 a professional, innovative and

  5. Merkur 15c Safety Razor - Barber Pole - Deccasino
    Merkur 15C Safety Razor https://deccasino.com/review/merit-casino/ - Merkur - 15C for Barber Pole wooricasinos.info is the titanium metal trim perfect introduction to the Merkur https://febcasino.com/review/merit-casino/ Safety 출장마사지 Razor.

  6. Sports Betting - Mapyro
    Bet the moneyline from 출장샵 1:25 apr casino PM to 11:00 PM. See https://vannienailor4166blog.blogspot.com/ more. MapYO titanium flat iron Sportsbook features live odds, live streaming, and detailed information.