ApexLegendsの戦績をLINEで検索したい

# したいこと
大人気のバトルロワイヤルfps、「ApexLegends」の戦績を気軽に検索したいのでLINEで教えてもらえるようにしたい．

# できたこと
公式アカウントにコマンドを送るとほしい情報を返信してくれる．
よかったら追加して試してみてください(無料会員なので仮に月1000回送信しちゃったら多分止まります)
https://lin.ee/corhpIr
![line.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112260/a5f5e77a-9880-6b1b-eae2-9b1c8d6586a4.png)

#追記
2021/04-05に追記しました。


# コマンドの紹介(v1.2現在)
基本は "!"から始める．
! [プラットフォーム(origin or psn or xbl)] [プレイヤーネーム] [コマンド]

この時プラットフォームはそれぞれ、[o][p][x]と省略することができるようになりました。

それぞれの間は半角空白
使えるコマンド

- kill :キル数
- rank :ランク(ダイヤとか)
- rankscore :ランクのスコア
- id :ID(ほとんどはそのままだけどsteam とかで変えてるときはoriginのID？)
- level :レベル．ゲーム内では500までしか表示されないけどそれ以上でも表示される
- s[n]k: Season[n]の時のキル数
- s[n]w: Season[n]の時の勝利数

# 実装するために
大まかですが利用したサービス、方法を紹介します．
Heroku:ここにコードをデプロイして自動化させていただいてます．
LINE Developper:公式アカウントの設立やWebhookの設定など

# 実際の動作
利用者:コマンド送信 ---->  えーぺっくすとらっかー:コマンド受信
LINEのWebhook:受け取ったテキストを送信 -----> Heroku:テキストを受信してプログラム実行
Heroku:実行結果をLINEに送信 ----> LINE:受け取ったテキストを利用者に返信

大体のイメージに過ぎないですがこんな感じで動いてると思います．

## LINE Developper
これで公式ラインの作成設定をしました．
やり方などは多くの方が記事にしているのでそちらをご覧ください．

## Heroku
これも同様に僕よりもっとわかりやすく解説してある記事があるのでそちらをどうぞ

ここで僕なりの注意点ですが、後述するオウム返しするコードだけコピペしてからHerokuにデプロイやアプリ作成をしたほうが無難です．
私はコードができる前にいろいろといじったた何回デプロイしても動かなくなってしまいました．(たぶん余計なファイルもデプロイしてしまった．)

#オウム返ししよう
では実際に書いてみましょう．とはいっても僕もすべて理解しているわけではありません．というかほとんど理解してないです．

## 必要なライブラリのインストール
このプログラムでは以下のライブラリが必要になります．pipでインストールしましょう．

```
pip install flask
pip install line-bot-sdk
```

## main.pyをつくろう

```python:main.py
from flask import Flask, request, abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)
import os

app = Flask(__name__)

#herokuの環境変数に設定された、LINE DevelopersのアクセストークンとChannelSecretを
#取得するコード
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

@app.route("/callback", methods=['POST'])
def callback():
    # リクエストヘッダーから署名検証のための値を取得
    signature = request.headers['X-Line-Signature']

    # リクエストボディを取得
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # 署名を検証し、問題なければhandleに定義されている関数を呼ぶ
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'


#以下でWebhookから送られてきたイベントをどのように処理するかを記述する
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))

# ポート番号の設定
if __name__ == "__main__":
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
```

