GAミント至上主義

Web monomaniacal developer@株式会社ビズオーシャン

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が「yuyamazaki」が長いので勝手に作ってくれた。読み方わからない)

株式会社ビズオーシャンのCYO(Chief Yagi Officer(自称))兼、エンジニアリング部リーダー職。
コミケ3日目で10年以上売り子無欠勤
アズールレーン@竹敷でUI、UX研究中(最初のケッコンはジャベリン)

GitHub: https://github.com/uyamazak/

続きを読む

Firestore + Cloud Functions + Hangouts Chatで簡易メッセージフォームを作る

yagish履歴書で、お問合せ用にGoogleフォームは設置しているけど、もっと気軽な問合せフォームみたいのを作りたい。

でも、メールで送るのも、受け取るのもちょっと面倒なので、チャットに来ればいいやと考えた。

Slackも使っているけど、会社でG Suiteを使っていることもあり、せっかくなのでHangouts Chatを使ってみることにした。

gsuite.google.co.jp


全体の流れとしては下記のような感じ。

1. Firebaseでログインしているユーザーがテキストフォームに書き込んで送信

2. Firestoreの新規ドキュメントに問い合わせ内容を書き込む

3. Hangouts ChatのWEB Hook URLを取得。

4. FunctionsのonCreateで2を受け取ってChatのAPIにPOSTする。


1はyagish履歴書で出来ているので省く。ほんとはログインなしでも書き込みさせたいけど、スパム対策とユーザーごとのディレクトリで管理しているのでそれをそのまま使いたいため、ログイン必須にする。
またログイン必須にすると名前とかメールアドレスを入れなくていいので、メッセージ蘭だけになり、シンプルにできる利点も。

2. Firestoreへの書き込み

普通にaddする。
Cloud Firestore にデータを追加する  |  Firebase

以下疑似コード。
今後運営からお返事を書くかもしれないので、データにはユーザー情報も持たせておく。あと最新順に並び替えるようにtimestampも持たせておく。

const postCardData = {
  content: message,
  author: {
    uid: user.uid,
    email: user.email
  },
  timestamp: firebase.firestore.FieldValue.serverTimestamp()
}
// {uid}は仮、ちゃんと入れないとダメ
db.collection('/userdir/{uid}/postcards/').add(postCardData)

3. Hangouts ChatのWEB Hook URLを取得

チャットルームを作り、左上のところからつくれる。場所は分かりづらいけど、名前とオプションでアイコンを設定するだけで作れる。

f:id:uyamazak:20180918143237p:plain

4. FunctionsのonCreateで2を受け取ってChatのAPIにPOST

公式通り、Firebase Functions環境を準備する。
Firebase Cloud Functions  |  Firebase

Hangouts ChatのWEB Hookについては公式参照。
Using incoming webhooks  |  Hangouts Chat API  |  Google Developers

リクエストにはrequestを使った。
www.npmjs.com

jsonという専用パラメータで送るのが分からずしばらくハマった。
jsonにオブジェクトを入れるとstringifyして送ってくれる。

まだログもいろいろ出してて、完成じゃないけどコードは下記のような感じ。
本文を作るところは何かしらテンプレート的なものを使った方がよさそう。

const functions = require('firebase-functions');
const request = require('request');
// https://firebase.google.com/docs/functions/manage-functions?hl=ja
const runtimeOptions = {
  timeoutSeconds: 30,
  memory: '128MB'
}

const WEBHOOK_URL = "https://chat.googleapis.com/v1/spaces/XXXXXXXXXXXXXXXXXXXXXXX"

exports.sendPostCard2Chat = functions
  .runWith(runtimeOptions)
  .firestore
  .document('/userdir/{uId}/postcards/{postCardId}')
  .onCreate((snap, context) => {
      console.log('snap', snap)
      console.log('context', context)
      const val = snap.data();
      console.log('bodyText', bodyText)
      const options = {
        url: WEBHOOK_URL,
        headers: {
           'Content-Type': 'application/json; charset=UTF-8'
        },
        json: {text: 'email: ' + val.author.email + ' \n' + 'uid: ' + val.author.uid + ' \n' + 'site: ' + val.site + ' \n' +  'message:\n' + val.content}
      };
      request.post(options,
        (e, r, body) => {
          console.log('callback e:', e)
          console.log('callback r:', r)
          console.log('callback body:', body)
        }
      )
    });

ハマりどころとして、タイムアウトとか、メモリ、リージョンもこのコード上で指定できるが、リージョンを日本にしてしまうと動かなかった。
おそらくFirestoreがus-centralだからかなぁと思ったがよく調べてない。

