IE3のWebサイトには、写真で日々の記録を残すLabがあります。制作実績として紹介するほどでも、ブログ記事にまとめるほどでもない、スナップショットや制作途中の実験を置いておく場所。自分たちのサイトにある、小さなInstagramのようなページです。
このLabを、Slackから更新する「Slack Postman」を作りました。写真や動画を投稿し、同じスレッドで設定すれば、サイトの更新まで進みます。この記事では、その仕組みと制作中に工夫したこと、Web以外への応用について紹介します。
「JSONを更新するだけ」が面倒だった
最初は、画像を置いて簡単なJSONを更新する仕組みでした。実装はシンプルですが、投稿のたびに画像を用意して、保存先を決め、データを書き換える必要があります。
気軽に写真を残すためのページなのに、更新には少し腰を据えないといけない。その手間を減らすため、普段使っているSlackを投稿画面にしました。
「Slack Postman」の操作は、次の流れです。
- 特定のチャンネルに写真や短い動画を投稿する
- 同じスレッドで日付、タイトル、リンクを設定する
- 公開完了の通知を受け取る
タイトルとリンクは省略でき、公開後の編集や削除もSlack上で行えます。
写真を送ると、Clippyのひと言から投稿の設定が始まります。
構成:Slackと静的サイトをWorkersでつなぐ
全体は、次の構成です。
Slack → Cloudflare Workers → GitHub → Cloudflare Pages → Slackへ公開完了を通知
WorkersはHono / TypeScriptで実装し、次のサービスにつないでいます。
- Workers KV:入力内容・処理状態・認証情報
- Cloudflare Images:画像の縮小・WebP変換
- Cloudflare Media Transformations:動画の変換・ポスター画像の切り出し
- Workers AI:写真へのコメント生成
- GitHub API:画像とJSONの保存
既存の画像+JSONというサイト構成はそのままに、更新作業をWorkersへ移しています。
スレッドを入力フォームとして使う
投稿はSlack Events API、ボタン操作はInteractivityで受け取り、Block Kitで質問や操作ボタンを表示します。
入力フローは「日付待ち → タイトル待ち → リンク待ち」と段階を進めるステートマシンとして実装しました。現在の段階と入力済みの値をWorkers KVへ保存し、返信のたびに読み出します。これで、複数回のやり取りをひとつの投稿として扱えます。
画像変換やアップロードはwaitUntilでバックグラウンドへ回し、Slackには先にHTTP応答を返します。イベントの再送に備えて処理済みのevent_idも記録し、重複処理を抑えています。なお、KVだけでは厳密な排他制御にはならないため、同時実行への対策には限界があります。
画像とJSONをひとつのコミットにまとめる
画像はCloudflare Imagesで長辺800px以内に縮小し、品質80のWebPへ変換します。
保存にはGitHub Git Data APIを使います。画像とJSONのBlobを作り、それらを含むTree、Commitを作成して、最後にブランチの参照を更新します。画像とJSONを同じコミットにすることで、片方だけがサイトへ反映される状態を避けています。
動画も同じスレッドから投稿する
写真だけでなく、MP4やMOV形式の短い動画にも対応しています。日付、タイトル、リンクを設定する流れは写真と共通で、受け取ったメディアの種類に応じて変換処理を分けています。
動画はSlack側の変換が終わるまで取得できないことがあります。そのため、files.infoを間隔を空けて再取得し、H.264のMP4が用意されるのを合計最大30秒待ちます。準備が整わなければ、スレッドに案内を返します。
取得後の処理は、次の順序です。
- 動画の長さを確認する。15秒を超える場合は、勝手に切らずに短縮をお願いする
- Cloudflare Media Transformationsで幅800px以内・音声なしのMP4に整える
- 先頭フレームを切り出し、WebPのポスター画像を作る
- MP4、ポスター画像、表示用JSONをひとつのコミットにまとめる
長さを取得できなかった場合は、その旨を伝えたうえで、変換時の上限を15秒に制限します。JSONには動画とポスターの両方のパスを保存し、公開完了の通知は写真と同じ仕組みで行います。
「保存完了」と「公開完了」を分ける
GitHubへのコミットが成功しても、Cloudflare Pagesのデプロイが終わるまでは公開されません。
そこでコミット直後は公開待ちとし、GitHubのcheck_run Webhookでデプロイ成功を受信します。署名や対象リポジトリ、ブランチ、投稿のコミットがデプロイに含まれることを確認してから、Slackの表示を「UP DONE」に切り替えます。
Slack Connectで手間がかかったこと
今回、特に調整が必要だったのがSlack Connectです。ひとつの共有チャンネルに見えても、裏側には複数のワークスペースと、それぞれの認証・権限があります。
1. 認証トークンをワークスペースごとに管理する
アプリを一方のワークスペースへ導入しても、接続先で同じ認証が使えるわけではありません。Slackの公式ドキュメントでも、ワークスペースごとのインストールと認可の違いが説明されています。
今回の構成では、両組織でアプリを導入し、共有チャンネルへ追加しました。OAuthで取得したBotトークンはワークスペースIDとひも付けてKVに保存。イベントやボタン操作を受け取るたびに、対応するトークンを選んでAPIを呼び出します。
単一のBotトークンで動かす実装から、操作ごとに認証情報を選ぶ実装へ広げる必要がありました。
2. 「写真が見える」と「APIで取得できる」は別
Slackの非公開ファイルURLを取得するには、認証トークンを付けたリクエストが必要です。ファイル取得が403で拒否された場合、URLだけを調べても原因は分かりません。
確認するのは、主に次の項目です。
- Botに
files:readの権限があるか - 対象チャンネルにアプリが追加されているか
- ワークスペースに対応するトークンが保存・選択されているか
- 組織の管理設定でアプリ利用が許可されているか
さらに、Slack Connectではファイルの詳細がイベントに揃わず、file_access: check_file_infoとして届く場合があります。その場合はfiles.infoによる追加取得が必要です。認証だけでなく、受信データの違いも考慮する必要があります。Slack Connectのファイル仕様
3. ファイル通知とスレッドの起点を分ける
file_sharedイベントだけでは、返信先となる親メッセージを特定できない場合があります。
そのため、この通知では入力フローを開始せず、後続のファイル付きmessageイベントを起点にしました。ファイルが共有されたことと、どのメッセージに返信するかを分けて扱うことで、設定のやり取りを同じスレッドにまとめています。
LLMで、投稿にひと言返す
投稿した写真をサイトに反映する。それでツールとしての役割は果たせます。でも、僕はそれだけだと面白くない。せっかく作るなら、使うときに何か面白みがほしいと思いました。
そこで、写真を送ると、写っている内容に触れた短いコメントを返すようにしています。Cloudflare Workers AIの画像を読めるモデル、Llama 4 Scoutを使い、写真へのひと言を生成します。
自分が送った写真に反応が返ってくると、投稿がちょっとしたやり取りになります。ただの道具を作るだけでなく、使う人とのコミュニケーションを作りたい。僕はツールにも、そうあってほしいと思っています。
ちなみに、サムネイルがClippyなのは、Slack PostmanのSlack上のアイコンにもClippyを使っているからです。写真を投稿するたびにひと言コメントしてくる、あのおせっかいな感じが好きなんですよね。ここは完全に、僕(小岩原)の趣味です。
実装では、LLMに任せるのはこのリアクションの部分です。入力内容の管理や公開処理は通常のプログラムで進め、生成待ちは最大8秒。エラーやタイムアウト、長すぎる出力があれば定型文へ切り替えます。投稿の道具としてきちんと動くことと、使っていて面白いこと。その両方を持たせました。なお、画像を見てコメントを生成するのは写真のみで、動画には定型文からランダムに選んだリアクションを返しています。
いつものSlackを、更新の入口に
今回作ったのはWebサイトへの投稿ツールですが、同じ考え方はほかの用途にも応用できます。Slackを入口にして、更新したい場所へ情報を届ける仕組みです。
たとえば、こんな使い方が考えられます。
- Webサイトに活動の記録を。 現場で撮った写真や動画を送り、日々の出来事を掲載する。
- 店頭サイネージに新しい案内を。 新商品の写真やイベント情報を投稿し、お店の画面を更新する。
- 社内モニターにお知らせを。 Slackの連絡をオフィスの画面にも届ける。
たとえば「明日の10時から、この写真を店頭に出して」とSlackに書く。LLMが文章から日時や配信先を読み取り、確認が済んだら予約を登録し、指定時刻に配信する。
そんな仕組みも考えられます。曖昧な指定は聞き返し、実際の予約・配信はプログラムで管理します。
更新の流れも、運用に合わせて設計できます。担当者が確認してから反映する承認フロー、決まった時間に内容を切り替える予約更新、複数の店舗や施設へ届ける一括配信など。上の図は、こうした個別設計による活用例です。
IE3では、Webサイトやサイネージの制作に加えて、日々の更新を支えるツールづくりや業務の最適化もお手伝いしています。「これもSlackから更新できたら」というご要望があれば、ぜひご相談ください。使い慣れた場所から、無理なく続けられる仕組みを一緒に考えます。
