回心誌

日々是回心

【Python】イカリング2APIから取得した戦績JSONデータをstat.inkにアップロードする

【前提】

イカリング2から戦績データ(JSON)を取得済みである

イカリング2から戦績データを取得する方法は、以下を参照のこと。
[Python]イカリング2のJsonを取得してみた - Qiita


・mitmproxy経由でiksm_sessionを取得済みである
・stati.inkに登録済みで、APIキーを取得済みである

【結論から言うと】

まず、splatnet2statinkを拾って来ます。

git clone https://github.com/frozenpandaman/splatnet2statink


初回起動して、設定ファイルを作ります。
なお、python2系で動かしてください。

python splatnet2statink/splanet2statink.py

iksm_session、APIキー、言語を聞かれるので、答えます。


こんな感じのシェルスクリプトを作ります。

files="../jsonfiles/*.json"
for filepath in $files; do
    echo -e "1\n" | python splatnet2statink.py -i $filepath
done

files="../jsonfiles/*.json"のところには、イカリング2から拾って来た戦績JSONデータを格納するディレクトリを指定します。


作ったシェルスクリプトを動かします。


以下は、手を動かしていろいろやってたときのログ。



【いろいろやってみる】

ググって見たら、すでにそういうことをしてくれるスクリプトが作られているようなので、それを再利用することを考える。

GitHub - frozenpandaman/splatnet2statink: Takes battle data from the SplatNet 2 app and uploads it to stat.ink.

使い方はここに。
https://archive.fo/td52p

どうやら、イカリング2から戦績データを取得し、そのままstat.inkへのアップロードまでやってくれるようだ。
(ただし、iksm_sessionの設定はmitmproxyを使って手動でやる必要があるみたい)

ここではstat.inkへのアップロードができればそれでいいので、どの部分でその機能を実現しているかを見ていく。
どうやら、splanet2statink.pyの634行目からの関数post_battleで実装しているようだ。

引数はi, results, s_flag, t_flag, m_flag, sendgears, debug, ismonitor。
それぞれ、こんな感じ?
i:戦績データを配列で渡す際の添字
results:戦績データ(イカリング2から受け取ったJSONをパースしたもの)
s_flag:プレイヤーの情報(ニックネーム、内部ID)をアップロードするかどうか? Trueならアップロードする、Falseならしない。
t_flag:テストか否か? よくわからん。
m_flag:監視モードか否か。
sendgears:ギア情報を送付するか否か? ソースを見る限り、戦績でなくprofileから取得しているようだが、なぜなのかはよく分からん。
debug:デバッグモードかどうか。多分。
ismonitor:監視モード? m_flagとの違いはよくわからん。


普通に使う分には、こんな感じで良さそう。
i:0
results:各ゲームの結果1つを長さ1の配列に格納して渡せば良さそう
s_flag:とりあえずFalseで。
t_flag:False
m_flag:False
sendgears:False
debug:False
ismonitor:False


とりあえず、githubからソースを落としてくる

git clone https://github.com/frozenpandaman/splatnet2statink


次に、設定ファイルについて。
どうやら初回起動すれば、ある程度設定ファイルを自動で作成してくれるようだ。

ひとまず叩いて見る。

python splanet2statink.py

と、ここエラーが返ってくる。で、重大な課題に気づく。

  File "splatnet2statink/splatnet2statink.py", line 18
    print "splatnet2statink v" + A_VERSION
                             ^
SyntaxError: Missing parentheses in call to 'print'

このコード、python2系だ。
イカリング2からJSONデータを取得する手製プログラムなど、python3系を前提に作成してしまっている。うーんどうしよう…。面倒なことになった。

ひとまず、pyenvを使って2系インタプリタに切り換え。3系と混在してるのはあとでなんとかしないとな〜。

pyenv local 2.7.11

モジュールが入ってないよとエラーが。

Traceback (most recent call last):
  File "splatnet2statink.py", line 8, in <module>
    import msgpack
ImportError: No module named msgpack

msgpackはちらっと調べたところ、JSONをバイナリデータに変換したりするっぽい。
とりあえずpipで入れちゃう。

