sa1911
@sa1911

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

LINE Botでテキストとスタンプを送る方法を教えて下さい

解決したいこと

以下の流れで、LINE BotでOpenAI APIによって生成されたテキストの後に、LINEスタンプを送るようにしたいです。

  1. LINE Botでユーザーがキーワードを入力(例:人形)
  2. OpenAI APIにリクエストを投げて、キーワードにまつわる怪談を生成
  3. LINEにテキストで怪談を返す
  4. テキストのあとにスタンプを送る

Messaging API[1]を使っています。

実行すると、1, 2, 3は成功します。しかし、スタンプが送られてきません。

実行環境は以下です。

  • GitHub Codespaces
  • JavaScript
"@line/bot-sdk": "^7.5.2",
"axios": "^1.4.0",
"express": "^4.18.2",
"line-bot-sdk": "^0.1.4",
"openai": "^3.2.1"

【参考リンク】
[1] https://developers.line.biz/ja/docs/messaging-api/sticker-list/

発生している問題・エラー

実行すると、以下のエラーが出力されます。スタンプメッセージを送るところで失敗しています。

@xxxx ➜ /workspaces/xxxx/boot/02-line-bot (main) $ node horror2.js 
ポート3000番でExpressサーバーを実行中です…
受信しました: [
  {
    type: 'message',
    message: { type: 'text', id: 'xxxx', text: 'お茶' },
    webhookEventId: 'xxxx',
    deliveryContext: { isRedelivery: false },
    timestamp: 1685232670580,
    source: { type: 'user', userId: 'xxxx' },
    replyToken: 'xxxx',
    mode: 'active'
  }
]
[
  {
    role: 'system',
    content: 'あなたは熟練の怪談師です。ユーザーから指定されたキーワードを使って怪談を書いてください。'
  },
  { role: 'user', content: 'お茶' }
]
スタンプメッセージ送信エラー: HTTPError: Request failed with status code 400
    at HTTPClient.wrapError (/workspaces/xxxx/boot/02-line-bot/node_modules/@line/bot-sdk/dist/http.js:89:20)
    at /workspaces/xxxx/boot/02-line-bot/node_modules/@line/bot-sdk/dist/http.js:19:88
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async HTTPClient.post (/workspaces/xxxx/boot/02-line-bot/node_modules/@line/bot-sdk/dist/http.js:33:21)
    at async sendStickerMessage (/workspaces/xxxx/boot/02-line-bot/horror2.js:82:5)
    at async handleEvent (/workspaces/xxxx/boot/02-line-bot/horror2.js:71:5)
    at async Promise.all (index 0) {
  statusCode: 400,
  statusMessage: 'Bad Request',
  originalError: [AxiosError: Request failed with status code 400] {
    code: 'ERR_BAD_REQUEST',
    config: {
      transitional: [Object],
      adapter: [Function: httpAdapter],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [Object],
      method: 'post',
      url: 'https://api.line.me/v2/bot/message/reply',
      data: '{"messages":[{"type":"sticker","packageId":"446","stickerId":"2027"}],"replyToken":"xxxx","notificationDisabled":false}'
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: false,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 147,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [TLSSocket],
      _header: 'POST /v2/bot/message/reply HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'Authorization: Bearer xxxx\r\n' +
        'User-Agent: @line/bot-sdk/7.5.2\r\n' +
        'Content-Length: 147\r\n' +
        'Host: api.line.me\r\n' +
        'Connection: keep-alive\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/v2/bot/message/reply',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: [Function: emitRequestTimeout],
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: true,
      host: 'api.line.me',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 16384,
      [Symbol(kUniqueHeaders)]: null
    },
    response: {
      status: 400,
      statusText: 'Bad Request',
      headers: [Object],
      config: [Object],
      request: [ClientRequest],
      data: [Object]
    }
  }
}
^C
@xxxx ➜ /workspaces/xxxx/boot/02-line-bot (main) $ 

該当するソースコード

'use strict';

// ########################################
//               初期設定など
// ########################################

// モジュールの読み込み
const line = require('@line/bot-sdk');
const openai = require('openai');
const express = require('express');

const PORT = process.env.PORT || 3000;

// 設定
const config = {
    channelSecret: 'チャネルシークレット',
    channelAccessToken: 'チャネルアクセストークン'
};

// クライアントの作成
const client = new line.Client(config);
const gptConfig = new openai.Configuration({
    organization: process.env.OPENAI_ORGANIZATION || "組織ID",
    apiKey: process.env.OPENAI_API_KEY || 'APIキー',
  });

const gpt = new openai.OpenAIApi(gptConfig);

const makeCompletion = async (userMessage) => {
  const prompt = {
    role: 'system',
    content: 'あなたは熟練の怪談師です。ユーザーから指定されたキーワードを使って怪談を書いてください。' // プロンプトを入力
  };

  userMessage.unshift(prompt);
  console.log(userMessage);
  return await gpt.createChatCompletion({
    model: 'gpt-3.5-turbo',
    messages: userMessage,
    temperature: 0.5,
    n: 1
  });
};

// メッセージイベントの処理
async function handleEvent(event) {
  // テキストメッセージ以外は無視
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  const userMessage = [
    {
      role: 'user',
      content: event.message.text
    }
  ];

  // ChatGPT APIにリクエストを送る
  try {
    const completion = await makeCompletion(userMessage);
    // レスポンスから返答を取得
    const reply = completion.data.choices[0].message.content;
    // 返答をLINEに送る
    await client.replyMessage(event.replyToken, {
      type: 'text',
      text: reply
    });

    // スタンプメッセージを送信する
    await sendStickerMessage(event.replyToken);
  } catch (error) {
    // エラーが発生した場合はログに出力
    console.error('メッセージ送信エラー:', error);
    return Promise.resolve(null);
  }
}

// スタンプメッセージを送信する
async function sendStickerMessage(replyToken) {
  try {
    await client.replyMessage(replyToken, {
      type: 'sticker',
      packageId: '446', // スタンプのパッケージIDに置き換える
      stickerId: '2027' // スタンプのIDに置き換える
    });
    console.log('スタンプメッセージを送信しました');
  } catch (error) {
    console.error('スタンプメッセージ送信エラー:', error);
  }
}

const app = express();
app.get('/', (req, res) => res.send('Hello LINE BOT! (HTTP GET)'));
app.post('/webhook', line.middleware(config), (req, res) => {
  if (req.body.events.length === 0) {
    res.send('Hello LINE BOT! (HTTP POST)');
    console.log('検証イベントを受信しました!');
    return;
  } else {
    console.log('受信しました:', req.body.events);
  }

  Promise.all(
    req.body.events.map((event) => {
      if (event.type === 'message' && event.message.type === 'text') {
        return handleEvent(event);
      } else if (event.type === 'message' && event.message.type === 'sticker') {
        return handleMessageEvent(event);
      } else {
        return null;
      }
    })
  ).then((result) => res.json(result));
});

app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);

自分で試したこと

ChatGPTに聞きながらトラブルシューティングをしましたが、修正箇所がわかりませんでした。
コードを書いた経験がないため、修正箇所の検討が尽きませんでした。
確認すべき勘所など、アドバイスだけでも頂けますと嬉しいです。

0

1Answer

他のところで回答を頂き、修正したところ成功したので本質問はクローズとします!

コード
'use strict';

// ########################################
//               初期設定など
// ########################################

// モジュールの読み込み
const line = require('@line/bot-sdk');
const openai = require('openai');
const express = require('express');

const PORT = process.env.PORT || 3000;

// 設定
const config = {
    channelSecret: 'チャネルシークレット', 
    channelAccessToken: 'チャネルアクセストークン'
};

// クライアントの作成
const client = new line.Client(config);
const gptConfig = new openai.Configuration({
    organization: process.env.OPENAI_ORGANIZATION || "組織ID",
    apiKey: process.env.OPENAI_API_KEY || 'APIキー',
  });

const gpt = new openai.OpenAIApi(gptConfig);

const makeCompletion = async (userMessage) => {
  const prompt = {
    role: 'system',
    content: 'あなたは熟練の怪談師です。ユーザーから指定されたキーワードを使って怪談を書いてください。' // プロンプトを入力
  };

  userMessage.unshift(prompt);
  console.log(userMessage);
  return await gpt.createChatCompletion({
    model: 'gpt-3.5-turbo',
    messages: userMessage,
    // temperature: 0.5,
    temperature: 0.2,
    n: 1
  });
};

// メッセージイベントの処理
async function handleEvent(event) {
  // テキストメッセージ以外は無視
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  const userMessage = [
    {
      role: 'user',
      content: event.message.text
    }
  ];

  // ChatGPT APIにリクエストを送る
  try {
    const completion = await makeCompletion(userMessage);
    // レスポンスから返答を取得
    const reply = completion.data.choices[0].message.content;
    // スタンプメッセージを送信する
    return sendStickerMessage(event.replyToken,reply); 
  } catch (error) {
    // エラーが発生した場合はログに出力
    console.error('メッセージ送信エラー:', error);
    return Promise.resolve(null);
  }
}


// スタンプメッセージを送信する
async function sendStickerMessage(replyToken,reply) {
  try {
    const stickermessage = {
      type: 'sticker',
      packageId: '446', // スタンプのパッケージIDに置き換える
      stickerId: '2027' // スタンプのIDに置き換える
    }
    const replyMessage = {
      type: 'text',
      text: reply      
    }
      return client.replyMessage(replyToken,[replyMessage,stickermessage]);
  } catch (error) {
    console.error('スタンプメッセージ送信エラー:', error.request.data);
  }
}

// ここ以降は理解しなくてOKです
const app = express();
app.get('/', (req, res) => res.send('Hello LINE BOT! (HTTP GET)'));
app.post('/webhook', line.middleware(config), (req, res) => {
  if (req.body.events.length === 0) {
    res.send('Hello LINE BOT! (HTTP POST)');
    console.log('検証イベントを受信しました!');
    return;
  } else {
    console.log('受信しました:', req.body.events);
  }

  Promise.all(
    req.body.events.map((event) => {
      if (event.type === 'message' && event.message.type === 'text') {
        return handleEvent(event);
      } else if (event.type === 'message' && event.message.type === 'sticker') {
        return handleMessageEvent(event);
      } else {
        return null;
      }
    })
  ).then((result) => res.json(result));
});

app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
0Like

Your answer might help someone💌