目標
- Embedにローカルで保存されている画像を使う
やること
- メッセージが作成されたとき、ローカル画像を添付したEmbedを送信する
使うもの
※環境構築は割愛
ディレクトリ構成
project_root/
├ main.go
├ utils
│ └ file.go - package utils
└ images
├ image_1.webp
└ thumbnail_1.webp
要点
一部を抜粋しています。実装は次の見出しを参照
1. 作業ディレクトリを基準に画像への絶対パスを作る。
utils/file.go
// 現在の作業ディレクトリを取得
currentDir, err := os.Getwd()
if err != nil {
return nil, "", "", fmt.Errorf("error getting current working directory: %w", err)
}
// 作業ディレクトリを基準にファイルの絶対パスを作成
log.Printf("cd: %s", currentDir)
absFilePath := fmt.Sprintf("%s/images/%s", currentDir, fileName)
Linuxのsystemdでbotを常駐させるときのWorkingDirectoryにあたります。
/etc/systemd/system/your_service.service
[Service]
WorkingDirectory=/home/your_user/project_root/
2. ファイルを読み込む
utils/fie.go
file, err := os.Open(absFilePath) // 生成した絶対パス
contentType := "image/webp"
3. 使用時に関数を呼び出す
4. discordgo.File{}
を作る
main.go
// 1. *os.Fileを取得
imageFile, contentType, err := utils.PrepareFile(imageFileName)
if err != nil {
log.Printf("Error preparing file: %v", err)
return
}
// 2. discordgo.File{}を作成
imageDiscordFile := &discordgo.File{
Name: imagePath,
ContentType:contentType,
Reader: imageFile,
}
5. s.ChannelMessageSendComplex
を使って送信する
main.go
// 送信するデータを作成
sendData := &discordgo.MessageSend{
Embed: embed,
Files: []*discordgo.File{
imageDiscordFile,
thumbnailDiscordFile,
},
}
// 送信
s.ChannelMessageSendComplex("your channelID", sendData)
普通のEmbedを送信する際のs.ChannelMessageSendEmbed()
ではありません。
実装
以下の順に記載
- utils/file.go
- main.go
utils/file.go
package utils
// ファイルを*os.Fileとして返す関数
func PrepareFile(fileName string) (*os.File, string, string, error) {
// 現在の作業ディレクトリを取得
currentDir, err := os.Getwd()
if err != nil {
return nil, "", "", fmt.Errorf("error getting current working directory: %w", err)
}
// 作業ディレクトリを基準にファイルの絶対パスを作成
log.Printf("cd: %s", currentDir)
absFilePath := fmt.Sprintf("%s/images/%s", currentDir, fileName)
// ファイルを開く
file, err := os.Open(absFilePath)
if err != nil {
return nil, "", "", fmt.Errorf("error opening file: %w", err)
} else {
log.Printf("successful open file")
}
// Mineタイプを設定する。拡張子が複数ある時はここで条件分岐
contentType := "image/webp"
// *os.File, MIMEタイプを返す
return file, contentType, nil
}
main.go
package main
func main() {
// セッションを作成する
session, err := discordgo.New("Bot " + "your token")
// ...略
// ハンドラを登録する
// 第二引数でトリガーを指定(今回は「メッセージが作成されたとき」)
session.AddHandler(func (s *discordgo.Session, m *discordgo.MessageCreate) {
imageFileName := "image_1.webp"
thumbnailFileName := "thumbnail_1.webp"
// 1. *os.Fileを取得
imageFile, contentType, err := utils.PrepareFile(imageFileName)
if err != nil {
log.Printf("Error preparing file: %v", err)
return
}
// 2. discordgo.File{}を作成
imageDiscordFile := &discordgo.File{
Name: imagePath,
ContentType:contentType,
Reader: imageFile,
}
// 1. 以下同じ
thumbnailFile, contentType, err := utils.PrepareFile(thumbnailFileName)
if err != nil {
log.Printf("Error preparing file: %v", err)
return
}
// 2
thumbnailDiscordFile := &discordgo.File{
Name: thumbnailPath,
ContentType:contentType,
Reader: thumbnailFile,
}
embed := &discordgo.MessageEmbed{
Type: "rich",
Title: "タイトル"
Color: (255 << 16) | (255 << 8) | 0, // R|G|B のシフト演算で指定
Image: &discordgo.MessageEmbedImage{
URL: fmt.Sprintf("attachment://%s", imageFileName), // 本題
},
Thumbnail: &discordgo.MessageEmbedThumbnail{
URL: fmt.Sprintf("attachment://%s", thumbnailFileName), // 本題
},
}
// 送信するデータを作成
sendData := &discordgo.MessageSend{
Embed: embed,
Files: []*discordgo.File{
imageDiscordFile,
thumbnailDiscordFile,
},
}
// 送信
s.ChannelMessageSendComplex("your channelID", sendData)
// × s.ChannelMessageSendEmbed()
})
// ...略
}
筆者が遭遇したエラー
*bytes.Buffer
を使わない
はじめ、PrepareFile()
では*bytes.Buffer
を返していた。
discordgo.File{}の定義
// File stores info about files you e.g. send in messages.
type File struct {
Name string
ContentType string
Reader io.Reader
}
Reader
に*bytes.Buffer
を渡してもエラーは発生しないが、Embedに画像が表示されなかった。PrepareFile()
の戻り値を*os.File
にしてReader
に渡したところ、画像が表示された。chatGPTなどがBufferを使ったコードを書くことがあるので、画像が表示されない場合は注意してほしい。