Python
Python3
GoogleSpreadSheet
ClashRoyale

クラッシュ・ロワイヤル API を使って Google スプレッドシートへクラン対戦の戦績を記録する

はじめに

エンジニアの @Yasuhisa です。

私はスマホのゲームアプリでよく遊びます。

最近は主に Supercell 社の CLASH ROYALE(クラロワ)BRAWL STARS(ブロスタ) で遊んでいます。どちらもスマホゲームの手軽さがあり、ルールのシンプルさと競技性の高さを兼ね備えているため、飽きずに続いています。

この記事では CLASH ROYALE(クラロワ) の公開 API Clash Royale API からクラン対戦結果を取得して、Google スプレッドシートに戦績を記録する方法を紹介します。

注)この記事の執筆時点(2019/01/05)に動作確認をしたため、API のアップデート次第では動作しなくなる可能性があります。詳細は文末の API リファレンスを参考にしてください。

クラッシュ・ロワイヤルおよびクラン対戦について

クラッシュ・ロワイヤル(通称クラロワ)は 2016年 1月4日 に Supercell 社からリリースされ、2018年 4月25日 にクラン対戦が追加されました。2018年からはプロリーグも活発で、YouTuber による生放送なども頻繁に行われています。

クラロワ API をどう使うのか

本題の Clash Royale API の使い方については、クラッシュロワイアル(ClashRoyale)のAPIが解放されたらしいので試してみる(その①) - Qiita の記事でわかりやすく紹介されています。

今回のスクリプトを作ったきっかけは、クラン対戦の戦績の閲覧に不便さを感じたからです。クラン対戦の戦績はゲーム内の ソーシャル > クラン名 > 過去の対戦 の順にタップして見ることができます。しかしながら、直近の5戦しか記録が確認できない(クラロワ API で 5 以上の Limit を指定しても見れない)という問題があるため、私とクランメンバーの友人でスプレッドシートに戦績を記録するようになりました。

01.png

筆者は以前からクラロワ API が試してみたかったので、年末年始の暇な時間に自動化しようと考えていました。試してみた結果、クラロワ API は実行元の IP アドレスをあらかじめ登録しておく必要があったので、Google Apps Script の トリガー などで完結することができませんでした。そのため、Python スクリプトをローカル端末から実行する方法で現在は記録しています。

やりたいこと

手入力している Google スプレッドシートのクラン対戦戦績を Python スクリプトで一括登録します。

02.png

スプレッドシートへの煩わしい手入力やスマートフォン・タブレットでのクラン対戦戦績の照会数・記録ミスが減り、しっかりと記録しつつも遊べる時間が増えせます。

03.png

事前準備

後述の Python コードを実行するには以下の準備が必要です。

  • 集計対象スプレッドシートの作成
    • スプレッドシート ID
    • 集計・記録対象セルの作成
  • Clash Royale API キーの取得、およびグローバル変数 TOKEN へセット
  • Google Spreadsheet API の有効化
  • Google Spreadsheet API 認証情報を実行環境のルートディレクトリに配置
    • token.json
    • credentials.json

Python コード

Gist でも公開しています。

import requests
import json
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

TOKEN = '{Your Clash Royale API Key}' # クラロワ API キー
URL = 'https://api.clashroyale.com/v1'

SCOPES = 'https://www.googleapis.com/auth/spreadsheets' # read/write
SPREADSHEET_ID = '{Your Spreadsheet ID}' # スプレッドシートの ID
DEFAULT_RANGE = '{Your Spreadsheet target rect}' # セルの集計対象範囲(「:」前後の矩形選択)
LAST_LOGGED_CELL = '{Your Spreadsheet CreatedDate cell identity}' # 最終更新日時セル
VALUE_INPUT_OPTION = 'RAW' # RAW 固定

NAME_COL = 0 # スプレッドシート A 列
WIN_COL = 1 # スプレッドシート B 列
LOSE_COL = 2 # スプレッドシート C 列

sheet = None

def get_current_clan_battle_results():
    """クラロワ API クラン対戦データを取得

    実行するためには事前にクラロワ API の API キーを `TOKEN` にセットする必要があります。

    Returns:
        List -- クラン対戦結果

    See Also:
        My Keys -- https://developer.clashroyale.com/#/account
        GET /clans/{clanTag}/warlog -- https://developer.clashroyale.com/#/documentation
    """

    endpoint = URL + "/clans/{your_clan_tag(need '#' to encode URL string '%23'}/warlog"

    headers = {
        "content-type": "application/json; charset=utf-8",
        "cache-control": "max-age=60",
        "authorization": "Bearer %s" % TOKEN
    }

    response = requests.get(endpoint, headers=headers)
    results = response.json()

    return results

