【Electron+GCP+Slack App】Slackのコメントをニコニコ動画風にプレゼンで流す方法

この記事は [DeNA Advent Calendar 2019](https://qiita.com/advent-calendar/2019/dena) の12/17(火)の記事です。

こんにちは、ゲーム事業部エンジニアの今江です。普段はゲームのサーバサイドの開発をやっています。

この記事では、社内の勉強会でニコニコ風コメントを作ってみたら良さそうだった話と作り方について紹介します。
最近は、Google Cloud Platform（GCP）を使って開発をしているので、この記事でもクラウドサービスはGCPを使っています。

## 事前準備
- GCP: https://cloud.google.com/
- Google Cloud SDK: https://cloud.google.com/sdk/docs/
- node.js: v10.15.1
- electron: v1.4.13

## 背景
DeNAでは社内の勉強会が結構活発に行われていて、チーム内、ゲーム事業部内、全社規模、特定の技術に関してと粒度さまざまな勉強会が行われています。
発表がメインの勉強会の場合、共通して行われている文化として、発表中にSlackでコメントが書き込まれて、聴講者による反応が飛び交っている光景(いわゆる実況)が繰り広げられていることをよく見かけます。

なので、大抵の場合は発表内容と、Slackの反応が合わさって盛り上がりが醸成されている感じになっています。
が、発表者のほうは発表に集中しているとSlackのコメントを見ている余裕もないので、発表が終わってからSlackのコメントを見て盛り上がりの理由を知ったりすることになるので、発表中に画面にコメントが流れてくる、ニコニコ動画風ができると良いのでは？となったのが始まりです。

## モチベーション

- プレゼンに双方向性を出したい
 - 聞くだけじゃなくて、聴講者も参加してる感があるほうが楽しい気がした
- 場の盛り上がりの最大化
 - 発表が終わった後にSlack上のコメント見ると、けっこう盛り上がってたりする
 - 発表者が、場の盛り上がりを察知できて且つ、反応が得られるので良さそう
- 作って見たかった
 - 思いついたらやりたくなった、あとちょうど社内勉強会の時期がやってきてた
 - ネットで検索してみると同様のことにチャレンジしている人がたくさんいたので参考にしてやってみた

## 技術的な仕組み
上記を実現するために、Electron + Node.js(WebSocket)&GAE + Slack App を使用して仕組みを作りました。 ソースコードは [kengo92i/niconico-slack](https://github.com/kengo92i/niconico-slack) です。

![image001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/5ab7a7fb-1aa3-205a-86bd-c7606d2dc992.png)

- Slack: Slack Appでコメント受信用のBot Userを作る
- サーバ: Google App Engine 上で WebSocketサーバを立てる
- クライアント: Electronのデスクトップアプリでコメント表示

まず、サーバのところから説明していきます。

### GAE: コメント送受信用のWebSocketサーバ(Node.js)
コメントの送受信を行うために、WebSocketのサーバを立てます。
ここで立てたWebSocketサーバを、Slack AppのEvent Subscriptionsに設定します。

```javascript:app.js
:
// 接続しているクライアントに対してコメントを送信
io.on('connection', function (socket) {
    socket.on('message', function (msg) {
        io.emit('message', msg);
    });
});

// Slack Appからコメントを受け取るAPI
app.post('/slack', function (req, res) {
    const { type, event } = req.body;

    if (type === 'challenge') {
        // 認証用リクエストなので、特に何もしない
    } else if (type === 'event_callback') {
        console.log(event.text);
        io.emit('message', event.text);
    }

    res.status(200).json(req.body);
});
:
```

参考: https://qiita.com/tonio0720/items/bd6362d27084738bbfe2#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BCec2

app.jsがある階層に、app.yaml ファイルを作成します。

```yaml:app.yaml
runtime: nodejs10
service: niconico-slack
```

準備ができたら、以下のコマンドでデプロイします。

```bash
$ gcloud app deploy
:
target url:      [https://niconico-slack-dot-xxxxx-xxxxx.appspot.com]
(xxxxx-xxxxxのところはproject_nameによって変わる)
```
※ GAEの利用が初回の場合はdefaultサービスがないとエラーになるので、serviceをdefaultに変更してください

ここで表示された target url をSlack Appの方で指定します。


### Slack App: Slackのコメント受信用Botを作成
以下の手順で、自分が所属しているWorkspaceにSlack Appを追加します。

1. https://api.slack.com/apps からSlack Appを作成する
 1. [Create New App] > App Name と Workspace を入力してCreate Appする
2. Bot Users
 1. Bot Userを作成する
 2. e.g. Display name:「niconico-slack」
3. Event Subscriptions
  1. Request URL: 「https://niconico-slack-dot-xxxxx-xxxxx.appspot.com/slack」を設定 ※GAE上のインスタンスのURLで変わる
  2. Subscribe to Bot Events: 「app_mention」と「message.im」を登録
4. Install App

DMでの動作確認用で設定しているので、message.imはなくても大丈夫です。

![slack-app-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/017e5613-5a72-4dfc-c34b-8b11e56e933f.png)

こんな感じになっていればOK

### Electron: 画面にコメントを流す方法
最後にSlack側から渡ってきたコメントを画面に表示する方法ですが、
Electronでは透明のウィンドウを持つアプリを実装することができるので、これを利用します。


```javascript
// Create the browser window.
mainWindow = new BrowserWindow({
    left: 0,
    top: 0,
    width: size.width,
    height: size.height,
    frame: false,
    show: true,
    transparent: true,  // 背景色だけ透明に
    resizable: false,
    hasShadow: false,
    alwaysOnTop: true  // 常に最前面に表示
});
```

また、このままだとマウスイベントを最前面に設置しているウィンドウが奪ってしまうので、
以下の設定でマウスイベントを透過するようにします。

```javascript
// マウスイベントを無視する
mainWindow.setIgnoreMouseEvents(true);
```
実際に透明のウィンドウが最前面に張り付いているのは `frame: true` にすると、左上のボタンだけ表示されるのでイメージしやすくなると思います。

![client-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/a4437d90-17e4-8e67-1529-7441eefbf703.png)

コメント描画のロジックは [nicoJS](https://github.com/yui540/nicoJS)というライブラリを作られている方がいたので、こちらのコードを参考に実装しました。
https://qiita.com/yui540/items/394450dcb0da8d3847b6

![client-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/1d502154-62a4-4cc9-7bcd-482bb283e48f.png)

## 実際に使う時
keynoteなどでプレゼンテーション再生をすると、画面全体が切り替わってしまうので最前面のウィンドウを維持することができない問題がありました。

最終的に行き着いた方法はGoogleスライドorPDFでのプレビューの再生なら、透明ウィンドウが最前面を維持しつつプレゼン再生ができました。

1. 作成したスライドをGoogleスライドに変換する
 1. Googleスライド: そのままでOK
 1. Keynote: Googleスライド形式に変換 or イメージ出力してGoogleスライドにコピペ or PDF
2. Googleスライドで再生する
 1. [プレゼンテーションを開始 ▽] > [プレゼンター表示]
 1. プレゼンテーション再生中に画面の最前面が奪われていないのを確認する
3. あとはGoogleスライドを使ってプレゼンする
 1. PCのキーボードを使って普通にスライドを動かせる

![a.001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/f0cb60a5-4de2-57c3-7604-ab8eca848357.png)

詳細に説明すると、こんな感じです。

## Slackがどんな感じになるのか？
コメント内容に関してはモザイクをかけてますが、チャンネルの雰囲気は掴めるかなと思います。

チャンネル内のコメント数が増えるので、すごい賑やかな感じになります。
thread内に書き込んでもらう運用が、今の所ベストプラクティス。赤枠のコメントがthreadを指しています。

![result-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/5ced4ca9-b9a9-21e7-3d2d-42a52941beeb.png)

Slack Botをメンションしてるコメントは表示されて、表示したくないコメントは普通に書き込まれる感じになります(ただBotメンションするの手間なので、workspaceの書き込み全てを表示も検討しても良さそうです)。

![result-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318777/e23d0903-e394-d656-3700-295cba37c553.png)
始まりと終わりにパチパチ(8888..)してくれたりして、画面が盛り上がる。

## 運用してみた感想

ざっと、Slackのコメントを画面に表示するための仕組みはできましたが、実際に運用してみた感想を以下に記載します。

### 悪かったこと

- niconico-slackのセットアップが結構手間になる
 - 個人で使う分にはトラブルは起きづらいが、初見の人に使ってもらうのは厳しい
 - 運営PCに資料を集約する方式ではなく、登壇者が入れ替わる方式だとうまく回らない(ミラーリング可否、メインディスプレイが逆になってる、etc.)
- 複数勉強会での対応
 - 同時に開催されることは少ないが、念のため勉強会ごとにインスタンスを立てている
 - 微々たるものだが費用が増えるし、管理コストも増える(が、現時点では総数が少ないので一番楽ではある)
- 最低限の機能しか作ってない
 - コメントのベストな表示加減、絵文字は表示できなかったり、文字に色つけたりとかは対応できてない
 - なにより、普通にプレゼンを再生しても使えるようになるのがベスト(調べたがElectronだとやり方がなさそう)。

### よかったこと

- 勉強会の空気感が変わった(双方向性が生まれた)
 - やっぱりユーザーの反応が見えるようになると、勉強会としての一体感があがる
 - 発表中にユーザーが気になっているところに補足ができたりして、聞くだけではなく双方向性が上がった
- 明らかに勉強会が盛り上がる
 - 勉強会の実況のポスト数が大幅に増えました。投稿の心理障壁が減って、議論が活発になったように思います。
 - 導入前後で 先月(100~200コメント)/今月(600~700コメント) と3倍程度のコメント数の差がありました。  
- サーバ側の管理コストはほぼ0だった
 - 3-4ヶ月くらい運用しているが、サーバ側に関してトラブル対応をした覚えがない
 - 突然、サーバが落ちて復旧が必要になったとかもなくサーバの管理から解放されている感がある


## まとめ

- Slackのコメントをプレゼン中にニコニコ動画風で流す方法を紹介しました。
- Electronで透明のウィンドウを作成し、 WebSocket&GAE と Slack App を利用してコメント描画を行いました。
- 実際に使ってみると問題点はいくつかありましたが、ニコニコ動画風のコメントで勉強会がより盛り上がるようになりました。

ひとまずサクッと作ったものなので、このコンセプトで良い感じに拡張していけると良さそうかなと思いました。
「人月の神話」でも、[最初に作ったシステムは微妙なので、2個目を作り直そう](https://ja.wikipedia.org/wiki/%E4%BA%BA%E6%9C%88%E3%81%AE%E7%A5%9E%E8%A9%B1#%E3%83%91%E3%82%A4%E3%83%AD%E3%83%83%E3%83%88%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%EF%BC%88%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%EF%BC%89) という話があるので、使いやすさ/運用しやすさの観点が次の大きな課題かと思いました。