Explore the Google Reader API

    There was a need to make the interface to the google chloride reader different from the standard one. Using the standard ajax reader api did not satisfy due to commercial restrictions. The API described in many imported blogs refused to work properly, so the firebug extension was adopted by everyone's favorite browser. The result was a small selection of useful URLs for working with the reader, which I want to share with the public.

    disclaimer: this collection does not pretend to be complete, but only sums up some result that allowed us to solve the problem, namely: get a list of subscriptions, unread items by subscription, mark items as read, mark subscriptions as read. All other information can be easily obtained usingofficial leadership .

    To implement the interaction of the client ajax-interface with google.com/reader/ we will use the intermediary in php + curl.

    Introducing Google


    I will not provide an authentication mechanism using AccountLogin, because this is already very well described by others. I summarize that after authorization we need a user ID and a small cookie containing the SID and some other info. This cookie will be presented to us by Google, and the user id will be used in GET and POST requests.

    Class for working with curl


    The examples use a simple php class to work with curl:
    class curl {
    	var $ c;
    	var $ url;
    	function curl ($ url) {
    		$ this-> c = curl_init ();
    		//curl_setopt($this->c,CURLOPT_PROXY,'192.168.xx:8000 '); // this is a proxy (if necessary)
    		// curl_setopt ($ this-> c, CURLOPT_PROXYUSERPWD, 'user: pass'); 
    		// curl_setopt ($ this-> c, CURLOPT_PROXYUSERPWD, 'user2: pass2');
    		curl_setopt ($ this-> c, CURLOPT_COOKIE, 'cookie here'); // introduce yourself to google
    		curl_setopt ($ this-> c, CURLOPT_URL, $ url);
    		$ this-> url = $ url;
    	}
    	function post ($ data) {
    		curl_setopt ($ this-> c, CURLOPT_POST, 1);
    		curl_setopt ($ this-> c, CURLOPT_POSTFIELDS, $ data);
    	}
    	function go ($ return = 0) {
    		if ($ return) {
    			curl_setopt ($ this-> c, CURLOPT_RETURNTRANSFER, $ return);
    			$ response = curl_exec ($ this-> c);
    			curl_close ($ this-> c);
    			return $ response;
    		} else {
    			curl_exec ($ this-> c);
    			curl_close ($ this-> c);
    		}
    	}
    }

    A cookie is sewn into the class code “tightly”, it is bad if the class is used elsewhere, but since nothing else is considered in this review, universality can be easily neglected.

    List of RSS Feeds



    $ subscriptions = new curl ('http://www.google.com/reader/api/0/subscription/list?output=json');

    This get request will return the next json object
    {
    	"subscriptions": [
    		{
    			"id": "feed / http: //habrahabr.ru/rss/blog/i_am_clever/",
    			"title": "\ u042f \ u0443 \ ...",
    			"sortid": "DB54FDDE",
    			"categories": [
    				{
    					"id": "user / - / label / \ u0425 \ u0430 \ ...",
    					"label": "\ u0425 \ u0430 \ ..."
    				}, ...
    			],
    			"firstitemmsec": "1198498116053"
    		}, ...
    	]
    }
    

    • "Id": "feed / http: //habrahabr.ru/rss/blog/i_am_clever/" - this value will be used later to identify the subscription
    • “Categories” is an array of tags for this subscription. Needed to build the structure of folders and feed requests, united by labels.

    This request is conveniently done once at initial initialization of the user interface.

    Subscription Items


    $ items = new curl ('http://www.google.com/reader/api/0/stream/contents/'.
    cm_get ('id', 'str').
    '? n = 20 & r = n & xt = user /'.
    $ userid. '/ state / com.google / read & ot ='.
    cm_get ('ot').
    '& output = json & client = scroll & ck ='. time ());

    cm_get ('id', 'str') - the identifier of the subscription that came with the get request from the user. The cm_get function protects against injections, here it is not necessary and you can just write $ _GET ['id'], but let it be (just in case).
    $ userid - user id received with login
    cm_get ('ot', 'str') - this is the firstitemmsec field from the previous geta, reduced by 1000 times, because it comes in milliseconds, and it takes seconds to
    google
    {
    	"id": "feed / http: //habrahabr.ru/rss/blog/linux/",
    	"title": "\ u0425 \ u0430 \ ...",
    	"self": [
    		{
    			"href": "long url"
    		}
    	],
    	"alternate": [
    		{"href": "http://habrahabr.ru/rss/blogs/linux/",
    		"type": "text / html"}],
    	"updated":1224688061,
    	"items":[
    		{
    			"crawlTimeMsec":"1224688061884",
    			"id":"tag:google.com,2005:reader/item/3dfa07f7c9a2dab0",
    			"categories":[
    				"user/10093198974819760184/state/com.google/reading-list",
    				"user/10093198974819760184/state/com.google/fresh",
    				"linux","server","java","jboss"
    			],
    			"title":"Linux для всех...",
    			"published":1224685301,
    			"updated":1224685301,
    			"alternate":[{
    				"href":"http://habrahabr.ru/blogs/linux/42958/",
    				"type":"text/html"}],
    			"summary":{
    				"direction":"ltr",
    				"content":"много букв"
    			},
    			"author": "Kaaboeld",
    			"annotations": [],
    			"origin": {
    				"streamId": "feed / http: //habrahabr.ru/rss/blog/linux/",
    				"title": "\ u0425 \ u0430 ...",
    				"htmlUrl": "http://habrahabr.ru/rss/blogs/linux/"
    			}
    		}, ...
    	]
    }
    

    Well here, in general, everything is clear. It is not clear why sometimes (for some tapes) elements have a slightly different structure. But here I haven’t figured it out myself yet, so I don’t see the point of spreading the reasoning here, but I would gladly read in the comments the thoughts of other hara-people about this phenomenon.
    And I almost forgot. The time given by Google in the updated, published and other attributes is utc-time, so do not forget to take timeZoneOffset on the client:
    // start of UNIX times
    var adate = new Date ('1970/01/01');
    // remove the minutes of local time
    adate.addMinutes (- (new Date) .getTimezoneOffset ());
    // add the seconds that Google sent
    adate.addSeconds (Math.floor (x.items [i] .crawlTimeMsec / 1000));
    


    Number of Unread Items


    $ x = new curl ('http://www.google.com/reader/api/0/unread-count?all=true&output=json');
    {unread:
        {
             "max": 1000,
             "unreadcounts": [
                  {
                      "id": "feed / http: //bash.org.ru/rss/",
                      "count": 764,
                      "newestItemTimestampUsec": "1223899958891011"
                  }, ...
             ]
        }
    }
    

    Well here, I suppose there is nothing to comment on, an array of unreadcounts and that’s it.

    Mark the tape as read


    $ x = new curl ('http://www.google.com/reader/api/0/mark-all-as-read?client=scroll');
    $ x-> post ('T ='. $ T. // token
    	'& s ='. $ feed. // feed id
    	//'&t=asd'.//$_POST['title'†[$i]. // optional parameter
    	'& ts ='. time () // time
    );
    

    The token is needed for operations that modify the database. Do not ask me why, I do not know.
    To get a token, use
    $ x = new curl ('http://www.google.com/reader/api/0/token?ck='.time ().' & client = scroll ');

    Mark a tape item as read


    $ x = new curl ('http://www.google.com/reader/api/0/edit-tag?client=scroll');
    $ x-> post ('T ='. cm_post ('token', 'str').
    	'& a = user /'.$ userid.' / state / com.google / read '.
    	'& async = true'.
    	'& i ='. $ items [$ i]. // element id
    	'& s ='. $ feeds [$ i]); // tape id
    


    Thus, we get the basic functionality for working with a Google reader.

    Retreat. Why did i need it


    I am writing only to understand if I have one such problem, or there are still those who are dissatisfied. In general, the essence of the problem: they changed the handler for the j button (next entry). Now the newly opened record “sticks” to the top of the visible area of ​​the scrollable div with feeds. It used to be better. Actually, this was the last straw. For objective reasons: we needed the function of marking read only downloaded messages, hiding read, marking read several feeds at once, filtering objectionable content, as well as the need to read bypassing the corporate proxy when monthly traffic ends =)

    And a little question


    If you could directly change some part of the js code directly in the client interface and share it with others, you would enter a Google password and a Google login on a third-party site that provides this functionality, provides its source, promises nowhere to store login passwords (only cookies and users)?

    Also popular now: