【chatGPT、LINEAPI、GAS】LINEでchatGPTと友達っぽくトークする。

こんばんは。

## １．初めに

最近chatGPTが流行っていますが、確かに使ってみるとすごいですね。プロンプトエンジニアリングという、今までのプログラムの概念を覆すような技術が使用されています（同じ答えが返ってくるとは限らない）。
今回は、chatGPT・GAS・LINEAPIを連携させて、LINEでchatGPTと会話するbotを作成しました。

2年前の記事でLINEとGASを連携させる記事を書いたのですが、それを流用しています。chatGPTのAPIを呼び出すファンクションを追加する事で、割と簡単にできました。
生成AIを身近に体感するにはいい題材だと思うので、良かったらぜひ読んでみてください。

まず、LINE APIの設定およびGASからの呼び出しは、自分が書いた以下の記事を参考にしてみてください。
https://qiita.com/ishikawaryou/items/d8ceb1e353843e24a9a0
記事は2年前のものですが、まだ賞味期限は切れていないと思います。


## ２．準備

①chatGPTのアカウントを取得する。
　ここは色々情報があるので確認のうえ、登録いただければと思います。
 
②有料プランに登録する。
　API利用の場合は、料金は従量課金制です。通常の有料プランだと20ドルという事で割と高いですが、API利用だと1トークン0.002ドル（約0.27円）という事で格安になります。
　そんなに使いまくらなければ月100円も行かないんじゃないでしょうか。僕もまだ登録したばかりなので、どのくらい請求されるか確認しようと思います。
 
有料プランでクレジットカード登録をする必要があります。
最初、無料プランのままAPIキーを取得して実装してもエラーが続き、最初は原因が分かりませんでしたが有料プランにしたら解消しました。
こちらの記事が大変参考になりました。

https://auto-worker.com/blog/?p=7459



## ３．プログラムを作る
１．で紹介した自分の記事にあるプログラムにロジックを追加します。

こちらの記事が大変参考になりました。というよりかなり流用させていただいております。
ありがとうございます。
https://qiita.com/investerinarian/items/99ab12fdd74fb31bb39b


①メッセージのパターンにより、chatGPTのファンクションを呼び出す。
「// chatgpt from」「// chatgpt to」となっているところが追加箇所です。
「てんき」と入力すれば天気情報のAPIを呼び出したり、「走ってきます」と言えばジョギングの開始時刻を記録したり、色々突っ込んでいるbotですがそれは気にしないでいただいて、そういった決まった文字列以外のものが入力されたら今回のファンクションを呼び出すようにしました。

```java
function doPost(e) {
  var event = JSON.parse(e.postData.contents).events[0];
  var replyToken= event.replyToken;

  if (typeof replyToken === 'undefined') {
    return; // エラー処理
  }
  var userId = event.source.userId;
  var nickname = getUserProfile(userId);

  if(event.type == 'follow') { 
    // ユーザーにbotがフォローされた場合に起きる処理
  }

  var dataruid = '';
  dataruid = yahoo(dataruid);
  
  if(event.type == 'message') {
    var userMessage = event.message.text;
    // 今回は鸚鵡返しなので届いたメッセージをそのまま返します。
    var replyMessage = userMessage 
    var replyMessage2 = ''; 

    switch (true) {
      case userMessage === 'ヤフー':
       dataruid = yahoo(dataruid);
      break
      case userMessage === 'イルカ':
       dataruid = iruca(dataruid);
      break
      case userMessage === '天気':
       dataruid = tenki(dataruid);
      break
      case userMessage === 'てんき':
       dataruid = tenki2(dataruid);
      break
      case userMessage === '貯金':
       dataruid = chokin(dataruid);
      break
      case userMessage === 'ちょきん':
       dataruid = chokin2(dataruid);
      break
      case userMessage === 'テスト':
       dataruid = test(dataruid);
      break
      case userMessage === '走ってきます':
       dataruid = start(dataruid);
      break
      case userMessage === 'いま':
       dataruid = ima(dataruid);
      break
      case userMessage === '戻りました':
       dataruid = end(dataruid);
      break
      case userMessage < 30000000:
      dataruid = testGptTurbo(userMessage);
      break
      // chatgpt from
      default:
       dataruid = testGptTurbo(userMessage);
      break
      // chatgpt to
    }
    replyMessage2 = dataruid; 
    
  
    var url = 'https://api.line.me/v2/bot/message/reply';
    
    UrlFetchApp.fetch(url, {
      'headers': {
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'replyToken': replyToken,
        'messages': [{
          'type': 'text',
          'text': replyMessage2,
        }],
      }),
    });
    return ContentService.createTextOutput(
      JSON.stringify({'content': 'post ok'})
    ).setMimeType(ContentService.MimeType.JSON);
  }
}

// profileを取得してくる関数
function getUserProfile(userId){ 
  var url = 'https://api.line.me/v2/bot/profile/' + userId;
  var userProfile = UrlFetchApp.fetch(url,{
    'headers': {
      'Authorization' :  'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
  })
  return JSON.parse(userProfile).displayName;
}
```


