その月に書いた記事のURL一覧を取得してくれるSlackアプリをつくった話

4 min read読了の目安(約4200字

毎月のふりかえりをしたいときにその月に書いた記事を一覧で欲しいなーと思い、Slackアプリをつくってみました。まだ、公開アプリにはしていないのですがここで紹介してみます。

こんな感じのをつくった

Image from Gyazo

使い方

  1. /monthlyと打ってメッセージを投稿
  2. モーダルが開くので、Qiita・Zenn・noteのユーザーIDを入力して送信
  3. しばらくするとその月に書いた記事一覧が返ってくる

※ 1回目はユーザーIDを入力してもらう必要がありますが、2回目以降はFirebaseから持ってくるので入力不要になります。
※ ちなみにコマンド実行した月の初日~末日で抽出しているため、月の途中で「どのくらい書いたかなー」とかのチェックにも使えます。

なんでつくったか?

最初はこちらの記事でも書いたのですがコマンドでやっていました。ぶっちゃけ個人で使うにはこのツールで十分だったのですが、

  • 所属しているコミュニティのSlackでも使ってもらえるようにしたい
  • Firebase使ってみたい

以上の理由からSlackアプリにすることにしました。

実装

コアとなるロジックは上記の記事で作成したワンライナーと同じですが、Slackアプリにする都合でTypeScriptに変更しました。
また、抽出の基準となる日時は以下をそれぞれ使用しています。

  • Qiita
    • 作成日
  • Zenn
    • 公開日
  • note
    • 月で指定できるので日時指定なし

更新日も対象にするか悩んだのですが、あくまで新しく書いた記事を対象にしたいのと
更新した記事も含めたいというのはそこまでニーズないだろうと判断して対象に含めませんでした。

qiita-client.ts
import axios from "axios";
import { getFirstDayOfMonth, getLastDayOfMonth } from "../date/date-getter";
import { Item } from "../types/qiita-types";

const BASE_URL = "https://qiita.com/api/v2/users/";

export const getQiitaArticleUrlsOfMonth = async (
  userId: string | null
): Promise<string> => {
  if (!userId) {
    return "QiitaのユーザーIDは登録されていません。";
  }

  const url = `${BASE_URL}/${userId}/items`;
  const firstDayOfMonth = getFirstDayOfMonth(new Date());
  const lastDayOfMonth = getLastDayOfMonth(new Date());

  const response = await axios.get<Item[]>(url);
  const urlList: string[] = response.data
    .filter((item) => {
      const createdAt: Date = new Date(item.created_at);
      return createdAt >= firstDayOfMonth && createdAt <= lastDayOfMonth;
    })
    .map((item) => item.url);

  return urlList.length > 0
    ? urlList.join("\n")
    : `${new Date().getMonth() + 1}月に書いたQiitaの記事はありませんでした。`;
};
zenn-client.ts
import axios from "axios";
import { getFirstDayOfMonth, getLastDayOfMonth } from "../date/date-getter";
import { ZennArticles } from "../types/zenn-types";

const BASE_URL = "https://api.zenn.dev/articles?username=";

export const getZennArticleUrlsOfMonth = async (
  userId: string | null
): Promise<string> => {
  if (!userId) {
    return "ZennのユーザーIDは登録されていません。";
  }

  const url = `${BASE_URL}${userId}`;
  const now = new Date();
  const firstDayOfMonth = getFirstDayOfMonth(now);
  const lastDayOfMonth = getLastDayOfMonth(now);

  const response = await axios.get<ZennArticles>(url);
  const urlList: string[] = response.data.articles
    .filter((item) => {
      const publishedAt: Date = new Date(item.published_at);
      return publishedAt >= firstDayOfMonth && publishedAt <= lastDayOfMonth;
    })
    .map(
      (item) => `https://zenn.dev/${item.user.username}/articles/${item.slug}`
    );

  return urlList.length > 0
    ? urlList.join("\n")
    : `${now.getMonth() + 1}月に書いたZennの記事はありませんでした。`;
};
note-client.ts
import axios from "axios";
import { Article, NoteArticles } from "../types/note-types";

const BASE_URL = "https://note.com/api/v2/creators";

export const getNoteArticleUrlsOfMonth = async (
  userId: string | null
): Promise<string> => {
  if (!userId) {
    return "noteのユーザーIDは登録されていません。";
  }

  const year = new Date().getFullYear();
  const monthNum = new Date().getMonth() + 1;
  const month =
    monthNum.toString().length === 1
      ? `0${monthNum.toString()}`
      : monthNum.toString();

  const url = `${BASE_URL}/${userId}/contents?kind=note&publish_on=${year}-${month}&disabled_pinned=true`;

  const response = await axios.get<NoteArticles>(url);
  const urlList: string[] = response.data.data.contents.map(
    (item: Article) => item.noteUrl
  );

  return urlList.length > 0
    ? urlList.join("\n")
    : `${monthNum}月に書いたnoteの記事はありませんでした。`;
};

ソース全体

https://github.com/captain-blue210/monthly-blog-reporter

最後に

今回始めてBolt.js + Firebaseを使ってSlackアプリを作成したのですが、たいしてハマらずに作ることができました。
それも@KawamataRyoさんの記事やGitHubのおかげです。
心の底から感謝!!😆😆 ソース読みやすくて勉強になりました!!

参考