Slack の ボット で 今日の天気を通知する - JSON 確認編

OpenWeatherMap の サービスを使って、天気情報を取得できるようになりました.
今回は取得した JSON の 内容を確認し、必要な情報が使えるようにしたいと思います. 雨と雪の情報もほしかったので、札幌市の天気を取得しました.

作業環境

  • Node.js 6.9.1 LTS

現在の天気情報 JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{ coord: { lon: 141.35, lat: 43.06 },
weather:
[ { id: 501,
main: 'Rain',
description: 'moderate rain',
icon: '10d' } ],
base: 'stations',
main:
{ temp: 4.01,
pressure: 994.64,
humidity: 97,
temp_min: 4.01,
temp_max: 4.01,
sea_level: 1008.09,
grnd_level: 994.64 },
wind: { speed: 9.56, deg: 194.002 },
rain: { 3h: 4.905 },
clouds: { all: 88 },
dt: 1485498017,
sys:
{ message: 0.0083,
country: 'JP',
sunrise: 1485467687,
sunset: 1485502824 },
id: 2128295,
name: 'Sapporo-shi',
cod: 200 }
ノード 参考値 概要
coord { lon: 141.35, lat: 43.06 } 都市の緯度経度
weather.id 501 天気状態 の ID*
weather.main Rain 天気状態 の グループ名*
weather.description moderate rain 天気状態*
weather.icon 10d 天気アイコン ID*
main.temp 4.01 気温
main.pressure 994.64 気圧、単位は hPa
main.humidity 97 湿度、単位は %
main.temp_min 4.01 現時点での最低気温
main.temp_max 4.01 現時点での最高気温
main.sea_level 1008.09 海上の気圧、単位は hPa
main.grnd_level 994.64 地上の気圧、単位は hPa
wind.speed 9.56 風速、単位は メートル/秒
wind.deg 194.002 風向、北 0 の 時計回り
clouds { all: 88 } 曇り度合、単位は %
rain.3h 4.905 過去3時間の降雨量
snow.3h 過去3時間の降雪量
dt 1485498017 データ計算時間、Unix,UTC
sys.country JP 国コード
sys.sunrise 1485467687 日の出時間、Unix,UTC
sys.sunset 1485502824 日の入り時間、Unix,UTC
id 2128295 都市 ID
name Sapporo-shi 都市名
  • Weather condition codes に 対応表がある
    ※ API ドキュメントに Internal parameter とあるものは除く

3時間ごと 5日間 の 天気予報 JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{ city:
{ id: 2128295,
name: 'Sapporo-shi',
coord: { lon: 141.346939, lat: 43.064171 },
country: 'JP',
population: 0,
sys: { population: 0 } },
cod: '200',
message: 0.1264,
cnt: 38,
list:
[ { dt: 1485507600,
main:
{ temp: 0.15,
temp_min: 0.15,
temp_max: 0.15,
pressure: 1000.35,
sea_level: 1013.94,
grnd_level: 1000.35,
humidity: 91,
temp_kf: 0 },
weather:
[ { id: 500,
main: 'Rain',
description: 'light rain',
icon: '10n' } ],
clouds: { all: 56 },
rain: { 3h: 0.2975},
snow: { 3h: 0.1575},
wind: { speed: 8.3, deg: 258.503 },
sys: { pod: 'n' },
dt_txt: '2017-01-27 09:00:00' },
{ ... (3時間ごとの繰り返し) }
]
}
ノード 参考値 概要
city.id 2128295 都市 ID
city.name Sapporo-shi 都市名
city.coord { lon: 141.346939, lat: 43.064171 } 都市の緯度経度
city.country JP 国コード
cnt 38 レコード件数
list.dt 1485507600 データ計算時間、Unix,UTC
list.main.temp 0.15 気温
list.main.temp_min 0.15 計算時の最低気温
list.main.temp_max 0.15 計算時の最高気温
list.main.pressure 1000.35 気圧、単位は hPa
list.main.sea_level 1013.94 海上の気圧、単位は hPa
list.main.grnd_level 1000.35 地上の気圧、単位は hPa
list.main.humidity 91 湿度、単位は %
list.weather.id 500 天気状態 の ID*
list.weather.main Rain 天気状態 の グループ名*
list.weather.description light rain 天気状態*
list.weather.icon 10n 天気アイコン ID*
list.clouds.all 56 曇り度合、単位は %
list.wind.speed 8.3 風速、単位は メートル/秒
list.wind.deg 258.503 風向、北 0 の 時計回り
list.rain.3h 0.2975 過去3時間の降雨量
list.snow.3h 0.1575 過去3時間の降雪量
list.dt_txt 2017-01-27 09:00:00 データ計算時間、UTC
  • Weather condition codes に 対応表がある
    ※ API ドキュメントに Internal parameter とあるものは除く

データの利用について検討

現在の天気と予報では JSON の パラメータ名が異なるものの、おおよそで同じような内容になっています.

天気を表す部分では weather.main が 中心で、グループ名(main) もしくは 天気状態(description) や アイコンを使って天気を表す感じでしょうか.
アイコンは http://openweathermap.org/img/w/10d.png の 最後の部分 10d に JSON で 取得した値を入れることで取得できます. 昼夜で 10d10n のようにアイコンが変わるようです.

気温などは main に 入っています. ここは単位を付けるだけで簡単に使えそうです. temp_mintemp_max は 大きい都市で気温差が出るようなケースに値が変わるようです.

風、特に風向きについては北を 0° として表現するようです. 現在の天気の JSON では wind: { speed: 9.56, deg: 194.002 } と なっていたので、南南西の風 9.5 メートル/秒 ですね.

降雨量 と 降雪量 は rain.3hsnow.3h に 過去3時間のデータが入ります. 降っていない場合は JSON に パラメータがないか、3h の パラメータがない状態となります. プログラム言語によって工夫が必要かもしれません. API ドキュメントには単位が乗ってませんでしたがミリメートルと思われます. 雨と雪の両方のデータが入っているケースがあり、その場合はどんな状況なのか、ちょっと気になります.

現在の天気のデータ計算時間は dt に 入っており、Unix,UTC と なっているのですが、Unix です. dtsunrisesunset を 扱う場合は、プログラム言語の処理系に合わせての対応が必要です.


必要なものがそろっており、JSON で 簡単にアクセスできるので助かります.
あとは、このデータを使わせてもらって、必要な時に必要な情報を流せるボットを作るだけですね!

