20200722のNode.jsに関する記事は13件です。

1日の仕事の振り返りで使えるコーチング用LINE BOTを作ろうとした

はじめに

自社のWEBサイトのQ&Aで、LINE BOT作れたらなと考え、
とりあえず何か簡単なLINE BOTを作ってみようと思いました。

普段、人材開発の仕事に携わっているので、
自分の1日の振り返り用のLINE BOTだったら実用的かなと、
作ることにチャレンジしました。

コーチング用LINE BOTの意図

振り返りで自動化したいことは、
・月間目標のリマインド
・月間の残り日数
・振り返りの質問
・PDCAを回す時の視点
です。

そのあたりを自動化しつつ、
明日の行動をイメージできるように、
明日の天気も自動で取得できるようにしてみました。

結果できたLINE BOT

天気apiを使うまでのLINE BOTは作れた。

8619_0.jpg

"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日でやってみましたが、
思いの外できた部分と、うまくいかない部分と味わった。

これからも色々チャレンジしながら、学んでいけたらと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

簡単レシート印刷 receiptline で仮想プリンターを使ってみた

日本発のオープンソース receiptline でレシート印刷に少しずつトライしています。

先日落札したレシートプリンターは、セルフテストして投稿用に写真撮影しました。
IP アドレス設定中のため、前回利用した開発ツールを引き続き使います。
今回は仮想プリンターを使った印刷です。

01.png

ファイルのロード

receiptline に添付されているサンプルデータを開発ツールで読み込んでみましょう。
フォルダアイコンをクリックすると、ダイアログボックスが開きます。

02.png

example/data/ja/receipt.txt を選択して、OK ボタンをクリック。
キャンセルしたいときは、ダイアログボックスの外をクリックします。

03.png

編集エリアにファイルがロードされました。

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

04.png

ちなみにセーブ機能はないです。コピペでファイルに保存するしかないようです。

テスト印刷

テスト印刷は、開発ツールを Node.js で実行している必要があります。
Web ブラウザーのアドレスが http://localhost:10080 であれば OK です。

開発ツールの右上にはテスト印刷用のボタンがあります。
printer1 は印刷するレシートプリンターの ID です。

05.png

プリンター 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  ..&#xa0;...&#xa0
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

コマンドを見ても、ちょっとわからないですよね・・・

次回は落札したレシートプリンターで印刷します!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【LINE BOT】WebAPIを使用し自動返信BOTを作ってみた

はじめに

LINE BOTを利用し、六曜日とその内容を自動で返信するものを作りをました。
入力文字を替えることで、NASA(非公式)の「今日の天文学写真」を楽しむこともできます。

目的

六曜にこだわる人は少なくなってきていると思いますが、まだ大安などの吉日を好む人は多いと思います、個人的なイベントが発生すると「今日は大安だっけ?」「友引かな?」などとたまに気にすることがあるので、「今日は」と入力する今日の六曜日を自動返信するBOTを製作しました。

別で作ったほうがいいかと思いましたが、今回は練習も兼ねてで画像も取得したいと思いましたので、「今日の画像は」と入力するとNASA(非公式)より「今日の天文学写真」を取得し自動で表示します。宇宙の美しさ広さをみて、自分の小ささを感じます。

作ったもの

Image from Gyazo

構成

環境

  • 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

コード

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を活用しアウトプットしたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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として位置付けられています。

infoq.jpg

本記事では、これらトレンドの2つのOSSを組み合わせて何ができるか見てみましょう。

開発するフロー

本記事では、下のスクリーンショットの様な画像のどこに何が写っているか認識するフローを作成してみます。

flow2.png

このフローは、黄色のノード部品を用いてブラウザからファイルをアップロードすると、右下の「元の画像」のノードにアップロードした画像を表示します。同時に橙色の「画像認識」のノードにて、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」と入力しましょう。

tensorflownode.png

上の画像の様に、検索結果に今回用いるTensorFlow.jsノードが表示されるため、「インストール」ボタンをクリックしてTensorFlowノードをインストールします。インストールが完了すると、左側パレットの分析カテゴリの中に橙色のTensorFlow.jsノードが4つ登場します。

