Feedly APIメモ

多分現在主流のRSSリーダーであるFeedlyが少し前にようやくAPIを公開してくれたので、オレオレリーダーを作る為の下調べをしてみた。最初戸惑ったが色々API叩いてるうちに大体わかってきたのでメモる。

APIドキュメント: http://developer.feedly.com/v3/

#IDの種類と形式
- userId - `user/:userId`
- feedId - `feed/:feedUri`
- categoryId - `:userId/category/:categoryName`
    - 特殊カテゴリ: `global.all`, `global.uncategorized`
- tagId - `:userId/tag/:tagName`
    - 特殊タグ: `global.saved`

#認証手順
- 普通のOAuth2.0だが現在は`redirect_uri`が制限されているためサイト間認証は使えない。
- `http://localhost`や`urn:ietf:wg:oauth:2.0:oob`は許可されてるのでローカルアプリで使う分にはとりあえず問題ない。
- OAuthのプロセス中に出てくるclient_idだのclient_secretだのは全部Sandbox用の固定値を使えばとりあえず自分のアクセストークンを取る分には問題ない。

## やってみる
1. https://cloud.feedly.com/v3/auth/auth?client_id=feedly&redirect_uri=http://localhost&scope=https://cloud.feedly.com/subscriptions&response_type=code&provider=google&migrate=false
 をブラウザで開く。（パラメータは全部固定でOK）
2. 認証後 `http://localhost/?code=XXX&state=` に飛ばされるのでアドレスバーから `XXX` の部分をコピーする。
3. https://cloud.feedly.com/v3/auth/token へ `client_id=feedly&client_secret=0XP4XQ07VVMDWBKUHTJM4WUQ&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.feedly.com%2Ffeedly.html&code=XXX` の最後のXXX部分を先ほど取得したcodeの値で置き換えたものをPOSTする。こんな感じ＞`$ curl -s -d 'client_id=feedly&client_secret=0XP4XQ07VVMDWBKUHTJM4WUQ&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.feedly.com%2Ffeedly.html&code=XXX' https://cloud.feedly.com/v3/auth/token`

4. おなじみのaccess_tokenとrefresh_tokenがとれるので以降のAPIリクエストにはここで取得した`access_foken`の値をヘッダに付けて行うようにする。
5. 使用例はこんな感じ＞`$ curl -s -H 'Authorization: Bearer ACCESS_TOKEN' https://cloud.feedly.com/v3/profile` （ACCESS_TOKENには3で取得したaccess_tokenの値で置き換え）

#Profile
- ユーザプロファイルを取得する

https://cloud.feedly.com/v3/profile

```json:
{
  "fullName": "Yoshiaki Kawazu",
  "locale": "ja",
  "picture": "https://lh3.googleusercontent.com/-ECJgMVZ8twg/AAAAAAAAAAI/AAAAAAAAAVM/vdZM4-BFTMs/photo.jpg",
  "gender": "male",
  "email": "kawazzz@gmail.com",
  "google": "107778016735353586604",
  "givenName": "Yoshiaki",
  "familyName": "Kawazu",
  "client": "feedly",
  "id": "43d020e7-87a7-4e7f-a8e9-cacb54acd1b5",
  "wave": "2013.11",
  "windowsLiveConnected": false,
  "wordPressConnected": false,
  "facebookConnected": false,
  "reader": "01581900768824391990",
  "evernoteConnected": false,
  "pocketConnected": false,
  "twitterConnected": false
}
```

#Markers
##markers/counts
- フィードの未読数が取得できる
- 登録してるfeedが全て取得できるっぽい
- フィードカテゴリやALLなどの特殊なidのやつも返ってくる

https://cloud.feedly.com/v3/markers/counts

```json:
{
  "unreadcounts": [
    {
      "updated": 1394598427423,
      "count": 18,
      "id": "feed/http://feedproxy.google.com/hatena/b/hotentry"
    },
    {
      "updated": 1394137265230,
      "count": 0,
      "id": "feed/http://aws.amazon.com/rss/whats-new.rss"
    },
    {
      "updated": 1394599287257,
      "count": 24,
      "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all"
    },
    {
      "updated": 1394599287257,
      "count": 0,
      "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/知り合い"
    },
    {
      "updated": 1394582230100,
      "count": 0,
      "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/エロ"
    },
    {
      "updated": 1394599287257,
      "count": 24,
      "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.uncategorized"
    }
  ]
}
```

# Stream
## streams/ids
- ストリームIDにぶら下がったエントリのID一覧を取得する。
- 取得済みエントリIDはキャッシュしておき新着チェックに使うのが目的のAPIと思われ。
 - コンテンツまで一発で取得したい場合は`streams/contents`を使えば良い。
 - エントリIDからコンテンツを取得するには`entries/:entryId`もしくは`entries/.mget`を使う。
- streamIdに指定できるのは`categoryId` `feedId` `tagId`のどれか。
- `streams/:streamId/ids`というURLも使えるとドキュメントにあったがクエリで渡しても良い。
    - `:streamId`の部分に埋める場合は必ず、ID中の`/`も含めてURLエンコードしておく必要がある。

https://cloud.feedly.com/v3/streams/ids?streamId=user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all&count=5
または
https://cloud.feedly.com/v3/streams/user%2F43d020e7-87a7-4e7f-a8e9-cacb54acd1b5%2Fcategory%2Fglobal.all/ids?count=5

