ゆっくりのんびり。

いの (@inox_ee) です

Node.js + HerokuでLINE Messaging APIを叩いてみる

とある用事でLINE BOTを作ってみよう!と思い立ち、すぐさま取り掛かることに。
しかし動かすまでに意外と手間取ったので、ブログとして書き残しておきました。

環境

  • Windows10
  • Node.js v8.11.1
  • Heroku Free

LINE Developerの登録

LINE BOTを作成するために、LINE Messaging API を利用します。
「今すぐ始めよう」をクリックするとログイン画面に飛ぶため、とりあえず自分の普通のアカウントでログインしましょう。ログイン後、チャネル(要はbot)を新規作成する プロバイダー を選択します。チャネルは普通のLINEアカウントとしてではなく、それと紐づけされた「プロバイダー」として作成者が定められるようです。
プロバイダーの設定が終わったら、いよいよチャネルの登録です。

f:id:puyobyee18:20181028171438j:plain

基本的に指示に沿って名前や説明欄を埋めるだけですが、上図のプランに注意。フリープランだと友達人数の上限がありませんが、[PUSH_MESSAGE]機能が使えなくなってしまいます。ここではとりあえず Developer Trial プランで始めることにしましょう。

とりあえずチャネルの登録はできました。しかしチャネル基本設定を見てもChanne IDやらアクセストークンやらwebhookなどイマイチ使い方が分からない……大丈夫です。アレコレ試行錯誤して使い方は理解したので後程お話します。

ローカルでコーディング

ここからはローカルの話。(以降ではNode.jsとnpm、gitは使えること前提で書きます)

$ mkdir sample-app
$ cd sample-app
$ npm init -y
$ npm install --save @line/bot-sdk express

上記のコードで適当にディレクトリを作成し、Node.js用のAPIである@line/bot-sdk、サーバ構築用にExpressをインストールします。

あとはプログラムですが、とりあえず公式のサンプルコードを拝借します。

'use strict';

const line = require('@line/bot-sdk');
const express = require('express');

// create LINE SDK config from env variables
const config = {
  channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
  channelSecret: process.env.CHANNEL_SECRET,
};

// create LINE SDK client
const client = new line.Client(config);

// create Express app
// about Express itself: https://expressjs.com/
const app = express();

// register a webhook handler with middleware
// about the middleware, please refer to doc
app.post('/callback', line.middleware(config), (req, res) => {
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result))
    .catch((err) => {
      console.error(err);
      res.status(500).end();
    });
});

// event handler
function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    // ignore non-text-message event
    return Promise.resolve(null);
  }

  // create a echoing text message
  const echo = { type: 'text', text: event.message.text };

  // use reply API
  return client.replyMessage(event.replyToken, echo);
}

// listen on port
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`listening on ${port}`);
});

これをserver.js等名前をつけて保存します。ここで重要なのがconfigオブジェクトにCHANNEL_ACCESS_TOKENおよびCHANNEL_SECRETを代入しているところです。どこかで聞いたような変数名ですね。

また、以下のファイルをProcfileという名前で保存します。かなり重要なファイルなので忘れないように。

web: node server.js

以上でローカルの操作は一通り終了です。後々のためにこれらのファイルをGithubにあげましょう。Githubでてきとうにレポジトリを作成したらpushしましょう。Gitの使い方は割愛します。

Herokuの設定

いよいよHerokuを使っていきます。HerokuはPaaSの一種で、個人開発程度ならFreeプランでアレコレできるので簡単にbotを実装できます。Azureとか個人サーバからngrokを利用してトンネルする(よくわかってない)方法とか選択肢はありますが、おそらくHerokuが一番親切で簡単に始められるでしょう。Micr〇S〇ftとかユーザーにAzure使わせる気あるんですか?というレベルでドキュメントが分かりにくいので

登録が終わると以下のような画面が出てくるでしょう。とりあえずページ右上のopen app を押し、なにも表示されませんがhttps://[アプリ名].herokuapp.com に接続できたら大丈夫です。

f:id:puyobyee18:20181028171443j:plain

それではこのherokuにデプロイしていきます。herokuのデプロイは2通りあり、

  • Heroku CLIを用いる方法
  • Githubと連携させる方法

