DiscordとLINEをPython+flask+Dockerで連携してみた。マグロ)DiscordとLINEをPython+flask+Dockerで連携してみた。2022-09-252022/11/24追記10月31日にLINE公式から発表された公式アカウントの料金プラン改定にて、23年6月以降本ボットの運用が困難になります。Notify対応用に作りなおしているので、本記事は打ち切りとなります。完成次第記事にいたしますので、今しばらくお待ちください。Notify版を記事にしました。Qiita投稿2回目のマグロです。皆さん、Discordって使ってますか?便利ですけどLINEより使う機会がない、、使い方が分からない、、なんて人もいると思います。実際私の友人たちがそんな感じでした。そんな時、LINEとDiscordのメッセージをつなげてみようかと思いつきました。調べてみると記事もあり、実際に運用してみると思っていたのと違う部分が多く、こうなったら自分で改良しよう!!というわけで作り方を、、、と言いたいのですがあまりにも膨大になりすぎたのでいくつか記事を分けます。書ききれるかもわからないので初回はとりあえず運用してみます。構築済みのものを配布しているのでそれの使い方を説明します。やることがめちゃくちゃ多いので、根気と知識が必要です。あと説明がかなり雑だと思うので、遠慮なく質問してください。可能な限りお答えいたします。偉大な先駆者様との違い思いっきり参考(というかパクリ)にしていますが、ほぼ別物といえるほど改良を施してます。ScriptでLINEに送信しています。ScriptはLINEのメッセージを受け取り、DiscordにWebHookでメッセージを送信してます。最低限のやり取りは十分にできますが、いくつかの課題があります。Discord側ではWebHookを使用しているため、指定したテキストチャンネルにメッセージを送信、ということができない。LINE側からDiscordへのメンションができない。スタンプ、画像、動画が送れない。BotをグループLINEに入れることが前提で、DMに対応していない。Discord-DiscordAPILINE-グループ、DMホスティングサービスのrailwayを使用して、1つのプログラム内でDiscordBotとLINEBotを同時に稼働させます。LINE側はエンドポイントを増やすことで2つ以上稼働させ、複数のサーバーごとに連携させることが可能です。先駆者様の場合、グループIDが必須でしたが、指定しない場合DMでのやり取りが可能になります。画像、動画はDiscordの場合、CDNで保存されるため、LINE側への送信は簡単なのですが、LINEの場合すべてバイナリデータで返されます。従って画像は画像保存サービスの「Gyazo」、動画は「YouTube」を使用してDiscord側へ送信します。・YouTubeアカウントとAPIキーGyazoとYouTubeに関しては参考リンクを挙げておくので、挙動の確認をお願いします。YouTubeの方は適当な動画をアップロードしてテストしてください。GCPに申請する際、アプリケーションの種類はデスクトップにしておきましょう。2つのjsonファイルが生成されるので控えておきましょう。YouTubeはちょっとハードルが高いので別途記事を書こうと思います。仕様とか例として、Discordサーバーによるコミュニティが二つあるとします。安直ですが、中学時代のコミュニティ、高校時代のコミュニティで、「CHUGAKU」「KOUKOU」で分けます。LINE側も「CHUGAKU」「KOUKOU」でLINEグループを形成させます。Botの配置、データの流れとかは以下の図の通りです。CHUGAKU、KOUKOUそれぞれ同じDiscordBotを配置します。配置したBotは各サーバのメッセージを、railwayで取得します。railway側でメッセージの種類、送信元サーバーを判断し、LINE用にメッセージを変換します。※一部改良前の画面を表示してます。基本的なやりとりメッセージ、画像をこんな感じで送受信できます。メンション、送信先チャンネル指定メンション、送信先チャンネルも指定可能です。(現在、メンションは「@ユーザー名#member」,「@ロール名#role」で指定します。)動画、スタンプLINE側の動画をYouTubeにアップロードさせて、疑似的に動画を共有させてます。プログラム側で標準で限定公開(URLを知ってる人しか見れない)に設定してあるので、プライバシーはあまり気にする必要はないです。時報と警告LINEAPIは無料プランの場合、月1000件の上限が設けられています。これを超える数のメッセージを送ることはできず、活発なサーバーならすぐに上限に達してしまいます。それを防ぐため1日ごとの上限をプログラム内で計算して定めています。テンプレフォークしてデプロイにアクセス。Railwayをクリックこんな感じの画面に遷移するのでDeployをクリック。repositoryにするかはご自由に。)すると早速ビルドが始まります。これだけではまだ動きません。環境変数を設定していないので設定します。環境変数Deploymentsの隣のVariablesから環境変数を設定します。Editorで編集しましょう。こんな感じで直感的に編集できます。環境変数を設定していきましょう。.env.sampleを見てみます。SERVER_NAME=FIVE_SECOND,FIVE_HOURFIVE_SECOND_WEBHOOK=FIVE_SECOND_ACCESS_TOKEN=FIVE_SECOND_CHANNEL_SECRET=FIVE_SECOND_GROUP_ID=FIVE_SECOND_GUILD_ID=FIVE_SECOND_TEMPLE_ID=FIVE_SECOND_NG_CHANNEL=FIVE_HOUR_WEBHOOK=FIVE_HOUR_ACCESS_TOKEN=FIVE_HOUR_CHANNEL_SECRET=FIVE_HOUR_GUILD_ID=FIVE_HOUR_TEMPLE_ID=FIVE_HOUR_NG_CHANNEL=PORT=8080GYAZO_TOKEN=TOKEN=USER_LIMIT=100CLIENT_SECRET_NAME=access_token=client_id=client_secret=refresh_token=project_id=token_expiry=VOICEVOX_KEY=めっちゃ多いです一応役割について説明します。SERVER_NAMESERVER_NAME=FIVE_SECOND,FIVE_HOURDiscordのサーバーを識別するための環境変数です。カンマで区切ることで、複数のサーバーを識別できるようにしてます。上記を例とすると、FIVESECOND,FIVEHOURがカンマ区切りなので、運用されるDiscordサーバーは二つ。その後の環境変数は以下のように命名する。被らなければ何でもいいです。今回は「CHUGAKU」「KOUKOU」なのでSERVER_NAME=CHUGAKU,KOUKOUCHUGAKU_WEBHOOK=KOUKOU_WEBHOOK=と命名しましょう。一つだけ指定する場合はカンマ区切りは不要です。わかりづらいと思うのでプログラム例です。プログラム内ではこういう風に指定してます。os.environ[f"{server_name}_GUILD_ID"]流れ1:server_nameにCHUGAKU,KOUKOUを代入。2:server_listにカンマ区切りでCHUGAKUとKOUKOUをそれぞれ配列として代入。{server_name}_GUILD_IDはそれぞれ「CHUGAKU_GUILD_ID」,「KOUKOU_GUILD_ID」となります。こんな感じで環境変数はプログラムが勝手に割り当ててくれます。それぞれの役割も解説します。_WEBHOOK_WEBHOOKDiscord側のWebhookです。前述した時報と警告機能を投稿します。_ACCESS_TOKEN_ACCESS_TOKEN=LINEBot側のアクセストークンです。例では2つのサーバーで運用しているので、その場合はLINEBotも2つ用意しましょう。_CHANNEL_SECRET_CHANNEL_SECRET=LINEBot側のチャネルシークレットキーです。line-bot-sdkでは必須です。_GROUP_ID_GROUP_ID=LINEのグループIDです。LINEBotがグループに所属していて、そこで発言させたい場合は必須です。設定しない場合は友達登録している人全員にメッセージが送信されます。ちなみに例のCHUGAKU(.env.sampleでのFIVE_SECOND)では設定されていますが、KOUKOU(.env.sampleでのFIVE_HOUR)では設定してません。これによりCHUGAKUにはグループLINEに、KOUKOUは友達登録している人のDMにメッセージを送信します。_GUILD_ID_GUILD_ID=Discord側のサーバーIDです。こちらもサーバーの識別に使用します。それぞれ対応させるサーバーのIDを入れましょう。_TEMPLE_ID_TEMPLE_ID=DiscordのチャンネルIDです。基本ここで設定したチャンネルへLINEからDiscordへ送信されます。_NG_CHANNEL_NG_CHANNEL=LINE側に送りたくないDiscordのメッセージチャンネルを設定します。こちらもSERVER_NAMEと同様にカンマ区切りで複数のチャンネルを指定できます。CHUGAKU_NG_CHANNEL=ログ,管理人用こうすることでCHUGAKU側のチャンネル名が「ログ」、「管理人用」の二つのチャンネルのメッセージがLINEに送られなくなります。定めない場合は存在しないチャンネル名を書き込んでください。こちらも一つだけ指定する場合はカンマ区切りは不要です。はい!これでLINEBot周りの設定は完了です!大体これで半分くらいです。次は他のAPI周りの環境変数になります。USER_LIMITPORT=8080USER_LIMIT=100PORTはflaskでサーバーを立ち上げるためのポート番号です。railway側でflaskを使う場合は必ず宣言する必要があるみたいです。(公式で言及してた)USER_LIMITはDiscordAPIでリクエストを行う際、取得するユーザーの上限を指定してます。値はなんでもよさそうですが、大きいと処理に時間がかかるかもしれません。TOKENTOKEN=DiscordBotのトークンです。出てきた感じですね(笑)GYAZO_TOKENGYAZO_TOKEN=GyazoAPIのトークンです。LINE側から画像が送信された場合、アップロードしてURLに変換します。API関連のものです。事前に生成した2つのjsonファイルから参照します。client_secret{ランダム生成された文字列}.jsonupload_video.py-oauth2.jsonこのjsonファイル、思いっきりGoogleアカウントの認証に使うキーがあるので、外部に漏れたらまずいです。その部分を環境変数に割り当てます。CLIENT_SECRET_NAME「client_secret{ランダム生成された文字列}」(.jsonまでは含まない)が入ります。要するにjsonファイルの名前です。中身はこうなってると思います。(os.environを使ってるのはpython内でjsonを生成するからです。}cilent_id,project_id,client_secretそれぞれ環境変数に該当するものになります。割り当てていきましょう。"oauth2client.client"}upload_video.py-oauyh2.jsonの中身です。access_token,refresh_token,token_expiryの該当する部分にこちらも割り当てます。これでDiscordBot側の最低限の動作ができるようになります。VOICEVOX_KEYVOICEVOX_KEY=ずんだもんで有名なvoicevoxのAPIキーです。必須ではありませんが、割り当てるとずんだもんが「/zunda」で読み上げしてくれます。APIキーの取得方法はこちらから。API側のWebhook前述の通り、LINEBot側は2つ必要です。それぞれにWebhookを設定します。CHUGAKU側のLINEBotはhttps://{railwayのプロジェクトにあるURL}/CHUGAKUKOUKOU側のLINEBotはhttps://{railwayのプロジェクトにあるURL}/KOUKOUと設定します。{railwayのプロジェクトにあるURL}はDeploymentsで確認できます。見れば分かると思いますがSERVER_NAMEで設定した名前がエンドポイントになります。レスポンスが200(成功)と帰ってくるのを忘れずに確認しておきましょう。前述の通り、flask側のプログラムをいじる必要があります。理由として、LINEBot1つにつき1個、エンドポイントを増やさなければならないからです。(本当は自動生成させたかったんだけどエラー吐きまくってダメでした。cloneでフォークしたテンプレをローカルに落とします。ディレクトリをapp/serversへ移動させるとmain_server.pyfive_hour.pyの2つのPythonファイルがあると思います。main_server.pyがFIVE_SECOND側のLINEBotのプログラムで、five_hour.pyがFIVE_HOUR側のプログラムになります。Flask(__name__)app.register_blueprint(app2)17行目は同階層のfive_hour.pyをインポートし、app2として扱います。app.register_blueprint(app2)で分割されているプログラムを統合しています。する事でLINEBotを分割しつつ二つホストしています。1つだけ運用したい場合1つだけ運用したい場合、説明した部分を削除し、環境変数SERVER_NAMEからも消しましょう。例としてCHUGAKUだけ運用したい場合はこうします。app.register_blueprint(app2)環境変数から削除SERVER_NAME=CHUGAKU3つ以上運用したい場合3つ以上運用したい場合、このように追加していきます。Discordのサーバー名は「DAIGAKU」にしておきます。新しいPythonファイルを作成します。名前はなんでもいいですが、ここではdaigaku.pyにします。2.コード追加daigaku.pyに以下のコードを追加します。2022/10/02追記:友達登録していない場合グループメッセージが読み取られない不具合を修正。)2022/09/30追記:友達登録していない場合グループメッセージが読み取られない不具合を修正。友達登録しているユーザーのみが読み取り可能だったため。3.コード追加app/servers/main_server.pyにコードを追加します。環境変数SERVER_NAMEにDAIGAKUを追加します。SERVER_NAME=CHUGAKU,KOUKOU,DAIGAKU5.LINEWebHookの設定LINEBot側のWebhookを指定します。https://{railwayのプロジェクトにあるURL}/DAIGAKU以上です。4つ目以降も追加する場合も同様にやります。server_nameはserver_list[3],server_list[4]app2,app3,app4と追加するたびに変更するのを忘れずに。完成!!これで完成です。やりとりできると思うのでなんか試しに送ってみましょう。