palette.png

各TensorFlow.jsノードの説明は以下の表の通りです。全て画像認識を行うノードですが、画像認識の機能以外にも認識結果付きの画像データを生成したり、エッジ分析で必要となるオフラインでの利用ができたりする等の違いがあります。

# 名前 説明 認識結果付き画像 オフライン利用
1 cocossd 画像に写っている物体名を返すノード 有り 可能
2 handpose 手の画像から指や関節の位置を推定するノード 無し 不可
3 mobilenet 画像に写っている物体名を返すノード 無し 可能
4 posenet 人物の画像から腕や頭、足の位置を推定するノード 有り 可能

その他、Node-REDで画像データを扱うために必要な以下のノードも同じ手順でインストールしておきます。

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ノードの入力端子に接続します。

flow1.png

右側のimage previewノードのみは、表示する画像データの変数を指定するため、ノードの設定を変更する必要があります。設定を変更するには、image previewノードをダブルクリックしてノードプロパティ画面を開きます。ノードプロパティ画面では、デフォルトではmsg.payloadに入っている画像データを表示する様になっています。これを以下のスクリーンショットの様に、msg.annotatedInputに変更することで、cocossdノードがアノテーションを付けた画像を表示できる様になります。

image_preview_node.png

各ノードに適切な名前を付けて右上の赤いデプロイボタンを押した後、file injectノードの左側のボタンをクリックして、ローカルPCにある空港の画像ファイルをアップロードしてみましょう。

flow2.png

「認識結果付き画像」のノードの下に機体に対して橙色のアノテーションが着いた画像が表示されました。また、右側のデバッグタブには、正しく「airplane(飛行機)」と表示されました。ぜひ手元にある画像をアップロードして、正しく認識できるか遊んでみてください。

最後に

今回は、cocossdというTensorFlow.jsノードをご紹介しました。その他のノードについてもほぼ同様のフロー作成手順で画像認識を行うことができます。本フローの応用例については、次回の記事で紹介してゆこうと思います。

※ 本記事はThe Linux FoundationのサイトLinux.comに投稿した下記記事の和訳です。
https://www.linux.com/news/using-tensorflow-js-and-node-red-with-image-recognition-applications/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初心者向け]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をダウンロードしましょう。
スクリーンショット 2020-07-22 15.38.22.png

②ダウンロードしたら基本的に「次へ」を進めてインストールを完了させる

これでNode.jsは無事にあなたのPCにインストールされたはずで、node.sassを操作する準備は整いました。
実際に、node.jsがインストールされているか確認してみましょう。

2.Node.jsがインストールされているかターミナル/コマンドプロンプトで確認

ターミナルorコマンドを開いて、以下のコマンドを打ってバージョンが表示されるか確認

$ node -v

スクリーンショット 2020-07-22 16.09.36.png
このようにバーションが表示されれば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ファイル」

の2つをVSCodeで作成しました。
スクリーンショット 2020-07-22 16.16.42.png
スクリーンショット 2020-07-22 16.17.00.png

②ターミナルを開いてpackage.jsonを作成

npm コマンドで必要なパッケージをインストールしましょう。
"cd"でディレクトリを移動させたあとに以下のコマンドを叩いてください。

$npm init -y

すると、package.jsonというファイルがindex.htmlらと同じ階層に生成されています
スクリーンショット 2020-07-22 17.07.13.png

5.gulpとgulp-sassをローカル環境にインストール

package.jsonを作成したら、gulpのパッケージをnpmコマンドによってインストールさせてあげましょう。

①まずは、開発用に使うgulpパッケージをインストール

pacage.jsonに変化が出るので、package.jsonを開いておきます。
スクリーンショット 2020-07-22 17.21.44.png

そして、以下のコマンドを叩きます。

$npm install -D gulp

package.jsonに変化が出ました。

スクリーンショット 2020-07-22 17.26.35.png

"devDependencies":{
 "gulp": "^4.0.2"
}

↑という項目が追加されたのが分かるでしょうか?

