[PYTHON] I made a LINE Bot that sends recommended images every day on time

Introduction

I, a fan of Mayu Matsuoka (commonly known as Mayura), I made a LINE Bot just for me, just for me.

After about 5 years as a fan

→ Search for tweets related to Mayu Matsuoka on the Twitter timeline. → Save Mayu Matsuoka's images and videos

I don't enjoy this work as much as I used to, and I myself become a member of society, so I don't have time to surf Twitter every day.

** Let's automate everything! !! ** **

So, as the first step, search for tweets with images and videos that are popular recently on Twitter, get the image URL, and send it on time every day using LINE Bot! !! !! !!

Please add friends if you like.

Add friend

L.png

The explanation of how to use twitter API and how to register LINE Developers is omitted here.

Tweet search, image / video acquisition with tweepy

Twitter Developers page https://developer.twitter.com/en/portal/dashboard

tweepy official documentation http://docs.tweepy.org/en/latest/

Setting environment variables when using tweepy

The environment variables are described in the Yaml file in the env folder.

search_tweets.py


import os
import tweepy
from datetime import datetime, date, timedelta
from dateutil.relativedelta import relativedelta

#Set of environment variables
consumer_key = os.getenv('TWITTER_CONSUMER_KEY')
consumer_secret = os.getenv('TWITTER_CONSUMER_SECRET')
access_token = os.getenv('TWITTER_ACCESS_TOKEN')
access_token_secret = os.getenv('TWITTER_ACCESS_TOKEN_SECRET')
bearer_token = os.getenv('TWITTER_ACCESS_TOKEN_SECRET')


#on twitter#Image search for Mayu Matsuoka and get URL function
def search_tweets():
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)
    api = tweepy.API(auth)

env/stg.yml


STAGE: stg
LINE_ACCESS_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LINE_CHANNEL_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
USER_ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_CONSUMER_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_CONSUMER_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_BEAR_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_ACCESS_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_ACCESS_TOKEN_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TZ: Asia/Tokyo

Tweet search and image / video acquisition

search_tweets.py


    #Get yesterday's date
    yesterday = datetime.strftime(datetime.today() - relativedelta(days=1), f"%Y-%m-%d")
    #Twitter search word
    q = f'#Mayu Matsuoka OR Mayu Matsuoka-Mayu Matsuoka similar filter:media exclude:retweets min_faves:10 since:{yesterday} min_retweets:2'
    #Search
    cric_tweet = tweepy.Cursor(
        api.search, q=q, 
        tweet_mode='extended', #Get all omitted tweets
        include_entities=True).items(20) #Get all omitted links

    #image
    contents = []

    for tweet in cric_tweet:
        print(tweet.full_text)
        try:
            #An array that stores URLs of images and videos in tweets
            media = tweet.extended_entities['media']
            print(media)
            for m in media:
                print(m)
                #Image URL of the preview screen required when sending with LINE Bot
                preview = m['media_url_https']
                #For video
                if m['type'] == 'video':
                    #When getting the video URL, content_type is video/If it is not mp4, the video will not play when sent by LINE Bot.
                    #Also, I wanted to write it in one line, so I used the comprehension notation forcibly and finally[0]I got the URL in.
                    origin = [variant['url'] for variant in m['video_info']
                              ['variants'] if variant['content_type'] == 'video/mp4'][0]
                #For images
                else:
                    #URL of the content image displayed when the preview image is clicked
                    origin = m['media_url_https']

                #Arrange it in the required form when sending with the LINE Bot Messaging API.
                content = {'preview': preview,
                           'origin': origin, 'type': m['type']}
                contents.append(content)

            print('--------------------------------------------')
        except:
            print('noUrl')
            print('--------------------------------------------')
    return contents

I wrote in the article about when I was addicted to this process, so please do not miss it ↓

A story I was addicted to trying to get a video url with tweepy https://qiita.com/soma_sekimoto/items/65c664f00573284b0b74

Send a message from LINE Bot

LINE Bot Official Reference https://developers.line.me/ja/docs/messaging-api/getting-started/

LINE Bot SDK Github https://github.com/line/line-bot-sdk-python

send_media.py


# -*- coding:utf-8 -*-
import logging
from linebot import LineBotApi

from linebot.exceptions import LineBotApiError
from linebot.models import (
    TextSendMessage, ImageSendMessage, VideoSendMessage
)

import requests
import os


