読者です 読者をやめる 読者になる 読者になる

鷹楠日記

美術展、博物展、神社、旅行、読書、IT関連などの雑感を書いていきます。

粘菌の LINE スタンプを作ってみました。

鷹楠です。

「粘菌スタンプ」という LINE スタンプを作ってみました。一度、LINE スタンプを作ってみたかったのと、粘菌という生き物の魅力を広めたかったのが、つくった理由です。(…最近、LINE 関係ばっかりですが、けっして LINE の回し者ではありません。笑)

LINE はいまや、日本ではかなりの利用率を占めるコミュニケーションツールであり、LINE Bot しかり、何かを広めるために有用なプラットフォームだなあと思うわけです。…まあ、LINE スタンプの数はすでに膨大になっていて、その中で突出するのは難しいのも事実ですが。

さて、ここでは、粘菌スタンプの宣伝も兼ねて、私がどういうやり方で LINE スタンプを作ったのか、制作過程を紹介したいと思います。


まず、完成形はこんな感じ。40 個のスタンプです。

https://store.line.me/stickershop/product/1397048

f:id:takagusu:20170311190322p:plain
f:id:takagusu:20170311190327p:plain

ところで、みなさんは粘菌という生き物をご存知ですか?その字のごとく「粘る菌」…と説明できればよかったのですが、そうではありません。「粘る」は部分的にあってますが、これはライフサイクルの一期間のあいだだけです。それから、「菌」ではありません(!)。粘菌は、アメーバみたいに動き回る時期と、キノコみたいに動かなくなる時期をもつ、動物でも植物でも菌類でもない不思議な生き物です。南方熊楠がこの粘菌の研究者だったことは有名です。なお、現在では粘菌の一部(真正粘菌)のことを「変形菌」と呼びます。
粘菌の注目すべきところはたくさんあるのですが、そのひとつが、見た目の美しさです。画像検索すると、カラフルで綺麗な姿に驚くと思います(気持ち悪さも多少ありますが 笑)。
ひとまず、粘菌の話はここまでとして、制作過程を説明します。


まず、スケッチブックに、アイデアスケッチ。
粘菌はいろいろ種類があるので、その調査・選別から始めました。スケッチはこんな感じ。

f:id:takagusu:20170311184451j:plain

f:id:takagusu:20170311184524j:plain

f:id:takagusu:20170311184601j:plain


上の画像では、どんなメッセージにすればよいか、案をいろいろ書いてみてます。


次に、実際に個々のスタンプをシャーペンで下書き。40 個は、意外と骨が折れます。

f:id:takagusu:20170311184921j:plain


f:id:takagusu:20170311185030j:plain


f:id:takagusu:20170311185109j:plain


f:id:takagusu:20170311185119j:plain


南方熊楠の名言。このスタンプの主題と言ってもいいでしょう。

f:id:takagusu:20170311185125j:plain


そして、これらの画像をスキャン…したかったのですが、スキャナーを持ってないので、スマホで写真に撮って PC に送りました。

Windows フォトで、必要な範囲だけクロップして、こんな感じ。

f:id:takagusu:20170311191530j:plain


次に、これらの画像を、線画に変換します。スキャンした画像をベースにして、ペイントソフトで本書き…してもいいんですが、めんどうだったので、エッジ検出で下書きをそのまま線画にすることにしました。写真加工.com というサイトのエッジ検出 http://www.photo-kako.com/edge.cgi を使いました。ブラウザ上で利用できるので Good です。

変換した結果は下記のような感じ。けっこう汚いですが…、それはこれからどうにかしていきます。

f:id:takagusu:20170311192227j:plain


次に、色付けです。どうやって色を塗ろうか考えていたところ、ちょうど、興味深い記事をみつけました。

日本発のPaintsChainerはAIで線画を自動着色―ニューラルネットワークが驚異の能力 | TechCrunch Japan

この技術、本当にすごいです。


「これを使って、色塗りをさせてみよう!!
世界初の AI 自動着色の LINE スタンプや!!」


…と思いました。世界初かどうかは、わかりません。


実際に、先ほどの線画をインプットして、色を塗ってみたら次のような感じ(だいぶ色を指定して、補正はしました)。
どうですか、かなりいい感じじゃないですか。

f:id:takagusu:20170311193405j:plain


こんな感じで、他の画像もすべて色を塗りました。
水彩画みたいで、優しいイメージです。私が思う粘菌のイメージにぴったりです。


続いて、GIMP を使って、必要なイラスト部分だけ切り取り、それ以外は透明化しました。このへんの詳しいやり方は、「GIMP 切り取り」や「GIMP 透明化」でググると出てくると思います。

必要な部分だけ切り取った結果はこんな感じ。

f:id:takagusu:20170311193928p:plain


そして最後に、所定のサイズに合わせて、文字を入れて完成!…までを GIMP でやりたかったんですが、GIMP だとなかなか操作が煩わしい…とくに、文字を入れたりフォントを選んだりするところ。これを 40 個分やる気にはならず、…どうしたかというと、だいぶ普通じゃないと思いますが、LibreOffice の Impress(パワーポイントみたいなやつ)を使って、文字入れと PNG 出力を行いました。

フォントは「ぎゃーてーるみねっせんす」を使用させていただきました。

スタンプに使える画像は PNG 形式で、サイズは最大 幅 370 × 高さ 320 ピクセルです。
LibreOffice を使って PNG 形式に出力した画像は、こんな感じ。なお、文字はすべて回りを白で囲っています(背景が白なのでわからないかもしれませんが)。

f:id:takagusu:20170311195210p:plain


LINE スタンプとして申請するには、40 個のスタンプ画像の他に、メイン画像(240×240 ピクセル)とトークルームタブ画像(96×74 ピクセル)が必要なので、これらも用意しました(…と言っても、作ったスタンプ画像からよさげなものをピックアップして、文字を消したりして、サイズを変えて出力しただけ)。
詳細は、ガイドラインを参照。

さて、LINE スタンプには、審査があります。
今回の粘菌スタンプの場合は、申請してから承認されるまで、1 週間ちょっとかかりました。幸運にも、リジェクトされずに一発 OK でした。

