ギニュー特戦隊に入りたい
皆さんも(特に男性なら)、人生で一度はギニュー特戦隊に入隊したいと思いましたよね?
今回はその願いを少しでも叶えるべく、次のようなボットを作成しました。
* まさかそんな人はいないと思いますが、ギニュー特戦隊を知らない方はこちらのwikipediaをご確認ください。
ギニュー特戦隊識別bot完成!
— 北城雅照@足立慶友整形外科 (@kutuyanomusuko) November 27, 2020
僕はリクームでしたwww#protoout pic.twitter.com/52wTNdbfTu
早速使ってみたい方は、一番下にQRコードを貼っておきますので、ご利用ください。
ちなみに、万が一、リクームとジース以外の方に似ていると判定されたら、その方は人間(ホモ・サピエンス)という概念を超えている方ですので、すぐにご連絡ください!
開発環境の下準備
1) VScodeのインストール
VScodeのインストールついては、googleなどで他の記事を検索してください。
2) node.jsとnpmのインストール
Macでの環境作りは、こちらの別の記事にまとめてあります。
参考にしてください。
Macにnode.js,npmのインストール方法
開発環境の準備
1) ginyuforceというフォルダを作成
2) VSCodeで上記フォルダを開き、フォルダ内にginyuforce.jsを作成
3) VSCode内でターミナルを開き、フォルダをnpm管理できるように初期化
$ npm init -y
Teachable Machineによる画像認識AIの作成
以前の記事(誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。)では画像判定AIをAIメーカーで作成したので、今回はTeachable Machineを使用し、画像判定AIを作成しました。
* 使い方については、こちらの記事>【備忘録】Teachable Machineの利用方法が参考になりました。ぜひ一度お読みください。(よかったらLGTMをお願いします。)
ginyuforce.jsのコード
'use strict'; // おまじない
const { JSDOM } = require('jsdom');
var dom = new JSDOM('');
global.document = dom.window.document;
global.HTMLVideoElement = dom.window.HTMLVideoElement;
const canvas = require('canvas');
global.fetch = require('node-fetch');
const tmImage = require('@teachablemachine/image');
const express = require('express');
const line = require('@line/bot-sdk');
const fs = require('fs');
const PORT = process.env.PORT || 3000;
const config = {
channelSecret: '自身が作成したLINEbotのシークレットトークンを入力',
channelAccessToken: '自身が作成したLINEbotのアクセストークンを入力',
};
// Teachable Machine
let model;
// https://teachablemachine.withgoogle.com/
// ここでエクスポート、クラウドにモデルをアップロードした後に取得できる
const URL = 'https://teachablemachine.withgoogle.com/models/ovKT5Lvf_/';
// ########################################
// Teachable Machineを使って画像分類をする部分
// ########################################
// 初期化が時間かかるので、node立ち上げ時に行うようにする
async function initTeachableMachine() {
const modelURL = URL + 'model.json';
const metadataURL = URL + 'metadata.json';
// モデルデータのロード
model = await tmImage.load(modelURL, metadataURL);
// クラスのリストを取得
// const classes = model.getClassLabels();
// console.log(classes);
}
initTeachableMachine();
async function predict(imgPath) {
// canvasに画像をロードする
const image = await canvas.loadImage(imgPath);
// 判定する
const predictions = await model.predict(image);
// 一番近いもの順でソート
predictions.sort((a, b) => {
return b.probability - a.probability;
});
return predictions;
}
// ########################################
// LINEサーバーからのWebhookデータを処理する部分
// ########################################
// LINE SDKを初期化します
const client = new line.Client(config);
// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
// 受信したWebhookが「画像以外」であればnullを返すことで無視します
if (event.message.type === 'image') {
console.log("画像が送られてきた");
// 画像を保存
const downloadPath = './01.png'; //左記の名前で使用中のフォルダ内に送信された画像が保存される
const getContent = await downloadContent(event.message.id, downloadPath);
const result = await predict(downloadPath);
// AIメーカーAPIの結果から、返信するメッセージを組み立てる
let text = '';
let name = '';
name = result[0].className
// 判定結果をテキストに代入
text = 'あなたは『' + name + "』に最も似ています!";
// これまでの結果を確認するためにコンソールに表示
console.log(result);
console.log(name);
console.log(text);
// 判定結果に応じた画像を送信
if(name === 'ジース'){
return client.replyMessage(event.replyToken, [{
type: 'text',
text: text
},{
type: 'image',
originalContentUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/gisu.jpg',
previewImageUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/gisu.jpg'
}]);
}else if(name === 'リクーム'){
return client.replyMessage(event.replyToken,[{
type: 'text',
text: text
},{
type: 'image',
originalContentUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/rikumu.jpg',
previewImageUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/rikumu.jpg'
}]);
}else if(name === 'グルド'){
return client.replyMessage(event.replyToken, [{
type: 'text',
text: text
},{
type: 'image',
originalContentUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/gurudo.jpg',
previewImageUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/gurudo.jpg'
}]);
}else if(name === 'バータ'){
return client.replyMessage(event.replyToken, [{
type: 'text',
text: text
},{
type: 'image',
originalContentUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/bata.jpg',
previewImageUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/bata.jpg'
}]);
}else if(name === 'ギニュー隊長'){
return client.replyMessage(event.replyToken, [{
type: 'text',
text: text
},{
type: 'image',
originalContentUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/ginyu.jpg',
previewImageUrl: 'https://sleepy-mirzakhani-f338f8.netlify.app/ginyu.jpg'
}]);
}
}
// 「テキストメッセージ」であれば、受信したテキストをそのまま返事します
if (event.message.type === 'text') {
return client.replyMessage(event.replyToken, {
type: 'text',
text: event.message.text // ← ここに入れた言葉が実際に返信されます
});
}
}
// ########################################
// LINEで送られた画像を保存する部分
// ########################################
function downloadContent(messageId, downloadPath) {
const data = [];
return client.getMessageContent(messageId)
.then((stream) => new Promise((resolve, reject) => {
const writable = fs.createWriteStream(downloadPath);
stream.on('data', (chunk) => data.push(Buffer.from(chunk)));
stream.pipe(writable);
stream.on('end', () => resolve(Buffer.concat(data)));
stream.on('error', reject);
}));
}
// ########################################
// Expressによるサーバー部分
// ########################################
// expressを初期化します
const app = express();
// HTTP GETによって '/' のパスにアクセスがあったときに 'Hello LINE BOT! (HTTP GET)' と返事します
// これはMessaging APIとは関係のない確認用のものです
app.get('/', (req, res) => res.send('<h1>Hello LINE BOT! (HTTP GET)</h1>'));
// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {
// Webhookの中身を確認用にターミナルに表示します
console.log(req.body.events);
// 空っぽの場合、検証ボタンをクリックしたときに飛んできた"接続確認"用
// 削除しても問題ありません
if (req.body.events.length == 0) {
res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します
console.log('検証イベントを受信しました!'); // ターミナルに表示します
return; // これより下は実行されません
}
// あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
// 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});
// 最初に決めたポート番号でサーバーをPC内だけに公開します
// (環境によってはローカルネットワーク内にも公開されます)
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
実行に必要なパッケージのインストルール
下記のコードを入力し、実行に必要なパッケージを入力する。
- Teachable Machineを動かすのに必要なパッケージ
$ npm i @line/bot-sdk express
1) LINEbotを動かすのに必要なパッケージ
$ npm i @teachablemachine/image @tensorflow/tfjs canvas jsdom
#Herokuへのデプロイ
node.jsが動作する無料のクラウドサーバーであるHerokuにデプロイします。
下準備
1) Heroku CLIのインストール
https://devcenter.heroku.com/articles/heroku-cli
2) Herokuアカウントの作成
https://signup.heroku.com/
デプロイ
ターミナルで下記コマンドを実行します。
$ heroku login
実行するとブラウザが自動的に起動するので、ログインします。
すると、ターミナル上でもログインが完了します。
その後、package.jsonの"scripts"の中に、"start"を下記のように追記し保存します。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node ginyuforce.js"
},
Heroku内にデプロイ先のディレクトリを作成します。
下記の順でターミナルにコマンドを入力します。
$ git init
$ heroku create 名付けたいデプロイ先の名前 //付けたい名前がなければ入力なしでもOK
Heroku内にデプロイ先のディレクトリが作成されると、ターミナルに下記のように表示されます。
Creating app... done, ⬢ デプロイ先の名前
https://デプロイ先の名前.com/ | https://git.heroku.com/デプロイ先の名前.git
このデプロイ先ディレクトリに、作成したディレクトリを入れていきます。
下記の順でターミナルにコマンドを入力します。
$ git add .
$ git commit -m 'init'
$ git push heroku master
最終的に下記のように表示されれば完了です。
remote: https://デプロイ先の名前.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/デプロイ先の名前.git
* [new branch] master -> master
これでデプロイ完了です。
ファイルを更新する時
まず、更新するファイルが入ったディレクトリがherokuのデプロイ先のディレクトリと紐づいているかを確認します。
$ git remote -v
紐づいている場合
紐づいている場合は、ターミナルに下記のように表記されます。
heroku https://git.heroku.com/heroku上のデプロイ先の名前 (fetch)
heroku https://git.heroku.com/heroku上のデプロイ先の名前 (push)
紐づいていない場合
紐づいていない場合は、ターミナルに下記のように表記されます。
fatal: not a git repository (or any of the parent directories): .git
その場合は、ターミナルに下記の順に入力し、空のディレクトリを作成し、heroku上のデプロイしたいディレクトリと紐付けを行います。
$ git init //空のディレクトリを作成
$ heroku git:remote -a heroku上のデプロイしたいディレクトリ名
//herokuのデプロイ先と先ほど作成した空のtディレクトリを紐付け
その上で、下記の順にコマンドを入力し、ファイルを更新します。
$ git add .
$ git commit -m 'upd'
$ git push heroku master
LINE Developersからbot作成
1) LINE Developersにアクセスし、LINEアカウントでログイン
2) プロバイダー作成
3) 新規チャンネルの作成
4) チャネルアクセストークンとチャネルシークレットの取得
1)〜4)までは下記を参考に進みました。
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest
5) webhookの設定
Herokuから取得したデプロイ先のURLを入力。
/webhookをつけることを忘れずに。
QRコード
完成したギニュー特戦隊判定LINEbotのQRコードがこちらです。
(*2021.7.24時点でherokuとの接続を終了しております。申し訳ありません。)
どうでしたか皆さん?
ギニュー特戦隊に成る願いは叶えらましたか??
その他の記事
近すぎると小池都知事が『密です。』と連呼するデバイスを作ったら腹筋が崩壊したので、皆さんにも試して欲しい。
誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。