MAGAZINE

キャリテク!マガジン

コラム

【おうちSEになろう!】おうちチャットで動くBotをつくる(池澤あやか氏 連載)

みなさん、こんにちは。池澤あやかです。

前回の連載では、Pushbullet を使って簡単なリマインダーを作りました。
今回はもう少し踏み込んで、Googleカレンダーと連携した「おうちチャットで動くBot」を作ってみましょう!

我が家では、Googleカレンダー上に、お知らせしたいことを登録しておく「Notification」 というカレンダーを用意し、ごみ出しや資料の提出日などの前日に、家族で使っている Discord に通知メッセージを送るようにしています。

実際のGoogle カレンダーの画像
我が家の愛犬がお知らせしてくれるようにしたので、幸福度が高いです

今回作成するBotの機能

今回のBotは、以下のような流れで動作します。

  1. Googleカレンダーから予定を取得:Google Calendar APIを使って、次の日の予定を取得します。
  2. Discordにメッセージを送信:取得した予定を元に、Discordの指定されたチャンネルにメッセーを送ります。
  3. 自動化:毎日決まった時間に自動的に翌日の予定を通知します。

では、具体的にコードを見ていきましょう。

プログラムの全容は GitHub のリポジトリ にアップロードしているので、そちらもあわせてどうぞ!

GoogleカレンダーAPIとの連携

まずは、ひとつめの機能を作っていきましょう!
Googleカレンダーにお知らせしたいもののタイトルと説明をイベントとして登録してあるので、GoogleカレンダーAPIを利用してそのイベントの予定を取得します。前日に次の日のお知らせを取得して、お知らせするような仕組みにしたいと思います。

ここでは、Googleのサービスアカウントを使って認証を行い、イベントを取得します。

GoogleCalendarAPIクラス

Python でプログラムを書きます。

google_calendar_api.py
import datetime
import os
from dotenv import load_dotenv
from googleapiclient.discovery import build
from google.oauth2 import service_account
 
load_dotenv()
 
GOOGLE_CALENDAR_ID = os.environ.get('GOOGLE_CALENDAR_ID')
GOOGLE_CALENDAR_CREDENTIAL_PATH = os.environ.get('GOOGLE_CALENDAR_CREDENTIAL_PATH')
 
class GoogleCalendarAPI:
   """
   Google カレンダー API をサービスアカウントの資格情報を使用して操作するためのクラス。
   """

 
   def __init__(self, credentials_path=GOOGLE_CALENDAR_CREDENTIAL_PATH, scopes=['https://www.googleapis.com/auth/calendar.readonly']):
       """
       GoogleCalendarAPI クラスのインスタンスを初期化します。
 
       Args:
           credentials_path (str): サービスアカウントの資格情報JSONファイルのパス。
           scopes (list): Google カレンダー API にアクセスするためのスコープのリスト。
       """

       self.credentials_path = credentials_path
       self.scopes = scopes
       self.creds = None
       self.service = None
       self.authenticate()
 
   def authenticate(self):
       """
       サービスアカウントの資格情報を使用してユーザーを認証し、Google カレンダー API サービスを設定します。
       """

       self.creds = service_account.Credentials.from_service_account_file(self.credentials_path, scopes=self.scopes)
       self.service = build('calendar', 'v3', credentials=self.creds)
 
   def list_events_next_day(self, calendar_id='primary'):
       """
       指定されたカレンダーの翌日のイベントを取得します。
 
       Args:
           calendar_id (str): イベントを取得するカレンダーのID。デフォルトは 'primary'。
 
       Returns:
           list: 翌日に発生するイベントのリスト。
       """

       t_delta = datetime.timedelta(hours=9) # 日本標準時 (JST) のタイムゾーンオフセット
       JST = datetime.timezone(t_delta, 'JST')
       # 翌日の0時から始まる時間範囲を設定
       time_min = datetime.datetime.now(JST).replace(hour=0, minute=0, second=0) + datetime.timedelta(days=1)
       time_max = time_min + datetime.timedelta(days=1)
       # イベントを取得
       events_result = self.service.events().list(
           calendarId=calendar_id, timeMin=time_min.isoformat(), timeMax=time_max.isoformat(), singleEvents=True, orderBy='startTime'
       ).execute()
       return events_result.get('items', [])
 
