実践形式で学ぶ! Go言語で作成するdiscordbot 1章: 基礎編
はじめに
本記事では、以下の流れでdicordbotをGo言語で作成する方法について紹介します。
- Botユーザーの作成
- サーバーへの招待
- 最小実装(オンラインにするだけ)
- オウム返しBot
- 小規模でも保守しやすいフォルダ分割
- トラブルシューティング
この記事で作成するプログラムは下記githubリポジトリで公開しています。
前提条件
- Discordアカウントを所持していること
- Botを招待できるDiscordサーバーがあること
- Go 1.25以降
1. Botユーザーの作成
-
右上の「New Aplication」をクリックする。

-
名前を入力してポリシーに同意したら「Create」ボタンを押下する。

-
左サイドバーのから「Bot」を開き、「Reset Token」を押下する。
初回は「Reset Token」で新規発行の扱いになります。

-
Yes, do it!

-
discordのパスワードを入力する(他の方法で認証してもOK)。

-
「Copy」を押下して、パスワードを控える。

トークンは機密情報です。公開リポジトリにコミットしたり、第三者へ共有したりしないでください。
-
「Bot」タブのまま、ページの下の方に行き、「Message Content Intent」を有効にする。

-
「Save Changes」を押下して設定を保存する。
2. サーバーへの招待
-
左サイドバーから「OAuth2」タブを開き、
OAuth2 URL Generatorセクションまで移動する。 -
ScopesのBotにチェックをつける。

-
Bot Permissionsは以下のみチェックをつける(最小権限)。
- Send Message
- Read Message History
-
ページ最下部の「Generated URL」にアクセスして招待したいサーバーにbotを招待する。

-
用意したサーバーにbotが参加したら完了です。

3. 最小実装(オンラインにするだけ)
- goモジュールの初期化
go mod init github.com/orchidee9392/discordbot-test
go get github.com/bwmarrin/discordgo@latest
goモジュール名の
github.com/orchidee9392/discordbot-testは自身のリポジトリに合わせて変更してください。
- 実装
main.go1ファイルにすべて実装します。
package main
import(
"log"
"os"
"github.com/bwmarrin/discordgo"
)
func main(){
//トークンの取得
token := os.Getenv("DISCORD_TOKEN")
if token == ""{
log.Fatal("DISCORD_TOKEN が未設定です")
}
//Discordセッションの生成
session, err := discordgo.New("Bot " + token)
if err != nil{
log.Fatal(err)
}
//Intentsの設定
session.Identify.Intents = discordgo.IntentsGuilds
//セッション開始
if err := session.Open(); err != nil{
log.Fatal(err)
}
defer session.Close()
log.Println("botが接続されました。")
fmt.Println("Enterで終了...")
fmt.Scanln()
}
- 起動
- macOS / Linux(ターミナル)
export DISCORD_TOKEN="ここにBotトークン"
go run .
- Windows(PowerShell)
$env:DISCORD_TOKEN="ここにBotトークン"
go run .
サーバーのメンバー一覧で Botがオンライン になっていれば成功です。
Enterで終了します。