def get_spreadsheet():
    """Google スプレッドシートを取得

    実行するためには事前に実行環境のルートディレクトリに「token.json」、および「credentials.json」を配置する必要があります。

    Raises:
        Exception -- スプレッドシートが取得できない場合

    Returns:
        Spreadsheet -- https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets?hl=ja#Spreadsheet

    See Also:
        Python Quickstart -- https://developers.google.com/sheets/api/quickstart/python
    """

    store = file.Storage('token.json')
    creds = store.get()

    if not creds or creds.invalid:
        flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
        creds = tools.run_flow(flow, store)

    service = build('sheets', 'v4', http=creds.authorize(Http()))
    sheet = service.spreadsheets()

    if not sheet:
        raise Exception('スプレッドシートが正常に取得できませんでした。')

    return sheet

def get_sheetvalues_by_range(range):
    """スプレッドシートの指定した範囲を取得

    Parameters:
        range {string} -- セルの取得範囲。指定のセル番号(ex, A1)もしくは、「:」区切りのセル範囲(ex, A1:B2、矩形選択)を指定する。

    Raises:
        ValueError -- range の指定がない場合

    Returns:
        spreadsheets.values -- https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values?hl=ja
    """

    if not range:
        raise ValueError('範囲を設定してください')

    return sheet.values().get(spreadsheetId=SPREADSHEET_ID, range=range).execute()

def update_area(batch_update_values_request):
    """スプレッドシートの範囲を更新する

    Arguments:
        batch_update_values_request {BatchUpdateValuesRequest} -- https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/batchUpdate?hl=ja#request-body

    Raises:
        ValueError -- batch_update_values_request の指定がない場合

    See Also:
        POST spreadsheets.values.batchUpdate -- https://developers.google.com/sheets/guides/values?hl=ja#_8
    """

    if not batch_update_values_request:
        raise ValueError('batch_update_values_request を指定してください')

    body = {
        'valueInputOption': VALUE_INPUT_OPTION,
        'data': batch_update_values_request
    }

    sheet.values().batchUpdate(spreadsheetId=SPREADSHEET_ID, body=body).execute()

def update_battle_resutls():
    """
    クラロワ API のクラン対戦取得結果でスプレッドシートの最終更新日時より新しい記録があれば、スプレッドシートの戦績を更新する。
    """

    global sheet
    sheet = get_spreadsheet()

    # スプレッドシート情報を取得
    last_logged_cell = get_sheetvalues_by_range(range=LAST_LOGGED_CELL)
    # 最終記録時間を取得
    last_logged_date = last_logged_cell['values'][0][0]

    # クラン対戦結果を取得
    battle_results = get_current_clan_battle_results()
    items = battle_results['items']

    # クラン対戦結果を createdDate(対戦履歴)順に昇順ソート
    sorted_items = sorted(items, key=lambda items: items['createdDate'])

    for item in sorted_items:
        created_date = item['createdDate']

        # クラロワ API で取得した最終更新日時(createdDate)がスプレッドシートの記録以前のものであったら処理しない
        if created_date <= last_logged_date:
            continue

        # クラロワ API クラン対戦参加者情報
        participants = item['participants']
        # スプレッドシートセル情報
        values = get_sheetvalues_by_range(range=DEFAULT_RANGE)['values']

        for participant in participants:
            participant_name = participant['name']

            for value in values:
                cell_name = value[NAME_COL]

                # クラン対戦参加者名とスプレッドシートの名前列が一致するかどうか
                if participant_name == cell_name:
                    win_count = participant['wins']
                    # 対戦結果勝ち数を追加
                    value[WIN_COL] = str(int(value[WIN_COL]) + win_count)
                    # 対戦結果負け数を追加 (試合数 - 勝数)
                    value[LOSE_COL] = str(int(value[LOSE_COL]) + participant['battlesPlayed'] - win_count)

        # スプレッドシート API の `BatchUpdateValuesRequest` オブジェクト
        batch_update_values_request = [
            {
                'range': LAST_LOGGED_CELL,
                'values': [[created_date]]
            },
            {
                'range': DEFAULT_RANGE,
                'values': values
            },
        ]

        # LAST_LOGGED_CELL の最終更新日時(createdDate)、クラン対戦結果を更新
        update_area(batch_update_values_request)

if __name__ == '__main__':
    update_battle_resutls()

所属クランの宣伝

私の所属しているクランは少人数で、コアメンバーの高い勝率が特徴です。

クランのルールでは クラン対戦本戦でカードレベルが 11 以上(ウルトラレアは 10 以上)でなければ許可なく参加しない となっているため、対戦の参加人数が 10 人に満たないことが多々あります。

平均のカードレベルが 11 以上(理想は 12 以上)で、少数精鋭のクランに入ってみたいという方がいましたら、是非 ツワキノクラロワ部 へ加入申請をお願いします。

おわりに

クラロワのフレンド登録なども歓迎しますので、何か用があれば筆者の Twitter へ気軽に連絡をください。

またクラロワ API を使う機会があれば記事にしていきたいです。

参考にしたドキュメント・記事