Skip to content

007 コミュニケーション設計 ​

ユーザーと開発者が、連絡先を交換せずに 1 対 1 の掲示板としてやりとりするための UI とデータフローを定義します。

コミュニケーション設計 v3.0 / 2026-09-21


1 目的 ​

これまでのフィードバック機能は、ユーザーから開発者へ一方向に届く仕組みでした。
前案では、開発者からの返信を付箋としてユーザーの画面に出す案を検討しましたが、これは体験として強すぎます。ユーザーの作業空間に開発者の返信が突然現れるため、誤配信時の心理的な負担も大きくなります。

本章では、返信を付箋として出すのではなく、設定画面内に「開発者とのやりとり」掲示板を作ります。ユーザーは必要なときだけ設定画面を開いて会話を確認します。

1.1 登場人物と用語 ​

本章では「サーバー」という曖昧な言葉を使わず、次の名前で役割を区別します。

表 1.1-1 登場人物と責務

No名前役割保持してよい情報
1PCアプリユーザーが投稿し、自分の会話ログを表示するconversation_id、生の secret_token、最終確認時刻
2Vercel APIPCアプリ、Firestore、DiscordをつなぐAPI処理Firebase Admin SDK認証情報、Discord Bot Token、ingest secret
3Firebase / Firestore会話データの正本を保存するDBsecret_token_hash、会話本文、開発者返信、Discord通知ID
4Discordサーバー開発者がユーザー投稿を確認し、同じ通知に返信する場所Discord通知メッセージ、開発者返信
5Discord WebhookVercel APIからDiscordサーバーへ通知を投稿する入口Webhook URL
6Discord BotDiscordサーバー上の返信をVercel APIへ取り込むために読む主体Bot Token
7Appwrite Storageユーザーが掲示板で明示的に添付した画像本体を保存する画像ファイル、所有者を示す読取・削除権限

2 方針 ​

表 2-1 方式の決定

No案採否理由
1開発者返信を付箋として出す不採用ユーザーの作業空間に突然表示され、体験として踏み込みすぎる
2Discord チャンネルをユーザー数分作る不採用チャンネル数、権限、API制限、削除、運用管理が重すぎる
3設定画面内に 1 対 1 掲示板を作る採用ユーザーが見に行く形なので自然で、誤表示時の影響範囲も設定画面内に閉じる
4ユーザーごとに会話ファイルを作って自前同期する将来候補考え方は近いが、初期実装では Vercel API + Firebase / Firestore で会話データを管理する方が単純

3 ユーザー体験 ​

設定画面に「開発者とのやりとり」を追加します。見た目は、シンプルな掲示板またはチャットログです。

表 3-1 ユーザー側 UI

No画面・状態表示操作
1設定画面「開発者とのやりとり」メニューユーザーが開く
2初回表示まだやりとりがない旨、本文入力欄、画像選択、送信ボタン必須の本文を書き、必要ならPNG・JPEG・WebP画像を最大3枚選ぶ
3送信後自分の投稿本文が会話ログに追加される。送信済み画像は会話ログへ再表示しないDiscordで開発者が本文と画像を確認する。必要なら続けて投稿する
4返信あり「アプリ開発者」名義の返信が会話ログに表示される読む、必要なら返信する
5未読返信あり右クリックメニューの「開発者とのやりとり」に ● 新着あり を付ける開いて確認する
6返信なし何も割り込んで表示しない通常利用を続ける
7通信失敗設定画面内に控えめな再試行表示付箋や通常操作には影響しない

表 3-2 会話ログの表示例

発言者表示例
ユーザーこんにちは
アプリ開発者こんにちは
ユーザー付箋の色を増やしてほしいです。
アプリ開発者ありがとうございます。検討します。

4 全体構成 ​

ユーザー側は設定画面だけを使います。開発者はDiscordで通知を読み、その通知に返信します。開発者PCのアプリが起動中で自動取り込みが有効なら、約1分ごとに新しいDiscord投稿だけをFirestoreへ取り込みます。ローカル会員ダッシュボードは一斉・宛先指定のお便り配信に使い、個別会話には使いません。Discord はユーザーに直接見せません。

図 4-1 1 対 1 掲示板方式の全体構成

表 4-1 責務