があります。ここでは先程pushしたGithubを使うことにします。

Deployページを開き、連携するGithub repository を選択します。ページ中部にconnected to [レポジトリ名] by [アカウント名]と表示されたら無事完了です。

最後に、前章でコメントしていたserver.jsの変数についてです。どこにも定義されずに登場したCHANNEL_ACCESS_TOKENおよびCHANNEL_SECRETは、LINE botのチャネルに依存する変数で、LINEで作成したチャネル基本設定に表示されていたアクセストーク(表示されていない場合は再発行しましょう)とChannel Secretがこれにあたります。
ではどのように定義すればよいでしょうか。server.jsでは、これらの変数は環境変数として扱われるので、herokuに登録することで使用可能になります。

f:id:puyobyee18:20181028171433j:plain

上図のようにSettingページ中部のConfig Varsから登録します。これで2つの変数が定義され、プログラム上でも使えるようになります。

webhook URLの登録

webhookとは、

A webhook in web development is a method of augmenting or altering the behavior of a web pages, or web application , with custom callbacks.

引用元:https://en.m.wikipedia.org/wiki/Webhook

とありますが、最近では外部サービスと連携させてアプリケーションを動かすことを指すことが多いです。

それでは、LINE Messaging APIとHerokuをひも付けしましょう。LINEのチャネル基本設定から、webhook URLを登録します。ここで/callbackを呼んでいますが、これはserver.jsのコードを読めば、ここにpostしていることが分かると思います。

f:id:puyobyee18:20181028171417j:plain

最後に、LINE botの諸設定を以下のようにすれば完了です。登録時のコメントは好きなように設定すればいいと思います。

f:id:puyobyee18:20181028171424j:plain

補足:コードの実行

1つ気になると思うのが、LINE botがプログラムをどう実行しているかだと思います。
これは、先程のProcfileが担っています。herokuでResourceページを見てみると、Free Dynosの欄にこのファイルの中身が記述されているのが確認できます。LINE botからHerokuにプログラムが呼び出されると、これが実行されてserver.jsが立ち上がる、という仕組みになっているようです。

動作確認

早速作成したLINE botを友だち登録し、なにか発言してみましょう。使用したソースコードは、発言した言葉をそのまま返す、オウム返しbotとなっているはずです。

以上で終了です。お疲れ様でした。

参考にしたサイト

【React.js】Electron + ReactでMastodonクライアントを作る

Reactの勉強を始めて早1ヶ月。環境構築やAtomのクラッシュなどの困難を乗り越えた夏休み…

ようやくElectronまでたどり着いたのでMastodonクライアントを作ってみた。

※コードは以下の書籍を参考にしています

環境

  • Windows 10 Home
  • Node.js v8.11.1
  • React v16.5.2
  • electron v3.0.0

まずはAPIを叩いてみる

アプリの認証(本来は「認可」というべきらしい。)

mastodon-apiを用いてMastodonのWeb API利用する。インスタンスは、Pixivが運営する https://pawoo.net にした。

このWeb APIはOAuth2という認証方法(*1)を行うため、以下の手順が必要。 1. アプリをインスタンスに登録 2. ユーザが認証し、アクセストークンを発行 3. アクセストークンを用いてAPIにアクセス

正直mastodon-apiのライブラリがexampleファイルを用意してくれてるので適当にコピペすればおk。ということで割愛。

タイムラインの取得

先ほどのアクセストークンを利用して以下のようなプログラムを書くと簡単にタイムラインを表示できる

// MastodonのAPIクライアントの作成
const Mstdn = new Mastodon({
access_token: token,
timeout_ms: 60 * 1000,
api_url: instanceUri + '/api/v1/'
})

// タイムラインの読み込み
Mstdn.get('timelines/public', {})   
.then(res => {
    const data = res.data
    console.log(data)
})

ちなみにtimelines/homeにすればホームタイムラインが、timelines/publicにすれば公開タイムラインが表示されます。

トゥートしてみる

トゥートするのも簡単。そう、mastodon-apiならね。

// MastodonのAPIクライアントの作成
const Mstdn = new Mastodon({
  access_token: token,
  timeout_ms: 60 * 1000,
  api_url: instanceUri + '/api/v1/'
})

