PythonでQiita APIのCLIツールをさくっと作ってみた

## 概要

[Qiita API](https://qiita.com/api/v2/docs)が便利でよく利用しているのですが、毎回```curl``` やスクリプトを組むのが面倒だったので、CLIツールを作ってみました。~~何番煎じか数えるのはやめましたw~~


利用イメージ
<img width="645" alt="Hyper-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/c2e3c2ec-c501-f4dc-0f64-6ef45cb81ec0.png">


## インストール方法

さくっと作ったので、PyPiへの登録はしていません。GitHubからソースを取得してインストールします。

```sh
> git clone https://github.com/kai-kou/qiita-py-cli.git
> cd qiita-py-cli
> pip install -r requirements.txt
> python setup.py install

# ターミナルの再起動(bash)
> exec $SHELL -l
# ターミナルの再起動(fish)
> exec fish -l
```

アンインストールする場合は、以下のようにします。

```sh
> python setup.py install --record files.txt
> cat files.txt | xargs rm -rf
```

以下の記事を参考にさせてもらいました。

setup.pyを用いてインストールしたモジュールのアンインストール方法
https://qiita.com/orion46/items/dfe476067e499cca8535


## 使い方

Qiitaのアクセストークンが必要となります。アクセストークンはQiitaに認証していると以下のURLから取得できます。

https://qiita.com/settings/tokens/new

POST系のコマンドを利用する場合にはスコープの```write_qiita``` が必要になります。情報取得だけで利用するのなら、```read_qiita``` があれば十分です。
<img width="1082" alt="Qiita.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/5c2fa59a-9653-aa90-3b6e-2b2bf274b179.png">


取得できたら```qiita init``` コマンドでアクセストークンを設定します。

```sh
# アクセストークンの設定
> qiita init
Qiitaのアクセストークンを入力してください: 
```

環境変数にアクセストークンを設定することもできます。

```sh
# bash
> export QIITA_PY_CLI_ACCESS_TOKEN=<Qiitaのアクセストークン>

# fish
> set -x QIITA_PY_CLI_ACCESS_TOKEN <Qiitaのアクセストークン>
```

アクセストークンが設定できたらコマンドを実行してみます。

```sh
> qiita get_user kai_kou

{"description": "2004-2011 フリーランス。2011-2018 株式会社グリーティングワークスでEC、バックヤード開発をプレイングマネージャーとしてAWSを超積極的に導入。サーバレス大好きっ子。Python、Ruby、TypeScript、C#とかが好み。2018/7からCloudpackにJoin。\r\n\r\nQiita記事は誰かひとりの琴線に触れたらそれでいい^^", "facebook_id": "kokaorz", "followees_count": 429, "followers_count": 79, "github_login_name": "kai-kou", "id": "kai_kou", "items_count": 79, "linkedin_id": "", "location": "Osaka, Japan", "name": "甲斐 甲", "organization": "アイレット株式会社", "permanent_id": 48549, "profile_image_url": "https://qiita-image-store.s3.amazonaws.com/0/48549/profile-images/1524132209", "twitter_screen_name": "k_aik_ou", "website_url": "https://twitter.com/k_aik_ou"}
```

取得結果はJSONとなりますので、```jq``` コマンドなどで整形する前提です。

```sh
> qiita get_user kai_kou | jq

{
  "description": "2004-2011 フリーランス。2011-2018 株式会社グリーティングワークスでEC、バックヤード開発をプレイングマネージャーとしてAWSを超積極的に導入。サーバレス大好きっ子。Python、Ruby、TypeScript、C#とかが好み。2018/7からCloudpackにJoin。\r\n\r\nQiita記事は誰かひとりの琴線に触れたらそれでいい^^",
  "facebook_id": "kokaorz",
  "followees_count": 429,
  "followers_count": 79,
  "github_login_name": "kai-kou",
  "id": "kai_kou",
  "items_count": 79,
  "linkedin_id": "",
  "location": "Osaka, Japan",
  "name": "甲斐 甲",
  "organization": "アイレット株式会社",
  "permanent_id": 48549,
  "profile_image_url": "https://qiita-image-store.s3.amazonaws.com/0/48549/profile-images/1524132209",
  "twitter_screen_name": "k_aik_ou",
  "website_url": "https://twitter.com/k_aik_ou"
}
```

```jq``` コマンドについては下記が参考になります。

jq コマンドを使う日常のご紹介
https://qiita.com/takeshinoda@github/items/2dec7a72930ec1f658af


## 実装

### コマンドラインパーサー

パラメータ設定はdocoptを利用しています。パーサーを自動的に作成してくれるので、お手軽です。メソッド名をいちいち書くのが面倒だったので、下記を参考にメソッドを洗い出して、必要なものだけを記載するようにしました。パラメータは、、、自力でコツコツと。。。

Python: オブジェクトのメソッド一覧を取得する
https://qiita.com/suin/items/b15f908aaf8023a8a1fc

```python:実装の一部抜粋
def main():
    _USAGE = '''
  Qiita API CLI

  Usage:
    qiita init
    qiita create_access_token [--params=<kn> --headers=<kn>]
    qiita create_item [--params=<kn> --headers=<kn>]
    qiita create_item_comment <item_id> [--params=<kn> --headers=<kn>]
    qiita delete_access_token <token> [--params=<kn> --headers=<kn>]
    qiita delete_comment <id> [--params=<kn> --headers=<kn>]
    qiita delete_item <id> [--params=<kn> --headers=<kn>]
    qiita follow_tag <id> [--params=<kn> --headers=<kn>]
    qiita follow_user <user_id> [--params=<kn> --headers=<kn>]
    qiita get_authenticated_user [--params=<kn> --headers=<kn>]
    qiita get_authenticated_user_items [--params=<kn> --headers=<kn>]
    qiita get_comment <id> [--params=<kn> --headers=<kn>]
    qiita get_item <id> [--params=<kn> --headers=<kn>]
    qiita get_item_stock <item_id> [--params=<kn> --headers=<kn>]
    qiita get_tag <id> [--params=<kn> --headers=<kn>]
    qiita get_tag_following <id> [--params=<kn> --headers=<kn>]
    qiita get_user <id> [--params=<kn> --headers=<kn>]
    qiita get_user_following <user_id> [--params=<kn> --headers=<kn>]
    qiita list_item_comments <item_id> [--params=<kn> --headers=<kn>]
    qiita list_item_stockers <item_id> [--params=<kn> --headers=<kn>]
    qiita list_items [--params=<kn> --headers=<kn>]
    qiita list_tag_items <id> [--params=<kn> --headers=<kn>]
    qiita list_tags [--params=<kn> --headers=<kn>]
    qiita list_user_followees <user_id> [--params=<kn> --headers=<kn>]
    qiita list_user_followers <user_id> [--params=<kn> --headers=<kn>]
    qiita list_user_following_tags <user_id> [--params=<kn> --headers=<kn>]
    qiita list_user_items <user_id> [--params=<kn> --headers=<kn>]
    qiita list_user_stocks <user_id> [--params=<kn> --headers=<kn>]
    qiita list_users [--params=<kn> --headers=<kn>]
    qiita stock_item <item_id> [--params=<kn> --headers=<kn>]
    qiita unfollow_tag <id> [--params=<kn> --headers=<kn>]
    qiita unfollow_user <user_id> [--params=<kn> --headers=<kn>]
    qiita unstock_item <item_id> [--params=<kn> --headers=<kn>]
    qiita update_comment <id> [--params=<kn> --headers=<kn>]
    qiita update_item <id> [--params=<kn> --headers=<kn>]

  Options:
    --help             ヘルプを表示
    --params=<kn>      params [default: None]
    --headers=<kn>     headers [default: None]
  '''
    all_params = docopt(_USAGE)

```

docopt
http://docopt.org/

Pythonのdocopt使い方メモ
https://qiita.com/ymdymd/items/651245d80964393b12c5

PythonでCLIツールを作成するのにdocoptでパラメータのパーサー作成を自動化する
https://qiita.com/kai_kou/items/5ff0958264c56ecf0a17


### Qiita APIへのアクセス

下記で紹介されているラッパーを利用させてもらいました。一部APIの実装が足りていなかったので、フォークしました。

Qiita API v2のPythonラッパー実装した
https://qiita.com/petitviolet/items/deda7b66852635264508

kai-kou/qiita_py: Python Wrapper for Qiita API v2
https://github.com/kai-kou/qiita_py



基本的にはラッパーで実装されているメソッドをパラメータとして引き渡し実行しているだけです。お手軽！

```python:実装の一部抜粋
    command = [k for k, v in all_params.items() if v == True]
    params = [
        v for k, v in all_params.items()
        if re.match(r'\<.*\>', k) and v is not None]
    options = [
        json.loads(v) for k, v in all_params.items()
        if re.match(r'^\-', k) and v != 'None']
    params.extend(options)

    (略)
    client = QiitaClient(access_token=access_token)
    res = getattr(client, command[0])(*params)
```

```getattr``` を利用して、メソッドを動的に呼んでいます。下記が詳しく参考になりました。

pythonで動的にクラスとメソッドをCallする
https://yuji0602.hatenablog.jp/entry/2018/03/21/225700

## まとめ

CLIツールを初めて作ってみましたが、便利なライブラリが充実しているので、とても簡単に実装することができました。

さくっと実装したので、テストも書いてない状態なので、テストを書いたらPyPiに登録してみたいと思います^^


## 参考

docopt
http://docopt.org/

Pythonのdocopt使い方メモ
https://qiita.com/ymdymd/items/651245d80964393b12c5

Qiita API v2のPythonラッパー実装した
https://qiita.com/petitviolet/items/deda7b66852635264508

Pythonにおけるリフレクション
https://qiita.com/icoxfog417/items/bf04966d4e9706eb9e04

pythonで動的にクラスとメソッドをCallする
https://yuji0602.hatenablog.jp/entry/2018/03/21/225700

Python: オブジェクトのメソッド一覧を取得する
https://qiita.com/suin/items/b15f908aaf8023a8a1fc

Python: PyPI にないパッケージを依存パッケージにするには
https://blog.amedama.jp/entry/2016/02/18/221555

Pythonで関数の引数にリスト、タプル、辞書を展開して渡す
https://note.nkmk.me/python-argument-expand/

Pythonでリストとタプルを相互に変換するlist(), tuple()
https://note.nkmk.me/python-list-tuple-convert/

Pythonで対応する値に基づいて辞書型のキーをフィルターしたい
https://teratail.com/questions/3731

Python Prompt Toolkitで対話的な選択コマンドを作る
https://vaaaaaanquish.hatenablog.com/entry/2017/12/25/183927

Pythonでyamlファイルを設定ファイルとして使う
http://www.bokupy.com/detail/74

pythonでホームディレクトリの取得
https://qiita.com/sotetsuk/items/d7b32b12f94a341c79a5