領域責務
PCアプリ匿名会話IDの保持、掲示板UI表示、投稿、画面表示中は約1分ごとの会話更新、投稿後24時間は常駐中に約10分ごとの新着確認、それ以外は日次確認
Vercel APIsecret token 照合、Firestore保存、Discord通知、Discord返信取り込み
Firebase / Firestore会話本文、開発者返信、既読状態、Discord通知との対応を永続保存
Appwrite Storage掲示板で明示的に選択した画像本体とファイル権限を保存する。会話本文の正本にはしない
Discordユーザー投稿を開発者へ知らせ、開発者が同じ通知に返信する裏側 UI
開発者Discordの対象通知に返信する。会員ダッシュボードではお便りを配信する

5 データモデル ​

ユーザーをメールアドレスや Discord ID で識別しません。アプリがローカルに保持する匿名の conversation_id と、本人確認用の secret_token で会話を守ります。

表 5-1 ローカルに保存する情報

No情報保存場所用途
1conversation_idPCローカル設定このアプリの掲示板を識別する
2secret_tokenPCローカル設定他人が会話を読めないようにする
3last_message_check_atPCローカル設定従来の確認時刻を保持する
4has_unread_developer_replyPCローカル設定右クリックメニューの新着表示に使う
5last_unread_check_datePCローカル設定JST 4:00 頃の自動確認を同じ日に重複実行しない
6投稿後24時間の期限・前回確認時刻PCローカル設定会話中だけ常駐確認を約10分間隔にする
7通知済みの返信IDPCローカル設定同じ返信の小窓を繰り返し出さない
8作者PCのingest secretと自動取り込み設定作者PCのlocalStorageのみ本人のPCだけでDiscord返信の自動取り込みを認証する
9作者PCのDiscord最終確認ID作者PCのlocalStorageのみ次回は新しい投稿だけを確認する

表 5-2 Firebase / Firestore に保存する情報

No情報用途
1conversation_id会話スレッドの識別
2secret_token_hashPCアプリからの読取・送信権限確認
3messages[]ユーザー投稿と開発者返信の本文
4discord_message_idDiscord通知と会話の紐付け
5author_typeuser または developer
6read_by_user開発者返信をユーザーが見たかどうか
7statusshadow accepted、ignored など

表 5-3 `secret_token` の発行と更新

No項目仕様
1発行タイミングPCアプリが開発者とのやりとりを初めて使い、ローカルに secret_token がないときに発行する
2更新タイミング通常は自動更新しない。ローカル保存が消えた、別環境を使った、または値が欠けた場合に新しく発行する
3長さ32バイトの乱数を base64url 文字列にするため、通常は43文字になる
4生成方法暗号用途の乱数生成器で32バイトを作り、URLに含められる base64url 形式へ変換する
5Firestore保存生の secret_token は保存せず、secret_token_hash だけを Firestore に保存する

表 5-4 会話データの分離ルール

Noルール理由
1Firestore の会話正本は feedback_conversations/{conversation_id} 単位で分離するユーザーごとの掲示板をDB上でも別スレッドとして扱う
2conversation_id だけで本人確認しないIDが見えたり推測された場合でも、会話を読ませない
3読み取り、投稿、既読更新は secret_token_hash が一致した場合だけ許可する別ユーザーの会話ログが返る事故を防ぐ
4Discord返信は元の通知メッセージまたは通知内の会話IDから対象会話を確定できた場合だけ保存するDiscord内の無関係な投稿がユーザーの掲示板に混入しないようにする
5対象会話を確定できない、token が一致しない、または重複している場合は保存せず拒否する判断できないものは表示しない fail closed にする
6右クリックメニュー表示時は Firestore へ問い合わせないメニュー表示を遅くせず、通信失敗で右クリックが効かなくなる事故を防ぐ
7掲示板を開いて表示できた開発者返信だけを既読化する見ていない返信を既読にしない

Firestore は Vercel の中にある保存領域ではなく、Firebase が提供する外部の永続データベースです。Vercel API は、Vercelの環境変数に設定した Firebase Admin SDK 認証情報を使って Firestore を読み書きします。PC アプリや iPhone PWA に Firebase の管理用認証情報を配布しません。

txt
feedback_conversations/{conversation_id}
  secret_token_hash
  created_at
  updated_at
  discord_message_id
  delivery_enabled

feedback_conversations/{conversation_id}/messages/{message_id}
  author_type: user | developer
  body
  created_at
  discord_message_id
  read_by_user
  shadow_only

表 5-5 掲示板へ添付した画像の保存情報

No情報保存場所用途
1画像本体Appwrite StorageDiscordで開発者が画像を直接確認する
2file_idPCアプリからVercel APIへ送信本文送信時に添付画像を指定する
3MIME形式・容量・権限Appwrite StorageのファイルメタデータVercel APIが形式、5MiB以下、会話所有者の読取・削除権限を確認する
415分間の表示トークンAppwrite StorageDiscordが非公開画像を短時間だけ表示する

