👼

推しの村山美羽ちゃんによる致死量を超えるかわいい写真の供給が始まったので自動で収集したい

2023/03/16に公開約3,500字

はじめに

おはようございます

https://sakurazaka46.com/s/s46/diary/detail/48078?ima=2341&cd=3rd_generation_m

祝 村山美羽ちゃん ブログ開始おめでとうございます! 祝

https://sakurazaka46.com/s/s46/diary/detail/49521?ima=2504&utm_source=dlvr.it&utm_medium=twitter

初回のブログから致死量を超えるかわいい写真を供給してくれてオタクは大歓喜でございます。

せっかくこんなにもかわいい写真を供給してくれているのに、保存のし忘れなんてしてしまっては絶望です。

そこで、今回はちょっぱやでどうにか仕組みを作ってみないかと思い手を動かしてみた次第です。

準備

https://api.slack.com/messaging/webhooks

こちらを参考に通知用のSlackアプリを作成します。
ここで推しのアーティスト写真を設定しておくのもポイントです。

構成

SlackへブログURL共有 -> サーバー -> ブログから情報収集 -> Slackへ通知

※ slackでイベント共有をトリガーにしてしまうと ブログURL共有 -> 写真URL共有の無限ループが起こりそうだったので今回はcurlで叩くことにしました。

という処理にします。

また、筆者はGo言語も推しています。

https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/go

いつしか知った、vercelserverless-functionsを用いてGoアプリをデプロイしようと思います。

実装

以前、櫻坂46のブログから画像のURLだけを引っこ抜くという実装はしたことがあったので今回はそれを使いまわします。
特に紹介するほどでもないので今回は割愛します。
goqueryというライブラリを使ってごにょごにょするだけです。

https://github.com/PuerkitoBio/goquery

vercelのドキュメントによるとapiというディレクトリを作ってそこにindex.goというファイルを配置しfunc Handler(w http.ResponseWriter, r *http.Request)を実装するようです。
コードは以下のような流れにしました。
エラー処理はものすごくザルです。

package handler

const (
	baseURL = "https://sakurazaka46.com"
)

type Body struct {
	URL string `json:"url"`
}

func Handler(w http.ResponseWriter, r *http.Request) {
	var body Body

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		w.WriteHeader(http.StatusBadRequest)

		return
	}

	if !Validate(body.URL) {
		w.WriteHeader(http.StatusBadRequest)

		return
	}

	pics, err := ExtractPhotoURLFromBlog(body.URL)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)

		return
	}

	for _, pic := range pics {
		Post(os.Getenv("ENDPOINT"), fmt.Sprintf("%s%s", baseURL, pic))
	}

	w.WriteHeader(http.StatusOK)
}

念のためバリデーションも実装しておきます。
先頭と末尾で櫻坂46のブログURLかどうか判定するだけです。
決め打ちなので今後は変わってしまうかもしれません。

func Validate(url string) bool {
	// baseURLから始まる
	if !strings.HasPrefix(url, baseURL) {
		return false
	}

	// 末尾がblogで終わる
	return strings.HasSuffix(url, "blog")
}

Slackへの通知は以下の実装です。
ただSlackアプリで払い出したWebhookのURLにPOSTするだけです。
(エラーハンドリングはpanic実装なので結構雑です。)

func Post(endpoint string, msg string) {
	body := map[string]string{
		"text": msg,
	}

	jsonString, err := json.Marshal(body)
	if err != nil {
		panic(err)
	}

	req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(jsonString))
	if err != nil {
		panic(err)
	}

	req.Header.Set("Content-Type", "application/json")

	client := new(http.Client)
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
}

デプロイ

このページを参照し作業しました。

https://vercel.com/docs/cli/deploying-from-cli

ただ、デプロイするのに以下ハマりました。

  • npm install vercel@latestnode_modulesに入れていたがgo mod tidyがうまく動かなかった
    -> とくにこだわりがないのであればnpm install -g vercel@latestでよさそう。
  • vercel deployしていたが prev 環境が作られすぎた。
    -> 個人開発とかちょっとした開発なら vercel deploy --prod でよさそう。
  • --env XXX=YYY で環境変数を指定できる。
    -> シークレット値とかはこれで管理することになりそう。

動作確認

Slackへの通知

Slackは画像のURLを共有すると自動で展開してくれるので良いですね。

curlによりvercel起動

400エラーも正しく動作することが確認できました。

お片付け

雑に作ったアプリなので以下のコマンドでお片付けしておきます。

vercel project rm <project-name>

深堀りしたいこと

  • ルーティングの設定
  • 複数functionをひとつのリポジトリで管理できるのか

展望

  • ちゃんとリポジトリ管理する
  • 村山美羽ちゃんの個人ブログが本格的に始まったら更新を自動検知したい
    • 方法要検討
    • cron使える?
  • 画像を自動保存する
    • 保存先要件等

おわりに

ブログ開始に合わせてちょっぱやで手を動かして作ったので完成度は低いです。
ですが、キャッチアップしてみたかったvercelserverless-functionsを試せたのでよかったです。
これでまたGo実装のデプロイ先としての手札が増えました。
(貧乏エンジニアリングでFaaSによりGoデプロイできるのは素晴らしい。)

村山美羽ちゃんかわいいです。

Discussion

ログインするとコメントできます