これで、いまどきのサーバーレス&チャットで簡単なメッセージフォームができた。
データもチャットだけでなくFirestoreにも残せるので夢が広がる。

問題としてはこれだけのために普段使ってないHangouts Chatを開かねばいけないところ。

あと注意点としてFirestoreもFunctionsもβなので、大事なお問合せには使わない方がよさそう。

5000兆円欲しい!.cssをHeadless Chrome+Google App Engineを使いサーバーで画像化する

Headless Chromeの登場で、HTML+CSSのPDF化だけでなく、画像化においてもWEB系開発者にとっては最強の手段となった気がする。

やりたいこと

この5000兆円欲しい!の装飾で動的にテキストを画像化したい。こんな素敵なCSSを公開してくれたVoQnさんに感謝と畏敬の念しかない
codepen.io

そしておみくじ機能?で使う(かもしれない)

5000兆円食べたい!
※画面は開発中のものです

こんなのができる
f:id:uyamazak:20180912111059p:plain

なぜわざわざサーバーで画像化するのか

最初はHTML+CSSのままやろうと思ったけど、いろいろ問題が出てきた。次にcanvasで画像化も考えたけど、難しすぎて力尽きた。
サーバー側で画像化することで下記の問題を解決できる

ブラウザ依存の問題(IE、Edge士ね)

上記のCSSChromeだとちゃんと出るけど、Edge、IEだとしょぼい

画像の方がサイズ変更が容易

スマホの時など画面幅によって改行されてしまったり、文字サイズを変えるとグラデなど装飾のバランスがおかしくなってしまう。
画像化されていればcssなどで問題なく拡大縮小できる。

シェア+保存しやすい

画像なので右クリで保存できるし、URLができるので、そのままTwitterなどでも共有できる

あとサーバーで描画処理をするので、ユーザー端末に優しいというのもある。

Headless Chromeを使う利点

  1. CSSとそれに対応するブラウザの進化でこれまでフォトショなどが無いとできないグラデなどの複雑な装飾ができるようになった
  2. 普段使いのChromeで表示されるHTML+CSSならそのまま画像化でき、ユーザー側のブラウザ(IE市ね)は全く気にしなくていい
  3. HTMLなのでプログラムでの操作が簡単で動的に内容を変更もやりやすい

つくるもの

途中まではPDF化と同じなので自作の下記アプリを流用する。
各種インストール・デプロイ詳細などはこちら参照。
github.com

express部分をいろいろ書き換える。
https://github.com/uyamazak/hcep-pdf-server/blob/gosenchoen/app/gosenExpressApp.js

HTMLテンプレートを使いたいのでEJSを使った
www.npmjs.com

受け取ったテキストなどのクエリはvalidatorを使ってバリエーションしておく。

HTMLファイルはこれ
https://github.com/uyamazak/hcep-pdf-server/blob/gosenchoen/app/gosen.html

指定した要素(今回は.gosen-preview)だけ出力したいので下記を参考にscreenshot時に座標を指定して切り出す。
Capture DOM element screenshot using Chrome headless · GitHub

フォントはfonts/にM+をダウンロードして使うやつ入れておく
M+ FONTS

screenshotの下記オプションで下記背景を透明化もできる。(デフォは白)

omitBackground: true

デフォルトだと横幅が狭く切れたり、改行したりするのでViewPortを大きくしておく
gosenExpressApp.js

await page.setViewport({ width: 3000, height: 1000 })

開発はDockerでやって、あとはGAEにデプロイする(雑)

こんな感じで動的に画像を作れるようになった
https://gosenchoen-dot-hcep-pdf.appspot.com/?line1=Google%20Pixelbook&line2=%E6%AC%B2%E3%81%97%E3%81%84%EF%BC%81

GAEは開発用でいつか止めるかもしれないので、念のため出力された画像も
f:id:uyamazak:20180912111059p:plain

まだ解決できてない問題としてキャッシュだと304でcontent typeが画像で返らないため?、Twitterなどで画像として認識されないことがある。あとファイルサイズがちょっと大きいので縮小する機能も必要かも。

ゲームとかで使うバナーなどもフォトショいらずで、全部WEBデザイナーがHTML+CSSでつくり、こんなサーバーで画像化して配信っていう未来も来るかもしれない。
すべてコードかできるのでGit管理できるしフォトショを起動しなくてもよいので変更がめっちゃ楽だと思う。

Nodeクックブック

Nodeクックブック

Node.js 超入門

Node.js 超入門

Webサービスの作り方(ふくろうの描き方)

勢いで作ってしまったけど、おおむねこれで合ってる気がする


元ネタ:ふくろうの描き方 - Google 検索