Windows 7 で OpenSSL を 使えるようにする

ちょっと OpenSSL が 必要なことってありませんか?(いや、そうそう無いか…)
普段使用している環境は Windows で、OpenSSL を 簡単に使うことができません. ちょうど OpenSSL が 必要な困った事案が発生、また環境構築にはまったので記録を残しておきたいと思います.

作業環境

  • Windows 7 64bit
  • Microsoft Windows SDK v7.1
  • Strawberry Perl 5.24.0.1 64bit
  • OpenSSL 1.0.2j LTS

ビルド環境の準備

OpenSSL は バイナリを配布していません. 自分でビルドしないと使えません. インターネットは広いので親切な方がビルドしたものを配布してくれていますが、今回は自前でビルドする環境を用意したいと思います.

C++ コンパイラ の 用意

まず C++ の コンパイラが必要です. “April 2005 Platform SDK is equipped – OpenSSL/INSTALL.W64” と なっていますが、”古い SDK が必要な場合は、以下の MSDN サブスクリプションのダウンロードをご検討ください – JAPAN Platform SDK(Windows SDK) Support Team Blog“ とのことで、有償です…
というか、ちゃんと現在の OS に 合わせた SDK を 使う必要があるので、Windows 7 の SDK を 取ってきます.
こちらから Microsoft Windows SDK for Windows 7 and .NET Framework 4 からダウンロードしてインストールします.
最低限のインストール・オプション は 以下となります.

  • Windows Headers and Libraries
  • Visual C++ Compilers
  • Microsoft Visual C++ 2010 (Redistributable)

Perl の 用意

