# LINE × Vercel × Obsidian 音声日記パイプライン構築ガイド

LINEに話しかけるだけで、Obsidianのデイリーノートに自動で日記が溜まる仕組みを作ります。

---

## 全体像

```
スマホの LINE でメッセージ送信
    ↓
LINE Bot が受信（自動）
    ↓
Vercel のサーバーレス関数がメッセージを保存（Upstash Redis）
    ↓
Mac の常駐スクリプトが5分おきに取得
    ↓
Obsidian のデイリーノート（YYYY-MM-DD.md）に自動追記
```

### 使うサービス（すべて無料）

| サービス | 役割 | 費用 |
|---|---|---|
| LINE Messaging API | メッセージの受け口 | 無料（月200通まで） |
| Vercel | サーバーレス関数のホスティング | 無料（Hobbyプラン） |
| Upstash Redis | メッセージの一時保存 | 無料（日10,000リクエストまで） |
| Obsidian | ノート管理 | 無料 |

### 前提条件

- Mac（Apple Silicon / Intel 両対応）
- Node.js v18 以上がインストール済み
- Vercel CLI がインストール済み（`npm i -g vercel`）
- Obsidian がインストール済みで、Vault が作成済み
- LINE アカウントを持っている

---

## Step 1: LINE 公式アカウントを作成する

LINE にメッセージを受け取る Bot を作ります。公式アカウントは何個でも無料で作れます。

### あなたがやること

#### 1-1. LINE Developers にログイン

