話題の「ChatGPT✖️Pubmed新着論文」のLINE Botに検索機能もつける

![ピンク　緑　黄色　サロン　LINE　リッチメニュー (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653579/4762843a-7005-9160-6a01-900c6f9cb27d.png)


### はじめに
ものづくりの先輩医師がこのような画期的なシステムを作ってくださいました。

https://slide.antaa.jp/article/view/d0de1bd8ea414420

https://twitter.com/naokiuchid/status/1639767213791145985

自分の興味のある検索ワードで実装すると、毎日定時にPubmedから検索してChatGPTで要約したものが届きます。
スキマ時間で読めるのでとても便利！かつPubmedから検索なので実在しない論文は出てきません。

これを参考に
・LINEへの通知（二つ目の機能はつけない場合は途中を読み飛ばして最後だけ読んでください。）
・自動通知は固定の検索ワードにしておいて、ふと気になった検索ワードで検索できる機能
をつけました。

なるべく元のをいじらず省エネで作っています（笑）

https://twitter.com/naokiuchid/status/1642764296622051328

## スプレッドシートを準備
![スクリーンショット 2023-04-03 15.06.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653579/4e63ce45-be59-d3d8-4fb0-c54e855a3ef3.png)
こんな感じで準備してください。
C3セルに"=365*C2"と入力しておいてください。
シート名はmainにします（スクリプトと統一していればなんでも良いです）。

## LINE通知の準備
ほぼ元のスクリプトと同様に書いて、通知の部分を書き換えます。
初めに、
```javascript
let CHANNEL_ACCESS_TOKEN =
  "LINEのチャンネルアクセストークン";
let line_endpoint = "https://api.line.me/v2/bot/message/reply";
let sheet= SpreadsheetApp.openById("準備したスプレッドシートID").getSheetByName("main")
```
を追加します。
スプレッドシートIDは該当のスプレッドシートのURL https://docs.google.com/spreadsheets/d/******/edit の******部分です。

## リプライメッセージ部分
```javascript
function doPost(e) {
  let json = JSON.parse(e.postData.contents);
  let reply_token = json.events[0].replyToken;
  if (typeof reply_token === "undefined") {
    return;
  }
  if (json.events[0].type == "message") {
    const usermessage = json.events[0].message.text;
    if (usermessage == "他の論文を検索する") {
      sheet.getRange("A2").setValue(1);
      messages = [
        {
          type: "text",
          text:
            "「検索ワード」、「publishされた期間(年)を半角数字で」、「論文上限数(max13個)を半角数字で」の順番に入れてください（各項目ごとにメッセージを送信）",
        },
      ];
    } else {
      let count = parseInt(sheet.getRange("A2").getValue());
      if (count == 3) {
        sheet.getRange("D2").setValue(usermessage);
        let otherPUBMED_TERM = sheet.getRange("C3").getValue();
        const date = new Date();
        const past = new Date(
          date.getFullYear(),
          date.getMonth(),
          date.getDate() - otherPUBMED_TERM
        );
        let otherPUBMED_QUERY = sheet.getRange("B2").getValue();
        const ids = getotherPaperIDsOn(past, date, otherPUBMED_QUERY);
        let output = "検索結果のお知らせ\n\n";
        let paperCount = 0;

        let otherMAX_PAPER_COUNT = usermessage;
        for (let i = 0; i < ids.length; i++) {
          Utilities.sleep(1000);
          const id = ids[i];
          const pubmedUrl = `https://pubmed.ncbi.nlm.nih.gov/${id}`;
          const summary = getPaperSummaryByID(id);
          const title = summary.title;
          console.log(`id: ${id}, pubtype: ${summary.pubtype.join(",")}`);
          if (!checkPubtype(summary.pubtype)) {
            console.log("pubtype: NG");
            continue;
          }
          console.log("pubtype: OK");
          if (++paperCount > otherMAX_PAPER_COUNT) break;
          const abstract = getPaperAbstractByID(id);
          const input =
            "\n" + "title: " + title + "\n" + "abstract: " + abstract;
          const res = callChatGPT([
            {
              role: "user",
              content: PROMPT_PREFIX + "\n" + input,
            },
          ]);
          const paragraphs = res.choices.map((c) => c.message.content.trim());
          output += `${paragraphs.join("\n")}\n\n${pubmedUrl}\n\n\n`;
        }
        output = output.trim();
        messages = [
          {
            type: "text",
            text: output,
          },
        ];
        sheet.getRange("A2").setValue(0);
      } else {
        sheet.getRange(2, count + 1).setValue(usermessage);
        sheet.getRange("A2").setValue(count + 1);
      }
    }
    sendmessage(messages, reply_token);
  }
  return ContentService.createTextOutput(
    JSON.stringify({ content: "post ok" })
  ).setMimeType(ContentService.MimeType.JSON);
}

function getotherPaperIDsOn(past,date,otherPUBMED_QUERY) {
  
    const url = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&retmode=json&sort=pub_date&term=${otherPUBMED_QUERY}&mindate=${toYYYYMMDD(past)}&maxdate=${toYYYYMMDD(date)}`;
    const res = JSON.parse(UrlFetchApp.fetch(url).getContentText());
    return res.esearchresult.idlist;
}

function sendmessage(message, reply_token) {
  UrlFetchApp.fetch(line_endpoint, {
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
      Authorization: "Bearer " + CHANNEL_ACCESS_TOKEN,
    },
    method: "post",
    payload: JSON.stringify({
      replyToken: reply_token,
      messages: message,
    }),
  });
}

```
## 仕組み
### モードセル(A2)の数値で挙動を区別
①「他の論文を検索する」が送信されるとモード1
②モード1,2の時はセルにusermessageを格納し、モードの数字を1増やす
③モード3の時はこれまでのメッセージをスプレッドシートから抽出
　→Pubmed検索→ChatGPT API→結果をLINE送信

## リッチメニューは作らなくても良い
私はリッチメニューを押したら「他の論文を検索する」のメッセージが送信され、そこからスタートする仕様にしましたが、「他の論文を検索する」のところを「検索」とか簡単なワードにして（何なら「あ」だけでもよい）、自分で入力するでも機能には影響ありません。


## 自動送信の部分をLINE通知に変更する方法
ちなみに、メール通知をLINE通知に変更する方法は、初めに
```javascript
let CHANNEL_ACCESS_TOKEN =
  "LINEのチャンネルアクセストークン";
```
を追加した上で、元のスクリプトの「sendEmail(output);」部分を消して、
```javascript
UrlFetchApp.fetch('https://api.line.me/v2/bot/message/broadcast', {
            method: 'post',
            headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
            },
            payload: JSON.stringify({
              messages: [
                {
                    type: 'text',
                    text: output
                },
                
              ]
              }),
          }) 
```
をコピペしてください。
LINEアカウントの作り方・GASとの連携は[こちら](https://qiita.com/cog1t0/items/f6e832d54a7b0e692434)から。

## おわりに
各部分もっとスマートなやり方はあると思いますが、初心者かつ「動けばよい」精神でやっているのでこんな感じです。
スマートなやり方を教えてくださる方はぜひコメントをお願いいたします！