import search_tweets

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def send_media(event, context):

    logger.info("Authentication OK.")
    #Create a LineBot API object
    token = os.getenv('LINE_ACCESS_TOKEN')

    line_bot_api = LineBotApi(token)

    try:
        #Get an array of image and video URL objects with twitter search
        all_media_list = search_tweets.search_tweets()
        print('all_media_list')
        print(all_media_list)

        messages = []

        #Further transform the array obtained by tweepy for LINE Bot
        for media in all_media_list:
            item = VideoSendMessage(
                original_content_url=media['origin'], preview_image_url=media['preview']) if media['type'] == 'video' else ImageSendMessage(
                original_content_url=media['origin'], preview_image_url=media['preview'])

            messages.append(item)

        print('messages')
        print(messages)
        #Be sure to send messages only to yourself, except in a production environment.
        if os.getenv('STAGE') == 'prod':
            #For some reason, I can only send 4 images at a time, so I will send them in two parts. (Still, it is still a mystery that only 7 sheets can be sent in total)
            line_bot_api.broadcast(
                messages[0:3]
            )
            line_bot_api.broadcast(
                messages[4:8]
            )
        else:
            user_id = os.getenv('USER_ID')
            line_bot_api.push_message(
                user_id,
                messages[0:3]
            )
            line_bot_api.push_message(
                user_id,
                messages[4:8]
            )

    except LineBotApiError as e:
        print(e.status_code)
        print(e.error.message)
        print(e.error.details)

    return {"stautsCode": 200, "body": "OK"}


if __name__ == '__main__':
    send_media(None, None)

Deploy with Serverless Framework

requirements.py


import os
import sys


requirements = os.path.join(
    os.path.split(__file__)[0],
    '.requirements',
)

if requirements not in sys.path:
    sys.path.append(requirements)

requirements.txt


requests 
print_function
line-bot-sdk #Module that can use LINE Bot SDK in Python
tweepy #Module that can use Twitter API in Python

serverless.yml


service: mayu-delivery

provider:
  name: aws
  runtime: python3.7
  region: ap-northeast-1
  stage: stg
  deploymentBucket: sls-deps #Specifying a deployment bucket
  environment: ${file(./env/${opt:stage, self:provider.stage}.yml)}

plugins:
  - serverless-python-requirements

custom:
  scheduleEnabled:
    prod: true
    stg: false
    local: false

functions:
  send_media:
    handler: media_deliver.send_media
    timeout: 300
    events:
      - http:
          path: linebot/send_media
          method: post
      - schedule:
          rate: cron(30 3 * * ? *)
          enabled: ${self:custom.scheduleEnabled.${opt:stage, self:provider.stage}}
sls deploy
endpoints:
  POST - https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/linebot/send_media
functions:
  send_media: mayu-delivery-prod-send_media

Don't forget to copy the URL of the above endpoints to the LINE Bot side.

スクリーンショット 2020-10-11 15.33.53.png

Completion code

search_tweets.py


import os
import tweepy
from datetime import datetime, date, timedelta
from dateutil.relativedelta import relativedelta

consumer_key = os.getenv('TWITTER_CONSUMER_KEY')
consumer_secret = os.getenv('TWITTER_CONSUMER_SECRET')
access_token = os.getenv('TWITTER_ACCESS_TOKEN')
access_token_secret = os.getenv('TWITTER_ACCESS_TOKEN_SECRET')
bearer_token = os.getenv('TWITTER_ACCESS_TOKEN_SECRET')


#on twitter#Image search for Mayu Matsuoka and get URL
def search_tweets():
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)
    api = tweepy.API(auth)

    yesterday = datetime.strftime(
        datetime.today() - relativedelta(days=1), f"%Y-%m-%d")

    q = f'#Mayu Matsuoka OR Mayu Matsuoka-Mayu Matsuoka similar filter:media exclude:retweets min_faves:10 since:{yesterday} min_retweets:2'

    cric_tweet = tweepy.Cursor(
        api.search, q=q, tweet_mode='extended', include_entities=True).items(20)

    contents = []
    for tweet in cric_tweet:
        print(tweet.full_text)
        try:
            media = tweet.extended_entities['media']
            print(media)
            for m in media:
                print(m)
                preview = m['media_url_https']
                if m['type'] == 'video':
                    origin = [variant['url'] for variant in m['video_info']
                              ['variants'] if variant['content_type'] == 'video/mp4'][0]
                else:
                    origin = m['media_url_https']

                content = {'preview': preview,
                           'origin': origin, 'type': m['type']}
                contents.append(content)

            print('--------------------------------------------')
        except:
            print('noUrl')
            print('--------------------------------------------')
    return contents


if __name__ == "__main__":
    search_tweets()

media_deliver.py


# -*- coding:utf-8 -*-
import logging
from linebot import LineBotApi

from linebot.exceptions import LineBotApiError
from linebot.models import (
    TextSendMessage, ImageSendMessage, VideoSendMessage
)

import requests
import os


import search_tweets

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def send_media(event, context):

    logger.info("Authentication OK.")
    #Create a LineBot API object
    token = os.getenv('LINE_ACCESS_TOKEN')

    line_bot_api = LineBotApi(token)

    try:
        #Get an array of image and video URL objects with twitter search
        all_media_list = search_tweets.search_tweets()
        print('all_media_list')
        print(all_media_list)

        messages = []

        for media in all_media_list:
            item = VideoSendMessage(
                original_content_url=media['origin'], preview_image_url=media['preview']) if media['type'] == 'video' else ImageSendMessage(
                original_content_url=media['origin'], preview_image_url=media['preview'])

            messages.append(item)

        print('messages')
        print(messages)
        if os.getenv('STAGE') == 'prod':
            line_bot_api.broadcast(
                messages[0:3]
            )
            line_bot_api.broadcast(
                messages[4:8]
            )
        else:
            user_id = os.getenv('USER_ID')
            line_bot_api.push_message(
                user_id,
                messages[0:3]
            )
            line_bot_api.push_message(
                user_id,
                messages[4:8]
            )

    except LineBotApiError as e:
        print(e.status_code)
        print(e.error.message)
        print(e.error.details)

    return {"stautsCode": 200, "body": "OK"}


