詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

テキストを画面に流していくアプリをElectronで作った

この記事は、はてなエンジニア Advent Calendar 2019の12日目の記事です。


任意のテキストを画面に流していきたいことってありませんか? ぼくはあります。定期的にエンジニアみんなの前でスライドを映しつつ話す機会があって、そんなとき Slack で実況的に反応がなされることがあるんだけど、Slack 映しっぱなしにするわけにもいかず、話し終わってあとからコメントに気づく……ってこともまあまあある。そんなとき、画面のスライドに重ねてコメントが流れてくれると自分も聞き手も共有できてうれしい。わけです。

それを達成するための1ステップとして、任意のテキストを画面に次々流してくれるアプリをElectronで作りました。

GitHub - motemen/TextCast

じつは過去のこのエントリたちも、「Slack の発言をリアルタイムにデスクトップに流したい」という欲望からうまれたものです:

使い方

ビルドしたものの配布までは作り込めてないので、git cloneyarn install した上で yarn dev で起動してみてください。 http://localhost:4140 で HTTP サーバが立つので、ここに POST リクエストを送るとテキストを描画できます。

  • curl localhost:4140/post -d text={text} で都度
  • curl localhost:4140/stream -T - でストリーミング

分かりづらいかもしれないけどスクリーンショット。デスクトップの右下からにゅっと出てきて、時間が経つと消える感じ。

f:id:motemen:20191212183358g:plain f:id:motemen:20191212145632g:plain

ニコニコ風にしてみたい気持ちもあるけどそれはみなさんの叡智に期待したい!!!

Slackとの連携

そしてSlackと連携するには! 以前の2つのエントリを組み合わせて……

こうするのじゃ:

slack-stream-json -f -i | \
    jq --raw-output --unbuffered 'select(.type == "message" and (.subtype | not) and .channel.name == "engineer") | "\(.user.name): \(.text)"' | \
    curl -s -T - localhost:4140/stream

#engineer チャンネルの内容がどんどん流れてくる! うおおこれややりたかったことは! やったね。コンテンツ的にスクショはありません。

Electron まわりの話

ウィンドウを透明化する

ウィンドウを透明化(マウスイベントを奪わないで)できんのかな~ってのが最大の障壁だったけど、以下の記事の通りにやってたらできてしまった。

qiita.com

electron-webpack

electron-webpack

Electron よくわからない状態からはじめたので、シュッとはじめたくてこういうやつを使った。TypeScript 化とかもドキュメントにある通りやればすぐできる。 バージョン番号を package.json から取りたくて少しだけ webpack 設定をいじる必要があったのだが、それもちょちょっと書けば実現できたのでたいへん楽だった。


そんなわけで、最近作ったアプリの紹介でした。どうぞご利用ください。 はてなエンジニアアドベントカレンダー2019、明日の担当は id:takuji31 です。よろしくお願いします!!!

漢字スライダー

会社では基本的にすべての場でインターネットネームによる運用なんだけど、たまに戸籍ネームが必要になることがあって、そういう時、とても威圧感がある。威圧感はどこからやってくるの? それは漢字の画数の多さだと思う。その着眼のもと、漢字の画数をコントロールできる「漢字スライダー」を作りました。

motemen.github.io

スライダーで画数をコントロールできます。文字列は好きに編集できるよ。

f:id:motemen:20191205202901g:plain

威圧感を自由に上げ下げできて、さまざまなシーンで活躍しそうですね!

unihan-etl で日本語の漢字の一覧を取得する

漢字とその画数の一覧は、ユニコードコンソーシアムによる Unihan というやつから取得するのがよさそうだった。単純に漢字(/\p{Han}/)を抽出すると見慣れない中国語の漢字まで含まれてしまうので、日本語の漢字のみに絞りたい。今回は kJis0 というフィールドを利用することで、JIS X 0208 の文字だけを対象にできそうなことがわかった。

Unihan のデータ抽出/加工には、unihan-etl という Python 製のツールが便利だった。以下のコマンドで、Unihan からデータをダウンロードして、それぞれの漢字の画数と、JIS X 0208 におけるコードを CSV で取得できる。

% unihan-etl -f kTotalStrokes kJis0 -d /dev/stdout

ちょっと余談だけど Python の argparse ではひとつのフラグに複数の引数を与えるとき上記のように書く(-f kTotalStrokes -f kJis0 とか -f -f kTotalStrokes,kJis0 とかではなく)ようで、ちょっとハマった。

GitHub ActionsでRustプロジェクトをクロスビルドしてリリースする