②続いて、gulp-sassをインストール

以下のコマンドも同様に叩きます

$npm install -D gulp-sass

スクリーンショット 2020-07-22 17.27.17.png
今度は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」が自動的に生成されています。
スクリーンショット 2020-07-22 18.13.03.png

7.gulpfile.jsをpackage.jsonと同じ階層に作成(自作)

それでは、さいごにgulpfile.jsを作成していきましょう。
package.jsonと同じ階層に、
スクリーンショット 2020-07-22 18.17.47.png
gulpfile.jsを作成します。
スクリーンショット 2020-07-22 18.18.07.png
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の教科書【備忘録】

スクリーンショット 2020-07-22 18.52.27.png

はい、gulpfile.jsの作成が終わりました。

8.Sassをコンパイル

これで準備は一通りおしまいです!
それでは実際にSassを記述してみて、ちゃんとCSSにコンパイルのか確認してみましょう。
使うコマンドは

gulp sass:watch

"watch"をつけると、ファイルの更新を監視してくれる。つまり、Sassファイルが更新されると自動的にコンパイルしてくれます。
watchを終了させるには、Ctrl + Cで停止します。

まず、Sassで記述してみます。Sassの書き方は以前書いたこの記事を参考に・・・
Sassについて最低限知っておきたい5つの基本機能
スクリーンショット 2020-07-22 19.02.00.png
ここで"gulp sass:watch"をターミナルで叩きます。
すると、CSSというファイルが新しくできていて⬇
スクリーンショット 2020-07-22 19.01.20.png
style.cssにうまくコンパイルされています
スクリーンショット 2020-07-22 19.07.41.png

お疲れさまでした!

[追記] カスタムされた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 -D

gulpfile.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" のみで自動コンパイルを行ってくれます。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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をダウンロードしましょう。
スクリーンショット 2020-07-22 15.38.22.png

②ダウンロードしたら基本的に「次へ」を進めてインストールを完了させる

これでNode.jsは無事にあなたのPCにインストールされたはずで、node.sassを操作する準備は整いました。
実際に、node.jsがインストールされているか確認してみましょう。

2.Node.jsがインストールされているかターミナル/コマンドプロンプトで確認

ターミナルorコマンドを開いて、以下のコマンドを打ってバージョンが表示されるか確認

$ node -v

スクリーンショット 2020-07-22 16.09.36.png
このようにバーションが表示されれば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ファイル」

の2つをVSCodeで作成しました。
スクリーンショット 2020-07-22 16.16.42.png
スクリーンショット 2020-07-22 16.17.00.png

②ターミナルを開いてpackage.jsonを作成

npm コマンドで必要なパッケージをインストールしましょう。
"cd"でディレクトリを移動させたあとに以下のコマンドを叩いてください。

$npm init -y

すると、package.jsonというファイルがindex.htmlらと同じ階層に生成されています
スクリーンショット 2020-07-22 17.07.13.png

5.gulpとgulp-sassをローカル環境にインストール

package.jsonを作成したら、gulpのパッケージをnpmコマンドによってインストールさせてあげましょう。

①まずは、開発用に使うgulpパッケージをインストール

pacage.jsonに変化が出るので、package.jsonを開いておきます。
スクリーンショット 2020-07-22 17.21.44.png

そして、以下のコマンドを叩きます。

$npm install -D gulp

package.jsonに変化が出ました。

スクリーンショット 2020-07-22 17.26.35.png

"devDependencies":{
 "gulp": "^4.0.2"
}

↑という項目が追加されたのが分かるでしょうか?

②続いて、gulp-sassをインストール

以下のコマンドも同様に叩きます

$npm install -D gulp-sass

スクリーンショット 2020-07-22 17.27.17.png
今度は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」が自動的に生成されています。
スクリーンショット 2020-07-22 18.13.03.png

7.gulpfile.jsをpackage.jsonと同じ階層に作成(自作)

それでは、さいごにgulpfile.jsを作成していきましょう。
package.jsonと同じ階層に、
スクリーンショット 2020-07-22 18.17.47.png
gulpfile.jsを作成します。
スクリーンショット 2020-07-22 18.18.07.png
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の教科書【備忘録】