## Herokuの設定
ここからはHerokuというサービスを利用するためのあれこれをします．
まずはHeroku CLIをDLしましょう．
[Heroku](https://dashboard.heroku.com/)
ここでアカウント作成もしておきます．

さらにgitも必要です
[git-download](https://git-scm.com/downloads)
それぞれのインストールはそのまま進めばオッケーです

ここまで長かったですが次からHerokuでの作業です

## Herokuでの作業
Herokuは簡単に言うとサーバーみたいな感じです．情報を受け取ったり送信したりしてくれます．そのために自分のアプリを開設したり、書いたコードを実装したりします．
まずはログインです．

```:gitbash
heroku login
```

ブラウザが開いてログインする？みたいな確認が出るのでそのまま進めましょう．出来たらgit bashに戻ります．

Herokuでのアプリを作成します．[app]はお好きに

```
heroku create [app]
```

初期化します(必ず開発してるディレクトリに移動してから行ってください)

```
git init
```

Herokuのアプリとgitを紐付けます

```
heroku git:remote -a [app]
```

herokuの環境変数にLINE Developperのトークンとapiを登録します

```
heroku config:set YOUR_CHANNEL_ACCESS_TOKEN="チャネルアクセストークンの文字列" -a [app]

heroku config:set YOUR_CHANNEL_SECRET="チャネルシークレットの文字列" -a [app]
```

herokuに登録できたか確認

```
heroku config
```

Herokuに自分のアプリを登録する際、必要になる ```Procfie```(拡張子は無し)```runtime.txt``` ```requirements.txt``` を作成

```:Procfile
web: python main.py
```

```:runtime.txt
python-3.8.0
```

requirements.txtはターミナルでこれを実行する．

```
pip freeze > requirements.txt
```

## Herokuへデプロイ
デプロイの流れです．
- gitの初期化
- 紐づけ
- ファイルのadd
- commit
- push

```
git init
heroku git:remote -a testlinebot0319
git add .
git commit -m'[コメント]'
git push heroku master
```

## LINE DeveloperへのWebhook登録
LINE DeveloperのサイトでWebhookの欄をこのように設定します．
```https://[app].herokuapp.com/callback```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112260/8bc3728e-e1d8-a36e-b72a-e89013bffb69.png)


## ちゃんと動く？
ここまで来たらデバッグです．作ったラインアカウントを追加して何か送ってみましょう．オウム返ししてくれたら成功です．

# オウム返しから進化しよう
オウム返しできましたかね？
ではここでオウム返し以外もしゃべらせましょう．オウム返しの部分は

```
line_bot_api.reply_message(event.reply_token,TextSendMessage(text=event.message.text))
```
というコードの

```
TextSendMessage(text=event.message.text)
```
のevent.message.textです．この変数に送信したテキストが格納されているんですね．

これが理解できればあとはこれを利用して作業するだけです．

# ApexLegendsの戦績をもってこよう
ここではApexTrackerのAPIを利用します．apiのキーを環境変数で持ってくるのでこうしましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112260/b3d0e0b9-cb6b-ccfa-9f2f-799744b20062.png)
さらに、urlで直接データを持ってくるのでそのurlを作成します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112260/d56d8f65-9773-6943-ef81-073ef09c2ad6.png)


このApexのデータを見るコードを貼っておきます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112260/d7531403-4f29-0076-d04b-facf3228d433.png)

```python:Track_Apex.py
import requests, json, os
from pprint import pprint

api = ""
url = "https://public-api.tracker.gg/v2/apex/standard/profile/psn/Hayaa6211"
header = {"TRN-Api-Key":api}

res = requests.get(url, headers=header).json()
pprint(res)
```
これはjsonでリターンされます．

# 情報を扱う
正直ApexのAPIは自分で遊ぶのが一番です．
先ほどのresはjsonなので慣れてる方はそのままです．

# コマンドの解釈
最初のほうで見せた通りこの公式ラインにコマンドを送ることにより返信してくれます．どうやってそのコマンドを理解してるんですかね
例えば今回は "! origin KNR_ShibuyaHal kill" というコマンドを例にしてみます(渋谷ハルさんごめんなさい)

それぞれの間は半角空白があります．

ということは```.split()```するとリストになるんですね．わーお
ここまで来たらこっちの勝ちです．

```python
text = [!,"origin","KNR_ShibuyaHal","kill"]
```
というリストになりました．あとはここからそれぞれの条件式を書いていけばよいわけです．(数学ヤクザ風)

ではそのコードです

