Azure
bot
linebot
AzureFunctions

夢野久作の ”瓶詰地獄” がサーバレスアーキテクチャの Azure Functions で送出されて3つの瓶が流れ着く LINEBOT

More than 1 year has passed since last update.


はじめに

探偵小説作家夢野久作の 瓶詰地獄(青空文庫リンク) は、昭和3年に書かれた書簡体形式の掌編です。

「三個とも封瓶のまま」打ち上げられた手紙を、読み進めていくと、最後に劇的な読後感に襲われる仕掛けになっています。

btl_typeB.png


作中手紙の ”書かれた順番”

”書かれた順番” については、


この作品における3篇の手紙が、第3、第2、第1の瓶の内容の順番で書かれたとする(Wikipedia


解釈が一般的なものです。

が、読者が読む順番(情報の与えられる順番) がそもそも違ったら、”書かれた順番”について、どんな印象を持てるでしょうか…?


作中手紙を ”読む順番”

”読む順番” が違うとどんな気持ちになるか、気になったので、タイトル要素で実装しました!(本題)

画面イメージは下記です。(”瓶詰地獄”ネタバレ防止のため、本文一部が表示されてしまう操作動画アニメGIFは本稿末尾に配置しました…)

gamenimg.JPG

操作の流れ

1. BOTにテキストを投げる

2. 浜辺の絵と、カルーセルに載った瓶がBOTから返ってくる

3. カルーセルの中にはランダムな順番で、作中の3つの手紙が詰まっている

4. 「瓶を開ける」ボタンをクリックすると、手紙本文画像が開く

「封を開けて読む」読書体験をしたかったので「瓶画像をクリックしたら、手紙が開く」2段階UIにしました。


環境(2017年4月現在)

UI: Line-BOT

静的コンテンツ(画像)配信: Azure BLOB ストレージ

Webhook: Azure Functions


UI:LineBOT

Line Messaging API は、去年の春(2016年春)のトライアル提供から、秋の正式提供への経緯を経ています。

その影響か各種仕様変更が多々あり、Line側準備は公式サイトの最新情報を見るのが一番だと思います。

ここ から ここ など。


静的コンテンツ(画像)配信: Azure BLOB ストレージ

前述のLineBotの細かい仕様変更のひとつに、Line へ reply/push できるコンテンツは https に限定 するというものがあります。

(去年はたしか http でよかったような…?)

自前 https 環境設定は面倒なので、Azure BLOB ストレージ に画像ファイルを置いて、静的配信することにしました(参考)。


Webhook: Azure Functions

去年リリースされた(参考 [techcrunch] MicrosoftがAWSのLambdaに続いてサーバー不要のイベント駆動型クラウドサービスAzure Functionsをローンチ) Azure Functions は 従来あった webJobs を lamda ライクに拵えたサービスです。

今回は、node.js で下記のように書きました。

(httpリクエストトリガー: リクエストをキューに溜める)


HTTPTrigger

module.exports = function (context, req) {

context.log('JavaScript HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);
context.log(req);
context.bindings.outputQueueItem = req.body;
res = { body : "" };
context.done();
};

(キュートリガー: キュー起動で処理実行)


QueueStoragetrigger

var https = require("https");

var url = require("url");

// 送出
function send_line(postData, context, mtd){
var parse_url = url.parse('https://api.line.me/v2/bot/message/'+ mtd);
var options = {
host: parse_url.host,
path: parse_url.path,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer {(キー)}'
}
};
var req = https.request(options, function(res) {
context.log('-- send_line : STATUS: ' + res.statusCode);
context.log('-- send_line : HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
context.log('-- send_line : BODY: ' + chunk);
});
});
req.on('error', function(e) {
context.log('-- send_line : problem with request: ' + e.message);
});
req.write(postData);
req.end();
}

// 手紙シャッフル
function shuffle(array) {
var n = array.length, t, i;
while (n) {
i = Math.floor(Math.random() * n--);
t = array[n];
array[n] = array[i];
array[i] = t;
}
return array;
}

// 瓶詰
function push_bottles(context, event) {

shuffled_lettersNum = shuffle(["1", "2", "3"]);

var postData = JSON.stringify(
{'to' : event.source.userId,
'messages' : [
{
"type": "template",
"altText": "『瓶画像と本文へのリンク』です。",
"template": {
"type": "carousel",
"columns": [
{
"thumbnailImageUrl": "https://(アカウント).blob.core.windows.net/bins/btl_typeB.png",
"text": "よほど以前に漂着致したる封瓶",
"actions": [
{
"type": "uri",
"label": "瓶を開ける",
"uri": "https://(アカウント).blob.core.windows.net/bins/" + shuffled_lettersNum[0] + ".jpg"
}
]
},
{
"thumbnailImageUrl": "https://(アカウント).blob.core.windows.net/bins/btl_typeA.png",
"text": "よほど以前に漂着致したる封瓶",
"actions": [
{
"type": "uri",
"label": "瓶を開ける",
"uri": "https://(アカウント).blob.core.windows.net/bins/" + shuffled_lettersNum[1] + ".jpg"
}
]
},
{
"thumbnailImageUrl": "https://(アカウント).blob.core.windows.net/bins/btl_typeB.png",
"text": "よほど以前に漂着致したる封瓶",
"actions": [
{
"type": "uri",
"label": "瓶を開ける",
"uri": "https://(アカウント).blob.core.windows.net/bins/" + shuffled_lettersNum[2] + ".jpg"
}
]
}
]
}
}
]}
);
send_line(postData, context, "push");
};

// 浜辺
function reply_beach(context, event) {
var postData = JSON.stringify({
'replyToken' : event.replyToken,
'messages' : [
{
"type": "image",
"originalContentUrl": "https://(アカウント).blob.core.windows.net/bins/beach.jpg",
"previewImageUrl": "https://(アカウント).blob.core.windows.net/bins/beach.jpg"
}
]
});
send_line(postData, context, "reply");
};

function post_message(context, event) {
if (event.type == 'message'){
var messageType = event.message.type;
if (messageType == 'text') {
reply_beach(context, event);
push_bottles(context, event);
} else
reply_beach(context, event);
};
}

module.exports = function (context, myQueueItem) {
context.log('------------ JavaScript queue trigger function', myQueueItem);
myQueueItem.events.forEach(event => post_message(context, event));
context.done();
};



BOT内画像&BOT操作動画

画像は下記で

準備しました。

また、操作動画は、PC版LINEだと今回使ったカルーセルテンプレートは表示されない(下記画像)ので、

pc.JPG

スマホアプリ版LINEの動画を撮る必要があり、


  • Androidアプリ「Az Screen Recorder」

を使いました。(無償録画機能+課金サブセット(トリミング、画面切り取り、GIF変換など、オールインワンで便利でした))


さいごに

満足いく読書(?)体験ができました!

操作動画アニメGIF(※1アクション(ループ再生))

sousa_loop.gif