Twitter API v2をAWSで動かし自動投稿するようにした件

TwitterのAPIをAWS Lambdaで動かす方法を紹介します。

Ishiguro Suguru

Twitter APIの仕様変更に伴う対応を行ったのでその時のメモです。

世間でも話題になりましたがTwitter APIが有料化になってしまい、 それにともない数多くの無料で使えていたTwitter関連サービスが廃止・有料化になりました…

私もIFTTTというサービスを使ってWebサイトの新規記事リンクをTwitter上に自動で投稿するように設定したのでしたが、 残念ながら有料化してしまいました。

ただしTwitter APIはすべて有料化してしまったわけではなく、 Freeプランだと投稿だけはできるようなのでAWSのLambdaでAPIを実行するように対応しました。

さくっとできるためご興味ある方はやってみてください。

対応概要

ゴール

  • AWS Lambda(今回はPythonで実装)でTwitter APIを呼び出しWebサイトの新規記事を投稿する
  • CloudWatchで定期的にLambdaを動かし、新規記事の更新状態をチェックする
  • 新規記事はWebサイトのRSS情報(xmlファイル)を読み取りS3に保存しておく

作業概要

  1. Twitter開発者用サイトでAPI利用設定 →今回は割愛。以下の参考サイトを確認ください。
  2. AWS SAMでLambda等の必要なリソース作成

参考サイト

1.Twitter開発者用サイトでAPI利用設定

詳細は割愛。上記サイトが参考になります。

アクセスキー等はメモして保存しておきましょう。

2.AWS SAMリソース設定(Lambda、CloudFormation)

  • AWS SAMでリソースは作成。
  • LambdaはDocker利用、実行コードはpython。

Lambda

srcディレクトリ配下に以下のファイルを置く。

  • app.py: メイン処理実行
  • Dockerfile: Docker定義
  • requirements.txt: 使用ライブラリ記述

app.py

import feedparser
import boto3
import os
import json
import tweepy


# AWS環境パラメータ
S3_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME')
CLIENT_AWS = boto3.client('s3')

# TwitterAPIパラメータ
CONSUMER_KEY = '{環境に合わせて変更してください}'
CONSUMER_SECRET = '{環境に合わせて変更してください}'
ACCESS_TOKEN = '{環境に合わせて変更してください}'
ACCESS_TOKEN_SECRET = '{環境に合わせて変更してください}'
CLIENT_TWITTER = tweepy.Client(
    consumer_key=CONSUMER_KEY,
    consumer_secret=CONSUMER_SECRET,
    access_token=ACCESS_TOKEN,
    access_token_secret=ACCESS_TOKEN_SECRET
)

# 記事一覧情報の一時保存先(S3)
POST_FILE = 'manga/posts.json'
# ツイート対象のWebサイトRSS情報
URL = "{環境に合わせて変更してください}"


def lambda_handler(event, context):
    # RSSの情報を取得
    feed = feedparser.parse(URL)
    # 過去の投稿情報を取得
    before = check_file(POST_FILE)
    # 現在の投稿保存先
    posts = []

    if before:
        # 取得した現在情報から記事の日付、タイトル、URLを取得
        for entry in feed.entries:
            # IDをURLから抽出
            post_id = int(entry.link.split("/")[-2])
            post = {
                'id': post_id,
                'title': entry.title,
                'url': entry.link,
                'published': entry.published,
            }

            # 前回実行時の投稿と比較して新規登録されているデータはツイート
            if not (post in before):
                msg = entry.title + "\n#まとめ #2ch #5ch #漫画" + "\n" + entry.link
                try:
                    CLIENT_TWITTER.create_tweet(text=msg)
                except Exception as e:
                    print('[error] ', e)

            posts.append(post)

        # 今回参照した記事一覧情報を保存
        CLIENT_AWS.put_object(Body=json.dumps(posts), Bucket=S3_BUCKET_NAME, Key=POST_FILE)

    else:
        print("Failed to get POST_FILE.")


def check_file(file_name):
    """登録先のファイルの有無をチェック
    :rtype: object
    """
    try:
        response = CLIENT_AWS.get_object(Bucket=S3_BUCKET_NAME, Key=file_name)
        check_result = json.loads(response['Body'].read())
    except:
        check_result = False

    return check_result

requirements.txt

feedparser
boto3
tweepy

SAM template.yaml

以下のコードでリソースを作成。 PyCharmでデプロイ。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  python3.9

  Lambda and EventBridge SAM for tweetForManga

Globals:
  Function:
    Timeout: 3
    Environment:
      Variables:
        TZ: Asia/Tokyo
        S3_BUCKET_NAME: {環境に合わせて変更してください}

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: TweetForMangaLambdaRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: "/"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonS3FullAccess

  LambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: TweetForMangaLambda
      Role: !GetAtt LambdaRole.Arn
      Timeout: 30
      MemorySize: 128
      PackageType: Image
      Architectures:
        - x86_64
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./src
      DockerTag: python3.9-v1

  Rule:
    Type: AWS::Events::Rule
    Properties:
      Description: Scheduled Rule AM7 ~ AM0 every 15,45min
      Name: TweetForMangaLambdaRule
      # AM7 ~ AM0 15,45min
      ScheduleExpression: 'cron(15,45 22-15 * * ? *)'
      State: ENABLED
      Targets:
        - Arn: !GetAtt LambdaFunction.Arn
          Id: lambda

  FunctionPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt Rule.Arn

Outputs:
  LambdaFunction:
    Description: "Lambda Function ARN"
    Value: !GetAtt LambdaFunction.Arn
  LambdaRole:
    Description: "Implicit IAM Role created for function"
    Value: !GetAtt LambdaRole.Arn

最後に

Twitter APIは無料枠だと1,500件/月ツイートできるみたいです。

comments powered by Disqus