pip install msgpack

んで、もう一回。

python splanet2statink.py

するとAPIキーを聞かれるので、stat.inkのプロフィール設定ページから拾ってきたやつを入れる。

splatnet2statink v0.2.21
Generating new config file.
stat.ink API key: 

次に言語を聞かれるので、ja-JPと入力

Default locale is en-US. Press Enter to accept, or enter your own (see readme for list).
ja-JP

その次、iksm_sessionを聞かれる。これはmitmproxy経由で入手しておいたものを入れる。

Go to the page below to find instructions to obtain your iksm_session cookie:
https://github.com/frozenpandaman/splatnet2statink/wiki/mitmproxy-instructions
Enter it here:

とりあえず、これで設定ファイルが自動で作成されたはず。

vi config.text

で作成されていることを確認。


ところで、iksm_sessionは定期的に変更されるのだが、自作のスクリプトでiksm_sessionの取得を半自動化している。
mitmproxy経由のiksm_sessionの取得は(証明書の発行やmitmproxyのインストールなど、初回作業は除き)以下の手順が必要だ。

  1. スマホ側でプロキシ設定
  2. mitmproxyの起動
  3. スマホ側でイカリング2を開く
  4. mitmproxyでイカリング2のリクエストを選択し、cookieをコピーし、どこかに保存
  5. mitmproxyを終了
  6. スマホ側でプロキシ設定を戻す

このうち、mitmproxyからcookieを取得する作業を自動化している。したがって、手順は以下のように(若干だが)簡素化される。

  1. スマホ側でプロキシ設定
  2. スクリプト起動(mitmdumpを利用)
  3. スマホ側でイカリング2を開く
  4. (iksm_sessionが取得されたことを確認し)スクリプトを終了
  5. スマホ側でプロキシ設定を戻す。

ここで、cookieの設定をsplanet2statinkのconfig.txtに書き込みに行くプロセスが必要になる。
この点も課題だ。


さて、いくつかの課題は後回しにして、splanet2statink.pyのpost_battle関数を使ってみよう。
splanet2statinkをインポートして、順次アップロードしていく仕組みだ。

import splatnet2statink as uploader
import os
import json
import glob

jsonfiledir1 = "../jsonfiles"

def import_jsonfile(jsonpath):
    jsonfile = open(jsonpath, 'r')
    content = json.load(jsonfile)
    return content

def main(jsonfiledir):
    for name in glob.glob(os.path.join(jsonfiledir, 'result-battle-*.json')):
        if os.path.getsize(name)==0:
            print name, " is 0 Byte. Skip."
        else:
            print "uploading ", name
            json_res = [import_jsonfile(name)]
            s_flag = False
            t_flag = False
            m_flag = False
            sendgears = False
            debug = False
            uploader.post_battle(0, json_res, s_flag, t_flag, m_flag, sendgears, debug)

if __name__ == '__main__':
    main(jsonfiledir1)

ところが、エラーが発生した。

Traceback (most recent call last):
  File "upload_statink.py", line 29, in <module>
    main(jsonfiledir1)
  File "upload_statink.py", line 26, in main
    uploader.post_battle(0, json_res, s_flag, t_flag, m_flag, sendgears, debug)
  File "users/hoge/splatnet2statink/splatnet2statink.py", line 879, in post_battle
    if filename == None:
NameError: global name 'filename' is not defined

filenameを定義していないというエラー。
filenameの使用箇所を調べているうち、さらに残念なことに気づいてしまった。

どうも、ファイル実行時のオプションでjsonファイルの単体アップロード機能があるようなのだ。
使い方としては、以下のような感じ。

python splanet2statink.py -i jsonfiles/result-battle-2512.json

つまり、-iオプションの引数にファイル名を指定すると、それに応じてファイルをアップロードしてくれるようだ。


ということは、もうこれシェルスクリプトで作った方が速そう。

files="../jsonfiles/*.json"
for filepath in $files; do
    echo -e "1\n" | python splatnet2statink.py -i $filepath
done

こんなんでどうや。
できたっぽい。