ZoomのMeetingを自動生成するGASライブラリ zoom-meeting-creator を作った

みなさん、Zoom使っていますか? ZoomのMeetingを自動生成するGASライブラリを公開しましたので、 そのきっかけと使い方について紹介しようと思います。

きっかけ

社のSlackで次のqiitaの記事を知りました。

qiita.com

GASからZoomのMeetingを作れるのって、簡単なんだな〜と思いつつ、 "cronのように使いたい"というSlackのコメントがあったので、サクッと一日で作ってみました。

定期的にZoomのMeeting(IDやパスワード)を更新する会社はあるはずです。 そういう会社にとっては、このツールは、便利かもしれません。

作ったもの

github.com

これをGAS側でライブラリ追加すると使えます。 このGASでは、

  • ZoomのMeetingを作成
  • Slackとの連携

ができます。この機能を、GASの定期実行と組み合わせれば、"ZoomのMeeting作成をcronのように"使えるようになります。

アカウント画像一括更新ツールを作ったので、紹介と学びについて

GoogleGithubなど、様々なサービスのプロフィール情報(画像, etc)を一括更新するツール、puppeteer-account-manager を開発しました。 開発の目的や、開発から得た知見を紹介します。

リポジトリは、こちらです。 github.com

なんで作ったの?

GithubTwitterFacebookなど、Webサービスにはプロフィール画像を登録することができます。 私の性格上、どのサービスでも、同じ画像で登録したいと考えています。

そのため、いい感じのプロフィール写真を手に入れたら、全サービスのプロフィール画像を再登録しないと気がすまなくなり、とても面倒です。 そこで、今回、その面倒さを解決したく、このツールを作りました。

それ、Gravatarで良くない?

今回の面倒さは、GravatarというWebサービスで解決できるかもしれません。

gravatar.com

このサービスは、グローバルなプロフィール画像を提供するサービスです。 API経由で、プロフィール画像を取得できます。

しかし、次の問題があったので、却下となりました。

  • gravatarが提供するプロフィール画像サイズは80px × 80px
    • サービスによっては、小さすぎる
      • 画像サイズを拡大することができるが、画質がよくない
  • gravatarが提供するプロフィール項目が固定
    • 画像だけではなく、プロフィール項目も一括登録したかった
      • サービスによっては、プロフィール項目がマッチしない

そこで、Contentful というAPIベースのCMSを使うことにしました。

www.contentful.com

Contentfulでは、自由に項目を決めることができます。 独自に作った項目 (画像や紹介文)を、API経由で取得できるため、とても便利です。

どうやって作ったの?

愚直なやり方です。 Puppeteerと呼ばれる Chromeブラウザを自動操作できるライブラリを使いました。 Chromeブラウザから、"各サービスへログインし、写真をアップロードする"処理を自動化しただけです。

github.com

プロフィール画像を更新するAPIは、なかったの?

サービスによってはあります。例えば、Twitterには、次のようなプロフィール画像を更新するAPIがあります。

developer.twitter.com

ただ、全てのサービスには、そのようなAPIはありません。 APIを使って更新するのが正しい姿ですが、全サービスの実装方法の足並みを揃えるために、 Puppeteer で自動操作することにしました。

パスワードって大丈夫?

Puppeteerを動かすnodeアプリケーションと、Chromeブラウザを同一マシン内で動作するようにしました。 そのため、nodeアプリケーション実行中に、パスワードを傍受されることはありません。 また、パスワードの設定は環境変数から注入するようにしています。 Dockerコンテナで動作できるようにしているので、ローカルでも、コンテナサービスでも動かすことができます。

今後、パスワードの管理は、KeepassやLastpassのようなサービスと連携したいと思っています。

github.com

どのサービスが対応している?

対応サービスは次のとおりです。

詳しくは、 https://github.com/Silver-birder/puppeteer-account-manager/blob/master/src/index.ts をご確認下さい。

どんな学びがあった?

結構色々とハマりました。

極力 セレクタ指定したコードを書かない

Webサービスが返すHTMLは、いつもずっと変わらないことはありません。 あるidやclassのhtmlタグがずっと残り続けるとは限りません。

そこで、できる限り、セレクタを指定せずにブラウザ操作をするようにしました。 例えば、

  • ボタンやリンクをクリックしてページ遷移するのではなく、目的のページへ最短で直接遷移する
  • submitボタンをクリックするのではなく、エンターキーを入力する

