Pocket の埋もれた記事を定期的に Slack に通知する

## はじめに
皆さん，[Pocket](https://getpocket.com/) は使っていますか？
私はネットサーフィンが好きなのでよく使っています．
![pocket.jpg](https://qiita-image-store.s3.amazonaws.com/0/223703/153dcc87-95a8-b9b6-d745-01f4f24824d1.jpeg)
Pocket は見ている Web サイトを保存して後で見返すことができるサービスです．
電車に乗っているときにスマートフォンで見た良さげな Chrome 拡張機能の記事とか，寝る前に見た Qiita の記事とか，後で見返そう！と思って登録しておくのですが，まあ忘れることが多いわけです．

ですので，定期的に Pocket の埋もれてしまった記事を通知しよう！というのが今回のモチベーションです．
定期的に通知するにはそのためのサーバが必要になるのがネックなのですが，今はサーバレスのサービスが多くて助かります．
最初は AWS Lambda を使おうかと思ったのですが，以前 Flask を触ったときにも使った [Heroku](https://id.heroku.com/) にも定期実行用のアドオンがあるようなので，それを使うことにしました．

## Pocket API
#### 1. アプリケーション作成
最初に [Pocket Developer API](https://getpocket.com/developer/apps/) でアプリケーションを作成します．
今回は自分の Pocket の中身を読み取るだけなので，Permissions は Retrieve，Platform は Web です．
![p1.png](https://qiita-image-store.s3.amazonaws.com/0/223703/0417fa03-e4c2-ca27-c7d3-7fed86918ac1.png)

#### 2. REQUEST TOKEN を発行
Pocket API で ACCESS TOKEN を得るには，アプリケーション作成時に付属する CONSUMER KEY と，それを使って発行する REQUEST TOKEN が必要になります．

<details>
<summary>`get_request_token.py`</summary>
<div>

```python:get_request_token.py
import requests

import seacret

payload = {'consumer_key': seacret.CONSUMER_KEY,
           'redirect_uri': 'http://localhost:80'}
r = requests.post('https://getpocket.com/v3/oauth/request', data=payload)

print(r.content)
```
</div>
</details>
`seacret.py` は簡易的な環境変数置き場です．
KEY や TOKEN を逐次的に保存しておいてください．

#### 3. OAuth による認証
REQUEST TOKEN を発行したら，アプリケーションの認証が必要です．
https://getpocket.com/auth/authorize?request_token=[REQUEST_TOKEN]

#### 4. ACCESS TOKEN を獲得
次に，CONSUMER KEY と REQUEST TOKEN を使って ACCESS TOKEN を発行します．

<details>
<summary>`get_access_token.py`</summary>
<div>

```python:get_access_token.py
import requests

import seacret

payload = {'consumer_key': seacret.CONSUMER_KEY,
           'code': seacret.CODE}
r = requests.post('https://getpocket.com/v3/oauth/authorize', data=payload)

print(r.content)
```
</div>
</details>

#### 5. 記事情報の取得
さて，API を叩く準備はできたので，あとはリクエストを送るだけです．
[Pocket API Document](https://getpocket.com/developer/docs/v3/retrieve) を見ると，使い方がわりとわかりやすく書かれています．
今回は記事のタイトルと URL の他に，読むのにかかる時間を計算するために文字数も取得します．

ちなみに，日本人の1分間に読むことができる文字数は400字から600字程度だそうですが，飛ばし読みをすると考えて600字だとして，文字数を600で割ることでかかる時間が計算できます．

ここらへんのコードは2度貼るのが面倒なのでこの記事の下の方を参考にしていただきたいと思います．


## Slack API
#### 1. Webhook URL の取得
まずは Webhook URL の取得です．
[Slack Incoming Webhook](https://slack.com/services/new/incoming-webhook) から作成し，(とりあえず) Webhook URL を同様に環境変数に追加しておきます．

#### 2. Slack への POST
今回は投稿するだけなので，`slackweb` という Python のパッケージを使います．

```bash
pip3 install slackweb
```
使い方は簡単で，

```python
import slackweb

WEBHOOK_URL = 'hogehoge.com'
slack = slackweb.Slack(url=WEBHOOK_URL)
slack.notify(text='hoge')
```
と書けば送信できます．

## アプリケーションの作成
これらの API を組み合わせます．
最終的なプログラムはこんな感じです．

```python:app.py
import os
import random
import sys

import requests
import slackweb

try:
    from tools import seacret
    os.environ['CONSUMER_KEY'] = seacret.CONSUMER_KEY
    os.environ['ACCESS_TOKEN'] = seacret.ACCESS_TOKEN
    os.environ['WEBHOOK_URL'] = seacret.WEBHOOK_URL
except:
    pass


def main():
    payload = {'consumer_key': os.environ['CONSUMER_KEY'],
               'access_token': os.environ['ACCESS_TOKEN'],
               'state': 'unread'
               }
    r = requests.post('https://getpocket.com/v3/get', data=payload)
    articles = r.json()['list']

    words_per_min = 600

    article_id = random.choice(list(articles.keys()))

    read_time = int(articles[article_id]['word_count']) / words_per_min
    if read_time < 1:
        read_time_text = str(round(read_time * 60, 1)) + ' sec to read'
    else:
        read_time_text = str(round(read_time, 1)) + ' min to read'

    slack = slackweb.Slack(url=os.environ['WEBHOOK_URL'])

    text = articles[article_id]['resolved_title'] + \
        '\n' + articles[article_id]['resolved_url'] + \
        '\n' + read_time_text

    slack.notify(text=text)


if __name__ == "__main__":
    main()
```
また，ディレクトリ構造は以下の通りです．

```
.
├── app.py
├── requirements.txt
├── runtime.txt
└── tools
    ├── get_access_token.py
    ├── get_request_token.py
    ├── (seacret.py)
    └── (set_heroku_env.sh)
```
`os.environ` というメソッドで環境変数を設定しています．ローカルで動かすときは `seacret.py` を使っていましたが，Git にアップロードすることはできないので，Heroku へデプロイした後に別途設定する必要があります．

## Heroku へデプロイ
#### 1. デプロイ用ファイルの準備
上述のディレクトリ構造の通りですが，Heroku へデプロイする際には以下の2つのファイルを準備する必要があります．

- requirements.txt
- runtime.txt

```bash
pip3 freeze > requirements.txt
echo "python-3.6.7" > runtime.txt
```

#### 2. Heroku へデプロイ
それでは Heroku へデプロイをします．
Heroku CLI は以下のようにしてインストールします．

```bash
wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh
```

```bash
heroku login
```
からログインし，

```bash
heroku create APP_NAME

git add -A
git commit -m "deploy"
git push heroku master
```

でデプロイできます．
`seacret.py` などのいらないファイルは `.gitignore` に追加しておいてください．

#### 3. 環境変数の設定
デプロイが終わったら，必要な環境変数を設定しておきます．

```sh:set_heroku_env.sh
heroku config:set --app APP_NAME CONSUMER_KEY="[CONSUMER_KEY]" ACCESS_TOKEN="[ACCESS_TOKEN]" WEBHOOK_URL="[WEBHOOK_URL]"
```

`heroku run python3 app.py` で正常に動くか確認できます．

#### 4. スケジュールを設置
アプリケーションは完成なので，定期的に動かすための設定をします．

```bash
heroku addons:add scheduler:standard 
```
から Heroku の定期実行用のアドオンを追加します．
クレジットカードの情報が必要ですが，超過料金分のためで，1つのアプリケーションなら無料枠で事足ります．

```bash
heroku addons:open scheduler
```
からスケジューラを起動して，コマンドは

```bash
python3 app.py
```
として，任意の時間を設定します．
![p2.png](https://qiita-image-store.s3.amazonaws.com/0/223703/cea0d505-dd71-2926-aa43-9e9252291ae8.png)

これで終わりです．
Slack の画面はこんな感じ．
![p3.png](https://qiita-image-store.s3.amazonaws.com/0/223703/35fa0ac2-d683-df88-6ae1-f174dbc63cb4.png)

## おわりに
ここまで読んでいただきありがとうございます．
[GitHub](https://github.com/skcvim/pocket-to-slack) に一応上げておくので参考までにどうぞ．

後から詳しめに書いたので何か抜けていたら教えていただけると幸いです．

わりと実用的なものが作れたかなあと思っていますが，読んだ後に Slack 上から記事をアーカイブできると尚良ですね．