// Toot
let message = process.argv[2]
Mstdn.post('statuses',
  {status: message},
  (err, data, res) => {
    if (err) {
      console.error(err)
      return
    }
  })

結論:GETメソッドとPOSTメソッドが優秀すぎる。

アプリ成型

構成

構成はいたってシンプル。Electronを立ち上げ、アクティブになったらindex.htmlを読み込むだけ。

index.htmlからReactで書いたindex.jsxを呼んであげましょう。

Electronの立ち上げ

テンプレプログラムそのまま。ちなみにcreateWindow()のなかのprotocolでコロンを書き忘れ、2時間溶かしました。つらい。

// Electron
let mainWindow
app.on('ready', createWindow)
app.on('window-all-closed', () => app.quit())
app.on('activate', () => {
  if (mainWindow === null) createWindow()
})

// ウィンドウの作成
function createWindow () {
  mainWindow = new BrowserWindow({width: 600, height: 800})
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))
  mainWindow.on('closed', function () {
    mainWindow = null
  })
}

表示するページ

Electronから呼ばれるindex.htmlですが、のちのちindex.jsxをwebpackでビルドしてあげるので./out/index.jsを読み込むようにしてあげましょう。プログラムは割愛。

メインコンポネント

長すぎるので大幅に割愛してます。そして残念ながらはてなブログはJSXのシンタックスに対応していない。

1 constructor()

まずはconstructor。loadInfo()内では、前項で取得したアクセストークンを読み込み、APIクライアントを作成。状態(state)にはトゥート内容を格納するtootdataと、タイムラインを構築するtimelinesを設定してあげる。

constructor(props) {
    super(props)
    this.apiUri = 'https://pawoo.net/api/v1/'
    this.loadInfo()
    this.state = {
        tootdata: '',
        timelines: []
    }
}

2 componentWillMount()

そして次にコンポネントがマウントしたときの動作を書いたのですが、componentWillMount()ってReact v16.3.0以降は非推奨、v17.0.0から撤廃されるらしいですね…。

Deprecation warnings will be enabled with a future 16.x release, but the legacy lifecycles will continue to work until version 17. Even in version 17, it will still be possible to use them, but they will be aliased with an “UNSAFE_” prefix to indicate that they might cause issues. We have also prepared an automated script to rename them in existing code.

出典: React v16.3.0: New lifecycles and context API March 29, 2018 by Brian Vaughn

書き終わってからeslintに指摘されて気が付きました。ググってもイマイチ理解できなかったので新しいライフサイクルがどのようになるのか誰か教えて~

ちなみにcomponentWillMount内ではタイムラインを読み込むloadTimelines()を30秒に1回リロードしています。

3 handleText(e) & tootFunc(e)

あと必要なのはトゥート処理。これも前項で紹介したPOSTメソッドを使い、「statuses」APIに投稿したのち、テキストボックスとタイムラインを更新してあげればよい。

4 render()

いよいよ描画。アプリの顔を作っていきましょう。

デザインにはあまり凝らなかったので、とりあえずトップに投稿フォーム、その下にタイムラインを表示するという構成に。以下のようにプログラムすれば十分でしょう。タイムライン表示のrenderTimelines()は後述。

render () {
    return (
        <div>
            <div>
                <h1>Mastodon Client</h1>
                <textarea
                value={this.state.tootdata}
                onChange={e => this.handleText(e)} />
                <div>
                <button onClick={e => this.tootFunc(e)}>Toot</button>
                </div>
            </div>
            <div style={{marginTop: 120}}></div>
            {this.renderTimelines()}
        </div>
    )
}

5 renderTimelines()

一番難しい、、、というか写経したあとなるほどな~って勉強してた()

タイムラインを構築するコツはmapメソッドを使うところですかね。あとGETメソッドで返ってくるHTTPレスポンスはHTMLタグで囲まれています(Reactではエスケープされる)。これをdangerouslySetInnerHTMLを利用して直接span要素に指定する という発想はなかった。というかそもそもプロパティが全く分からないのも苦労した…。下記参考リンクを見つけなければ迷宮入りしてたよ。(accountがトゥートした投稿者を表すオブジェクト、contentがトゥート内容)

というわけでプログラム。