です。こうすることで、安定した自動化ができました。

XPathが意外と使える

GoogleやMediumでは、idやclassがランダム値になっています。 そのため、単純なidやclassを指定して進めることができません。

そこで、『○○』のテキストが含まれているセレクタの指定することが、XPathでできます。 これは、助かりました。

ログインが難しいものは、無理せず諦める

Amazonのログインは、2段階認証が発生します。 テキストメッセージや、音声電話によるログインが求められ、Puppeteer単体ではどうしようもありません。

この2段階認証の機能を解除することもできますが、セキュリティ上よろしくないので、ここは無理せず諦めることにしました。

並列処理をガンガン実行する

処理速度向上のため、全サービスを Promise.allで並列処理しました。それぞれが、シークレットウィンドウで開くことで、独立して処理するようにもしました。 しかし、たまにPuppeteerが落ちてしまうことがあります。原因は、実行しているマシンのスペック(Core数)にも影響しますが、サービス側からの影響も受けたりします。 そのため、落ちても大丈夫のようにエラーハンドリングし、リトライするようにしました。

また、失敗したらどういった画面なのか知りたいので、スクリーンショットを撮るようにもしました。

Docker で実行可能に

Puppeteerに必要なモジュールをDockerに詰め込み、ログイン情報等を環境変数から外注することで、 環境非依存の実行環境ができました。そのため、Pub/SubとContainer Engine等を組み合わせれば、 ContentfulのWebfook経由で、アカウント情報を更新することができます。

終わりに

私の性格がもっと大雑把であれば、このツールを作らなかったのですが、どうしても気になって仕方がなく... (笑) 最後まで読んでいただき、ありがとうございました。

Micro Frontends を学んだすべて

Micro FrontendsというWebフロントエンドアーキテクチャがあります。 このアーキテクチャを知るために、書籍を読み、簡単なサンプルWebアプリを開発しました。 そこから学んだことをすべて議事録として残したいと思います。

モノリシックな Webアプリケーション

マイクロサービスという考え方の多くは、バックエンドへ適用されることが一般的です。 一方で、フロントエンドは依然モノリシックなままの状態です。

ECサイトのようなWebアプリケーションでは、様々な専門知識(商品、注文、検索など)を必要とし、フロントエンド開発者の守備範囲がとても広くなってしまいます。 開発者には限界があり、いつしかトラブルシューティングに追われる日々になってしまいます。

そこで、Micro Frontendsというアーキテクチャの出番です。

Micro Frontends とは

それはマイクロサービスの考え方をフロントエンドに拡張したものです。 https://micro-frontends-japanese.org/resources/monolith-frontback-microservices.png https://micro-frontends-japanese.org/resources/verticals-headline.pnghttps://micro-frontends-japanese.org

要は、バックエンドだけでなく、バックエンドからフロントエンドまでをマイクロサービス化することです。

さらに詳しく知りたい方は、次のページをご参考下さい。とてもわかりやすいです。 micro-frontends-japanese.org

また、次の書籍を読むと、 www.manning.com

Amazon does not talk a lot about its internal development structure. However, there are reports that the teams who run its e-commerce site have been working like this for a long time. ...

Micro frontends are indeed quite popular in the e-commerce sector. In 2012 the Otto Group, a Germany based mail order company and one of the world’s largest e-commerce players started to split up its monolith. ...

The Swedish furniture company IKEA and Zalando, one of Europes biggest fashion retailers, moved to this model. ...

But micro frontends are also used in other industries. Spotify organizes itself in autonomous end-to-end teams they call Squads. ...

Excerpt From: Michael Geers. “Micro Frontends in Action MEAP V03.” iBooks.

という内容があります。

IKEAやZalando といったECサイトがMicro Frontendsを採用するケースが多く、公には言っていませんが、AmazonもMicro Frontendsで取り組んでいるようです。 ECサイトだけでなく、Spotifyのようなサービスにも適用されるケースがあります。

Micro Frontends の良さ

私が思う Micro Frontends から得られる最大の恩恵は、"局所化" だと思います。

