1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-29

はじめに

探偵小説作家夢野久作の 瓶詰地獄(青空文庫リンク) は、昭和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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?