スクリーンショット 2020-07-22 18.52.27.png

はい、gulpfile.jsの作成が終わりました。

8.Sassをコンパイル

これで準備は一通りおしまいです!
それでは実際にSassを記述してみて、ちゃんとCSSにコンパイルのか確認してみましょう。
使うコマンドは

gulp sass:watch

"watch"をつけると、ファイルの更新を監視してくれる。つまり、Sassファイルが更新されると自動的にコンパイルしてくれます。
watchを終了させるには、Ctrl + Cで停止します。

まず、Sassで記述してみます。Sassの書き方は以前書いたこの記事を参考に・・・
Sassについて最低限知っておきたい5つの基本機能
スクリーンショット 2020-07-22 19.02.00.png
ここで"gulp sass:watch"をターミナルで叩きます。
すると、CSSというファイルが新しくできていて⬇
スクリーンショット 2020-07-22 19.01.20.png
style.cssにうまくコンパイルされています
スクリーンショット 2020-07-22 19.07.41.png

お疲れさまでした!

[追記] カスタムされた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 -D

gulpfile.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" のみで自動コンパイルを行ってくれます。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS]Slack + AWS Chatbot + Lambdaで朝会のファシリテーター指名してみた

現職場では、デイリースタンドアップとして、朝会を実施している。
そして、いつも開始日時になると、

「え、今日・・・司会担当・・・だれ・・・?」

とTheWordになる。
※TheWord ・・・ リモート会議において、 無音が続き、最初に話した人がファシリテーターをしなければいけない空間のこと

一応、SlackBotを使用して、それとなく機械的にファシリテーターを指名しているのだが、

「この人お休みだよ〜」 や 「昨日もやったんですけど〜」 とか

いちいち面倒臭い。

よし。朝会のファシリテーターをいい感じ(前回当たった人は当たらない、スムーズに再抽選ができる、自分が選ばれない)に決めるToolを作ろう。

はじめに

コミュニケーションToolはSlack
プラットフォームはAWSとする。

構成

全体構成はこんな感じ。
構成.png

朝会の担当者をランダムに選択し、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.yml
AWSTemplateFormatVersion: 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.Arn

CLI

デプロイする際は、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_NAME

AWS Chatbot

クライアントの設定

AWS Chatbotのコンソールを開き、チャットクライアント「Slack」を選択し、クライアントを設定を押下。
スクリーンショット 2020-07-22 14.55.16.png

権限を求められるので、許可する。
スクリーンショット 2020-07-22 14.56.05.png

チャンネルの設定

新しいチャンネルを設定。
スクリーンショット 2020-07-22 14.58.40.png

各種任意の値を入力し登録。
screencapture-us-east-2-console-aws-amazon-chatbot-home-2020-07-22-15_02_56.png

以上でAWS側の設定は完了。

Slack

SlackからLambda実行

メンバーが揃っているチャンネルで、@awsさんをインバイトする。

/invite @aws

このように表示されればOK。
スクリーンショット 2020-07-22 15.22.48.png

SlackからLambdaの実行。

@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1

すると、ファシリテーターが指名される。
スクリーンショット 2020-07-22 15.27.03.png

ただ、毎回このようなコマンドを入力するのは効率が悪いので、Slackワークフロー登録する。

Slackワークフロー

ワークフロービルダーを開く。
スクリーンショット 2020-07-22 15.31.16.png

作成を押下して、適当な名前を入力。
※ 伸ばし棒が入らない・・・
スクリーンショット 2020-07-22 15.32.36.png

ショートカットを選択、作成。
チャンネル名はメンバーが揃っているチャンネルを選択。
スクリーンショット 2020-07-22 15.32.49.png
スクリーンショット 2020-07-22 15.33.25.png

ステップを追加し、メッセージを送信を選択。
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330303433302f62646265323338632d656633302d373332392d306630352d3337386435383331643434662e706e67.png
スクリーンショット 2020-07-22 15.33.46.png

