🌟

Instapaperに保存した技術記事をSlackに自動投稿してAIに要約させる仕組みを作った

に公開

はじめに

「あとで読む」に保存した記事、ちゃんと読めていますか?私は読めていませんでした。
Instapaper に保存するものの、記事が溜まり、先延ばしにして結局何もしなくなってしまいます。
しかし、Slackなら毎日開き、AIで要約など記事の内容を読み解く手助けをしてくれれば、そして、タイムラインに流れてきてくれれば、自然に読み、少しずつ記事を消化していくはず。そこで作り始めたのがこのボットです。

完成した仕組みは「Instapaperにフォルダ分けして保存するだけ」で、24時間間隔でSlackに記事が投稿され、AIがスレッドで要約・洞察・示唆を返してくれるものです。

1. 全体アーキテクチャ

Instapaper(特定フォルダに記事を追加)
    ↓ RSSフィードに新着
Make.com(15分ごとにRSS監視・無料)
    ↓ POST /webhook/instapaper {"title", "url"}
Railway(Node.js + Honoの常時起動)
    ↓ 記事本文をスクレイピング → AI で分析
Slack(チャンネルに投稿 + スレッドにAI分析)
サービス 役割 コスト
Instapaper 記事の保存・RSSフィード提供 無料
Make.com RSS監視 → Webhook送信の自動化 無料(月1,000クレジット)
Railway Node.jsボットのホスティング 登録後30日間使える無料枠あり(月$5相当)
OpenAI 記事分析・要約生成 従量課金(GPT-4.1-mini)
Slack 通知チャンネルとしての活用 無料

2. なぜこの構成にしたか

構成を決めるにあたって、いくつか条件がありました。

求めていた要件 選んだ解決策
記事を保存したタイミングを自動で検知したい Instapaper RSS フィード
無料で RSS 監視 → Webhook 送信を自動化したい Make.com
常時起動・GitHub から自動デプロイしたい Railway
軽量なWebhookサーバーが欲しい(+気になるもの) Hono
記事を分析して自然な日本語で洞察を返してほしい OpenAI GPT-4.1-mini

Instapaper

InstapaperはWebhook に対応していません。
「記事を保存したタイミング」でリアルタイムに何かを起動する公式な方法がないので、RSSフィードを定期監視する方法で代替しました。
InstapaperはフォルダごとにRSSフィードURLを発行してくれるので、特定フォルダ(例:「AI/tech」)に保存した記事だけを対象にできます。

Make.com を選んだ理由

Zapier も同じようなことができますが、Webhook 送信(HTTP POST)は有料プランのみです(月$20〜)。Make.com は無料プランでも HTTP POST が使えます。月1,000クレジット付与で、1実行=2クレジットなので月500回分。1日に1回なら十分です。

Railway を選んだ理由

Render の無料プランは 15 分間アクセスがないとスリープします。Slack の Socket Mode は WebSocket で常時接続するため、スリープされると接続が切れます。Railway は常時起動で、GitHub に push するだけで自動デプロイされます。Dockerfile も不要です。

Hono を選んだ理由

Webhook サーバーに ExpressではなくHonoを選びました。TypeScript の型定義が内蔵されていて @types/xxx が不要で、ESM プロジェクトとも相性がいい。小規模な Webhookサーバーなら十分です。
また、シンプルに興味本位で使ってみたかったです。

3. ハマりどころと解決策

3-1. Make.com の JSON エンコード問題

Make.com の HTTP モジュールを実行すると InvalidConfigurationError: The provided JSON body content is not valid JSON が出ます。Body input method を JSON string(Raw)のまま {"title": "{{1.title}}"} と書いていると、記事タイトルに " が含まれたときに JSON が壊れるためです。

Body input method を Data structure に変えると解決します。Key/Value 形式で入力するので Make.com が自動でエスケープしてくれます。

Body content type: application/json
Body input method: Data structure
  Key: title  Value: {{1.title}}
  Key: url    Value: {{url}}

3-2. Railway の 502 エラー

デプロイは成功しているのに curl で叩くと 502 Application failed to respond が返ってくる。ログには「Webhook server listening on port 8080」と出ているのにエラーが出てしまいました。

