Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

A library and REST API that serves Facebook and Twitter user data in PortableContacts format

branch: master
README.md

portablecontacts-unofficial PortableContacts

About

This is a library and REST API that converts Facebook and Twitter user data to the PortableContacts format. You can try it out with these interactive demos:

http://facebook-poco.appspot.com/
http://twitter-poco.appspot.com/

It's part of a suite of projects that implement the OStatus federation protocols for the major social networks. The other projects include activitystreams-, salmon-, webfinger-, and ostatus-unofficial.

Google isn't included because it already serves Gmail contacts and user data in PortableContacts format at gmail.com.

License: This project is placed in the public domain.

Using

The library and REST API are both based on the PortableContacts 1.0 Draft C spec

Let's start with an example. This code using the library:

from portablecontacts_unofficial import twitter
...
tw = twitter.Twitter(ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET)
tw.get_contacts()

is equivalent to this HTTP GET request:

https://twitter-poco.appspot.com/poco/@me/@all/
  ?access_token_key=ACCESS_TOKEN_KEY&access_token_secret=ACCESS_TOKEN_SECRET

They return the Twitter users that the authenticated user follows. Here's the JSON output:

{
  "itemsPerPage": 10,
  "startIndex": 0,
  "totalResults": 12
  "items": [{
    "id": "139199211",
    "displayName": "Ryan Barrett",
    "name": {"formatted": "Ryan Barrett"},
    "accounts": [{"domain": "twitter.com",
                  "userid": "139199211",
                  "username": "snarfed_org",
                  }],
    "addresses": [{"formatted": "San Francisco, CA", "type": "home", }],
    "photos": [{"value": "http://a1.twimg.com/profile_images/866165047/ryan_normal.jpg",
                "primary": "true",
                }],
    "urls": [{"value": "http://snarfed.org/",
              "type": "home",
              }],
    ...
  }, ...]
  ...
}

The request parameters are the same for both, all optional: group_id may be @all or @self. USER_ID may be a specific user id or unset for all users.

Paging is supported via the startIndex and count parameters. They're self explanatory, and described in detail in the OpenSearch spec.

Output data is PoCo JSON formatted response object dicts, which put the contacts in a top-level entry list alongside itemsPerPage, totalCount, etc. fields.

Most Facebook requests and all Twitter requests will need OAuth access tokens. If you're using Python on Google App Engine, oauth-dropins is an easy way to add OAuth client flows for these sites. Otherwise, here are the sites' authentication docs: Facebook, Twitter.

If you get an access token and pass it along, it will be used to sign and authorize the underlying requests to the sources providers. See the demos on the REST API endpoints above for examples.

Using the REST API

The endpoints above all implement the PoCo REST API. Request paths are of the form:

/poco/@me/GROUP_ID/[USER_ID]?startIndex=...&count=...&format=FORMAT&access_token=...

All query parameters are optional. FORMAT may be json (the default), xml, or atom, both of which return Atom. The rest of the path elements and query params are described above.

Errors are returned with the appropriate HTTP response code, e.g. 403 for Unauthorized, with details in the response body.

To use the REST API in an existing PoCo client, you'll need to hard-code exceptions for the domains you want to use e.g. facebook.com, and redirect HTTP requests to the corresponding endpoint above.

Using the library

See the example above for a quick start guide.

Clone or download this repo into a directory named portablecontacts_unofficial (note the underscore instead of dash). Each source works the same way. Import the module for the source you want to use, then instantiate its class by passing the HTTP handler object. The handler should have a request attribute for the current HTTP request.

The useful methods are get_contact() and get_current_user(), which returns the current authenticated user (if any). See the individual method docstrings for details. All return values are Python dicts of decoded PortableContacts JSON objects.

Future work

The REST APIs are currently much more usable than the library. We need to make the library easier to use. Most of the hard work is already done; here's what remains.

  • Allow passing OAuth tokens as keyword args.
  • Expose the initial OAuth permission flow. The hard work is already done, we just need to let users trigger it programmatically.
  • Expose the format arg and let users request Atom output.

We'd also love to add more sites! Off the top of my head, Yahoo, Outlook.com, Apple's iCloud, Instagram, and Sina Weibo would be good candidates. If you're looking to get started, implementing a new site is a good place to start. It's pretty self contained and the existing sites are good examples to follow, but it's a decent amount of work, so you'll be familiar with the whole project by the end.

Finally, here are some Facebook tweaks we should implement to make sure we comply with their TOS:

Development

Pull requests are welcome! Feel free to ping me with any questions.

Most dependencies are included as git submodules. Be sure to run git submodule init after cloning this repo.

This PortableContacts test client and validator is useful for manual testing.

You can run the unit tests with ./alltests.py. They depend on the App Engine SDK and mox, both of which you'll need to install yourself.

Note the app.yaml.* files, one for each App Engine app id. To work on or deploy a specific app id, symlink app.yaml to its app.yaml.xxx file. Likewise, if you add a new site, you'll need to add a corresponding app.yaml.xxx file.

To deploy:

rm -f app.yaml && ln -s app.yaml.twitter app.yaml && \
  ~/google_appengine/appcfg.py --oauth2 update . && \
rm -f app.yaml && ln -s app.yaml.facebook app.yaml && \
  ~/google_appengine/appcfg.py --oauth2 update .
Something went wrong with that request. Please try again.