メッセージの送信先に「ワークフローを開始したチャンネル」
メッセージテキストに@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1を入力。
スクリーンショット 2020-07-22 15.34.16.png

保存、そして公開。
スクリーンショット 2020-07-22 15.34.23.png

これでワークフロー登録完了。

ショートカットから起動することが可能。
スクリーンショット 2020-07-22 15.45.44.png

運用してみよう

カタカタカタ...

「...ふぅ。」

「おっとそろそろ朝会だなぁ。」

「今日の担当は・・・?」

スクリーンショット 2020-07-22 15.51.15.png

「病弱太郎か。あれ?あいつ今日病気で休みだったよな・・」

「しょうがない。選ぶか。」

「ショートカットから...えい」

スクリーンショット 2020-07-22 15.55.32.png

「ほうほう。朝会大好き子か。よしよし。今日の仕事を頑張ろう」

まとめ

正直、AWSでやるにはオーバースペック感が否めない。笑
とわ言え、

  • 朝会前のTheWordが少なくなる(かも?)
  • 興味のあったAWS Chatbotをさわれた

のでよかったかと。

AWS Chatbotは触ってみて、無限の可能性を感じた。
SlackトリガーでAWSのServiceを使用できるので、CI/CDを全てAWS上で構築することができれば、SlackOnlyで業務が回りそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS ChatBot]朝会のファシリテーターを決めて!

現職場では、デイリースタンドアップとして、朝会を実施している。
そして、いつも開始日時になると、

「え、今日・・・司会担当・・・だれ・・・?」

とTheWordになる。
※TheWord ・・・ リモート会議において、 無音が続き、最初に話した人がファシリテーターをしなければいけない空間のこと

一応、SlackBotを使用して、それとなく機械的にファシリテーターを指名しているのだが、

「この人お休みだよ〜」 や 「昨日もやったんですけど〜」 とか

いちいち面倒臭い。

よし。朝会のファシリテーターをいい感じ(前回当たった人は当たらない、スムーズに再抽選ができる、自分が選ばれない)に決めるToolを作ろう。

はじめに

コミュニケーションToolはSlack
プラットフォームはAWSとする。

構成

全体構成はこんな感じ。
構成.png

朝会の担当者をランダムに選択し、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.yml
AWSTemplateFormatVersion: 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.Arn

CLI

デプロイする際は、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_NAME

AWS Chatbot

クライアントの設定

AWS Chatbotのコンソールを開き、チャットクライアント「Slack」を選択し、クライアントを設定を押下。
スクリーンショット 2020-07-22 14.55.16.png

権限を求められるので、許可する。
スクリーンショット 2020-07-22 14.56.05.png

チャンネルの設定

新しいチャンネルを設定。
スクリーンショット 2020-07-22 14.58.40.png

各種任意の値を入力し登録。
screencapture-us-east-2-console-aws-amazon-chatbot-home-2020-07-22-15_02_56.png

以上でAWS側の設定は完了。

Slack

SlackからLambda実行

メンバーが揃っているチャンネルで、@awsさんをインバイトする。

/invite @aws

このように表示されればOK。
スクリーンショット 2020-07-22 15.22.48.png

SlackからLambdaの実行。

@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1

すると、ファシリテーターが指名される。
スクリーンショット 2020-07-22 15.27.03.png

ただ、毎回このようなコマンドを入力するのは効率が悪いので、Slackワークフロー登録する。

Slackワークフロー

ワークフロービルダーを開く。
スクリーンショット 2020-07-22 15.31.16.png

作成を押下して、適当な名前を入力。
※ 伸ばし棒が入らない・・・
スクリーンショット 2020-07-22 15.32.36.png

ショートカットを選択、作成。
チャンネル名はメンバーが揃っているチャンネルを選択。
スクリーンショット 2020-07-22 15.32.49.png
スクリーンショット 2020-07-22 15.33.25.png

ステップを追加し、メッセージを送信を選択。
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330303433302f62646265323338632d656633302d373332392d306630352d3337386435383331643434662e706e67.png
スクリーンショット 2020-07-22 15.33.46.png