renderTimelines () {
    const lines = this.state.timelines.map(e => {
        console.log(e)
        // when boosted
        let memo = null
        if (e.reblog) {
            memo = (<p>{e.account.display_name}さんがブーストしました</p>)
            e = e.reblog
        }
        // content every toot
        return (
        <div key={e.id}>
            <img src={e.account.avatar} />
            <div>{memo}{e.account.display_name}<span dangerouslySetInnerHTML={{__html: e.content}} /></div>
            <div style={{clear: 'both'}} />
        </div>
        )
    })
    return (
        <div>
            <h2>TimeLines</h2>
            {lines}
        </div>
    )
}

むずかしいね

6 css

お好みで。

アプリの実行

あとはwebpackでビルドしてelectron .で実行してあげるだけ。

以下のように立ち上がったら完成です。

f:id:puyobyee18:20180924113003p:plainf:id:puyobyee18:20180924113012p:plain

お疲れ様でした。(つかれた)

参考サイト一覧

API

OAuth2

【JavaScript】JS自動補完パッケージ:atom-ternjsの設定

atom-ternjsによるJavaScript補完機能を構築するには…


Ternとは

Github上で公開されているTern.js(またはこちら)はJavaScript用のコード解析エンジンのひとつ。atomでも「atom-ternjs」という名でパッケージが公開されており、ユーザ数は50万人近くにものぼる

環境

Windows × atom

ことの発端

  1. atom-ternjsをインストール。
  2. しかし補完してくれない(例:"docu..."と入力しても、doしか出てこない)。しかもインスタンスの補完もイマイチ
  3. 色々ググってみた結果…

どうやらconfigureしなければいけないっぽい?!

ググってみると以下のサイトどちらも ".tern-project" というJSONファイルを作っている(正確にはJSONではないらしい…?)

https://qiita.com/s-shin/items/33bdfc5b819dab320808 http://blog.aqutras.com/entry/2016/04/28/210000

しかしJSONという気味の悪い拡張子の知識がなくて読めない&書けない

なかなか解決策が思いつかない。補完機能が使えないまま書くしかないのか…と思った矢先

YouTubeにあったatom-ternjsのインストール動画をたまたま発見。
メニューバー→パッケージ→Atom-Ternjs→Config projectの手順でConfigureの書き方を紹介。

bowserとjqueryにチェックしてからサーバの再起動を掛けると…
プロジェクトフォルダ内に .tern-project ファイルが!!!

{
  "ecmaVersion": 6,
  "libs": [
    "browser",
    "jquery"
  ],
  "loadEagerly": [],
  "dontLoad": [
    "node_modules/**"
  ],
  "plugins": {
    "doc_comment": true
  }
}

無事補完機能が動いた!

ひとまず動くようにはなったけど

課題

  • 毎回configしなくちゃいけないの?
  • package.jsonがあるフォルダに.tern-projectファイルを作っても機能せず。
  • .tern-project のほかに .tern-configファイル が必要?!

などなど

追記