フロントエンドをサービス毎(商品、注文、検索など)に分割することで

  • サービスの専門性向上
    • ex. 対象サービスのフロントエンドだけに集中できる
  • サービスの開発速度向上
    • ex. 対象サービスのソースコードだけ読めば良い
    • ex. 対象サービスだけにライブラリアップデートすれば良い
    • ex. フレームワークの切り替えは対象サービスだけすれば良い

少し薄っぺらいかも知れませんが、↑のように実感しています。

※ Micro Frontendsは Webベースのアーキテクチャになります。

Micro Frontends の難しさ

ここは、まだちゃんと掘り下げれていませんが、次のようなものがあります。

  • 特定チームが改善しても、チーム全体が改善しない
    • ex. あるチームがwebpackのビルド時間短縮に成功しても、他のチームは影響を受けない
    • ex. 全てのチームが採用しているライブラリのセキュリティパッチは、それぞれのチームが更新しなければならない
  • チーム全体へ共有する仕組みを考える必要がある
    • ex. デザインシステム、パフォーマンス、ナレッジ
  • エッジな技術スタック採用は、チームメンバー移動を困難にする

Micro Frontends の作る上で考えること

フロントエンドをマイクロサービス化するということは、各サービスで HTML/CSS/JSを作ることになります。 それらのサービスを統合するサービスが重要になってきます。

大きく分けて2つの統合パターンがあります。

種類 解決手段 メリット デメリット
サーバーサイド統合 SSI, ESI, Tailor, Podium SEO対策上良い
・ユーザーのネットワークレイテンシーが少ない
・初回ロードパフォーマンスが優れている
・インタラクションアプローチが不得意
クライアントサイド統合 Ajax, Iframe, Web Components Web標準
・シャドウDOMによる堅牢な作り
・サポートブラウザに依存する
・クライアント側のJavaScriptが有効であること

また、これら2つの選択基準は次のようになります。

種類 選択基準
サーバーサイド統合 良好な読み込みパフォーマンスと検索エンジンのランキングがプロジェクトの優先事項であること
クライアントサイド統合 さまざまなチームのユーザーインターフェイスを1つの画面に統合する必要があるインタラクティブなアプリケーションを構築すること

今回、私はサーバーサイド統合(Podium)を選択しました。 ただ、インタラクティブなアプローチも必要だったため、Hydrationを使いました。

Hydration refers to the client-side process during which Vue takes over the static HTML sent by the server and turns it into dynamic DOM that can react to client-side data changes.

https://ssr.vuejs.org/guide/hydration.html

Hydrationは、サーバーサイドでレンダリングした静的HTMLに、クライアントサイドの動的レンダリングができるようにするようなものです。

ちなみにWeb Componentsは、次の入門書(500円)を執筆したため、ご興味がある人は見てみて下さい。 silverbirder.booth.pm

※ クライアントサイド統合(Web Components)でも良かったのですが、私都合により却下となりました。

Micro Frontends サンプルWebアプリ

apple, banana, orangeという商品を検索するだけのサンプルWebアプリを作りました。

概要図はこちらです。 https://res.cloudinary.com/silverbirder/image/upload/v1588513402/micro-frontends-sample-code/micro_frontends_sample.jpghttp://team-page.fly.dev/ 停止しました。

サンプルコードは、ここに置いています。 github.com

サービス

サービス 役割 JSフレームワーク
team-search 商品を検索するサービス Vue.js
team-product 商品を表示するサービス React.js
team-page サービスを統合するサービス フレームワーク未使用 (Node.js)

仕組み

Podium というライブラリを採用しました。

github.com

これは、フロントエンドのサービスを簡単に統合できるようなライブラリになっています。 Podium には大きく分けて3つの機能があります。

  • @podium/podlet
    • ページフラグメントサーバーを構築する
    • ex. team-search, team-product
  • @podium/layout
    • Podletを集めて、ページ全体のレイアウトを構築する
    • ex. team-page
  • @podium/browser
    • ブラウザベースの機能を提供する
    • MessageBus による Podlet同士のコミュニケーション
    • ex. team-search, team-product で publish/subscribe

@podium/podlet

Podletには、manifest.json と呼ばれる値を返却することが必須になっています。 menifest.jsonには、サービスのエンドポイントや、Asset(JSやCSS)のパスが明記されています。

team-search では

