heroku + electron でニコニコメソッドを作ってみました。

# はじめに
* きっかけはプレゼンをインタラクティブにしたくて、聴講者の声がリアルタイムにニコニコ動画のようにプレゼン画面に投影したら面白いと思ったことです。
* ネットを探してみたら同じようなことを考えている人がいたため、下記サイトを参考にしながらツールを作ってみました。
* Javascriptを扱うのは初めてだったのですが何とか形になりました。

## 参考にしたサイトは
* [ニコニコメソッドプレゼンを全社会議に取り入れてみたら会議が面白くなった](http://tech.uzabase.com/entry/2015/06/01/143202)
* [社内プレゼン大会にニコニコメソッドを取り入れてみた話](http://atoms.loftwork.jp/20170925_nicomethod/)

## めんどくさい準備なしに動作を試してみる
*　https://github.com/UC-SADA/package_file ←githubからzipをダウンロード
*　解凍後「nicoG_1_3-win32-x64」⇒「nicoG_1_3.exe」ファイルを実行。
*　https://nico-chat.herokuapp.com/controller にアクセスしてスタンプやコメントを入力して画面に表示されればOK
*　終了するときは実行ファイルを閉じつつ、タスクマネージャーで「nicoG_1_3.exe」を終了させてください。

## 準備と使い方
* PC環境はwindows7 64bitです。
* ここのページではどんな動作かのお試し用です。
* 後々、ソースコードの中身の説明もアップしていきます。

### node.jsの導入
* [node.js](https://nodejs.org/ja/)のサイトからPCにインストール、コマンドプロンプト画面で下記を実行しそれぞれバージョンが表示されればOK。

```
$ npm-v
$ node -v
```

### electronの導入
下記コマンドでグローバルにelectronをインストールし、バージョン表示されればOK

```
$ npm -g install electron
$ electron -v
```

### ソースのダウンロードと実行
GitHubに[niconico-Display](https://github.com/UC-SADA/niconico-Display)としてソースコードをアップしました。ここからダウンロードしてコマンドプロンプト画面で格納したフォルダまで移動。その後下記コマンドを実行

```
$ npm init
$ electron .
```

https://nico-chat.herokuapp.com/controller にアクセスしてスタンプやコメントを入力してプレゼン画面に表示されればOK。

# 実装内容の概要
* herokuサーバー上にnode.jsで動く [操作画面](https://nico-chat.herokuapp.com/controller/) と [表示画面](https://nico-chat.herokuapp.com/display)を用意。
* 操作画面でスタンプを押したり、コメントを入力すると表示画面にsocket通信で表示される。
* electron にて背景が透明なwindowを作成し、上記「表示画面」のURLを開く。

## herokuサーバー上の実装内容
* https://github.com/UC-SADA/nico-chat-heroku　にソースコードをアップしています。
* herokuってなに？　どうやってアプリを作成する？　等についてはここでの解説を避けるので https://qiita.com/tags/heroku 等を参考にしてください。

##メインとなるindex.jsの動作は

* socke.ioに必要な基本的なモジュールの読み込みとサーバーの確立、
* 操作画面、表示画面をサーバー上に設置
* socket通信のアクションの作成

### サーバーの確立

heroku上では動的PORTになるため process.env.PORT を指定。

```index.js
"use strict";
const path = require('path');
const express = require('express');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const extend = require('util')._extend

http.listen(process.env.PORT || 2525, function(){
  console.log("PORT : " + process.env.PORT || 2525);
});
```

### 操作画面、表示画面をサーバー上に設置

```index.js
//操作画面
app.use("/controller",express.static(path.join(__dirname, 'public')))

//表示画面
app.get("/display", function(req, res){
  res.sendFile(__dirname + '/index_nico-Display.html');
});
```

### socket通信のアクションを作成

```index.js
//スタンプ動作
app.get('/comment', function (req, res) {
  const msg = extend({}, req.query)
  io.emit('comment', msg);
  res.end()
})
//コメント動作
app.get('/like', function (req, res) {
  const msg = extend({}, req.query);
  io.emit('like', msg);
  res.end()
})
```
### 入力画面のhtmlファイルの作成
ここはほとんど「NicoNico SPEENYA」さんのぱくりですが
https://github.com/UC-SADA/nico-chat-heroku/tree/master/public
フォルダの中身を用意。

### 表示画面のhtmlファイルの作成
注意点）入力画面と表示画面で読み込んでいるsocket.ioのバージョンは揃えましょう。Ver1系とVer2系の間では互換性がないです。

```index_nico-Display.html
<!DOCTYPE html>
<html>
    <head>
      <script type="text/javascript" src="controller/JS/jquery_1_11_1.js"></script>
      <script type="text/javascript" src="controller/JS/socketio_2_2_0.js"></script>
      <script type="text/javascript" src="controller/JS/niconico.js"></script>
    </head>
  <body>
  <script>
var socket = io();
socket.on('comment', function(msg){
    handleComment (msg) ;
  });
socket.on('like', function(msg){
    handleLike (msg) ;
  });
  </script>
</body>
</html>
```
## electronでの実装内容
* https://github.com/UC-SADA/niconico-Display　にソースコードをアップしています。
* electronってなに？　等についてはここでの解説を避けるので https://qiita.com/tags/electron 等を参考にしてください。

### メインとなるindex.jsの動作は
* 透明ディスプレーの作成
* プロキシ認証画面の作成

### 透明ディスプレーの作成

```index.js
"use strict";
const electron = require('electron');
const elc_app = electron.app;
const elc_Window = electron.BrowserWindow;
var mainWindow = null;

elc_app.on('ready', function () {
    var size = electron.screen.getPrimaryDisplay().size;
    mainWindow = new elc_Window({
        left: 0,
        top: 0,
        width: size.width,
        height: size.height,
        frame: false,
        show: true,
        transparent: true,
        resizable: true,
        webPreferences: {nodeIntegration: false}
    });
    mainWindow.setIgnoreMouseEvents(true);
    mainWindow.maximize();
    mainWindow.setAlwaysOnTop(true);
    mainWindow.loadURL("https://nico-chat.herokuapp.com/display");
//    mainWindow.openDevTools();
    mainWindow.on('closed', function () {
    mainWindow = null;
      });
});
```

### プロキシ認証画面の作成
外部サイトにアクセスする際にプロキシ認証等が必要なこともあるため用意

```index.js
const ipcMain = electron.ipcMain;
var mainWindow = null;
let loginWindow;
let isLoginWindow = false;

// ログイン要求時に発火するイベント
elc_app.on("login", (event, webContents, request, authInfo, callback)=>{
    // プロキシサーバーからの要求だったら続行
    if(authInfo.isProxy){
        // 重複発火対策
        if(!isLoginWindow){
            isLoginWindow = true;
        }
        else{
            return;
        }
        // 認証情報が送信されるまで待機
        event.preventDefault();
        loginWindow = new electron.BrowserWindow({
            width: 300,
            height: 180,
            resizable: false
        });
        loginWindow.setAlwaysOnTop(true);
        // 入力ウィンドウをロード
        loginWindow.setMenu(null);
        loginWindow.loadURL('file://' + __dirname + '/login.html');
        // IPCチャネル"proxy-auth"で受信待機
        ipcMain.on("proxy-auth", (event, username, password)=>{
            // 受信した認証情報をプロキシサーバーへ転送
            callback(username, password);
            isLoginWindow = false;
        });
    }
});
```

```login.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Proxy Login</title>
</head>

<body>
    <p>username<input type="text" id="username"></p>
    <p>password<input type="password" id="password"></p>
    <button onclick="sendIpc();">ログイン</button>
</body>

<script>
  const electron = require("electron");
  // レンダラープロセス側のIPCメンバ
  const ipcRenderer = electron.ipcRenderer;
  // レンダラープロセス側からメインプロセス側を操作するメンバ
  const remote = electron.remote;
  // ログインボタン
  function sendIpc(){
    // 入力ウィンドウをカレントにする
    const currentWindow = remote.getCurrentWindow();
    // IPCチャネル"proxy-auth"でユーザー名/パスワードを送信
    ipcRenderer.send(
        "proxy-auth",
        document.getElementById("username").value,
        document.getElementById("password").value
    );
    // 入力ウィンドウを自動的に閉じる
    currentWindow.close();
}
</script>
</html>
```

#最後に
以上がこれまでに実装した内容になります。
現状、操作と表示にログイン等を設けていないオープンな状態のため同時に使うと混線します。
それはそれで面白そうなので残しつつ、ログイン画面を設けてプライベートで使える機能も付加していきたいと思っています。

また、プライベート環境で使うように https://github.com/UC-SADA/niconico-Display2 も作ってみました。
こちらの実装内容は後日。