以下 README.md
-----
# ささっとチャット掲示板 — Google Chat <-> スプレッドシート <-> Webアプリ
Google Chat のスペースとスプレッドシートを双方向連携し、掲示板風の Web アプリとして表示するシステムです。
---
## システムアーキテクチャ
```
┌─────────────────────────────────────────────────────────────────┐
│ Google Chat スペース │
│ (spaces/XXXXXXXXX) │
└──────────┬──────────────────────────────────┬──────────────────┘
│ ① 定期取得(1分ごと) │ ④ スレッド返信
│ Chat拡張サービス │ サービスアカウント
│ (ユーザーOAuth) │ (JWT
chat.bot)
▼ │
┌──────────────────────────────────────────────┴──────────────────┐
│ Google Apps Script (GAS) │
│ コンテナバインド型 │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │fetchNew │ │submitFromWeb │ │doGet │ │
│ │Messages() │ │() │ │() │ │
│ │ │ │ │ │ │ │
│ │ Chat API │ │ SS記録 │ │ HTML生成 │ │
│ │ ↓ │ │ │ │ ↓ │ │
│ │ SS記録 │ │ Chat API投稿 │ │ 掲示板ページ │ │
│ │ │ │ │ │ │ │
│ │ 添付→Drive │ │ 添付→Drive │ │ │ │
│ └─────────────┘ └──────────────┘ └────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 時間主導型トリガー │ │
│ │ 06:00 startFetchTrigger() ──→ 1分ごとに取得開始 │ │
│ │ 23:00 stopFetchTrigger() ──→ 取得停止 │ │
│ │ LockService で同時実行防止 │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────┬──────────────────────────────────┬──────────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌──────────────────────────────┐
│ Google スプレッド │ │ Web ブラウザ(掲示板UI) │
│ シート │ │ │
│ │ │ ┌────────────────────────┐ │
│ ┌────────────────┐ │ │ │ ヘッダー │ │
│ │「ログ」シート │ │ │ │ 🔍検索 | 日付↓ |全文|更新│ │
│ │ A:タイムスタンプ │ │ │ ├────────────────────────┤ │
│ │ B:日付 │ │ │ │ 📌 ピン留めセクション │ │
│ │ C:時刻 │ │ │ ├────────────────────────┤ │
│ │ D:投稿者 │ │ │ │ ── 2026/03/22(日) ── │ │
│ │ E:本文 │ │ │ │ ┌──────────────────┐ │ │
│ │ F:添付ファイル │ │ │ │ │ 投稿カード │ │ │
│ │ G:スレッドID │ │ │ │ │ [↩][📌][🗑] │ │ │
│ │ H:返信フラグ │ │ │ │ │ ├─ 返信1 │ │ │
│ │ I:メッセージID │ │ │ │ │ └─ 返信2 │ │ │
│ │ J:削除フラグ │ │ │ │ └──────────────────┘ │ │
│ │ K:ピン留め │ │ │ ├────────────────────────┤ │
│ ├────────────────┤ │ │ │ 投稿フォーム │ │
│ │「設定」シート │ │ │ │ [名前][本文][ ][送信] │ │
│ │ SPACE_ID │ │ │ └────────────────────────┘ │
│ │ users/ID→名前 │ │ │ │
│ └────────────────┘ │ │ localStorage: │
│ │ │ bulletinName (投稿者名) │
└─────────────────────┘ │ lastVisit (NEW判定用) │
└──────────────────────────────┘
│
▼
┌─────────────────────┐
│ Google Drive │
│ 「掲示板_添付 │
│ ファイル」フォルダ │
│ │
│ 画像・PDF・Office │
│ ANYONE_WITH_LINK │
└─────────────────────┘
```
## データフロー
```
【読み取り】Chat → スプレッドシート
Chat スペース
│
│ Chat.Spaces.Messages.list()
│ (ユーザーOAuth / chat.messages.readonly)
│ ページネーション対応(100件ずつ全件取得)
▼
fetchNewMessages()
│
├─ メッセージID で重複チェック ──→ 既存ならスキップ
├─ sender.type === 'BOT' ──→ スキップ
├─ 添付ファイルあり ──→ Chat API media download ──→ Drive保存
├─ 設定シートからユーザーID→名前マッピング
├─ スレッドID でisReply判定
└─ スプレッドシートに appendRow
```
```
【書き込み】Web → スプレッドシート Chat
ブラウザ(投稿フォーム)
│
│
google.script.run.submitFromWeb()
│ テキスト ファイル(base64)
▼
submitFromWeb()
│
├─ ファイル → Drive保存(掲示板_添付ファイルフォルダ)
├─ スプレッドシートに appendRow
└─ postToGoogleChat()
│
│ サービスアカウント JWT
│
chat.bot スコープ
│ スレッド返信対応 (messageReplyOption)
▼
Chat スペースに投稿
```
```
【表示】スプレッドシート → Web
doGet()
│
├─ refreshMessages() ──→ バックグラウンドで新着取得
├─ スプレッドシート全行読み取り
├─ DEBUG行・削除フラグ行を除外
├─ スレッド構造を構築(親→子)
├─ ピン留め投稿を分離(上部固定)
├─ 日付別グループ化
└─ HTML生成 ──→ ブラウザに返却
```
## 機能一覧
### コア機能
| 機能 | 説明 |
|---|---|
| Chat自動取得 | 1分ごとにChat APIでメッセージ取得(6:00〜23:00) |
| Web投稿 | テキスト ファイル添付(10MB上限)→ SS Chat同時投稿 |
| スレッド返信 | 親投稿にツリー形式で返信表示 |
| 添付ファイル | Chat/Web両方の添付をDriveに自動保存、リンク表示 |
| 削除 | 削除フラグ方式(物理削除なし、復元可能) |
| ピン留め | 重要な投稿を最上部に固定表示 |
### UI/UX
| 機能 | 説明 |
|---|---|
| 検索 | リアルタイム絞り込み キーワードハイライト 件数表示 |
| 日付ジャンプ | プルダウンから日付選択でスクロール |
| NEW バッジ | 前回閲覧以降の新着投稿に赤いマーク |
| 全文/折畳トグル | 全投稿を一括展開/折畳 |
| トップに戻る | スクロール時に↑ボタン表示 |
| 投稿数表示 | ヘッダーに総件数 |
| 名前記憶 | localStorageで投稿者名を保存 |
### アニメーション(CSS Transitions、ライブラリ不使用)
| アニメーション | 説明 |
|---|---|
| 投稿スライドイン | 新規投稿が左からポコンと出現 |
| 返信スライドイン | 返信がスレッド内にスッと出現 |
| 削除スライドアウト | 右にスライド→高さ縮小→隙間詰め(Gmail風) |
| ホバーアクション | ふわっと浮き上がって出現/消失 |
| カードホバー | ふわっと浮き上がる影効果 |
| 送信ボタン | ホバーで拡大 回転 |
| トースト | バウンスポップアップ |
### バッジアイコン
| バッジ | 色 | 意味 |
|---|---|---|
| 🖼 N | 黄色 | 画像添付あり |
| 📎 N | ピンク | ファイル添付あり |
| 🔗 N | グレー | リンクあり |
| 💬 N | 青 | 返信あり |
## スプレッドシート構成
### 「ログ」シート
| 列 | ヘッダー | 内容 | 例 |
|---|---|---|---|
| A | タイムスタンプ | メッセージ作成日時 | 2026/03/22 09:15:00 |
| B | 日付 | yyyy/MM/dd | 2026/03/22 |
| C | 時刻 | HH:mm | 09:15 |
| D | 投稿者 | 表示名 | ささ |
| E | 本文 | メッセージテキスト | 明日の会議の資料を… |
| F | 添付ファイル | ファイル名::DriveFileID | 資料.pdf::1abc2def |
| G | スレッドID | spaces/xxx/threads/yyy | spaces/AAA/threads/BBB |
| H | 返信フラグ | TRUE/FALSE | TRUE |
| I | メッセージID | spaces/xxx/messages/yyy | spaces/AAA/messages/CCC |
| J | 削除 | TRUE で非表示 | TRUE |
| K | ピン留め | TRUE で上部固定 | TRUE |
### 「設定」シート
| A列(キー) | B列(値) | 説明 |
|---|---|---|
| SPACE_ID | spaces/XXXXXXXXX | 連携するChatスペースのID |
| users/117209... | ささ | ユーザーIDと表示名のマッピング |
### スクリプトプロパティ
| キー | 値 | 用途 |
|---|---|---|
| CHAT_SA_KEY | サービスアカウントJSON全体 | Web→Chat投稿時の認証 |
## セットアップ手順
### 1. GCPプロジェクト作成
1. [Google Cloud Console](
console.cloud.google.com) でプロジェクトを作成
2. **Google Chat API** を有効化
3. **Google Drive API** を有効化
4. **OAuth 同意画面** を設定(内部、アプリ名は任意)
5. サービスアカウントを作成し、JSONキーをダウンロード
### 2. スプレッドシート GAS
1. 新規スプレッドシートを作成
2. 「拡張機能」→「Apps Script」を開く
3. `bulletin_board.gs` のコードを貼り付け
4. 「プロジェクトの設定」→「GCP プロジェクト番号」を入力
5. `setupSheets` を実行(シート自動作成)
6. 設定シートに `SPACE_ID` を入力
7. スクリプトプロパティに `CHAT_SA_KEY` を設定
### 3. デプロイ
1. 「デプロイ」→「新しいデプロイ」→ ウェブアプリ
2. 実行ユーザー:**自分**、アクセス:**全員**
3. デプロイ → URL をコピー
### 4. Chat Bot 登録(Web→Chat投稿用)
1. Cloud Console → Chat API →「構成」
2. HTTP エンドポイント URL にデプロイ URL を設定
3. Bot をスペースに追加
### 5. トリガー設定
Apps Script エディタで `setupSchedule` を実行
→ 6:00〜23:00 の間、1分ごとに自動取得開始
### 6. ユーザーマッピング
設定シートに `users/ユーザーID | 表示名` の形式で追加
(`listMembers` 関数でID一覧を取得可能)
### 7. ポータルサイトに埋め込み
```html
<iframe src="
script.google.com/macros/s/デプロイID/exec"
width="100%" height="800" frameborder="0"></iframe>
```
## 主要関数一覧
```
fetchNewMessages() Chat APIでメッセージ取得→SS記録
fetchNewMessagesInner() 実際の取得処理(ロック内で実行)
setupSchedule() 6:00-23:00の自動取得スケジュール設定
startFetchTrigger() 1分トリガー開始
stopFetchTrigger() 1分トリガー停止
refreshMessages() バックグラウンド最新取得(Web用)
doGet() 掲示板HTML生成
doPost() Chat webhook応答
submitFromWeb() Web投稿→SS Chat
postToGoogleChat() サービスアカウントでChat投稿
getChatAccessToken() JWT→アクセストークン取得
deletePost() 削除フラグ設定
togglePin() ピン留めトグル
setupSheets() 初期シートセットアップ
getConfig() 設定シート読み取り
getAttachmentFolder() 添付ファイルフォルダ取得/作成
buildHtml() HTML組み立て
buildPostCard() 投稿カードHTML生成
```
## 技術スタック
```
Backend: Google Apps Script (V8)
Frontend: HTML5 CSS3 Vanilla JS(フレームワーク/ライブラリなし)
Storage: Google Spreadsheet Google Drive
API: Google Chat API (拡張サービス REST)
Auth: OAuth 2.0 (ユーザー) JWT (サービスアカウント)
Deploy: clasp (CLI) GAS Web App
```
## ファイル構成
| ファイル | 説明 |
|---|---|
| `bulletin_board.gs` | GAS全コード(バックエンド フロントエンドHTML) |
| `appsscript.json` | マニフェスト(スコープ、依存関係) |
| `bulletin_preview.html` | デザインプレビュー(ダミーデータ入り) |
| `.clasp.json` | clasp設定(スクリプトID、親ID) |
| `作業ログ_20260322.md` | 開発作業ログ |
| `README.md` | 本ドキュメント |