When `Config.dev` is true, the operator typically registers slash
commands per-guild (Config.guildId) during iteration so they appear
instantly. Once those leak past dev, the same commands show up in
Discord twice — once from the dev guild registration and once from
the global registration.
Before doing the global PUT, also PUT an empty array against the
dev guild so any stale per-guild commands are cleared. Behavior in
production (Config.dev=false) is unchanged.
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.
- Logger.Timestamp now formats via Intl with timeZone Asia/Seoul, so
the timestamp is correct regardless of the container/host TZ. The
previous setHours(+9) hack assumed the system clock was UTC.
- Transcode.mp3BufferToPcmStream now attaches error/stderr handlers
to the ffmpeg child process and its streams, swallows EPIPE on
early downstream close, and force-kills on spawn error so failed
conversions can't leak processes. Log level bumped from 'quiet'
to 'error' so real ffmpeg errors surface.
- package.json homepage/bugs/repository pointed at github.com/tkrmagid/bot.ts
which doesn't reflect this repo. Repoint to the actual Gitea origin.
- New optional env SIGNATURE_HOST overrides the hardcoded
192.168.10.5:2967 (defaults preserved for back-compat).
- WebSocket now reconnects with exponential backoff (1s, 2s, 4s ...
capped at 30s) on close/error. Previously a dropped signature
server connection silently disabled signature playback until the
bot was restarted.
stmt.user.update was issuing UPDATE guilds instead of UPDATE users,
so any DB.user.update() call would silently corrupt guild rows that
happened to share the same WHERE clause shape and never touch the
intended user row.