$ curl https://team-search.fly.dev/manifest.json | jq .
  {
    "name": "search",
    "version": "1.0.0",
    "content": "/",
    "fallback": "",
    "assets": {
      "js": "/search/static/fragment.js",
      "css": ""
    },
    "css": [],
    "js": [
      {
        "value": "/search/static/fragment.js",
        "async": true,
        "defer": true,
        "type": "default"
      }
    ],
    "proxy": {}
  }

というレスポンス結果になります。

@podium/layout

Layoutでは、Podletのmanifest.jsonの定義に従って fetchすることになります。

team-page では

// server.js (express)
app.get(`/`, async (req, res) => {
    const incoming = res.locals.podium;

    const [searchBox] = await Promise.all([
        podletSearch.fetch(incoming, {pathname: '/search/box', query: req.query}),
    ]);
    const [items] = await  Promise.all([
       podletProduct.fetch(incoming, {pathname: '/product/items', query: {id: searchBox.headers['x-product-items']}})
    ]);

    res.podiumSend(`
        <html>
            <head>
                <title>Shop</title>
                ${searchBox.js.map(js => js.toHTML())}
                ${items.js.map(js => js.toHTML())}
            </head>
            <body>
                <div id="app-shell">
                    ${searchBox.content}
                    ${items.content}
                </div>
            </body>
        </html>
    `);
});

のようにPodletを使って、ページ全体を構築します。このようにサーバーサイドで統合しています(SSR)。 しかし、インタラクティブなアクションも必要なため、PodletからHydrateするためのjsを読み込んでいます。

また、team-searchの検索結果(x-product-items)をteam-productへ渡しているため、商品の検索結果を含めてSSRが実現できます。

@podium/browser

サーバーサイドは、podium/podlet, podium/layoutで連携できます。 クライアントサイドは、この @podium/browserのMessageBusで連携できます。

今回のサンプルWebアプリでは、次のようなユースケースに使用しています。

  1. ユーザーが検索ボックスにキーワードを入力する
  2. team-searchがキーワードから商品を検索する
  3. team-searchが2の結果をpublishする
  4. team-productが3をsubscribeし、商品を更新する
// team-search.js
messageBus.publish('search', 'search.word', {items: hitItems});
// team-product.js
messageBus.subscribe('search', 'search.word', event => {
    hydrate(<Items {...{items: event.payload.items}} />, document.querySelector('#team-product-items'));
});

このようにすることで、画面更新ではなく部分更新ができました。 インタラクティブな操作も実現可能です。

状態管理, ルーティング

ここは、まだきちんと作っていませんが、次のようなコンセプトで設計するのが良いと思います。

  • 状態管理
    • 各サービスが状態管理する。状態は共有しない。
    • 統合サービスが共通的な状態を管理する。
  • ルーティング
    • 各サービスがqueryを設定する。
    • 統合サービスがURLパスを管理する。

その他

各サービスは、fly.io というPaaSへデプロイしています。

fly.io

CDNSSRが実行できる Edge Workerを使用しています。 これにより、SSR結果をキャッシュし、高速にレスポンスを返却できます。

ただ、サンプルWebアプリでは、全くその力を引き出せていないです...

※ 参考記事 mizchi.hatenablog.com

サンプルWebアプリで分かったこと

SSR + CSR (Hydration) が実現可能

サーバーサイド統合であっても、CSRは実現可能です。 ただし、Hydrationにはパフォーマンス面に難有りなため、このあたりは課題として残ります。 また、CSRするためのbundleしたjavascriptのsizeには注意が必要です。

例えば、次のリポジトリにある "shared_vendor_webpack_dll" のように、vendorファイルを共有することで、 javascriptのsizeを減らすといった手段があります。

github.com

また、次のリポジトリにある zalando tailorは、script loadをstreamingすることで、 全体のscript load完了時間を短縮するツールもあります。

github.com

サービス内で技術スタックを選択できる

マイクロサービスでは、よくあるメリットとして挙げられるものです。 フロントエンドでも、同様に技術スタックを自由に選択できます。

今回では、React.jsとVue.jsを使用しています。 これを Riot.jsやSvelte.jsにも切り替えることも可能です。 フロントエンド界隈では、JSフレームワークの変化が激しいので、 このメリットは大切だと思います。

ただし、Podiumのmanifest.jsonを返却しなければなりません。 今の所、Podiumに対応しているのはExpressのみなので、Expressを使用する フレームワークのみとなります。

