ご挨拶
お初にお目にかかります。0x00000Exと申します。普段はGoやTypeScript、Pythonでdiscord botを創ったり、C++でゲーム作ったりしています。趣味でプログラミングをやっていますので知識や経験に偏りがあって、これから書いていくであろうこのQiitaのページの内容にもその傾向が出てしまうかもしれませんし、このようなノートを書くのは初心者なので至らぬ点があるとは思いますが、その都度ここがわからない、ここはこうしたほうがわかりやすい、などコメントをいただきますと非常にありがたいです。
さて、最初に書きますのはいきなり備忘録でございます。需要がどこまであるのかは未知数ですが私のように知識や経験に飢えるプログラマーのためになればと思っております。
それでは、本題へまいりましょう
本編
使用したシステム・言語・サービスなど
ホスティング
- ubuntu server
言語・ライブラリ
- Go
- Tempest
サービス
- Cloudflare tunnel(会員登録なんかはしてません)
Tempestって何?
github.com/amatsagu/tempest
amatsagu氏が作成しているGo言語のdiscordのAPIラッパーです。discordgoの方が有名ですが、公式ドキュメントにてその名前を見たので今回使ってみます。
Exampleを見る限り、コマンドの定義やインタラクションハンドラーの書き方が非常にわかりやすいのと、この記事を書いている時点でdiscordgoが公式のAPIに追いついていなかったので用途によってはTempestを使うのが有利かなと思います。
準備したこと
ホスティングするサーバー
- Goのインストール
- Tempestのインストール
- Cloudflare tunnelのインストール
ホスティングするサーバー外
- Discord Developer Portalでアプリケーションを作成してサーバーに導入、
Token,Public Keyを取得しておく - BotをテストするDiscord サーバーのサーバーID
それぞれの詳しいやり方なんかは今回省きます。特にCloudfalre tunnelのインスト―ルちょっと手間かかった。
ファイル構成
今回はテストなのですごくシンプルです
.env // TokenやPublic Key、テストサーバーのIDなど入れます
commands.go // テストするコマンドをここに書きます
main.go
go.mod
go.sum
- .env
リポジトリのテンプレートのまんまです
DISCORD_BOT_TOKEN="自分のBotのトークン"
DISCORD_PUBLIC_KEY="自分のBotのPublic key"
DISCORD_TEST_SERVER_ID="テストするサーバーのサーバーID"
APP_ADDRESS="127.0.0.1:8080"
これをコピーしてもらっても構いません。ただしAPP_ADRESSはそのままにしてください
- main.go
これもリポジトリのテンプレートをほぼパクってます
package main
import (
"log"
"net/http"
"os"
"github.com/amatsagu/tempest"
"github.com/joho/godotenv"
)
func loadComamnds(c *tempest.BaseClient){
c.RegisterCommand(Ping)
testserverID, err := tempest.StringToSnowflake(
os.Getenv("DISCORD_TEST_SERVER_ID"),
)
if err != nil {
log.Fatalf("サーバーIDをsnowflakeに変換するのに失敗しました: %v", err)
}
err = c.SyncCommandsWithDiscord([]tempest.Snowflake{testserverID}, nil, false)
if err != nil {
log.Fatalf("コマンド登録に失敗しました: %v", err)
}
}
func startHTTP(addr string, trase bool) error {
client := tempest.NewHTTPClient(tempest.HTTPClientOptions{
Trace: trase,
BaseClientOptions: tempest.BaseClientOptions{
Token: os.Getenv("DISCORD_BOT_TOKEN"),
},
PublicKey: os.Getenv("DISCORD_PUBLIC_KEY"),
})
loadComamnds(client.BaseClient)
http.HandleFunc("/interaction", client.DiscordRequestHandler)
return http.ListenAndServe(addr, nil)
}
func main(){
log.Println("起動開始")
if err := godotenv.Load(".env"); err != nil {
log.Fatalf("環境変数の読み込みに失敗しました: %v", err)
}
log.Println("クライアントを作成します")
if err := startHTTP(os.Getenv("APP_ADDRESS"), true); err != nil {
log.Panicln(err)
}
}
- commands.go
これはもはやコピペ
package main
import (
"log"
"time"
"github.com/amatsagu/tempest"
)
var Ping tempest.Command = tempest.Command{
Name: "ping",
Description: "send test message to check discord api latency and returns latency value.",
SlashCommandHandler: h,
}
func h(itx *tempest.CommandInteraction){
if itx.GatewayClient != nil {
_, latency, err := itx.GatewayClient.Gateway.ShardDetails(itx.ShardID)
if err != nil{
log.Println("failed to fetch current shard details: ", err)
itx.SendLinearReply("failed to fetch shart details", false)
return
}
itx.SendLinearReply("current average latency: " + latency.String(),false)
return
}
startedat := time.Now()
_, _ = itx.HTTPClient.FetchUser(itx.HTTPClient.ApplicationID)
finalLatency := time.Since(startedat)
itx.SendLinearReply("Current network + Discord internal latency: " + finalLatency.String(), false)
}
Exampleではハンドラー関数を無名関数のように構造体の中に入れていますが、私は別で書いた方がわかりやすいかなと思います。
起動までの手順
Botを起動する。
go run .などでまず起動します。
Cloudflare tunnelを起動する
cloudflared tunnel --url http://localhost:8080をターミナルで動かします。そうすると大量のログがでてきますが、落ち着いて、
2026-08-07T02:10:37Z INF +--------------------------------------------------------------------------------------------+
2026-08-07T02:10:37Z INF | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |
2026-08-07T02:10:37Z INF | https://<url>.trycloudflare.com |
2026-08-07T02:10:37Z INF +--------------------------------------------------------------------------------------------+
の部分を探して https://<url>.trycloudflare.comのリンクをコピーします。
エンドポイントURLとして登録する
Developer Poratlに行きます。使うBotのページにいって General Infomation > Interaction EndPoint URLの欄にhttps://<url>.trycloudflare.com/interactionと、末尾に/intaractionをつけたものを入力します。
終わったらSave Changeを押す、もしBotとの通信が成功すれば、変更はちゃんと保存されます!
コマンドを使ってみる
起動して、登録できたら実際にコマンドを使ってみましょう!テスト用のサーバーにいくとBotがオフラインのままになっていますが、安心してください。うまくいっているなら、コマンドの一覧にPingコマンドが見えているはずです。あとは実行して結果が返ってきたら大成功!初めてのHttpによるインタラクション処理ができました!
ところで、Httpで処理するメリットって何?
私自身の口から説明できることはないです。今回取り上げた、もとい試したのも単純な興味です。tempestのreadmeやAIによれば、スケーリングのしやすさやコストの低減などがあるようですが、小規模であればGatewayというdiscordgoが使う接続手法でも対して変化はありませんし、httpを使う方法ではインタラクションしか処理できない分、gatewayに比べてできることは少ないです。特に、メッセージの送信を感知したりVCへの接続はgatewayしかできないので、音楽botや読み上げ、荒らしへの自動対処などと言った用途でhttpは使えないです。
そもそも、httpとgatewayでは接続の仕方が異なっていて、httpはリクエストの数だけdiscordとつながりますが、gatewayではずっとつながりっぱなしって感じです。
なので規模に比例して差が出ると思いますが、先にも言ったように友達と使うだけ、みたいな小規模なものであれば、httpを無理に使う必要もなくむしろgatewayを使うべきでしょう。
最後に
最後まで読んでいただきありがとうございます。これからも不定期ながら自分が興味を持った事項について備忘録的な観点で書いていこうと思います。よろしければコメントやいいねなどを送ってくださるとモチベになります!