ないんのブログ

気になったネタをつぶやきます

スキー旅行で一人ハッカソンをした話

先日、毎年恒例の大学時代の友達とのスキー旅行に行ってきました。早いもので7歳の僕を除いては参加者の平均年齢も30にさしかかり、既婚者も増え始めてきました。そして今回からはついに家族参加がOKとなり、1歳の子供が参加するようになりました。ふとしたきっかけから一人ハッカソンをする運びになって、お披露目したらまあまあウケたので紹介してみようと思います。

 

一人ハッカソンのきっかけ

時は遡り年末の忘年会。

僕「今回から家族参加OKにしよう」

A氏「奥さんと子供(1歳)連れていくわ。でも、教育に悪いから下品なネタ禁止な」(スキー旅行はとにかく下品な会話が多い)

B氏「下品な話をするたびに、罰金1000円にしよう」

僕「じゃあ、罰金管理システムが要るね」

  という会話がきっかけに、スキー旅行の罰金管理システム(+α)作りハッカソンが始まったのでした。

仕様

単に罰金がカウントされるだけだと面白くないので、色々楽しめそうな要素を入れようと考えていました。 主に以下の要素を盛り込むように意識して設計しました。

  1. リアルタイムに罰金額が分かる
  2. 誰でも簡単に罰金を登録出来るようにする
  3. 罰金が発生すると何か演出が走る
  4. その他なんか面白そうな機能を入れる(+α要素)

実装期間は1ヶ月弱でした。

設計

その結果の全体構成(概略)は以下のようになりました。色々詰め込んだのでなかなかカオスな構成です…。

f:id:osanine:20180304212559p:plain

Webベースのアプリです。フロントエンドは流行りのReact+ReduxをAmazon S3 + Cloudfront(独自ドメイン用)でホスティングしました。バックエンドはAPI Gateway+Lambda+Dynamo DBです。その他イベント同期にPubSubサービスのPubNub、画像ホスティング&加工が可能なCloudinaryを利用しました。運用費ほぼ0円なサーバーレスな構成です。

システムの大まかなイメージとしては、タブレットにランキングボードが常時表示されおり、スマホから罰金を登録したら、タブレット側で演出が流れるようなイメージです。

各機能の詳細について紹介します。

モバイル画面

各人のユーザーの罰金登録画面です。罰金ボタンを押すと1000円罰金が発生します。

f:id:osanine:20180304164921j:plain:w300

ロゴは流行りのゆるキャン風で作ってみました。ゆるキャンと同じフォント(漢字タイポス415 R)を使うためだけに、AdobeのComplete Pack(月額4000円)に登録しました(金欠)。

誰かが罰金ボタンを押すとこのように通知が表示されます。 みんながボタンを押しまくると、ついったーのふぁぼ爆撃みたいに通知で画面が埋まります。

f:id:osanine:20180304165328j:plain:w300

ダッシュボード画面

ダッシュボードを宴会部屋に置いておき、罰金額をランキング形式でいつでも確認出来るようにしました。

また、スマホで罰金ボタンが押されると、罰金サウンド(笑ってはいけないのパクr…オマージュです)が流れるようにしてみました(下記動画)。罰金ボタンが押されると、バックエンドAPIからPubNubのWebSocketでダッシュボードに罰金更新の通知を送っています。ちなみに、罰金額の同期もPubNubのイベントを利用して、APIコール数を減らしています。

動画音声中の名前の部分はgoogle-ttsで動的に生成しています。ブラウザだとCORSの関係でうまく動かなかったので、Lambdaで一度S3に保存してそれを再生するようにしています。

ちなみにタブレットは自立出来て音もそこそこ鳴るものが良かったので、YOGA TAB 3 PLUSを買いました(4万くらい)(金欠)。 モバイル端末だとタッチイベント等のユーザーイベントが無いと音が再生出来ない制約がありますが、今回はChromeのflagを編集するワークアラウンドで対処しました。

異議申し立て機能

かねてから罰金の濫用が懸念されていたので、異議申し立てを出来る機能を実装しました。 Google Home に「OK Google, 異議あり!」としゃべったら審判機能が開始します。

流れは以下のような感じです。

審判(ジャッジメント)の開始

LINE botを使ってアウトかセーフの集計

f:id:osanine:20180304195647p:plain:w300

1分後に審判結果をしゃべってくれる(本番では上手く動かなかった…)

Webアプリに投票機能を実装すると、アプリを開いていない人が投票出来ず、一票の格差が生まれる可能性があったため、みんなが居るLINE部屋に投稿するようにしました。 ちなみに、動画再生にはreact-playerを利用しました。ジャッジメントですの!の動画は、Youtube動画を埋め込みで再生しています(検索して適当に見付けた)。

裏側では、LambdaでPubNubに動画再生命令を送る→LINE Messaging APIでスキーのグループにTemplate Messageを送信(結果はwebhook経由でDynamoDBに保存)→1分スリープ→結果をDBから読み出す→LINEに投稿する→PubNubにテキスト再生命令を送る、という流れになっています。この部分はスキー旅行前日に実装したのでハードコーディングでゴリゴリ書きましたが、こういうシナリオ的なのを簡単に扱えるような仕組みってないんですかね。

画像・動画表示機能

もはや罰金とは全く関係ないですが、罰金機能を実装する際にLINE botや動画再生機能など色々実装したので、機能を流用してLINE部屋に投稿した画像や動画が自動で再生されるようにしてみました。

LINE botに画像や動画を送るとこんな感じでダッシュボードに表示してくれます。

最初はS3で画像と動画のホスティングをしようと思っていましたが、似たようなアイデアCloudinaryを使っているのを見かけて自分も使ってみました。APIが充実していてとても使いやすかったです。 tomoima525.hatenablog.com

スキー中の写真や動画を表示させたら面白いかなと思いましたが、画面が小さくてイマイチでした。宿泊先の大画面テレビが故障してたのが一番の敗因(Chromecast持っていったのに…)。

まとめ

後からAWSのログを見たら1晩で1万回ぐらい罰金ボタンが押されていました。1回1000円なので合計1000万円ですね、やばい。

アウトの所で名前入りのサウンドを鳴らすようにしたのがなかなか好評でした。

面倒臭さ排除のためにログインの仕組みは設けず、URLにアクセス出来る人なら誰でも罰金ボタンを押せるような仕様にしていたのですが、突然アウト〜の音声が流れてきたりして、誰が押したんだろうという探り合いが始まるのも面白かったです。

糞みたいなアプリでしたが、意外な所が盛り上がり要素に繋がるんだなととても勉強になりました。

最後の方はデスマーチ状態でかなり汚いコードになってしまったので、ソースコードはまた後程整理して公開しようと思っています。

おまけ

他の参加者にAPIがハックされた

f:id:osanine:20180304202428j:plain:w300

f:id:osanine:20180304202617j:plain:w300