1. ブラウザで [LINE Developers](https://developers.line.biz/ja/) を開く
2. 画面右上の **「ログイン」** をクリック
3. 普段使っている LINE アカウントでログイン（メールアドレス + パスワード、または QR コード）

#### 1-2. プロバイダーを確認

1. ログイン後、**コンソール画面**が表示される
2. プロバイダー（開発者名のようなもの）が一覧に表示される
3. すでにプロバイダーがあればそれを使ってOK
4. **なければ**「作成」ボタンを押して新規作成（名前は何でもOK、例：`my-tools`）

#### 1-3. Messaging API チャネルを作成

1. プロバイダーを選択して中に入る
2. **「Messaging API」** を選択
3. 現在は LINE Developers コンソールから直接は作れなくなっているため、画面に表示される **緑色の「LINE公式アカウントを作成する」ボタン** をクリック
4. **LINE Official Account Manager** という別サイトに移動する
5. 以下を入力して作成：
   - **アカウント名**: 好きな名前（例：`音声日記Bot`）
   - **業種**: 「個人」を選択
   - **プライバシーポリシー**: 空欄のまま（任意）
   - **利用規約**: 空欄のまま（任意）

#### 1-4. Messaging API を有効化

1. LINE Official Account Manager の画面で、左メニューの **「設定」**（歯車アイコン）をクリック
2. 左メニューから **「Messaging API」** を選択
3. **「Messaging APIを利用する」** をクリック
4. プロバイダーを選ぶ画面が出る → さっき作ったプロバイダー、または既存のものを選択
5. 有効化完了

#### 1-5. 応答メッセージをオフにする

LINE 公式アカウントにはデフォルトの自動応答が設定されています。これが邪魔になるのでオフにします。

1. LINE Official Account Manager の左メニュー **「設定」** → **「応答設定」**
2. **「応答メッセージ」をオフ** にする

#### 1-6. 必要な値を2つ控える

ここからは **LINE Developers コンソール** に戻って作業します。

1. [LINE Developers コンソール](https://developers.line.biz/console/) を開く
2. プロバイダー → さっき作成したチャネルをクリック

**Channel Secret を取得：**
1. **「チャネル基本設定」タブ** を開く
2. 「Channel secret」の横にある **「コピー」ボタン** をクリック
3. テキストエディタ（メモ帳など）に貼り付けて保存

**Channel Access Token を取得：**
1. **「Messaging API設定」タブ** に切り替える
2. 一番下にスクロールして **「チャネルアクセストークン（長期）」** を見つける
3. **「発行」ボタン** をクリック
4. 表示された長い文字列の横にある **「コピー」ボタン** をクリック
5. テキストエディタに貼り付けて保存

> **重要**: この2つの値は後で使います。コードに直接書かず、テキストエディタに一時的に保存しておいてください。

---

## Step 2: Upstash Redis を作成する

LINE から受け取ったメッセージを一時的に保存するデータベースを作ります。

### あなたがやること

1. ブラウザで [Upstash Console](https://console.upstash.com/) を開く
2. **Google アカウントでログイン**（新規登録も無料、クレジットカード不要）
3. ダッシュボードが表示されたら **「Create Database」** をクリック
4. 以下を入力：
   - **Name**: `voice-diary`
   - **Region**: `ap-northeast-1`（Tokyo）を選択
5. 画面に **「Monthly: $0」** と表示されていることを確認
6. **「Create」** をクリック

#### 必要な値を2つ控える

データベース作成後の画面に、接続情報が表示されます。

1. **UPSTASH_REDIS_REST_URL** — `https://xxxx-xxxx.upstash.io` という形式の URL
2. **UPSTASH_REDIS_REST_TOKEN** — 長い文字列

それぞれの横にある **コピーボタン** をクリックして、テキストエディタに貼り付けて保存してください。

---

## Step 3: プロジェクトを作成する

ここからはターミナル（Mac の「ターミナル」アプリ）での作業です。

### あなたがやること

#### 3-1. ターミナルを開く

- Spotlight 検索（`Cmd + Space`）で「ターミナル」と入力して開く
- または、アプリケーション → ユーティリティ → ターミナル

#### 3-2. プロジェクトフォルダを作成

以下をターミナルに貼り付けて Enter を押してください。

```bash
mkdir ~/voice-diary-bot
cd ~/voice-diary-bot
```

#### 3-3. 設定ファイルを作成

以下の3つのファイルを作成します。テキストエディタ（VS Code など）で `~/voice-diary-bot` フォルダを開き、それぞれのファイルを新規作成してください。

**ファイル 1: `package.json`**

```json
{
  "name": "voice-diary-bot",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "vercel dev",
    "sync": "npx tsx scripts/sync-to-obsidian.ts"
  },
  "dependencies": {
    "@line/bot-sdk": "^9.0.0",
    "@upstash/redis": "^1.37.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "tsx": "^4.21.0",
    "typescript": "^5.0.0",
    "vercel": "^39.0.0"
  }
}
```

**ファイル 2: `tsconfig.json`**

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "dist",
    "rootDir": ".",
    "resolveJsonModule": true
  },
  "include": ["api/**/*", "scripts/**/*"]
}
```

**ファイル 3: `vercel.json`**

```json
{
  "version": 2,
  "routes": [
    {
      "src": "/api/webhook",
      "methods": ["POST"],
      "dest": "/api/webhook.ts"
    },
    {
      "src": "/api/messages",
      "methods": ["GET"],
      "dest": "/api/messages.ts"
    }
  ]
}
```

#### 3-4. 依存パッケージをインストール

ターミナルで以下を実行：

```bash
cd ~/voice-diary-bot
npm install
```

たくさんの文字が流れますが、最後に `added xxx packages` と表示されれば成功です。

---

## Step 4: Webhook 関数を作る（LINE → Vercel）

LINE からメッセージを受け取り、Redis に保存する関数を作ります。

### あなたがやること

#### 4-1. api フォルダを作成

ターミナルで：

```bash
mkdir ~/voice-diary-bot/api
```

#### 4-2. `api/webhook.ts` ファイルを作成

テキストエディタで `api/webhook.ts` を新規作成し、以下のコードを**そのままコピー＆ペースト**してください。変更する箇所はありません（値は環境変数から自動で読み込まれます）。

```typescript
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { Redis } from "@upstash/redis";
import crypto from "crypto";

const CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET!;
const CHANNEL_ACCESS_TOKEN = process.env.LINE_CHANNEL_ACCESS_TOKEN!;

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

interface LineEvent {
  type: string;
  message?: { type: string; id: string; text?: string };
  timestamp: number;
  replyToken?: string;
}

// LINE からのリクエストが本物か検証する
function verifySignature(body: string, signature: string): boolean {
  const hash = crypto
    .createHmac("SHA256", CHANNEL_SECRET)
    .update(body)
    .digest("base64");
  return hash === signature;
}

// LINE にリプライを送る
async function replyMessage(replyToken: string, text: string) {
  await fetch("https://api.line.me/v2/bot/message/reply", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${CHANNEL_ACCESS_TOKEN}`,
    },
    body: JSON.stringify({
      replyToken,
      messages: [{ type: "text", text }],
    }),
  });
}

export default async function handler(req: VercelRequest, res: VercelResponse) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  // 署名検証（なりすまし防止）
  const signature = req.headers["x-line-signature"] as string;
  const body = JSON.stringify(req.body);

  if (!verifySignature(body, signature)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const events: LineEvent[] = req.body.events;

  for (const event of events) {
    if (event.type === "message" && event.message?.type === "text") {
      const text = event.message.text!;
      // 日本時間に変換
      const jst = new Date(event.timestamp + 9 * 60 * 60 * 1000);
      const timestamp = jst.toISOString().replace("Z", "+09:00");

      // Redis にメッセージを追加
      await redis.rpush("diary:messages", JSON.stringify({ timestamp, text }));

      // LINE に「記録しました」と返す
      if (event.replyToken) {
        await replyMessage(event.replyToken, "📝 記録しました");
      }
    }
  }

  return res.status(200).json({ ok: true });
}
```

> コードの中身を理解する必要はありません。そのままコピペすれば動きます。
> ざっくり言うと「LINE からメッセージを受け取って、データベースに保存して、LINE に「記録しました」と返信する」という処理です。

---

## Step 5: メッセージ取得 API を作る（Mac → Vercel）

Mac のスクリプトが、溜まったメッセージを取りに来るための API です。

### あなたがやること

#### `api/messages.ts` ファイルを作成

テキストエディタで `api/messages.ts` を新規作成し、以下のコードを**そのままコピー＆ペースト**してください。変更する箇所はありません。

```typescript
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { Redis } from "@upstash/redis";

const API_SECRET = process.env.API_SECRET!;

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

export default async function handler(req: VercelRequest, res: VercelResponse) {
  if (req.method !== "GET") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  // 認証チェック（誰でもメッセージを取れないようにする）
  const auth = req.headers["x-api-secret"] as string;
  if (auth !== API_SECRET) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  // Redis からメッセージを全件取得
  const raw: string[] = await redis.lrange("diary:messages", 0, -1);
  const messages = raw.map((item) =>
    typeof item === "string" ? JSON.parse(item) : item
  );

  // 取得後にクリア（二重取得を防ぐ）
  if (messages.length > 0) {
    await redis.del("diary:messages");
  }

  return res.status(200).json({ messages });
}
```

> こちらもそのままコピペでOKです。
> ざっくり言うと「Mac からアクセスしたときに、溜まっているメッセージを全部返して、データベースをクリアする」という処理です。

---

## Step 6: Vercel にデプロイする

作ったコードをインターネット上のサーバー（Vercel）に公開します。

### あなたがやること

#### 6-1. Vercel にデプロイ

ターミナルで以下を実行：

```bash
cd ~/voice-diary-bot
vercel --yes
```

- 初めての場合、ブラウザが開いて Vercel へのログインを求められます。ログインしてください。
- デプロイが成功すると URL が表示されます（例：`https://voice-diary-bot.vercel.app`）
- **この URL をテキストエディタにメモしてください**（後で使います）

#### 6-2. 環境変数を設定する

Step 1 と Step 2 で控えた値を、Vercel に安全に登録します。以下の5つのコマンドを **1つずつ** ターミナルで実行してください。

`"ここに値を貼る"` の部分を、自分の値に置き換えてから実行します。

```bash
cd ~/voice-diary-bot

vercel env add UPSTASH_REDIS_REST_URL production --value "Step 2 で控えた Upstash の URL"

vercel env add UPSTASH_REDIS_REST_TOKEN production --value "Step 2 で控えた Upstash のトークン"

vercel env add LINE_CHANNEL_SECRET production --value "Step 1 で控えた Channel Secret"

vercel env add LINE_CHANNEL_ACCESS_TOKEN production --value "Step 1 で控えた Access Token"

vercel env add API_SECRET production --value "好きな文字列を決めて入れる（例: my-diary-secret-2026）"
```

> **環境変数とは？**
> サーバー上に安全に保存される設定値のことです。コードに直接書くと GitHub 等で公開されてしまうリスクがあるため、環境変数として分離して管理します。

> **API_SECRET とは？**
> Mac のスクリプトが Vercel にアクセスするためのパスワードです。自分で好きな文字列を決めてください。Step 8 でもう一度使うので忘れないようにメモしてください。

#### 6-3. 環境変数を反映して再デプロイ

環境変数は再デプロイしないと反映されません。以下を実行：

```bash
vercel --prod --yes
```

「Deployment ... ready」と表示されれば成功です。

---

## Step 7: LINE Webhook を接続する

LINE で送ったメッセージが Vercel に届くように接続します。

### あなたがやること

#### 7-1. Webhook URL を設定する

**LINE Official Account Manager** と **LINE Developers コンソール** の両方に設定が必要です。

**LINE Official Account Manager で設定：**
1. [LINE Official Account Manager](https://manager.line.biz/) を開く
2. 作成したアカウントを選択
3. 左メニュー **「設定」** → **「Messaging API」**
4. **Webhook URL** の欄に以下を入力：
   ```
   https://あなたのプロジェクト名.vercel.app/api/webhook
   ```
   （例：`https://voice-diary-bot.vercel.app/api/webhook`）
5. **「保存」** をクリック

**LINE Developers コンソールで設定：**
1. [LINE Developers コンソール](https://developers.line.biz/console/) を開く
2. プロバイダー → チャネルを選択
3. **「Messaging API設定」タブ** を開く
4. **Webhook URL** が上で入力したものと同じになっていることを確認

#### 7-2. Webhook の利用をオンにする（重要！）

同じ「Messaging API設定」タブの画面で：

1. **「Webhookの利用」** のトグルスイッチを見つける
2. **オンにする**（緑色になればOK）

> **ここが最もハマりやすいポイントです！** Webhook URL を設定しても、このトグルがオフのままだとメッセージが一切届きません。必ずオンになっていることを確認してください。

#### 7-3. 接続を検証する

1. 同じ画面にある **「検証」ボタン** をクリック
2. **「成功」** と表示されればOK
3. エラーが出た場合は、Webhook URL のスペルミスや環境変数の設定漏れを確認

#### 7-4. Bot を友だち追加してテストする

1. LINE Developers コンソールの「Messaging API設定」タブをスクロール
2. **QR コード** が表示されているので、スマホの LINE で読み取る
3. Bot を **友だち追加** する
4. 何かメッセージを送ってみる（例：「テスト」）
5. **「📝 記録しました」** と返ってくれば成功！

返ってこない場合は、以下を確認：
- [ ] Webhook の利用がオンになっているか
- [ ] Webhook URL が正しいか（`/api/webhook` まで含まれているか）
- [ ] 環境変数が5つとも設定されているか（`vercel env ls` で確認）
- [ ] 再デプロイしたか（`vercel --prod --yes`）

---

## Step 8: Mac 同期スクリプトを作る

Vercel に溜まったメッセージを取得して、Obsidian のデイリーノートに書き込むスクリプトです。

### あなたがやること

#### 8-1. scripts フォルダを作成

ターミナルで：

```bash
mkdir ~/voice-diary-bot/scripts
```

#### 8-2. `scripts/sync-to-obsidian.ts` ファイルを作成

テキストエディタで `scripts/sync-to-obsidian.ts` を新規作成し、以下のコードを貼り付けてください。

**貼り付けた後、先頭の3行を自分の環境に合わせて変更してください：**

```typescript
import fs from "fs";
import path from "path";

// ★★★ この3行を自分の環境に合わせて変更する ★★★
const API_URL = "https://あなたのプロジェクト名.vercel.app/api/messages";
// ↑ Step 6-1 でメモした URL の末尾に /api/messages を付けたもの

const API_SECRET = "Step 6-2 で API_SECRET に設定したのと同じ文字列";
// ↑ 例: "my-diary-secret-2026"

const VAULT_PATH = "/Users/あなたのユーザー名/Documents/Obsidian Vault";
// ↑ 自分の Obsidian Vault のフルパス
//   確認方法: Obsidian を開く → 左下の「Vault 名」を右クリック → 「Finderで表示」→ パスをコピー
// ★★★ ここまで ★★★

interface DiaryMessage {
  timestamp: string;
  text: string;
}

function getTodayFileName(): string {
  const now = new Date();
  const jst = new Date(now.getTime() + 9 * 60 * 60 * 1000);
  const year = jst.getUTCFullYear();
  const month = String(jst.getUTCMonth() + 1).padStart(2, "0");
  const day = String(jst.getUTCDate()).padStart(2, "0");
  return `${year}-${month}-${day}.md`;
}

function formatTime(timestamp: string): string {
  const date = new Date(timestamp);
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${hours}:${minutes}`;
}

function appendToDaily(messages: DiaryMessage[]) {
  const byDate = new Map<string, DiaryMessage[]>();
  for (const msg of messages) {
    const date = new Date(msg.timestamp);
    const jst = new Date(date.getTime() + 9 * 60 * 60 * 1000);
    const key = `${jst.getUTCFullYear()}-${String(jst.getUTCMonth() + 1).padStart(2, "0")}-${String(jst.getUTCDate()).padStart(2, "0")}`;
    if (!byDate.has(key)) byDate.set(key, []);
    byDate.get(key)!.push(msg);
  }

  for (const [dateKey, msgs] of byDate) {
    const targetPath = path.join(VAULT_PATH, `${dateKey}.md`);
    let content = "";

    if (fs.existsSync(targetPath)) {
      content = fs.readFileSync(targetPath, "utf-8");
    } else {
      content = `## ${dateKey} デイリーログ\n`;
    }

    if (!content.includes("### 🎙️ 音声メモ")) {
      const headerEnd = content.indexOf("\n", content.indexOf("デイリーログ"));
      if (headerEnd !== -1) {
        content =
          content.slice(0, headerEnd + 1) +
          "\n### 🎙️ 音声メモ\n" +
          content.slice(headerEnd + 1);
      } else {
        content += "\n### 🎙️ 音声メモ\n";
      }
    }

    const sectionIndex = content.indexOf("### 🎙️ 音声メモ");
    const sectionEnd = content.indexOf("\n---", sectionIndex);
    const insertPos =
      sectionEnd !== -1
        ? sectionEnd
        : content.indexOf("\n###", sectionIndex + 1) !== -1
          ? content.indexOf("\n###", sectionIndex + 1)
          : content.length;

    const newEntries = msgs
      .map((msg) => `- ${formatTime(msg.timestamp)} — ${msg.text}`)
      .join("\n");

    content =
      content.slice(0, insertPos) +
      "\n" +
      newEntries +
      content.slice(insertPos);

    fs.writeFileSync(targetPath, content, "utf-8");
    console.log(`✅ ${msgs.length} 件を ${dateKey}.md に追記しました`);
  }
}

async function sync() {
  try {
    const res = await fetch(API_URL, {
      headers: { "x-api-secret": API_SECRET },
    });

    if (!res.ok) {
      console.error(`❌ API エラー: ${res.status}`);
      return;
    }

    const data = await res.json();
    const messages: DiaryMessage[] = data.messages;

    if (messages.length === 0) {
      console.log("📭 新しいメッセージはありません");
      return;
    }

    console.log(`📬 ${messages.length} 件のメッセージを取得`);
    appendToDaily(messages);
  } catch (err) {
    console.error("❌ 同期エラー:", err);
  }
}

// 5分ごとに自動実行
console.log("🚀 音声日記同期スクリプト開始");
sync();
setInterval(sync, 5 * 60 * 1000);
```

#### 8-3. 動作テスト

1. まず、スマホの LINE で Bot に何かメッセージを送る
2. 「📝 記録しました」と返ってくるのを確認
3. ターミナルで以下を実行：

```bash
cd ~/voice-diary-bot
npm run sync
```

4. 以下のような表示が出れば成功：
   ```
   🚀 音声日記同期スクリプト開始
   📬 1 件のメッセージを取得
   ✅ 1 件を 2026-04-08.md に追記しました
   ```

5. **Obsidian を開いて**、今日の日付のファイル（例：`2026-04-08.md`）に `### 🎙️ 音声メモ` セクションが追加されていることを確認

6. `Ctrl + C` でスクリプトを停止

> テスト後に「📭 新しいメッセージはありません」と出る場合は、もう一度 LINE からメッセージを送ってから再実行してください。

---

## Step 9: Mac 起動時に自動実行する

Mac にログインしたら自動でスクリプトが常駐するように設定します。これを設定すると、手動で `npm run sync` を実行しなくても、5分おきに自動で同期されるようになります。

### あなたがやること

#### 9-1. ログフォルダを作成

ターミナルで：

```bash
mkdir -p ~/voice-diary-bot/logs
```

#### 9-2. 自分の Mac が Apple Silicon か Intel か確認する

ターミナルで以下を実行：

```bash
which npx
```

- `/opt/homebrew/bin/npx` と表示される → **Apple Silicon**（M1/M2/M3/M4）
- `/usr/local/bin/npx` と表示される → **Intel**

表示された結果をメモしてください。次のステップで使います。

#### 9-3. LaunchAgent ファイルを作成

ターミナルで以下を実行して、ファイルを作成するフォルダに移動：

```bash
cd ~/Library/LaunchAgents
```

テキストエディタで `com.yourname.voice-diary-sync.plist` というファイルを新規作成し、以下を貼り付けてください。

**「あなたのユーザー名」の部分（4箇所）を自分の Mac のユーザー名に置き換えてください。**

ユーザー名の確認方法：ターミナルで `whoami` を実行すると表示されます。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.yourname.voice-diary-sync</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/npx</string>
        <string>tsx</string>
        <string>/Users/あなたのユーザー名/voice-diary-bot/scripts/sync-to-obsidian.ts</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/Users/あなたのユーザー名/voice-diary-bot</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/あなたのユーザー名/voice-diary-bot/logs/sync.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/あなたのユーザー名/voice-diary-bot/logs/error.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    </dict>
</dict>
</plist>
```

> **Intel Mac の場合**: `<string>/opt/homebrew/bin/npx</string>` を `<string>/usr/local/bin/npx</string>` に変更してください。

#### 9-4. サービスを登録・起動

ターミナルで以下を実行：

```bash
launchctl load ~/Library/LaunchAgents/com.yourname.voice-diary-sync.plist
```

何も表示されなければ成功です（エラーがなければ無言で完了します）。

#### 9-5. 動作確認

ターミナルで以下を実行：

```bash
cat ~/voice-diary-bot/logs/sync.log
```

**「🚀 音声日記同期スクリプト開始」** と表示されていれば、自動実行が動いています。

---

## Step 9（Windows版）: サインイン時に自動実行する

> このStep 9は Mac 専用（launchd）です。**Windows の場合はこの節を代わりに使ってください。** Step 8までのコード（`sync-to-obsidian.ts` 等）はWindowsでもそのまま動きます。ただし先頭の `VAULT_PATH` は `C:/Users/ユーザー名/Documents/Obsidian Vault` のように **`\` ではなく `/`（スラッシュ）** で書いてください。

Windowsには launchd がないので、「サインイン時に自動で立ち上がるフォルダ（スタートアップ）」に起動用ファイルを置きます。

#### W-1. 起動用バッチファイルを作る

テキストエディタで `%USERPROFILE%\voice-diary-bot\start-sync.bat` を新規作成し、以下を貼り付けて保存します。

```bat
@echo off
cd /d "%USERPROFILE%\voice-diary-bot"
npx tsx scripts\sync-to-obsidian.ts
```

#### W-2. スタートアップフォルダを開く

1. キーボードの `Windows` ＋ `R` を押す
2. `shell:startup` と入力して Enter → スタートアップフォルダが開く

#### W-3. ショートカットを置く

1. `start-sync.bat` を右クリック →「ショートカットの作成」
2. できたショートカットを、W-2で開いたスタートアップフォルダに移動する

これで、次回サインイン時から自動で同期スクリプトが立ち上がります。

#### W-4. 動作確認

`start-sync.bat` をダブルクリックすると黒い画面（コマンドプロンプト）が開き、**「🚀 音声日記同期スクリプト開始」** と表示されればOK。この画面は開いたままにしておくと同期が続きます。

> 黒い画面を出したくない場合は「タスクスケジューラ」で「ログオン時に実行・非表示」に設定する方法もあります。

---

## 完成！日常の使い方

### やることはこれだけ

1. **スマホの LINE を開いて**、Bot のトーク画面でメッセージを送る
   - テキスト入力でもOK
   - LINE の音声入力（マイクボタン）でもOK
   - Typeless などの音声入力アプリを使ってもOK
2. **「📝 記録しました」** と返信が来る
3. あとは何もしなくてOK。**5分以内に Mac の Obsidian に自動追記される**

### Obsidian での表示例

```markdown
## 2026-04-08 デイリーログ

### 🎙️ 音声メモ
- 09:15 — 今日の朝会で来週のリリーススケジュールが決まった
- 12:30 — ランチで田中さんと話した件、企画書にまとめる
- 18:00 — 明日の準備：資料の印刷と会議室の予約を忘れずに

---

### 📌 今日やったこと
（他の作業ログなど）
```

---

## 困ったときの対処法

### LINE で送ったのに「記録しました」が返ってこない

1. LINE Developers コンソールを開く
2. Messaging API設定タブで **「Webhookの利用」がオンか確認**
3. 「検証」ボタンをクリックして接続テスト
4. ターミナルで `cd ~/voice-diary-bot && vercel env ls` を実行して、環境変数が5つ登録されているか確認
5. 環境変数を追加・修正した場合は `vercel --prod --yes` で再デプロイ

### Obsidian にメッセージが書き込まれない

1. ターミナルで `cat ~/voice-diary-bot/logs/error.log` を実行してエラーを確認
2. 手動で同期テスト：`cd ~/voice-diary-bot && npm run sync`
3. `scripts/sync-to-obsidian.ts` を開いて、`VAULT_PATH` が自分の Obsidian Vault のパスと一致しているか確認

### Mac を再起動したら同期されなくなった

ターミナルで以下を実行：

```bash
launchctl load ~/Library/LaunchAgents/com.yourname.voice-diary-sync.plist
```

### 今すぐ手動で同期したい（5分待てない）

```bash
cd ~/voice-diary-bot && npm run sync
```

---

## 便利なコマンド一覧

| やりたいこと | コマンド |
|---|---|
| 今すぐ手動で同期 | `cd ~/voice-diary-bot && npm run sync` |
| ログを確認 | `cat ~/voice-diary-bot/logs/sync.log` |
| エラーログを確認 | `cat ~/voice-diary-bot/logs/error.log` |
| 同期を停止 | `launchctl unload ~/Library/LaunchAgents/com.yourname.voice-diary-sync.plist` |
| 同期を再開 | `launchctl load ~/Library/LaunchAgents/com.yourname.voice-diary-sync.plist` |
| 環境変数を確認 | `cd ~/voice-diary-bot && vercel env ls` |

---

## ファイル構成（最終状態）

```
voice-diary-bot/
├── api/
│   ├── webhook.ts           ← LINE からメッセージを受け取る関数
│   └── messages.ts          ← Mac がメッセージを取得する関数
├── scripts/
│   └── sync-to-obsidian.ts  ← Obsidian に書き込む常駐スクリプト
├── logs/
│   ├── sync.log             ← 実行ログ
│   └── error.log            ← エラーログ
├── node_modules/            ← npm install で自動生成（触らない）
├── package.json             ← プロジェクト設定
├── tsconfig.json            ← TypeScript 設定
└── vercel.json              ← Vercel のルーティング設定
```

---

## セキュリティに関する注意

| やること | 理由 |
|---|---|
| API キーやトークンはコードに書かない | GitHub 等で公開されると悪用される |
| Vercel の環境変数として保存する | サーバー上で安全に管理される |
| LINE の署名検証を行う | なりすましリクエストを防ぐ |
| API_SECRET で認証する | メッセージ取得 API への不正アクセスを防ぐ |
| 作業後はトークンを再発行する | チャット等に貼った値を無効化できる |

---

## 発展アイデア

- LINE の音声メッセージ（音声ファイル）を Whisper API で文字起こしして自動記録
- 特定のキーワード（例：`#todo`）でタスクリストに振り分け
- 1日の終わりに AI で日記を要約してまとめセクションを自動生成
- 複数人で使えるようにユーザーID別に振り分け