# GoogleCalendarAPI クラスの使用例
if __name__ == "__main__":
   gc = GoogleCalendarAPI()
   events = gc.list_events_next_day(calendar_id=GOOGLE_CALENDAR_ID)
 
   if events:
       for event in events:
           print(event['start'].get('dateTime', event['start'].get('date')))
           print(event['summary'])
           print(event.get('description'))
           print('---')
Google カレンダー の API を使えるようにする

このプログラムを動かすためには、GOOGLE_CALENDAR_IDとGOOGLE_CALENDAR_CREDENTIAL_PATH の設定が必要です。

APIにアクセスするための鍵ファイルを取得する手順を説明します。
Google Cloud Console にアクセスし、ログイン、画面上部のプロジェクト ドロップダウンから既存のプロジェクトを選択するか、「新しいプロジェクト」をクリックして新規プロジェクトを作成します。

Google Calendar API を有効化します。左側のメニューから「API とサービス」 > 「ライブラリ」を選択します。検索バーに「Google Calendar API」と入力、選択したのち、「有効にする」ボタンをクリックして、API を有効化します。

サービスアカウントの作成を行います。左側のナビゲーション メニューから「API とサービス」 > 「認証情報」を選択します。「認証情報を作成」ボタンをクリックし、「サービス アカウント」を選択。ガイドに従ってサービスアカウントを作成します。

作成したサービス アカウントの詳細ページで、「キー」タブを選択し、新しいカギを作成したのち、credentials.json ファイルをダウンロードしてください。

このファイルをプログラムと同じディレクトリ階層などにおいてください。どこに置いたかを相対パスでGOOGLE_CALENDAR_CREDENTIAL_PATHに設定しておきます。

また、APIをつかえるようにするために、カレンダーのIDも取得してください。カレンダーIDはカレンダーの設定ページで取得することができます。

対象のカレンダーの「設定と共有」を開いて、サービス アカウントのメールアドレス(credentials.json 内の client_email)を入力し、「予定の表示」の権限を付与します。

これらカレンダーIDや鍵ファイルのパスは環境変数に設定しておきます。プログラム上では、.env ファイルに記述しておき、dotenv を使って読み込みます。

プログラミング初心者の方は、GOOGLE_CALENDAR_ID、GOOGLE_CALENDAR_CREDENTIAL_PATHには、環境変数を使わず、直接値を設定してみてもOKです。ただし、キーを悪用される危険性があるので、GitHubにあげるなどパブリックに公開しないように注意してください。

Discordへの通知を送る

次に、取得した予定をDiscordに通知する部分を作成します。今回は、DiscordのBotを利用して、指定のチャンネルにメッセージを送信します。

通知メッセージを作成・送信するプログラム
notification_daily.py
import asyncio
import discord
import os
import schedule
import time
 
from dotenv import load_dotenv
from google_calendar_api import GoogleCalendarAPI
 
load_dotenv()
 
GOOGLE_CALENDAR_ID = os.environ.get('GOOGLE_CALENDAR_ID')
DISCORD_TOKEN = os.environ.get('DISCORD_TOKEN')
DISCORD_NOTIFICATION_CHANNEL_ID = int(os.environ.get('DISCORD_NOTIFICATION_CHANNEL_ID'))
 
intents = discord.Intents.default()
bot = discord.Client(intents=intents)
 
def create_daily_notification():
   """
   翌日のGoogleカレンダーのイベント情報を取得し、通知メッセージを作成します。
 
   Returns:
       str: 翌日のイベント情報を含むメッセージ。イベントがない場合は空の文字列を返します。
   """

   gc = GoogleCalendarAPI()
   events = gc.list_events_next_day(calendar_id=GOOGLE_CALENDAR_ID)
   message = ''
 
   if events:
       message = f'明日 {events[0]["start"].get("date")} のおしらせです🐾\n\n'
       for event in events:
           message += f'■ {event["summary"]}\n'
           if 'description' in event:
               message += f'{event["description"]}\n'
   return message
 
async def send_daily_notification():
   """
   指定されたDiscordチャンネルに通知メッセージを送信します。
   """

   message = create_daily_notification()
   channel = bot.get_channel(DISCORD_NOTIFICATION_CHANNEL_ID)
   if channel and message:
       await channel.send(message)
 
def run_schedule():
   """
   `schedule`モジュールを使用してスケジュールされたタスクを実行します。
   """

   while True:
       schedule.run_pending()
       time.sleep(60)
 
