Electron:ver12のcontextBridgeとipcRenderer.invoke

　本記事では、ElectronのVer.12以降でセキュアにモジュールを使用する際に躓きがちな「ContextBridge.ipcMainWorld」と「ipcRenderer.invoke」について解説します！
　さらに、具体例にSlackAPIによるメッセージの送信を使用することで、一緒に使い方を覚えてしまおうという魂胆です！

(注意)
　非プログラマーの独学&初投稿になりますのでどうぞお手柔らかに。
　ここ直すといいよ！というアドバイスいただけたら幸いです！

##目次
1. 作成する機能
2. セキュアにモジュールを使用するには
3. 実際にやってみる
4. SlackAPIでメッセージ送信
5. まとめ

# 1.作成する機能
　「ボタンをクリックしたら、Slackにあらかじめ決められたメッセージ（かめはめ波）を飛ばす」だけの非常にシンプルな機能を作成します。
　という建前のもと、デバックの取り方わからなかったので、代わりにAPIでメッセージを投げます（笑）

electronについてはこちら

https://qiita.com/tomomoss/items/2b4112d6e22065c8615b

# 2.セキュアにモジュールを使用するには
electronでは、以前からXSSの危険性が指摘されています。
バージョン更新のたびにデフォルトの設定が変更になるなど、
私を含め初心者にとってややこしい状況かと。

まずは公式ドキュメントを見てみましょう。また併せて日本語でも検索します。
それぞれ共通するキーワードをピックアップします。

* preload.js
* ContextBridge.ipcMainWorld
* ipcRenderer.invoke
* ipcMain.handle

これらを組み合わせることにより、従来より指摘されていたXSSの危険性を回避しつつ、モジュールを使用することができるっぽいですね。

全体のイメージはこんな感じでしょうか。
![無題の図形描画.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115033/b380b02d-a8a3-d939-81c8-7ba32573c115.jpeg)



# 3.実際にやってみる
まずはじめに、画面となるHTMLを準備します。

```html:test.html
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta content="width=device-width" name="viewport">
  <title>テスト</title>
</head>

<body>
  <p><input type="button" value="かめはめ波" id=Button></p>
  <script src="./renderer.js"></script>
</body>

</html>
```


　rendererプロセス側から「window.任意のAPIキー.任意のAPI名(引数)」という形でpreload.jsの処理を呼び出します。

```javascript:renderer.js
const button = document.getElementById("Button");
button.addEventListener('click',async () => {
    const result = await window.electron.sendToMain("かめはめ波");
    console.log(result);
  }) 
```

　次にpreload.jsでは、「contextBridge.exposeInMainWorld('任意のAPIキー', {任意のAPI名：処理内容})」という形で窓口を用意します。
　この時、処理内容には「ipcRenderer.invoke（'任意の名前', 引数）」を記述し、mainプロセスの処理を呼びに行きます。

```javascript:preload.js
const {contextBridge,ipcRenderer} = require("electron");

contextBridge.exposeInMainWorld(
    'electron', {
        sendToMain: async (skill) => { 
            const result = await ipcRenderer.invoke('invoke-test', skill) ;
            return result;
        }
})
```

　最後にmainプロセスでは、preload.jsに対応する「ipcMain.handle('任意の名前',async(event,data) => { 処理内容...｝」と記述し、呼び出しの受け取りと処理の実行を行います。

```javascript:main.js
ipcMain.handle('invoke-test', async(event,data) => {
  const Result = await sendMessageSlack();
  return Result
});

const sendMessageSlack = async() => {
  //処理
}
```



# SlackAPIでメッセージ送信
SlackのApiの使用に関してはこちら

https://qiita.com/kou_pg_0131/items/56dd81f2f4716ca292ef

　上記のMainプロセスで呼び出される関数を以下のように記述します。TokenやChannel名は態々外に出さなくてもいいかもですが、後から見てわかりやすいかなと思ってconstしてます。

```javascript:main.js
const { WebClient, LogLevel } = require("@slack/web-api");
const apiToken = "Slackの設定で取得したApiToken";
const chatGroup = "任意のチャンネル名";
const client = new WebClient(apiToken, {logLevel: LogLevel.DEBUG});

const sendMessageSlack = async(text) => {
  try {
    const result = await client.chat.postMessage({
      token: apiToken,
      channel: chatGroup,
      text: text
    });
    console.log(result);
  }
  catch (error) {
    console.error(err);
  }
}
```

# まとめ
その他の参考記事

https://qiita.com/hibara/items/c59fb6924610fc22a9db

https://qiita.com/ArtcYu/items/a053f5b6295efaed74bf

所感
　初心者にとって、async/awaitが入れ子になっている上に、他では使わない概念の理解やMethodの使用が求められ、なかなか大変でした。
　すでに同様の内容を書かれている記事も見ましたが、うまく動かなかったり、
かゆいところに届かなかったりで、自分で試行錯誤して何とかたどり着きました。
　もし同じような境遇の方がこれをタタキにして、やりたかったことができると👍