💬

クラスの Discord サーバー向けの役立つ Bot を 3 種類自作した話

2023/05/14に公開

はじめに

キュレェです。普段は高専という謎の教育機関の 3 年生(2023 年現在)として暮らしています。

弊学は学科の配属がなぜか 3 年生にあり、やっと待望の情報系の学科に配属されました。高専はオタクを受け入れやすい風土であるからか、Discord サーバーもある程度活発です。

クラスのはじめに大声を出すなど、自主性が強かったので Discord の管理人になってしまいました。管理人の特権でサーバーを管理できるわけだし、Node.js の勉強がてらに Bot を作ってみよう[^1]という感じになりました。

以下、作った Bot を GitHub リポジトリとともに紹介します。

次の日の時間割を通知する Bot

https://github.com/Kyure-A/pttb

説明

スプレッドシートに時間割表を書き込み、JSON に加工してから Webhook に Post するといい感じになります。本当にそれだけしかしていないので詳しい説明は割愛します。コード見てください。

コード

https://github.com/Kyure-A/pttb/blob/master/src/modules/index.ts

電車の運行情報を通知する Bot

https://github.com/Kyure-A/ntdb

説明

Yahoo! 乗換案内 > 運行情報 > 近畿 にある各路線の HTML をスクレイピングしたものを Parse して JSON に加工してから Webhook で Discord に Post しています。

TypeScript で開発する都合上 Clasp を使っているのですが、GAS が用意してくれている HTML Parser の情報が types にないためコンパイルエラーが発生してしまいます。そのため、Webpack で Node.js 環境で使える HTML Parser を Bundle したコードを push しています。(追記: 書いた当時はなかったと思ってたけど、今見るとあるかもしれん......、まあ動くからいいじゃん)

コード

https://github.com/Kyure-A/ntdb/blob/master/src/modules/index.ts

現在出されている課題を管理する Bot

https://github.com/Kyure-A/mhnb

説明

ここからが本命です。前述した Webhook を用いた通知 Bot は GAS のみで完結していましたが、ここからは GAS + Glitch で運用しています。
Glitch で使える Node.js は少し古くて、v16 らへんしか使えません。asdf を使うとバージョン管理は一任できるのでいいです。

つまづきポイント

  • node server.js を実行しないといけない

    • まあ当然なんですが、ファイルは実行しないと意味がないです。
  • monorepo 管理

    • /gas と /glitch を monorepo っぽく管理するとき、npm workspace と git subtree を使わないといけないのがかなり面倒でした。
  • CommonJS と ECMA Script の違い

    • とりあえず Glitch 側では Node.js を使うので、CommonJS のコードを吐くようにすれば Node.js だと動いてくれます。CommonJS では基本的には require を使わないといけないらしいけど import でも動きます。
    • ECMA Script は GAS 側で使います
  • async/await をつけた doGet, doPost

    • doGet と doPost は特殊な関数として名前が予約されていて、ウェブアプリとして実行した際に GET/POST を受け取って処理を実行してくれます。しかし、async/await をつけて同期処理を行う関数にしていると途端に動かなくなりました。
    • 本来 doGet/doPost は GoogleAppsScript.Content.TextOutput または GoogleAppsScript.Content.HtmlOutput のみが return できる型として許されています。
    • パッと見だと本来返すべき型を返り値にしている関数ですが、async/await をつけると Promise に包んで返すので、Promise<GoogleAppsScript.Content.TextOutput> または Promise<GoogleAppsScript.Content.HtmlOutput> を返してしまいます。
    • doGet/doPost から「スクリプトが完了しましたが、返された値はサポートされている戻り値の型ではありませんでした。」が返ってきたが、GoogleAppsScript.Content.TextOutput または GoogleAppsScript.Content.HtmlOutput を返しているはずだというときは async/await がついていないか確認したほうがいいです。
      (これググっても出てこなかったんだけど、みんな async/await をつけて処理しないのかなあ、それとも俺がバカなだけ?)
  • axios.get で返ってくる response
    response.data がまさか受け取った JSON の文字列をデフォルトで parse してくれているとは思わなかったです。axios、かなり偉い

  • Glitch (remote) と PC (local) のリポジトリでの *.ts から生成した *.js の管理
    remote でファイルをいじると local のリポジトリと Conflict をしてしまうので、一時期は src から生成した JavaScript ファイルをそのまま リポジトリに Push していましたが、やはり Glitch 側でコンパイルしたほうがいいと思ったので、空の commit を pull することを許容していい感じにしました(詳しくは README を見るとわかりやすい)

コード

SlashCommand の一例

https://github.com/Kyure-A/mhnb/blob/master/glitch/src/commands/create.ts

サーバー側のコード

https://github.com/Kyure-A/mhnb/blob/master/glitch/src/server.ts

参考にした記事群

https://qiita.com/50m_regent/items/68395d604b03cb570d76
Discord.py なので直接コードを参考にしたわけではないですが、先行例があったので書きました。

https://qiita.com/OJII3/items/cf8188b69bc7f00c5fbc
Glitch をサーバーとして使う小ネタが載っていてよかったですが、ちょっと Bot 側の説明が簡素すぎるかなあという感想があります。

https://zenn.dev/fuwari/articles/3bac30678b01e2
Discord.js で書かれた Bot についての全体像が把握しやすい記事構成で、わかりやすかったです。

https://www.geeklibrary.jp/counter-attack/discord-js-bot/
調べた中だと一番体系立てて説明されていてかなりよかったです。

https://discordjs.guide/interactions/modals.html
https://discordjs.guide/popular-topics/embeds.html
modal と embed に関しては Discord.js の公式ドキュメントを見るほうがよかったです。

https://www.memory-lovers.blog/entry/2022/05/31/110000
前述したつまづきポイントの 3 項目はこの記事で気づきました

余談

  • リポジトリの名前が思いつかないのでアクロニムっぽくするのって俺だけでしょうか(英語がガバなのでだめ)
  • 高専を topics に加えようとして先例の記事を見たら半分くらいお気持ちポエムで怖くなりました
  • つまづきポイントの章が書きたかっただけです

Discussion