Curated repos, tools, and frameworks shaping the developer ecosystem.
Live data from GitHub.
by coah80
Self-hostable Discord Tomodachi Life TTS bot using Talkmodachi-style Citra rendering
okay so this is TTSmodachi.
it is a Discord TTS bot that talks through the Tomodachi Life / Talkmodachi 3DS voice renderer. you run the Discord bot, a renderer service, and a patched Citra worker pool. people type in the voice channel chat, or a text channel you pick with /channel, and the bot reads it out loud with goofy Tomodachi voices.
this repo does not include ROMs, bring your own legally dumped game files.
/join, /leave, /channel, /skip, /voice, /voices, /replace, and /settingsyou need:
do not upload ROMs to GitHub. keep them in roms/ only.
the default permission integer in .env.example is 36785216. that is what this setup uses for the invite URL.
git clone https://github.com/coah80/ttsmodachi.git
cd ttsmodachi
cp .env.example .env
on Windows PowerShell, use:
Copy-Item .env.example .env
now edit .env.
minimum local test values:
DISCORD_TOKEN=replace_me
TTSMODACHI_BOT_CLIENT_ID=your_discord_application_client_id_here
TTSMODACHI_PANEL_URL=http://127.0.0.1:18080
TTSMODACHI_WORKER_ROMS=US
TTSMODACHI_US_WORKERS=1
TTSMODACHI_MAX_INFLIGHT_RENDERS=4
TTSMODACHI_BOT_RENDER_CONCURRENCY=2
for a public host, change these:
TTSMODACHI_PANEL_URL=https://your-domain.example
TTSMODACHI_PUBLIC_HOSTS=your-domain.example
TTSMODACHI_PANEL_SIGNING_KEY=replace_me
TTSMODACHI_SUPPORT_INVITE_URL=https://discord.gg/your-support-server
you can make a signing key with:
python -c "import secrets; print(secrets.token_urlsafe(48))"
this part is the least friendly part, sorry. there is a tool way now, and then the manual way if the tool is annoying on your setup.
you need a legal Tomodachi Life dump as a CXI. the renderer expects patched files named like this:
roms/US.cxi
roms/EU.cxi
roms/JP.cxi
roms/KR.cxi
only put the regions you are actually using in .env. for example:
TTSMODACHI_WORKER_ROMS=US
TTSMODACHI_US_WORKERS=1
you still need 3dstool and Magikoopa installed. the helper does the extract and rebuild steps, then pauses while you run the Magikoopa patch step.
from the repo folder:
python tools/patch_rom.py --input ./TomodachiLife.cxi --region US
on Windows, if python is not on PATH:
py -3 tools\patch_rom.py --input C:\path\to\TomodachiLife.cxi --region US
if 3dstool is not on PATH:
python tools/patch_rom.py --input ./TomodachiLife.cxi --region US --three-dstool /path/to/3dstool
if you want the helper to launch Magikoopa for you:
python tools/patch_rom.py --input ./TomodachiLife.cxi --region US --magikoopa /path/to/Magikoopa
what the tool does:
.rom-patch-work/code.bin.rom-patch-work/gamePatch/roms/US.cxiif Magikoopa is easier to run manually, start with:
python tools/patch_rom.py --input ./TomodachiLife.cxi --region US --prepare-only
open .rom-patch-work/gamePatch/ in Magikoopa, press Make and Insert, then run the resume command the tool prints.
the original Talkmodachi patch flow is:
code.bin and exheader.bin from your CXI with 3dstoolcode.bin is compressed, decompress itcode.bin and exheader.bin in gamePatch/roms/example 3dstool commands, using a US dump:
3dstool -xvtf cxi ./TomodachiLife.cxi --header header --exefs exefs --exh exheader.bin --logo logo --plain plain --romfs romfs
3dstool -xvtf exefs ./exefs --exefs-dir exefsd --header exfsheader
3dstool -u --file ./exefsd/code.bin --compress-type blz --compress-out ./exefsd/code_unc.bin
mv ./exefsd/code_unc.bin ./exefsd/code.bin
after patching with Magikoopa:
3dstool -cvtf exefs ./exefs --exefs-dir exefsd --header exfsheader
3dstool -cvtf cxi ./US.cxi --header header --exefs exefs --exh exheader.bin --logo logo --plain plain --romfs romfs --not-encrypt
mkdir -p roms
mv ./US.cxi ./roms/US.cxi
tools you probably need:
3dstool: https://github.com/dnasdw/3dstoolbuild and start the normal one-bot setup:
docker compose up --build
or run it in the background:
docker compose up --build -d
docker compose logs -f tts-worker bot
open the panel locally:
http://127.0.0.1:18080
check renderer health:
curl http://127.0.0.1:18080/health
if you also want the second bot container:
docker compose --profile bot2 up --build -d
then set DISCORD_TOKEN_2 and TTSMODACHI_SECOND_BOT_CLIENT_ID in .env.
when the renderer is up, visit:
http://127.0.0.1:18080
if TTSMODACHI_BOT_CLIENT_ID is set, the page can generate an invite link. you can also build one yourself:
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=36785216&scope=bot%20applications.commands
inside Discord:
/join/channel #some-channel to pick a normal text channel/voice to open your personal voice dashboard/join joins your current voice channel/leave leaves voice/channel sets the text channel the bot reads into the voice channel it is joined in/skip clears queued TTS and stops current playback/settings shows current server settings/set read_non_vc_messages true reads voice-channel chat from people not in that voice channel/voice opens the signed voice dashboard link/unlink removes your dashboard link and panel voice preset/voices list shows built-in and saved voices/voices save saves a custom voice/voices use selects your voice/voices default sets the server default/replace add/remove/list/clear manages pronunciation replacementstext shortcuts also exist:
-skip skips the current playback-message here skips your own message from TTS/channel #some-channel sets a normal text channel for TTS. messages sent there get read into the voice channel TTSmodachi is currently joined in. run /channel with no channel to clear it and go back to just voice channel chat.
/set bot_ignore false lets other bots and webhooks get read from the current voice channel chat or configured /channel text channel too, including embed-only messages. Automated messages do not need a prefix or required role once you opt in. TTSmodachi still needs to already be in voice, because bots and webhooks do not tell it which voice channel to join.
/set read_non_vc_messages true is off by default. when it is on, people who are not in voice can type in a voice channel's built-in chat and be read in that same voice channel. it still needs text_in_voice on, and it does not make TTSmodachi read the rest of the server.
start small:
TTSMODACHI_US_WORKERS=1
TTSMODACHI_MAX_INFLIGHT_RENDERS=4
TTSMODACHI_BOT_RENDER_CONCURRENCY=2
TTSMODACHI_OUTPUT_GAIN_PERCENT=125
TTSMODACHI_GRAMMAR_PAUSES=true
if your CPU has room, raise workers later. each worker is a warm Citra instance. more workers can make latency worse if the machine is already out of CPU.
the defaults in .env.example are intentionally small. once it works, raise workers and concurrency slowly.
TTSMODACHI_OUTPUT_GAIN_PERCENT is the master volume boost after rendering. 100 is normal, 125 is the louder default, and 150 is pretty spicy.
TTSMODACHI_GRAMMAR_PAUSES adds short native pauses after punctuation so sentences read less flat. leave it on unless the renderer starts acting weird.
by default, the bot joins voice undeafened so Discord does not show it as deafened. if you want the old behavior:
TTSMODACHI_VOICE_SELF_DEAF=true
keep the renderer bound to localhost unless you know what you are doing:
RENDERER_BIND=127.0.0.1
if you put a reverse proxy in front of it, set:
TTSMODACHI_PANEL_URL=https://your-domain.example
TTSMODACHI_PUBLIC_HOSTS=your-domain.example
TTSMODACHI_PANEL_SIGNING_KEY=replace_me
TTSMODACHI_PANEL_TOKEN=replace_me
TTSMODACHI_PANEL_TOKEN locks public /render and /api/config requests. internal Docker calls from the Discord bot still work without sending that token.
.env.gitignore and .dockerignore try to help, but still check before pushing.
this is based on:
Talkmodachi is the reason the Tomodachi Life renderer part exists. Discord-TTS/Bot inspired a lot of the Discord bot shape and command ideas.
there is no new license file in this repo right now because the upstream Talkmodachi repo does not publish a license file at the time this README was written. check the upstream projects before redistributing modified copies or using this for anything serious.
Stable Diffusion web UI