ブックマークレットでさくっと再配布を簡単にする方法

## ブックマークレットでサクッとWEBページを自動で動かしたい時ありますよね？

例えば、ChatGPTを社内展開したけど毎回独自のプロンプトのテンプレート配布するのつらいなーって時ないですか？

あったとします。

（あとGPTs使えばいいじゃん、とか他サービス使えば・・とかまぁ方法はありますが今回はブックマークレットに絞ってお話しします）

そんな時にブックマークレットって形でChromeのブックマークに以下のJavaScriptを登録すれば動くわけです
<br/>

## Chromeで使えるぽちー通せばJSが実行できるブックマークレット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653035/3719ffb2-4546-7608-c482-16412c0d0e6a.png)

### ブックマークレットの中身のJS
（わからない単語を入力するとプロンプトにしてChatJPTの入力欄に入力して実行までを教えてくれる）
```js
javascript:(function() {
    var windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    var popupX = window.screenX + (windowWidth - 400) / 2;
    var popupY = window.screenY + (windowHeight - 200) / 2;
    
    var popup = window.open("", "わからない単語を入力してね", "width=400,height=200,left=" + popupX + ",top=" + popupY);
    
    var html = `
    <html>
    <head>
    <title>Popup</title>
    <style>
    body {
    background-color: rgba(255, 255, 255, 0.5);
    font-family: Arial, sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    margin: 0;
    }
    input[type="text"] {
    width: 80%;
    padding: 10px;
    margin-bottom: 10px;
    border: none;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }
    button {
    padding: 10px 20px;
    margin: 5px;
    border: none;
    border-radius: 5px;
    background-color: #007BFF;
    color: white;
    cursor: pointer;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }
    button:hover {
    background-color: #0056b3;
    }
    #closeBtn {
    background-color: #6c757d;
    }
    #closeBtn:hover {
    background-color: #5a6268;
    }
    </style>
    </head>
    <body>
    <input type="text" id="inputText" placeholder="テキストを入力してください">
    <div>
    <button id="executeBtn">実行</button>
    <button id="closeBtn">閉じる</button>
    </div>
    
    
        <script>
          document.getElementById("executeBtn").addEventListener("click", function() {
            var inputText = document.getElementById("inputText").value;
            var promptText = \`\${inputText}について教えてください。\`;
            
            var textarea = window.opener.document.getElementById("prompt-textarea");
            textarea.value = promptText;
            textarea.dispatchEvent(new Event('input', { bubbles: true }));
            
            setTimeout(function() {
              var button = window.opener.document.querySelector('button[data-testid="send-button"]');
              button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
              
              window.close();
            }, 300);
          });
          
          document.getElementById("closeBtn").addEventListener("click", function() {
            window.close();
          });
          
          var activeWindow = window.opener;
          var activeWindowRect = activeWindow.getBoundingClientRect();
          var popupX = activeWindowRect.left + (activeWindowRect.width - 400) / 2;
          var popupY = activeWindowRect.top + (activeWindowRect.height - 200) / 2;
          window.moveTo(popupX, popupY);
        </script>
      </body>
    </html>
    `;
    
    popup.document.open();
    popup.document.write(html);
    popup.document.close();
    })();
```
（長いね。。）

<br/>

### こんな感じで動きます
<img alt="demo" width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653035/39ae7f09-2456-cac3-29c6-c48d98a5659f.gif">
（bookmarkバーにあるブックマークレットをポチる＞ポップアップが出てそこに「雷鳥」と入力＞実行＞
ChatGPTのテキストに自動で入力され、送信ボタンも自動でクリックされている）
<br/><br/>

## 便利ではあるがこれって再配布で死ぬよね？
ただこのJSが今後変わった時に配布するの正直だるすぎます
あ、このプロンプト変えたいなーとかChatGPTの仕様が変わってタグが変わったようとかあると、
「あ、この修正版のJSをまたブックマークレットの編集画面でコピペしてください :pray: 」
。。。これはしんどすぎます。


## JSの中身を外だししましょう
まぁ普通にWEBアプリ作ってれば当然っちゃ当然なんですがブックマークレットってサクッと作れちゃうものなのでそこに全部書いてただけなんで、
JSの中身を単純にどっか（S3)とかにおいて以下のようにしても同じように動くわけです

### で、ブックマークレットのJSをS3において呼び出した例です
ブックマークレット改善版
```
javascript:(function(){
  var script = document.createElement('script');
  script.src = 'https://hogefuga.s3.ap-northeast-1.amazonaws.com/chat_gpt_helper.js';
  document.body.appendChild(script);
})();
```

`chat_gpt_helper.js`の中身を例えばS3にアップしておきます
```
    var windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    var popupX = window.screenX + (windowWidth - 400) / 2;
    var popupY = window.screenY + (windowHeight - 200) / 2;
    
    var popup = window.open("", "わからない単語を入力してね", "width=400,height=200,left=" + popupX + ",top=" + popupY);
    
    var html = `
    <html>
    <head>
    <title>Popup</title>
    <style>
    body {
    background-color: rgba(255, 255, 255, 0.5);
    font-family: Arial, sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    margin: 0;
    }
    input[type="text"] {
    width: 80%;
    padding: 10px;
    margin-bottom: 10px;
    border: none;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }
    button {
    padding: 10px 20px;
    margin: 5px;
    border: none;
    border-radius: 5px;
    background-color: #007BFF;
    color: white;
    cursor: pointer;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }
    button:hover {
    background-color: #0056b3;
    }
    #closeBtn {
    background-color: #6c757d;
    }
    #closeBtn:hover {
    background-color: #5a6268;
    }
    </style>
    </head>
    <body>
    <input type="text" id="inputText" placeholder="テキストを入力してください">
    <div>
    <button id="executeBtn">実行</button>
    <button id="closeBtn">閉じる</button>
    </div>
    
    
        <script>
          document.getElementById("executeBtn").addEventListener("click", function() {
            var inputText = document.getElementById("inputText").value;
            var promptText = \`\${inputText}について教えてください。\`;
            
            var textarea = window.opener.document.getElementById("prompt-textarea");
            textarea.value = promptText;
            textarea.dispatchEvent(new Event('input', { bubbles: true }));
            
            setTimeout(function() {
              var button = window.opener.document.querySelector('button[data-testid="send-button"]');
              button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
              
              window.close();
            }, 300);
          });
          
          document.getElementById("closeBtn").addEventListener("click", function() {
            window.close();
          });
          
          var activeWindow = window.opener;
          var activeWindowRect = activeWindow.getBoundingClientRect();
          var popupX = activeWindowRect.left + (activeWindowRect.width - 400) / 2;
          var popupY = activeWindowRect.top + (activeWindowRect.height - 200) / 2;
          window.moveTo(popupX, popupY);
        </script>
      </body>
    </html>
    `;
    
    popup.document.open();
    popup.document.write(html);
    popup.document.close();
```
<br>
あとは先ほどと同じように動かせます。

### 変更あった時はサクッとGithubにmerge
で、変更あったら呼び出しているJSを変更したら使っているユーザーに変更が反映されるわけです :thumbsup: 
例えばGithubのリリース用ブランチにmergeしたら
S3にJSをアップするみたいなCI/CD組んでおけば、
ブックマークレットの中身が更新配布がよりやりやすいですね

### まとめ
ちょっとこのWEBページでスクレイピングしたりRPA的に自動実行したい！！
という要望が社内で上がった時などに、
サクッとブックマークレット化して対応するのも良いかもですね。
ただそのままブックマークレットに全部JSを突っ込むのではなく、
外だし管理して再配布も楽になるようにするといいですね。