Claude Owner 1841828f7a feat(voice): per-user TTS voice selection via /목소리
Until now every TTS message was synthesized with VoiceType.가람
regardless of who spoke. This adds a user-scoped voice preference
persisted in SQLite so each member can pick their own voice and
keep it across bot restarts.

Changes
- db/schema.sql: add nullable `voice_type` column to `users`.
- src/utils/Database.ts:
  - run a PRAGMA-driven ALTER TABLE migration so existing DBs gain
    the column without dropping data.
  - add `DB.user.setVoice(guildId, userId, name, voiceType)` that
    upserts the row.
- src/classes/TTSClient.ts:
  - export `DEFAULT_VOICE` (= 가람).
  - resolve the speaking member's stored voice in `tts()` and
    thread it through `getSource()` instead of hardcoding 가람.
  - validate stored slug against `VoiceType` so stale/unknown
    values silently fall back to the default.
- src/commands/voice.ts (new):
  - `/목소리` slash command shows the user's current voice and a
    StringSelectMenu of all `VoiceType` entries (현재 + 이전 보이스
    모두). Selection writes to `users.voice_type` and confirms
    ephemerally.
  - defensively creates a guild row if `/tts channel register`
    hasn't run yet, to satisfy the ON DELETE CASCADE FK.

Deploy
Run `npm run prod` after pulling so Discord sees the new
`/목소리` command. No env or config changes required.
2026-05-26 22:50:53 +09:00
2026-05-26 14:15:09 +09:00
2026-05-26 14:15:09 +09:00
2026-05-26 14:15:09 +09:00

tts_bot

Discord 음성채널에서 채팅을 읽어주는 TTS 봇. TypeScript + discord.js 기반.

주요 기능

  • 지정한 텍스트 채널의 채팅을 음성채널에서 자동 낭독 (/tts channel register로 채널 지정)
  • 치지직(Chzzk) 알림 TTS 엔진 사용 (utils/tts/Chzzk.ts). 현재 코드는 VoiceType.가람 고정 (VoiceType enum에 다른 보이스도 정의돼 있어 코드 한 줄 수정으로 교체 가능)
  • 대체 엔진으로 Google Translate TTS 구현체(utils/tts/Google.ts)가 포함돼 있으나 현재 비활성(TTSClient.ts에서 import 주석 처리). 사용하려면 import 교체 필요
  • 시그니처(짤·효과음) 재생: 외부 시그니처 서버(192.168.10.5:2967)와 WebSocket으로 동기화해 채팅 중 키워드 매칭 시 mp3 삽입
  • URL, 멘션, 이모지, 자주 쓰는 초성(ㄹㅇ, ㅇㅋ 등)을 자동 치환해 읽음
  • 10분 idle 후 음성채널 자동 퇴장, 사용자 음성채널 이동 시 따라가기

명령어

명령어 설명
/tts channel make "TTS채널" 텍스트 채널을 새로 만들고 등록
`/tts channel register [channel channel_id]`
/시그니처 (/signature) 시그니처 관리 사이트 링크
/help (/도움말) 명령어 목록
/ping (/핑) 응답 지연 확인

메시지 prefix(PREFIX 환경변수)로도 help/ping/시그니처/example은 동일하게 호출된다. tts는 슬래시 전용이며, prefix tts는 현재 더미 응답("예시 명령어")만 보낸다.

기술 스택

  • Node.js 20 (Dockerfile 기준) / TypeScript (strict 모드)
  • discord.js v14, @discordjs/voice, @discordjs/opus
  • better-sqlite3 — 길드별 등록 채널 저장 (db/schema.sql)
  • fluent-ffmpeg + ffmpeg-static — 시그니처 mp3 볼륨 조절, PCM 변환
  • ws — 시그니처 서버 동기화
  • axios — Chzzk / Google TTS HTTP 호출

디렉터리 구조

