Twitter APIを使って自動でTweetする（６）

今までの内容は以下の通り

- [Twitter APIを使って自動でTweetする（１）](https://qiita.com/jnakanaka1/items/bf4f450bd9dd3bd5005f)<br>&nbsp;&nbsp;&nbsp;&nbsp;Request Tokenの取得
- [Twitter APIを使って自動でTweetする（２）](https://qiita.com/jnakanaka1/items/3b8fbedb14e1a8dcf406)<br>&nbsp;&nbsp;&nbsp;&nbsp;
AWSのAPI GatewayとLambdaへのデプロイ
- [Twitter APIを使って自動でTweetする（３）](https://qiita.com/jnakanaka1/items/94c342e6e2a46f4ae64c)<br>&nbsp;&nbsp;&nbsp;&nbsp;
Vueを使ったTwitterの認証・認可の取得
- [Twitter APIを使って自動でTweetする（４）](https://qiita.com/jnakanaka1/items/94c342e6e2a46f4ae64c)<br>&nbsp;&nbsp;&nbsp;&nbsp;
AWS S3のホスティングでコンテンツの公開
- [Twitter APIを使って自動でTweetする（５）](https://qiita.com/jnakanaka1/items/01bb63df167ba4cd7390)<br>&nbsp;&nbsp;&nbsp;&nbsp;
Access Tokenの取得

今回は、取得したAccess Tokenを使って定期的にTweetする仕組みを作る。まず、Access TokenをDynamoDBに保存する。そして、CloudWatch Eventを使って定期的にLambdaを実行して、DynamoDBからAccess Tokenを取得してTweetする。

---

##**DynamoDB Tableの作成**

[DynamoDBコンソール](https://console.aws.amazon.com/dynamodb/)にアクセスし、テーブルを作成する。テーブル名は「token-table」、プライマリーキーは「id」として、「作成」をクリックする。

<img width="674" alt="dynamodb-1.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/6d056d01-3035-846c-a646-d995609b2e22.png">


##**DynamoDBアクセス用Lambda Layerの作成**

DynamoDBにtokenを保存したり、保存したtokenを取得するために、DynamoDBにアクセスする関数を作成し、Layerに登録する。Layerに登録することで複数のLambda関数からLayerを使ってDynamoDBにアクセスすることができる。以下はDynamoDBへアクセスするためのコード。Layerの使い方については[「Twitter APIを使って自動でTweetする（５）」](https://qiita.com/jnakanaka1/items/01bb63df167ba4cd7390)を参照してほしい。

```python:dynamodb.py
import boto3

class TokenTable:
    parition_name = 'id'
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('token-table')
    
    @classmethod
    def register(cls, data):
        cls.table.put_item(Item=data)

    @classmethod
    def get_item(cls, id):
        response = cls.table.get_item(
            Key={
                'id': id
            }
        )

        return response.get('Item')
```

また、LambdaからDynamoDBにアクセスするためにLambdaに権限を追加する必要がある。
DynamoDBにアクセスする必要のあるget_access_token関数を開き、「IAMコンソールでget_access_token-xxxxロールを表示します。」をクリックする。
<img width="863" alt="dynamodb-2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/f697a484-6f69-e0cc-021f-ad74f8d6ec47.png">
「インラインポリシーの追加」をクリックする。
<img width="855" alt="IAM-1.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/99c54632-0cab-980a-4505-f69905fb6a41.png">
「JSON」タブをクリックする。
<img width="612" alt="IAM-2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/a7748e50-d2ff-58a6-0b87-9d2d602c8dd6.png">
インラインポリシーをJSON形式で入力する。
<img width="619" alt="IAM-3.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/b704bd08-6082-9110-04e7-e097e90224e7.png">

ポリシーは以下のように入力し、「ポリシーの確認」をクリックする。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "dynamodb:*",
            "Resource": "arn:aws:dynamodb:*:*:table/token-table"
        }
    ]
}
```
ポリシー名を入力し、「ポリシーの作成」をクリックする。
<img width="621" alt="IAM-4.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/71e0d8de-542c-5018-99fc-5a8f39828fc6.png">
次に、get_access_token関数を以下のように修正し、取得したAccess TokenをDynamoDBのtoken-tableに保存するように変更する。

```python:get_access_token.py
import os
import urllib.parse
import requests
import utilities
from dynamodb import TokenTable

REQUEST_URL = 'https://api.twitter.com/oauth/access_token'
REQUEST_METHOD = 'POST'
API_KEY = os.environ.get('API_KEY')
API_SECRET = os.environ.get('API_SECRET')
TOKEN_SECRET = ''
SIGNATURE_METHOD = 'HMAC-SHA1'

class GetAccessToken:

    def __init__(self, event, context):
        self.oauth_token = event.get('queryStringParameters').get('oauth_token')
        self.oauth_verifier = event.get('queryStringParameters').get('oauth_verifier')

    def _create_authorization_params(self):
        authorization_params = {}
        authorization_params['oauth_timestamp'] = utilities.create_timestamp()
        authorization_params['oauth_consumer_key'] = urllib.parse.quote(API_KEY, safe='')
        authorization_params['oauth_nonce'] = utilities.create_nonce()
        authorization_params['oauth_signature_method'] = SIGNATURE_METHOD
        authorization_params['oauth_version'] = '1.0'
        authorization_params['oauth_token'] = self.oauth_token
        authorization_params['oauth_verifier'] = self.oauth_verifier
        authorization_params['oauth_signature'] = utilities.create_signature(utilities.create_base_string(authorization_params), REQUEST_METHOD, REQUEST_URL, API_SECRET, TOKEN_SECRET)

        return authorization_params

    def _create_authorization_header(self):
        authorization_header_val = 'OAuth '
        params = self._create_authorization_params()
        authorization_header_val += utilities.convert_dict(params)

        return {'Authorization': authorization_header_val}

    def _parse_response(self, response):
        response_dict = {
            (response.text.split('&')[0]).split('=')[0]: (response.text.split('&')[0]).split('=')[1],
            (response.text.split('&')[1]).split('=')[0]: (response.text.split('&')[1]).split('=')[1],
            (response.text.split('&')[2]).split('=')[0]: (response.text.split('&')[2]).split('=')[1],
            (response.text.split('&')[3]).split('=')[0]: (response.text.split('&')[3]).split('=')[1]
        }
        data = {
            'id': response_dict['user_id'],
            'token': response_dict['oauth_token'],
            'secret': response_dict['oauth_token_secret'],
            'name': response_dict['screen_name']
        }
        return data

    def _register_id(self, data):
        token_table = TokenTable()
        token_table.register(data)

    def _execute(self):
        headers = self._create_authorization_header()
        response = requests.post(REQUEST_URL, headers=headers)
        data = self._parse_response(response)
        self._register_id(data)

def lambda_handler(event, context):

    get_access_token = GetAccessToken(event, context)
    get_access_token._execute()

    return {
        'statusCode': 200,
        'headers': {'Access-Control-Allow-Origin': '*'},
        'body': 'Registration Success!'
    }

```

これで、Access TokenをDynamoDBに保存されるようになった。あとは、このAccess Tokenを使ってTweetする関数を作成し、CloudWatch Eventを使って定期的にそのLambdaを実行すれば自動でTweetすることができる。

Tweetする関数のコードは以下のように作成した。環境変数、ロール、レイヤーはget_access_tokenの関数と同様に設定している。
また、token-tableにアクセスする際のパーティションキーであるidの値を今回は決め打ちで設定している。この値は自身のtoken-tableを確認し、保存されているidに書き換えて使う必要がある。

```python:auto_tweet.py
import os
import urllib.parse
import requests
import utilities
from dynamodb import TokenTable

REQUEST_METHOD = 'POST'
API_KEY = os.environ.get('API_KEY')
API_SECRET = os.environ.get('API_SECRET')
SIGNATURE_METHOD = 'HMAC-SHA1'

TWEET_URL = 'https://api.twitter.com/1.1/statuses/update.json'

class AutoTweet:
    
    def __init__(self, event, context):
        pass
    
    def get_token(self, id):
        token_table = TokenTable()
        res = TokenTable.get_item(id)
        return {'token': res.get('token'), 'secret': res.get('secret')}
        
    def _create_authorization_params_for_tweet(self, token, token_secret, message):
        signature_params = {}
        signature_params['status'] = urllib.parse.quote(message, safe='')
        
        authorization_params = {}
        authorization_params['status'] = urllib.parse.quote(message, safe='')
        authorization_params['oauth_timestamp'] = utilities.create_timestamp()
        authorization_params['oauth_consumer_key'] = urllib.parse.quote(API_KEY, safe='')
        authorization_params['oauth_nonce'] = utilities.create_nonce()
        authorization_params['oauth_version'] = '1.0'
        authorization_params['oauth_signature_method'] = SIGNATURE_METHOD
        authorization_params['oauth_token'] = token
        
        signature_params.update(authorization_params)
        
        authorization_params['oauth_signature'] = utilities.create_signature(utilities.create_base_string(signature_params), REQUEST_METHOD, TWEET_URL, API_SECRET, token_secret)

        return authorization_params
    
    def _create_authorization_header_for_tweet(self, token, token_secret, message):
        authorization_header_val = 'OAuth '
        params = self._create_authorization_params_for_tweet(token, token_secret, message)
        authorization_header_val += utilities.convert_dict(params)
        
        return {'Authorization': authorization_header_val}
        
    def _tweet(self, token, token_secret, message):
        ENDPOINT = TWEET_URL + '?status=' + urllib.parse.quote(message, safe='')
        headers = self._create_authorization_header_for_tweet(token, token_secret, message)
        return requests.post(ENDPOINT, headers=headers)

    def _execute(self):
        id = '1132669739304513536'
        token_info = self.get_token(id)
        
        return self._tweet(token_info['token'], token_info['secret'], 'Hello,  this is auto tweet test:' + utilities.create_timestamp())

def lambda_handler(event, context):
    
    auto_tweet = AutoTweet(event, context)
    try:
        auto_tweet._execute()

    except Exception as e:

        raise e

    return {
        'statusCode': 200,
        'headers': {'Access-Control-Allow-Origin': '*'},
        'body': 'success!'
    }
```

最後に、CloudWatch Eventの設定をする。
まず、[CloudWatchコンソール](https://console.aws.amazon.com/cloudwatch/)にアクセスし、イベントのルールからルール作成をクリックする。
<img width="948" alt="CloudWatch-1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/1a383ca1-3f61-f4e3-a247-ebd3222a94b3.png">
イベントソースでスケジュールを選択し、適当な値を設定してターゲットで作成したLambda関数を選択して、「設定の詳細」をクリックする。
<img width="941" alt="CloudWatch-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/5a95eff9-90b1-b19b-c2e6-9c2da1f4abb8.png">
名前を入力し、状態を「有効」にして「ルールの作成」をクリックする。
<img width="946" alt="CloudWatch-3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407848/e8df5392-0c01-b88a-8d74-fb5a090b12f9.png">

これで、「[Twitter APIを使って自動でTweetする(3)](https://qiita.com/jnakanaka1/items/94c342e6e2a46f4ae64c)」で作成したVueアプリからログインをすると、そのログインしたTwitterのアカウント（実際は、上記で指定したidに紐づくアカウント）で定期的にTweetされるようになる。

次回はフロントエンドを追加して、汎用的に使えるようにする予定。

前回までの投稿は以下
[Twitter APIを使って自動でTweetする(1)](https://qiita.com/jnakanaka1/items/bf4f450bd9dd3bd5005f)
[Twitter APIを使って自動でTweetする(2)](https://qiita.com/jnakanaka1/items/3b8fbedb14e1a8dcf406)
[Twitter APIを使って自動でTweetする(3)](https://qiita.com/jnakanaka1/items/94c342e6e2a46f4ae64c)
[Twitter APIを使って自動でTweetする(4)](https://qiita.com/jnakanaka1/items/6c18aef2a64b0b20fd67)
[Twitter APIを使って自動でTweetする(5)](https://qiita.com/jnakanaka1/items/01bb63df167ba4cd7390)