discordの読み上げbot作成 (Mac Intel上で作成)


## 1. Discordボットの作成  
1. [Discord Developer Portal](https://discord.com/developers/applications) にアクセス  
2. 「New Application」を作成  
3. 「Bot」タブで「Add Bot」を押す  
4. 「ResetTOKEN」でトークンをリセットして「TOKEN」をコピー＆メモ
    （このトークンは絶対に公開しないでください！）  
5. 「Privileged Gateway Intents」の「MESSAGE CONTENT INTENT」を有効化  
6. 「OAuth2」>「URL Generator」で「bot」と「applications.commands」を選択  
7. 「Voice」権限を付与し、生成されたURLからサーバーに招待
   私はテキトーに以下の権限を選択しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699916/cd3ec0be-d9d4-4f45-ae85-dfc158d9b512.png)

Python環境のセットアップを進めましょう！  

## 2.Pythonのインストール  
### 🔹 手順  
1. [公式サイト](https://www.python.org/downloads/) から **Python 3.10以上** をダウンロード  
2. インストーラーを実行し、**「Add Python to PATH」** にチェックを入れる  
3. 「Install Now」を選択してインストール  

### ✅ 確認  
インストール後、ターミナル（コマンドプロンプト）で次のコマンドを実行してください。  
```sh
python --version
```
または  
```sh
python3 --version
```
`Python 3.x.x` と表示されれば成功です！  

---

## 3.仮想環境の作成（推奨）  
プロジェクトごとに環境を管理しやすくするため、仮想環境を作成します。  
```sh
python -m venv venv
```
### ✅ 仮想環境の有効化  
- **Windows:**  
  ```sh
  venv\Scripts\activate
  ```
- **Mac/Linux:**  
  ```sh
  source venv/bin/activate
  ```

  仮想環境が有効になると、ターミナルの先頭に `(venv)` が表示されるはずです。  
  確認できたら以下で非アクティブに戻しておきます。
  ```
  deactivate
  ```


## 4.VOICEVOXエンジン（CLI版）を導入 
**Mac（Intel）向けのCLI版VOICEVOXエンジンを手動で導入**し、APIとして利用できます。  

### 🔹 手順  
1. **VOICEVOXエンジン（Mac版）をダウンロード**  
   - GitHubのリリースページから **「Intel Mac用」** をダウンロード  
   👉 [VOICEVOX Engine Releases](https://github.com/VOICEVOX/voicevox_engine/releases)  
   - `「エンジン本体」 → 「macOS（CPU/x64版）」` をダウンロード
   私の場合 https://github.com/VOICEVOX/voicevox_engine/releases/download/0.23.0/voicevox_engine-macos-x64-0.23.0.7z.001 が実際のダウンロードURLでした

2. **ZIPを解凍し、適当なフォルダに配置**  
   [The Unarchiver](https://apps.apple.com/jp/app/the-unarchiver/id425424353?mt=12)で解凍しました。
   配置は以下にしました。
   ```
   /Users/user/voicevox_engine/macos-x64
   ```
3. **実行権限を付与**  
   ```sh
   cd /Users/user/voicevox_engine/macos-x64
   chmod +x run
   ```



4. **エンジンを起動**  
   ```sh
   ./run
   ```
   - 正常に動作すると、ターミナルに `http://127.0.0.1:50021` のようにアドレスが表示される


   以下のエラーがある場合：
   ```
   “run”が悪質なソフトウェアかどうかをAppleでは確認できないため、このソフトウェアは開けません。
   ```
   以下を実行して許可（パスは環境に合わせて変更）
   ```
   sudo xattr -rd com.apple.quarantine /Users/uƒser/voicevox_engine/macos-x64
   ```
   再度``./run``する
   
✅ **これでAPIが動作するようになります！**  


## 5. PythonでVOICEVOXエンジンAPIを呼び出す（動作確認）
まず、エンジンが正常に動作するか **Pythonでテスト** してみましょう。  
ターミナルで ``source venv/bin/activate`` してvenvをアクティブにし、以下のコードを実行してください。

### **1️⃣ 依存ライブラリのインストール**
ターミナルで以下を実行:
```sh
pip install requests soundfile numpy
```

wav->mp3変換用
```
brew install ffmpeg
```
```
pip install pydub
```
---

### **2️⃣ Pythonコードを用意（ローカルテストy）**
Pythonスクリプトを作成し、VOICEVOXエンジンが正しく動作するか確認します。

**test_voicevox.py**
```python
import requests
from pathlib import Path
from pydub import AudioSegment

# VOICEVOXエンジンのURL
VOICEVOX_URL = "http://localhost:50021"
url_audio_query = VOICEVOX_URL + "/audio_query"
url_synthesis = VOICEVOX_URL + "/synthesis"

# テキストを指定
text = "こんにちは、MP3での音声合成テストです。"

# 音声合成のクエリを作成
query_payload = {"text": text, "speaker": 1}
query_response = requests.post(url_audio_query, params=query_payload)

if query_response.status_code == 200:
    query_data = query_response.json()

    # 音声合成のリクエスト
    synthesis_response = requests.post(
        f"{url_synthesis}",
        json=query_data,
        params={"speaker": 1}
    )

    if synthesis_response.status_code == 200:
        # WAVデータを保存
        wav_path = "test_voice.wav"
        with open(wav_path, "wb") as f:
            f.write(synthesis_response.content)

        # WAV → MP3 変換
        mp3_path = "test_voice.mp3"
        audio = AudioSegment.from_wav(wav_path)
        audio.export(mp3_path, format="mp3")

        print(f"✅ MP3ファイルが作成されました！ → {mp3_path}")
    else:
        print("❌ 音声合成に失敗しました:", synthesis_response.text)
else:
    print("❌ クエリ作成に失敗しました:", query_response.text)


```

---

### **3️⃣ 実行**
ターミナルで以下のコマンドを実行:
```sh
python3 test_voicevox.py
```


✅ **確認すること**
- **`test_voice.wav` が生成されること**
- **再生して音声が出ること**（`open test_voice.wav` コマンドで開けます）


---


## 6. Discordでbotを動作させる
### **1️⃣ `discord.py` をインストール**
まず `discord.py` をインストールします。  
venv仮想環境をアクティブにした状態で、以下を実行してください：
```sh
pip install "discord.py[voice]"
pip install ffmpeg
```
> **`discord.py[voice]` をインストールすると、ボイス関連の機能が使えます。**  

---

### **2️⃣ pythonコードを用意**
以下のコードを作成します。

### **`bot.py`**
```python
import discord
import requests
from discord import app_commands
from discord.ext import commands
from pydub import AudioSegment

TOKEN = "YOUR_BOT_TOKEN"  # 🔴 自分のBotトークンを入れてください
VOICEVOX_URL = "http://localhost:50021"

intents = discord.Intents.default()
intents.message_content = True  # メッセージの取得を許可
bot = commands.Bot(command_prefix="/", intents=intents)

# ボットが接続しているVCのテキストチャンネルを記録
vc_text_channels = {}

@bot.event
async def on_ready():
    print(f"✅ {bot.user} が起動しました！")
    try:
        synced = await bot.tree.sync()  # スラッシュコマンドを同期
        print(f"✅ スラッシュコマンド {len(synced)} 個を同期しました！")
    except Exception as e:
        print(f"❌ スラッシュコマンドの同期に失敗: {e}")

@bot.tree.command(name="join", description="ボイスチャットに参加します")
async def join(interaction: discord.Interaction):
    """ボットをVCに参加させる"""
    if interaction.user.voice:
        channel = interaction.user.voice.channel
        await channel.connect()
        vc_text_channels[interaction.guild.id] = interaction.channel.id  # ボットが接続したVCのテキストチャンネルを記録
        await interaction.response.send_message("✅ ボイスチャンネルに参加しました！")
    else:
        await interaction.response.send_message("❌ 先にボイスチャンネルに参加してください！", ephemeral=True)

@bot.tree.command(name="play", description="VCで音声ファイルを再生")
async def play(interaction: discord.Interaction):
    """VCで音声ファイルを再生"""
    if interaction.guild.voice_client:
        source = discord.FFmpegPCMAudio("test_voice.wav")
        interaction.guild.voice_client.play(source)
        await interaction.response.send_message("🎵 音声を再生します！")
    else:
        await interaction.response.send_message("❌ 先に `/join` でVCに参加させてください！", ephemeral=True)

@bot.tree.command(name="disconnect", description="ボイスチャットから退出")
async def disconnect(interaction: discord.Interaction):
    """ボットをVCから切断"""
    if interaction.guild.voice_client:
        await interaction.guild.voice_client.disconnect()
        vc_text_channels.pop(interaction.guild.id, None)  # 記録していたテキストチャンネルIDを削除
        await interaction.response.send_message("✅ ボイスチャンネルから切断しました！")
    else:
        await interaction.response.send_message("❌ ボットはVCにいません！", ephemeral=True)

@bot.event
async def on_message(message):
    """ボットが接続しているVCのテキストチャンネルのメッセージのみを読み上げ"""

    if message.author == bot.user or message.author.bot:
        return  # ボット自身や他のBotのメッセージは無視

    # ボットがVCに接続していなければ無視
    if not message.guild or not message.guild.voice_client:
        return

    # 読み上げるメッセージ内容
    text = message.content

    # ボットが接続しているVCのテキストチャンネルか確認
    if message.guild.id not in vc_text_channels or message.channel.id != vc_text_channels[message.guild.id]:
        print(f"対象外のメッセージを無視: {text}")
        return  # VCのテキストチャンネル以外は無視


    # VOICEVOXで音声合成
    query_payload = {"text": text, "speaker": 1}
    query_response = requests.post(f"{VOICEVOX_URL}/audio_query", params=query_payload)

    if query_response.status_code != 200:
        return  # エラーなら無視

    query_data = query_response.json()
    synthesis_response = requests.post(f"{VOICEVOX_URL}/synthesis", json=query_data, params={"speaker": 1})

    if synthesis_response.status_code != 200:
        return  # エラーなら無視

    # 音声を保存（WAV形式のまま）
    wav_path = "voice.wav"
    with open(wav_path, "wb") as f:
        f.write(synthesis_response.content)

    # VCで再生（WAVをそのまま）
    source = discord.FFmpegPCMAudio(wav_path)
    message.guild.voice_client.play(source)

    await bot.process_commands(message)  # 他のコマンドも処理

bot.run(TOKEN)


```

---
### **3️⃣python実行**
ターミナルからbot.pyのあるディレクトリで以下を実行してbotを起動します
```
python3 bot.py
```

```ssl.SSLCertVerificationError``` のエラーがある場合は次の手順で証明書を取得しましょう。

### **3️⃣-2 証明書インストール**
 macOSでは、Pythonに付属するSSL証明書を手動でインストールする必要がある場合があるそうで、次のコマンドを実行してください。


### **4️⃣ discordで呼び出す**


### **Discordでテスト**
1. **ボットをVCに呼ぶ** → ` /join`
2. **テキストを読み上げさせる** → `(例：ボイスチャット内で)こんにちは！`
3. **保管済みの音声を再生** → `/play`
4. **ボットをVCから切断** → `/disconnect`

---

## 🎯 **次のステップ**
- **`/voicechange` で話者変更**
- **VCに誰もいなくなったら自動切断**
- **音声のキュー管理（複数のメッセージを順番に再生）**

---

今後また更新予定です 🚀