継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

SendGrid のバウンスの発生を Golang で取得して Slack に通知する

タダです.

前回記事の続きになります.SendGrid のバウンス発生を Golang で取得するまでやったので,その結果を Slack に通知してみました.この記事でその模様をまとめます.

sadayoshi-tada.hatenablog.com

前回記事の振り返り

前回記事では SendGrid API を叩いて bounceDetail の構造体に指定したレスポンスデータを返すところまで書きました.そのため,今回はレスポンスデータを返して以降の Slack 投稿までのコードをまとめます.

前回記事のコード

package main

import (
     "encoding/json"
    "fmt"
    "strconv"
    "time"

    sendgrid "github.com/sendgrid/sendgrid-go"
)
type bounceDetail struct {
    Created int    `json:"created"`
    Email   string `json:"email"`
    Reason  string `json:"reason"`
    Status  string `json:"status"`
}

func getBounceMail(apiKey string) ([]bounceDetail, error) {
    var bounceDetail []bounceDetail
    startTime := time.Now().Add(-10 * time.Minute).Unix()
    endTime := time.Now().Unix()
    req := sendgrid.GetRequest(apiKey, "/v3/suppression/bounces", "https://api.sendgrid.com")
    req.Method = "GET"
    queryParams := make(map[string]string)
    queryParams["start_time"] = strconv.FormatInt(startTime, 10)
    queryParams["end_time"] = strconv.FormatInt(endTime, 10)
    req.QueryParams = queryParams
    res, err := sendgrid.API(req)
    if err != nil {
        fmt.Println(err.Error())
        return nil, err
    }
    if err := json.Unmarshal([]byte(res.Body), &bounceDetail); err != nil {
        fmt.Println(err)
        return nil, err
    }
    return bounceDetail, nil
}

バウンスの発生を受け取ってから Slack 投稿までのコード

バウンスの発生を受け取りを getBounceMail()で行い,そのレスポンスを response 変数に入れています.その後,for 文を回して BouncePostData 構造体に定義したフィールドにマッピングして配列化し,sendSlack を使って Slack に投稿する形です.10分の間隔でその時間帯内におけるバウンスの発生を確認しますが,1件のバウンスメールに付き1個の通知が出て,バウンスの数分その投稿が増える形になるため,1つのメッセージにすべてのバウンスメールと原因をまとめたい場合は,工夫が必要になりますmm

コード抜粋

type SendGridBounceResult struct {
    Result string `json:"result"`
}

type SlackPayload struct {
    UserName string `json:"username"`
    Text     string `json:"text"`
}

type BouncePostData struct {
    Email  string `json:"email"`
    Reason string `json:"reason"`
}

func sendSlack(webhookUrl string, bounceDetails []BouncePostData) error {
    for _, message := range bounceDetails {
        payload, err := json.Marshal(
            SlackPayload{
                UserName: "バウンスチェック",
                Text: fmt.Sprintf("```\nバウンスの発生したメールアドレス:%s\n原因:%s\n```",
                    message.Email, message.Reason,
                ),
            },
        )
        if err != nil {
            fmt.Println(err.Error())
            return err
        }
        res, err := http.PostForm(
            webhookUrl,
            url.Values{"payload": {
                string(payload),
            },
            })
        if err != nil {
            return err
        }
        defer res.Body.Close()
    }
    return nil
}

func main() (SendGridBounceResult, error) {
    var bounceDetails []BouncePostData
    webhookUrl := "Slack Incoming Webhook URL"
    // 前回記事のバウンスメールの取得関数呼び出し
    response, err := getBounceMail(apiKey)
    if err != nil {
        fmt.Println(err.Error())
        return SendGridBounceResult{Result: "Error"}, err
    }
    if len(response) == 0 {
        fmt.Println("No Bounce mail.")
        return SendGridBounceResult{Result: "Success"}, nil
    }
    for _, value := range response {
        bounceDetails = append(bounceDetails, BouncePostData{
            Email:  value.Email,
            Reason: value.Reason,
        })
    }
    err = sendSlack(webhookUrl, bounceDetails)
    if err != nil {
        fmt.Println(err.Error())
        return SendGridBounceResult{Result: "Error"}, err
    }
    return SendGridBounceResult{
        Result: "Success",
    }, nil
}

まとめ

SendGrid のバウンスが発生しているメールは通常都度コンソールをみることになりますが,バウンスが発生してたら Slack に投稿する仕組みを作ったのでその模様をまとめました.実運用に載せましたが,今の所問題は起こっていなく,今まで気づけなかったバウンスに気づきやすく問題に当たれるようになった体感があります.今後も普段のオペレーションから仕組み化すべきなことをコードで解決図っていきたいと思います.