画像バイトはVercel APIやFirestoreへ保存しません。送信途中で失敗した場合、PCアプリは今回アップロードした画像の削除を試みます。送信成功後の30日自動削除は今回の最小版には含めず、別対応とします。


6 シーケンス ​

6.1 ユーザーからの問い合わせ ​

個別の会話は、ユーザー側では「開発者とのやりとり」、開発者側ではDiscordで続けます。添付画像がある場合の詳細は図 6-2を参照します。

図 6-1 ユーザーからの問い合わせとDiscordでの返信

6.2 添付画像を含む問い合わせの送信 ​

画像本体はPCアプリからAppwrite Storageへ直接アップロードします。Vercel APIには本文と画像の参照情報を送ります。Discordからユーザーへ画像を返信する経路は含めません。

図 6-2 PCからDiscordへ本文と最大3枚の画像を送る最小版シーケンス

6.3 開発者からのお便りと返信 ​

ダッシュボードは全会員または指定した宛先へのお便り配信に使います。お便りを受け取ったユーザーが返信した後は、図 6-1と同じ個別会話としてDiscordで続けます。ユーザーの返信には元のお便りのタイトルとIDを付け、アプリでは元のお便りとの対応を表示します。

図 6-3 開発者からのお便りとDiscordで続ける個別会話


7 Discord連携 ​

Discordは、開発者の作業場所としてだけ使います。ユーザーごとのDiscordチャンネルは作りません。 開発者は、1つの開発者用チャンネルに届く通知を見て、対象通知に返信を書きます。 会話の正本は Firestore に保存します。作者PCのアプリは保存済みの管理用secretで約1分ごとに新着Discord投稿だけを取り込み、日次Cronも予備として継続します。

表 7-1 Discordをユーザー数分作らない理由

No理由内容
1運用負荷ユーザー数分のチャンネル、権限、削除、検索を管理できない
2API制限大量チャンネル作成・更新はDiscord側の制限を受けやすい
3誤操作リスク開発者がチャンネルを間違えると、別ユーザーの会話に返信しやすい
4プライバシーDiscord側の構造にユーザー数や会話単位が露出しやすい

表 7-2 開発者側の確認方法

No項目内容
1確認場所Discord の新着通知と返信スレッド
2会話単位会員番号と会話IDで対象を確認する
3返信方法開発者はDiscordの対象通知へ返信する
4正本Firestore の conversation_id 単位の会話データ
5Discordの役割新着投稿の通知と開発者返信。ユーザーに直接見せる画面ではない
6過去ログDiscord通知に直近5件のやりとりを添える。久しぶりの相手にも最低限の文脈を持って返せる
7100人規模チャンネルは増やさず、通知とスレッドで扱う。未対応・対応済み・重要などの管理が必要になったら Firestore を読む管理画面を追加する

表 7-3 開発者返信の取り込み条件

No条件対策
1元の会話通知に紐付いているdiscord_message_id -> conversation_id の対応表で確認する
2開発者として許可されているALLOWED_DISCORD_USER_IDS に含まれる author_id だけを受け付ける
3Bot自身ではないBot投稿は取り込まない
4空本文ではない空文字、空白だけの返信は取り込まない
5未取り込みである同じDiscord返信IDは一度だけ保存する
6shadow mode を通過している初期運用では保存してもユーザーには返さない

7.1 過去ログの確認 ​

開発者が返信するときは、直近の投稿だけでなく、その conversation_id の直近5件のやりとりを確認できるようにします。
これにより、以前やりとりした相手に「お久しぶりです」と自然に返せます。

表 7.1-1 過去ログ確認の方針

No項目内容
1保存先Firestore の feedback_conversations/{conversation_id}/messages
2並び順created_at 昇順でユーザー投稿と開発者返信を表示する
3開発者の確認方法Discord通知に直近5件の会話ログを含める
4Discord上の限界Discordの過去表示だけを正本にしない。削除・流れ・検索性の問題があるため Firestore を正本にする
5ユーザー側設定画面の掲示板で、自分の会話履歴を確認できる
6全履歴初期実装では開発者向けに全履歴UIは作らない。必要になったら管理画面で追加する

8 安全要求と距離感 ​

この機能で特に守る安全要求は4つです。加えて、ユーザーと開発者の距離感を守ります。
開発者から返信できることは便利ですが、ユーザーの作業空間へ突然入り込む体験にはしません。ユーザーが必要なときに設定画面を開き、自分の意思で確認できる関係にします。

