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を取得
- Instapaper にログイン → 対象フォルダを開く
- 「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 にデプロイ
- railway.app →「New Project」→「Deploy from GitHub repo」
- リポジトリを選択してデプロイ
- Variables タブで環境変数を設定(
PORT=8080も忘れずに設定) - 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チャンネルを振り分けたり、週次でハイライトをまとめて送ったりできます。色々試していきたいと思います。
Discussion