今回の反省点として…

  • 完成したスタンプの文字が意外と小さい。シミュレータで確認してたつもりですが、もっと大きくしたほうがよかったです。
  • なんか画質が悪い。PNG の画質の設定がよくなかったかもしれません。
  • 使いどころがわからないスタンプがいくつかある。

…などなど


…ということで、粘菌スタンプの制作過程のご紹介でした。

気に入った方は、ご購入いただけると嬉しいです。
こちらからどうぞ:https://store.line.me/stickershop/product/1397048
または、スマホの LINE アプリから、スタンプで「粘菌スタンプ」と検索すれば出てくると思います。


ここで紹介したのはあくまで作り方の一例ですが、
LINE スタンプを作ってみたいという方は、参考にしてみてください。

では。

近くの神社を教えてくれる LINE Bot を作ってみました。

鷹楠です。前回前々回と、LINE Bot を作ってきましたが、今回もまた新しい LINE Bot を作ってみました。

機能はシンプル。近くの神社を教えてくれて、参拝済みの記録をつけられる LINE Bot です。

今回は、LINE APIカルーセル型のテンプレートメッセージを使ってみました。それから、Google Apps Script で無料で簡単に使えるデータベース Google Fusion Tables も使ってみました。

きっかけ&こんなものが作りたい

  • 神社が好きで、有名どころはけっこうまわってる。
  • とはいえ、まだまだ知らない神社はいっぱい。
  • 近所や、ちょっと出かけた先で、まだ行ったことがない神社をみつけたい。暇つぶしや、ウォーキングがてら。
  • 行ったことがある神社は、何かしらの形でわかるようにしたい。
  • 社格なんかをすぐ知りたい。
  • 御朱印を管理したい。

…とまあ、常日頃からこういう想いがあって、とりあえず作れるものを作ってみました。ちなみに、最後の 2 つの機能(社格なんかの表示、御朱印の管理)は、残念ながらこの記事を書いている時点では実現できていません。今後の課題です。

こんなものができた

  • 位置情報を送信すると、そこから半径 3 km 以内の神社の情報を、最大 5 件教えてくれる。
  • 神社の情報には、「名前」「そこまでの直線距離」「住所」が含まれる。
  • 「その神社を Google 検索するためのボタン」「その神社までのルート(by Google マップ)を検索するためのボタン」もいっしょに表示される。詳細が知りたかったら、ここから検索できる。神社名だけでなく住所もいっしょに検索ワードになってるので、一意に神社を特定して検索できる。
  • 「行ったことがある」ボタンもいっしょに表示される。これを押すと、次回からこの神社の情報には「参拝済み」マークがつく。


外観を紹介します。

Bot の名前は「近くの神社」。

f:id:takagusu:20170307193911j:plain


友だち追加時のメッセージ:

f:id:takagusu:20170307193934j:plain


位置情報を送信すると、

f:id:takagusu:20170307194130j:plain


そこから半径 3 km 以内の神社の情報を、最大 5 件教えてくれます。
今回は、カルーセル型のテンプレートメッセージでメッセージを送ってます。横にスライドするやつです。最大 5 件というのは、LINE API の制限だったり…。

f:id:takagusu:20170307194014j:plain


タップできるボタンが 3 つありまして、

f:id:takagusu:20170307194458j:plain


「この神社を検索」ボタンをタップすると、「神社の名前」+「住所」で Google 検索します。(LINE 内のブラウザで開かないで欲しいんですけどね…)

f:id:takagusu:20170307194602j:plain


「ここからのルート」ボタンをタップすると、Google マップのルート検索結果を表示します。徒歩の場合のルートにしてます。

f:id:takagusu:20170307195250j:plain


最後に、「行ったことがある」ボタンついて。タップすると、内部で持ってるデータベースにユーザーと神社の ID が保存されて、次に神社が表示されたときには、「参拝済み」という文字がいっしょに表示されます。

f:id:takagusu:20170307195723j:plain

大まかな作り方

  • 前回と同じく、LINE Bot アカウントを作り、Google Apps Script で LINE Bot 用の web アプリケーションを作成した。
  • 位置情報(緯度・経度)の近くにある神社を探すために、Yahoo!ローカルサーチAPI を利用した。この API の優れているところは、「コンビニ」「カフェ」「動物園」「歯科」などかなり具体的な施設のカテゴリ(業種コード)で検索できるところ。嬉しいことに、このカテゴリの中に「神社」があった。業種コードの一覧はここを参照(一覧の csv をダウンロードできる)。なお、利用する際はクレジット表示が必要。
  • 神社の情報を伝えるために LINE APIカルーセル型のテンプレートメッセージ を利用した。タップしたときに URL リンクを開けるように uri アクションを使い、神社に行ったことがあるという情報を受け取るために postback アクションを使った。
  • ユーザーごとの参拝済み情報(神社に行ったことがあるかどうか)は、Google Fusion Tables というデータベースで管理。Google DriveFusion Tables(試験運用) のアプリを追加して、Google API Console で有効化すると、Google Apps Script のコードから呼び出せるようになる。Google Fusion Tables は試験運用中らしく、今後どうなるかちょっと不安なところはあるけど、とりあえず気にしない。

ソースコードの紹介

GitHub に格納しています。

Google Apps Script(.gs ファイル)で実装してます。Google Apps Script を使った LINE Bot 用の基本的なコードについては、前回の記事も参照。

位置情報から、近くの神社を検索する

今回の LINE Bot で最も重要な部分です。送信された位置情報から、近くにある神社の情報を取得するための、getNearShrines() という関数を作りました。このコードでは、Yahoo!ローカルサーチAPI の仕様に従って、指定した 緯度 (latitude) と経度 (longitude) から、半径 3 km 以内にある「神社」(業種コード: 0423002)を最大 5 件、近い順に検索するための HTTP リクエストを投げています。結果は JSON 形式でもらうように指定してます。なお、事前に Yahoo! の アプリ ID を取得しておく必要があります。この Web API のレスポンスとして、神社の名前・住所・緯度・経度などを取得できます。