前のエントリではバイナリを GitHub のリリースとして配布しているけれど、これは GitHub Actions でビルドとリリースのアップロードを行っている。いろいろ試行錯誤した結果なのでメモを残しておく。

ビルドに成功してからリリースを作成する、とした違いはあるけど、基本的に以下のエントリを参考にしている。

【GitHub Actions】Go言語の自動テストからリリースまでを作ってみた - Qiita

できあがりの workflow はこういう感じ。

https://github.com/motemen/slack-stream-json/blob/97d3745dcc8931a1d75217573d5ca60705be632f/.github/workflows/release.yml

一度 CI をセットアップしてしまうと、そのあと触ることは全然ないので、時間をあけて完全に忘れてしまった状態でも、問題なくメンテできるようになっていてほしい。そういう観点では、公式っぽいアクションだけで構成できたのはよかった。

ジョブはこういう流れになっている。

  • Build (matrix)
  • Create-release
  • Upload-release (matrix)

Build

  build:
    strategy:
      matrix:
        target:
          - x86_64-unknown-linux-gnu
          - x86_64-pc-windows-gnu
          - x86_64-apple-darwin
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
          - target: x86_64-pc-windows-gnu
            os: ubuntu-latest
          - target: x86_64-apple-darwin
            os: macos-latest

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v1

      # https://github.com/actions/cache/blob/master/examples.md#rust---cargo
      - name: Cache cargo registry
        uses: actions/cache@v1
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
      - name: Cache cargo index
        uses: actions/cache@v1
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
      - name: Cache cargo build
        uses: actions/cache@v1
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      - uses: actions-rs/cargo@v1.0.1
        with:
          command: build
          args: --release --target=${{ matrix.target }}
          use-cross: true

      - run: |
          zip --junk-paths slack-stream-json-${{ matrix.target }} target/${{ matrix.target }}/release/slack-stream-json{,.exe}
      - uses: actions/upload-artifact@v1
        with:
          name: build-${{ matrix.target }}
          path: slack-stream-json-${{ matrix.target }}.zip

Linux、macOS、Windows それぞれでビルドしたいので、matrix を使う。なんか cross というのを使えば(actions-rs/cargo のオプションにある)1ホスト上で全部クロスビルドできそうだったんだけど、x86_64-apple-darwin 向けのビルドで "error: failed to run custom build command for backtrace-sys v0.1.32" などと言われてしまい簡単にはできなそうだった。このへんの仕組みは何も知らないので、OSごと切り替えられるのであればそちらのほうがシンプルだろうと、そのようにした。

x86_64-pc-windows-gnu 向けは Linux でビルドできた。ここが Windows でしかビルドできなかったら、zip コマンドを使い変えないといけなくて大変だったのではないだろうか……。

actions/cache も Rust/Cargo に対応している

ビルドしたバイナリは、あとでリリースに上げたいので actions/upload-artifact しておく。

Create-release

  create-release:
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - id: create-release
        uses: actions/create-release@v1.0.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false
      - run: |
          echo '${{ steps.create-release.outputs.upload_url }}' > release_upload_url.txt
      - uses: actions/upload-artifact@v1
        with:
          name: create-release
          path: release_upload_url.txt

actions/create-release でタグに対応するリリースを作成する。リリースを作るのは一度しかできない(エラーになる。ここでちょっとハマった)ので、matrix から外して、build とは別のジョブとして立てる。

リリースのアップロード用 URL を、これも後で使うので upload-artifact しておく。

Upload-release

  upload-release:
    strategy:
      matrix:
        target:
          - x86_64-unknown-linux-gnu
          - x86_64-pc-windows-gnu
          - x86_64-apple-darwin
    needs: [create-release]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v1
        with:
          name: create-release
      - id: upload-url
        run: |
          echo "::set-output name=url::$(cat create-release/release_upload_url.txt)"
      - uses: actions/download-artifact@v1
        with:
          name: build-${{ matrix.target }}
      - uses: actions/upload-release-asset@v1.0.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.upload-url.outputs.url }}
          asset_path: ./build-${{ matrix.target }}/slack-stream-json-${{ matrix.target }}.zip
          asset_name: slack-stream-json-${{ matrix.target }}.zip
          asset_content_type: application/zip

これが最後のフェーズ。あまりそうする必要もないけど、build と合わせるため matrix を使う。actions/download-artifact で前の2つのジョブの結果を受け取って、actions/upload-release-asset する。これで各 OS 分のファイルをリリースに添付できる。

標準出力に ::set-output と送ることで、ステップの output を設定できるらしい。おおう。ステップ名には - も使えるので、今回はケバブケースを採用してみた。

はてなで一緒に働きませんか?