0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Discord BotをGoで作ってみた

0
Posted at

はじめに

ふとディスコ―ドボットを作ってみたいと思いましたが、ディスコ―ドボットを作るのにメジャーな言語であるPythonやJavaScriptで作るのは面白くないので今回はGoでディスコ―ドボットを作ってみたいと思います。

ディスコードボットに最低限必要な機能

ディスコードボットは最低限機能するために以下のような機能を持つ必要があります。

  • トークン
    トークンはBotがDiscord APIにログインするために必要なパスワードのようなものであり、コード内でこの発行されたトークンを使用しないと機能しない。
  • セッションの確立
    BotがDiscord APIの機能(メッセージの送信、チャンネルの編集など)を呼び出すために使用するツール。
    セッションの開始と終了が必要。
  • 起動確認
    Botがセッションを確立した時に一度だけ呼び出されるイベント。Botが正常に起動したことを確認するため、ターミナルにログを残す機能。(なくても問題ないがあった方が安心)
  • 必要なIntentの有効化
    どのようなIntent(通知)をbotが受け取るかの設定。
  • イベントハンドラの登録
    特定のイベントが起こったときに呼び起される関数を登録する。
  • ロジックの実装
    ユーザーからのメッセージを確認し、それがコマンドであるかを判定するロジックを実装する。

ライブラリを入れよう

開発環境
Go version: go1.25.1
まずプロジェクトファイルを以下のコマンドで作成します。

go mod init discord_bot

次にGo言語でDiscord botを作るのに必要なライブラリであるDisGoをインストールします。
DisGoのGithubリンク
このリンクでライブラリのインストール方法とサンプルコードが丁寧に書いてくれています。

go get github.com/disgoorg/disgo

上記のコマンドでgo.modの中身にDiscord botに必要なライブラリが追加されると思います。

go.modの中身
require (
	github.com/disgoorg/disgo v0.18.16 // indirect
	github.com/disgoorg/json v1.2.0 // indirect
	github.com/disgoorg/snowflake/v2 v2.0.3 // indirect
	github.com/gorilla/websocket v1.5.3 // indirect
	github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
	golang.org/x/crypto v0.31.0 // indirect
	golang.org/x/sys v0.28.0 // indirect
)

サンプルコードを実装してみよう

一旦main.goにDisGoのGithubに記載されているサンプルコードをコピペします。

サンプルコード
package main

import (
	"context"
	"os"
	"os/signal"
	"syscall"

	"github.com/disgoorg/disgo"
	"github.com/disgoorg/disgo/bot"
	"github.com/disgoorg/disgo/events"
	"github.com/disgoorg/disgo/gateway"
)