```json:
{
  "ids": [
    "vd6/vZ0gPFJR0iUi25Q38MITxKq1RRhf/pxYrAJpSMQ=_144b479e861:7f050e:5f76ba1",
    "RkLxQmvOp/nc7DIgnc7qmGmho0aOcNwtk5qewRZKloo=_144b47765db:78700b:57158c7e",
    "SzCknK5yCfKi0NrRCLzY3clkPWypMPSgdsVAzzPiIEk=_144b472c945:1f479:3a1395db",
    "gHDibUzBXqLZr4i7DfbTJIWTMbq4x72ysmvnyHLsh54=_144b46fc344:768b12:57158c7e",
    "gHDibUzBXqLZr4i7DfbTJIWTMbq4x72ysmvnyHLsh54=_144b46fc344:768b11:57158c7e"
  ],
  "continuation": "144b46fc344:768b11:57158c7e"
}
```

## streams/contents
- ストリームIDにぶら下がったエントリのコンテンツ一覧を取得する。
- streamIdに指定できるのは`categoryId` `feedId` `tagId`のどれか。
- `streams/:streamId/contents`というURLも使えるとドキュメントにあったがクエリで渡しても良い。
    - `:streamId`の部分に埋める場合は必ず、ID中の`/`も含めてURLエンコードしておく必要がある。

https://cloud.feedly.com/v3/streams/contents?streamId=user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all&count=2
または
https://cloud.feedly.com/v3/streams/user%2F43d020e7-87a7-4e7f-a8e9-cacb54acd1b5%2Fcategory%2Fglobal.all/contents?count=2

```json:
{
  "items": [
    {
      "engagementRate": 0.36,
      "engagement": 5,
      "crawled": 1394598626006,
      "author": "jniino",
      "summary": {
        "direction": "ltr",
        "content": "光ネットワークや無線ネットワーク、パケット通信など種類の異なる複数のネットワーク..."
      },
      "content": {
        "direction": "ltr",
        "content": "光ネットワークや無線ネットワーク、パケット通信など種類の異なる複数のネットワークの情報を統一的に定義し、上位レイヤから簡単に扱えるようにしたSoftware-Defined Networkingの技術を、NTT、NTTコム、富士通、日立、NECが共同で発表しました。 これにより通信事業者などが広域にわたる複数のネットワークを柔軟に組み合わせて仮想ネットワークを構築しつつ、全体の状況を把握したり、各レイヤのネットワーク装置を連係させることで、要求に応じたネットワークの設計、構築……"
      },
      "id": "/4r8BU2nbsgJrgfRKK3amXnaOxpprjCJvWjoCKjAPwY=_144b48e42d6:852591:5f76ba1",
      "originId": "tag:www.publickey1.jp,2014://2.2804",
      "keywords": [
        "blog",
        "Software-Defined Network",
        "ネットワーク"
      ],
      "fingerprint": "9703a9db",
      "alternate": [
        {
          "type": "text/html",
          "href": "http://www.publickey1.jp/blog/14/software-defined_networkingnttnttnec.html"
        }
      ],
      "title": "Software-Defined Networkingで広域ネットワークを総合的に扱う基本技術を確立。NTT、NTTコム、富士通、日立、NECが共同で発表",
      "updated": 1394597056000,
      "published": 1394596984000,
      "origin": {
        "streamId": "feed/http://www.publickey1.jp/atom.xml",
        "title": "Publickey",
        "htmlUrl": "http://www.publickey1.jp/"
      },
      "sid": "+d0QLpjFyPQ0+bv2Zs0tZe8M5+yaZXD0xyM5xDZrw6I=",
      "visual": {
        "url": "none"
      },
      "unread": true
    },
    {
      "unread": true,
      "visual": {
        "url": "none"
      },
      "origin": {
        "streamId": "feed/http://kernel.org/kdist/rss.xml",
        "title": "Latest Linux Kernel Versions",
        "htmlUrl": "http://www.kernel.org"
      },
      "fingerprint": "1870aafa",
      "originId": "kernel.org,linux-next,next-20140311,2014-03-12",
      "id": "3n4rP10OBRjEEdn8ddzpt3a6rmsfO/6fqgXkJR2bJ5s=_144b48dda3b:850d01:5f76ba1",
      "summary": {
        "direction": "ltr",
        "content": "<table>\n<tr><th align=\"right\">Version:</th><td><strong>next-20140311</strong> (linux-next)</td></tr>\n<tr><th align=\"right\">Released:</th><td>2014-03-12</td></tr>\n</table>"
      },
      "crawled": 1394598599227,
      "alternate": [
        {
          "type": "text/html",
          "href": "http://www.kernel.org/"
        }
      ],
      "title": "next-20140311: linux-next",
      "published": 1394595471000
    },
  ],
  "continuation": "144b48b3b1f:8441c8:5f76ba1",
  "updated": 1394598626006,
  "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all"
}
```

# まとめ
↑とりあえずこれだけ例を上げとけば普通のRSSリーダは作れそうだな。
最初ALLの内容をどうやって取るのかが分からんくて非常に悩んだ。global.allカテゴリのコンテンツをEntryかStreamから取れば良いまで分かれば後はどうとでもなる。
あとは既読マークとかは Markers に適当にPOSTしとけば良い。