Skip to content

Commit dd4c2c6

Browse files
committed
update
0 parents  commit dd4c2c6

34 files changed

+396
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# AI_Kaiwa Discord Bot
2+
3+
Кратко: Discord-бот, который организует двусторонний диалог между персонажами Stasik и Valdos: их реплики сначала синтезируются через edge-tts, затем преобразуются в голоса моделей RVC, и воспроизводятся в голосовом канале.
4+
5+
## Функции
6+
7+
- Команда `!dialog <тема>` — запускает диалог на заданную тему.
8+
- Команда `!stopdialog` — останавливает текущий диалог.
9+
- Лог прогресса отображается в консоли с помощью прогресс-бара `tqdm`.
10+
11+
## Установка
12+
13+
```bash
14+
#!/usr/bin/env bash
15+
set -e
16+
17+
# 1. Установка Ollama и модели
18+
curl -fsSL https://ollama.com/install.sh | sudo sh
19+
ollama pull llama2-uncensored:7b
20+
21+
# 2. Установка ffmpeg (Ubuntu)
22+
sudo apt update
23+
sudo apt install -y ffmpeg
24+
25+
# 3. Установка Python-зависимостей
26+
pip install --upgrade pip
27+
pip install -r requirements.txt
28+
29+
# 4. Создание .env
30+
cat > .env <<EOF
31+
EDGE_VOICE=ru-RU-DmitryNeural
32+
TEMP_DIR=./temp
33+
OLLAMA_MODEL=llama2-uncensored:7b
34+
HUBERT_PATH="C:/Program Files/RVC1006Nvidia/assets/hubert/hubert_base.pt"
35+
rmvpe_root="C:/Program Files/RVC1006Nvidia/assets/rmvpe"
36+
index_root=./characters/Stasik
37+
DISCORD_TOKEN=ВАШ_DISCORD_TOKEN
38+
VOICE_CHANNEL_ID=1132250821657100372
39+
EOF
40+
41+
# 5. Создание папки temp
42+
mkdir -p "${TEMP_DIR}"
43+
44+
# 6. Запуск бота
45+
python bot.py
46+
```
47+
48+
## Файлы проекта
49+
50+
- `bot.py` — реализация Discord-бота с командами `!dialog` и `!stopdialog`.
51+
- `dialog_module.py` — интерфейс для генерации ответов через ollama.
52+
- `tts.py` — синтез речи через edge-tts.
53+
- `rvc_module.py` — переозвучка через RVC-модели.
54+
- `characters/` — папки с моделями и системными промптами для каждого персонажа.
55+
- `temp/` — папка для временных и выходных аудиофайлов.
56+
- `requirements.txt` — список зависимостей проекта.
57+
58+
## Переменные окружения
59+
60+
Переменные в `.env`:
61+
62+
| Ключ | Описание |
63+
|--------------------|---------------------------------------------------|
64+
| `EDGE_VOICE` | Голос edge-tts (например `ru-RU-DmitryNeural`) |
65+
| `TEMP_DIR` | Папка для временных файлов (например `./temp`) |
66+
| `OLLAMA_MODEL` | Модель ollama (например `llama2-uncensored:7b`) |
67+
| `HUBERT_PATH` | Путь к `hubert_base.pt` |
68+
| `rmvpe_root` | Папка с `rmvpe.pt` |
69+
| `index_root` | Путь к `.index` для RVC (перезаписывается на лету) |
70+
| `DISCORD_TOKEN` | Токен Discord-бота (секрет) |
71+
| `VOICE_CHANNEL_ID` | ID голосового канала для воспроизведения |
72+
73+
---
1022 Bytes
Binary file not shown.
1.49 KB
Binary file not shown.

__pycache__/tts.cpython-310.pyc

1.28 KB
Binary file not shown.

