[PYTHON] [Google Photo & Slack Photo Bot] A story about making a bot that acquires photos in Google Photos and sends them to Slack.

Preface

――I wanted to become a development engineer in the fall of my second year of university, so I started self-taught programming. ――I joined the company as a server engineer in August 2017. We are developing with Rails. ――For the first time, I developed in Python. ――Since I was a beginner in programming, I've been reading Qiita and always wanted to write an easy-to-understand article, so I'll write it in an easy-to-understand manner. (It will be longer, but I hope you understand.) ――Here, I will write about the API and modules that I am using. ――All the code is posted on github and the url is pasted, so if you want to see the code, please move it to the bottom.

Why I decided to write an article

--One day, on the random channel of slack, a company that works as an intern for PHP development until July 2017. --CEO "I thought, but if there is a bot that randomly pulls one photo from google photo a day and throws it to #random, it seems like it will rise. @CTO @CTO @CTO @CTO @CTO @CTO You're busy, right? --CTO "CS has no choice but to make it lol" ――I was watching that slack "Ah ..., can I make it?" --CEO & CTO "Oh! I left it to you!"

In this way, I decided to make a photo bot, but I thought it was difficult ... at this time, there is still a long way to go. I often get stuck, so I decided to write an article. (The reason for writing in Python is that I wanted to study because it was a language I had never written personally, because the CTO likes Python lol)

goal

As shown in the picture below, call the bot called gallery, enter gallery, and then let the picture flow to slack.

Let's look at the processing one by one.

procedure

  1. Create a bot user in Slack
  2. Install the Bot library implemented in Python
  3. Install gdata module
  4. Google Auth authentication (get a photo from Google Photos)
  5. Post text and photos to Slack using Rtmbot and Slack API.

Development environment

--Python 2.7 (Prepare the development environment with pyenv)

1. Create a bot user in Slack

First, create a bot user on the Slack Apps page. The one written in red rectangle is a bot token, so copy it somewhere else. スクリーンショット 2019-06-22 12.44.19.png

2. Install the Bot library implemented in Python

