3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS LambdaとServerlessAdvent Calendar 2020

Day 11

クラロワAPIをlambdaから叩いてLINEに送る

Last updated at Posted at 2020-12-10

この記事は、AWS LambdaとServerless Advent Calendar 2020の11日目の記事です
昨日の記事は_kenshさんによる「AWS Lambda - Provisioned Concurrencyのベストプラクティス」でした

こんにちは、meiheiです

Clash Royaleというゲームでクランの運営をしています
このゲームではAPI(Clash Royale API)が提供されており、ゲーム内のデータを取得することが出来ます

このAPIを使って、1週間以上の放置しているクランメンバーを取得し、LINEにアラートを送るシステムを作っていきます

概要

完成したもの

スクリーンショット 2020-12-11 1.15.44.png
こんな感じに、プレイヤー名放置している期間がLINEに届きます

構成

今回作っていくシステムの構成は以下の通りです

aws-and-cr.png

クラロワAPIはIPアドレスを指定する必要があるので、NAT GatewayにEIPを割り当てています

  1. VPCの設定
  2. Clash Royale APIのKeyの作成
  3. Line Notifyの設定
  4. Lambda関数の作成・デプロイ(Serverless Framework)

の順で説明していきます

VPCの設定

以下の記事を参考(というか全く同じ)に設定していきます

まず、VPCを作成します
適当な名前タグを設定して、IPv4 CIDR ブロック10.0.0.0/16にします
スクリーンショット 2020-12-10 23.00.42.png
次に適当な名前のインターネットゲートウェイを作成して、先程作ったVPCにアタッチします
スクリーンショット 2020-12-10 23.07.51.png
スクリーンショット 2020-12-10 23.08.42.png
そしてVPCの中に、publicとprivateの2つのサブネットを作成します
「サブネットを作成」からVPCにvpc-lambdaを選択
スクリーンショット 2020-12-10 23.25.18.png
適当な名前のサブネット名を設定して、IPv4 CIDR ブロック10.0.10.0/24にします
同様にprivateのサブネットも設定します。IPv4 CIDR ブロック10.0.1.0/24にします
スクリーンショット 2020-12-10 23.26.54.png
次にVPC作成時に作られたルートテーブルを選択して、送信先に0.0.0.0/0、ターゲットに先ほど作成したインターネットゲートウェイを設定します
スクリーンショット 2020-12-10 23.35.56.png

次に、IPアドレスを固定するNAT Gateway&EIPを作成します(VPCの設定はあとちょっとです!)
適当な名前を入力し、サブネットに先程作ったpublicの方のサブネットを選択します。そしてElastic IP 割り当てというボタンを押してEIPを生成し、NAT Gatewayを作成します。
スクリーンショット 2020-12-10 23.41.13.png

最後に、VPCの新規ルートテーブルを作成し、送信先0.0.0.0/0、ターゲットに先ほど作成したNAT Gatewayを設定します
サブネット関連付けの編集からprivateのサブネットを関連付けで完了です
スクリーンショット 2020-12-10 23.47.58.png
スクリーンショット 2020-12-10 23.48.32.png
スクリーンショット 2020-12-10 23.51.03.png

これで、Elastic IPの割り当てられた IPv4 アドレスに表示されているIPアドレスが、LambdaからAPIを叩く時に使われる固定IPアドレスになります
スクリーンショット 2020-12-10 23.52.45.png

クラロワAPIのKeyの作成

前提として、クラロワのdeveloperアカウントを作成してあるとします。

developer.clashroyale.com の右上の名前のところからMy Accountを選択し、Create New Keyから新しい鍵を作成します

KEY NAMEにわかりやすい名前、DESCRIPTIONにわかりやすい説明文、ALLOWED IP ADDRESSに先程作成したIPアドレスを入力し、Create Keyで作成します。
スクリーンショット 2020-12-11 0.19.40.png

するとKEY NAMEで設定した名前の鍵が生成されています
スクリーンショット 2020-12-11 0.19.55.png

こちらのTOKENを使います

LINE Notifyの設定

LINE Notify はWEBサービスから簡単にLINEに通知を送れるサービスです

まずログインしてもらって、右上の名前のところからマイページに移動し、下の方にあるアクセストークンの発行(開発者向け)からトークンを発行するを選択します

適当なトークン名を記入し、1:1でLINE Notifyから通知を受け取るを選択してトークンを発行します
スクリーンショット 2020-12-11 0.15.09.png
発行したトークンが表示されるので、こちらをコピーして保存します(トークンが表示されるのはコレきりなので、コピーを忘れたら再発行しましょう)
スクリーンショット 2020-12-11 0.15.19.png

Lambda関数の作成・デプロイ(Serverless Framework)

前提として、Serverless Frameworkが使える環境にします。以下の記事がおすすめです

新規のサービスを作成します

$ serverless create --template aws-python3 -p cr-abandon-to-line

次に、必要なプラグインを入れます

$ npm install --save serverless-python-requirements
$ npm install -D serverless-dotenv-plugin