サービス毎のフロントエンドに集中できる

検索サービスだと、検索に特化したフロントエンドのみに集中することができます。 商品サービスだと、商品の表示内容のみに集中することができます。

ただ、どうしても他サービスと連携する要件が出てきます。 これは、マイクロサービスとしての難しさだと思います。 例えば、各サービスがどのタイミングでイベント登録するのかを考える必要があります。

最後に

ECサイトのようなアプリケーションでは『商品を探しやすくする』『買いたくなるような商品を表示する』 『商品を簡単に購入できる』などフロントエンドでやるべきことが多くあります。

そういうサービスにおけるフロントエンドがモノリシックであれば、 統一性が欠けてしまったり、知らぬ間にバグを埋め込んでしまうケースが発生してしまいます。

Micro Frontendsは、このような複雑化するフロントエンドにメスを入れる良いアーキテクチャだと思います。 ただし、バックエンドにおけるマイクロサービス化による課題があるように、フロントエンドにおける マイクロサービス化にも課題はあるはずです。

日本では、Micro Frontendsの導入実績が少なく、まだまだ発展途上だと思います。 この記事が、どこかのサービスへの参考になればと思います。

最後まで読んで頂き、ありがとうございました。

参考リンク

github.com

TwitterにあるLinkを収集するツール Cotlin で、世界中のプレゼンテーション資料を知ろう

https://res.cloudinary.com/silverbirder/image/upload/v1584017984/cotlin/overview.png

Twitterに投稿されているLinkを収集するツール Cotlin を作りました。

Collect links in tweet

から、Cotlinという名前にしました。Androidのアレに似ています。

github.com www.npmjs.com

動機

私は、connpass等を使って、技術系のカンファレンスに参加することがありました。 カンファレンスで発表された資料は、Twitterで公開されることが多々あるので、それを自動収集できるようにしたいと考えたのが、Cotlinを作った動機です。

技術スタック

Google Apps Script (Clasp) + Twitter API (tweet search API) です。 ライブラリを公開していて、それを元にAPIClientを簡単に作れるようにサンプルコードも用意しています。使い方は、全てREADME.mdにあります。

困ったこと

Tweetに記述したリンクは、全てt.coに短縮されてしまいます。 この短縮URLからオリジナルURLを手に入れるために、リダイレクトする必要があります。 実際に作ってみると、次のような記事に書いたとおり、GASで書くと、少し困ったことがありました。

silverbirder180.hatenablog.com

そこで、複数のリダイレクトURLへリクエストする処理を並列化するために、Golangで開発していました。

しかし、そもそもTwitter API (tweet search API)のレスポンスには、オリジナルURLが含まれている(expanded_url)ことに気づき、結局、Google Apps Script (Clasp)で開発することになりました。

良かったこと

毎日、プレゼンテーション資料を収集し、スプレットシートに記録するよう、自動化しました。
※ 都合により、URLのリンクとTweetのリンクのみ記載しています。

docs.google.com

次のような資料を発見できるようになりました。

  • 世界中のプレゼンテーション資料
  • 知らない技術カンファレンスの資料
  • 個人や学生が公開した資料

そこから、次のような良かったことがありました。

  • 『テストについて、同じように困っている人がいた。○○という技術で解決してるんだ!知らなかった!』
  • 『最近気になっている○○のアーキテクチャの資料だ。メリット・デメリットがよくまとまってて良い!』
  • 『リモートワークを取り組んでいる企業の話だ。これから私もリモートワークになるから、先に知見を知っておこう!』

毎日、資料を読んでいると、1日2,3件ほど、自分の琴線に触れるものが現れます。とても刺激を受けて、作ってよかったなと思いました。

最後に

動機であった技術カンファレンスの参加は、実は最近減少しています。 理由は色々ありますが、まあ伏せておきます。

こういったツールによって、アンテナを広く張り巡らせておくことができます。 これのおかげで、様々な関心事をキャッチアップできるようになりました。

上のスプレットシートは、ずっと更新し続ける予定ですので、活用下さい。

Mac で バ美肉 りたい! (Zoom + Gachikoe + 3Tene or Reality)

きっかけ

みなさん、リモートワーク(テレワーク)してますか? Hangouts MeetやZoomといったビデオ会議ツールを使う機会が増えたと思います。

