Amazon LambdaでTwitterの自動リプライ

# やりたいこと
Lambdaで定期的に特定のハッシュタグが含まれるTweetを取得して、それに対して自動で画像付きリプライを送る。

# 実装方法
- CloudWatchのイベント機能で、n時間に一回Lambdaの関数を実行させる
- Lambdaで、
    - S3からツイートに添付する画像ファイルを取得
    - n時間分の特定のつぶやき(例: `#W杯` を含むツイート)を取得
    - 取得した特定のつぶやきに対して画像付きでリプライを送る

Lambdaで実装する理由としては、

- 1日に数回しか実行しないスクリプトの為に常に稼働しているサーバを構築、管理するのはコストと時間がかかるのでやりたくない
    - コスト面では、Lambdaは月々100万リクエストまで無料
- 添付画像に使うファイルをS3にアップロードするだけでよく、管理が楽
    - AWSのアカウントがあれば誰でも扱える

といったことが挙げられます。

# TwitterAPIを扱う為のPythonライブラリ Tweepy
今回はPythonのライブラリTweepyを使って実装します。
http://www.tweepy.org/

API Referenceは[こちら](http://tweepy.readthedocs.io/en/v3.5.0/api.html#id1)です。
TwitterのAPIが簡単に扱えるようになっています。
例えば検索APIは、

```python
api.search(q='"#W杯"', lang='ja', result_type='recent',count=10)
```

このようにすることで、`#W杯`を含む日本語のツイートを最新のものから10件取得してくれます。

# 環境
- Python 3.6.3
- tweepy 3.6.0

# 必要なライブラリ等の導入

```sh
# Tweepyのインストール
pip install tweepy
```

今回はApexを使ってLambda関数をデプロイします。
導入については以下の記事を参照してください。(自演)
https://qiita.com/shioiyan/items/9ef9d309b8c16fe6b427

# ファイル構成
今回のファイル構成は以下のようになります。

```
.
├── functions
│   └── twitter_reply
│       ├── function.json
│       ├── index.py
│       ├── requirements.txt
│       └── site-packages
└── project.json
```

# 実装

```json:function.json
{
  "description": "twitter reply method",
  "runtime": "python3.6",
  "handler": "index.lambda_handler",
  "environment":{
    "PYTHONPATH": "/var/runtime:/var/task/site-packages",
    "CK": "xxxxx(Consumer Key)",
    "CS": "xxxxx(Consumer Secret)",
    "AT": "xxxxx(Access Token)",
    "AS": "xxxxx(Access Token Secret)",
    "KEYWORD": "#W杯",
    "IMAGE_NAME": "football.png",
    "BUCKET_NAME": "xxxxx(S3のbucket名)",
    "TEXT": "ニッポンチャチャチャ"
  }
}
```

TwitterアプリケーションのConsumer Key等は[こちら](https://apps.twitter.com/)でアプリケーション登録を行うことで取得することができます。
`KEYWORD`にはリプライを送りたいハッシュタグを入れておきます。
`IMAGE_NAME`リプライで添付する画像(S3の`BUCKET_NAME`配下に置いておく)の名前です。
`TEXT`はリプライで画像と共に送る文章です。

```python:index.py
# coding: UTF-8
import tweepy
from tweepy import TweepError
import os
import boto3

# Twitterの認証周りのkey
CK = os.environ['CK']
CS = os.environ['CS']
AT = os.environ['AT']
AS = os.environ['AS']

# Lambdaで実行される関数
def lambda_handler(event, context):
    # Twitterの認証
    auth = tweepy.OAuthHandler(CK, CS)
    auth.set_access_token(AT, AS)
    api = tweepy.API(auth)

    # s3から画像ファイルを取ってきてローカルに保存
    s3          = boto3.resource('s3')
    bucket_name = os.environ['BUCKET_NAME']
    filename    = os.environ['IMAGE_NAME']
    file_path   = '/tmp/' + 'image.png'
    bucket      = s3.Bucket(bucket_name)
    bucket.download_file(filename, file_path)

    # キーワードに当てはまるつぶやきをしている人に画像付きでリプライ
    keyword = os.environ['KEYWORD']
    text    = os.environ['TEXT']
    for status in api.search(q=keyword, result_type='recent',count=1):
        print(status._json)
        statusId  = status.id
        name      = status.author.name
        replyText = name + ", " + text
        try:
            # reply
            api.update_with_media(file_path, status=replyText.encode("UTF-8"), in_reply_to_status_id=statusId)
        except TweepError:
            print("Debug::TweepError reply failed...")
```


```text:requirements.txt
tweepy==3.6.0
```
今回使うインストールしておく必要のあるライブラリはtweepyだけです。

これをApexでデプロイしましょう。

```sh
$ apex deploy twitter_reply --profile lambda-user --region ap-northeast-1
```

# 実行例
```sh
$ apex invoke twitter_reply --profile lambda-user --region ap-northeast-1
```
実行することで`KEYWORD`で設定したハッシュタグの含むツイート一件に対してリプライを画像付きで送ることができます。

また、定期的に実行するには、[CloudWatchのイベント](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/RunLambdaSchedule.html)を使うことで実現できます。

# 注意点
- 無差別にリプライを送ることはやめましょう。
- 鍵垢(Tweet非公開)のTweetは`api.search()`で取得できません。
  - Tweetを公開に設定を変えても、鍵垢の時にTweetしたものは取得できないです。(自分が試した限りではそうでした)
- Twitter APIは[時間あたりのリクエスト制限](http://westplain.sakuraweb.com/translate/twitter/Documentation/REST-APIs/Public-API/API-Rate-Limits.cgi)があるのであらかじめ確認しておいた方が良いです。

# まとめ
Amazon Lambdaを使ってTwitterの特定のハッシュタグを含むツイートに画像付きでリプライを送ることができました。
鍵垢に対してリプライを送れないのが不便で、デバッグが大変だと感じました。
たくさんのツイートにリプライを送るときにどれくらいの実行時間が必要なのかはLambdaを使う上で確認しておく必要がありそうです。