feat: Alexa TTS API サーバーを追加

- alexa-api/: Echo デバイスに TTS を送る Node.js API サーバー
  - server.js: alexa-remote2 を使わない直接 Alexa API 実装
    - GET /api/language で CSRF トークン取得
    - GET /api/bootstrap でカスタマー ID 取得
    - POST /api/behaviors/preview で TTS 実行
  - Dockerfile + docker-compose.yml: windmill_windmill-internal ネットワーク接続
  - auth4.js: Amazon Japan OpenID フローで Cookie 取得(WORKING)
- scripts/alexa_speak.ts: Windmill から alexa-api を呼び出すスクリプト

Windmill (u/admin/alexa_speak) → alexa_api:3500/speak → Echo デバイス の
パスで日本語 TTS が動作することを確認済み。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Akira
2026-03-02 16:34:22 +09:00
parent 593d13d8a1
commit 34107f98a2
13 changed files with 2083 additions and 0 deletions

76
alexa-api/auth2.js Normal file
View File

@@ -0,0 +1,76 @@
/**
* auth2.js - alexa-remote2 自身の認証フローを使う
* alexa-cookie2 より確実(ライブラリ内蔵の OAuth プロキシを使用)
*
* 使い方:
* node auth2.js
* → ブラウザで http://localhost:3001/ を開いて Amazon にログイン
* → 成功するとコンソールに Cookie が出力される → .env に保存
*/
const AlexaRemote = require('alexa-remote2');
const fs = require('fs');
const path = require('path');
const PORT = 3001;
console.log('==============================================');
console.log(' Alexa 認証ツール (alexa-remote2 内蔵プロキシ)');
console.log('==============================================');
console.log(`\nプロキシ起動中... (port ${PORT})`);
console.log(`\n【手順】ブラウザで http://localhost:${PORT}/ を開いて Amazon にログイン\n`);
const alexa = new AlexaRemote();
alexa.init(
{
cookie: null,
alexaServiceHost: 'alexa.amazon.co.jp',
amazonPage: 'amazon.co.jp',
acceptLanguage: 'ja-JP',
useWsMqtt: false,
setupProxy: true,
proxyOwnIp: '127.0.0.1',
proxyPort: PORT,
proxyListenBind: '0.0.0.0',
logger: console.log,
onSucess: (refreshedCookie) => {
// 認証成功時にリフレッシュされた Cookie を受け取る
console.log('\n[onSucess] Cookie refreshed');
},
},
(err, result) => {
if (err) {
const msg = err.message || String(err);
if (msg.includes('open') && (msg.includes('http://') || msg.includes('localhost'))) {
console.log(`\n>>> ブラウザで http://localhost:${PORT}/ を開いてください <<<\n`);
// プロキシを生かしたまま待機
return;
}
console.error('\n[ERROR]', msg);
return;
}
// 認証成功
console.log('\n==============================================');
console.log(' 認証成功!');
console.log('==============================================');
// alexa-remote2 内部の Cookie を取得
const cookie = alexa.cookie || alexa._options?.cookie;
if (cookie) {
const cookieStr = typeof cookie === 'string' ? cookie : JSON.stringify(cookie);
console.log('\n以下を alexa-api/.env の ALEXA_COOKIE に設定してください:\n');
console.log('ALEXA_COOKIE=' + cookieStr);
// .env に自動保存
const envPath = path.join(__dirname, '.env');
fs.writeFileSync(envPath, 'ALEXA_COOKIE=' + cookieStr + '\n');
console.log(`\n.env に自動保存しました: ${envPath}`);
} else {
console.log('Cookie を取得できませんでした。alexa オブジェクト:', Object.keys(alexa));
}
process.exit(0);
}
);