- 投稿日:2020-07-22T23:43:52+09:00
1日の仕事の振り返りで使えるコーチング用LINE BOTを作ろうとした
はじめに
自社のWEBサイトのQ&Aで、LINE BOT作れたらなと考え、
とりあえず何か簡単なLINE BOTを作ってみようと思いました。普段、人材開発の仕事に携わっているので、
自分の1日の振り返り用のLINE BOTだったら実用的かなと、
作ることにチャレンジしました。コーチング用LINE BOTの意図
振り返りで自動化したいことは、
・月間目標のリマインド
・月間の残り日数
・振り返りの質問
・PDCAを回す時の視点
です。そのあたりを自動化しつつ、
明日の行動をイメージできるように、
明日の天気も自動で取得できるようにしてみました。結果できたLINE BOT
天気apiを使うまでのLINE BOTは作れた。
"use strict"; const express = require("express"); const line = require("@line/bot-sdk"); const axios = require('axios'); const PORT = process.env.PORT || 3000; const config = { channelSecret: "channelSecret", channelAccessToken: "channelAccessToken", }; const app = express(); app.get("/", (req, res) => res.send("Hello LINE BOT!(GET)")); app.post("/webhook", line.middleware(config), (req, res) => { console.log(req.body.events); if ( req.body.events[0].replyToken === "00000000000000000000000000000000" && req.body.events[1].replyToken === "ffffffffffffffffffffffffffffffff" ) { res.send("Hello LINE BOT!(POST)"); console.log("疎通確認用"); return; } Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result) ); }); const client = new line.Client(config); async function handleEvent(event) { if (event.type !== "message" || event.message.type !== "text") { return Promise.resolve(null); } const message = event.message.text; let replyMessage = event.message.text; if (message == "完了") { replyMessage = "お疲れ様!1日振り返りましょう!"; } if (message == "はい") { replyMessage = "今月の目標は、Qiitaの記事を3つ書くことです。現状は?"; } let date = new Date(); let year = date.getFullYear(); let month = date.getMonth() + 1; let day = date.getDate(); let eom = new Date( year, month, 0 ).getDate(); let number = eom - day ; if (message.indexOf("現状") > -1) { replyMessage = `今月は残り${number}日です。順調ですか?`; } if (message == "いいえ") { replyMessage = "いますぐリスケしましょう。"; } if (message == "やばい") { replyMessage = "6つの変数<質・量・納期・方法・代替・金>どこ変えますか?"; } if (message.indexOf("目標") > -1) { replyMessage = "今月は「WEBサイトに新サービスを追加」です"; } if (message == "順調") { const randomMessages = [ "さすが!間違いなく成長してます!", "素晴らしい!!いうことなし!", "成功間違いなし!明日も頑張りましょう!", ]; replyMessage = randomMessages[Math.floor(Math.random() * 3)]; } if (message == "ヒント") { const randomMessages = [ "Tips:タスクじゃなく得たい結果を意識しよう", "Tips:タスクを15分単位に分解してみよう", "Tips:移動時間でもできるタスクを見つけよう", ]; replyMessage = randomMessages[Math.floor(Math.random() * 3)]; } if (message == "明日もよろしく") { const randomMessages = [ "こちらこそ!!", "明日もがんばって!", "たのしんで!!", ]; replyMessage = randomMessages[Math.floor(Math.random() * 3)]; } return client.replyMessage(event.replyToken, { type: 'text', text: mes }); } app.listen(PORT); console.log(`Server running at ${PORT}`);最後に天気apiで明日の天気を取得し、明日の行動スケジュールを立てやすくしようとしたが、うまくいかなかった。
上のソースコードに、挿入したのがこちら。
if(event.message.text == '天気教えて') { let replyText = ''; replyText = '考え中'; await client.replyMessage(event.replyToken, { type: 'text', text: replyText }); } const CITY_ID = `400040`; const URL = `http://weather.livedoor.com/forecast/webservice/json/v1?city=${CITY_ID}`; const res = await axios.get(URL); const pushText = res.data.description.text; return client.pushMessage(event.source.userId, { type: 'text', text: pushText, }); } return client.replyMessage(event.replyToken, { type: 'text', text: mes }); }やはりプログラム1つ1つの意味が解読できないと、どこをどう変えればいいかわからない。
まずはJavascriptのプログラムの勉強からはじめる必要がありそう。
工夫したこと
・目標達成に向けて順調に進んでいたら、褒める、労う。
・目標達成ノウハウで、たまに思い出したいものを、いつでも引き出せるようにする
・今月の残り日数を自動計算することで、目標達成の具体的なスケジュールを意識できるようにした今後の改善事項
・まずはapiを使ったプログラムを完成させる。
・状況に合わせて、効果的な問いが自動で出てくるようにしたい
・もっとリマインドしたい目標達成ノウハウを詰め込みたい
・計画をリスケしたら自動でgoogleカレンダーに入力されるようにしたい今後、改善して、より実用的なものに繋げていこうと思います。
まとめ
LINE BOTの勉強をはじめて3日でやってみましたが、
思いの外できた部分と、うまくいかない部分と味わった。これからも色々チャレンジしながら、学んでいけたらと思います。
- 投稿日:2020-07-22T23:00:43+09:00
簡単レシート印刷 receiptline で仮想プリンターを使ってみた
日本発のオープンソース receiptline でレシート印刷に少しずつトライしています。
先日落札したレシートプリンターは、セルフテストして投稿用に写真撮影しました。
IP アドレス設定中のため、前回利用した開発ツールを引き続き使います。
今回は仮想プリンターを使った印刷です。ファイルのロード
receiptline に添付されているサンプルデータを開発ツールで読み込んでみましょう。
フォルダアイコンをクリックすると、ダイアログボックスが開きます。
example/data/ja/receipt.txt
を選択して、OK ボタンをクリック。
キャンセルしたいときは、ダイアログボックスの外をクリックします。編集エリアにファイルがロードされました。
ReceiptLine{image:iVBORw0KGgoAAAANSUhEUgAAAIAAAAAwAQMAAADjOuD9AAAABlBMVEUAAAD///+l2Z/dAAAAZklEQVQoz2P4jwYYRrrABwYGOwYG5gMMDBUMDPxAgQcMDDJAgQYGhgJcAv//yMj//9/8//+HerAZRAsAzUASAJoGMhRF4AC6ANCIAhQz8AkAXQoUOIDidBQBkG8hAj8gAqPJAa8AAGjulhOsX97yAAAAAElFTkSuQmCC} 市ヶ谷駅前店 東京都千代田区九段1-Y-X 2019年 2月19日(火) 19:00 {border:line} ^領 収 証 {border:space} {width:*,2,10} ビール | 2| ¥1,300 千鳥コース | 2| ¥17,280 ------------------------------------- {width:*,20} ^合計 | ^¥18,580 現 金 | ¥20,000 お 釣 り | ¥1,420ちなみにセーブ機能はないです。コピペでファイルに保存するしかないようです。
テスト印刷
テスト印刷は、開発ツールを Node.js で実行している必要があります。
Web ブラウザーのアドレスがhttp://localhost:10080
であれば OK です。開発ツールの右上にはテスト印刷用のボタンがあります。
printer1
は印刷するレシートプリンターの ID です。プリンター ID
初期状態では以下のレシートプリンターが登録されています。
プリンターID 桁数 言語 用紙排出 出力データ printer1 42 英語 なし SVG printer2 48 日本語 なし SVG printer3 48 日本語 上方向 ⤴ ESC/POS tm_series1 42 英語 上方向 ⤴ ESC/POS (エプソン) mc_series1 48 日本語 前方向 ⤵ StarPRNT (スター) mc_series2 48 英語 前方向 ⤵ StarPRNT (スター) rp_series1 48 日本語 前方向 ⤵ ESC/POS (セイコー) ct_series1 48 日本語 前方向 ⤵ ESC/POS (シチズン) fp_series1 48 日本語 上方向 ⤴ ESC/POS (富士通) 送信先はすべて仮想プリンター (TCP ポート 19100) となっています。
printers.json
プリンター ID と設定情報は、
printers.json
に保存されています。
host と port は通信用。他は receiptline の変換 API に渡されます。
実機で印刷するときは、このファイルを変更する必要があります。printers.json{ "printer1": { "host": "127.0.0.1", "port": 19100, "cpl": 42, "encoding": "cp437", "gamma": 1.0, "upsideDown": false, "command": "svg" }, ... "fp_series1": { "host": "127.0.0.1", "port": 19100, "cpl": 48, "encoding": "cp932", "gamma": 1.8, "upsideDown": false, "command": "fit" } }仮想プリンター
送信ボタンをクリックすると、編集エリアのテキストが HTTP で Node.js へ送られて変換されます。
仮想プリンターが受信したデータは 16 進ダンプで表示されます。
printer2
へ送信。仮想プリンターが受信したデータは SVG です。Virtual printer running at 127.0.0.1:19100 Server running at http://127.0.0.1:10080/ Virtual printer received: 00000000 3c 73 76 67 20 77 69 64 74 68 3d 22 35 37 36 70 <svg width="576p 00000010 78 22 20 68 65 69 67 68 74 3d 22 33 36 30 70 78 x" height="360px 00000020 22 20 76 69 65 77 42 6f 78 3d 22 30 20 30 20 35 " viewBox="0 0 5 00000030 37 36 20 33 36 30 22 20 70 72 65 73 65 72 76 65 76 360" preserve 00000040 41 73 70 65 63 74 52 61 74 69 6f 3d 22 78 4d 69 AspectRatio="xMi 00000050 6e 59 4d 69 6e 20 6d 65 65 74 22 20 78 6d 6c 6e nYMin meet" xmln 00000060 73 3d 22 68 74 74 70 3a 2f 2f 77 77 77 2e 77 33 s="http://www.w3 00000070 2e 6f 72 67 2f 32 30 30 30 2f 73 76 67 22 20 78 .org/2000/svg" x ... 00000e70 2c 33 36 30 29 22 3e 3c 74 65 78 74 20 78 3d 22 ,360)"><text x=" 00000e80 30 2c 32 34 2c 33 36 2c 36 30 2c 37 32 22 3e e3 0,24,36,60,72">. 00000e90 81 8a 26 23 78 61 30 3b e9 87 a3 26 23 78 61 30 .. ...  00000ea0 3b e3 82 8a 3c 2f 74 65 78 74 3e 3c 74 65 78 74 ;...</text><text 00000eb0 20 78 3d 22 35 30 34 2c 35 31 36 2c 35 32 38 2c x="504,516,528, 00000ec0 35 34 30 2c 35 35 32 2c 35 36 34 22 3e c2 a5 31 540,552,564">..1 00000ed0 2c 34 32 30 3c 2f 74 65 78 74 3e 3c 2f 67 3e 3c ,420</text></g>< 00000ee0 2f 67 3e 3c 2f 73 76 67 3e 0a /g></svg>.
printer3
へ送信。仮想プリンターが受信したデータは ESC/POS です。Virtual printer running at 127.0.0.1:19100 Server running at http://127.0.0.1:10080/ Virtual printer received: 00000000 1b 40 1d 61 00 1b 4d 30 1c 28 41 02 00 30 00 1b .@.a..M0.(A..0.. 00000010 20 00 1c 53 00 00 1b 33 00 1b 7b 00 1b 2d 30 1c ..S...3..{..-0. 00000020 2d 30 1b 45 30 1d 42 30 1d 21 00 1d 4c 00 00 1d -0.E0.B0.!..L... 00000030 57 40 02 1b 61 01 1d 38 4c 0a 03 00 00 30 70 30 W@..a..8L....0p0 00000040 01 01 31 80 00 30 00 00 00 00 00 00 00 00 00 00 ..1..0.......... 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ... 000007b0 52 08 5c 32 30 2c 30 30 30 0a 1b 2d 30 1c 2d 30 R.\20,000..-0.-0 000007c0 1b 45 30 1d 42 30 1d 21 00 1d 4c 00 00 1d 57 40 .E0.B0.!..L...W@ 000007d0 02 1b 61 00 1b 24 00 00 1b 5c 00 00 1b 2d 30 1c ..a..$...\...-0. 000007e0 2d 30 1b 45 30 1d 42 30 1d 21 00 1b 74 01 1c 43 -0.E0.B0.!..t..C 000007f0 31 1b 52 08 82 a8 20 92 de 20 82 e8 1b 24 50 01 1.R... .. ...$P. 00000800 1b 5c a8 00 1b 2d 30 1c 2d 30 1b 45 30 1d 42 30 .\...-0.-0.E0.B0 00000810 1d 21 00 1b 74 01 1c 43 31 1b 52 08 5c 31 2c 34 .!..t..C1.R.\1,4 00000820 32 30 0a 1d 56 42 00 1d 72 31 20..VB..r1コマンドを見ても、ちょっとわからないですよね・・・
次回は落札したレシートプリンターで印刷します!
- 投稿日:2020-07-22T21:03:28+09:00
【LINE BOT】WebAPIを使用し自動返信BOTを作ってみた
はじめに
LINE BOTを利用し、六曜日とその内容を自動で返信するものを作りをました。
入力文字を替えることで、NASA(非公式)の「今日の天文学写真」を楽しむこともできます。目的
六曜にこだわる人は少なくなってきていると思いますが、まだ大安などの吉日を好む人は多いと思います、個人的なイベントが発生すると「今日は大安だっけ?」「友引かな?」などとたまに気にすることがあるので、「今日は」と入力する今日の六曜日を自動返信するBOTを製作しました。
別で作ったほうがいいかと思いましたが、今回は練習も兼ねてで画像も取得したいと思いましたので、「今日の画像は」と入力するとNASA(非公式)より「今日の天文学写真」を取得し自動で表示します。宇宙の美しさ広さをみて、自分の小ささを感じます。
作ったもの
構成
環境
- node.js v14.5.0
- @line/bot-sdk 7.0.0
- axios 0.19.2
- date-utils 1.2.21
- express 4.17.1
LINE Messaging API
Node.jsでLINE BOTを作る方法はこちらを参考にしました。
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest
今回使用したWEBAPI
旧暦や六曜など日付に関する情報を取得できるAPI(外部サイト)
- 指定した日付に関する様々な(「今日は何の日」的な)情報を取得することができます。
- 「今日の天文学写真」を取得するのに使用しました。
コード
server.js"use strict"; const express = require("express"); const line = require("@line/bot-sdk"); const axios = require('axios'); const PORT = process.env.PORT || 3030; const config = { channelSecret: 'channelSecret', channelAccessToken: 'channelAccessToken' }; const app = express(); require('date-utils'); var dt = new Date(); var formatted = dt.toFormat("YYYY-MM-DD"); console.log(formatted); app.get("/", (req, res) => res.send("Hello LINE BOT!(GET)")); //ブラウザ確認用(無くても問題ない) app.post("/webhook", line.middleware(config), (req, res) => { console.log(req.body.events); //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。 if ( req.body.events[0].replyToken === "00000000000000000000000000000000" && req.body.events[1].replyToken === "ffffffffffffffffffffffffffffffff" ) { res.send("Hello LINE BOT!(POST)"); console.log("疎通確認用"); return; } Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result) ); }); const client = new line.Client(config); async function handleEvent(event) { if(event.type !== 'message' || event.message.type !== 'text'|| event.message.type !== 'location') { return Promise.resolve(null); } let shakkou=''; let mes = ''; if(event.message.text === '今日は') {//「きょうは」と入力する。 const res = await axios.get('https://dateinfoapi.appspot.com/v1?date='+ formatted); const pushText = res.data.rokuyo; if(res.data.rokuyo === '赤口'){ shakkou ="正午の前後を除いて凶日、午前11時ごろから午後1時ごろまでは吉です。"; }else if(res.data.rokuyo === '大安'){ shakkou ="何事においても吉、成功しないことはない日"; }else if(res.data.rokuyo === '先勝'){ shakkou ="午前は吉、午後は凶"; }else if(res.data.rokuyo === '友引'){ shakkou ="朝晩は吉、昼は凶"; }else if(res.data.rokuyo === '先負'){ shakkou ="午前は凶、午後吉"; }else if(res.data.rokuyo === '仏滅'){ shakkou ="大凶日"; } console.log(pushText); mes = '今日は「'+ pushText +'」です。\n'+ shakkou; return client.replyMessage(event.replyToken, { type: 'text', text: mes, }); }else if(event.message.text === '今日の画像は'){ const response = await axios.get('https://api.nasa.gov/planetary/apod?api_key='+ {APIkey}); console.log(response.data.url); await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl:response.data.url, previewImageUrl:response.data.url, }); } } app.listen(PORT); console.log(`Server running at ${PORT}`);終わりに
LINEの国内ユーザー数は8400万人であり、SNSの中ではダントツで1位である、ちなみに2位のTwitterは4500万人(2020年6月時点)。メッセージインフラとして定着しているという利点があり。UIなども簡単にできるようなので、サービスを高速で構築できそう。
今後もLINE Messaging APIを活用しアウトプットしたいと思います。
- 投稿日:2020-07-22T19:48:34+09:00
TensorFlow.jsノードをNode-REDで使ってみる
日立製作所OSSソリューションセンタの横井です。今回は、画像認識を行うNode-REDのTensorflow.jsノードの使い方をご紹介します。
Tensorflow.jsとNode-RED
TensorFlow.jsとは、TensorFlowのJavaScript実装です。TensorFlow.jsを用いることで、ブラウザ上やサーバサイドのNode.jsで学習や推論処理をリアルタイムに実行できます。また、Node-REDは、主にIoT向けに開発されたピジュアルプログラミングツールです。InfoQの記事によると、2020年のトレンドとしてTensorlow.jsはEarly Majority、Node-REDはEarly Adoptersという流行っている/流行りつつあるOSSとして位置付けられています。
本記事では、これらトレンドの2つのOSSを組み合わせて何ができるか見てみましょう。
開発するフロー
本記事では、下のスクリーンショットの様な画像のどこに何が写っているか認識するフローを作成してみます。
このフローは、黄色のノード部品を用いてブラウザからファイルをアップロードすると、右下の「元の画像」のノードにアップロードした画像を表示します。同時に橙色の「画像認識」のノードにて、TensorFlow.jsの学習済モデルを用いて画像に何が写っているかを分析します。最後に、右上緑色の「認識結果出力」のノードを用いて右側のデパッグタブに写っている物の名前を表示します。同時に「認識結果付き画像」のノードの下に橙色の四角でアノテーションを付けた画像も表示してくれます。画像のどこの部分を認識したかが分かりやすいですね。
以降で、本フローの作成手順を説明します。今回の手順はローカル環境、Raspberry Pi環境、クラウド環境のNode-REDのどれを使っても動作します。ブラウザはNode-REDプロジェクトのUI動作テストで用いられているGoogle Chromeが良いでしょう。
Tensorflowノードをインストール
Node-REDのフローサイトには、TensorFlowを利用できるノードが複数公開されています。その中でも、学習済のモデルが入ったnode-red-contrib-tensorflowモジュールをインストールしてみます。Node-REDにTensorFlowノードをインストールするには、フローエディタの右上のメニュー -> 「パレットの管理」 -> 「パレット」 -> 「ノードを追加」と移動します。その後、検索キーワード入力欄に「node-red-contrib-tensorflow」と入力しましょう。
上の画像の様に、検索結果に今回用いるTensorFlow.jsノードが表示されるため、「インストール」ボタンをクリックしてTensorFlowノードをインストールします。インストールが完了すると、左側パレットの分析カテゴリの中に橙色のTensorFlow.jsノードが4つ登場します。
各TensorFlow.jsノードの説明は以下の表の通りです。全て画像認識を行うノードですが、画像認識の機能以外にも認識結果付きの画像データを生成したり、エッジ分析で必要となるオフラインでの利用ができたりする等の違いがあります。
# 名前 説明 認識結果付き画像 オフライン利用 1 cocossd 画像に写っている物体名を返すノード 有り 可能 2 handpose 手の画像から指や関節の位置を推定するノード 無し 不可 3 mobilenet 画像に写っている物体名を返すノード 無し 可能 4 posenet 人物の画像から腕や頭、足の位置を推定するノード 有り 可能 その他、Node-REDで画像データを扱うために必要な以下のノードも同じ手順でインストールしておきます。
- node-red-contrib-browser-utils : フローエディタ上から画像ファイルや音声ファイルをアップロードするノード
- node-red-contrib-image-output : フローエディタ上に画像を表示するノード
node-red-contrib-browser-utilsのインストールが終わると、入力カテゴリの中にfile-injectノード、microphoneノード、cameraノードが入っているはずです。また、node-red-contrib-image-outputのインストールが完了すると、出力カテゴリの中にimageノードが表示されていると思います。
フローを作成
必要なノードを準備できたため、早速フローを開発してみましょう。
右側のパレットから黄色のfile injectノード、橙色のcocossdノード、緑色のdebugノード(ワークスペースに配置すると、msg.payloadという名前に変わります)を配置し、各ノードの端子をワイヤーで接続します。ワイヤーを流れている画像データを確認するため、2つのimageノード(ワークスペース上に置くとimage previewという名前になります)をフローの下へ配置し、それぞれfile injectノードの出力端子、debugノードの入力端子に接続します。右側のimage previewノードのみは、表示する画像データの変数を指定するため、ノードの設定を変更する必要があります。設定を変更するには、image previewノードをダブルクリックしてノードプロパティ画面を開きます。ノードプロパティ画面では、デフォルトではmsg.payloadに入っている画像データを表示する様になっています。これを以下のスクリーンショットの様に、msg.annotatedInputに変更することで、cocossdノードがアノテーションを付けた画像を表示できる様になります。
各ノードに適切な名前を付けて右上の赤いデプロイボタンを押した後、file injectノードの左側のボタンをクリックして、ローカルPCにある空港の画像ファイルをアップロードしてみましょう。
「認識結果付き画像」のノードの下に機体に対して橙色のアノテーションが着いた画像が表示されました。また、右側のデバッグタブには、正しく「airplane(飛行機)」と表示されました。ぜひ手元にある画像をアップロードして、正しく認識できるか遊んでみてください。
最後に
今回は、cocossdというTensorFlow.jsノードをご紹介しました。その他のノードについてもほぼ同様のフロー作成手順で画像認識を行うことができます。本フローの応用例については、次回の記事で紹介してゆこうと思います。
※ 本記事はThe Linux FoundationのサイトLinux.comに投稿した下記記事の和訳です。
https://www.linux.com/news/using-tensorflow-js-and-node-red-with-image-recognition-applications/
- 投稿日:2020-07-22T19:20:29+09:00
[初心者向け]Sassを今この瞬間から使えるようになるまでの手順
はじめに
HP制作の段階で「CSSある程度理解して使えるようになったから、Sassを触ってみよう!」と思っても実際に使えるようになるまでにはいくつかの壁があります。
そのうちの1つが環境構築です。HTMLファイルのheadタグ内にはcssを読み込ませる必要がありますね。
しかし、Sassを使ってサイトのスタイルを整えるためにはそのままHTMLに読み込ませる事はできず、Sass→CSSに変換する必要があります。(これをコンパイル)といいます。そこで今回は、僕のような初心者向けに”Sass”を使って開発できるようになるまでの手順を示します。
目次
1.Node.jsをインストールする
2.Node.jsがインストールされているかターミナル/コマンドプロンプトで確認
3.gulp-cliをインストールする
4.package.jsonを作成する(←gulpインストール済みの人はここから)
5.gulpとgulp-sassをローカル環境にインストール
6.node_modulesとpackage-lock.jsonを作成
7.gulpfile.jsをpackage.jsonと同じ階層に作成(自作)
8.Sassをコンパイル準備
ターミナル(Mac),コマンドプロンプト(Windows)を多少触ったことある。
1.Node.jsをインストールする
Sassをコンパイルするにあたって、いろんな方法があると思います。自分は、「Web制作者のためのSassの教科書 これからのWebデザインの現場で必須のCSSメタ言語」という本を使用しながら環境構築したのでこの本に準拠した形で説明します。ちなみに、この本は初心者にとっても優しく書かれているのでWeb制作を行う人にはおすすめです。
まず、Sassを使えるようになるまでの大枠を逆算的に掴みます。
・C/C++ で開発されたLibSassを使えるようになる
・LibSassのライブラリであるnode-sassを使えるようにする
・node-sassを動かすためにNode.jsを使えるようにする
・Node.jsを使えるようになることでnpmコマンドが使えるようになる
・npmコマンドでgulpが使えるようになる
・gulpでSassをCSSにコンパイルできるようになる以上。
つまり、手始めにnode.sassという本体を操作するためにはNode.jsというコントローラーを手元に持ってこなくてはいけないわけですね。
このNode.jsはインストールするとnpmというパッケージ管理マネージャを使えるようになります。
Node.jsがコントローラーなら、npmはAボタンとかそういう感じでしょうか?npmコマンドは後述しますが、これによって必要な機能をインストールできたりします。と、いうわけでnode-sassを動かすためにNode.jsをインストールしましょう!
①Node.js公式サイトからインストーラーをダウンロード
2つ緑のボタンが中央にありますが、「LTS」と書かれた左のほうのボタンを押して最新のLTSをダウンロードしましょう。
②ダウンロードしたら基本的に「次へ」を進めてインストールを完了させる
これでNode.jsは無事にあなたのPCにインストールされたはずで、node.sassを操作する準備は整いました。
実際に、node.jsがインストールされているか確認してみましょう。2.Node.jsがインストールされているかターミナル/コマンドプロンプトで確認
ターミナルorコマンドを開いて、以下のコマンドを打ってバージョンが表示されるか確認
$ node -v
このようにバーションが表示されればPCにnode.jsがインストールされています!3.gulp-cliをインストールする
つぎにgulpというさまざまなタスクを自動化してくれるツール(タスクランナー)をnode.jsをインストールしたことで使えるようになったnpmコマンドでgulpをインストールしましょう。以下のコマンドを叩いてください
$npm install --global gulp-cli上記のコマンドを叩いたあとに、インストールがちゃんとされているか確認しましょう。下記のコマンドです。
$gulp -v*(余談ですが、僕はこのgulpのインストールがうまく行かず何度やっても
gulp: command not foundという表記がでて、心を折られたんですがerrorメッセージをよく見ると【管理者権限が〜】みたいなこと書かれていたので試しに
$sudo npm install --global gulp-cliと打ち直して、管理者権限を確認させるようなコマンドを叩いたら無事にうまくいきました・・・
(この時、errorメッセージはちゃんと読もうと心の底から思いました。)これで、ようやくSassを使えるようになる道具が揃いました。
4.package.jsonを作成する
①任意の場所にファイルを作って、cd コマンドで現在地に移動
僕はデスクトップに「test」というファイルを作り、
「index.html」
「style.scssが入ったsassファイル」②ターミナルを開いてpackage.jsonを作成
npm コマンドで必要なパッケージをインストールしましょう。
"cd"でディレクトリを移動させたあとに以下のコマンドを叩いてください。$npm init -yすると、package.jsonというファイルがindex.htmlらと同じ階層に生成されています
5.gulpとgulp-sassをローカル環境にインストール
package.jsonを作成したら、gulpのパッケージをnpmコマンドによってインストールさせてあげましょう。
①まずは、開発用に使うgulpパッケージをインストール
pacage.jsonに変化が出るので、package.jsonを開いておきます。
そして、以下のコマンドを叩きます。
$npm install -D gulppackage.jsonに変化が出ました。
"devDependencies":{ "gulp": "^4.0.2" }↑という項目が追加されたのが分かるでしょうか?
②続いて、gulp-sassをインストール
以下のコマンドも同様に叩きます
$npm install -D gulp-sass"gulp-sass": "^4.1.0" }これでgulpとgulp-sassのローカルインストールが終了しました。
6.node_modulesとpackage-lock.jsonを作成
5でローカルにgulpとgulp-sassをインストールすると、「node_modules」と「package-lock.json」が自動的に生成されています。
7.gulpfile.jsをpackage.jsonと同じ階層に作成(自作)
それでは、さいごにgulpfile.jsを作成していきましょう。
package.jsonと同じ階層に、
gulpfile.jsを作成します。
gulpfile.jsの中身を書いていきましょう。"use strict"; var gulp = require("gulp"); var sass = require("gulp-sass"); sass.compiler = require("node-sass"); gulp.task("sass", function () { return gulp .src("./sass/**/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(sass({ outputStyle: "expanded" })) .pipe(gulp.dest("./css")); }); gulp.task("sass:watch", function () { gulp.watch("./sass/**/*.scss", gulp.task("sass")); });こちらがデフォルトになります。ですが、次々にgulpのタスクを追加することもできますのでこちらの記事を参考にしてください。
【覚えるべき技5選】Web制作者のためのSassの教科書【備忘録】8.Sassをコンパイル
これで準備は一通りおしまいです!
それでは実際にSassを記述してみて、ちゃんとCSSにコンパイルのか確認してみましょう。
使うコマンドはgulp sass:watch"watch"をつけると、ファイルの更新を監視してくれる。つまり、Sassファイルが更新されると自動的にコンパイルしてくれます。
watchを終了させるには、Ctrl + Cで停止します。まず、Sassで記述してみます。Sassの書き方は以前書いたこの記事を参考に・・・
Sassについて最低限知っておきたい5つの基本機能
ここで"gulp sass:watch"をターミナルで叩きます。
すると、CSSというファイルが新しくできていて⬇
style.cssにうまくコンパイルされています
お疲れさまでした!
[追記] カスタムされたgulpfile.jsの作成
セクション7では、デフォルトのgulpfile.jsを作成しましたが、Sassの他の機能を使うためにはカスタマイズが必要です。
まずは、パッケージをインストールします。
$ npm install gulp gulp-sass gulp-sass-glob gulp-sourcemaps gulp-plumber gulp-notify gulp-postcss autoprefixer css-declaration-sorter css-mqpacker -Dgulpfile.jsを以下のように記述します。
var gulp = require('gulp'); var sass = require('gulp-sass'); var sassGlob = require('gulp-sass-glob'); var plumber = require('gulp-plumber'); var notify = require("gulp-notify"); var postcss = require('gulp-postcss'); var autoprefixer = require('autoprefixer'); var cssdeclsort = require('css-declaration-sorter'); var mqpacker = require('css-mqpacker'); var browserSync = require('browser-sync'); gulp.task('sass', function() { return gulp.src('./sass/**/*.scss') .pipe(plumber({errorHandler: notify.onError("Error:<%= error.message %>")})) .pipe(sassGlob()) .pipe(sass({outputStyle: 'expanded'})) .pipe(postcss([mqpacker()])) .pipe(postcss([cssdeclsort({order: 'alphabetical'})])) .pipe(postcss([autoprefixer()])) .pipe(gulp.dest('./css')); }); gulp.task( 'default', function() { gulp.watch( './sass/**/*.scss', gulp.task('sass')); });ここでは"gulp sass:watch"ではなく、"gulp" のみで自動コンパイルを行ってくれます。
以上です。
- 投稿日:2020-07-22T19:20:29+09:00
Sassを今この瞬間から使えるようになるまでの手順
はじめに
HP制作の段階で「CSSある程度理解して使えるようになったから、Sassを触ってみよう!」と思っても実際に使えるようになるまでにはいくつかの壁があります。
そのうちの1つが環境構築です。HTMLファイルのheadタグ内にはcssを読み込ませる必要がありますね。
しかし、Sassを使ってサイトのスタイルを整えるためにはそのままHTMLに読み込ませる事はできず、Sass→CSSに変換する必要があります。(これをコンパイル)といいます。そこで今回は、僕のような初心者向けに”Sass”を使って開発できるようになるまでの手順を示します。
目次
1.Node.jsをインストールする
2.Node.jsがインストールされているかターミナル/コマンドプロンプトで確認
3.gulp-cliをインストールする
4.package.jsonを作成する(←gulpインストール済みの人はここから)
5.gulpとgulp-sassをローカル環境にインストール
6.node_modulesとpackage-lock.jsonを作成
7.gulpfile.jsをpackage.jsonと同じ階層に作成(自作)
8.Sassをコンパイル準備
ターミナル(Mac),コマンドプロンプト(Windows)を多少触ったことある。
1.Node.jsをインストールする
Sassをコンパイルするにあたって、いろんな方法があると思います。自分は、「Web制作者のためのSassの教科書 これからのWebデザインの現場で必須のCSSメタ言語」という本を使用しながら環境構築したのでこの本に準拠した形で説明します。ちなみに、この本は初心者にとっても優しく書かれているのでWeb制作を行う人にはおすすめです。
まず、Sassを使えるようになるまでの大枠を逆算的に掴みます。
・C/C++ で開発されたLibSassを使えるようになる
・LibSassのライブラリであるnode-sassを使えるようにする
・node-sassを動かすためにNode.jsを使えるようにする
・Node.jsを使えるようになることでnpmコマンドが使えるようになる
・npmコマンドでgulpが使えるようになる
・gulpでSassをCSSにコンパイルできるようになる以上。
つまり、手始めにnode.sassという本体を操作するためにはNode.jsというコントローラーを手元に持ってこなくてはいけないわけですね。
このNode.jsはインストールするとnpmというパッケージ管理マネージャを使えるようになります。
Node.jsがコントローラーなら、npmはAボタンとかそういう感じでしょうか?npmコマンドは後述しますが、これによって必要な機能をインストールできたりします。と、いうわけでnode-sassを動かすためにNode.jsをインストールしましょう!
①Node.js公式サイトからインストーラーをダウンロード
2つ緑のボタンが中央にありますが、「LTS」と書かれた左のほうのボタンを押して最新のLTSをダウンロードしましょう。
②ダウンロードしたら基本的に「次へ」を進めてインストールを完了させる
これでNode.jsは無事にあなたのPCにインストールされたはずで、node.sassを操作する準備は整いました。
実際に、node.jsがインストールされているか確認してみましょう。2.Node.jsがインストールされているかターミナル/コマンドプロンプトで確認
ターミナルorコマンドを開いて、以下のコマンドを打ってバージョンが表示されるか確認
$ node -v
このようにバーションが表示されればPCにnode.jsがインストールされています!3.gulp-cliをインストールする
つぎにgulpというさまざまなタスクを自動化してくれるツール(タスクランナー)をnode.jsをインストールしたことで使えるようになったnpmコマンドでgulpをインストールしましょう。以下のコマンドを叩いてください
$npm install --global gulp-cli上記のコマンドを叩いたあとに、インストールがちゃんとされているか確認しましょう。下記のコマンドです。
$gulp -v*(余談ですが、僕はこのgulpのインストールがうまく行かず何度やっても
gulp: command not foundという表記がでて、心を折られたんですがerrorメッセージをよく見ると【管理者権限が〜】みたいなこと書かれていたので試しに
$sudo npm install --global gulp-cliと打ち直して、管理者権限を確認させるようなコマンドを叩いたら無事にうまくいきました・・・
(この時、errorメッセージはちゃんと読もうと心の底から思いました。)これで、ようやくSassを使えるようになる道具が揃いました。
4.package.jsonを作成する
①任意の場所にファイルを作って、cd コマンドで現在地に移動
僕はデスクトップに「test」というファイルを作り、
「index.html」
「style.scssが入ったsassファイル」②ターミナルを開いてpackage.jsonを作成
npm コマンドで必要なパッケージをインストールしましょう。
"cd"でディレクトリを移動させたあとに以下のコマンドを叩いてください。$npm init -yすると、package.jsonというファイルがindex.htmlらと同じ階層に生成されています
5.gulpとgulp-sassをローカル環境にインストール
package.jsonを作成したら、gulpのパッケージをnpmコマンドによってインストールさせてあげましょう。
①まずは、開発用に使うgulpパッケージをインストール
pacage.jsonに変化が出るので、package.jsonを開いておきます。
そして、以下のコマンドを叩きます。
$npm install -D gulppackage.jsonに変化が出ました。
"devDependencies":{ "gulp": "^4.0.2" }↑という項目が追加されたのが分かるでしょうか?
②続いて、gulp-sassをインストール
以下のコマンドも同様に叩きます
$npm install -D gulp-sass"gulp-sass": "^4.1.0" }これでgulpとgulp-sassのローカルインストールが終了しました。
6.node_modulesとpackage-lock.jsonを作成
5でローカルにgulpとgulp-sassをインストールすると、「node_modules」と「package-lock.json」が自動的に生成されています。
7.gulpfile.jsをpackage.jsonと同じ階層に作成(自作)
それでは、さいごにgulpfile.jsを作成していきましょう。
package.jsonと同じ階層に、
gulpfile.jsを作成します。
gulpfile.jsの中身を書いていきましょう。"use strict"; var gulp = require("gulp"); var sass = require("gulp-sass"); sass.compiler = require("node-sass"); gulp.task("sass", function () { return gulp .src("./sass/**/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(sass({ outputStyle: "expanded" })) .pipe(gulp.dest("./css")); }); gulp.task("sass:watch", function () { gulp.watch("./sass/**/*.scss", gulp.task("sass")); });こちらがデフォルトになります。ですが、次々にgulpのタスクを追加することもできますのでこちらの記事を参考にしてください。
【覚えるべき技5選】Web制作者のためのSassの教科書【備忘録】8.Sassをコンパイル
これで準備は一通りおしまいです!
それでは実際にSassを記述してみて、ちゃんとCSSにコンパイルのか確認してみましょう。
使うコマンドはgulp sass:watch"watch"をつけると、ファイルの更新を監視してくれる。つまり、Sassファイルが更新されると自動的にコンパイルしてくれます。
watchを終了させるには、Ctrl + Cで停止します。まず、Sassで記述してみます。Sassの書き方は以前書いたこの記事を参考に・・・
Sassについて最低限知っておきたい5つの基本機能
ここで"gulp sass:watch"をターミナルで叩きます。
すると、CSSというファイルが新しくできていて⬇
style.cssにうまくコンパイルされています
お疲れさまでした!
[追記] カスタムされたgulpfile.jsの作成
セクション7では、デフォルトのgulpfile.jsを作成しましたが、Sassの他の機能を使うためにはカスタマイズが必要です。
まずは、パッケージをインストールします。
$ npm install gulp gulp-sass gulp-sass-glob gulp-sourcemaps gulp-plumber gulp-notify gulp-postcss autoprefixer css-declaration-sorter css-mqpacker -Dgulpfile.jsを以下のように記述します。
var gulp = require('gulp'); var sass = require('gulp-sass'); var sassGlob = require('gulp-sass-glob'); var plumber = require('gulp-plumber'); var notify = require("gulp-notify"); var postcss = require('gulp-postcss'); var autoprefixer = require('autoprefixer'); var cssdeclsort = require('css-declaration-sorter'); var mqpacker = require('css-mqpacker'); var browserSync = require('browser-sync'); gulp.task('sass', function() { return gulp.src('./sass/**/*.scss') .pipe(plumber({errorHandler: notify.onError("Error:<%= error.message %>")})) .pipe(sassGlob()) .pipe(sass({outputStyle: 'expanded'})) .pipe(postcss([mqpacker()])) .pipe(postcss([cssdeclsort({order: 'alphabetical'})])) .pipe(postcss([autoprefixer()])) .pipe(gulp.dest('./css')); }); gulp.task( 'default', function() { gulp.watch( './sass/**/*.scss', gulp.task('sass')); });ここでは"gulp sass:watch"ではなく、"gulp" のみで自動コンパイルを行ってくれます。
以上です。
- 投稿日:2020-07-22T16:06:59+09:00
[AWS]Slack + AWS Chatbot + Lambdaで朝会のファシリテーター指名してみた
現職場では、デイリースタンドアップとして、朝会を実施している。
そして、いつも開始日時になると、「え、今日・・・司会担当・・・だれ・・・?」
とTheWordになる。
※TheWord ・・・ リモート会議において、 無音が続き、最初に話した人がファシリテーターをしなければいけない空間のこと一応、SlackBotを使用して、それとなく機械的にファシリテーターを指名しているのだが、
「この人お休みだよ〜」 や 「昨日もやったんですけど〜」 とか
いちいち面倒臭い。
よし。朝会のファシリテーターをいい感じ(前回当たった人は当たらない、スムーズに再抽選ができる、
自分が選ばれない)に決めるToolを作ろう。はじめに
コミュニケーションToolはSlack
プラットフォームはAWSとする。構成
朝会の担当者をランダムに選択し、Slackに通知するTool。
実行トリガーは2種類存在する。
1. CloudWathEventsから定期的(朝会の始まる5分前)に実行
2. Slack→AWS ChatBotから実行DynamoDBではファシリテーター担当履歴を管理。
(現時点では直近の担当者しか保存していない。)構築
AWS
ChatBot以外はCloudFormationで構築。
IAM等のユーザーは事前に発行しておくこと。
ソースコードLambda
ソース
Node.jsで実装。
コード全体とは以下の通り。index.js'use strict' const requestPromise = require('request-promise') const AWS = require('aws-sdk') const dynamodb = new AWS.DynamoDB.DocumentClient({ region: 'ap-northeast-1' }) let slackPostOption = { url: 'https://slack.com/api/chat.postMessage', method: 'POST', qs: { token: process.env.SLACK_TOKEN, channel: process.env.SLACK_CHANNEL, text: '', username: 'ぼくのいうことはぜったい' }, json: true } exports.handler = async () => { return new Promise((resolve, reject) => { Promise.resolve() .then(() => { // 直近のファシリテーターを取得 return getLatestFacilitator() }) .then((latestFacilitator) => { // ランダムに取得(直近を除く) return getFacilitator(latestFacilitator) }) .then((facilitator) => { // 直近のファシリテーターを登録 return putLatestFacilitator(facilitator) }) .then((facilitator) => { // Slackに通知 return postSlack(facilitator) }) .then(() => { resolve('Finish') }) .catch(reject) }) } const getLatestFacilitator = () => { const param = { TableName: 'FacilitatorHistory', Key: { status: 'latest' } } return new Promise((resolve, reject) => { dynamodb.get(param, (err, data) => { if (err) reject(err) resolve(data.Item ? data.Item.member : '') }) }) } const getFacilitator = (latestFacilitator) => { return new Promise((resolve, reject) => { const memberList = process.env.MEMBER.split(',') const get = () => { const facilitator = memberList[Math.floor(Math.random() * memberList.length)] if (latestFacilitator == facilitator) { get() return } resolve(facilitator) } get() }) } const putLatestFacilitator = (facilitator) => { return new Promise((resolve, reject) => { var param = { TableName: 'FacilitatorHistory', Item:{ status: 'latest', member: facilitator } } dynamodb.put(param, (err, data) => { if (err) reject(err) resolve(facilitator) }) }) } const postSlack = (facilitator) => { return new Promise((resolve, reject) => { slackPostOption.qs.text = `きょうのあさかいは <${facilitator}> だ。` requestPromise(slackPostOption) .then(resolve) .catch(reject) }) }内容
本来なら並列にできる部分もあるが、わかりやすく直列化している。
流れとしては
1. 直近のファシリテーターを取得
2. ファシリテーターをランダムに取得(直近のファシリテーターは除く)
3. Slackに担当者を通知
4. 2.で選ばれたファシリテーターを登録
といったシンプルなもの。
2.のファシリテーター取得ロジック部分で担当者ごとに重みをつけて、選ばれやすさを制御したり、自身だけ選ばれづらくするなどのイカサマはできそう。CloudFormation
yml
一部内容がベタが記されている部分があるので、使用する際には修正する必要がある。
template.ymlAWSTemplateFormatVersion: 2010-09-09 Resources: MorningFacilitatorFunction: Type: AWS::Lambda::Function Properties: Code: ./release/app.zip FunctionName: MorningFacilitatorFunction Handler: index.handler Runtime: nodejs12.x # Lambdaの実行ロール Role: {lambda arn} MemorySize: 128 Timeout: 30 Environment: Variables: TZ: Asia/Tokyo # カンマ区切りでファシリテーター候補のSlackユーザーIDを定義 MEMBER: '@yamada,@yamamoto,@yamashita,@yamagami,@yamsuda' # 各種Slackの情報 SLACK_TOKEN: {slack api token} SLACK_CHANNEL: {slack channel} FacilitatorHistory: Type: AWS::DynamoDB::Table Properties: TableName: FacilitatorHistory AttributeDefinitions: - AttributeName: status AttributeType: S KeySchema: - AttributeName: status KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 3 WriteCapacityUnits: 3 FacilitatorEventsRule: Type: AWS::Events::Rule Properties: Name: FacilitatorEventsRule # 朝会開始時間5分前(GMTなのでJST変換すると+9時間) ScheduleExpression: cron(55 0 * * ? *) State: ENABLED Targets: - Arn: !GetAtt MorningFacilitatorFunction.Arn Id: lambda MorningFacilitatorPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref MorningFacilitatorFunction Principal: events.amazonaws.com SourceArn: !GetAtt FacilitatorEventsRule.ArnCLI
デプロイする際は、AWS CLIを使用。
毎回入力するのは面倒なため、shを作成deploy.sh#!/bin/sh # ソースコードをアーカイブ rm -fr release && mkdir release zip -r app.zip index.js node_modules > /dev/null 2>&1 mv app.zip release/ # 必要な情報はベタがきするか、shの引数で対応 AWS_IAM_USER_NAME=$1 AWS_ACCESS_KEY_ID=$2 AWS_SECRET_ACCESS_KEY=$3 AWS_DEFAULT_REGION=$4 AWS_S3_BUCKET=$5 AWS_STACK=$6 aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile $AWS_IAM_USER_NAME aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile $AWS_IAM_USER_NAME aws configure set region $AWS_DEFAULT_REGION --profile $AWS_IAM_USER_NAME aws cloudformation package --template-file template.yml --s3-bucket $AWS_S3_BUCKET --s3-prefix `date '+%Y%m%d%H%M%S'` --output-template-file output.yml --profile $AWS_IAM_USER_NAME aws cloudformation deploy --region $AWS_DEFAULT_REGION --template-file output.yml --stack-name $AWS_STACK --profile $AWS_IAM_USER_NAMEAWS Chatbot
クライアントの設定
AWS Chatbotのコンソールを開き、チャットクライアント「Slack」を選択し、クライアントを設定を押下。
チャンネルの設定
以上でAWS側の設定は完了。
Slack
SlackからLambda実行
メンバーが揃っているチャンネルで、@awsさんをインバイトする。
/invite @aws
このように表示されればOK。
SlackからLambdaの実行。
@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1
すると、ファシリテーターが指名される。
ただ、毎回このようなコマンドを入力するのは効率が悪いので、Slackワークフロー登録する。
Slackワークフロー
作成を押下して、適当な名前を入力。
※ 伸ばし棒が入らない・・・
ショートカットを選択、作成。
チャンネル名はメンバーが揃っているチャンネルを選択。
メッセージの送信先に「ワークフローを開始したチャンネル」
メッセージテキストに@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1
を入力。
これでワークフロー登録完了。
運用してみよう
カタカタカタ...
「...ふぅ。」
「おっとそろそろ朝会だなぁ。」
「今日の担当は・・・?」
「病弱太郎か。あれ?あいつ今日病気で休みだったよな・・」
「しょうがない。選ぶか。」
「ショートカットから...えい」
「ほうほう。朝会大好き子か。よしよし。今日の仕事を頑張ろう」
まとめ
正直、AWSでやるにはオーバースペック感が否めない。笑
とわ言え、
- 朝会前のTheWordが少なくなる(かも?)
- 興味のあったAWS Chatbotをさわれた
のでよかったかと。
AWS Chatbotは触ってみて、無限の可能性を感じた。
SlackトリガーでAWSのServiceを使用できるので、CI/CDを全てAWS上で構築することができれば、SlackOnlyで業務が回りそう。
- 投稿日:2020-07-22T16:06:59+09:00
[AWS ChatBot]朝会のファシリテーターを決めて!
現職場では、デイリースタンドアップとして、朝会を実施している。
そして、いつも開始日時になると、「え、今日・・・司会担当・・・だれ・・・?」
とTheWordになる。
※TheWord ・・・ リモート会議において、 無音が続き、最初に話した人がファシリテーターをしなければいけない空間のこと一応、SlackBotを使用して、それとなく機械的にファシリテーターを指名しているのだが、
「この人お休みだよ〜」 や 「昨日もやったんですけど〜」 とか
いちいち面倒臭い。
よし。朝会のファシリテーターをいい感じ(前回当たった人は当たらない、スムーズに再抽選ができる、
自分が選ばれない)に決めるToolを作ろう。はじめに
コミュニケーションToolはSlack
プラットフォームはAWSとする。構成
朝会の担当者をランダムに選択し、Slackに通知するTool。
実行トリガーは2種類存在する。
1. CloudWathEventsから定期的(朝会の始まる5分前)に実行
2. Slack→AWS ChatBotから実行DynamoDBではファシリテーター担当履歴を管理。
(現時点では直近の担当者しか保存していない。)構築
AWS
ChatBot以外はCloudFormationで構築。
IAM等のユーザーは事前に発行しておくこと。
ソースコードLambda
ソース
Node.jsで実装。
コード全体とは以下の通り。index.js'use strict' const requestPromise = require('request-promise') const AWS = require('aws-sdk') const dynamodb = new AWS.DynamoDB.DocumentClient({ region: 'ap-northeast-1' }) let slackPostOption = { url: 'https://slack.com/api/chat.postMessage', method: 'POST', qs: { token: process.env.SLACK_TOKEN, channel: process.env.SLACK_CHANNEL, text: '', username: 'ぼくのいうことはぜったい' }, json: true } exports.handler = async () => { return new Promise((resolve, reject) => { Promise.resolve() .then(() => { // 直近のファシリテーターを取得 return getLatestFacilitator() }) .then((latestFacilitator) => { // ランダムに取得(直近を除く) return getFacilitator(latestFacilitator) }) .then((facilitator) => { // 直近のファシリテーターを登録 return putLatestFacilitator(facilitator) }) .then((facilitator) => { // Slackに通知 return postSlack(facilitator) }) .then(() => { resolve('Finish') }) .catch(reject) }) } const getLatestFacilitator = () => { const param = { TableName: 'FacilitatorHistory', Key: { status: 'latest' } } return new Promise((resolve, reject) => { dynamodb.get(param, (err, data) => { if (err) reject(err) resolve(data.Item ? data.Item.member : '') }) }) } const getFacilitator = (latestFacilitator) => { return new Promise((resolve, reject) => { const memberList = process.env.MEMBER.split(',') const get = () => { const facilitator = memberList[Math.floor(Math.random() * memberList.length)] if (latestFacilitator == facilitator) { get() return } resolve(facilitator) } get() }) } const putLatestFacilitator = (facilitator) => { return new Promise((resolve, reject) => { var param = { TableName: 'FacilitatorHistory', Item:{ status: 'latest', member: facilitator } } dynamodb.put(param, (err, data) => { if (err) reject(err) resolve(facilitator) }) }) } const postSlack = (facilitator) => { return new Promise((resolve, reject) => { slackPostOption.qs.text = `きょうのあさかいは <${facilitator}> だ。` requestPromise(slackPostOption) .then(resolve) .catch(reject) }) }内容
本来なら並列にできる部分もあるが、わかりやすく直列化している。
流れとしては
1. 直近のファシリテーターを取得
2. ファシリテーターをランダムに取得(直近のファシリテーターは除く)
3. Slackに担当者を通知
4. 2.で選ばれたファシリテーターを登録
といったシンプルなもの。
2.のファシリテーター取得ロジック部分で担当者ごとに重みをつけて、選ばれやすさを制御したり、自身だけ選ばれづらくするなどのイカサマはできそう。CloudFormation
yml
一部内容がベタが記されている部分があるので、使用する際には修正する必要がある。
template.ymlAWSTemplateFormatVersion: 2010-09-09 Resources: MorningFacilitatorFunction: Type: AWS::Lambda::Function Properties: Code: ./release/app.zip FunctionName: MorningFacilitatorFunction Handler: index.handler Runtime: nodejs12.x # Lambdaの実行ロール Role: {lambda arn} MemorySize: 128 Timeout: 30 Environment: Variables: TZ: Asia/Tokyo # カンマ区切りでファシリテーター候補のSlackユーザーIDを定義 MEMBER: '@yamada,@yamamoto,@yamashita,@yamagami,@yamsuda' # 各種Slackの情報 SLACK_TOKEN: {slack api token} SLACK_CHANNEL: {slack channel} FacilitatorHistory: Type: AWS::DynamoDB::Table Properties: TableName: FacilitatorHistory AttributeDefinitions: - AttributeName: status AttributeType: S KeySchema: - AttributeName: status KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 3 WriteCapacityUnits: 3 FacilitatorEventsRule: Type: AWS::Events::Rule Properties: Name: FacilitatorEventsRule # 朝会開始時間5分前(GMTなのでJST変換すると+9時間) ScheduleExpression: cron(55 0 * * ? *) State: ENABLED Targets: - Arn: !GetAtt MorningFacilitatorFunction.Arn Id: lambda MorningFacilitatorPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref MorningFacilitatorFunction Principal: events.amazonaws.com SourceArn: !GetAtt FacilitatorEventsRule.ArnCLI
デプロイする際は、AWS CLIを使用。
毎回入力するのは面倒なため、shを作成deploy.sh#!/bin/sh # ソースコードをアーカイブ rm -fr release && mkdir release zip -r app.zip index.js node_modules > /dev/null 2>&1 mv app.zip release/ # 必要な情報はベタがきするか、shの引数で対応 AWS_IAM_USER_NAME=$1 AWS_ACCESS_KEY_ID=$2 AWS_SECRET_ACCESS_KEY=$3 AWS_DEFAULT_REGION=$4 AWS_S3_BUCKET=$5 AWS_STACK=$6 aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile $AWS_IAM_USER_NAME aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile $AWS_IAM_USER_NAME aws configure set region $AWS_DEFAULT_REGION --profile $AWS_IAM_USER_NAME aws cloudformation package --template-file template.yml --s3-bucket $AWS_S3_BUCKET --s3-prefix `date '+%Y%m%d%H%M%S'` --output-template-file output.yml --profile $AWS_IAM_USER_NAME aws cloudformation deploy --region $AWS_DEFAULT_REGION --template-file output.yml --stack-name $AWS_STACK --profile $AWS_IAM_USER_NAMEAWS Chatbot
クライアントの設定
AWS Chatbotのコンソールを開き、チャットクライアント「Slack」を選択し、クライアントを設定を押下。
チャンネルの設定
以上でAWS側の設定は完了。
Slack
SlackからLambda実行
メンバーが揃っているチャンネルで、@awsさんをインバイトする。
/invite @aws
このように表示されればOK。
SlackからLambdaの実行。
@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1
すると、ファシリテーターが指名される。
ただ、毎回このようなコマンドを入力するのは効率が悪いので、Slackワークフロー登録する。
Slackワークフロー
作成を押下して、適当な名前を入力。
※ 伸ばし棒が入らない・・・
ショートカットを選択、作成。
チャンネル名はメンバーが揃っているチャンネルを選択。
メッセージの送信先に「ワークフローを開始したチャンネル」
メッセージテキストに@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1
を入力。
これでワークフロー登録完了。
運用してみよう
カタカタカタ...
「...ふぅ。」
「おっとそろそろ朝会だなぁ。」
「今日の担当は・・・?」
「病弱太郎か。あれ?あいつ今日病気で休みだったよな・・」
「しょうがない。選ぶか。」
「ショートカットから...えい」
「ほうほう。朝会大好き子か。よしよし。今日の仕事を頑張ろう」
まとめ
正直、AWSでやるにはオーバースペック感が否めない。笑
とわ言え、
- 朝会前のTheWordが少なくなる(かも?)
- 興味のあったAWS Chatbotをさわれた
のでよかったかと。
AWS Chatbotは触ってみて、無限の可能性を感じた。
SlackトリガーでAWSのServiceを使用できるので、CI/CDを全てAWS上で構築することができれば、SlackOnlyで業務が回りそう。
- 投稿日:2020-07-22T15:10:15+09:00
[Firebase] cloudFunctions node8から10に変更する
背景
普段、SwiftとFirebaseでiOSアプリを作っている者ですが、
ある日FirebaseのCloudFunctionsのコンソールをみてみると、なにやら忠告が。。。
要は、「cloudFunctionsの関数のnodeバージョンを10にアップグレードしてね」ということ。
ではその備忘録です。お得な情報もあるのでどうぞ。
方法
①
まず、cloudFunctionsをNode.js10に対応させるには、
Firebaseの料金プランをSpark(無料)→Blaze(従量課金)に変更させる必要ありとのこと。
というわけで、Firebaseのコンソールの料金プランから料金プランをSpark(無料)→Blaze(従量課金)に変更。クレカ登録。お得な情報
ちなみに、方法①の前段階で、GCPの無料クレジットを申し込んだのでFirebase(GCP)有効期限一年間$300分の無料クレジットを受け取ることができる。
対象の人や具体的な手順についてはこちらの記事を参考にどうぞ↓
https://qiita.com/qrusadorz/items/bfb22e061bb122fbef65②
次に、functions/ ディレクトリに作成された package.json ファイルの engines フィールドにバージョンを設定します。バージョン10を使用したいので、package.json の次の行を編集。
package.json"engines": {"node": "10"}あとは、
プロジェクトのディレクトリにて以下を実行するだけ。cd functions npm run deploy結果
まとめ
・Firebaseの料金プランを従量課金制に
・cloudFunctionsのNode.js10に
・無料枠をgetすればオーバーしたとしても$300まではとりまお金かからない
- 投稿日:2020-07-22T15:10:15+09:00
[Firebase] cloudFunctions node8から10に変更する手順
背景
普段、SwiftとFirebaseでiOSアプリを作っている者ですが、
ある日FirebaseのCloudFunctionsのコンソールをみてみると、なにやら忠告が。。。
要は、「cloudFunctionsの関数のnodeバージョンを10にアップグレードしてね」ということ。
ではその備忘録です。お得な情報もあるのでどうぞ。
方法
手順①
まず、cloudFunctionsをNode.js10に対応させるには、
Firebaseの料金プランをSpark(無料)→Blaze(従量課金)に変更させる必要ありとのこと。
というわけで、Firebaseのコンソールの料金プランから料金プランをSpark(無料)→Blaze(従量課金)に変更。クレカ登録。お得な情報
ちなみに、手順①の前段階で、GCPの無料クレジットを申し込んだのでFirebase(GCP)有効期限一年間$300分の無料クレジットを受け取ることができる!!
3万ちょっとって大きいですよね〜♪対象の人や具体的な手順についてはこちらの記事を参考にどうぞ↓
https://qiita.com/qrusadorz/items/bfb22e061bb122fbef65手順②
次に、functions/ ディレクトリに作成された package.json ファイルの engines フィールドにバージョンを設定します。バージョン10を使用したいので、package.json の次の行を編集。
package.json"engines": {"node": "10"}あとは、
プロジェクトのディレクトリにて以下を実行するだけ。cd functions npm run deploy結果
まとめ
・Firebaseの料金プランを従量課金制に
・cloudFunctionsのNode.js10に
・無料枠をgetすればオーバーしたとしても$300まではとりまお金かからない
- 投稿日:2020-07-22T14:33:41+09:00
Node.js でお手軽スクレイピング 2020 年夏(cheerio版) ポエム
まずは環境から。
$ node -v v12.16.1そしてプロジェクトの初期化を行って、2 つほどライブラリをインストールします。
$ npm init -y $ npm install node-fetch cheerio --save-dev必要なライブラリが揃ったところで早速スクリプトを書いていきましょう。サンプルに気象庁の東京都の週間天気予報のページを選びました。
index.js#!/usr/bin/env node const fetch = require('node-fetch'); const cheerio = require('cheerio'); const main = async () => { const res = await fetch('https://www.jma.go.jp/jp/week/319.html'); if (res.status !== 200) { console.log(`error status:${res.status}`); return; } // jqueryチックに使えるように変換 const $ = cheerio.load(await res.text()); const nodes = $('#infotablefont tr:nth-child(4) td'); nodes.map(function (i) { console.log(nodes.eq(i).text().trim()); }); }; main();そのコードをスクリプトファイルに貼り付ければそれだけでもうスクレイピングの完成です。このスクリプトを実行すると以下のような結果が得られます。
$ ./index.js 曇時々雨 曇一時雨 曇 曇時々晴 曇時々晴 曇時々晴 曇時々晴
以上でスクリプトの解説は終わりです。
最後に、忘れてはならないのはスクレイピングは最終手段であるという事です。API が提供されているサービスであれば必ずそちらを使うべきですし、やむを得ずスクレイピングする際はサーバに過度な負荷を与えることの無いよう気をつけましょう。
参考記事
- 投稿日:2020-07-22T10:47:49+09:00
node+nodemonのdockerコンテナ
node + nodemon の開発用コンテナを作成した。実行環境はwin10とdocker-machine
. ├── app.js ├── docker-compose.yml ├── Dockerfile ├── package-lock.json ├── package.json └── node_modules#Dockerfile FROM node:12 WORKDIR /app # ホストのpackage.jsonとpackage-lock.jsonを # コンテナの/appにコピー COPY ./package*.json ./ CMD bash -c "npm install && npm run dev"docker-compose.ymlversion: '3.3' volumes: node_modules: services: app: build: . container_name: node ports: - '80:8081' restart: always volumes: # ホストのdocker-compose.ymlがあるディレクトリを # コンテナの/appマウントする - '.:/app' # ただしnode_modulesだけ除外するためnode-mudulesボリュームを # コンテナの/app/node_modulesにマウントする # コンテナ内でnpm install してもホストのnode_modulesは空のまま - 'node_modules:/app/node_modules' # 下2行はdocker run コマンドの-itオプションに相当する # 無いとコンテナが停止してしまう tty: true stdin_open: truepackage.json{ "name": "hoge", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "start": "node ./app.js", "dev": "nodemon -L ./app.js" }, "dependencies": { "express": "^4.16.1" }, "devDependencies": { "nodemon": "^2.0.4" }, "author": "", "license": "ISC" }以下のようにしてDockerfileで
npm start
するとnodemonは機能しなかった。start
キーではダメ。さらに-Lオプションが必要。"scripts": { "start": "nodemon ./app.js" }起動コマンドなど
コンテナ起動
docker-compose up -d
node_modulesボリュームは自動で作成される。project_node_modulesという名前になる。docker-machineのIPアドレスでブラウザからnodeサーバーにアクセスできる
nodemonが機能してることを確認する
docker-compose logs -f
または
docker logs node -f
コンテナ削除
docker-compose down --rmi all
--rmi all でイメージも削除される
node_modulesボリュームを削除
docker volue rm project_node_modules
ボリューム名を確認
docker volume ls
docker-machineのIPを調べる
docker-machine ls
または
docker-machine ipdocker-machine ip default
- 投稿日:2020-07-22T10:02:54+09:00
ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそBOTにしてみた。
ジェイソン・ステイサムとは?
イギリス出身のハリウッド俳優です。主にアクション映画に出演していて、代表作に「ワイルドスピード」シリーズ、「トランスポーター」シリーズなどがあります。スタントマンを使わず、自身でアクションシーンを演じることがほとんど。鍛えぬいた体が素晴らしいです。。。
(参照:https://ja.wikipedia.org/wiki/%E3%82%B8%E3%82%A7%E3%82%A4%E3%82%BD%E3%83%B3%E3%83%BB%E3%82%B9%E3%83%86%E3%82%A4%E3%82%B5%E3%83%A0 )なぜジェイソン・ステイサムのBOTを作るのか
ステイサムを好きになって10年ほど経ちました。ワイルドな顔とマッチョなボディはもちろんですが、彼の声と演技、そしてストイックなプロ意識が大好きなのです。近年その想いが加速し、やる気を出したいとき、疲れて癒されたいとき、キュンキュンしたいときなどに、ステイサムの画像を検索して、妄想するのが日課になってしまいました。
どうせなら、そんな妄想を具現化して、さらなる高みを目指したいと思い、今回LINE BOTを作ることにしました。完成デモ
(私の中のステイサムたんは、ちょっとSです。)
環境
Visual Studio Code v1.47.0
node v14.5.0
@line/bot-sdk作り方
LINE BOTアカウントを作成
「1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest」
https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d▲こちらの記事を参考に、まずLINE BOTアカウントを作成
(きゃぁ、もうこれだけでもカッコイイ!)
Node.jsでプロジェクト作成~トンネリング
先ほどの記事をまた参考にして、ngrokでトンネリングするところまで行いました!
ジェイソン・ステイサムの画像を取得する
色んなステイサムに会いたいとなると、大量の画像が必要となります。
今回のLINE BOTで一番重要なポイントと言っても過言ではないでしょう。「Google Chrome検索結果画像をコマンド使わず一括でダウンロードした話」
https://qiita.com/ka0ru19/items/64bfee2c7e904b9524d2▲こちらの記事を参考に、ステイサムの画像をGoogleの画像検索から一括ダウンロードしました。
ファイル形式がJFIFになっていたのですが、LINE BOTではjpgのみしか使えないので、
コマンドプロンプトで一括でファイル形式を変換
(参考:Windows10 拡張子を一括で変更する方法 https://petitcc.exblog.jp/26140363/ )
キレイに全部jpgになりました!画像URLも必要なので、はてなさんのサービスを使って、URLを作成しました。
https://f.hatena.ne.jp/ステイサムとの妄想をひたすら打ち込む
私が話しかけたら、画像とテキストで理想の答えが返ってくるように、
とにかく思いつくレパートリーをif文で打ち込みました。if(event.message.text.match('おはよう')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184554.jpg', previewImageUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184554.jpg', }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'おはよう。ラジオ体操の時間だ。', }) //テキスト } if(event.message.text === '痩せたい') { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081414.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081414.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: '一緒に筋トレだ' }) //テキスト } if(event.message.text === 'もうやだ') { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184654.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184654.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: '弱音を吐くな。死にたいのか?' }) //テキスト }はぁ・・・最高すぎますね。
おわりに
本当はAPI連携して、Wikiから情報引っ張ってきたり、
「本日のステイサム占い」でランダムに画像が出てくる仕様とかも試みたりしたのですが、うまくいかず…。
7月26日がステイサムの誕生日なので、それまでに完成できたらいいなと思っています。最後にとりあえず今日までに作れたところまでコード全文載せておきます!
私利私欲に満ちた記事でしたが、最後までご覧いただきありがとうございました!!!サンプルコード全文
"use strict"; const express = require("express"); const line = require("@line/bot-sdk"); const PORT = process.env.PORT || 3000; const config = { channelSecret: "チャンネルシークレットをコピペ", channelAccessToken: "アクセストークンをコピペ", }; const app = express(); app.get("/", (req, res) => res.send("Hello LINE BOT!(GET)")); //ブラウザ確認用(無くても問題ない) app.post("/webhook", line.middleware(config), (req, res) => { console.log(req.body.events); //ここのif分はdeveloper consoleの'接続確認'用なので削除して問題ないです。 if ( req.body.events[0].replyToken === "00000000000000000000000000000000" && req.body.events[1].replyToken === "ffffffffffffffffffffffffffffffff" ) { res.send("Hello LINE BOT!(POST)"); console.log("疎通確認用"); return; } Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result) ); }); const client = new line.Client(config); async function handleEvent(event) { if (event.type !== "message" || event.message.type !== "text") { return Promise.resolve(null); } if(event.message.text.match('おはよう')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184554.jpg', previewImageUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184554.jpg', }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'おはよう。ラジオ体操の時間だ。', }) //テキスト } if(event.message.text === '痩せたい') { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081414.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081414.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: '一緒に筋トレだ' }) //テキスト } if(event.message.text === 'もうやだ') { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184654.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184654.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: '弱音を吐くな。死にたいのか?' }) //テキスト } if(event.message.text.match('かっこいい')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081123.jpg', previewImageUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081123.jpg', }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'ほう。ご褒美の上裸だ。', }) //テキスト } if(event.message.text === 'トランスポーター') { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184550.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184550.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'ルール1 契約厳守\nルール2 名前は聞かない\nルール3 荷物は開けない' }) //テキスト } if(event.message.text === 'メカニック') { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721190805.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721190805.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'Victory Loves Preparation\n周到な準備が勝利を招く' }) //テキスト } if(event.message.text.match('うしろ')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081128.jpg', previewImageUrl:'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081128.jpg' }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'え?' }) //テキスト } if(event.message.text.match('好き')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081635.jpg', previewImageUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721081635.jpg', }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: '彼女いるんで', }) //テキスト } if(event.message.text.match('愛してる')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184638.jpg', previewImageUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184638.jpg', }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: '子供いるんで', }) //テキスト } if(event.message.text.match('罵って')) { await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184853.jpg', previewImageUrl: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200721/20200721184853.jpg', }); //画像 return client.replyMessage(event.replyToken, { type: 'text', text: 'うわ~なんてだらしない体してんだ、10キロ泳いでこい', }) //テキスト } } app.listen(PORT); console.log(`Server running at ${PORT}`);