この記事は 新歓ブログリレー2025 22日目の記事です。
25Bの皆さん、合格おめでとうございます!
23Bの dye (ダイ) といいます。よろしゅう。
去年はB1の振り返りを記事にしていましたが、今年は BOT を作っていきます。
初心者なのであまり刺さないでーー…
はじめに
さて、皆さんは「謎解き」をやったことはありますか?
謎解きとは、「ひらめきを駆使して、与えられた問題を解き進めることで、目標達成を目指す体験型アトラクション」のことです。
ひとくちに謎解きといっても、家で遊べるものや、街を歩きながら問題を解き進めていくもの、実際に部屋に閉じ込められてしまうもの...などいろいろな種類があります。
こうした、多くの謎解きイベントの共通点に、ネタバレの禁止があげられます。
ネタバレを踏んでしまうと、解き方が事前に分かってしまったり、結末を先に知ってしまったりと体験を大きく損なってしまうためです。
するとどうなるか、感想で言えるが大きく限られてしまいます。イベントの中身に触れてはいけないため、「面白かった!」「好きな公演だった!」とか薄っぺらい感想になってしまうわけです。
ですが、既に公演に参加した友達と「どこが難しかったー」とか「ここが好き」とか話したい!( traP では一つのイベントに対して複数のグループができることが多々あります)しかし、大学で会っても他の人がいますし、イベントに参加した人たちだけで集まるのは難しいです。また、オンライン上で会話しようにも、traQ(部内SNS) には公開チャンネルとDMしか存在しません。
そこで今回は Discord のプライベートチャンネルを使って、ネタバレを気にせずに、謎解きの感想を言える場所を作っていきます。
イベント名のロールを作り、そのロール所持者のみが閲覧できるプライベートチャンネルを作ることで求めていた環境を作ることができます。しかし、いちいちロールの管理画面を開いて、ロールを作ってーーーと工程が多く面倒です。
これらの工程を楽にできるBOTを作ることが今回の目的です。
本編
前置きが長くなりましたが、ここからが実装パートです。
Discord BOT は様々な言語で書くことができますが、今回は Go を使っていきます。
環境構築はなろう講習第一部のテキスト等を参考にするといいです(なろう講習は traP 内で行われる、初心者向け講習の一つ)。
BOT の設定
では、Discord の BOT を作っていきましょう。
まずは、discord.dev にアクセスして、New Application をクリックし、BOT名を入力すると、My Applications に追加されます。追加されたアプリケーションを選択してください。
左側から Bot を選択し、トークンを取得します。Reset Token を押すと、確認ののちトークンが表示されます。後で使うのでどこかにメモしてください(再表示されません)。また、トークンは絶対に公開しないようにしましょう。
次に、OAuth2 で権限の設定をします。
OAuth2 URL Generator から bot を選択し、下にスクロールして必要な権限をチェックしましょう(権限は後で設定することもできます)。権限は必要最低限にすることをおすすめします。
そして、一番下の GENERATED URL をコピーしてアクセスすることで、Discord サーバーに招待することができます(管理者権限が必要です)。
コードを書く
go で discord BOT を作る際は、discordgo が便利です。
ドキュメントやコードサンプルを参考にコードを書いていきましょう。
↓のコードを拡張していきます
package main
import (
"log"
"os"
"os/signal"
"syscall"
"github.com/bwmarrin/discordgo"
"github.com/joho/godotenv"
)
func main() {
err := godotenv.Load(".env")
if err != nil {
log.Fatal(err)
}
// TOKEN は先ほど取得した文字列
TOKEN := os.Getenv("TOKEN")
dg, err := discordgo.New("Bot " + TOKEN)
if err != nil {
log.Fatal(err)
}
// ... はイベントハンドラ
dg.AddHandler(...)
dg.AddHandler(...)
dg.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsGuildMessageReactions
err = dg.Open()
if err != nil {
log.Fatal(err)
}
log.Println("Bot is running")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-sc
log.Println("Bot is stopping")
err = dg.Close()
if err != nil {
log.Fatal(err)
}
}
今回の BOT は
- /send_channel {{チャンネル名}} で招待投稿を送信するチャンネルを設定する
- /new_event {{イベント名}} で
- {{イベント名}}ロールを作る
- {{イベント名}}チャンネルを作る
- ↑のチャンネルの閲覧権限を設定する
- 招待投稿を送信する
- 招待投稿に👍をつけた人に{{イベント名}}ロールを与える
- {{イベント名}}チャンネルに、参加通知を送る
- /delete_event {{イベント名}} でロール、チャンネルを削除する
機能を作りました。
ここからは実装していくうえで困ったことを話していきます。
コードの全文を上げるのは長くなるので git リポジトリ を見てください(Go 初心者なのでコードが汚いかも...)。
m.Content が空
症状: 完成版には残っていませんが、送信されたメッセージの内容を取得(MessageCreate)しようとしたとき、内容(m.Content)が空になる。
解決法: discord.dev の bot から、Privileged Gateway Intents を有効にする。
コマンドが反応しない
症状: スラッシュコマンドはサジェストが出るようになっているが、自作コマンドがその候補に出てこない
解決法: ApplicationCommandCreate の第2引数(GuildID)を空文字列にすることで、グローバルコマンド(どのサーバーでも使えるコマンド)になるが、反映に時間がかかる。GuildIDを設定すれば即時反映されるが、そのサーバーでしか使うことができない。
チャンネル名がうまく取得できない
症状: アルファベットを含むイベント名を入れると、参加通知が送られない。
解決法: Discord のチャンネル名はアルファベットが自動的に小文字に変換されてしまう仕様が原因。
イベント名からチャンネルIDを検索するときに、イベント名を小文字に変換することで解決した。
注意点
Discord のプライベートチャンネルはロールによる閲覧権限をつけることができますが、サーバー所有者、管理者はすべてのチャンネルを見ることができます。
そのため、サーバー所有者は自分の参加していないイベントのチャンネルを見ないようにする必要があります。自分はサーバー所有用にアカウントを新しく作り、ログアウトしました。
完成
招待チャンネルを設定して...イベント名を入力して...
参加した人がリアクションを付けると...
そのイベントのチャンネルに招待される!
おわりに
いかがでしたか?
前から興味があったBOT制作をこの期にやってみました。
謎解きもBOT制作も、興味を持ったらぜひ挑戦してみてくださいねー
明日はの担当は kamecha さんです。お楽しみに~