はじまり

それじゃあ、早速作っていきますかあ

あのボットだね、改良だ改良だっ

よし、行くぜえ
何をしようとしているのか
僕は以前、「Discord.py」というライブラリとその派生のライブラリ「Pycord」を使って、Discord上で動くボットを作りました。
こんな感じのイメージで・・・(これは改良後のやつです)

そして、そのボットのプログラムが入ったコンテナを、「Render」というビルドサービスに間借りして運用していたわけです。
Renderでビルドした時の記事はこれになります。
そして、この時に作ったボットのシステム概略はこんな感じです。
このボットは、Discordから打ち込んだ内容を元に、PythonからGoogleスプレッドシートを参照及び操作して、Discordで返事を返すというものです。

しかし、前回作ったそのボットには、問題点がいくつかありました。ザッと上げると・・・
- Renderだとサービスのコスパが悪い?(Renderは定額制だがそこまで使っていなかった。1000円くらい。)
- PythonからGoogleスプシへの参照及び更新処理のロジックは、Google Apps Scriptでも使っている。そのため、ソースコードが二重管理になっている。
- PythonからGoogleスプシを参照するためには、Serviceaccountのjsonファイルを管理する必要がある。
・・・みたいな感じで、金銭面と管理面の問題がありました。
そのため、今回はこのDiscordのボットの機能追加含めて、色々とサービス構成を刷新していきたいと思います。
今のところ、こんな構成で考えています。

まあ、色々と色が入っていてアレですが、変更点は以下になります。
- Pythonコンテナをビルドする場所を、Google Kubernetes Engineに移設する。
- Googleスプシへの参照及び更新処理は、Google Apps Script(以降、GAS)のみが行うこととする。Pythonは、GASアプリに対してリクエストすることによって、Discordクライアントへの返信内容を決める。
- そのため、Serviceaccountのjsonファイルを管理する必要がなくなる。
- Discordから指示する内容を、一部スラッシュコマンドで実装する。
とまあ、こんな感じでサーバ構成を変更していきます。水色の点線の中が今回想定している範囲になります。
水色の点線以外にも別に機能があるので(「Webhook通知」は第1回の天気情報の部分ですね)、出来上がったら果たしてどうなるんでしょうか。
さあ、行ってみましょう。(Google Kubernetes Engineに移設するのは今回やりません。他にも載せたい機能があるのでその後です。)
一旦、一段落。
はい、作る過程は飛ばします・・・
色々と新しいことが出てきて、てんてこ舞いだったのであまり上手くまとめられる自信がありません・・・
とりあえず、いくつか取り上げてみると・・・
- GASアプリは、リクエストメソッドを「GET」と「POST」しか受け取れない。
- なので、リクエストヘッダーではなく、ボディにメソッドを入れて分類する。
- GASにリクエストを送る処理を行うと、Discord上でインタラクションエラーになることが多発する。(スラッシュコマンド時)
まだ、Pycordに関して理解が及んでいないので、細かい部分の実装が出来ていません。
しかしながら、エラーになっているのは、Googleスプシの参照を行う部分のみなので、まあ一旦よしとしましょう・・・
それでは、少しボットサービスの説明です。
改良後のサービス概略
概略です。
今回使うライブラリ
今回使う主なライブラリは、引き続きこれです。pycordです。

久しぶりにpycordのドキュメントを開くと、色々と機能やら説明やらが充実していて驚きました!
前回から1年も経っていないと思いますが・・・、知らない間に僕が成長したのでしょうか?(そんなバカな)
このボットは現在、Googleスプシをいじっていると言いました。
そのことについて、もう少し詳細を。
Google Spreadsheetの中身
そして、今回使うボットの肝となるGoogleスプレッドシートの中身がこんな感じです。
このGoogleスプレッドシートでは、自分が気になったWebサイトのタイトルとURLを管理しています。E〜J列では、そのWebサイトのカテゴリ分けもしています。

このシートを使って、Discord上で一部のやり取りをボットと行っていきます。
Google Spreadsheetを使った機能その1:あるかどうか検索して登録する
使う機能は2つです。
1つ目は、Discord→Python→GASという風に渡されたURLがシートの中にあるかどうかを検索して(赤線)、あったらその旨をDiscordで返信する。なかったらそのURLに関する情報を登録するかどうかを質問する(水色線)。

Google Spreadsheetを使った機能その2:タグに紐づく記事を一覧表示する
もう1つは、Discord→Python→GASという風に渡されたカテゴリ(タグ)がシートの中にあるかどうかを検索して(赤線)、あったら該当する記事の情報をすべてDiscord上に掲載する(水色線)。なかったらその旨をDiscordで返信する。