if __name__ == '__main__':
    send_media(None, None)

requirements.py


import os
import sys


requirements = os.path.join(
    os.path.split(__file__)[0],
    '.requirements',
)

if requirements not in sys.path:
    sys.path.append(requirements)

requirements.txt


requests
line-bot-sdk
print_function
tweepy

serverless.yml


service: mayu-delivery

provider:
  name: aws
  runtime: python3.7
  region: ap-northeast-1
  stage: stg
  deploymentBucket: sls-deps
  environment: ${file(./env/${opt:stage, self:provider.stage}.yml)}

plugins:
  - serverless-python-requirements

custom:
  scheduleEnabled:
    prod: true
    stg: false
    local: false

functions:
  send_media:
    handler: media_deliver.send_media
    timeout: 300
    events:
      - http:
          path: linebot/send_media
          method: post
      - schedule:
          rate: cron(30 3 * * ? *)
          enabled: ${self:custom.scheduleEnabled.${opt:stage, self:provider.stage}}

env/stg.yml


STAGE: stg
LINE_ACCESS_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LINE_CHANNEL_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
USER_ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_CONSUMER_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_CONSUMER_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_BEAR_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_ACCESS_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWITTER_ACCESS_TOKEN_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TZ: Asia/Tokyo

in conclusion

The LINE Bot is still unsatisfactory for me, but I will continue to improve and add functions.

If you find something wrong or something more like this, please leave a comment! !!

And please add friends! !! !! !!

Add friend

L.png

Reference article

https://blog.serverworks.co.jp/sls-line-beginner

Recommended Posts

I made a LINE Bot that sends recommended images every day on time
In Python, I made a LINE Bot that sends pollen information from location information.
I made a LINE BOT that returns parrots with Go
[Python] I made a Line bot that randomly asks English words.
I made a stamp substitute bot with line
I made a LINE Bot with Serverless Framework!
[Python] I made a bot that tells me the current temperature when I enter a place name on LINE
I made a LINE BOT that returns a terrorist image using the Flickr API
I made a Line Bot that uses Python to retrieve unread Gmail emails!
[Python] I made a LINE Bot that detects faces and performs mosaic processing.
[AWS] I made a reminder BOT with LINE WORKS
I made a household account book bot with LINE Bot
I made a LINE BOT with Python and Heroku
I want to restart CentOS 8 on time every day.
I made a discord bot
I made a neural network generator that runs on FPGA
[AWS] I made a reminder BOT with LINE WORKS (implementation)
I made a program to collect images in tweets that I liked on twitter with Python
I made a wikipedia gacha bot
I made a Twitter bot that mutters Pokemon caught by #PokemonGO
I made a slack bot that notifies me of the temperature
I made a Line bot that guesses the gender and age of a person from an image
I started to work at different times, so I made a bot that tells me the time to leave
I made a Discord bot in Python that translates when it reacts
I made a Chrome extension that displays a graph on an AMeDAS page
A memo that I stumbled upon when doing a quote RT on Twitter Bot
I made a SlackBot that notifies me of AtCoder contest information every week
I made a Linebot that notifies me of nearby evacuation sites on AWS
I tried to make a translation BOT that works on Discord using googletrans
I made a Mattermost bot with Python (+ Flask)
I made a Twitter BOT with GAE (python) (with a reference)
I made a VM that runs OpenCV for Python
Make a parrot return LINE Bot on AWS Cloud9
I made a Python3 environment on Ubuntu with direnv.
I created a Discord bot on Docker that reports the number of corona infected people in Tokyo at a specified time.
[Valentine special project] I made a LINE compatibility diagnosis!
Procedure for creating a Line Bot on AWS Lambda
A script that sends a lot of websites to people who regularly visit them every day
I tried to make a bot that randomly acquires Wikipedia articles and tweets once a day
〇✕ I made a game
A story that stumbled when I made a chatbot with Transformer
I made a package to filter time series with python
I made a class that easily performs unixtime ← → datetime conversion
I made a fucking app that won't let you skip
Until I return something with a line bot in Django!
I want to send a message from Python to LINE Bot
I made a VGG16 model using TensorFlow (on the way)
I made an anomaly detection model that works on iOS
I made a rigid Pomodoro timer that works with CUI
When I made a Discord Bot, my classmates destroyed my computer
I made a plug-in that can "Daruma-san fell" with Minecraft
I made a Chatbot using LINE Messaging API and Python
[Python] I tried to make a simple program that works on the command line using argparse.
A story that I had a hard time displaying graphs on top of each other with matplotlib
I made a LINE bot that tells me the type and strength of Pokemon in the Galar region with Heroku + Flask + PostgreSQL (Heroku Postgres)