続いて、Perl が 必要となります. これまた Windows ユーザ としては困るところです…
ドキュメントでは “You can run under Cygwin or you can download
ActiveState Perl – OpenSSL/INSTALL.W64” となっています. [ActivePerl(http://www.activestate.com/activeperl)] は 沢山お世話になり今回もお世話になるところですが、最近は Strawberry Perl なるものもあるようです.

cpanppm などの 違いがあったりするようですが、今回 Strawberry Perl を 選択したのは、PortableZIP edition が あったからになります.
OpenSSL の Configure を 実行したいだけなので、Zip を 解凍するだけで使えるはとてもありがたいです. できればインストーラであれこれ入れてほしくないし、フォルダの削除だけで全て無かったことにできるのは助かります. ということで、Strawberry Perl で 行きます.

ウェブサイトからダウンロードして解凍して終了です.
今回は C:\Develop\sdk\strawberry-perl-5.24.0.1-64bit-portable に 解凍したものとします. README.txt に 書かれていますが、スペースや日本語が含まれないディレクトリにするとのことです.

参考情報
Perl の 選択肢については、以下のサイトを参考にさせて頂きました. すばらしい情報ありがとうございます!

OpenSSL を ビルド

OpenSSL の ソース を ダウンロードします. OpenSSL の ウェブサイト から取得しますが、Web サーバ で 使うわけではないので、安定版ということで 今回は LTS(Long Term Support) の openssl-1.0.2j.tar.gz を 選択しました.

ダウンロードしたアーカイブを解凍します.
今回は C:\Develop\tool\openssl-1.0.2j に 解凍したものとします.

続いて Windows SDK 7.1 Command Prompt を 起動します. (通常 の コマンド プロンプト ではないことに注意)

1
2
3
4
5
6
7
8
9
10
11
c:\Develop\tool\openssl-1.0.2j> set PATH=%PATH%;c:\Develop\sdk\strawberry-perl-5.24.0.1-64bit-portable\perl\bin
c:\Develop\tool\openssl-1.0.2j> perl Configure VC-WIN64A
c:\Develop\tool\openssl-1.0.2j> ms\do_win64a
c:\Develop\tool\openssl-1.0.2j> nmake -f ms\ntdll.mak
c:\Develop\tool\openssl-1.0.2j> cd out32dll
c:\Develop\tool\openssl-1.0.2j> ..\ms\test
...(省略)
passed all tests
c:\Develop\sdk\openssl-1.0.2j\out32dll>openssl version
OpenSSL 1.0.2j 26 Sep 2016

無事、ビルドできました!

鍵生成 や 自己署名証明書発行 の テスト

OpenSSL の 利用にあたっては、通常 の コマンド プロンプト で 大丈夫です.
実行に当たっては環境変数の設定が必要なものがあったりしますので注意が必要です. (ちゃんとインストールしようよということでもあるのですが、ちょっと使うだけだから… と 言い訳してみる)

1
2
3
4
5
6
7
8
9
c:\Temp> set PATH=%PATH%;c:\Develop\tool\openssl-1.0.2j\out32dll
c:\Temp> set RANDFILE=%TEMP%\.rnd
c:\Temp> set OPENSSL_CONF=c:\Develop\tool\openssl-1.0.2j\apps\openssl.cnf
c:\Temp> openssl genrsa -aes256
c:\Temp> openssl genrsa -out test.key 4096
c:\Temp> openssl req -x509 -new -key test.key -out test.pem

はまったところ

Visual C++ Compilers が 選択できない

.NET 4 (4.x ではなく 4) が 必要になります. しかも、新しいバージョンが入っているとインストールできないというトラップがありました… 新しいバージョンが入っている場合はアンインストールして、古いバージョンから順番に入れなおす必要があります.
.NET の バージョンについては、こちら Tech TIPS:.NET Frameworkのバージョンを整理する - @IT が 詳しいです.

インストーラは正常終了しているのに、インストールされていない

何が起こったのかよくわからない事象で、見事にどはまりしました orz
インストーラは正常終了したように見えているのに、肝心のプログラムが入っていない状況が起こりました. 1 GB の インストールにしては、やたら早く終わったなぁというのが気になったぐらいです.

どうやら、こちら Windows SDK for Windows 7.1 をインストールするとエラーが発生する - Windows - Project Group の 現象だったようです. “A problem occurred…” なんて表示されなかったようにも思いますが、色が付いているわけでもないので…
上記サイトの情報をもとに、Microsoft Visual C++ 2010 x64/x86 Redistributable を 確認したところ、見事に x64 の方が入っていました. アンインストールしてから再度 SDK を 入れたところ、無事にインストールができました. こちらのサイトの情報がなかったら完全にアウトだったかもしれません. 助かりました. 有益な情報ありがとうございます!


いろいろと難所はありましたが、無事に OpenSSL を Windows に 入れることができました.
Windows 10 の Bash on Ubuntu on Windows なら、こんな苦労をしなくても良いのかなぁと思いつつも、まだまだ Windows 7 を 使うのでした…

Slack の ボット で 今日の天気を通知する - JSON 取得編

ボットを使って、Slack に 今日の天気を通知してもらうようにしたいと思います.
天気関連はいろいろなサービスがあり、それを使うのも手なのですが自分でカスタマイズした細かいところにこだわったツールにしたいと思うので、ボットに教えてもらうことにします.

作業環境

  • Node.js 6.9.1 LTS

天気の情報を提供してくれるサービス

今回は OpenWeatherMap の API を 使って情報を取得したいと思います.
Openweathermap - Wikipedia によると、OpenWeatherMap は 各種気象データを 無料 で API 提供してくれるサービスとのことです. しかも全データは、CC BY-SA に 準じるとのことで、情報の改変や商用利用が許可されています. ありがたい!
※ OpenWeatherMap の ToS は こちら Terms of Service

無料で利用できますが、主な利用条件は以下となります.

  • 1分間 の API 呼び出し回数は 60回
  • 現在の天気 に 加え、3時間ごと 5日間の予報が使える
  • 天気情報の更新 は 2時間以内
    ※ 情報の更新について、Wikipedia に 10分以内となってますが、10分以内は Pro 以上で、Free は 2時間以内となっている

参考情報
天気情報が提供されているサービスについては、こちらのサイトを参考にさせて頂きました. 素晴らしい情報 ありがとうございます!

API キー の 取得

OpenWeatherMap の Sing Up サイト https://home.openweathermap.org/users/sign_up へ アクセスします.
全ての項目を入力、ToS/PP を 確認し同意したら I agree ~~~ に チェックして、[Create Account] ボタンをクリックします.

利用目的を聞かれるので、法人利用の場合は [Company] も 入力し、[Purpose] を 選択します.

無事、API キー が 発行されました.
Welcome メール は 届きますが URL クリックによる認証などがなく、簡単にサインアップさせてくれるので助かります.

現在の天気情報を取得する

Botkit に 組み込むので、Node.js で コーディングします.
※ [API_KEY] を 上記で取得した API キー に 置き換えます.

1
2
3
4
5
6
7
8
9
const http = require('http');
http.get("http://api.openweathermap.org/data/2.5/weather?id=1850147&units=metric&appid=[API_KEY]", (response) => {
let buffer = '';
response.setEncoding('utf8').on('data', (chunk) => { buffer += chunk; });
response.on('end', () => {
let current = JSON.parse(buffer);
console.log(current)
});
});

HTTP GET で 取得して、JSON 出力をする簡単なものになります. 特別な実装部分はありません.
URL の パラメーターは以下となります.

  • id=1850147 は、天気情報を取得する 都市 の ID (今回は東京、ID の 取得は後述)
  • units=metric は、摂氏華氏(°C/°F) を気温を摂氏にするために指定 (デフォルトは華氏)
  • appid=[API_KEY] で、API キーを指定

実行すると以下のような JSON が 出力されます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{ coord: { lon: 139.69, lat: 35.69 },
weather:
[ { id: 801,
main: 'Clouds',
description: 'few clouds',
icon: '02d' } ],
base: 'stations',
main:
{ temp: 6.85,
pressure: 1019.67,
humidity: 80,
temp_min: 6.85,
temp_max: 6.85,
sea_level: 1023.45,
grnd_level: 1019.67 },
wind: { speed: 2.96, deg: 2.5058 },
clouds: { all: 12 },
dt: 1485145194,
sys:
{ message: 0.0063,
country: 'JP',
sunrise: 1485121634,
sunset: 1485158355 },
id: 1850147,
name: 'Tokyo',
cod: 200 }

天気は、weather[0].main に なります. なぜ weather が 配列なのかはちょっとわかりませんが…
なお、今回の例では Clouds なので曇りです.
JSON の 内容については、次回にしたいと思います.
※ OpenWeatherMap の 現在の天気 API 仕様 は こちら、Current weather data

3時間ごと 5日間 の 天気予報を取得

先の実装と変わるのは URL の パス が /weather から /forecast に なる点です.

1
2
3
4
5
6
7
8
9
const http = require('http');
http.get("http://api.openweathermap.org/data/2.5/forecast?id=1850147&units=metric&appid=[API_KEY]", (response) => {
let buffer = '';
response.setEncoding('utf8').on('data', (chunk) => { buffer += chunk; });
response.on('end', () => {
let forecast = JSON.parse(buffer);
console.log(forecast)
});
});

実行すると以下のような JSON が 出力されます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{ city:
{ id: 1850147,
name: 'Tokyo',
coord: { lon: 139.691711, lat: 35.689499 },
country: 'JP',
population: 0,
sys: { population: 0 } },
cod: '200',
message: 0.0147,
cnt: 38,
list:
[ { dt: 1485151200,
main:
{ temp: 6.12,
temp_min: 6.12,
temp_max: 6.12,
pressure: 1022.42,
sea_level: 1023.53,
grnd_level: 1022.42,
humidity: 59,
temp_kf: 0 },
weather:
[ { id: 802,
main: 'Clouds',
description: 'scattered clouds',
icon: '03d' } ],
clouds: { all: 36 },
wind: { speed: 7.62, deg: 319.001 },
sys: { pod: 'd' },
dt_txt: '2017-01-23 06:00:00' },
{ ... (3時間ごとの繰り返し) }
]
}

天気は、予報 38回分なのでリスト list に 入っています. その中の構造は現在の天気とほぼ同じです.
直近の予報を取得するには list[0].weather[0].main に なります. 今回の例では Clouds なので曇りです.
※ OpenWeatherMap の 天気予報 API 仕様 は こちら、5 day weather forecast

都市 ID の 取得方法 と 他の検索方法 について

OpenWeatherMap の 天気情報および予報について、取得する都市や地域の指定は以下の 4種類で指定できます.

指定方法  クエリ例 概要
都市名  q=tokyo,jp 都道府県以下は曖昧になり意図とは異なる場合も.
都市 ID  id=1850147 OpenWeatherMap の ID 指定. 確実!
地理座標  lat=35.69&lon=139.69 明確な指定方法と思われるが緯度経度を調べるのが大変…
郵便番号  zip=1000005,jp これも明確な指定方法と思われる

いろいろな方法がありますが当然のことながら、すべての都市や緯度経度に天気観測のステーションがあるわけではなく、近隣のステーション情報へのマッピングがされていると考えると、都市 ID が 一番確実だと考え、今回は 都市 ID で 指定するようにしました.

また、公式にも “We recommend to call API by city ID to get unambiguous result for your city. – OpenWeatherMap“ と 謳われているので、都市 ID で 指定したほうがよいのでしょう.

都市 ID は http://bulk.openweathermap.org/sample/ から取得します.

city.list.json.gz が 都市情報のデータになります.
全世界の都市データが入っており、2017年1月現在で 全 209,578 件 で、"country":"JP" でも 1,402件です. すごい.
そして、なぜか、御影 芦屋 松山市 は 漢字で都市名が入っている. なぜだろう…

以下に 日本 と 東京、御影芦屋松山市 を 抜粋し、Google マップ による緯度経度検索結果の場所を載せておきます.
_id の 値が 都市 ID になります. (実際はスペースなし、投稿用に整形済み)

1
2
3
4
5
6
7
8
9
{ "_id": 1861060, "name": "Japan", "country": "JP", "coord": { "lon": 139.753098, "lat": 35.68536 }} // 皇居
{ "_id": 1850147, "name": "Tokyo", "country": "JP", "coord": { "lon": 139.691711, "lat": 35.689499 }} // 東京都庁
{ "_id": 1850144, "name": "Tōkyō-to", "country": "JP", "coord": { "lon": 139.691711, "lat": 35.689499 }} // 東京都庁
{ "_id": 1864529, "name": "Chiyoda-ku", "country": "JP", "coord": { "lon": 139.753632, "lat": 35.694019 }} // 千代田区役所
{ "_id": 7302982, "name": "御影", "country": "JP", "coord": { "lon": 135.252426, "lat": 34.724258 }} // 阪急 御影駅?
{ "_id": 7302983, "name": "芦屋", "country": "JP", "coord": { "lon": 135.30719, "lat": 34.733921 }} // JR 芦屋駅?
{ "_id": 1864985, "name": "Ashiya", "country": "JP", "coord": { "lon": 135.302643, "lat": 34.728069 }} // 阪急 芦屋駅?
{ "_id": 7303001, "name": "松山市", "country": "JP", "coord": { "lon": 132.756729, "lat": 33.83905 }} // 松山市内?
{ "_id": 1926099, "name": "Matsuyama-shi", "country": "JP", "coord": { "lon": 132.765747, "lat": 33.839161 }} // 松山市役所


簡単に利用できる API を 無料で公開してくれるサービスのおかげで、容易に天気情報が取得できました. 毎朝の天気を Slack に 流すだけなら、もう Botkit に 組み込めそうですが、取得できたデータをしっかり把握したいので、次回は JSON の 内容について確認したいと思います.
Slack の ボットで定時アクション が できるようになったところで、定番の天気予報に入らず Slack の ボット で JRA 競馬 の 開催日を通知 と いきなり脱線しましたが、軌道修正して基本の天気予報を流せるようにしていきたいと思います.

Slack の ボット で JRA 競馬 の 開催日を通知する - Slack ボット 実装編

前回、JRA の サイト から 開催日 iCalendar を 取得し、ついに Slack の ボットへ組み込む準備ができました! ボットへ組み込み、開催日を教えてもらいましょう.

作業環境

  • Slack
  • Node.js 6.9.1 LTS
  • Botkit 0.4.9
  • unzip 0.1.11
  • ical2json 1.0.0
  • node-cron 1.2.1
  • moment-timezone 0.5.10

通知方法の検討

今回は GI レースに限定し、開催日 10日前 の 午前10時 に 通知するようにしたいと思います. Slack の チャンネルは、とりあえず実験用の #sandbox へ ポストするようにして様子見します.
土日などの連続開催がある場合、開催日 10日前 だと連続して通知が来るようになってしまいますが、GI レースに限るとあまりないのでよしとします. GII や GIII が 入ってくると、土日連続がかなりあるようなので本格的に通知する場合は、同時通知できるようにした方がよさそうです.

Slack ボット の 実装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
'use strict';
const Botkit = require('botkit');
const http = require('http');
const cron = require('cron');
const unzip = require('unzip');
const moment = require('moment-timezone');
const ical2json = require('ical2json');
const controller = Botkit.slackbot();
controller.spawn({
token: process.env.bot_access_token
}).startRTM((err, bot, payload) => {
moment.locale('ja');
moment.tz.setDefault('Asia/Tokyo');
http.get(`http://www.jra.go.jp/keiba/common/calendar/jrarace2017.zip`, (response) => {
let buffer = '';
response.pipe(unzip.Parse()).on('entry', (entry) => {
entry.on('data', (chunk) => { buffer += chunk; });
entry.on('end', () => {
let ical = ical2json.convert(buffer)['VEVENT'];
for (let i in ical) {
let data = ical[i];
if (data['SUMMARY'].includes('GII')) { continue; }
new cron.CronJob({
cronTime: moment(`${data['DTSTART;VALUE=DATE']}T1000+0900`).subtract(10, 'days').toDate(),
onTick: () => {
let date = moment(this.race['DTSTART;VALUE=DATE']).format('M/D(ddd)');
let race = this.race['SUMMARY'].substring(0, this.race['SUMMARY'].indexOf('('))
bot.say({
text: `${date}${race}${this.race['LOCATION']} だよ`,
channel: 'sandbox'
});
},
start: true,
timeZone: 'Asia/Tokyo',
context: { race: data }
});
}
});
});
});
});

Slack ボット の 基本的な作りは Slack の ボット で 定時アクション の 実装と変わりがありません. また、iCalendar の 取得部分も Slack の ボット で JRA 競馬 の 開催日を通知する - iCalendar 取得編 と なります. これらの組み合わせでできています.

cron.CronJob が HTTP GET の後に、繰り返しで作られているところがポイントになります.
開催日の情報を moment-timezone モジュールでパースし通知したい時刻とタイムゾーンを付けます. そして subtract(10, 'days') で 10日前の日付にし、cronTimeDate オブジェクトとして渡します. node-cron は crontab の 文字列だけでなく Date オブジェクトも渡すことができるので、このように特定日の10日前といった指定ができます.

あとは onTick で ボットに話してもらうだけなのですが、ボットが話す際に開催情報が必要となります. これは new cron.CronJob() する際に、context で ジョブが起動した際に渡したいオブジェクトを指定することで可能となります. 今回は context: { race: data } とすることで iCalendar の データ data オブジェクト を race という名前でコンテキストに登録しておきました.
onTick の 際には、this.race と コンテキストに渡した名前に this を つけてアクセスします.

※ 今回は #sandbox へ 発言するので channel: 'sandbox' としていますが、ID で 指定したほうがよいので https://slack.com/api/channels.list?token=[API_TOKEN] へ アクセスして、ID を 調べます. また、発言先のチャンネルへボットが参加している必要があります.

通知!


今年最初の GI レースは 2月19日(日曜日) フェブラリーステークス @東京競馬場 が 通知されました!
ちょっと先なので、実験用に cronTime の 指定をいじりました. T1000+09001000 でなく動作検証をする時間にし、
subtract(10, 'days') を 2月19日 から 動作検証する日まで引いてあげます.
本投稿を書いているタイミング 1月21日 23時ごろで考えると T2300+0900subtract(29, 'days') に なります.


ボットで JRA の GI レース開催日 を Slack に 通知することができました. これで予め準備の上で観戦に臨めそうで楽しみです. (その前に基本的なことを勉強しておかないと…)

定時アクションができるようになり 定番の天気予報に着手しようと思っていましたが、思わぬ方向の機能を作ることになりましたが、おもしろい物ができたと思います. 思いついた時こそ勉強のチャンスですね.

Slack の ボット で JRA 競馬 の 開催日を通知する - iCalendar 取得編

前回、JRA の サイト から 開催日 JSON を 取得しました. ただ この方法については公開されているものを使っておらず、ウェブサイトのためのデータを勝手に使っているので お行儀が悪いです.
適切なデータを使って開催日の情報を扱えるようにしたいと思います.

作業環境

  • Node.js 6.9.1 LTS
  • unzip 0.1.11
  • ical2json 1.0.0

開催日情報の取得方法について「再」検討

データを取得する方法について、もう少し検討を進めたいと思います.
重賞レース一覧 の ページがあり、こちらは HTML に 開催日情報 が 記述されています. スクレイピングすることでデータ化することができそうですが、HTML の 構造が変わるなどすると使えなくなってしまうので、最終手段にとっておきたいところです.

よくよくウェブサイトを見ていくと、レーシングカレンダー・ページ の 末尾 に 他のカレンダー と 連携 の リンク が ありました!

この 他のカレンダー と 連携 ページを確認すると、iCalendar 形式のファイルをダウンロードすることができるとのことです.

これなら公開されているデータなので、安心して使うことができます. 年間開催のスケジュール と 年間の重賞スケジュール の 二つが用意されているようで、今回は重賞スケジュールのファイルを取得することにします.
また “開催中止等に伴うスケジュールの変更には対応しておりません。予めご了承ください。 – JRA” とのことですが、今回は開催日を通知することが目的で厳密なスケジュール運用は必要としないので、よしとします.

iCalendar の 取得方法

前回と変わらず http モジュールで取得したいと思います.
今回は iCalendar ファイル が Zip で アーカイブされているので、その取扱いが必要となります.
Zip の 解凍は HTTP の レスポンスを Stream で 流し込めるので unzip モジュールを使わせてもらいました. また iCalendar の 扱いは JSON に してほしかったので ical2json モジュールを使わせてもらいます. それぞれ標準では入っていないので npm install [module name] --save で インストールしておく必要があります.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const http = require('http');
const unzip = require('unzip');
const ical2json = require('ical2json');
http.get(`http://www.jra.go.jp/keiba/common/calendar/jrarace2017.zip`, (response) => {
let buffer = '';
response.pipe(unzip.Parse()).on('entry', (entry) => {
entry.on('data', (chunk) => { buffer += chunk; });
entry.on('end', () => {
let ical = ical2json.convert(buffer)['VEVENT'];
for (let i in ical) {
let data = ical[i];
console.log(`${data['DTSTART;VALUE=DATE']} ${data['SUMMARY']}`);
}
});
});
});

構造としては、前回 の JSON 取得と変わりありません.
response を 直接扱うのではなく unzipParse() に パイプして、HTTP GET で 取得するデータを直接 unzip に 流すところがポイントになります.
これにより 一時ファイルに落とさず直接解凍して処理を行うことができます. 今回も解凍して 45 KB ちょっとなので、変数に入れてメモリ上で処理してしまいます.
あとは ical2json モジュールが iCalendar の フィールド名 を キーとして JSON に してくれるので、プログラムで簡単に扱うことができます.


無事に公開されているデータで開催日を取得することができました. JSON のように データが構造化しきれないので、グレード は 自前で文字列処理が必要となりますが、十分利用できそうです. なにより 1年分が 1回で取れるのがよいですね.
ファイル置き場の URL が 変わらなければ、年数の部分だけ自動処理すれば使いづづけられそうです. 年単位なので いきなり変わることもありそうだけど…

Node.js には いろいろなモジュールがあるので選択に迷いますが、素晴らしいモジュールを提供してくださっている方々のおかげで簡単に実装できるので助かります. ありがとうございます!

Slack の ボット で JRA 競馬 の 開催日を通知する - JSON 取得編

2016年 の 有馬記念 を 見て、急に競馬が気になりだしました. とはいえ、ちゃんとやったこともないし、いつやっているのかも知らない、といったレベルなので、次週の開催予定をボットに通知してもらうようにして、下調べぐらいはしてから見れるようにしたいと思います.
ボットにしゃべってもらうにはデータが必要なので、まずは開催日のデータ探しと取得方法について考えます.
→ 公開されているデータを取得するよう Slack の ボット で JRA 競馬 の 開催日を通知する - iCalendar 取得編 の 記事を追加しました. (2017年1月18日追記)

作業環境

  • Node.js 6.9.1 LTS

開催日情報の取得方法について検討

JRA の サイトに レーシングカレンダー があり、さまざまな情報が載っています. しかし開催情報を取得できるような Web API は なさそうです. また、そのような情報を提供しているサイトも見当たりませんでした. (なんか、ありそうな気もするんですが…)

レーシングカレンダー の ソースを見ると、JavaScript で カレンダー生成を行っていることが分かります. ソースには 2015 と ありますが、2017年も順調に機能しているようです.

その中に JSON ファイルを取得している部分があります! この JSON ファイル を 使う手もありそうです.

過去の JSON ファイルを探っていくと、2012年1月 が 一番古いようです. カレンダーの描画部分は 2015年にリニューアルしたとして、システム自体は 2012年には稼働していたのでしょうか.

JSON の 取得方法

とりあえず、この JSON を 取得してみたいと思います.
Node.js で HTTP リクエストするには、http モジュールを使います. 標準で組み込まれているので簡単に使い始められます. もう少し手軽に扱ったり、複雑なことをするには request が よいようですが、今回は簡単な HTTP GET なので http で いきたいとおもいます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let http = require('http');
http.get(`http://www.jra.go.jp/keiba/common/calendar/json/201701.json`, (response) => {
let buffer = '';
response.setEncoding('utf8').on('data', (chunk) => { buffer += chunk; });
response.on('end', () => {
let json = JSON.parse(buffer)[0].data;
for (i in json) {
for (j in json[i].info[0].gradeRace) {
let data = json[i];
let race = data.info[0].gradeRace[j];
console.log(`${data.date}(${data.day}) ${race.grade} ${race.name}`);
}
}
});
});

一時ファイルに落としても良かったのですが、ファイルとして取っておく必要もなく 1 JSON ファイルあたり 7 KB ちょっとなので変数で直接持ってしまいます.
httpdata イベントで読み込んだ分の情報を返します. 全てを読み込んだ結果ではないことに注意です. 読み込みが終わるまで変数に追加し保持しておきます.
全てが読み込み終わると end イベントが発生するので、このタイミングで保持しておいたデータを処理します. 今回は読み込んだ情報を JSON オブジェクトにし、1行ごとに開催日とグレード、レース名をコンソールに出すようにしました.

これを 月ごとの JSON ファイルを取得するようにすれば、開催日の情報をボットに通知してもらうことができそうです.


JRA の 開催日情報を取得できました. これを活用することで事前にレース開催日を知ることができるので、心して観戦に望めそう
ところで この JSON、JRA の ウェブサイトのカレンダーを描画するためのデータをもらっているわけですが、公式に公開されているものではないので勝手に使うのは お行儀がよくないです. やはりちゃんと公開されているデータを使いたいところです.

Slack の ボット で 定時アクション

Slack に 設置したボットを一定時間ごとに発言するようにしたいと思います.
定時アクションができるようになると、天気の情報を毎朝ポストしてもらうなど活用の幅が広がります.

作業環境

  • Slack
  • Node.js 6.9.1 LTS
  • Botkit 0.4.9
  • node-cron 1.2.1

Node.js に cron を 追加

Linux などの OS で 定期的な処理を行うには cron → crontab - Wikipedia が 使われます. Node.js でも同様に cron と呼ばれるモジュールがいくつかあります.
今回は Kelektivさん の node-cron を 使いたいと思います.

ボットのプロジェクト・ディレクトリで以下のコマンドを実行し node-cron モジュールをインストールします.

1
2
c:\Develop\repos\slack-bot> npm install cron --save
`-- cron@1.2.1

メモ
node-cron という名前のモジュールは Kelektivさん と Merenciaさん の 2つがあるので注意が必要です.
今回は タイムゾーンが指定できることと 、日付指定の実行ができることなどから Kelektivさん のを使わせていただきました.
特にタイムゾーンを意識する必要がない場合は、Merenciaさん のも シンプルな実装でよいかもしれません.

Botkit での 定時処理

とりあえず、月曜日~金曜日 の 毎朝 9:00 に 挨拶をするようにしたいと思います.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Botkit = require('botkit');
const cron = require('cron');
const controller = Botkit.slackbot();
controller.spawn({
token : process.env.token
}).startRTM((err, bot, payload) => {
new cron.CronJob({
cronTime: '00 00 09 * * 1-5',
onTick: () => {
bot.say({
channel: 'random',
text: ':smiley: おはようございます!'
});
},
start: true,
timeZone: 'Asia/Tokyo'
});
});

  • ボット起動時に cron の 処理を登録したいので、startRTM() の 中で new cron.CronJob() します.
  • cronTimeCron Ranges に従い、起動したい時間を指定します. 今回は「月曜日~金曜日 の 毎朝 9:00」なので、前半 の 00 00 09 ~~~ で 9:00 を 指定し、後半 の ~~~ * * 1-5 で 毎月毎日(* * ~~~) に 加えて 最後の 1-5 で 月曜日~金曜日(0 は 日曜日) を 指定します.
  • onTickcronTime で 指定したタイミングで行う処理を記述します. 今回は Botkit に #random チャンネル へ 挨拶を発言しさせます.
  • starttrue にすることで、そのまま cron の 処理を開始させます. 処理開始を明示的に行いたい場合は false にし、開始したい場所で start() を 呼び出しますが、今回は即時に処理を開始してよいので true にしました.
  • timeZonecronTime に 指定するタイムゾーンを明示します. 最近はクラウド環境を使ったりするのでタイムゾーンは意識的に書いておいた方がトラブルに合いにくい気がします.

※ 今回は #random へ 発言するので channel: 'random' としていますが、ID で 指定したほうがよいので https://slack.com/api/channels.list?token=[API_TOKEN] へ アクセスして、ID を 調べます. また、発言先のチャンネルへボットが参加している必要があります.

実行!

通常通り起動し、cronTime で 指定した時間を待ちます. 無事、ボットが挨拶をしてくれたでしょうか!?


Node.js の モジュールを使うことで簡単に、ボットの定時アクションを実装することができました! モジュールを作ってくださる方々のおかげで、やりたいことがすぐにできるので助かります. 感謝感謝です.
次回はオーソドックスに天気を知らせてくれる機能を追加しようかな~

Hexo の 投稿記事 URL を 変更する

Hexo の 投稿記事 の URL は、タイトルが使われます. その際に半角スペースをハイフン(-) に 置き換えてくれるのですが、私は日本語とアルファベットが混ざる際に半角スペースを入れてレイアウトを調整します. そうなると URL 日本語なのにハイフンだらけとなってしまいます. 昔のブラウザは URL エンコードされた文字列だったので気にもならなかったのですが、最近のブラウザはでコードして日本語で表示してくれるので気になってしまうので、極力 ハイフンなし の URL に 戻したいと思います.

作業環境

  • Windows 7
  • Hexo 3.2
  • hexo-generator-alias 0.1.3

記事のファイル名を変更する

URL を 変更するには、各投稿記事のファイル名を変更し再生成するだけです. 簡単!
アルファベットの区切りとしての半角スペース(=ハイフン) は よいので、日本語とアルファベットの間でレイアウト調整した分だけ、ハイフンを消してファイル名変更します.

メモ
ファイル名を変更するということは、古いファイルを消して、新しいファイルを作ることなのですが、hexo generate は 生成済みのファイルの削除は行ってくれないようです. 基本的に投稿を削除するということはないので通常はそれでよいのですが、今回のようにファイル名変更した場合は hexo clean して生成済みのファイルを削除してから hexo generate する必要があります.

古い URL からのリダイレクトを設定

ファイル名を変更することで URL を 変更できましたが、古い URL へのケアが必要です. リンクしてくださっている サイト や SNS の 投稿などがありましたら 404 Not Found に なってしまいますし、Google などの 検索エンジンからは重複記事と見えてしまいます. そのようなことにならないよう、古い URL に リダイレクトの設定をしておきます.

Hexo で リダイレクト設定するには、hexo-generator-alias を 使います.
Hexo の ソースがあるフォルダで npm install hexo-generator-alias --save を実行します. ついでに各 Plugin の アップデートもしておきます. (下記例の [username] は 自分の GitHub ユーザ名)

1
2
3
4
5
C:\Develop\repos\[username].github.io> npm update
C:\Develop\repos\[username].github.io> npm install hexo-generator-alias --save
`-- hexo-generator-alias@0.1.3
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.0.17: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

リダイレクトの設定 は 各投稿のファイル もしくは、_config.yml で 行います.
_config.yml は 全体設定を置いておきたいので、今回は各投稿のファイルに書きます. たとえば本ブログの最初の記事の例で、以下のように Front-matter へ alias: [古い URL] を 追加します.

1
2
3
4
5
6
7
8
---
title: Hexo GitHub Pages ブログ環境の構築
date: 2016-11-01
categories: ブログ
tags:
- Hexo
alias: /2016/11/01/Hexo-と-GitHub-Pages-で-ブログ環境の構築/
---

対象となる投稿記事の設定が終わったら、hexo clean, hexo generate で 再生成します.
これにより公開フォルダが以下のように生成され、古い URL にも index.html が 生成されています.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
c:\Develop\repos\personal\[username].github.io>tree /F
C:.
├─.deploy_git
├─.settings
├─node_modules
├─public
│ ├─2016
│ │ ├─11
│ │ │ ├─01
│ │ │ │ ├─Hexo-と-GitHub-Pages-で-ブログ環境の構築
│ │ │ │ │ index.html
│ │ │ │ └─HexoとGitHub-Pagesでブログ環境の構築
│ │ │ │ index.html
├─scaffolds
├─source
└─themes

古い URL の index.html は、以下のようにリダイレクトのソースが生成されています. (実際は 1行、投稿用に整形済み)

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Redirecting...</title>
<link rel="canonical" href="/2016/11/01/HexoとGitHub-Pagesでブログ環境の構築/">
<meta http-equiv="refresh" content="0; url=/2016/11/01/HexoとGitHub-Pagesでブログ環境の構築/">
</head>
</html>


URL の 変更を無事に行うことができました. 最初からちゃんと URL 構成を考えておけば、こんなことにならなかったわけですが、まぁ仕方ない. エイリアス設定の勉強ができたのでよしとしましょう.

ところで本件の原因となった、全角と半角が混ざる際に半角スペースを入れたくなる問題、本当は CSS なりのスタイル側で処理してくれるとよいもので、文章上の意味を持たないのにレイアウトとして半角スペースを入れるのはよくないのかなぁとは考えています…
とはいえ現状はスタイル側で対応できないので致し方なし、といういうのと対比となる文の場合で強調するほどでもないときに半角スペースを入れることで、自然な強調にできるのでよく使っていたりもします.

このあたり、みなさん いろいろと苦労されていらっしゃるようで、私もよい解があったら ぜひ乗りたいなぁ.

Hexo で PubSubHubbub 通知をする

Hexo の フィード出力を設定した際に、設定項目で気になったものがありました. “hub - URL of the PubSubHubbub hubs (Leave it empty if you don’t use it) – hexo-generator-feed“ です.

そもそも PubSubHubbub 自体を知らなかったのですが、”パブサブハバブ – Wikipedia“ と 読むそうで、データの変更(ここでは、ブログの更新情報) を リアルタイムに通知するためのプロトコルなのだそうです.

この仕組みを使うことで、Google などの検索エンジンにブログの更新を効率よく通知することができ、インデックス化を早めることができるとのことで、早速導入したいと思います.

作業環境

  • Windows 7
  • Hexo 3.2
  • hexo-generator-feed 1.2.0
  • GitHub (GitHub Pages / Webhook)

PubSubHubbub とは?

PubSubHubbub は、分散型 の パブリッシュ(発行)/サブスクライブ(購読) コミュニケーション を 行うためのオープンなプロトコルで、仕様は https://github.com/pubsubhubbub/PubSubHubbub で 公開されています. 2017年1月現在のバージョン は 0.4 です.

ざっくり言うと、情報の提供者と利用者の間にハブを置くことで、それぞれが分離した作業ができるようにし、またプッシュ型にすることで情報の利用者が提供者のサーバへ定期的なポーリングせず不要な負荷を下げることができるようになります.
利用例として気象庁の電文公開があります.

今回は、この仕組みを使い Google PubSubHubbub Hub へ ブログの更新情報をプッシュし、Google の クローラー へ ブログの更新に関する情報を購読してもらいます. これによって Google の クローラー が 定期的にブログの更新を確認しに来てくれていたのを、こちらからクローラーへ来てもらうことができるようになります.

これまでは Google の クローラーが来てくれるのを ただ待っていただけですが、PubSubHubbub を 使うことでクローラーを呼び込むことができ、検索エンジンのインデックス化を格段に早めることができるようになります.

Hexo に PubSubHubbub を 設定

PubSubHubbub は RSS や Atom フィード で 情報を提供します. hexo-generator-feed が PubSubHubbub の 情報生成に対応しているので、まずは hexo-generator-feed の 設定を行います.

config.yml の hexo-generator-feed に 関する設定に hub: http://pubsubhubbub.appspot.com を 追加します.

1
2
3
4
5
feed:
type: atom
path: atom.xml
limit: 20
hub: http://pubsubhubbub.appspot.com

hexo generate で サイトを再生成してローカルサーバ http://localhost:4000/atom.xml でフィードを確認してみると、<link href="http://pubsubhubbub.appspot.com" rel="hub"/> が 増えているのが分かります.

GitHub の Webhook で PubSubHubbub 通知設定

hexo-generator-feed の 設定はフィードの出力設定で、PubSubHubbub の ハブ (Google PubSubHubbub Hub) への通知は別途行う必要があります.
GitHub Pages で ウェブサイトを公開しているので、今回は GitHub の Webhook を 使って更新時にハブへ通知するようにしたいと思います.

GitHub リポジトリ の Setting ページ から、Webhooks を 表示し、右側の [Add webhook] ボタンをクリックします.

以下の情報を入力し、[Add webhook] ボタンをクリックします.

設定項目 設定内容
Payload URL [PubSubHubbub 通知 URL(※)]
Content type application/x-www-form-urlencoded を 選択
Secret [空欄]
Which events… Let me select individual events.Page build に チェック

[PubSubHubbub 通知 URL] は https://pubsubhubbub.appspot.com/publish?hub.mode=publish&hub.url=https://[username].github.io/atom.xml で、[username] を 自分のユーザ名に置き換える、もしくは hub.url= 以降に自分のサイトのフィード の URL にします.
心配な場合は https://pubsubhubbub.appspot.com/publish で 正しいか検証することができます.

また [Which events…] は Page build だけにし、GitHub Pages の ページがビルドされた時だけ通知するようにします. Push だと、ドラフトの記事を GitHub に 上げた際にも通知されてしまい、不要な通知がハブへ行ってしまいます.

Webhook が 追加されました. タイミングによっては通知のテストが行われておらずチェックがついてません. URL 部分をクリックし設定から通知テストを確認するかリロードして確認します.

通知テストの結果は Wehbook の 設定画面の一番下にあり、Response が 204 に なっていれば成功です.
実動作の確認としては CircleCI から再ビルドするか、hexo deploy で サイトを更新します. 正しくデプロイできると、この画面 の Recent Deliveries が増えます.


ブログ に PubSubHubbub の 通知を追加できました. これにより、より早く記事が検索できるようになるといいですね. また読んでいただけるような、しっかりした記事をかけるようにしていきたいと思います. どうぞ 今後ともよろしくお願いいたします.

Slack の ボットによる代理ポストで、簡易匿名化

ようやく常時稼働するボットを Slack に 常駐できるようになりました. 今回は Slack で 匿名発言する方法について考えたいと思います.

作業環境

  • Slack
  • Node.js 6.9.1 LTS
  • Botkit 0.4.2

Slack で 匿名発言

そもそもチャットなのに、なんで匿名で発言する必要があるのか、といった話もありますが、チームや組織の改善などを忌憚なくディスカスするには匿名はある程度意味があるようです.
行き過ぎると匿名のネット掲示板のように荒れるというケースもあるようですが、Slack は 招待性のチームで作られるので、ある程度は自制・自浄されることは期待できそうです.

Slack で 匿名発言する方法ですが、公式でその機能はありません. したがって何らかの仕掛けを作る必要があります.
API を 使って発言する方法なども考えられますが、せっかくボットを常駐させたので代理で発言してもらって、匿名化してみたいと思います.

ボット で 代理発言

Slack の ボットは、以前に設置したボット を 使うことにします.
ボットへの代理発言依頼は、ダイレクト・メッセージを使うことにします.

匿名で発言する先のチャンネルを選択(or 作成) します. 今回は sandbox に しました.
続いて チャンネル の ID を 調べるために、以下の URL へ アクセスします. [API_TOKEN] は ボットの起動に使用している API トークンです.
https://slack.com/api/channels.list?token=[API_TOKEN]

ページへアクセスすると JSON 形式 の チャンネル・リストが表示されるので、目的のチャンネルの ID を コピーします. 以下に今回使用する sandbox の 部分を抜粋します. "id": "C3ADKXXXX",C3ADKXXXX に 当たる部分になります.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"ok": true,
"channels": [{
"id": "C3ADKXXXX",
"name": "sandbox",
"is_channel": true,
"created": 1481422490,
"creator": "U3A3DXXXX",
"is_archived": false,
"is_general": false,
"is_member": false,
"members": [ "U3A3DXXXX", U3AQBXXXX ],
"topic": { "value": "", "creator": "", "last_set": 0 },
"purpose": { "value": "", "creator": "", "last_set": 0 },
"num_members": 2
}]
}

ボットの代理発言を実装します.
以下のコードをボットの実装に追加します. [CHANNEL_ID] は 上記で取得した発言先チャンネル の ID です.

1
2
3
4
5
6
controller.on('direct_message', (bot, message) => {
bot.reply(message, '匿名でポストしました.');
bot.startConversation({ channel : '[CHANNEL_ID]' }, (err, convo) => {
convo.say(message);
});
});

実装の内容としては以下となります.

  • controller.on()direct_message イベント に 反応するようにします.
  • bot.reply() で ダイレクト・メッセージに返事をします.
  • bot.startConversation() で ID 指定したチャンネルで会話を開始します.
  • convo.say(message); で 指定チャンネルへ message、ユーザから入力された内容を そのままに指定チャンネルへ発言します.

匿名で発言!

DIRECT MESSAGES の ボット名をクリックし、発言したい内容を入力します. (つまりボットに対してダイレクト・メッセージを送ります)

ダイレクト・メッセージを送るとボットから返事があり、すぐに匿名発言先のチャンネル (ここでは sandbox) に 発言があるハイライトがされます.

チャンネルを開くと、先ほどボットにダイレクト・メッセージした内容を、ボットが発言しています.
匿名発言ができるようになりました.


ボットを使って匿名発言ができるようになりました. Botkit が いろいろやってくれるので簡単ですね.

ただし匿名とはいえ単にボットで代理発言をさせているだけなので、ボット・プログラムでデバッグログなりを出すことで発言を残すことはできますので、完全匿名とまではいかないですが、自由な発言から議論が広がるといいですね.