OpenClaw TeamSpeak Plugin
A real OpenClaw channel plugin for TeamSpeak, not just a one-off workspace hack.
This project bridges the local ts client/plugin runtime into OpenClaw so TeamSpeak text and voice can participate in normal OpenClaw sessions.
What it does
- registers a
teamspeakchannel plugin - receives TeamSpeak text and client-move events through
ts daemonhooks - sends outbound text replies back through
ts message send - consumes the TeamSpeak media socket for voice input and playback
- transcribes finalized utterances through OpenClaw STT
- synthesizes spoken replies through OpenClaw TTS
- applies per-session defaults for TeamSpeak sessions
- exposes runtime diagnostics through
teamspeak.voice.status
Project status
This is live code and currently used in a real OpenClaw runtime.
Current shape:
- production code is still intentionally small and flat
- unit tests cover the stable plugin seams; live/manual verification still covers TeamSpeak/OpenClaw integration
- broader non-message TeamSpeak event routing is deferred until
teamspeak-clievent docs are clearer
Repository layout
teamspeak/
├── index.js # main OpenClaw plugin entrypoint
├── hook-relay.js # ts daemon hook -> local HTTP ingress relay
├── openclaw.plugin.json # plugin id + config schema
├── package.json # local project metadata + check scripts
├── AGENTS.md # project-specific maintainer notes
├── CHANGELOG.md # human changelog
├── CONTRIBUTING.md # contribution / verification guidance
├── .gitignore
└── docs/
├── architecture.md
├── configuration.md
├── development.md
├── live-contracts.md
└── event-surfaces.md
Runtime dependencies
- OpenClaw with plugin loading enabled
- local
tsCLI installed - TeamSpeak 3 client with
teamspeak-cliplugin bridge loaded - a reachable OpenClaw gateway
Optional for voice:
- OpenClaw TTS configured (
talk.speak) - OpenClaw STT configured (
runtime.stt.transcribeAudioFile(...))
Quick start
Configure channels.teamspeak in ~/.openclaw/openclaw.json:
{
"channels": {
"teamspeak": {
"enabled": true,
"cliPath": "ts",
"daemonPollMs": 1000,
"ingressPath": "/plugins/teamspeak/inbound",
"sessionDefaults": {
"model": "openai-codex/gpt-5.3-codex-spark",
"fastMode": true,
"thinkingLevel": "off"
},
"commandAuthorization": {
"mode": "none"
},
"channelMessages": {
"trust": "trusted"
},
"directMessages": {
"trust": "trusted"
},
"voice": {
"enabled": true,
"mode": "wake_word",
"silenceTimeoutMs": 1200,
"interruptOnSpeech": true,
"interruptMode": "wake_word",
"stripWakeWord": true,
"mirrorTextReplies": false,
"transcriptionLanguage": "en"
}
}
}
}
If cliPath is omitted, the plugin now falls back to the first configured environment override it finds:
OPENCLAW_TEAMSPEAK_CLI_PATHTEAMSPEAK_CLI_PATHTS_CLI_PATH
If none are set, it uses ts from PATH.
interruptMode supports:
"any_speech"— current behavior, interrupt playback as soon as another speaker starts"wake_word"— only interrupt after a finalized/transcribed utterance is accepted via wake word
For STT/TTS and config details, see:
docs/configuration.mddocs/event-surfaces.md
Checks
Syntax plus unit tests:
npm run check
Unit tests only:
npm test
Useful live checks:
ts status
ts daemon status --json
ts events hook list --json
openclaw gateway call teamspeak.voice.status --json
Optional local contract smoke:
npm run smoke:contracts
This validates the local ts and openclaw JSON shapes that unit tests cannot prove. See docs/live-contracts.md for options and caveats.
Manual verification
Text path: 1. send a TeamSpeak DM 2. send a TeamSpeak channel message 3. confirm the correct OpenClaw session appears 4. reply from OpenClaw 5. confirm the reply goes back to TeamSpeak
Channel chat intentionally keeps one shared OpenClaw channel session because the bot is one TeamSpeak client that can move between server channels. Replies still target the originating TeamSpeak channel id.
Voice path: 1. confirm teamspeak.voice.status reports connected: true 2. speak a wake-word-qualified utterance 3. confirm the sanitized lastTranscriptionMetrics summary updates 4. confirm the agent replies in the correct TeamSpeak session 5. confirm spoken playback returns through the TeamSpeak client
Known limitations
- TeamSpeak daemon hooks currently reconcile
message.receivedfor text andclient.movedmovement notifications client.movedturns are informational and are never command-authorized- broader
ts events watchevent hookup exists in principle but is not wired into OpenClaw yet - the media bridge is still the authoritative path for live voice activity
- live TeamSpeak/OpenClaw behavior still needs targeted integration checks after runtime changes
voice.mode: "wake_or_ptt"currently behaves like wake-word mode because media frames do not expose PTT state- UID-based proactive DMs depend on a volatile live UID to client-id cache or successful live client-list lookup
Related docs
docs/architecture.mddocs/configuration.mddocs/development.mddocs/event-surfaces.mddocs/live-contracts.md





