Slack + Electron + Socket.ioを使ってニコニコ動画風にメッセージを表示する

nicotv.gif

はじめに

Slack APIを使ってみたく、ただメッセージを表示するだけではつまらないので、
ニコニコ動画風にメッセージを表示するアプリケーションを作成してみました。

構成

  • Slack:Slack Events APIを使って、メッセージ受信時にサーバーに通知
  • サーバー(EC2):Events APIを受けっとって、Electronアプリにメッセージを送信
  • Electron:Socket.ioでサーバーからメッセージを受け取りニコニコ動画風に描画 image.png

必要なもの

  • PC
  • サーバー(EC2)
  • Slackのアカウント

参考にしたサイト

作り方

サーバー(EC2)

Node.jsのExpressを使ってWebサーバーを立ち上げる。
Electronアプリとの接続用にSocket.io通信も設定する。
ここではポート3000番に起動しているので、必要に応じて変更する。

app.js
const express = require('express');
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const PORT = process.env.PORT || 3000;

const bodyParser = require('body-parser');

app.use(bodyParser.json());

io.on('connection', function (socket) {
    socket.on('message', function (msg) {
        io.emit('message', msg);
    });
});

app.post('/slack', function (req, res) {

    const { type, event } = req.body;

    if (type === 'challenge') {

    } else if (type === 'event_callback') {
        io.emit('message', event.txt);
    }

    res.status(200).json(req.body);

});

http.listen(PORT, function () {
    console.log('server listening. Port:' + PORT);
});
npm i -S http express socket.io body-parser
node app.js & #バックグラウンドで実行

Slackの設定

  • https://api.slack.com/apps
  • Create New Apps image.png
  • App Name:任意(Nicotv)
  • Development Slack Workspace:ワークスペースを指定
  • [Create App] image.png
  • 左下の[Event Subscriptions]から
  • Request URLに先ほど設定したサーバーのURLを設定 image.png
  • Bot Eventsを下記のように設定 image.png
  • 右下の[Save Changes]
  • [Install App]から手順に従いSlackにアプリをインストール

Electronアプリの作成

超シンプルなHTMLファイル

index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Nico TV</title>
    <script src="https://xxx.xxx.com/socket.io/socket.io.js"></script>
    <script>
        window.jQuery = window.$ = require('./lib/jquery-3.3.1.min.js');
    </script>
    <link href="./css/style.css" rel="stylesheet">
</head>

<body>

    <div class="msgbox"> </div>

    <script>
        require('./renderer.js')
    </script>

</body>

</html>

Socket.ioに接続し、メッセージを受け取ったらニコニコ風に描画

renderer.js
const colors = [
    'lime',
    'aqua',
    'yellow',
    'red',
    'fuchsia'
];

const colorsLen = colors.length;

const layerCnt = 6;

const winWidth = window.innerWidth;
const socketio = io('https://xxx.xxx.com');

const layerIds = [];

const $msgbox = $('.msgbox');

for (let i = 0; i < layerCnt; i++) {
    $('<div>', { class: 'layer' }).appendTo($msgbox);
    layerIds.push(0);
}

const $layers = $('.layer');

let msgId_ = 1;

socketio.on('message', function (msg) {

    let msgId = msgId_++;

    let minId = msgId;
    for (let layerId of layerIds) {
        if (layerId < minId) {
            minId = layerId;
        }
    }

    let index = layerIds.findIndex((id) => id === minId);

    layerIds[index] = msgId;

    const $layer = $layers.eq(index);
    const $msg = $('<div>', { class: 'msg', text: msg })
        .css('color', colors[Math.floor(Math.random() * colorsLen)])
        .appendTo($layer);

    let right = -$msg.width();

    let intervalId = setInterval(function () {

        right += 1;
        $msg.show().css('right', right);

        if (right > winWidth) {

            $msg.remove();

            if (layerIds[index] === msgId) {
                layerIds[index] = 0;
            }

            clearInterval(intervalId);

        }

    });

});
style.css
html {
    height: 100%;
    width: 100%;
}

body {
    margin: 0;
    height: 100%;
    width: 100%;
    background-color: rgba(0, 0, 0, 0);
    overflow: hidden;
}

.msgbox {
    font-size: 128px;
}

.layer {
    position: relative;
    width: 100%;
    height: 128px;
    margin: 8px 0;
}

.msg {
    position: absolute;
    display: none;
    right: 0;
    white-space: nowrap;
    font-weight: 700;
}

Electronアプリを最大化、フレームなし、透明化、リサイズ不可、常に最前面で起動する。

main.js
// Modules to control application life and create native browser window
const electron = require('electron');
const { app, BrowserWindow } = electron;

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

function createWindow() {

    // const screen = require('screen');
    const { screen } = electron;
    let size = screen.getPrimaryDisplay().size;

    // 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,
        alwaysOnTop: true
    });

    mainWindow.setIgnoreMouseEvents(true);
    mainWindow.maximize();

    // and load the index.html of the app.
    mainWindow.loadFile('index.html');

    // Open the DevTools.
    // mainWindow.webContents.openDevTools()

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null;
    });

}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', function () {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow();
    }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

electron-packagerを使ってExeファイルを作成する。

electron-packager . nicotv --platform=win32 --arch=x64 --asar

使い方

  • メッセージを表示したいPCでElectronアプリを起動
  • SlackでNicotv宛てにダイレクトメッセージを送ると、Electronアプリ上でメッセージが表示される

さいごに

Slack連携のアプリケーションが意外に簡単に作れました。
SlackのBot Eventsを変更することで、チャネルの投稿をトリガーにメッセージを表示するなどということも可能です。
Electronアプリは最前面、背景透明で起動しているので、他のアプリケーションと合わせて実行することが可能です。
弊社ではこのアプリを全員が見えるところに配置した巨大なモニタに映し出して使用しています。

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした