空談録

世界で5人くらいに役立ちたい

PHPでSlackに現在の降雨情報を投げてみる

暑い寒い寒い寒い暑い暑い寒いみたいなの本当につらいのでやめてほしい。

たまにSlackで現在の降水情報を見たいと思うときがあります。ないですかそうですねそんな気もします。
アメッシュ見ろやって話ではあるんですが、範囲の狭さ的に「そろそろやばいな」オーラを把握するのが面倒です。

というわけで、誰が得するのかわかりませんが現在の雨情報をSlackで投げてみましょう。
LambdaとかAzure Functionとかでやればいいんでしょうが、お金がないのでサイト動かしてるサーバーで簡単に動くPHPにします。
ほかの選択肢もあるにはあるんですけど、いまいち動かし方わからんという感じなので…。

全体の流れ

今回はSlackで適当に雨って投げたら動いてくれる仕組みにします。

というわけで
・Slackに投稿 (ユーザー)

↓(Outgoing Webhook)

PHPで雨情報取得 (サーバー) ←→ どこかの降水量情報

↓(Slack API (files.upload))

Slackに投稿される

みたいな感じにしましょう。

なんかOutgoing WebhookはLegacyとか言われていますが、特に終わる気配はないはずなのでこちらで。
internal integrationsとかいまいちわからないですし…?

まあなんにせよSlackから雨情報くれというのがサーバーに渡ればいいので、slash commandあたりでも作れると思います。
サクサクと作っていきましょう。

降水量を取得しよう

Outgoing Webhookの設定は世界のどこかにあると思うので(というか面倒なので)すっ飛ばしていきます。

降水量については、できれば高解像度降水ナウキャストがほしいんですけど、使う手段がよくわからない状態です。
気象庁 | 高解像度降水ナウキャスト

そのため今回はYahooのスタティックマップを使って現在の降水情報を取得し、Slackに投げていきます。
便利なことに画像でそのままくれるので、そのまま投げればよい感じです。

developer.yahoo.co.jp

使い方は上からどうぞ。
多彩なオプションが使えるので自分だけの最強アメッシュ画像を作ろう!とかができます。

今回はこんな感じで使っていきます。

https://map.yahooapis.jp/map/V1/static?
appid={appId}&
lat=35.504193&
lon=139.473445&
z=9&
width=600&
height=450&
overlay=type:rainfall|
datelabel:off&style=base:green|
off:background,address,area_name,line_name|
on:nature,water,prefecture

appIdは自分のアプリケーションIDをいれます。
lat, lonは座標です。好きな座標を入れましょう。
zはズームレベルを示します。値が大きいほど狭い範囲の拡大画像となります。
width, heightは画像の大きさを示すので好きな値で。
overlayは地図上に出すメタ情報的なものを指定できます。
例えば富士山などの地名、東名高速道路などの道路名、山手線などの線路などを表示するかを指定できます。
雨が見たいので割とoffでざっくり消してます。県の名前くらいはさすがに欲しいのでprefectureはon。
降雨情報はtype:rainfallで表示できます。

これでこんな画像ができあがります。
f:id:fantasticswallow:20170903175414p:plain
色合いとかはstyleとかでいじってください…。

さて、この画像をPHPで取得していきます。
コードはこんな感じ。

$amedasLink = "https://map.yahooapis.jp/map/V1/static?appid=...";
$imageData = curl_get_contents($amedasLink);
$ts = strval(time());

$imageFileName = "amedas-" . $ts . ".png";
$fileRet = file_put_contents("./" . $imageFileName, $imageData);


function curl_get_contents($url)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);

    $data = curl_exec($ch);
    curl_close($ch);

    return $data;
}

もともとはamedasLinkをfile_get_contentsでとっていたのですが、なんかうまくいかず…。
助けてStackOverflowをしたらこんな記事が見つかったのでcurl_get_contentsをそのままもらいましょう。
stackoverflow.com

これでひとまずyahooの降雨情報をサーバー上で取得して保存まではできました。
保存した画像をSlackに投稿していきます。

files.uploadをたたこう

ところでOutgoing Webhookではファイルアップロードはできません。
attachmentsのimage_urlを指定すればいいじゃん!っていう話ではありますが、諸事情であんまりドメインとか見られたくないとかあったりあったりで。
まあなんにせよSlackへのファイルアップロード自体はできません。参照はできます。

というわけでアップロードしていきましょう。
アップロード自体はSlack APIのリファレンス読めばまあ何とかなるかなぁと。
files.upload method | Slack

POSTで投げるパラメータ自体はある程度理解できそうなのでこちらもcurl_execで投げつけていきます。

$slackUpload = 'https://slack.com/api/files.upload';
$ch = curl_init($slackUpload);

if(array_key_exists("channel_id", $_POST) == true)
{
    $channel_id = $_POST["channel_id"];
}

// Create a CURLFile object
$cfile = curl_file_create($imageFileName);

// Assign POST data
$postParams = array(
    'token' => '{your token}',
    'channels' => $channel_id,
    'filename' => $imageFileName,
    'file' => $cfile
);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data;charset=utf-8', 'User-Agent: curl']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postParams);

// Execute the handle
curl_exec($ch);
curl_close($ch);

試行錯誤の成果みたいな状態…。

Outgoing WebhookではPOSTメソッドでtrigger_textやらを投げてくるので、POSTパラメータから投稿されたメッセージのchannel_idを取得します。
その後curlで投げるPOSTパラメータにchannelsという形で指定します。これで投稿されたチャンネルに画像が投稿されます。

curlでPOST投げるコードについては次の記事あたりを参考にしました。
masabloggers.blogspot.jp

curl周りは良しなに設定してなげつけます。
これで基本的にはうまくいくはず…?

最終的なコード

大体こんな感じです。

function amedas()
{
	$amedasLink = "https://map.yahooapis.jp/map/V1/static?appid=...";
	$imageData = curl_get_contents($amedasLink);
	$ts = strval(time());

	if(array_key_exists("channel_id", $_POST) == true)
	{
		$channel_id = $_POST["channel_id"];
	}

	$imageFileName = "tiger-amedas-" . $ts . ".png";
	$fileRet = file_put_contents("./" . $imageFileName, $imageData);

	if ($fileRet > 0)
	{
		$slackUpload = 'https://slack.com/api/files.upload';
		$ch = curl_init($slackUpload);

		// Create a CURLFile object
		$cfile = curl_file_create($imageFileName);

		// Assign POST data
		$postParams = array(
			'token' => '{your token}',
			'channels' => $channel_id,
			'filename' => $imageFileName,
			'file' => $cfile
		);
		curl_setopt($ch, CURLOPT_POST,TRUE);
		curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data;charset=utf-8', 'User-Agent: curl']);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $postParams);

		// Execute the handle
		curl_exec($ch);
		curl_close($ch);
	}
}

file_put_contentsの戻り値はファイルサイズを返すので、0だったらどちらにしろダメという感じで抜けます。
あとは戻り値を取るとファイル書き込みの完了を明示的にできるというあれそれ。



説明っぽい説明がすっ飛んでいる記事になってしまった。
そもそもPHPが詳しくないというのが問題な気がする。

最近いろいろ思うところもあるので、またOfficeとか追っていきたいなぁという感情はありつつ。
しかし会社でOffice使わないのがスーパーモチベーション下がる…。
うーんうーん。

この辺で