表 8-1 安全要求と対策

No要求失敗した場合対策
1返信は元のユーザーだけに届く別ユーザーの掲示板に返信が表示される。これはセキュリティ事故として扱うconversation_id + secret_token が一致した場合だけ返す。conversation_id だけでは返さない
2開発者の返信だけが届くDiscord内の無関係な投稿が表示される許可済みDiscordユーザーID、返信元メッセージID、重複状態をすべて検証する
3DB上で他人の会話と混ざらないFirestoreから別ユーザーの messages を取得または保存するfeedback_conversations/{conversation_id} 単位で保存し、読み書きのたびに secret_token_hash を照合する
4判断できない返信は表示しない対象会話が不明なDiscord投稿がユーザーの掲示板に出る会話IDを確定できない返信は保存せず、ignored または rejected として扱う

表 8-2 ユーザーとの距離感を守る制約

No制約理由
1付箋として自動表示しない開発者の返信本文が付箋の作業空間に混ざらない
2小窓で新着を知らせ、本文は会話画面で見る普段の常駐利用で気づけて、内容は本人が開いて確認する
3会話中だけ高頻度に確認する会話の応答速度と通常利用時の通信量を両立する
4返信がない日は何も出さない通常利用の流れを邪魔しない
5kill switch を持つ問題があれば Vercel API 側で即停止できる
6shadow mode から始める実配信前にDiscord返信分類を確認できる

9 API設計 ​

表 9-1 API一覧

NoAPI用途
1POST /api/feedback/conversation/messagesユーザー投稿を保存し、Discordへ通知する
2POST /api/feedback/conversation/poll設定画面が会話ログ・未読返信を取得する
3POST /api/feedback/conversation/ackユーザーが見た返信を既読にする
4POST /api/feedback/conversation/deletesecret tokenを照合し、会話・メッセージ・Discord対応表を完全に削除する
5POST /api/feedback/discord/ingest開発者PCのアプリが約1分ごとに新着Discord返信を取り込む。手動実行も可能
6GET /api/feedback/discord/cronVercel CronでDiscord返信を取り込む
7POST /api/feedback/conversation/session会話を認証し、PCがAppwrite Storageへ直接画像を送るための15分JWTを発行する

公開POST APIはJSON本文と各文字列の長さを検証する。/api/feedback と conversation/messages は本文32KB以内、conversation/poll は4KB以内、conversation/ack は16KB以内とする。Discord Webhookへの送信は10秒で中止し、応答待ちによるVercel実行枠の占有を防ぐ。

2 の conversation/poll は、PC アプリが「自分の掲示板を見せる」ために呼びます。右クリックメニュー表示時には呼びません。
PC アプリは通常 JST 4:00 頃に1日1回 conversation/poll を呼びます。ユーザーの投稿成功後24時間は常駐中に約10分ごとに確認します。「開発者とのやりとり」を開いている間は約1分ごとに取得します。未読返信はローカルの has_unread_developer_reply を true にし、新しい返信IDごとに小窓を1回表示します。掲示板に表示できた返信だけ conversation/ack で既読化します。 5 の discord/ingest は、開発者PCのアプリが自動取り込みを有効にした場合に約1分ごとに呼びます。Discordの前回確認IDをそのPCだけに保持し、次回は新しい投稿だけを確認します。新着がなければFirestoreへはアクセスしません。失敗時は5分待って再試行し、カーソルを進めません。管理者ツールからの手動実行も可能です。 6 の discord/cron は、Vercel Cron が同じ取り込み処理を本番環境で JST 3:00 に1日1回実行するために呼びます。CRON_SECRET による Authorization ヘッダー認証を必須にします。 7 の conversation/session は画像添付時だけ呼び、Vercel APIが画像バイトを中継しない直接アップロードに使います。

管理者ツールでは開発者PCの localStorage に FEEDBACK_CONVERSATION_INGEST_SECRET を保存できます。自動取り込みは保存と専用チェックを両方有効にした場合だけ動作し、配布アプリにはsecretを埋め込みません。CRON_SECRET は通常PCアプリで入力しないため、保存対象にしません。

表 9-2 環境変数