src/
  index.ts              # 엔트리: 싱글톤(client, handler, tts, signature) 구성
  classes/
    BotClient.ts        # discord.js Client 확장 (embed/메시지 삭제 헬퍼)
    Handler.ts          # commands/ 자동 로드 및 슬래시 라우팅
    TTSClient.ts        # 메시지 → TTS 변환 파이프라인
    VoiceSession.ts     # 길드별 음성 연결/플레이어 관리
    SignatureClient.ts  # 시그니처 서버 WS 연결 + 파일 캐싱
  commands/             # tts, signature, help, ping, example
  events/               # clientReady, interactionCreate, messageCreate, voiceStateUpdate
  utils/
    Config.ts           # 환경변수 게터 (누락 시 ReferenceError)
    Database.ts         # better-sqlite3 래퍼 (guild/user CRUD)
    Logger.ts           # colors 기반 타임스탬프 로거
    Transcode.ts        # mp3 Buffer → 48k/16bit/stereo PCM 스트림
    Prod-commands.ts    # 전역 슬래시 명령어 일괄 등록 스크립트
    tts/                # Chzzk, Google, Utils(치환 사전)
db/
  schema.sql            # guilds, users 테이블
  db.d.ts               # 타입 정의

환경 변수

.env 또는 시스템 환경변수로 설정. 모든 값은 Config.ts에서 trim 후 읽음.

필수 설명
APPID Discord 애플리케이션 ID
TOKEN 봇 토큰
PREFIX 메시지 명령어 prefix (예: !)
DBPATH SQLite 파일 경로 (예: ./db/data.db)
CHZZK_NID_AUT 네이버 NID_AUT 쿠키 (치지직 TTS 인증)
CHZZK_NID_SES 네이버 NID_SES 쿠키
GUILDID DEV=true일 때만 사용 (DEV 길드 한정 슬래시 등록 대상). 그 외에는 미설정이어도 무방
DEV 선택 true면 글로벌 대신 GUILDID에만 슬래시 등록
DEBUG 선택 true면 명령어 오류 스택 로깅
REPLACETEXT 선택 치환 사전(JSON 배열). 기본 사전(def_replaceObj)과 병합
SIGNATURE_HOST 선택 시그니처 서버 host:port. 미설정 시 192.168.10.5:2967 기본값
TTSPATH 선택 Config.ttsPath getter만 정의돼 있고 현재 호출처 없음. 향후 사용 대비 환경변수만 받아둠

실행

# 의존성 설치
npm install

# 개발 (ts-node)
npm run dev

# 빌드 후 실행 (build 스크립트가 dist 디렉터리를 자동 생성)
npm run build
npm start

# 전역 슬래시 명령어 등록 (배포 파이프라인)
npm run prod

Docker

docker build -t tts_bot .
docker run --env-file .env -v $(pwd)/db:/app/db tts_bot

Dockerfile은 node:20-alpine 기반이며 ffmpeg을 설치한다.

동작 흐름 요약

  1. messageCreate → 등록된 TTS 채널이면 TTSClient.tts() 호출
  2. 텍스트 전처리(URL/멘션/이모지 치환) → 시그니처 키워드 분할
  3. 각 조각을 Chzzk TTS로 합성 또는 캐시된 시그니처 mp3 사용
  4. mp3 Buffer 결합 → Transcode에서 PCM 변환 → VoiceSession.player.play()
  5. 일정 시간 idle 시 세션 자동 종료, 사용자 채널 이동 시 따라감

참고

  • 시그니처 서버 주소(192.168.10.5:2967)는 SignatureClient.ts에 하드코딩 — 환경에 맞게 수정 필요
  • commands/ 내 파일은 default export class 형태로 자동 로드되므로 새 명령어 추가 시 같은 패턴 유지
  • 현재 users 테이블 스키마는 정의돼 있으나 코드에서 사용 흔적은 적음 (향후 확장 여지)
Description
No description provided
Readme 139 KiB
Languages
TypeScript 99.5%
Dockerfile 0.5%