Denen blog

株式会社電縁の社員によるブログです。

GoogleアラートをGoogle Hangouts Chatに連携する

こんにちは、イノベーションオフィスの吉田です。

 

皆さんは G Suite ってご存知でしょうか?

gsuite.google.co.jp

G Suite は Google の各種サービスの企業向けの有料サービスなのですが、今年になってから G Suite 向けに Google Hangtouts Chat というサービスがリリースされました。

こちらがいわゆるチャットツールで、Slack の対抗みたいな感じなんです。

G Suite ユーザなら追加料金無しで使えるということで早速使ってみていたのですが、さすが Google だけあって Bot が簡単に作れるようでした。

 

というわけで、試しに Google アラートの更新を定期的に Google Hangouts Chat に投稿する Bot を作ってみましたのでご紹介したいと思います。

 

Google アラートの作成

ではまず Google アラートにアクセスしてアラートを作成してみましょう。

f:id:yasuaki-sakai:20180611185306j:plain

「アラートを作成」欄に取得したいキーワードを入力します。

今回は「ブロックチェーン」というキーワードを入力し、「配信先」を「RSSフィード」に、「件数」を「すべての結果」に指定しました。

入力・選択が完了したら「アラートを作成」ボタンを押してアラートを作成します。

f:id:yasuaki-sakai:20180611185645j:plain

作成したアラートの RSS のアドレスをコピーしておいてください。

 

Google Hangouts Chat の準備

続いて Google Hangouts Chat に移動し、投稿を行いたい部屋に入ります。

f:id:yasuaki-sakai:20180611190350j:plain

画面上部のメニューから「Webhook を設定」を選択します。

f:id:yasuaki-sakai:20180611190417j:plain

着信 Webhook の設定画面が開くので、「+ WEBHOOK を追加」を選択します。

f:id:yasuaki-sakai:20180611190506j:plain

「名前」の部分に Bot の名前を入力してください。

アバターの URL は空でも問題ありません。

入力が終わったら「保存」をクリックします。

f:id:yasuaki-sakai:20180611190554j:plain

無事着信用の Webhook が作成され、URL が発行されました。

こちらもコピーしておいてください。

 

スクリプトの作成

いよいよこれらを連携するスクリプトを作成したいと思います。

流れとしては以下の感じです。

  1. Google アラートの RSS を取得
  2. 更新が有ったら件数分 Google Hangouts Chat に投稿

簡単ですね。

RSS のパースや、更新記事が有るか無いかは SQLite を用いて URL をキーにして判断しており、以下の記事を参考にさせていただきました。

imabari.hateblo.jp

また、URL を urldecode する為に以下の記事も参考にさせていただきました。

qiita.com

というわけで出来上がったコードが以下になります。

# -*- coding: utf-8 -*-

import feedparser
import sqlite3
import json
import datetime
import sys
import io
import re
import urllib.parse
from html.parser import HTMLParser
from httplib2 import Http

FEED_URL    = 'https://www.google.co.jp/alerts/feeds/XXXXX/XXXXX'
WEBHOOK_URL = 'https://chat.googleapis.com/v1/spaces/XXXXX/messages?key=XXXXX&token=XXXXX'
DATABASE    = '/home/user/data/rss.sqlite'

class MyHtmlStripper(HTMLParser):
    def __init__(self, s):
        super().__init__()
        self.sio = io.StringIO()
        self.feed(s)

    def handle_starttag(self, tag, attrs):
        pass

    def handle_endtag(self, tag):
        pass

    def handle_data(self, data):
        self.sio.write(data)

    @property
    def value(self):
        return self.sio.getvalue()

# フィード取得
fdp = feedparser.parse(FEED_URL)

# SQLite接続
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
c = con.cursor()

# テーブル生成
c.execute('CREATE TABLE IF NOT EXISTS RSSEntries (entry_id INTEGER PRIMARY KEY AUTOINCREMENT, title, link, published);')

for entry in fdp.entries:
    # リンクの重複チェック
    c.execute('SELECT entry_id from RSSEntries WHERE link=?', (entry.link,))

    if len(c.fetchall()) == 0:
        # 新着情報
        c.execute('INSERT INTO RSSEntries (title, link, published) VALUES (?,?,?)', (entry.title, entry.link,datetime. datetime.strptime(entry.published, '%Y-%m-%dT%H:%M:%SZ')))

        # Hangouts Chat に投げる
        tmp_url = re.search('url=(.*)&ct=', entry.link)
        url = urllib.parse.unquote(tmp_url.group(1))
        message = {'text': ''.join([MyHtmlStripper(entry.title).value, '\n', url, '\n', MyHtmlStripper(entry.content[0].value).value])}
        headers = { 'Content-Type': 'application/json; charset=UTF-8' }
        http_obj = Http()
        response = http_obj.request(
          uri=WEBHOOK_URL,
          method='POST',
          headers=headers,
          body=json.dumps(message),
        )

con.commit()

# 過去データ削除
scope = datetime.datetime.now() - datetime.timedelta(days = 14);
c.execute('DELETE FROM RSSEntries WHERE datetime(published) < datetime(?);', (scope,)) 

con.commit()
con.close()

FEED_URL、WEBHOOK_URL に先ほどコピーした URL をそれぞれ設定してください。

そして SQLite のパスを DATABASE に設定してください。

これを実行すると以下のように Google Hangouts Chat の方に投稿されるかと思います。

f:id:yasuaki-sakai:20180611192214j:plain

最後に cron で定期的にこのスクリプトが実行されるようにすれば完了です!

私は試しに5分間隔にして動かしていますが、問題なく動作しています。

 

最後に

記事が長くなってしまいましたがいかがだったでしょうか?

今回は着信 WEBHOOK だけしか使っていないので、今度はもうちょっと Bot らしいものを作ってみたいと思います。

 

それでは!

 

書いた人:イノベーションオフィス 室長 吉田

f:id:yasuaki-sakai:20171204190702j:plain