server.js を3点変更しました:

locale: 'ja-JP' → locale: ''(ローカルPCで成功していた設定)
Content-Length ヘッダーを除去(test_tts.js では送っていなかった、これが差異の一つ)
デバッグログを追加(Amazonへのリクエスト内容とレスポンスをログ出力)
認証問題について

test_tts.js(ローカルで成功)と server.js(サーバーで失敗)を比較した結果、実は使っているCookieは全く同じもの(.env から読み込んでいる)なので、認証情報自体の差は本来ないはずです。

ただし、気になる点が1つあります:

test_tts.js は locale: 'ja-JP' でローカルから成功しているのに、server.js は locale: 'ja-JP' でサーバーから失敗している

これは実は「どこから接続しているか(IPアドレス) で Amazon 側の挙動が変わっている」可能性を示唆します。ただし、先ほど確認したように keinafarm.net は大阪のIPなので、この説明も矛盾します。

デプロイ手順:

bash
# ローカルから scp でサーバーへ転送
scp alexa-api/server.js keinafarm-claude:/home/claude/alexa-api/server.js
# サーバーへSSHしてビルド&再起動
ssh keinafarm-claude 'cd /home/claude/alexa-api && sudo docker compose build && sudo docker compose up -d && sudo docker restart traefik'
デプロイ後、sudo docker logs alexa_api -f でログを確認して、[DEBUG] 行の内容を教えてください。どんな JSON が Amazon に送られているか、Amazon が何を返しているかが見えてきます。
This commit is contained in:
Akira
2026-03-03 11:46:02 +09:00
parent b2a4012ab5
commit ee59724093
3 changed files with 2289 additions and 4 deletions

View File

@@ -37,6 +37,7 @@ function httpsRequest(path, options, extraCookies) {
extraCookies = extraCookies || '';
return new Promise(function(resolve, reject) {
var allCookies = ALEXA_COOKIE + (extraCookies ? '; ' + extraCookies : '');
// bodyBuf はバイト列変換(マルチバイト文字に対応)
var bodyBuf = options.body ? Buffer.from(options.body, 'utf8') : null;
var reqOpts = {
hostname: ALEXA_HOST,
@@ -47,7 +48,8 @@ function httpsRequest(path, options, extraCookies) {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'ja-JP,ja;q=0.9',
'Cookie': allCookies,
}, bodyBuf ? { 'Content-Length': bodyBuf.length } : {}, options.headers || {}),
// Content-Length は送らないtest_tts.js で動作実績あり、Amazonが自動判定
}, options.headers || {}),
};
var req = https.request(reqOpts, function(res) {
var body = '';
@@ -128,8 +130,8 @@ app.post('/speak', async function(req, res) {
console.log(' -> ' + target.accountName + ' (type=' + target.deviceType + ', serial=' + target.serialNumber + ')');
var ssml = '<speak>' + text + '</speak>';
// locale: '' はローカルPCでは日本語発話成功サーバーからは要検証
// Alexa.SpeakSsml 系は全滅のため Alexa.Speak に戻す
var sequenceObj = {
'@type': 'com.amazon.alexa.behaviors.model.Sequence',
startNode: {
@@ -139,7 +141,7 @@ var sequenceObj = {
deviceType: target.deviceType,
deviceSerialNumber: target.serialNumber,
customerId: customerId,
locale: 'ja-JP',
locale: '',
textToSpeak: text
},
},
@@ -151,6 +153,9 @@ var sequenceObj = {
status: 'ENABLED',
});
// リクエストボディをログ出力(認証・パラメータ確認用)
console.log('[DEBUG] sequenceJson:', JSON.stringify(sequenceObj, null, 2));
var ttsRes = await httpsRequest('/api/behaviors/preview', {
method: 'POST',
headers: {
@@ -162,6 +167,9 @@ var sequenceObj = {
body: bodyStr,
}, 'csrf=' + csrfToken);
// Amazonからのレスポンスをログ出力
console.log('[DEBUG] Alexa API response: ' + ttsRes.status + ' body=' + ttsRes.body.substring(0, 200));
if (ttsRes.status === 200 || ttsRes.status === 202) {
console.log(' [OK] TTS sent to ' + target.accountName);
res.json({ ok: true, device: target.accountName, text: text });