Node-RED
やNode.js
+ express
でおうむ返しのLINE Botを作れる方が主に対象となります。参考記事は掲載していますが、webhook URL
などわからないと難しいかもしれません
AIお絵描きが流行ったので私もやってみたい😍
そう思った方も多いかと思います。
stable diffusion
の発表からいち早く実装されたこのBotは凄まじい登録者数を獲得しました。LINE Botだったら自分にもできそう!!私も作ってみたい!!と思いstable diffusionのGitHubを訪ねてみると、待ち受けているのは残酷な現実です。
ここで自分にはハードルが高いと感じ、諦めてしまった方もいらっしゃるのではないでしょうか。
本記事ではNode-RED
とrinna Developers
を活用し、ノーコードでお絵描きLINE Bot
の実装を目指します。
下準備
Node-RED
この記事の通りRailway上にデプロイしておくと後々楽です。
rinna Developers
こちらText to Image API
のサブスクリプションキーを発行しておいてください。適当に名前入力してSubscribe
のボタンを押すだけです。
rinna API
こちらのNodeをお借りします。Node-RED上にインストールしておいてください。
LINE Developers
Messaging API
使うので登録しておいてください。
gyazo API
生成した画像をアップロードしてURLを生成、LINEに送ります。
参考記事はこちら。
Step.0 Node-RED上でテキストから画像生成してみる
再掲となりますがこちらのGitHub上にあるサンプル通り、テキスト入力から画像を生成してみます。わかる方は飛ばしていただいて構いません。
詳細はこちら
1.画像の通りノードを並べてつなげます
2.ノードの中身を設定します。(inject
は送りたい文字列、image previes
は設定不要です)
3.inject
のボタンを押すと画像が生成されます。
お好きな言葉で画像を生成することができます。
Step.1 Node-REDの全体像
インポート用JSON配列
[
{
"id": "679b23ea477d9d4c",
"type": "rinna",
"z": "736a148da489128d",
"name": "",
"api": "ldm",
"subscription": "",
"x": 330,
"y": 400,
"wires": [
[
"286ed77437845460",
"f2a8012cd7145063",
"24a5cb304229cf7c"
]
]
},
{
"id": "286ed77437845460",
"type": "image",
"z": "736a148da489128d",
"name": "",
"width": 160,
"data": "payload",
"dataType": "msg",
"thumbnail": false,
"active": true,
"pass": false,
"outputs": 0,
"x": 320,
"y": 540,
"wires": []
},
{
"id": "a26acea9f7fe5dff",
"type": "Webhook",
"z": "736a148da489128d",
"name": "",
"url": "/webhook",
"x": 160,
"y": 400,
"wires": [
[
"6c8d62d1fed760fb",
"679b23ea477d9d4c"
]
]
},
{
"id": "6c8d62d1fed760fb",
"type": "debug",
"z": "736a148da489128d",
"name": "debug 5",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 160,
"y": 480,
"wires": []
},
{
"id": "02eb29bdbc5713d2",
"type": "ReplyMessage",
"z": "736a148da489128d",
"name": "",
"replyMessage": "",
"x": 760,
"y": 760,
"wires": []
},
{
"id": "9b13730b6012b5b3",
"type": "change",
"z": "736a148da489128d",
"name": "",
"rules": [
{
"t": "set",
"p": "headers.content-type",
"pt": "msg",
"to": "multipart/form-data",
"tot": "str"
},
{
"t": "set",
"p": "temp",
"pt": "msg",
"to": "payload",
"tot": "msg",
"dc": true
},
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "{}",
"tot": "json"
},
{
"t": "set",
"p": "payload.access_token",
"pt": "msg",
"to": "GYAZO_API_TOKEN",
"tot": "str"
},
{
"t": "set",
"p": "payload.imagedata.value",
"pt": "msg",
"to": "temp",
"tot": "msg",
"dc": true
},
{
"t": "set",
"p": "payload.imagedata.options.filename",
"pt": "msg",
"to": "image.png",
"tot": "str"
},
{
"t": "delete",
"p": "tmp",
"pt": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 720,
"y": 400,
"wires": [
[
"01bbb139ce04e4ae",
"523f4e49785e176d"
]
]
},
{
"id": "f2a8012cd7145063",
"type": "debug",
"z": "736a148da489128d",
"name": "debug 6",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 340,
"y": 480,
"wires": []
},
{
"id": "efc81245f7303bb6",
"type": "debug",
"z": "736a148da489128d",
"name": "debug 7",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 520,
"y": 480,
"wires": []
},
{
"id": "24a5cb304229cf7c",
"type": "function",
"z": "736a148da489128d",
"name": "function 4",
"func": "const base64 = msg.payload.split(',')[1];\nmsg.payload = Buffer.from(base64, 'base64') //Buffer形式の画像バイナリに変換\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 500,
"y": 400,
"wires": [
[
"efc81245f7303bb6",
"9b13730b6012b5b3"
]
]
},
{
"id": "01bbb139ce04e4ae",
"type": "debug",
"z": "736a148da489128d",
"name": "debug 8",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 700,
"y": 480,
"wires": []
},
{
"id": "523f4e49785e176d",
"type": "http request",
"z": "736a148da489128d",
"name": "",
"method": "POST",
"ret": "txt",
"paytoqs": "ignore",
"url": "https://upload.gyazo.com/api/upload",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "basic",
"senderr": false,
"headers": [],
"x": 150,
"y": 760,
"wires": [
[
"4d28f0dadd0a39b0",
"8037b4c8.94c7c8"
]
]
},
{
"id": "4d28f0dadd0a39b0",
"type": "debug",
"z": "736a148da489128d",
"name": "debug 9",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 160,
"y": 840,
"wires": []
},
{
"id": "cf90dab5.815908",
"type": "debug",
"z": "736a148da489128d",
"name": "debug10",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 340,
"y": 840,
"wires": []
},
{
"id": "8037b4c8.94c7c8",
"type": "json",
"z": "736a148da489128d",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 350,
"y": 760,
"wires": [
[
"cf90dab5.815908",
"0adf342694d1eb65"
]
]
},
{
"id": "0adf342694d1eb65",
"type": "change",
"z": "736a148da489128d",
"name": "",
"rules": [
{
"t": "set",
"p": "temp",
"pt": "msg",
"to": "{}",
"tot": "json"
},
{
"t": "set",
"p": "temp.thumb_url",
"pt": "msg",
"to": "payload.thumb_url",
"tot": "msg",
"dc": true
},
{
"t": "set",
"p": "temp.url",
"pt": "msg",
"to": "payload.url",
"tot": "msg",
"dc": true
},
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "{}",
"tot": "json"
},
{
"t": "set",
"p": "payload.type",
"pt": "msg",
"to": "image",
"tot": "str"
},
{
"t": "set",
"p": "payload.originalContentUrl",
"pt": "msg",
"to": "temp.url",
"tot": "msg",
"dc": true
},
{
"t": "set",
"p": "payload.previewImageUrl",
"pt": "msg",
"to": "temp.thumb_url",
"tot": "msg",
"dc": true
},
{
"t": "delete",
"p": "tmp",
"pt": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 760,
"wires": [
[
"a92b30d9b13897eb",
"02eb29bdbc5713d2"
]
]
},
{
"id": "a92b30d9b13897eb",
"type": "debug",
"z": "736a148da489128d",
"name": "debug 11",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 540,
"y": 840,
"wires": []
}
]
Step.2 LINE側の設定をする
LINE側のwebhook URLは、Railway
上にデプロイしているとして、
https://node-red-railway-production-{自身のURL中文字列を入力}.up.railway.app/webhook
Node-RED上のwebhook URLは/webhook
で設定しておいてください。
Step.3 各ノードを設定する
画像は折りたたんでいますので必要に応じて参照ください。
1. LINE webhook
Path: /webhook
2. rinna
API: Text to Image API
subscrioption: 設定したsubscription key
3. function
詳細画像
ノーコードで実装するといったな。あれは嘘だ
const base64 = msg.payload.split(',')[1];
msg.payload = Buffer.from(base64, 'base64') //Buffer形式の画像バイナリに変換
return msg;
これをコピペするだけなので許してください。
msg.payload = data:image/png;base64,{{base64}}
とうデータが入っているので、data:image/png;base64,
を削除して、base64文字列をBuffer
形式に変換できるノードがあればfunction使わずにすみます。
4. change 1つめ
この記事を参考に
msg.headers.content-type = 'multipart/form-data'
msg.payload = {
access_token: 'Gyazoアクセストークン文字列',
imagedata: {
value: (Buffer形式の画像バイナリ),
options: {
filename: 'image.png',
},
},
}
このようなmsgオブジェクトを生成してください。
インポート用JSON
[
{
"id": "9b13730b6012b5b3",
"type": "change",
"z": "736a148da489128d",
"name": "",
"rules": [
{
"t": "set",
"p": "headers.content-type",
"pt": "msg",
"to": "multipart/form-data",
"tot": "str"
},
{
"t": "set",
"p": "temp",
"pt": "msg",
"to": "payload",
"tot": "msg",
"dc": true
},
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "{}",
"tot": "json"
},
{
"t": "set",
"p": "payload.access_token",
"pt": "msg",
"to": "GYAZO_API_KEY",
"tot": "str"
},
{
"t": "set",
"p": "payload.imagedata.value",
"pt": "msg",
"to": "temp",
"tot": "msg",
"dc": true
},
{
"t": "set",
"p": "payload.imagedata.options.filename",
"pt": "msg",
"to": "image.png",
"tot": "str"
},
{
"t": "delete",
"p": "tmp",
"pt": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 720,
"y": 400,
"wires": [
[
"01bbb139ce04e4ae",
"523f4e49785e176d"
]
]
}
]
5. http request
メソッド: POST
URL: https://upload.gyazo.com/api/upload
認証を使用にチェックし、種別はBasic認証
6. JSON
responseをJSON形式にしてみやすくします。
別段設定は必要ありません。
7. change 2つめ
LINE Messaging APIリファレンスより
{
"type": "image",
"originalContentUrl": "https://example.com/original.jpg",
"previewImageUrl": "https://example.com/preview.jpg"
}
このようなJSONを生成します。
originalContentURL
は詳細画像なので、GYAZO APIから返ってくるurl
の画像URLを、previewImageURL
はサムネですのでthumb_url
の画像URLをそれぞれ渡してあげましょう。
インポート用JSON
[
{
"id": "0adf342694d1eb65",
"type": "change",
"z": "736a148da489128d",
"name": "",
"rules": [
{
"t": "set",
"p": "temp",
"pt": "msg",
"to": "{}",
"tot": "json"
},
{
"t": "set",
"p": "temp.thumb_url",
"pt": "msg",
"to": "payload.thumb_url",
"tot": "msg",
"dc": true
},
{
"t": "set",
"p": "temp.url",
"pt": "msg",
"to": "payload.url",
"tot": "msg",
"dc": true
},
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "{}",
"tot": "json"
},
{
"t": "set",
"p": "payload.type",
"pt": "msg",
"to": "image",
"tot": "str"
},
{
"t": "set",
"p": "payload.originalContentUrl",
"pt": "msg",
"to": "temp.url",
"tot": "msg",
"dc": true
},
{
"t": "set",
"p": "payload.previewImageUrl",
"pt": "msg",
"to": "temp.thumb_url",
"tot": "msg",
"dc": true
},
{
"t": "delete",
"p": "tmp",
"pt": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 760,
"wires": [
[
"a92b30d9b13897eb",
"02eb29bdbc5713d2"
]
]
}
]
8. LINE ReplyMessage
LINE Developersからチャネルシークレット、チャネルアクセストークンをコピーして貼り付けましょう。
Step.4 デプロイ
赤いボタンを押す
言葉から画像を生成してリプライしてもらった
編集で短くしていますが、メッセージ送った後それなりに待たされます。
難点
やめてくれrinna。
待たされた後この画像出すのは俺に効く。