atom-ternjsの公式サポートを読んでみると普通に書いてたっぽい。(英語
おそらくThurd party pluginsに書かれているファイルを atom-ternjs直下に入れれば毎度configしなくてよさそう...(?)

【Python】csvやtxt形式でのデータの取り込み

csv形式やtxt形式で外部ファイルに保存されたデータをPython上で取り込みたいとき、みなさんはどうしているだろうか。
一般に方法は3~4通りあり、

  • csvモジュールを使う
  • numpyを使う
  • pandasを使う

に大別されるであろう。普段私は速度重視でnumpyを好んで使っているので以下のようなコードで取り込みを行っている。

import numpy as np
import matplotlib.pyplot as plt
data = np.loadtxt('./hogehoge.txt', delimiter = '\t', skiprows = hoge)
plt.plot(data[0,:], data[1,:])
plt.show()

至って基本的である。その他の方法も各々メリットデメリットがあるのだが、一つ困った点が…
それはどれも決められた単一の区切り文字でしか処理できない点にある。
データの抜けがある場合はgenfromtxtで対応できるのだが、これは未だに対応策が思いついていない。

リニアテクノロジー社が提供する回路シミュレータであるLTSpiceは機能こそ充実しているものの、出力の形式が独特で悩ましい。
(出力電圧、位相)をカンマ区切りで出力すればいいものを、"frecency(Hz) \t ( voltage(dB)dB, phase(degree) --)" としてくるものだから困ったものである。
おそらく力技で攻めるなら read() とsplit() を駆使する方法であろう。しかしそれではとても面倒…
現状ではExcelで成形することで対応しているが、良い方法があったら教えていただきたい。

5/9~13

またも実験レポートの波に飲まれて忙しい…

とりあえず最近の進捗。

 

1. JavaScriptフレームワーク入門」(掌田津那乃,秀和システム)

TypeScriptの章を読破。TSはJavaScript特有の過剰(?)キャスト機能やオブジェクト指向を修正している。固有の文法も受容しやすいものばかりであるのでJSの代わりに積極的に使っていきたい。

Vue.jsの章を読み始めた。NodeやBackboneも読みたいしこの本は購入してしまおうか悩む。

 

2.実験

レポート現在作成中。pn接合、JFET、ソース接地増幅回路を学習した。

静特性のプロットはおおよそ理論通り…と思ったが正直大学のデジタルテスタ(やその他素子の性能)では逆バイアス時の電流は測定できないし、JFETを流れるドレイン電流のV_DS^2に対する係数も分からなかったので理論曲線というよりただのfittingにとどまってしまったのが残念。まぁ一応ソース接地増幅回路ではチャネル長変調効果も確認できたし御の字では。ところどころ理解していないところもある(小信号等価回路で負荷抵抗に並列して発生する浮遊容量とか)あるけど…

5/8

今日はMDNではなく「JavaScriptフレームワーク入門」(掌田津那乃,秀和システム)を読む。図書館で借りていたことを忘れていた。

 

進捗

1.jQueryの章を読破。JavaScriptをいかに短く書くかにこだわったライブラリのよう。一昔前にjQueryからJavaScriptに慣れろ、と言われていたのがよく分かる。

2.TypeScriptの序章を読む。JavaScriptの欠点を補う、とうたっているが未だ型宣言のことしか出てこない。というか「動的型付け」と言えどJSってキャスト機能強すぎでは…

 

疑問

1.Ajax通信、非同期通信のことはよく分からず。

2.TypeScriptをつかってみたいのにnpmからコンパイラをインストールする必要があるらしい。npmの章を先に読むべきか…

 

その他

1.「UniteTokyo2018」なるものが開催されているらしい。Unityにも触れてみたいんだよなぁと思ったり。Unityを使えるようになる分にはC#の学習効率は高いらしいのでいつか勉強したい。

5/7

今日は課題に余裕があるのでMDN(以下参照)でJavaScriptの勉強。

developer.mozilla.org

 

進捗

1.リテラル:値の表現を端的に文字化したもの。データ型のうち、「真偽値」「数値」「文字列」「配列(リスト)」「正規表現」など各々に存在。

     「真偽値」→true/false

     「数値」 →整数、浮動小数、…

     「文字列」→” ”で囲まれたモノ。シングル・ダブルの区別はない

     「リスト」→[hoge, hoge, hoge, ...]と表される。Arrayオブジェクトである。

     「オブジェクト」→後述

2.オブジェクト:名前のついた箱のようなもの。値、プロパティ(性質)を格納する。

      const object = {};       //objectという名前の空の箱(オブジェクト)ができた。

      const object = {property1 : value, property2 : value, ....} とすると

      object.propertyn でpropertynに格納されている値が出てくる。

     オブジェクトをさらに入れ子状にすることもでき、プロパティ名は数値でも文字列でもよい。

 

非進捗

1.「関数」と「関数式」の使い分け

     function foo() {

          console.log('Hello');
     }

     と
     var foo = function() {

          console.log('Hello')
     }

     の違いがよく分からない(特に後者)。まぁC言語勉強してた時も「関数のポインタ」の理解がイマイチだったしプログラムにおける「関数」の概念が苦手なのかも。

2.プリミティブな真偽値としてのtrue/falseと、Booleanオブジェクトとしてのtrue/falseの違い

     プリミティブな真偽値は性質というか、データ型の基本。

     一方Booleanオブジェクトは→?

 

制御フロー文まで進もうと思ってたのに眠くなったので終わり。