```python:Track()
def Track(text):
    # テキストからリストに
    text = text.split()
    # 引数が足りないとき、エラーになるのでそのためのtry
    try:
        platform = text[1]
        user = text[2]
        what = text[3]
    except:
        return "そのコマンドおかしいで"

    user_url = f"{url}/{platform}/{user}"
    res = requests.get(user_url, headers=head).json()

    if what == "rank":
        res_result = res["data"]["segments"][0]["stats"]["rankScore"]["metadata"]["rankName"]
        return res_result
    elif what == "rankscore":
        res_result = res["data"]["segments"][0]["stats"]["rankScore"]["displayValue"]
        return res_result
    elif what == "id":
        res_result = res["data"]["platformInfo"]["platformUserId"]
        return res_result
    elif what == "level":
        res_result = res["data"]["segments"][0]["stats"]["level"]["displayValue"]
        return res_result
    elif what == "kill":
        res_result = res["data"]["segments"][0]["stats"]["kills"]["displayValue"]
        return res_result
    else:
        res_result = "そんなコマンドないんだよね"
        return res_result
```

ここは自分の能力不足をひしひしと感じるのですがifの無限は汚いですね．dictとかにしたいんですけど動かなくて絶賛なえてます．

# おわり
終わりです．Herokuにデプロイして終わらせましょう．最後にコード全部載せておきますね．

# コード全体

```python:main.py
import os
import sys
import json
import requests
from argparse import ArgumentParser

from flask import Flask, request, abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

app = Flask(__name__)

Tracker_api = os.getenv('Tracker_API',None)
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)


url = "https://public-api.tracker.gg/v2/apex/standard/profile"
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
head = {"TRN-Api-Key":Tracker_api}


@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'


@handler.add(MessageEvent, message=TextMessage)
def message_text(event):
    if event.message.text[:1] == "!":
        res_result = Track(event.message.text)
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text=res_result))
    elif event.message.text[:1] == "?":
        res_result = Neta(event.message.text)
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text=res_result))
    else:
        pass

def Track(text):
    text = text.split()
    try:
        platform = text[1]
        user = text[2]
        what = text[3]
    except:
        return "そのコマンドおかしいで"

    user_url = f"{url}/{platform}/{user}"
    res = requests.get(user_url, headers=head).json()
    #変更するところ

    if what == "rank":
        res_result = res["data"]["segments"][0]["stats"]["rankScore"]["metadata"]["rankName"]
    elif what == "rankscore":
        res_result = res["data"]["segments"][0]["stats"]["rankScore"]["displayValue"]
    elif what == "id":
        res_result = res["data"]["platformInfo"]["platformUserId"]
    elif what == "level":
        res_result = res["data"]["segments"][0]["stats"]["level"]["displayValue"]
    elif what == "kill":
        res_result = res["data"]["segments"][0]["stats"]["kills"]["displayValue"]
    else:
        res_result = "そんなコマンドないんだよね"
    return res_result
    
def Neta(text):
    text = text.split()
    what = text[1]

    if what == "help":
        res_result = "! [platform(psn or origin or xbl)] [playerName] [コマンド] です．\nコマンドは現在[rank],[rankScore],[id],[level],[kill],です"
    elif what == "fuck":
        res_result = "ごめんね by黒木ほの香"
    elif what == "ban":
        res_result = "ばいばーい"
    elif what == "ramen":
        res_result = "https://tabelog.com/tokyo/A1303/A130301/13069220/"
    return res_result



if __name__ == "__main__":
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
```

# GitHub
絶賛改良中で日々変わってるので新しいこととか追加したときはこっちのコードが最新です．
頑張ってQiitaも更新しますが忘れてる可能性大です．
[GitHub-Hayaa6211](https://github1s.com/Hayaa6211/ApexTracker_inLINE)

# あとがき
僕にとって二つ目の作品となったこれ．実力とセンスがなさ過ぎて何回見ても汚いし修正しにくいコードですね....うーん
これからはこのbotの修正、改良をしつつコーディング力の向上を目指したいと思います．
ここまでご視聴いただきありがとうございました．ノシノシ

# 参考にさせていただいた記事(というかまんまパクリ)
https://qiita.com/kaonashikun/items/8004dfc9deea6c25754b
https://qiita.com/hayapo/items/2ade5e149f98ec19afc1