bot.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Discord-бот: диалог Stasik <-> Valdos с edge-tts и RVC.
4+
Прогресс и красивые логи через tqdm.
5+
"""
6+
import os
7+
import sys
8+
import asyncio
9+
import logging
10+
import warnings
11+
from pathlib import Path
12+
from dotenv import load_dotenv
13+
14+
import discord
15+
from discord.ext import commands
16+
from tqdm import tqdm
17+
18+
from tts import synthesize
19+
from rvc_module import revoice
20+
from dialog_module import generate
21+
22+
# ========== Настройка логирования и фильтрация предупреждений ==========
23+
logging.basicConfig(level=logging.WARNING)
24+
logging.getLogger('discord').setLevel(logging.WARNING)
25+
logging.getLogger('rvc').setLevel(logging.ERROR)
26+
logging.getLogger('fairseq').setLevel(logging.ERROR)
27+
logging.getLogger('httpx').setLevel(logging.ERROR)
28+
logging.getLogger('faiss').setLevel(logging.ERROR)
29+
warnings.filterwarnings('ignore', category=FutureWarning)
30+
31+
# ========== Загрузка окружения ==========
32+
load_dotenv()
33+
TOKEN = os.getenv("DISCORD_TOKEN")
34+
VOICE_CHANNEL_ID = int(os.getenv("VOICE_CHANNEL_ID", 0))
35+
TEMP_DIR = Path(os.getenv("TEMP_DIR", "./temp"))
36+
TEMP_DIR.mkdir(exist_ok=True)
37+
FFMPEG_PATH = str(Path(__file__).parent / "ffmpeg.exe")
38+
39+
# ========== Инициализация бота ==========
40+
intents = discord.Intents.default()
41+
intents.message_content = True
42+
bot = commands.Bot(command_prefix="!", intents=intents)
43+
44+
@bot.event
45+
async def on_ready():
46+
tqdm.write(f"✅ Logged in as {bot.user} (ID: {bot.user.id})")
47+
48+
async def join_voice_channel():
49+
channel = bot.get_channel(VOICE_CHANNEL_ID)
50+
if not isinstance(channel, discord.VoiceChannel):
51+
raise RuntimeError(f"Voice channel {VOICE_CHANNEL_ID} not found.")
52+
vc = discord.utils.get(bot.voice_clients, guild=channel.guild)
53+
if vc:
54+
if vc.channel.id != VOICE_CHANNEL_ID:
55+
await vc.move_to(channel)
56+
else:
57+
vc = await channel.connect()
58+
return vc
59+
60+
async def play_audio(vc: discord.VoiceClient, path: Path):
61+
source = discord.FFmpegPCMAudio(str(path), executable=FFMPEG_PATH)
62+
vc.play(source)
63+
while vc.is_playing():
64+
await asyncio.sleep(0.2)
65+
66+
async def process_turn(vc: discord.VoiceClient, speaker: str, text: str, turn: int, pbar: tqdm):
67+
# Вывод в консоль поверх прогресса
68+
pbar.set_description(f"{speaker}[{turn:02d}]")
69+
tqdm.write(f"[{turn:02d}] {speaker}: {text}")
70+
# 1) edge-tts → mp3
71+
mp3 = await asyncio.to_thread(synthesize, text, f"{speaker}_{turn}")
72+
# 2) RVC → wav
73+
wav = await asyncio.to_thread(revoice, speaker, mp3, f"{speaker}_{turn}")
74+
# 3) воспроизведение
75+
await play_audio(vc, wav)
76+
pbar.update(1)
77+
78+
async def run_dialog(ctx, topic: str):
79+
vc = await join_voice_channel()
80+
# шагов: анонс + первый + 5*2 обменов
81+
total = 1 + 1 + 5*2
82+
pbar = tqdm(total=total, dynamic_ncols=True, bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} turns")
83+
try:
84+
# анонс темы
85+
announcement = f"Диалог на тему «{topic}»"
86+
tqdm.write(f"🔊 Announcement: {announcement}")
87+
ann_mp3 = await asyncio.to_thread(synthesize, announcement, "topic_announcement")
88+
await play_audio(vc, Path(ann_mp3))
89+
pbar.update(1)
90+
91+
history = {"Stasik": [], "Valdos": []}
92+
# первый ход Stasik
93+
init_msg = f"Привет, Valdos! Давай поговорим на тему: {topic}"
94+
history["Stasik"].append({"role":"user","content":init_msg})
95+
reply = await asyncio.to_thread(generate, "Stasik", history["Stasik"])
96+
history["Valdos"].append({"role":"user","content":reply})
97+
await process_turn(vc, "Stasik", reply, 0, pbar)
98+
99+
# последующие ходы
100+
for i in range(1, 6):
101+
for speaker, listener in [("Valdos","Stasik"), ("Stasik","Valdos")]:
102+
reply = await asyncio.to_thread(generate, speaker, history[speaker])
103+
history[listener].append({"role":"user","content":reply})
104+
await process_turn(vc, speaker, reply, i, pbar)
105+
except asyncio.CancelledError:
106+
tqdm.write("⏹️ Dialog was stopped.")
107+
finally:
108+
pbar.close()
109+
await vc.disconnect()
110+
await ctx.reply("✅ Диалог завершён.")
111+
112+
@bot.command(name="dialog")
113+
async def dialog(ctx, *, topic: str):
114+
if hasattr(bot, "dialog_task") and not bot.dialog_task.done():
115+
return await ctx.reply("⚠️ Диалог уже запущен.")
116+
bot.dialog_task = bot.loop.create_task(run_dialog(ctx, topic))
117+
await ctx.reply(f"▶️ Запущен диалог на тему: {topic}")
118+
119+
@bot.command(name="stopdialog")
120+
async def stopdialog(ctx):
121+
task = getattr(bot, "dialog_task", None)
122+
if task and not task.done():
123+
task.cancel()
124+
await ctx.reply("⏹ Диалог остановлен.")
125+
else:
126+
await ctx.reply("❌ Нет активного диалога.")
127+
128+
if __name__ == "__main__":
129+
bot.run(TOKEN)

characters/Stasik/old_voice.pth

52.6 MB
Binary file not shown.

characters/Stasik/prompt.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ты — Стасик. Ты говоришь только по-русски, исключительно грубо и с матом. Общение происходит с Владосом. Тема задаётся заранее, и вы обсуждаете её, перебрасываясь короткими фразами — строго одно предложение за раз. Вы ругаетесь, общаетесь дерзко, обсуждаете покемонов. Вежливости быть не должно — только жёсткие, грубые, матерные ответы.

0 commit comments

Comments
 (0)