@bot.event
async def on_ready():
   """
   ボットが準備完了したときに呼び出されるイベントハンドラ。
   `send_daily_notification` 関数を毎日20時に実行するようにスケジュールを設定します。
   """

   print(f'{bot.user} Ready')
   # 毎日20時にお知らせを送信
   schedule.every().day.at("02:50").do(lambda: asyncio.run_coroutine_threadsafe(send_daily_notification(), bot.loop))
 
if __name__ == "__main__":
   import threading
   threading.Thread(target=run_schedule, daemon=True).start()
   bot.run(DISCORD_TOKEN)

Botの作成・実行

Discord Bot を実行する際には、事前に Discord で Bot を作成し、Bot のトークンと投稿したいチャンネルのIDを取得しておく必要があります。

​​Discordの開発者ポータルでBotを作成する
  • 管理者権限のあるサーバーで動かすのを前提としています。
  • ブラウザから Discord Developer Portal にアクセスします。Discordアカウントにログインしていない場合は、ログインしてください。
  • ログイン後、「New Application」ボタンをクリックして新しいアプリケーションを作成します。アプリケーション名を入力し、「Create」をクリックします。

また、左側のメニューから「Installation」を選択し、「Installation Contexts」の「User Install」のチェックを外し、「Install Link」を「None」に設定しておきます。

アプリケーションが作成されたら、左側のメニューから「Bot」を選択し、追加設定を行います。

わたしは以下の追加設定を行いました。

  • アイコンの変更
  • 「Public Bot」の設定をオフ。プライベートで使う Bot のため。

続いて、Bot のトークンの取得を行います。同じページ内の「Reset Token」ボタンを押すと、トークンを取得できます。 Bot が Discord サーバーに接続する際に必要となる情報です。後ほど使うので、コピーしておいてください。

なお、このトークンは絶対に外部には公開しないでください。

Botをサーバーに招待する

家族で使っている Discord のサーバーに Bot のアプリケーションを招待してください。

左側のメニューから「OAuth2」を選択し、「OAuth2 URL Generator」で Scope に「bot」を選択し、ページ下に表示された「Generated URL」をコピーして、URLを開きます。「認証」ボタンを押し、Bot を Discord のサーバーに招待します。

チャンネルIDをコピー

discord のアプリケーション上から、投稿したいチャンネルのIDをコピーしておきます。

トークンとチャンネルIDは、環境変数に設定します。プログラム上では、.env ファイルに記述しておき、dotenv を使って読み込みます。

プログラミング初心者の方は、環境変数を使わず、プログラムに直接値を設定してみても良いですが、パブリックにプログラムを公開しないように注意してください。

Bot を起動する

Bot が準備できたら、適宜ライブラリをインストールしたのち、プログラムを実行します。

shell
$ python notification_daily.py

このプログラムを実行することで、毎日20時に Google カレンダーの翌日の予定が Discord に通知されます。プログラムは常に実行されている必要があるので、バックグラウンドで動かすか、サーバーなどで実行しておくと便利です。

家族の連絡手段に Slack や LINE など他のチャットツールを使っている方は、Slack や LINE にも同様の API があるので、それを利用して作成できます。

おわりに

このようにカレンダーとチャットを連携させることで、登録も楽々、便利なおしらせBotをつくることができます。

登録用の UI を作るのは地味に手間なので、Googleカレンダーのような普段からよく使うアプリケーションを活用するのも一手です。

連載で使用したプログラムは、以下の GitHubリポジトリ で公開しています。
ぜひご活用ください!

AltXからのお知らせ

池澤あやか氏のコラムはいかがでしたでしょうか?

当社のIT未経験者向け育成研修「キャリテク!」を趣味でパソコンを活用している方にもぜひ知っていただきたく、池澤あやか氏に「おうちSE」のコラムを執筆いただきました。
当社の「キャリテク!」はITエンジニア未経験の方でも2~3ヶ月間の研修を通してゼロからITエンジニアとしてのキャリアをスタート出来るようにサポートする入社時研修です。

「キャリテク!」については個別説明会を実施しています。

少しでも興味がある方は、まずは以下の個別説明会でざっくばらんにお話しましょう!
https://www.altx.co.jp/careetec/seminar/tokyo/

関連記事

facebook シェアシェア
LINE シェアシェア