Railwayは外部リクエストをPORT経由でアプリに転送します。アプリが WEBHOOK_PORT=8080(自分で設定)でリッスンしていても、Railway の PORT と一致していなければ 502 になります。

コードと Railway Variables の2か所を直したことで解決しました。

コード側(src/index.ts):

const port = Number(process.env.PORT ?? process.env.WEBHOOK_PORT ?? "3000");
serve({ fetch: webhookApp.fetch, port, hostname: "0.0.0.0" });

Railway 側:

  • Variables タブで PORT=8080 を明示設定する

3-3. Make.com のタイムアウト対策

Make.com のシナリオが「The operation failed with an error」でタイムアウトします。OpenAI での記事分析は数秒〜十数秒かかるので、Make.com がレスポンスを待ちきれないためです。
Webhookを受け取ったら即200 を返して、処理はバックグラウンドで実行します。

webhookApp.post("/webhook/instapaper", async (c) => {
  const { title, url } = await c.req.json();

  void (async () => {
    const message = await slackApp.client.chat.postMessage({ ... });
    const article = await fetchArticleContent(url);
    const analysis = await analyzeArticle(article);
    await slackApp.client.chat.postMessage({ thread_ts: message.ts, ... });
  })();

  return c.json({ ok: true });
});

3-4. Railway で Cannot find module '/app/index.js'

Railway デプロイ直後に起動がクラッシュします。package.json"main": "index.js" が残っていると、Railway がこれを見て node index.js を実行しようとするためです。TypeScript プロジェクトにはコンパイル済みの index.js がありません。

"main" フィールドを削除して、"start" スクリプトで起動コマンドを明示したことで解決しました。

{
  "scripts": {
    "start": "tsx src/index.ts",
    "dev": "dotenvx run -- tsx src/index.ts"
  }
}

4. 実装方法

Step 1: InstapaperでフォルダとRSS URLを取得

  1. Instapaper にログイン → 対象フォルダを開く
  2. 「Downloads」→「RSS」からフィード URL をコピー

Step 2: コードを実装してGitHubにpush

必要なパッケージ:

npm install hono @hono/node-server @slack/bolt openai cheerio

必要な環境変数:

SLACK_BOT_TOKEN=...
SLACK_APP_TOKEN=...
OPENAI_API_KEY=...
SLACK_CHANNEL_ID=...
WEBHOOK_SECRET=任意の文字列
WEBHOOK_PORT=8080

Step 3: Railway にデプロイ

  1. railway.app →「New Project」→「Deploy from GitHub repo」
  2. リポジトリを選択してデプロイ
  3. Variables タブで環境変数を設定(PORT=8080も忘れずに設定)
  4. Settings → Networking →「Generate Domain」で URL を発行

Step 4: Make.com でシナリオを設定

モジュール1: Watch RSS Feed Items(トリガー)

項目 設定値
URL Instapaper のフォルダ RSS フィード URL
Maximum number of items 5
初回開始位置 From now on(過去記事を再送しないため)

モジュール2: HTTP - Make a request(アクション)

項目 設定値
URL https://xxxx.up.railway.app/webhook/instapaper
Method POST
Body content type application/json
Body input method Data structure
Key: title {{1.title}}
Key: url {{url}}

スケジュール: シナリオ画面下部の時計アイコン → 24時間間隔

Step 5: 動作確認

curl -X POST https://xxxx.up.railway.app/webhook/instapaper \
  -H "Content-Type: application/json" \
  -H "x-webhook-secret: 設定したシークレット" \
  -d '{"title":"テスト記事","url":"https://zenn.dev"}'

{"ok":true} が返り、添付画像のようにSlack に投稿と AI 分析スレッドが投稿されれば完成です。

まとめ

動かしてみてわかったのは、「読むか読まないかをすぐ決められるようになった」ことにより積読が減ったことです。SlackにAI要約が流れてきて、関係ありそうなら本文を開く、そうでなければスルーと判断が速くなりました。
また、全体像を最初に把握できて、記事の吸収度合いが上がった気がします。
プロンプトを変えるだけで拡張/変更もできます。これからはおすすめ度スコアをつけたり、カテゴリ分類してSlackチャンネルを振り分けたり、週次でハイライトをまとめて送ったりできます。色々試していきたいと思います。

コード: https://github.com/daijudev/slack-tech-insight-bot

Discussion