No環境変数用途
1FEEDBACK_CONVERSATION_ENABLED掲示板機能の有効・無効
2FEEDBACK_CONVERSATION_SHADOW_MODE返信を保存するがユーザーへ返さない運用
3FEEDBACK_CONVERSATION_INGEST_SECRETDiscord取り込みAPIの認証
4DISCORD_BOT_TOKENDiscord返信の読み取り
5DISCORD_FEEDBACK_CHANNEL_ID監視対象チャンネル
6ALLOWED_DISCORD_USER_IDS返信を許可する開発者ID
7FIREBASE_PROJECT_IDFirestore のプロジェクト識別子
8FIREBASE_CLIENT_EMAILVercel API が使う Firebase サービスアカウント
9FIREBASE_PRIVATE_KEYFirestore 読み書き用の秘密鍵
10FIREBASE_DATABASE_IDFirestore のデータベースID。通常は (default)

10 実装境界 ​

表 10-1 今回実装するもの・しないもの

区分内容
実装する設定画面内の掲示板、匿名会話ID、secret token、Firestore保存、本文+最大3枚の画像をDiscordへ送信、開発者PCでのDiscord返信自動取り込み、会話中の短い間隔の確認、新着小窓
実装しない返信付箋、ユーザー数分のDiscordチャンネル、自動プッシュ通知、Google Drive内の会話ファイル同期、Discordからユーザーへの画像返信、送信済み画像の会話ログ再表示、30日自動削除
将来検討開発者からの画像返信、画像の自動削除、既読表示、サポート対応ステータス

11 リリース手順 ​

表 11-1 リリース手順

No手順確認
1FEEDBACK_CONVERSATION_ENABLED=false でデプロイ既存フィードバック送信が壊れない
2Discord通知だけ有効化開発者に投稿が届く
3shadow mode で返信分類誤分類がない
4自分のテスト会話だけで取得確認正しい会話にだけ返信が出る
5public delivery 有効化設定画面内に返信が表示される
6監視rejected理由、重複、誤配信ゼロを確認する

12 改版履歴 ​

表 12-1 改版履歴

Noバージョン日付変更内容
11.026-06-01ユーザーと開発者の 1 対 1 会話、ありがとう付箋、開発者返信付箋、日次確認、安全制約を定義
21.126-06-01返信が元ユーザーだけに届くこと、開発者返信だけが届くことの安全要求を追加
32.026-06-01返信付箋方式を廃止し、設定画面内の 1 対 1 掲示板方式へ変更
42.126-06-01会話ストアを Firebase / Firestore 前提に変更し、API図と環境変数を更新
52.226-06-01「怖くないための制約」を「ユーザーとの距離感を守る制約」に変更し、開発者が作業空間へ割り込まない意図を明確化
62.326-06-01開発者がDiscordで会話を確認する方法、100人規模でもチャンネルを増やさない運用、管理画面追加の判断基準を追加
72.426-06-01開発者が直近5件の過去ログを確認して返信できる方針を追加し、Firestoreを会話履歴の正本と明記
82.526-06-02secret_token の発行仕様と、Firestore上で他人の会話を混在させない分離ルールを追加
92.626-06-02登場人物の用語を定義し、曖昧な「サーバー」表現を Vercel API、Firebase / Firestore、Discordサーバーに分解
102.726-06-04Vercel Cron は JST 3:00、PCアプリは JST 4:00 頃に1日1回確認する運用を追加。右クリックメニューは通信せずローカル未読状態だけで新着表示し、掲示板表示時に既読化する仕様を追加
112.826-06-05管理者ツールの手動 ingest 用に、開発者PCの localStorage へ FEEDBACK_CONVERSATION_INGEST_SECRET を保存できる仕様を追加。CRON_SECRET は保存対象外と明記。
122.926-07-31公開フィードバックAPIのJSON本文・文字列上限と、Discord Webhook通信の10秒タイムアウトを追加。
133.026-09-21掲示板の必須本文と最大3枚の画像を、15分JWTでPCからAppwrite Storageへ直接アップロードし、Vercel APIのメタデータ検証後にDiscordへ本文と画像を送る最小版シーケンスを追加。画像返信、会話ログへの画像再表示、30日自動削除は対象外とした。
143.126-09-28会話表示中の約1分更新、投稿後24時間の常駐中約10分確認、新着返信の通知小窓を追加。
153.226-09-28開発者返信をDiscordに一本化し、作者PCで約1分ごとに新着だけを自動取り込みする方式を追加。会員ダッシュボードはお便り配信専用とする。
163.326-09-28重複したシーケンス図を6章へ集約。ユーザーからの問い合わせ、画像添付、開発者からのお便りと返信を分け、Discord通知成功後に会話へ保存する順序を実装と一致させた。9章はAPIと実行間隔の説明に限定。