メッセージの送信先に「ワークフローを開始したチャンネル」
メッセージテキストに@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1を入力。
スクリーンショット 2020-07-22 15.34.16.png

保存、そして公開。
スクリーンショット 2020-07-22 15.34.23.png

これでワークフロー登録完了。

ショートカットから起動することが可能。
スクリーンショット 2020-07-22 15.45.44.png

運用してみよう

カタカタカタ...

「...ふぅ。」

「おっとそろそろ朝会だなぁ。」

「今日の担当は・・・?」

スクリーンショット 2020-07-22 15.51.15.png

「病弱太郎か。あれ?あいつ今日病気で休みだったよな・・」

「しょうがない。選ぶか。」

「ショートカットから...えい」

スクリーンショット 2020-07-22 15.55.32.png

「ほうほう。朝会大好き子か。よしよし。今日の仕事を頑張ろう」

まとめ

正直、AWSでやるにはオーバースペック感が否めない。笑
とわ言え、

  • 朝会前のTheWordが少なくなる(かも?)
  • 興味のあったAWS Chatbotをさわれた

のでよかったかと。

AWS Chatbotは触ってみて、無限の可能性を感じた。
SlackトリガーでAWSのServiceを使用できるので、CI/CDを全てAWS上で構築することができれば、SlackOnlyで業務が回りそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Firebase] cloudFunctions node8から10に変更する

背景

普段、SwiftとFirebaseでiOSアプリを作っている者ですが、
ある日FirebaseのCloudFunctionsのコンソールをみてみると、なにやら忠告が。。。
pic1.png

要は、「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

結果

バージョンアップに成功(画像参照↓)。
pic2.png

まとめ

・Firebaseの料金プランを従量課金制に
・cloudFunctionsのNode.js10に
・無料枠をgetすればオーバーしたとしても$300まではとりまお金かからない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Firebase] cloudFunctions node8から10に変更する手順

背景

普段、SwiftとFirebaseでiOSアプリを作っている者ですが、
ある日FirebaseのCloudFunctionsのコンソールをみてみると、なにやら忠告が。。。
pic1.png

要は、「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

結果

バージョンアップに成功(画像参照↓)。
pic2.png

まとめ

・Firebaseの料金プランを従量課金制に
・cloudFunctionsのNode.js10に
・無料枠をgetすればオーバーしたとしても$300まではとりまお金かからない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 が提供されているサービスであれば必ずそちらを使うべきですし、やむを得ずスクレイピングする際はサーバに過度な負荷を与えることの無いよう気をつけましょう。

参考記事

https://qiita.com/otchy/items/244c19c561ecb7211fa5

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.yml
version: '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: true
package.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 ip
docker-machine ip default
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそ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を作ることにしました。

完成デモ

Image from Gyazo

(私の中のステイサムたんは、ちょっと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アカウントを作成
linebot.png

(きゃぁ、もうこれだけでもカッコイイ!)

Node.jsでプロジェクト作成~トンネリング

先ほどの記事をまた参考にして、ngrokでトンネリングするところまで行いました!

ジェイソン・ステイサムの画像を取得する

色んなステイサムに会いたいとなると、大量の画像が必要となります。
今回のLINE BOTで一番重要なポイントと言っても過言ではないでしょう。

「Google Chrome検索結果画像をコマンド使わず一括でダウンロードした話」
https://qiita.com/ka0ru19/items/64bfee2c7e904b9524d2

▲こちらの記事を参考に、ステイサムの画像をGoogleの画像検索から一括ダウンロードしました。
jfif.png

ファイル形式がJFIFになっていたのですが、LINE BOTではjpgのみしか使えないので、
コマンドプロンプトで一括でファイル形式を変換
コマンドプロンプト.png
(参考:Windows10 拡張子を一括で変更する方法 https://petitcc.exblog.jp/26140363/
jpg.png
キレイに全部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: '弱音を吐くな。死にたいのか?'
          }) //テキスト

      }

Image from Gyazo

はぁ・・・最高すぎますね。

おわりに

本当は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}`);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む