実際の挙動
そして、新しくなったボットの実際の挙動を見ていきます。
まずは、既存の機能から。
1. Googleスプシを使った機能その1:記事がある場合
/webclippings regist <url>
を打った時、シート上に既に登録されているURLがある場合はその旨を教えてくれます。

2. Googleスプシを使った機能その1:記事がなくて、登録しない場合
/webclippings regist <url>
で、シート上にURLがなかったらシートに新規登録するかどうかを質問してきます。「いいえ」を選べば登録しません。

3. Googleスプシを使った機能その1:記事がなくて、登録する場合
/webclippings regist <url>
でシート上にURLがない場合に「よろしく」を選ぶと、URLとそのタイトル、そして登録した時の日時を、シートに登録します。
この機能に関する挙動があまり安定しないんですよね・・・難しい・・・

4. Googleスプシを使った機能その2:タグに紐づく記事を一覧表示する
/webclippings search <tags>
で、引数に入力したタグが紐付いている記事をシートの中から検索してDiscord上にタイトルとURLを表示してくれます。

送信後の挙動

次に、新しい機能に行きます。
5. あいさつする
/hello
のスラッシュコマンドで、ボットが挨拶してきます。性別と年齢は見ていません。

6. 特定の言葉に反応する:その1
これはスラッシュコマンドではなく、単にメッセージを送信します。
「あゆあゆ」と打つと、特定の反応が返ってきます。

7. 特定の言葉に反応する:その2
これもスラッシュコマンドではなく、単にメッセージを送信します。
「たいやき」とか「たい焼き」と打つと、特定の反応が返ってきます。

8. 今日食べたいものを選択する
/food <food_type> <food>
で、今日食べたいものを選択して送信します。
今回はラーメンを選んでみようと思います。「noodle」を選んだときは、「ramen」、「pasta」、「udon」でオートコンプリートされるようにしています。

あれ・・・?
ラーメンにしたのに、たい焼きと言い出す・・・?
次はピザで送ってみましょう。

あれ・・・?
ピッツァにしたのに、たい焼きと言い出す・・・?
はい、この機能はどの食べを選んでも「たい焼き」を選んだことになります。
そうだね!じゃあ、今日はたい焼きにしようか!」
9. 現在の日時を表示する
/now
と、スラッシュコマンドを打ち込むと現在の日時が返信されます。
個人的に、諸々の作業時間をメモしたかったので実装しました。

10. Notionに新規ページを投稿する
/notion
と、スラッシュコマンドを打ち込むと、メモ取りアプリ「Notion」に新規ページを投稿してくれます。
この機能はまだ完璧じゃないです、また今度で!
Google Apps Script側の機能
GAS側で行っている処理は、この記事を書いた頃から殆ど変わっていません。
今回のボットからGASアプリへの入り口には、URLが入りますが、下記の記事もURLから書く内容を捏ねていきます。
下記のGAS内の関数でPythonからのリクエストを受け取って、上記の記事の処理を行うわけです。
funciton doGet(e){
// GETメソッドを受け取った時の処理
return res;
}
function doPost(e){
// POSTメソッドを受け取った時の処理
return res;
}
まとめと、次回やること
まとめです。
今回の作業で、Discordクライアントとやり取りをする部分は出来ました。
それで、主に刷新しようと思っていた事柄が4つの内、3つが解決しました。
- Pythonコンテナをビルドする場所を、Google Kubernetes Engineに移設する。
- ✅Googleスプシへの参照及び更新処理は、Google Apps Script(以降、GAS)のみが行うこととする。Pythonは、GASアプリに対してリクエストすることによって、Discordクライアントへの返信内容を決める。
- ✅そのため、Serviceaccountのjsonファイルを管理する必要がなくなる。
- ✅Discordから指示する内容を、一部スラッシュコマンドで実装する。
さて、次回から取り組むことをもう少し細かく見るとこうかな。
- Notionに新規投稿する機能を作る
- その機能をGitHub Actionsからも呼び出したいので、投稿機能をパッケージ化する(まずは、notion-pyというパッケージを試してみます。)
- これで、①Discordクライアントとやり取りするボットPod、②Notion投稿用のPod、の2種類のPodをKubernetesクラスター上にデプロイする。
今までやったことがないことで時間が掛かりそうですが、やってやります。
おしまい

とりあえず、Discordでは使えそうだね!

これからが本番ですねえ〜
以上になります!
コメント