そんな中、次の記事が流行りました。 level69.net

バ美肉(バびにく)とは、バーチャル美少女受肉またはバーチャル美少女セルフ受肉の略語 ja.wikipedia.org

これにより、ビデオ会議(例はZoom)で、次のようなバーチャル美少女 (になりきった私)が参加できるようになります。もちろん、声もボイスチェンジできます。

バーチャル美少女 (私)
Whiteboard in Zoom

Windowsでは、Facerigというアプリで簡単に構築できるみたいです。

これを Mac で構築する方法を紹介しようと思います。 Mac + Bootcamp → Windows10 + Facering でもできると思いますが、動作不安定になる可能性があったため、極力避けようと思い、却下しました。

構成

私は、次のような構成になりました。

"バ美肉" 's structure

音声と動画の2つに分かれます。 また、ビデオ会議ツールと連携するため、仮想デバイス(Soundflower, CamTwist)が必要になります。

音声

Voice Changer: Gachikoe

野太い声じゃなくて、かわいい声が聞きたいですよね。そうですよね。はい。

Gachikoeを使いました。 booth.pm

Gachikoeは、次のような設定にしました。

Gachikoe
Gachikoe settings

Outputを soundflower (2ch)にしています。

仮想マイク

仮想マイクは、Soundflowerを使います。 github.com

音声出力のルーティングを制御するために、LadioCastも使いました。

LadioCast

LadioCast

  • Yosirou Sawayanagi
  • エンターテインメント
  • 無料
apps.apple.com

LadioCastは、次のような設定にしました。

LadioCast

Inputを soundflower (2ch)とし、Outputを soundflower (64ch)としています。

動画

Application for VTuber

Desktop: 3tene

デスクトップで動かす場合は、3tene(ミテネ)を使いました。

3tene.com

3teneは、特に設定は必要ありません。 撮影前には、Webカメラリップシンク(口の動きの同期)を起動しておきましょう。

3tene

Asserts

肝心のキャラクターですが、3teneはVRM形式でなければならないそうです。(よくわかっていません)
私は、次のサイトでダウンロードしました。

hub.vroid.com 3d.nicovideo.jp

Mobile: Reality

モバイルで動かす場合は、Realityを使いました。

apps.apple.com

Realityは、特に設定は必要ありません。 好みのキャラクターをカスタマイズして簡単に作れます。

私は、これです。

https://reality.wrightflyer.net/profile/443e9213

iPhoneで撮影している画面をMacに反映する必要があります。 MaciPhoneを接続し、QuickTime Playerへ出力します。こんな感じです。

iPhone To QuickTime Player

noneは、私のiPhoneバイス名です。

仮想カメラ

CamTwistという仮想カメラを使いました。 camtwiststudio.com

CamTwistは、次のような設定にしました。

CamTwist
例では、QuickTime Playerのアプリケーションを選択しています。3teneの場合は、3teneの選択肢を選択すれば良いです。

使い方 (Zoom)

今まで説明したものを起動した状態で、Zoomを起動します。 Zoomは、次のような設定にしました。

Zoom > Settings > Video

動画は、CamTwistから取得するようにします。

Zoom > Settings > Audio

音声は、Soundeflower (64ch)から取得するようにします。

これで、Macバ美肉 することができました!

終わりに

テレビ会議で、こういった "リアルな姿を出さず、異なる人物を出す" のは、実際役立つものなのでしょうか。 テレビ会議ツール、例えばZoomでは、音声や動画を隠せる機能はあります。 "リアルな姿を隠したい"要求は、すでに解決できています。

今回のような"バ美肉"って、どういうメリットがあるのか、んーってなりました。 ネタ的には『可愛い女の子と会話すると、生産性があがる』なのですが...脳が震える。

参考リンク

kumak1.hatenablog.com kuroyam.hatenablog.com mzyy94.com www.excite.co.jp https://www.cg-method.com/entry/gachikoe/#Gachikoewww.cg-method.com vtuberkaibougaku.site

追記

Snap Cameraという面白いツールを↓から知りました。
リモートワークの導入から3ヶ月 / Connehito marche online 20200311 - Speaker Deck

snapcamera.snapchat.com

手軽に遊べるので、試してみるのも良いかと思います。