Install the open source bot management library via pip (https://github.com/slackapi/python-rtmbot)

$ pip install rtmbot 

After that, create rtmbot.conf and edit it as shown below.

rtmbot.conf


# Add the following to rtmbot.conf
 DEBUG: True # make this False in production

 # 1.Please write the token of the bot created at the time here.
 SLACK_TOKEN: "xoxb-11111111111-222222222222222"

  #Write your bot's path here.(Between the names of directories/not,.Let's write.)
 ACTIVE_PLUGINS:
     - plugins.google_photo_to_slack.GooglePhotoToSlackBot

3. Install gdata module

$ pip install gdata

4. Google OAuth authentication (get a photo from Google Photos)

Here, we will explain how to use Google OAuth 2.0 authentication.

Google Cloud Platform Dashboard-> Use Google APIs-> Credentials, OAuth2.0 Client Create an ID and download its secret json file. Select "Other" as the type.

スクリーンショット 2017-08-11 20.19.37.png

As the number of files has increased, the directory structure is as follows. For now, you don't have to write anything about the contents of credentials.dat.

rtmbot
├── photo-gallery.json
├── credentials.dat
├── plugins
│   ├── __init__.py
│   └── google_photo_to_slack.py
├── rtmbot.conf
└── rtmbot.log

First, to confirm your login, write something like the following. If any of the modules listed in the file below are not installed, install them.

google_photo_to_slack.py


#!/usr/bin/python2.7
 
# module ----  
import os
import webbrowser
from datetime import datetime, timedelta
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
import gdata.photos.service
import gdata.media
import gdata.geo
import httplib2
import json
import urllib2
 
# Google Authetication
def OAuth2Login(client_secrets, credential_store, email):
    scope='https://picasaweb.google.com/data/'
    user_agent='picasawebuploader'
    storage = Storage(credential_store)
    credentials = storage.get()
 
    if credentials is None or credentials.invalid:
        flow = flow_from_clientsecrets(client_secrets, scope=scope, redirect_uri='urn:ietf:wg:oauth:2.0:oob')
        uri = flow.step1_get_authorize_url()
        webbrowser.open(uri)
        code = raw_input('Enter the authentication code: ').strip()
        credentials = flow.step2_exchange(code)
        storage.put(credentials)
 
    if (credentials.token_expiry - datetime.utcnow()) < timedelta(minutes=5):
        http = httplib2.Http()
        http = credentials.authorize(http)
        credentials.refresh(http)
 
    gd_client = gdata.photos.service.PhotosService(source=user_agent,
                                               email=email,
                                               additional_headers={'Authorization' : 'Bearer %s' % credentials.access_token})
    return gd_client
 

# main ----- 
if __name__ == '__main__':
    email = os.environ['EMAIL']
    confDir = os.path.abspath(os.path.dirname(__file__))
    client_secrets = os.path.join(confDir, 'photo-gallery.json') 
    credential_store = os.path.join(confDir, 'credentials.dat') 
    gd_client = OAuth2Login(client_secrets, credential_store, email)
 
    albums = gd_client.GetUserFeed()
    for album in albums.entry:
        print 'Album: %s (%s)' % (album.title.text, album.numphotos.text)
 
        photos = gd_client.GetFeed('/data/feed/api/user/default/albumid/%s?kind=photo' % (album.gphoto_id.text))
        for photo in photos.entry:
            print(photo.title.text)
            f = open(photo.title.text, 'w')
            f.write(urllib2.urlopen(photo.content.src).read())
            f.close()

And when I run the above file,

$ python google_photo_to_slack.py

As shown below, the browser will display the photo page, and the console will ask you to enter the authentication code displayed in the browser.

Enter the authentication code: 

Enter the authentication code displayed on your browser to complete the login.

once again,

$ python google_photo_to_slack.py 

If you execute, all the photos in google photos will be downloaded.

5. Post text and photos to Slack using Rtmbot and Slack API.

Edit the above GooglePhotoToSlack file as below.

GooglePhotoToSlackBot.py


class GooglePhotoToSlackBot (Plugin):
    MEDIA_ARR = []
    RANDOM_NUMBER = 0
    EMAIL = os.version['EMAIL']
    CHANNEL_POST = ''
    SLACK_BOT_TOKEN = os.version['SLACK_BOT_TOKEN']

    PLUGIN_CHILD_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
    PLUGIN_DIRECTORY = os.path.abspath(
        os.path.join(PLUGIN_CHILD_DIRECTORY, os.pardir)
    )
    RTMBOT_DIRECTORY = os.path.abspath(
        os.path.join(PLUGIN_DIRECTORY, os.pardir)
    )
    CLIENT_SECRETS = os.path.join(RTMBOT_DIRECTORY, os.version['SECRET_JSON'])
    CREDENTIAL_STORE = os.path.join(
        RTMBOT_DIRECTORY, os.version['CREDENTIAL_DAT']
    )

    def process_message(self, data):
        feedback_pattern = re.compile(
            #Here, enter the id of the bot that starts with U displayed in the slack users list api
            r'.*<@UAAAAAAA.*(gallery).*', re.DOTALL | re.IGNORECASE
        )

        if not (re.match(feedback_pattern, data['text'])):
            return

        self.CHANNEL_POST = data['channel']

        message = u"Today's image/I'm downloading the video! Please wait for a while!"
        message += "For videos, it may take some time to download."
        response = self.slack_client.api_call(
            "chat.postMessage",
            channel=self.CHANNEL_POST,
            text=message,
            link_names=1,
            as_user=True
        )

        self.fetch_all_media()
        self.post_random_media()

    def oauth_login(self, client_secrets, credential_store, email):
        scope = 'https://picasaweb.google.com/data/'
        user_agent = 'picasawebuploader'
        storage = Storage(credential_store)
        credentials = storage.get()
        now_time = datetime.utcnow()

        if credentials is None or credentials.invalid:
            flow = flow_from_clientsecrets(
                client_secrets,
                scope=scope,
                redirect_uri='urn:ietf:wg:oauth:2.0:oob'
            )
            uri = flow.step1_get_authorize_url()
            webbrowser.open(uri)
            code = raw_input('Enter the authentication code: ').strip()
            credentials = flow.step2_exchange(code)
            storage.put(credentials)

        if (credentials.token_expiry - now_time) < timedelta(minutes=5):
            http = httplib2.Http()
            http = credentials.authorize(http)
            credentials.refresh(http)

        gd_client = gdata.photos.service.PhotosService(
            source=user_agent,
            email=email,
            additional_headers={
                'Authorization': 'Bearer %s' % credentials.access_token
            }
        )

        return gd_client

    def fetch_all_media(self):
        gd_client = self.oauth_login(
            self.CLIENT_SECRETS,
            self.CREDENTIAL_STORE,
            self.EMAIL
        )

        albums = gd_client.GetUserFeed()
        for album in albums.entry:
            medias = gd_client.GetFeed(
                '/data/feed/api/user/default/albumid/%s' %
                (album.gphoto_id.text)
            )
            for media in medias.entry:
                self.MEDIA_ARR.append(media)

    def get_random_number_in_array(self, arr):
        max_length = len(arr)
        return random.randint(0, max_length)
               
    def post_random_media(self):
        self.RANDOM_NUMBER = self.get_random_number_in_array(self.MEDIA_ARR)
        media_object = self.MEDIA_ARR[self.RANDOM_NUMBER]
        media_file = open(media_object.title.text, 'wb')
        media_file.write(response.content)
        media_file.close()
        media_path = self.RTMBOT_DIRECTORY + "/" + media_object.title.text

        with open(media_path, 'rb') as f:
            param = {
                'token': self.SLACK_BOT_TOKEN,
                'channels': self.CHANNEL_POST,
                'title': u'Today\'s ' + media
            }
            r = requests.post(
                "https://slack.com/api/files.upload",
                params = param,
                files = {'file': f}
            )

Then, in the rtmbot directory, enter the rtmbot command to start the rtmbot server. Then in slack, type @gallery. (Any channel is fine.)

$ rtmbot

Download the `Today's image / video in the code above! Please wait for a while! If the photo flows to slack, the photo bot is complete !!

All chords

I have posted on github with various functions (such as deleting photos so as not to collect downloaded photos), so I hope you can see it if you are interested. https://github.com/romukey/PythonAlgorithm/blob/master/google_gallery/plugins/google_photo_to_slack.py

Referenced

Picasa Web API Documentation https://developers.google.com/picasa-web/docs/1.0/developers_guide_python

How to log in with Google OAuth https://stackoverflow.com/questions/30474269/using-google-picasa-api-with-python

Recommended Posts

[Google Photo & Slack Photo Bot] A story about making a bot that acquires photos in Google Photos and sends them to Slack.
A story connecting Slack and google spreadsheets
A story about a beginner making a VTuber notification bot from scratch in Python
The story of creating Botonyan that returns the contents of Google Docs in response to a specific keyword on Slack
A story of trial and error trying to create a dynamic user group in Slack
A story about reflecting Discord activity in Slack Status
A story about trying to use cron on a Raspberry Pi and getting stuck in space
I tried to make a bot that randomly acquires Wikipedia articles and tweets once a day
A story about how to specify a relative path in python.
A story about everything from data collection to AI development and Web application release in Python (3. AI development)
The story of creating a bot that displays active members in a specific channel of slack with python
A story about a build error in a shared library that references libusb
Try to write a program that abuses the program and sends 100 emails
How to make a slack bot
Create a bot that posts the number of people positive for the new coronavirus in Tokyo to Slack
A story about creating a program that will increase the number of Instagram followers from 0 to 700 in a week
A story about trying to run JavaScripthon on Windows and giving up.
Tweet in Chama Slack Bot ~ How to make a Slack Bot using AWS Lambda ~
A story about trying to connect to MySQL using Heroku and giving up
A story about Python pop and append
A story about trying to install uwsgi on an EC2 instance and failing
The story of making a box that interconnects Pepper's AL Memory and MQTT
The story of making a Line Bot that tells us the schedule of competitive programming
In Python, I made a LINE Bot that sends pollen information from location information.
A story about making an x86 bootloader that can boot vmlinux with Rust
I want a Slack bot that calculates and tells me the salary of a part-time job from the schedule of Google Calendar!