Chatworkの全ルームを15分おきにn8nで巡回させたら、見逃しがゼロになった
「あのメッセージ、見た?」
この一言が飛んでくるたびに、胃がキュッとなる。Chatworkのルームが増えるほど、見逃しのリスクは上がる。20ルーム超えたあたりから、もう人間の目視じゃ無理だと気づいた。実際、クライアントからの確認依頼を丸一日放置してしまったことがある。ルームが多すぎて、通知が他のメッセージに埋もれた。
言い訳しても始まらない。仕組みで解決する。
n8n(セルフホスト、VPS上のDocker)で15分おきに全ルームを巡回するワークフローを組んだ。結果、見逃しがゼロになった。この記事ではその構成と、ハマったポイントを書く。
なぜn8nか
先にこの選択について触れておく。
GASでもできなくはない。だが6分の実行時間制限がある。ルーム数が多いとAPI呼び出しだけで数十秒かかるし、仕分け処理まで含めると余裕がない。それに、GASはトリガーの最短間隔が1分だが、実行時間と合わせて考えると15分おきの安定運用はちょっと怪しい。
n8nならVPSのDocker上で24時間動く。制限時間なし。ワークフローのビジュアルエディタで全体像が見える。Webhookも受けられるが(#10参照)、Chatwork Webhook APIが使えないアカウントもあるので、ポーリング方式のほうが確実だ。
ワークフローの全体構成
5ステップで構成される。
Schedule Trigger(15分おき)
→ HTTP Request: GET /rooms(全ルーム一覧)
→ Loop: 各ルームに対して
→ HTTP Request: GET /rooms/{room_id}/messages?force=0
→ IF: メッセージあり?
→ Function: 仕分け → ファイル保存
順番に見ていく。
1. Schedule Trigger
Cron: */15 * * * *
15分おきに起動する。ここで一つ注意。n8nのSchedule TriggerはデフォルトでUTCになっている。JSTに設定しないと、日本時間の深夜帯にバッチが走るタイミングがズレる。n8nの環境変数で GENERIC_TIMEZONE=Asia/Tokyo を設定しておくこと。
2. 全ルーム取得
GET https://api.chatwork.com/v2/rooms
ヘッダーに X-ChatWorkToken を入れるだけ。レスポンスは全ルームの配列。ここで room_id と name を取得する。
3. 各ルームのメッセージ取得
GET https://api.chatwork.com/v2/rooms/{room_id}/messages?force=0
ここが肝だ。force=0 は未読メッセージのみを返す。force=1 だと既読含む直近100件が返るが、「100件の壁」問題がある(#11参照)。定期巡回なら force=0 のほうが筋がいい。
n8nではLoop(Split In Batches)ノードで各ルームを回す。バッチサイズは10くらいが安全圏。理由は後述するレートリミットの話。
4. メッセージの有無を判定
IFノードで判定する。
{{ $json.length > 0 }}
地味だが、ここを忘れるとメッセージゼロのルームも後続の処理に流れてきて、空配列でエラーを吐く。n8nはデータが空のときの挙動が独特で、「何もないこと」を明示的にハンドリングしないと予期しないところで止まる。
5. 仕分けロジック(Functionノード)
取得したメッセージをカテゴリ分けする。
const messages = $input.all();
const categorized = messages.map(item => {
const body = item.json.body || '';
let category = 'other';
if (body.match(/お願い|頼む|やって|確認して|至急/)) {
category = 'instruction';
} else if (body.match(/\?|?|教えて|どう思/)) {
category = 'question';
} else if (body.length < 100) {
category = 'memo';
}
return {
json: {
message_id: item.json.message_id,
account: item.json.account?.name || 'unknown',
body: body.substring(0, 200),
category,
send_time: item.json.send_time,
room_id: item.json.room_id
}
};
});
return categorized;
正規表現はざっくりでいい。完璧な分類を目指すとキリがない。「指示っぽい」「質問っぽい」「短いメモ」「その他」。この4分類で十分だった。指示と質問だけ優先的に目を通せばいい。
仕分け結果はVPS上のファイルに日付ごとにJSON保存する。1日分をまとめて翌朝に確認する運用だ。
force=0の罠
#12で詳しく書いたが、ここでも改めて。
GET /messages を叩くと、APIレベルで既読になる。つまり、n8nが15分おきに巡回すると、人間がChatworkのアプリを開いたときには全部「既読」になっている。未読バッジが消える。
これは地味に困る。「n8nが取得した=自分が確認した」ではないのに、Chatwork上では既読扱いになってしまう。
対策は PUT /rooms/{room_id}/messages/unread で未読に戻すこと。
PUT https://api.chatwork.com/v2/rooms/{room_id}/messages/unread
Body: { "message_id": "{最古の未読メッセージID}" }
巡回ワークフローの最後にこのAPIを叩いて、取得したメッセージを未読に戻す。こうすれば、n8nはログを取りつつ、人間のChatwork上の未読状態は維持される。
正直、最初はこれに気づかなくて「なんか最近Chatwork静かだな」と思っていた。静かだったんじゃなくて、n8nが全部既読にしていただけだった。
レートリミットとの戦い
Chatwork APIのレートリミットは5分間に300リクエスト。
巡回ワークフローは1ルームあたり最低2リクエスト消費する。GET /rooms で1回、各ルームの GET /messages で1回ずつ。未読戻しの PUT /unread も入れると3リクエスト。
30ルームなら 1 + 30×2 = 61リクエスト。余裕がある。
100ルームなら 1 + 100×2 = 201リクエスト。まだ大丈夫。
150ルーム超えると危険水域に入る。
対策は2つ。
バッチサイズを制御する。n8nのSplit In Batchesノードでバッチサイズを10にし、各バッチの間に1秒のWaitノードを挟む。一気に叩かない。
不要なルームをスキップする。全ルームを巡回する必要は実はない。GET /rooms のレスポンスには unread_num が含まれている。これが0のルームはスキップすればいい。
// Functionノードでフィルタ
const rooms = $input.all();
return rooms.filter(item => item.json.unread_num > 0);
これだけでAPIコール数が激減する。地味だが効果は大きい。
バックアップログの活用
巡回結果はVPS上に日付ごとにJSON保存している。
/data/chatwork-logs/2026-03-21.json
これが地味に便利だ。
- 「昨日の○○ルームで何が話されていたか」をセッション外で確認できる
- メッセージの仕分け結果から、朝一で「今日対応すべきもの」がわかる
- 過去ログの検索もJSONなので
jqで一発
Chatwork本体の検索機能は正直あまり強くない。自前でログを持っておくと、後から「あのとき何て言ってたっけ」が即座に引ける。
15分おきにした理由
1分おきでもいい。技術的には。だがレートリミットとの兼ね合い、そしてそもそもChatworkに1分以内のリアルタイム性を求めるのかという話がある。
Slackなら即時通知でいい。でもChatworkを使っている現場は、15分くらいの遅延は許容範囲であることが多い。逆に言えば、15分おきで十分だった。至急の連絡はそもそも電話が来る。
まとめないまとめ
人間は忘れる。見逃す。通知を見て「あとで返そう」と思って、そのまま消える。
バッチは忘れない。15分おきに全ルームを舐めて、仕分けして、ログに残す。淡々と。
正直、Chatworkの使い方としては想定外かもしれない。でも、APIが公開されている以上、こういう使い方は正当だと思っている。見逃しで信頼を失うくらいなら、仕組みで防いだほうがいい。
次はこの巡回ログをAIに食わせて、「今日対応すべきものリスト」を自動生成する話を書くかもしれない。書かないかもしれない。
Chatworkシリーズ
- #1 なぜ2026年にまだChatworkを使い倒しているのか
- #2 chatwork-client-gas、ぶっちゃけいるの?
- #3 ルームの参加者データだけで、組織の人間関係マップを作った
- #4 「Chatworkに確定連絡が来たら請求書を送る」をGASで自動化する
- #5 Chatwork MCPを繋いだら、17ルームの未読が10秒で片付いた
- #6 MCP vs GAS — Chatwork自動化の「正解」はどっちか
- #7 コンタクト承認をn8nで自動化しようとしたら、3つの罠にハマった
- #8 ChatworkにAIチームを住まわせたら、勝手に会話が始まった
- #9 Chatwork 8ルームの全メッセージからFAQ429件を自動抽出した
- #10 Webhook署名検証を入れたら全メッセージが消えた
- #11 過去メッセージを全件取得しようとしたら、APIの「100件の壁」にハマった
- #12 Chatwork APIの「既読」は自分で制御できる
- #13 Chatwork APIのファイル機能、使ったことある?
- #14 Chatworkの全ルームを15分おきにn8nで巡回させたら、見逃しがゼロになった(この記事)