serverless-python-requirementspip installした外部モジュールを取り込みます(参考
serverless-dotenv-pluginserverless.ymlに直接書きたくない環境変数を設定ファイルからいい感じ取り出してくれます(参考

プロジェクトディレクトリ下に.envファイルを作成し、先程発行したトークンなどを入れておきます

CR_ACCESS_KEY=[クラロワAPIのトークン]
LINE_NOTIFY_ACCESS_TOKEN=[LINE Notify用に発行したトークン]
VPC_SECURITY_GROUP_IDS=[VPCのセキュリティグループのID]
VPC_SUBNET_IDS=[private-subnetのid]

LambdaがVPCのprivateサブネットに入るようにserverless.ymlに記述します(参考

serverless.yml
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ec2:CreateNetworkInterface"
        - "ec2:DescribeNetworkInterfaces"
        - "ec2:DeleteNetworkInterface"
      Resource:
        - "*"
  vpc:
    securityGroupIds:
      ${env:VPC_SECURITY_GROUP_IDS}
    subnetIds:
      ${env:VPC_SUBNET_IDS}

また、Pythonから環境変数として呼び出しを行うので、その設定もserverless.ymlに記述します

serverless.yml
  environment:
    CR_ACCESS_KEY: ${env:CR_ACCESS_KEY}
    LINE_NOTIFY_ACCESS_TOKEN: ${env:LINE_NOTIFY_ACCESS_TOKEN}

そして、クラロワAPIを叩いて、データを整形し、LINE Notifyに送るように実装します。

handler.py
import json
import os
import requests
from datetime import datetime, timedelta
from typing import Dict, List


CR_BASE_URL = 'https://api.clashroyale.com/v1'
LINE_NOTIFY_URL = "https://notify-api.line.me/api/notify"
CR_ACCESS_KEY = os.environ['CR_ACCESS_KEY']
LINE_NOTIFY_ACCESS_TOKEN = os.environ['LINE_NOTIFY_ACCESS_TOKEN']


def init_headers(api_key: str) -> Dict[str, str]:
    """初期化されたヘッダー情報の辞書を返す"""
    return {'authorization': f'Bearer {api_key}'}

def get_member(clan_tag: str) -> Dict[str, str]:
    """クラロワAPIからclan_tagのメンバー情報をGETする"""
    clan_tag = clan_tag.replace('#', '%23')
    url = CR_BASE_URL + f'/clans/{clan_tag}/members'
    headers = init_headers(CR_ACCESS_KEY)

    res = requests.get(url, headers=headers)

    return res.json()

def last_seen_to_datetime(last_seen: str) -> datetime:
    """last_seenの記法からdatetime型に変換する"""
    return datetime.strptime(last_seen, '%Y%m%dT%H%M%S.000Z')

def filter_by_last_seen(items: List[dict], dead_line: datetime) -> List[dict]:
    """最終ログインがdead_lineのクラメンの情報を抽出する"""
    before_last_seen = lambda i: last_seen_to_datetime(i['lastSeen']) < dead_line

    return [item for item in items if before_last_seen(item)]

def generate_message(cr_items: List[dict]) -> str:
    """line notify送信用のメッセージを作成する"""
    message = ''
    for member in cr_items:
        last_seen_diff = str(datetime.now() - last_seen_to_datetime(member['lastSeen'])).split('.')[0]
        message += f'\n{member["name"]}: {last_seen_diff}'

    return message

def send_line(message: str):
    """LINE Notifyにmessageを送る"""
    headers = init_headers(LINE_NOTIFY_ACCESS_TOKEN)
    data = {'message': message}

    requests.post(LINE_NOTIFY_URL, data=data, headers=headers)

def lambda_function(event, context) -> Dict[str, str]:
    """実行用の関数"""
    data = get_member('#228UCY92')
    dead_line = datetime.now() - timedelta(days=1)
    filtered_data = filter_by_last_seen(data['items'], dead_line)

    if (filtered_data):
        message = generate_message(filtered_data)
        send_line(message)

    return f'ok: {len(filtered_data)}件取得しました'

最後に、hundler.pylambda_functionが定期的に実行されるようにserverless.ymlに記述します(参考

serverless.yml
functions:
  abandon-member-to-line-notify:
    handler: handler.lambda_function
    events:
      - schedule: cron(0 0 * * ? *)  # 日本時間で朝の9時

AWSにデプロイしたら、このシステムの完成です

$ serverless deploy

終わりに

何でも楽にやってくれるLambdaさんだと思っていたら、IPアドレスが固定できないため、なかなか面倒くさい構成になりました

気になるお値段は11日ぐらい使って1,500円ぐらい。月換算だと4,000円ぐらいです(高い)
スクリーンショット 2020-12-11 1.46.32.png
NAT Gatewayが動いているだけでお金がかかるので、こんなにも高くついているようです

AWSの勉強目的だといいですけど、クラロワAPI+Lambdaは普通にオススメできないですね。

ソースコード

今回のソースコードはすべてGitHubにあげてあります。READMEは書いてません…

クランメンバー募集

僕の運営しているクラン「ことりカフェ」ではメンバーを募集しています

ほぼ毎日クラロワやってるけど、そこまでガチ勢じゃない、まったりプレイヤーが集うクランです
興味ある方はぜひTwitterにてご連絡ください!


明日は22tさんによる「chaliceで簡単pythonボット制作」です。お楽しみに!

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?