tts_voice_send.sh 2.36 KB
#!/bin/bash
# tts_voice_send.sh — generate TTS audio and send as WeChat voice message
#
# Usage:
#   ./tts_voice_send.sh <wxid> <text> [voice]
#
# Args:
#   <wxid>   — talker id (use 'filehelper' for safe testing)
#   <text>   — Chinese text to synthesize
#   [voice]  — edge-tts voice name (default: zh-CN-XiaoxiaoNeural)
#
# Examples:
#   ./tts_voice_send.sh filehelper "你好,今天天气不错。"
#   ./tts_voice_send.sh filehelper "下午三点开会" zh-CN-YunxiNeural
#
# IMPORTANT: test only with 'filehelper' to avoid account risk.
#
# Dependencies (Mac):
#   pip3 install edge-tts
#   brew install ffmpeg
#   npm install -g silk-wasm (or use openclaw's bundled one)
#   adb in PATH or set ADB env var

set -e

WXID="${1:?Usage: $0 <wxid> <text> [voice]}"
TEXT="${2:?Missing text argument}"
VOICE="${3:-zh-CN-XiaoxiaoNeural}"

ADB="${ADB:-/opt/homebrew/share/android-commandlinetools/platform-tools/adb}"
SILK_WASM="${SILK_WASM:-/opt/homebrew/lib/node_modules/openclaw/node_modules/silk-wasm}"
TMPDIR="$(mktemp -d /tmp/tts_send_XXXXXX)"
trap "rm -rf $TMPDIR" EXIT

echo "[1/4] TTS: \"$TEXT\" (voice=$VOICE)"
python3 -c "
import asyncio, edge_tts
async def go():
    tts = edge_tts.Communicate('''$TEXT''', '$VOICE')
    await tts.save('$TMPDIR/out.mp3')
asyncio.run(go())
"

echo "[2/4] Convert MP3 → PCM 16kHz mono"
ffmpeg -y -i "$TMPDIR/out.mp3" -ar 16000 -ac 1 -f s16le "$TMPDIR/out.pcm" -loglevel error

echo "[3/4] Encode PCM → SILK V3"
DURATION=$(node -e "
const silk = require('$SILK_WASM');
const fs = require('fs');
silk.encode(fs.readFileSync('$TMPDIR/out.pcm'), 16000).then(r => {
    fs.writeFileSync('$TMPDIR/out.amr', r.data);
    process.stdout.write(String(r.duration));
});
")
echo "    → SILK $(wc -c < "$TMPDIR/out.amr" | tr -d ' ') bytes, duration=${DURATION}ms"

echo "[4/4] Push + send to WeChat ($WXID)"
$ADB push "$TMPDIR/out.amr" /sdcard/hook_voice_tmp.amr -q
$ADB shell "su -c 'cp /sdcard/hook_voice_tmp.amr /data/data/com.tencent.mm/cache/hook_voice_src.amr \
    && chmod 644 /data/data/com.tencent.mm/cache/hook_voice_src.amr \
    && chown u0_a330:u0_a330 /data/data/com.tencent.mm/cache/hook_voice_src.amr \
    && rm /sdcard/hook_voice_tmp.amr'"
$ADB shell "su -c 'am broadcast -a com.hermes.wechat.SEND_VOICE_INTERNAL \
    --es to $WXID \
    --es src_path /data/data/com.tencent.mm/cache/hook_voice_src.amr \
    --ei duration $DURATION'"

echo "Done."