❣️

はてブでブックマークした後についたコメントをslackへ通知させるようにした

2021/12/20に公開

この記事はSlack Advent Calendar2021の19日目の記事です。

https://qiita.com/advent-calendar/2021/slack

この記事では、最近作ったslackアプリと工夫した点などを書きたいと思います。(単に作ったアプリの紹介程度で学びの少ない記事かもしれません)

何を作ったか

はてなブックマークで自分がブックマークした後に同じ記事についたコメントをSlackへ通知するアプリを作りました。(下記は実際の動作画面)

(モザイクしているところは、実際にコメントをしたはてブのユーザー名とアイコンが入ります。)
(コメントにタグemojiがついてるやつは実際のはてブのタグに該当します)

なんで作ったか

はてブの面白いコメントを見落とす可能性を減らしたかった

はてブをよく使うのですが目的として、

  • 今注目を浴びてる記事の確認(キュレーション目的)
  • 記事に対する他の人のコメント見て知見を得る

特に後者を強い目的として利用してました。が基本的に他の人のコメントを確認するのは自分がブクマする時くらいで、自分がコメントつけた後についた面白いコメントを見落とす可能性は高いなと常に思ってました。

かといってずっとはてブに張り付くのもよくないので、Slackに自分がした後のコメントを通知させるような仕組みを作ってみました。
個人用ワークスペースで動かしており、仕事で一息入れる時にパララと眺めたりしてます。

通知が入るチャンネル自体をミュートにして、Botから自分に向かってメンションさせておけば
スッコココという邪魔入らず、気づいた時に見れるのでいい感じです。

自宅のk8sクラスタが無駄になってた

上のはてブの課題感と合わせて、過去にお家k8sクラスタをラズパイで作ったのですが、動かすものがなくて持て余してました。
お家k8sで動かせる、外からリクエストを受けず、自分から外部へリクエストを送るだけのようなアプリで何かできないかなと考えてた時に上のができそうだったので作ってみたのも理由の1つです。

このあとアーキテクチャとか書いていくのですが、k8sで動かすの前提にしてたので、ちょっと欲が出てアプリの分離とかしており少し無駄感はある構成だと思います。

全体構成

3つのアプリ(全てGo言語で書いてます)と、データのストアとしてRedisを利用してます。
Redis使ったのとしては単純に慣れてるのと、個人アプリなのでデータ消失しても問題ないのでインメモリストアにしたのと、key-valueストアとしてだけでなく、streamやqueue的な多種のデータ構造に対応してるからです(実際今回はSetとしての利用とQueueとしての利用をしています)

アプリのリポジトリは下記になります

一連の流れ

ざっくり書くと

  1. はてブのマイページをスクレイピングして最新のブックマーク情報を取得する
  2. 取得したブックマーク情報を監視対象にして、定期的に新しいコメントがついてないかを確認しに行く
  3. 新しいコメントがついていると、それをslackへ通知する

という流れになります。少し細かく説明していきます

1. はてブのマイページをスクレイピングして最新のブックマーク情報を取得する

構成図の矢印で言うと1、2、3のScraperというアプリが動作している箇所になります。

Scraperというアプリで定期的に(5分に1回)はてブのマイページへスクレイピングして、自分がつけた最新のコメントを取得しにいきます。
取得したブックマーク情報のうち、ブックマーク日が今日のもの, まだSlackへ投稿してないの条件を満たすブックマークのみSlackへ通知した後にRedisのDBへ保存します。

RedisにはHSETを利用して、keyにentryID, valueで必要な情報をfield-valueのセットでDB1に保存してます。

redis> HSET <entryID> url "https://..." thread_timestamp "0000.." update_timestamp "0000.." 

情報として、

  • url: ブックマークしたページのurl
  • thread_timestamp: slackへ投稿したメッセージのタイムスタンプ(以降、threadとして返信する際に利用します)
  • update_timestamp: 処理時刻

を保存します

はてブのAPI使わずにスクレイピングしてる理由