var YAHOO_APP_ID = 'XXXXX' // Yahoo! のアプリ ID
var YAHOO_SEARCH_URL = 'https://map.yahooapis.jp/search/local/V1/localSearch';

function getNearShrines(latitude, longitude) {
  var url = YAHOO_SEARCH_URL
          + '?appid=' + YAHOO_APP_ID
          + '&dist=3'     // 3 km 以内
          + '&gc=0424002' // 業種コード: 神社
          + '&results=5'  // 最大 5 件
          + '&lat=' + latitude
          + '&lon=' + longitude
          + '&output=json&sort=dist';
  var response = UrlFetchApp.fetch(url);
  
  var shrines = [];
  var features = JSON.parse(response.getContentText('UTF-8'))['Feature'];
  for (i = 0; i < features.length; i++) {
    var uid = features[i]['Property'].Uid;         // 場所の ID
    var name = features[i].Name;                   // 場所の名前
    var address = features[i]['Property'].Address; // 場所の住所
    var coords = features[i]['Geometry'].Coordinates.split(',');
    var shrine_longitude = coords[0];              // 経度
    var shrine_latitude = coords[1];               // 緯度

    // 神社の情報を配列に入れる処理

  }
  return shrines;
}

2 点の場所の距離を求める

Yahoo!API を使うことによって、近くの神社の緯度・経度などの情報が取れました。ほんとはこの中に、検索した場所からの距離が含まれていてほしかったのですが、どうもこの情報は含まれてない。そこで、検索した場所から、取得した神社の場所までの直線距離を、2点間距離API(これも Yahoo! 提供)を使って求めました。…まあ、ほんとうは道なり距離が知りたかったんですけどね。ぱっと探した感じだと、無料でたくさんリクエストできる API が見つかりませんでした。直線距離でも、ないよりはマシな情報かな、と。

コードは下記のような感じ。2 つの 緯度・経度 から、直線距離(地球の楕円体に合わせた距離)を求めます。パラメータ coordinates に、「coordinates=139.73091159286,35.665662327613 135.49513388889,34.701974166667」のように、2 つの位置情報をスペースで繋げて渡すみたいなんですが、当然といえば当然ながら、パーセントエンコードしないとうまく動きません(ハマりました)。Google Apps Script 標準の関数を使って、encodeURIComponent(' ') とやってます(「%20」と直に書いてももちろん OK)。最後の Math.round(distance * 10) / 10 は、小数点以下 1 桁の距離 (km) にするためです。

function getDistanceInKilloMeters(latitude1, longitude1, latitude2, longitude2) {
  var url = YAHOO_DIST_URL
          + '?appid=' + YAHOO_APP_ID
          + '&coordinates=' + longitude1 + ',' + latitude1 + encodeURIComponent(' ') + longitude2 + ',' + latitude2
          + '&output=json';
  var response = UrlFetchApp.fetch(url);
  var distance = JSON.parse(response.getContentText('UTF-8'))['Feature'][0]['Geometry'].Distance;
  return Math.round(distance * 10) / 10;
}

Google 検索の URL と Google マップのルート検索の URL

Google 検索の URL は、次のコードで生成しています。https://www.google.co.jp/search という URL に、パラメータ q を渡します。検索する文字列がマルチバイト文字の場合は、「入力文字のエンコードUTF-8 ですよ」と指定する ie=UTF-8 もつけます(説明として正確ではないかも)。

function getGoogleSearchUrl(query) {
  return 'https://www.google.co.jp/search?q=' + encodeURIComponent(query) + '&ie=UTF-8';
}


Google マップのルート検索の URL は、次のコードで生成しています。http://maps.google.com/maps という URL に、2 つの緯度経度を saddr と daddr で渡します。dirflg=w をつけると、徒歩でのルート検索になります。ただ、このへんの URL 生成は、やってみたらできた、って感じなので、そのうち動かなくなるかもしれません。…いやー、しっかし、このような URL にアクセスするだけで一瞬でルートを示してくれるって、かなり凄いことだと思います。

function getGoogleMapRouteUrl(srcLatitude, srcLongitude, destLatitude, destLongitude) {
  return 'http://maps.google.com/maps'
         + '?saddr=' + srcLatitude + ',' + srcLongitude
         + '&daddr=' + destLatitude + ',' + destLongitude
         + '&dirflg=w';
}

ルーセル型のテンプレートメッセージと、ポストバック機能

横にスライドするやつです(下記画像のようなかんじ)。LINE APIリファレンスはこちら

f:id:takagusu:20170307194014j:plain


リファレンスに書いてあるとおりに送れば実現できるのですが、リファレンスは、なんだかリンク飛ばされまくって、どこに何をいれたらいいのかわかりづらいと感じます。結果的に、下記のような JSON を送ればカルーセル型のメッセージになりました。

// ユーザーがタップするボタンになるもの. 最大 3 個まで作成可能.
var actions = [
  {
    'type': 'postback',          // ポストバックの場合は postback
    'label': '行ったことがある',
    'data': 'action=visited&uid=<場所のID>'  // ポストバックする任意の文字列
  },
  {
    'type': 'uri',               // リンクの場合は uri
    'label': 'この神社を検索',
    'uri': googleSearchUrl       // 神社の 検索 URL
  },
  {
    'type': 'uri',               // リンクの場合は uri
    'label': 'ここからのルート',
    'uri': googleMapRouteUrl     // 神社の ルート検索 URL
  }
];

// 横にスライドされる 1 単位. 最大 5 個まで作成可能.
var columns = [
  {
    'title': title,
    'text': 'ここから ' + distance + 'km ― ' + address,
    'actions': actions
  },
  
  ...

]

// ユーザーに返信するメッセージ. 最大 5 個まで作成可能.
var messages = [
  {
    'type': 'template',
    'altText': '代替のテキストメッセージ',  // 通知窓なんかにもこれが表示される
    'template': {
      'type': 'carousel', // カルーセル型のテンプレートメッセージの指定
      'columns': columns
    }
  }
];


アクションの type に postback を指定した場合、ユーザーがそれをタップすると、サーバーにポストバックが送信されます。今回、この機能を使って、ユーザーが行ったことがある神社を記録する処理に繋げてます。

