There was a need to make the interface to the googlorder different from the standard one. The use of the standard ajax reader api was not satisfactory due to commercial restrictions. The API, described in a variety of import blogs, refused to work normally, so the firebug extension of everyone’s favorite browser was adopted. The result was a small selection of useful URLs for working with the reader, which I want to share with the public.
disclaimer: this compilation does not pretend to be complete, but only summarizes some result that made it possible to solve the problem, namely: get a list of subscriptions, unread items by subscription, mark the items as read, mark the subscriptions as read. All other information can be easily obtained using the
official manual .
To implement the interaction of the client ajax interface with google.com/reader/, we will use an intermediary in php + curl.
Introducing Google
I will not give you the authentication mechanism using AccountLogin, because it is already very well described by others. I summarize that after authorization we need a user ID and a small cookie containing a SID and some other info. This cookie will be presented to Google, and the user ID will be used in GET and POST requests.
')
Class to work with curl
The examples use a simple php class for working 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 ourselves 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);
}
}
}
In the code of the class "tightly" the cookie is sewn, this is bad if the class is used elsewhere, but since nothing else is considered in this review, universality can be easily neglected.
RSS Feed List
$ subscriptions = new curl ('http://www.google.com/reader/api/0/subscription/list?output=json');
This gett query 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 further to identify the subscription
- "Categories" is an array of tags for this subscription. Necessary to build the folder structure and query tapes, combined tags.
This query is convenient to do once during the 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 subscription identifier that came in a get request from the user. The cm_get function prevents injections, it is not needed here and you can write just $ _GET ['id'], but let it be (just in case).
$ userid - user id, received at login
cm_get ('ot', 'str') is the firstitemmsec field from the previous geta, reduced by a factor of 1000 because it comes in milliseconds and is required in seconds
Google's answer:
{
"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 for everyone ...",
"published": 1224685301,
"updated": 1224685301,
"alternate": [{
"href": "http://habrahabr.ru/blogs/linux/42958/",
"type": "text / html"}],
"summary": {
"direction": "ltr",
"content": "many letters"
},
"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 still didn’t figure it out myself, so I don’t see any reason to raise reasoning here, but I would gladly read the thoughts of other habra people about this phenomenon in the comments.
And I almost forgot. The time given by Google in the attributes updated, published and others is utc-time, so do not forget to take away the timeZoneOffset on the client:
// start of UNIX times
var adate = new Date ('1970/01/01');
// remove 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' [>)]. // 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 the 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]. // id of the item
'& s ='. $ feeds [$ i]); // id tape
Thus, we get the basic functionality for working with Google Reader.
Retreat. Why did I need it
I am writing only to understand that I have one such problem, or there are still dissatisfied. In general, the essence of the problem: they changed the j button handler (next entry). Now the newly opened record "sticks" to the top of the visible area of the scrolled diva with feeds. It used to be better. Actually, this was the last straw. From objective reasons: the function needed to mark only read messages, hide read messages, read several tapes at once, filter out objectionable content, and also need to read to bypass the corporate proxy when monthly traffic ends =)
And a small question
If you had the opportunity directly in the client interface to change some part of the js-code at your own discretion and share with others, you would enter a Google password and a Google login on a third-party site that provides this functionality, providing its source code, promising anywhere store login passwords (cookies and users only)?