func main() {
	client, err := disgo.New("token",
		// set gateway options
		bot.WithGatewayConfigOpts(
			// set enabled intents
			gateway.WithIntents(
				gateway.IntentGuilds,
				gateway.IntentGuildMessages,
				gateway.IntentDirectMessages,
			),
		),
		// add event listeners
		bot.WithEventListenerFunc(func(e *events.MessageCreate) {
			// event code here
		}),
	)
	if err != nil {
		panic(err)
	}
	// connect to the gateway
	if err = client.OpenGateway(context.TODO()); err != nil {
		panic(err)
	}

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)
	<-s
}
ぱっと見何が行われているのかよく分からないのでそれぞれのコードをGemini君に分解してもらいました。
func main() {

プログラムが最初に実行される関数(エントリポイント)の始まりです。

client, err := disgo.New("token",

Discordボットのクライアントオブジェクトを作成します。"token"の部分には実際のボットトークンが入ります。

bot.WithGatewayConfigOpts(

クライアント設定のうち、Discordとの接続(Gateway)に関する設定を開始します。

gateway.WithIntents(

Gateway経由でボットが受け取るイベントの種類を設定します(インテント)。

gateway.IntentGuilds,

サーバー(ギルド)関連のイベントを許可します。

gateway.IntentGuildMessages,

サーバー内のメッセージ関連のイベントを許可します。

gateway.IntentDirectMessages,

DM(ダイレクトメッセージ)関連のイベントを許可します。

bot.WithEventListenerFunc(func(e *events.MessageCreate) {

ボットのイベントハンドラ(リスナー)を設定します。ここでは特に新しいメッセージが作成されたときのイベント(events.MessageCreate)に対応する処理を登録しています。

// event code here

メッセージが投稿されたときに実行したいカスタムコード(例: メッセージへの応答)を記述する場所です。

if err != nil { panic(err) }

クライアントの初期化中にエラーが発生した場合、プログラムを強制終了(パニック)させます。

if err = client.OpenGateway(context.TODO()); err != nil {

DiscordのGatewayに接続し、イベントの受信を開始します。context.TODO()は一時的なコンテキストです。

panic(err)

Gatewayへの接続に失敗した場合、プログラムを強制終了させます。

s := make(chan os.Signal, 1)

OSからのシグナル(信号)を受け取るためのチャネル(s)を作成します。バッファサイズは1です。

signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)

Ctrl+C(SIGINT)や終了コマンド(SIGTERM)といった終了シグナルが送られたときに、それをチャネルsに送信するようOSに通知します。

<-s

チャネル s からシグナルが送られてくるのを待機します。プログラムはここで ブロック(一時停止) され、シグナルを受信するまで実行が続行されます。これにより、ボットはバックグラウンドで接続を維持し続けることができます。

サンプルコードではセッションの確立から停止までの流れを書いてくれているようです。 // event code here の行でロジックの実装をするみたいですね。

pingpong処理とトークンを.envファイルに入れよう

.envファイルとは?

プログラムのコードに直接disocrd botのトークンのような外部に公開したらまずいものを直接コードに書かずに.envという外部ファイルに保存する仕組みです。
.envの中身の例

DISCORD_BOT_TOKEN = xxxxx

こうすることでgitignoreに.envを登録しとくとgithubなどで公開する場合にトークンの漏洩を防ぐことができます。
このライブラリを入れることでgo言語で.envを使えるようにします。

go get github.com/joho/godotenv

pingpong処理とは

クライアント側でpingと入力したらbot側がそのメッセージを検出してpongを返す簡単なプログラムを書いてみましょう。これは基本的な処理であるクライアント側にインプットに対してサーバー側がアウトプットを出すという処理の練習にもなります。

以下がpingpon処理のサンプルコードになります。

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/disgoorg/disgo"
	"github.com/disgoorg/disgo/bot"
	"github.com/disgoorg/disgo/discord"
	"github.com/disgoorg/disgo/events"
	"github.com/disgoorg/disgo/gateway"
	"github.com/joho/godotenv"
)

func main() {

	if err := godotenv.Load(); err != nil {
		log.Println("could not find .env file")
	}

	token := os.Getenv("DISCORD_BOT_TOKEN")

	client, err := disgo.New(token,
		// set gateway options
		bot.WithGatewayConfigOpts(
			// set enabled intents
			gateway.WithIntents(
				gateway.IntentGuilds,
				gateway.IntentGuildMessages,
				gateway.IntentMessageContent,
			),
		),
		// add event listeners
		bot.WithEventListenerFunc(func(e *events.MessageCreate) {
			if e.Message.Author.Bot {
				return
			} else if e.Message.Content == "ping" {
				e.Client().Rest().CreateMessage(e.ChannelID, discord.NewMessageCreateBuilder().SetContent("pong").Build())
			}
		}),
	)
	if err != nil {
		panic(err)
	}
	// connect to the gateway
	if err = client.OpenGateway(context.TODO()); err != nil {
		panic(err)
	}

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)
	<-s
}

権限設定

gateway.WithIntentsで、サーバー(Guild)の情報やメッセージの内容(IntentMessageContent)を読み取るための必須権限を設定しています。

処理の内容

if e.Message.Author.Bot { return }: 最初にBot自身のメッセージや他のBotのメッセージを弾くことで、無限ループに陥るのを防いでいます。

else if e.Message.Content == "ping": メッセージの内容が完全に「ping」と一致した場合のみ、APIを叩いて同じチャンネル(e.ChannelID)に「pong」と返信しています。

おわりに

今回はGo言語と「DisGo」ライブラリを使用して、簡単なPing-Pong処理を返すDiscord Botを作成しました。

Discord Bot開発といえばPythonやJavaScript(Node.js)が主流ですが、Go言語を使うことで、静的型付けの安心感や実行速度の速さ、そして強力な並行処理の恩恵を受けながら開発を進めることができるのではないでしょうか。Golangでディスコードボットを作って遊んでみてください!最後まで読んでいただき、ありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?