送信されてきたデータを JSON に変換して(変数名 json)、json.events[0].postback.data でポストバックデータを取得できます。データの設計・使い方は自由です。この LINE Bot では、「action=visited&uid=<場所のID>」という文字列を渡して、これを受け取ってデータベースを操作するようにしています。

var LINE_BOT_CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty('LINE_BOT_CHANNEL_ACCESS_TOKEN'); // Google Apps Script の [プロジェクトのプロパティ] > [スクリプトのプロパティ] で値を設定
var LINE_REPLY_URL = 'https://api.line.me/v2/bot/message/reply';


function doPost(e) {
  var json = JSON.parse(e.postData.contents);

  // 中略

  if ('postback' == json.events[0].type) {
    var data = json.events[0].postback.data;

    // 送信されてきたデータを使った処理
    // 今回の場合、「visited」or「unvisited」の文字列と、神社の場所 ID が送信される。

    messages = [{'type': 'text', 'text': '行ったことがある神社を更新しました'}]; 
  }

  UrlFetchApp.fetch(LINE_REPLY_URL, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + LINE_BOT_CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': messages,
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}

Google Apps Script でデータベースを利用する

Google Apps Script で無料で簡単に使えるデータベース Google Fusion Tables を使って、ユーザーごとの神社参拝記録を行うための、ごく簡単なデータベースを作りました。

上の方でも少し書きましたが、Google DriveFusion Tables(試験運用) のアプリを追加して、Google API Console で有効化すると、Google Apps Script のコードから呼び出せるようになります。

まず、Google Drive で任意の場所に Fusion Tables のファイルを作成します。そしてこれを開いて、あらかじめ、カラムを定義しておきます。下記のような感じです(※これはデータがいくつか入ってるとき)。Text 型とか Date 型とか使えます。ちなみに、Location 型という特殊な型もあって、これを使うと地図上に簡単にマッピングできるらしいです(今回使ってませんが)。

f:id:takagusu:20170308221114p:plain


さて、こうして作ったデータベースに Google Apps Script からアクセスするには、下記のようにします。あんまり綺麗じゃないですが(undefined で判断してるとことか特に…)、「場所の ID」(uid) と「ユーザー ID」(userId) と「参拝の有無」(visited, 1 or 0) が引数で、指定した docId の Fusion Tables データベースに対して、select や insert、update の操作を行っています。既に「場所の ID」と「ユーザー ID」の組み合わせがある場合には、その rowId を取得して、これを使って update を実行します(rowId を指定しないと update ができないという、少し特殊なつくりになってます)。「場所の ID」と「ユーザー ID」の組み合わせがない場合には、新規に行を insert します。

// Google Apps Script の [プロジェクトのプロパティ] > [スクリプトのプロパティ] で値を設定
var GOOGLE_FUSION_TABLES_DOC_ID = PropertiesService.getScriptProperties().getProperty('GOOGLE_FUSION_TABLES_DOC_ID');

function changeVisited(uid, userId, visited) {
  var sql = "SELECT ROWID FROM " + GOOGLE_FUSION_TABLES_DOC_ID
          + " WHERE uid = '" + uid + "' and userId = '" + userId + "'";
  var result = FusionTables.Query.sqlGet(sql);
  if (typeof result.rows === 'undefined') {
    sql = "INSERT INTO " + GOOGLE_FUSION_TABLES_DOC_ID
        + " (uid, userId, visited)"
        + " VALUES ('" + uid + "', '" + userId + "', " + visited + ")";
    FusionTables.Query.sql(sql); 
  } else {
    var rowid = result.rows[0];
    sql = "UPDATE " + GOOGLE_FUSION_TABLES_DOC_ID
        + " SET visited = " + visited
        + " WHERE ROWID = '" + rowid + "'";
    FusionTables.Query.sql(sql);
  }
}

簡単なデータベースであれば、この Fusion Tables で十分ですね。


長くなってしまいましたが、とりあえずソースコードの紹介はこんな感じで。
最終的な .gs ファイルは、GitHub に格納しているので、よければ参考にしてください(あんまりいいコードじゃないですが)。

では。

アイデアを記録・想起する LINE Bot を作ってみました。

鷹楠です。
思いついたアイデアやメモを記録できて、さらに、過去のアイデアを思い出させてくれる LINE Bot を作ってみました。前回作った LINE Bot では、IBM の Bluemix でサーバーを用意しましたが、今回は、もっとお手軽な Google Apps Script を使ってみました。

コンセプト(こんなものが作りたい)

  • 思いついたアイデアやメモを、LINE を使って記録したい
  • 記録したアイデアは、LINE 以外からでもアクセス、活用したい
  • 過去に記録したアイデアを、あとから思い出したい

ちなみに、私は普段、LINE に自分一人だけのグループを作っていて、メモ帳代わりに使ってます。今回は、それをもっと活用できるように拡張したかった…という感じです。

こんなものができた


Bot の名前は「アイデアの泉」(…幹並みですが)。




友だち追加時のメッセージはこんな感じ。


イデアを入力すると、自動的に Google スプレッドシートに記録します。


「アイデア」と入力すると、これまでに入力したアイデアを、日時とともにランダムに 2 つ表示します。この機能によって、なにか新しいアイデアの組み合わせが見つかればいいなあ。


スプレッドシート」と入力すると、アイデアが記録されている Google スプレッドシートの URL を教えてくれます。ブラウザやスプレッドシートからアクセスできるので、アイデアやメモを活用しやすくなります。


スマホGoogle スプレッドシートのアプリで開いた様子。
入力したアイデアやメモが、投稿日時とともに記録されてます。


スプレッドシートはユーザーごとに 1 つ、自動で作成されます。とりあえず、権限は閲覧のみとしました。権限は、どうするのがベストか、悩ましい。…というのも、LINE のユーザーと Google のユーザーが紐付けられないので、Everyone が閲覧可能 or 編集可能 のどちらかを選ぶしかないのです。

おおまかな作り方

  • LINE Bot のアカウントを作成する。
  • Google Drive に、Google Apps Script のプラグインを入れて、Google Apps Script のファイルを新規作成する。
  • Google Apps Script のコードをブラウザ上で書いて、web アプリケーションとして公開する(メニューから選ぶだけ)。このとき URL が決まるので、コピって、LINE Bot の Webhook URL に設定する。

Google Apps Script では、とても簡単に web アプリケーションを公開できます。この手軽さは、本当に素晴らしいです。しかも無料。何かアプリを作るときにいちいちサーバーを立てるのが、ばかばかしく思えてきます。使えるのは JavaScript をベースにした Google 独自の言語であり、なんでもやれるというわけではないですが、それでも、かなりのことが実現できます。

ソースコードの説明

ソースコードは、下記の GitHub リポジトリに格納してます。
GitHub - avcdsld/IdeaLogger: This is a LINE Bot to record your ideas input in Google SpreadSheet. By using this bot, you can also recall past ideas randomly.


Google Apps Script の拡張子は .gs となります。
web アプリを作成するには、.gs ファイルに下記の関数を定義します。LINE Bot に対してメッセージが送信されると、この関数が実行されます。

function doPost(e) {
  // Post送信されてきたときの処理
}


どんな HTTP リクエストが来るかは、LINE の REST API のリファレンス で確認します。
e.postData.contents を JSON としてパースして、必要なパラメータを取得します。Bot からメッセージを返信するために必要となるトークンや、ユーザーから送信されたメッセージなど。

今回の場合、LINE Bot へのメッセージに返信する処理は、下記のようになります(下記のコードだと未実装部分があるので注意。完成版は GitHub のコード を参照)。

var CHANNEL_ACCESS_TOKEN = 'dummy';  // LINE Bot のアクセストークンを入力してください
var line_endpoint = 'https://api.line.me/v2/bot/message/reply';

function doPost(e) {
  // JSONをパース
  var json = JSON.parse(e.postData.contents);

  // 送信されてきたメッセージを取得
  var user_message = json.events[0].message.text;  
  
  // 返信するためのトークンを取得
  var reply_token= json.events[0].replyToken;
  if (typeof reply_token === 'undefined') {
    return;
  }

  // 返信するメッセージを配列で用意する
  var reply_messages;
  if ('ヘルプ' == user_message) {
    // 「ヘルプ」と入力されたときの返信メッセージ
    reply_messages = ["スプレッドシートにアクセスしたい場合は「スプレッドシート」と入力してください。\n\nアイデアを思い出したくなったら「アイデア」と入力してください。あなたの過去のアイデアをランダムにお伝えします。\n\n使い方がわからなくなったら「ヘルプ」と入力してみてください。"];

  } else if ('スプレッドシート' == user_message) {
    //「スプレッドシート」と入力されたときの処理
    // Google スプレッドシートの URL を返信メッセージとする
    // reply_messages = ...

  } else if ('アイデア' == user_message) {
    //「アイデア」と入力されたときの処理
    // 過去のアイデアからランダムに取得して、返信メッセージとする
    // reply_messages = ...

  } else {
    // アイデアやメモが入力されたときの処理
    reply_messages = ['アイデアが追加されました'];
  }

  // メッセージを返信
  var messages = reply_messages.map(function (v) {
    return {'type': 'text', 'text': v};    
  });    
  UrlFetchApp.fetch(line_endpoint, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': reply_token,
      'messages': messages,
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}


Google スプレッドシートを操作する処理について説明します。
SpreadsheetApp.create(<任意のタイトル>) で、スプレッドシートを新規に作成できます。
書き込むためには、シートを指定する必要があります。下記のコードの変数名だと、spreadSheet.getSheets()[0] で最初のシートを取得でき、spreadSheet.appendRow(<追加する行の配列>) で行を追加できます。楽でいいです。権限は、DriveApp でファイルを取得して setSharing() で設定できます。

  // スプレッドシートの作成と、ヘッダ行の追加
  var spreadSheet = SpreadsheetApp.create("スプレードシートのタイトル");
  var sheet = spreadSheet.getSheets()[0];
  sheet.appendRow(['日時', 'メッセージ']);

  // 権限の設定(全員が閲覧可能)
  var file = DriveApp.getFileById(spreadSheet.getId());
  file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
  

なお今回、ひとりのユーザーにただ 1 つのスプレッドシートを作成するために、LINE のユーザー ID(API 用の ID)とスプレッドシート ID の組み合わせを保管することにしました。保管には ScriptProperties を使っています(PropertiesService.getScriptProperties() で取得可能)。

スプレッドシートからランダムにセル値を取得する方法については割愛します。

雑ですが (笑)、ソースコードの説明はこんな感じで。

LINE Bot のしくみで、何か別のものも作ってみたいです。

では。

毎朝 天気を通知する LINE Bot を作ってみました。

鷹楠です。今年は「思いついたらとりあえずやってみる」精神でいきたいです。
クオリティはひとまず気にしない。既存のものがあるとかも気にしない。

きっかけ&こんなものをつくりたい

朝、傘を持っていくのを忘れる。
天気予報をみればいいんだけど、それをみるのを忘れる。
毎朝、LINEに通知が来れば嬉しい。特に知りたいのは降水確率。
地域の設定はなるべく手間をかけずにやりたい。
※ちなみに、毎日天気を知らせてくれる、似たようなサービスは既にあるようです(「LINEお天気」など)。

こんなものができた

名付けて、「毎朝お知らせ!天気予報」


f:id:takagusu:20170124194725j:plain


使い方の表示はこんな感じ(友だち追加時と、何か話しかけたときに表示)。


f:id:takagusu:20170124194802j:plain


位置情報を送信すると、地域を設定できる。


f:id:takagusu:20170124194917j:plain


f:id:takagusu:20170124200046j:plain


「天気」と入力すると、その日の天気予報をお知らせ。

そして、「スタート」と入力すると、毎朝7時に天気が通知されるようになる。


f:id:takagusu:20170124200317j:plain


朝、通知が届いている様子。これで傘を忘れることもなくなりそう。


f:id:takagusu:20170124195216j:plain


おおまかなつくりかた

  • LINE Bot 自体は、LINE Business Center に登録すれば無料で作成できる。ためしにということで、Messaging API Developer Trial でアカウントを作成した。返信だけでなく、Bot 側からユーザーにメッセージを送信する Push API が利用できる。ただし、追加可能友だち数は50人という制限がある。ブラウザ上で、Bot のプロフィールなどを設定する。
  • IBM Bluemix にアカウントを作成し、LINE Bot の返信のためのサーバーアプリを作る。Bluemix は Amazon Web Service や Google Cloud Platform のような、開発者向けクラウドサービスで、工夫すれば無料で利用できる。
  • 今回、サーバーアプリは、Bluemix が提供している Ruby + Sinatra のテンプレートをベースにして、LINE が提供している Ruby 用の API を使って実装した。LINE Business Center から辿れる LINE Developers > Channels ページで、作成した LINE Bot の Webhook URL に、サーバーアプリの URL を設定することで、Bot の返信がサーバーアプリに任される。
  • 天気の情報は、JP Weather Forecast という天気予報 API を利用させてもらった。無料で降水確率が取得できる API はこれ以外にほとんどなかった。ありがたい。(ただ、欲を言うともっと細かい地域の情報が知りたい。)日本の主要な地域の天気予報が XML または JSONP 形式で取得できるので、これを Ruby でパースする。
  • どこの天気をお知らせするかという 地域 の設定は、LINE の位置情報を利用できるようにした(これは LINE のかなり売りな機能だと思う)。ユーザーが位置情報を送信すると、サーバーアプリでは 緯度・経度 が取得できるので、天気予報 API から取得できる最も近い観測地点を自動的に選択する。各都道府県ごとに、数個の地点があるようである。詳細な位置情報は、プライバシーの観点から、保存しない。
  • ユーザーごとの ID と地域を保存するために、Bluemix のサービスのひとつ ClearDB (中身は MySQL)を利用した。5 MB までなら無料で使える。
  • 毎朝メッセージを送信するために、Bluemix のサービスのひとつ Workload Scheduler を利用した。Windows のタスクスケジューラのようなことができる(というより、UI そのまんま)。月 50 回までなら無料で使える。

ソースコードの紹介

LINE Bot の受け口と返信

/callback という URL を受け口にすることにします(名前は任意です)。ユーザーからメッセージが来た場合、この URL が呼ばれます。Sinatra のコードだとこんな感じ。

post '/callback' do

  # 処理

  "OK"
end


LINE の Ruby 用の API を利用するために、require に下記を書く(Gemfile にも)。

require 'line/bot'


Ruby 用の API のサンプルコードに倣って、まず client を定義。ここで、LINE_CHANNEL_SECRET と LINE_CHANNEL_TOKEN は、Bluemix に設定した環境変数です。値は LINE Developers で取得・確認できます。

def client
  @client ||= Line::Bot::Client.new { |config|
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end


client.parse_events_from でリクエスト body をパースして、

  body = request.body.read

  events = client.parse_events_from(body)


events.each で回して、イベントの種類ごとに処理を書きます。
文字列が入力された場合は event.type の値が Line::Bot::Event::MessageType::Text に、位置情報が入力された場合は Line::Bot::Event::MessageType::Location になるようです。
メッセージを返信するには、まず message オブジェクトを作成し、client.reply_message(event['replyToken'], message) を実行します(下記のコード参照)。一定時間(記事作成時点で30秒)以内に返事を返さないといけないようです。

  events.each { |event|
    case event
    when Line::Bot::Event::Message
      case event.type

      when Line::Bot::Event::MessageType::Text
        # 文字列が入力された場合

        case event.message['text']
        when 'スタート'
          # 「スタート」と入力されたときの処理
        when 'ストップ'
          # 「ストップ」と入力されたときの処理
        when /.*天気.*/
          # 「天気」を含む文字列が入力されたときの処理

        end

      when Line::Bot::Event::MessageType::Location
        # 位置情報が入力された場合
        latitude = event.message['latitude'] # 緯度
        longitude = event.message['longitude'] # 経度

        # 経度・経度を使った処理
      end

      message = { type: 'text', text: "デフォルトの返信メッセージ"}
      client.reply_message(event['replyToken'], message)
    end
  }

天気の取得

必要なモジュール(Ruby標準です)

require 'net/http'
require 'uri'
require 'rexml/document'


JP Weather Forecast という天気予報 API で、天気を XML 形式で取得し、パースします。Ruby だと、とても簡単に書けます。これは東京都の天気を取得する例です。

    uri = URI.parse('http://www.drk7.jp/weather/xml/13.xml')
    xml = Net::HTTP.get(uri)
    doc = REXML::Document.new(xml)


各情報を取得します。doc.elements[<xpath>].text で値を取得、doc.elements[<xpath>].attributes[<属性名>] で属性値を取得できます。この簡便さ。RubyXML パーサは神ですね。上記のコードに引き続き、東京都 東京地方 の天気を取得する例です。

    xpath = 'weatherforecast/pref/area[4]'

    weather = doc.elements[xpath + '/info/weather'].text # 天気(例:「晴れ」)
    max = doc.elements[xpath + '/info/temperature/range[1]'].text # 最高気温
    min = doc.elements[xpath + '/info/temperature/range[2]'].text # 最低気温
    per00to06 = doc.elements[xpath + '/info/rainfallchance/period[1]'].text # 0-6時の降水確率
    per06to12 = doc.elements[xpath + '/info/rainfallchance/period[2]'].text # 6-12時の降水確率
    per12to18 = doc.elements[xpath + '/info/rainfallchance/period[3]'].text # 12-18時の降水確率
    per18to24 = doc.elements[xpath + '/info/rainfallchance/period[4]'].text # 18-24時の降水確率

位置情報から地域の選定

LINE の位置情報を利用して、天気を取得できる地域のうち、最も近いものを選定します。
天気を取得できる地域の緯度・軽度はあらかじめ天気予報 API から取得し、データベースに格納しておきます。

先ほど、LINE でユーザーから位置情報が送信された場合に 緯度・経度 を取得するコードを紹介しましたので、その情報を使って、下記の SQL を実行します。area_info は、地域の緯度・経度を格納しているテーブル名です。

select * from area_info order by abs(latitude - 緯度) + abs(longitude - 経度) asc

Bot 起点でユーザーにメッセージを送る

返信ではなく、Bot 起点でユーザーにメッセージを送るには、下記のように書きます。Push API というらしいです。
こちらは、Developer Trial だと無料で利用できます(ただし先ほども書いたように、追加可能友だち数は 50 人まで)。フリー版だと Push API は利用できません。

message = { type: 'text', text: '送信したいメッセージ' }
client.push_message(row['user_id'], message)

今回、毎朝この処理を実行したいのですが、Bluemix の無料枠では、Sinatra 上で定期実行するのは難しそう…(できるのかな?)。Sinatra 上でこのようなことをする方法をどなたか知ってれば教えてほしいです。

ということで、処理を実行するための URL をひとつ用意することにして、Bluemix の Workload Scheduler から HTTP リクエストを投げることで実現しました。
サーバー側の受け口は下記のような感じ。/send という URL にしました。

get '/send' do
  protected! # basic auth

  # メッセージ送信処理

  "OK"
end

さすがにセキュリティが何もないとまずいと思うので、Basic 認証を入れました。上記、protected! を呼んでいるところの定義は下記です。こちらは、Sinatra の Q&A に載っていたものをそのまま使いました(まあ、Basic 認証もセキュリティ上よくないですが…)。BASIC_AUTH_USERNAME と BASIC_AUTH_PASSWORD は Bluemix の環境変数で、ユーザー名とパスワードです。

helpers do
  def protected!
    return if authorized?
    headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
    halt 401, "Not authorized\n"
  end

  def authorized?
    @auth ||=  Rack::Auth::Basic::Request.new(request.env)
    @auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == [ENV['BASIC_AUTH_USERNAME'], ENV['BASIC_AUTH_PASSWORD']]
  end
end

所感

LINE Bot は、いろんなAPIと組み合わせることができるとても良いプラットフォームだなと思います。しくみは単純で、それゆえに奥が深い。LINE BOT AWARD なるものもあり、APIを提供しているいろんな企業が協賛になってるみたいです。他のプラットフォームやAPIも活用して、何か作ってみたいですね。

では。

南方熊楠に関するアート。

鷹楠です。

決めたことがあります。

 

私の職業の中で、一番、南方熊楠に詳しい人になる。

その根底にあるのは、生半可な気持ちでアート作品を作らない、という決心です。

 

南方熊楠の研究者にとって、やはり自分の研究対象を、

中途半端な形で利用されるのは、よい気持ちはしないでしょう。

 

きっかけは、このセミナー。

偉人?巨人?超人? 南方熊楠はどのように語られてきたか | 青山ブックセンター

 

世の中には、南方熊楠をテーマにしてアート作品をつくっているひとたちがたくさんいるんですね。

私も、その一人になりたいです。ただ、やるからには、ただのネタにするのではなく、覚悟してやります。

 

特に、アーティストの方々は、南方曼荼羅に関心が強いということで、私もそこにとても関心を持っていたので、共感できて嬉しいとともに、誰しも考えるよなあ・・・と不甲斐なく感じました。

そもそも、この世界観、理解できている人はいるのだろうか。

文字では表現できない世界なのだと思います。

 

まずは、勉強します。

研究者の方によると、下記の本が、読みやすくて良いとのこと。さっそくアマゾンでポチりました。

 

南方熊楠の生涯 単行本 – 1994/5 仁科 悟朗

https://www.amazon.co.jp/dp/4404021070

 

読みやすさで言ったら、下記もおすすめとのこと。

 

南方熊楠―森羅万象に挑んだ巨人 (別冊太陽 日本のこころ 192) 大型本 – 2012/1/19 中瀬 喜陽

https://www.amazon.co.jp/dp/4582921922

 

そういえば、下記の本、熊楠の小説ですが、読んでないと思っていたら、過去に読んでました。読んだものについて、小説かどうかすら忘れてしまうとは・・・。読み方、変えたほうがいいかもしれない。

 

縛られた巨人―南方熊楠の生涯 (新潮文庫) 文庫 – 1991/12/24 神坂 次郎

https://www.amazon.co.jp/dp/410120912X

 

知らないことだらけで不甲斐ないですが、頑張ります。

では。

 

柳田國男と南方熊楠 セミナー聴講

鷹楠です。

柳田國男南方熊楠 ~2 人が築いた民俗学の道しるべ。」というセミナーを聴講しました。

 

www.aoyamabc.jp

 

好きな偉人は誰かと聞かれたら、迷わず「南方熊楠」と答える鷹楠です。私のハンドルネームの由来でもあります。

ご存じ無い方のために念のため説明すると、南方 熊楠(みなかた くまぐす)は、明治を生きた 生物学者・民俗学者博物学者・思想家・エコロジストです。

「日本人の可能性の極限」と呼ばれた人で、その生き様・思想・能力・業績は凄まじいです。明治の時代に、西洋の研究者と対等に肩を並べて、科学雑誌『ネイチャー』に50報、『ノーツ・アンド・クエリーズ』に323報の論文が掲載されています。それだけでもう、信じられないくらい凄い。

『ネイチャー』に論文が掲載されるというのは、科学者の夢であり、1報掲載されるだけでたいへんな偉業です。それが50報・・・。もちろん、いまとは掲載基準が違うと思いますが、当時も今も、これだけの論文が掲載された東洋人はいません。

熱心な菌類学者でもあり、収集した標本は6000点を超えます。新種を10種も見つけています。

また、当時としてはまったく新しい概念であるエコロジーについて提唱し、自然を壊す悪因となる神社合祀への反対運動を発起し、何年も根強く続けました。

民俗学の父・柳田國男や、近代日本仏教史を代表する仏教学者・僧侶の土宜法龍などと大量に書簡をやり取りしており、幅広い分野に貢献しています。

まさに、日本人のヒーロー的な人物なわけです。

 

さて、何の話だったっけ・・・

あ、そうだ。うん、その彼が、もうすぐ生誕150年ということで、青山ブックセンターではここ半年ほど、南方熊楠関連セミナーを開催しています。

熊楠研究者の田村義也さんをはじめとして、これまで、詩人の吉増剛造さん、編集者の都築響一さん、熊楠研究者の中沢新一さん、漫画家の辛酸なめ子さんなどを交えた、魅力たっぷりのセミナーが開催されました。

 

そして今回は、柳田國男研究者との対談。

ちょうどいま、千葉の国立歴史民俗博物館(略してレキハクというらしい)で、「柳田國男と考古学」という特別展をやっているらしいです。2016/10/10まで。

なんでも、柳田國男は実は考古学に関心があり(熊楠に対してとても批判的なことを書いたりしていたけど)、収集物もあったとのこと。どんな特別展なのか気になる。

第4展示室 特集展示 「柳田國男と考古学」|プレスリリース|歴博の活動|国立歴史民俗博物館

 

今回のセミナーの内容は、柳田國男および南方熊楠という人物のわかりやすい説明と、二人がどのように接して、意見交換し、お互いの考えを深めていったのかという考察でした。

この対談を聴講して、柳田國男がつくった民俗学という学問について理解が深まりました。

民俗学というと、「お祭りの研究」や「妖怪の研究」といったイメージが強いかもしれませんが、柳田國男が考える民俗学の根底には、次のような想いがあったとのことです。

 

「普通の人が、普通に働いて、考えて、生きてきた様を観察することで、人間について明らかにする。」

 

人を理解することが、民俗学の目的なんですね。なるほど。

歴史の表舞台にでてこない、庶民の文化と歴史を理解するためには、文献に残っていないことを調べる必要あります。そのために、例えばお祭りのしきたりや踊りを注意深く観察したり、「カッパ伝説」などの伝承されている昔話を掘り起こすことが重要になるというわけです。

 

熊楠と柳田の出会いは、熊楠が書いた、山の神に魚のお供えをする慣習についての掲載記事(?)を柳田がみつけたことがはじまりのようです。柳田は、自分も同じことについて以前報告したことがあるという内容とともに、山に住む人についての知見を、熊楠に手紙で求め、そこから書簡のやり取りがはじまっています。

熊楠と柳田はだいぶ考え方の違いがあったようですが、その裏には、柳田が官僚という立場であったことも影響しているみたいでした。

 

今回、セミナーの中でいくつか書籍が紹介されました。下記のものが気になっています。「ノーツ・アンド・クエリーズ」に掲載された熊楠論文の日本語訳!素晴らしい。ただ、もう少し値段が安ければ・・・><

 

www.amazon.co.jp

 

あとこれ。 岡 茂雄 著「本屋風情」(中公文庫) 。

https://www.amazon.co.jp/dp/4122050332

 

柳田がみつけた、熊楠の報告。

・「山神オコゼ魚を好むと云う事」

 『南方随筆』(沖積舎) 、『南方熊楠コレクション〈第2巻〉南方民俗学』 (河出文庫) に収録。

 

 こんなサイトが! オコゼ:山神オコゼ魚を好むと云う事(口語訳1):南方熊楠の随筆

 

特別展「柳田國男と考古学」については、本も出ているようです。読んでみようかな。

www.amazon.co.jp

 

・・・まとまりがないですが、結構長くなってしまったので、今回はこれまで。 

九頭龍神社 月次祭に参加しました。

鷹楠です。
箱根にある九頭龍神社 本宮。毎月13日に行われる、その月次祭(つきなみさい)に参加しました。

一度行ってみたかった、九頭龍神社 本宮。
新宮は、比較的アクセスのいい箱根神社の隣にありますが、本宮はかなり行きづらい場所にあります。毎月13日には参拝船が出るので、土日と重なるときを狙ってました。しかも、その日にしか神職さんがいないとのことでなおさら。

早朝の芦ノ湖。受付をして、乗船券の購入と祈祷の申し込みをします。
f:id:takagusu:20160213212234j:image

参拝船に乗り込みます。かなり大きいです。
f:id:takagusu:20160213212324j:image

富士山が綺麗にみえました。
f:id:takagusu:20160213212519j:image

箱根元宮が山の上にあります。残念ながら強風のためこの日のロープウェーは運休で、参拝はできませんでした。
f:id:takagusu:20160213212558j:image

到着。多くの月次祭参加者がいます。特に女性が多い。恋愛成就の神社として知れ渡ってるみたいです。
f:id:takagusu:20160213212817j:image

かなりの混み具合です。10時から月次祭が始まり、一人一人の名前と祈願を読み上げてくれます。
f:id:takagusu:20160213212934j:image

f:id:takagusu:20160213213135j:image

f:id:takagusu:20160213213217j:image

摂社に参るのにも、並ぶ、並ぶ。
f:id:takagusu:20160213213413j:image

帰り道。本当に森の中にあるっていう感じです。
f:id:takagusu:20160213213549j:image

その後、箱根神社に参拝。月次祭に参加すると、ここで引き換え券を渡して、九頭龍神社の御札と霊水を貰えます。
f:id:takagusu:20160213213659j:image

f:id:takagusu:20160213213910j:image

隣には、九頭龍神社の新宮。
f:id:takagusu:20160213213938j:image

天候不良のため箱根元宮に参れなかったのが残念でしたが、念願だった九頭龍神社 本宮に参れて、月次祭を体験できたのでよかったです。
数名の神職さんがひたすら名前と祈願を読み上げている図には、宗教の持つ力の大きさを実感しました。若干、距離を置きたい気持ちも…笑

箱根神社は、神仏習合の活動で有名な万巻上人が、箱根大神の神託を受けて建てた神社(もとは修験道関連)みたいです。
そして、九頭龍神社は、その万巻上人が改心させた芦ノ湖の龍、九頭龍大神を祀った神社みたいです。
九頭龍大神は、各地に伝承が残っていて、さらにインド仏教とも関連付けられてる神なんですね。

川が氾濫してたのを例えるのはわかりやすいけど、箱根の芦ノ湖の場合は、いったい何が伝承の元ネタなんでしょうね。
この小さめの湖に、海賊的な輩がいたとか…?

月次祭の授与品と、御朱印。箱根神社の龍神水は、なめらかでスッキリした味わいでした。
f:id:takagusu:20160213221147j:image


さて今日は箱根に宿泊です。
f:id:takagusu:20160213214009j:image

では。