自分がちゃんと調べられてないだけかもですが、はてブでマイブックマークを取得するAPIは
マイブックマーク全文検索APIだけのように見えました。
これを試してみたのですが、ちょっと使いづらく全てのブックマークを取得したくてもできなかったです。(全文検索のキーワードでワイルドカードや色々試したみたのですが、漏れが出る)

というのもあり、今回はスクレイピングで取得しました。

マイページでブックマーク一覧出せるということは内部的には対応したAPIあると思うのですが、公開されてないか自分がちゃんと調べられてないだけだと思います。

2. 取得したブックマーク情報を監視対象にして、定期的に新しいコメントがついてないかを確認しに行く

構成図の矢印で言うと4、5、6のObserverというアプリが動作している箇所になります。

こちらも定期的(5分に1回)に下記の動作を行います。

まず、1つ前のScraperが保存した情報をRedisから取得します。こちらに保存されているurl情報を利用してはてなブックマークエントリー情報取得APIに投げて他の人のコメントを取得します。

取得したコメントを書き込み時刻でソート後に、同じく保存していた情報のupdate_timestampより後のデータを新規コメントとして判定します。

新規コメントはRedisの別のDB(DB2とする)にLPUSHコマンドで追加します。これはQueue的に利用できるコマンドで、取得するときに自動的に保存した情報を削除してくれるので利用してます(コメントは取得してSlackへ通知後は保存の必要ないため)

redis> LPUSH notify-queue "{\"key\":\"value\"}"

Slack通知に必要な情報をjsonにまとめた後、文字列にして突っ込んでます

最後にDB1の情報を更新します。
新しくコメントがあった記事については、最新のコメントの時刻でupdate_timestampを更新しておきます。
コメントがないかつ、update_timestampと現在時刻の差分が24時間以上のエントリーについては削除します。(1日コメントがない記事は以降も頻繁にコメントつくことないと思われるため、あと監視対象増えすぎると流石に重くなりそうなので)

3. 新しいコメントがついていると、それをslackへ通知する

構成図の矢印で言うと7、8のNotifierというアプリが動作している箇所になります。

こちらはRedisのDB2につなげて、新しいコメントがLpushで追加されるたびに取得して
SlackへpostMessageAPIで通知を行います。

通知したら2秒sleepした後再びRedisからデータを取得します。
(postMessageのRate制限で1秒に1回の呼び出し制限があるからです)

この動作を繰り返します

Slack APIの呼び出しでこだわった部分など

Slackのアドベントカレンダーなので、書きます。
特にアイコンと名前の書き換えに今回はこだわってみました。

最初は全て同じicon, bot名でやってたのですが、見栄えというか人のコメントと頭が認識できなくてユーザー名, iconを変えてみました。(あと初回通知時のアイコンもはてブのカテゴリアイコンを利用してます)

特にiconを変えたのが効果的だったように思えます。
人はやっぱり文字とかでなく、図とか絵で識別する方が強いと思うので
1コメント毎に別々のiconがついたことでコメントを読みやすくなった気がします。

このicon, usernameはSlack API呼び出し時にオプションで変更することができます。

https://api.slack.com/methods/chat.postMessage

iconUrl, username, iconEmojiの3つのオプションを利用しています。

はてブユーザーのiconは https://cdn.profile-image.st-hatena.com/users/<username>/profile.png に保存されているので, iconUrlにはこれを利用しています。

iconEmojiはiconをslackのワークスペースに登録しているemojiにすることができる機能です
以下のようなはてブで用意されているアイコンのURLをそのまま使いたかったのですが、元がsvg画像でsvgはそのままiconUrlに指定できなかったので、一度ダウンロードしてpngにした後にemojiへ登録して利用しています。(自分で画像ホスティングするめんどかったので)

最後に

最初は上でも書いたようにお家ラズパイk8sクラスタで動かしてたのですが、寝てる間もずっと赤く光り続けるラズパイに嫌気が刺して(当時は寝室に置いてたので)、最近GCPに移行させました(GKEがほぼ無料で使えること知ったので)、1日あたり大体4円で動いてます。(GCPすごい!)

この移行に関しては、GCPのアドベントカレンダー記事で執筆してますので、よかったら見てくれると嬉しいです。

https://zenn.dev/esaka/articles/0e240428ac7596

Discussion