LoginSignup
3
2

More than 3 years have passed since last update.

無視されたリプライをPython+AWS Lambdaで毎日自動削除する

Last updated at Posted at 2020-04-11

メンヘラすぎて無視されたツイートは消したい

これは私ではないが、こういうツイートを消したい

  • 無視されるよりは無視したほうが良いという、被害者より加害者よりの思考へシフトし、陽キャよりのコミュニケーションにコミットを行う
    • これはセキュリティ・オートメーションに対してのコミュニティ・オートメーションであり、ある意味革命である
    • もちろん例にあげた公式垢にクソリプを送るのは対象外で、日常のTwitterコミュニケーションにおける無視されたツイートが対象である

削除対象はこれ

  • 無視されたツイート
    • リプライをしたが、リプライが返ってこないか、ふぁぼられていないツイート
    • 具体的にはこんなのの下のほうにある、ふぁぼられてないしリプライもされてないツイート

簡単な解説

  • ツイにはリプライされたフラグが存在しない
    Tweetが保持しているID.png

  • Tweet1がTweet2にリプライした場合、図の通りTweet2にはリプライされたというフラグが存在しない為、下記手順で抽出する必要がある

    • user_timelineからふぁぼ・RTされていないツイートを抽出
    • mentions_timelineから直近のリプライを取得し、in_reply_to_status_id(リプライされたツイート)の一覧を取得する
    • 2つを突合してリプライされていない、かわいそうなツイートを抽出する
    • かわいそうだが、それらを消す
  • API取得上限の問題

    • user_timelineは一度の取得上限が3200件
    • mentions_timelineは一度の取得上限が800件
      • そのままぶつけるとuser_timelineが圧倒的に多く、問題がある
      • user_timeline =< mentions_timelineにしたい
  • できるなら毎日ジョブで消したいが、面倒なのでサーバーは立てたくない

    • そこでAWS Lambdaなら簡単だし、無料

前提条件

  • TwitterのAPIキーを発行済み
    • 英作文はググればいくらでも出てくるから頑張る
  • AWSのアカウントを作成済み
    • AWS CLIを使えるようにしておく

インフラの構築

フォルダ構造

DeleteNoReactionTweet
|   lambda.yml
|   README.md
|
\---code
        DeleteNoReactionReply.py

パラメーターは外だしして再利用できるようにする

  • NoEchoをつけるとセキュアだが、Lambdaのコンソールで環境変数は見えてしまうので、あまり意味がない
  • SSMパラメータストアやKMSを使うとよりにセキュアにできるみたいだが、今回はそこまでしない
AWSTemplateFormatVersion: '2010-09-09'
# Twitter API Key
Parameters:
  ConsumerKey:
    Type: String
    NoEcho: true
  ConsumerSecret:
    Type: String
    NoEcho: true
  AccessKey:
    Type: String
    NoEcho: true
  AccessSecret:
    Type: String
    NoEcho: true

Lambda,IAM Role,Rule,Permissionの設定


Resources:
# Lambda
  Lambda:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code: code # カレントディレクトリ下のcodeフォルダを読み込む
      Environment: # Lambda環境変数
        Variables:
          ConsumerKeyVar: !Ref ConsumerKey
          ConsumerSecretVar: !Ref ConsumerSecret
          AccessKeyVar: !Ref AccessKey
          AccessSecretVar: !Ref AccessSecret
      Description: DeleteNoReactionReply
      FunctionName: DeleteNoReactionReply
      Handler: DeleteNoReactionReply.lambda_handler
      MemorySize: 128
      Role: !Sub 
        - arn:aws:iam::${AWS::AccountId}:role/${roleName}
        -  { roleName: !Ref LambdaRole }
      Runtime: python3.8
      Timeout: 180
  # CloudWatchEvents Rule
  Rule:
    Type: 'AWS::Events::Rule'
    Properties:
      Description: DeleteNoReactionReplyRule
      Name: DeleteNoReactionReplyRule
      ScheduleExpression: 'cron(0 18 * * ? *)' # 夜中三時に消す
      State: ENABLED
      Targets:
        - Arn: !GetAtt Lambda.Arn
          Id: lambda
  # Lambda IAM Role
  LambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: DeleteNoReactionReplyRole
      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
  # CloudWatchEvents Lambda excute enable
  LambdaEvent:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref Lambda
      Principal: 'events.amazonaws.com'
      SourceArn: !GetAtt Rule.Arn

Pythonで削除スクリプトを書く

import tweepy
import os

# API Key デプロイ時にCLIで指定する
consumer_key = os.environ['ConsumerKeyVar']
consumer_secret = os.environ['ConsumerSecretVar']
access_key = os.environ['AccessKeyVar']
access_secret = os.environ['AccessSecretVar']

# Tweepy Auth
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_key, access_secret)
api = tweepy.API(auth)

myReplyTweet = []
mentionInReplyTweet = []
noDeleteTweet = []
deleteTweet = []

# Get My Reply
print("==========Get My Reply==========")
for tweet in tweepy.Cursor(api.user_timeline,exclude_replies = False,include_rts = False).items(200): # あふれない程度に
    if "@" in tweet.text and tweet.favorite_count == 0 and tweet.retweet_count == 0:
        print(tweet.id,tweet.created_at,tweet.text.replace('\n',''),tweet.favorite_count,tweet.retweet_count)
        myReplyTweet.append(tweet.id)

# Get in_reply_to_status_id in mentions
print("==========Reply Tweet==========")
for mentions in tweepy.Cursor(api.mentions_timeline).items(400): # 800まで
    print(mentions.id,mentions.created_at,mentions.text.replace('\n',''))
    mentionInReplyTweet.append(mentions.in_reply_to_status_id)

# Extraction Delete Tweet
print("==========Extraction Delete tweet==========")
for mntnrptw in mentionInReplyTweet:
    for myrptw in myReplyTweet:
        if mntnrptw == myrptw:
            deleteTweet.append(myrptw)
preparateDeleteTweet = set(myReplyTweet) ^ set(deleteTweet)
print(list(preparateDeleteTweet))

# Delete Tweet
print("==========delete tweet==========")
for deltw in preparateDeleteTweet:
    print(api.get_status(deltw).id,api.get_status(deltw).created_at,api.get_status(deltw).text)
    api.destroy_status(deltw)

AWSへのデプロイ

  • Tweepyは外部モジュールなので同梱する
pip install tweepy --target ./code
  • パッケージにしてS3に転送
aws cloudformation package --s3-bucket $YourBucketName `
--template-file lambda.yml `
--output-template-file lambda-packaged.yml
  • インフラ構築とデプロイ
    • ここでTwitterのAPI KEYをLambdaの環境変数に指定する
aws cloudformation deploy `
--template-file lambda-packaged.yml `
--stack-name $YourStackName `
--parameter-overrides AccessSecret=$YourAccessSecret `
ConsumerKey=$YourConsumerKey ` 
ConsumerSecret=$YourConsumerSecret ` 
AccessKey=$YourAccessKey `
--capabilities CAPABILITY_NAMED_IAM
  • マネコンから確認する

GitHubにあげた完全版

張っておきます。
https://github.com/natsukawadaisuki/DeleteNoReactionReply

そもそも

  • フォロワーが無視しなければいい
3
2
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
2