メンヘラすぎて無視されたツイートは消したい
これは私ではないが、こういうツイートを消したい
自分がモテないのも、もしかして瀧さんのせいですか?
— ねっとのひと (@_tanosikuikouze) April 6, 2020
- 無視されるよりは無視したほうが良いという、被害者より加害者よりの思考へシフトし、陽キャよりのコミュニケーションにコミットを行う
- これはセキュリティ・オートメーションに対してのコミュニティ・オートメーションであり、ある意味革命である
- もちろん例にあげた公式垢にクソリプを送るのは対象外で、日常のTwitterコミュニケーションにおける無視されたツイートが対象である
 
削除対象はこれ
- 無視されたツイート
- リプライをしたが、リプライが返ってこないか、ふぁぼられていないツイート
- 具体的にはこんなのの下のほうにある、ふぁぼられてないしリプライもされてないツイート
 
簡単な解説
- 
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
そもそも
- フォロワーが無視しなければいい