③chatgpt呼び出しファンクションを追加

まず、function testGptTurboでは、LINEで入力されたメッセージを、宣言しています。
"xxxxxxxxxxxxxxxxxxxxxxxxxxxx"は、自身で取得したchatgptのAPIキーを指定します。
返ってきたメッセージを、rtnmsgという変数に整形して受け渡し、呼び出し元に返却します。
これがChatgptからの返答になります。


```java
function testGptTurbo(userMessage) {
  var apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  //const msg = apiKey;
  //var msg = "中国の大連が朝8時だと、日本は何時でしょうか";
  var msg = userMessage;

  var messages = [
    { "role": "user", "content": msg },

  ];
  var temperature = 0.3;
  var response = gptTurbo(messages, temperature, apiKey);
  //Logger.log(response.choices[0].message.content);
  var rtnmsg = response.choices[0].message.content;
  return rtnmsg;
  }




```

function GptTurboでは、そのメッセージを元に、chatGPTにアクセスします。


```java
  function gptTurbo(messages, temperature, apiKey) {
  const msg = 'gptturbo';
  console.log(msg);
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + apiKey
  };
  var payload = {
    "model": "gpt-3.5-turbo",
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers,
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    Logger.log(response)
  } catch (e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
  }
  var data = JSON.parse(response.getContentText());
  return data;
  }
    function gptTurbo(messages, temperature, apiKey) {
  const msg = 'gptturbo';
  console.log(msg);
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + apiKey
  };
  var payload = {
    "model": "gpt-3.5-turbo",
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers,
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    Logger.log(response)
  } catch (e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
  }
  var data = JSON.parse(response.getContentText());
  return data;
  }  function gptTurbo(messages, temperature, apiKey) {
  const msg = 'gptturbo';
  console.log(msg);
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + apiKey
  };
  var payload = {
    "model": "gpt-3.5-turbo",
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers,
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    Logger.log(response)
  } catch (e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
  }
  var data = JSON.parse(response.getContentText());
  return data;
  }  function gptTurbo(messages, temperature, apiKey) {
  const msg = 'gptturbo';
  console.log(msg);
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + apiKey
  };
  var payload = {
    "model": "gpt-3.5-turbo",
    "messages": messages,
    "temperature": temperature
  };
  var options = {
    'method': 'POST',
    'headers': headers,
    'payload': JSON.stringify(payload),
    "muteHttpExceptions": true
  };
  try {
    var response = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', options);
    Logger.log(response)
  } catch (e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
  }
  var data = JSON.parse(response.getContentText());
  return data;
  }
  ```

## ４．結果

無事メッセージのやりとりができました！！
ちょっと変な事言ってたり（大連は日本と1時間時差あるよね）、情報が古かったりするので（プリキュアの最新作はひろがるスカイ！プリキュア）実用的なのか疑問がありますが、今後賢くなってもらうとしよう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402487/8db4d761-61db-062c-c07f-60013b603883.png)


## ５．最後に

最近このセミナーにも参加しました。生成AIを少しだけ学ぼうと思っています。
生成AIの技術そのものは難しいかもしれませんが、せめてAPI利用等を通して利用するための予備知識は身に付けておいた方が何かと良さそうですね。

https://forkwell.connpass.com/event/301152/