4. オウム返しBotの作成
オウム返しBotもmain.go1ファイルで実装してみます。
package main
import (
"fmt"
"log"
"os"
"github.com/bwmarrin/discordgo"
)
func main() {
//トークンの取得
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatal("DISCORD_TOKEN が未設定です")
}
//Discordセッションの生成
session, err := discordgo.New("Bot " + token)
if err != nil {
log.Fatal(err)
}
//Intentsの設定
session.Identify.Intents = discordgo.IntentsGuilds |
discordgo.IntentsGuildMessages |
discordgo.IntentsMessageContent
//メッセージ受信ハンドラを登録
session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
// Botの発言には反応しない(無限ループ防止)
if m.Author.Bot {
return
}
_, _ = s.ChannelMessageSend(m.ChannelID, m.Content)
})
//セッション開始
if err := session.Open(); err != nil {
log.Fatal(err)
}
defer session.Close()
log.Println("botが接続されました。")
fmt.Println("Enterで終了...")
fmt.Scanln()
}
メッセージ内容を受信・送信するには、Developer Portal側でMessage Content Intentを有効にし(手順1-8)、コード側でもIntentとしてIntentsMessageContentを追加する必要があります。
5. 小規模でも保守しやすいフォルダ分割
規模が大きくなくても、責務毎にファイルを分けておくと長期運用が容易になります。
正解はありませんが、以下に分割例を記します。
.
├── README.md
├── cmd
│ └── sample
│ └── main.go
├── go.mod
├── go.sum
└── internal
├── app
│ └── bot.go
├── config
│ └── config.go
└── handlers
└── message_create.go
internal/config/config.go
設定を表すクラスです。
package config
import (
"fmt"
"os"
)
//設定構造体
type Config struct {
Token string
}
//環境変数を読み込んで設定structを作成します
func FromEnv() (Config, error){
t := os.Getenv("DISCORD_TOKEN")
if t == "" {
return Config{}, fmt.Errorf("DISCORD_TOKEN が設定されていません。")
}
return Config{Token: t}, nil
}
internal/app/bot.go
discordbotを表すクラスです。
package app
import "github.com/bwmarrin/discordgo"
// ボット構造体
type Bot struct {
session *discordgo.Session
}
// 初期化関数
func New(token string, intents discordgo.Intent) (*Bot, error) {
s, err := discordgo.New("Bot " + token)
if err != nil {
return nil, err
}
//Intents未指定なら最小のIntentsを与える
if intents == 0 {
intents = discordgo.IntentsGuilds
}
s.Identify.Intents = intents
return &Bot{session: s}, nil
}
// 接続開始
func (b *Bot) Start() error {
return b.session.Open()
}
// 接続終了
func (b *Bot) Stop() {
_ = b.session.Close()
}
// メッセージ作成イベントのハンドラを登録
func (b *Bot) OnMessageCreate(h func(*discordgo.Session, *discordgo.MessageCreate)) func() {
return b.session.AddHandler(h)
}
internal/handlers/message_create.go
メッセージ作成のイベントハンドラを表すクラスです。
package handlers
import "github.com/bwmarrin/discordgo"
//オウム返しするイベント
func Echo(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.Bot {
return
}
_, _ = s.ChannelMessageSend(m.ChannelID, m.Content)
}
cmd/sample/main.go
メインクラスです。
package main
import (
"fmt"
"log"
"github.com/bwmarrin/discordgo"
"github.com/orchidee9392/discordbot-tutorial/internal/app"
"github.com/orchidee9392/discordbot-tutorial/internal/config"
"github.com/orchidee9392/discordbot-tutorial/internal/handlers"
)
func main() {
//設定ロード
cfg, err := config.FromEnv()
if err != nil {
log.Fatal(err)
}
//使用イベント設定
intents := discordgo.IntentsGuilds |
discordgo.IntentsGuildMessages |
discordgo.IntentsMessageContent
//Bot生成
bot, err := app.New(cfg.Token, intents)
if err != nil {
log.Fatal(err)
}
//ハンドラ登録
removeEcho := bot.OnMessageCreate(handlers.Echo)
defer removeEcho()
//接続
if err := bot.Start(); err != nil {
log.Fatal(err)
}
defer bot.Stop()
log.Println("接続完了")
fmt.Println("Enterで終了...")
fmt.Scanln()
}
以上で、discordbotの作成から最小動作、簡単なメッセージ処理、フォルダ分割までを紹介しました。
次章以降では、スラッシュコマンドなどについて紹介していきたいと思います。
6. トラブルシューティング
| 症状 | 主な原因 | 対処 |
|---|---|---|
| オンラインにならない | トークンが間違っている/ネットワークがつながっていない | トークンの確認、ファイアウォール・プロキシ確認 |
| Botがメッセージを受け取っていない | Message Content Intent 未有効(Portal側 or コード側) | Portalで有効化 → Save、コードで IntentsMessageContent を追加 |
| Botからメッセージを送信できない | チャンネル権限不足 | サーバー側で Bot のロールに View/Read/Send を付与 |
Discussion