はじめに
大学の研究室ではDrobpoxのフォルダを共有して、卒論や修論のファイルを置いてもらって、順番に添削を行っています。
添削後は添削済み用のフォルダに入れます。
フォルダ構成は以下のような感じです。
原稿を添削したら、添削済みのフォルダに入れてSlackで通知します。
研究室では30名近くを1名で見ていて、
添削ごとに返信するのは正直手間です。
ですので、添削済みのフォルダに入れれば自動的に通知するアプリを作りました。
対象者
- Dropboxの変更をSlackで通知したい方
処理の流れ
- Dropboxで監視したいフォルダの内容変更
- Dropbox APIで変更が検知され、WebhookによりPOSTパラメータが送られる
=>Webhookについてはこちらの記事を参考にしてください - あらかじめ指定されたWebサーバでPOSTパラメータを受け取る
- SlackにPOSTパラメータを送る
- Slackでメッセージが通知される
前準備
Dropboxアプリの登録 => こちらの記事を参考にしてください
Dropboxのアプリの作成
Dropboxのアプリを作成したら、アクセストークンを取得してメモしておきます。
そのあと、files/list_folderでアクセストークンと監視したいフォルダのパスを入力します。
- フォルダのパスの例:
/Lab/2_添削フォルダ_開発版/
Submit Callを押すとResponseのところに、該当のフォルダ内容が表示されます。
上記の内容は、フォルダ「/Lab/2_添削フォルダ_開発版」に 「a.txt」 が入っている状況です。
このResponseの中にcursorがあります。これは(おそらく)フォルダの内容を時間軸で保持している状態になります。このcursorの内容もメモしておきます。
Dropbox API用のプログラムの作成
Dropboxのフォルダで変更があったとき、通知を受け取るプログラムを作ります。
こちらの記事 (ぱふぅ家のホームページ)を参考にPHPブログラムを作っていきます。
これからのプログラムはWebサーバに置きます。ただし、httpsが使用されるWebサーバに限ります。
まずDropboxのアクセストークンをメモしたdropboxAccessToken.txtを作成します。
xxxxxxx <= Dropboxのアクセストークンを記述する
つぎに、先ほどメモしたcursorを記述します。
xxxxxxx <= cursorを記述する
DropboxからWebhookで通知を受け取ったら、
変更ファイルのみを抽出するプログラムを記載します。
<?php
//Dropboxの更新を通知するクラス
class dropboxUpdateNotificationAPI {
private $error; //エラーフラグ(エラーがあればTRUE)
private $errmsg; //エラーメッセージ(APIが返す error_summary の内容)
private $responses; //直前の結果
private $access_token_path; //Dropboxで得られるアクセストークンのファイルパス
private $cursor_file_path; // cursorが保存されるファイルのパス
/**
* コンストラクタ
* @param なし
* @return なし
*/
function __construct() {
//$this->webapi = '';
$this->error = FALSE;
$this->errmsg = '';
$this->responses = NULL;
$this->access_token_path = __DIR__ . "/dropboxAccessToken.txt";
$this->cursor_file_path = __DIR__ . "/cursor.txt";
}
/**
* デストラクタ
* @return なし
*/
function __destruct() {
unset($this->responses);
}
/**
* Dropbox API v2:アクセストークンを用いたリクエスト
* @param string $url リクエストURL
* @param array $argv Dropbox-API-Argに渡すパラメータ配列(省略可能)
* @param array $post POSTに渡すパラメータ配列(省略可能)
* @param string $upload アップロードするデータ
* @return bool TRUE:リクエスト成功/FALSE:失敗
*/
function requestDropboxAPIv2($url, $argv=NULL, $post=NULL, $upload=NULL) {
$flagArg = FALSE;
$headers[] = 'Authorization: Bearer ' . $this->getAccessToken();
// cURLを使ってリクエスト
$curl = curl_init() ;
curl_setopt($curl, CURLOPT_URL , $url);
curl_setopt($curl, CURLOPT_HEADER, 1) ;
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER , FALSE);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
if ($argv != NULL) {
$headers[] = 'Dropbox-API-Arg: ' . json_encode($argv);
$flagArg = TRUE;
}
if ($upload != NULL) {
$headers[] = 'Content-Type: application/octet-stream';
curl_setopt($curl, CURLOPT_POSTFIELDS, $upload);
$flagArg = FALSE;
} else if ($post != NULL) {
$headers[] = 'Content-Type: application/json';
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($post));
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$res1 = curl_exec($curl);
$res2 = curl_getinfo($curl);
curl_close($curl);
$res = substr($res1, $res2['header_size']);
//結果処理
//$this->webapi = $url;
preg_match("/HTTP\/[0-9\.]+\s([0-9]+)\s(.+)/ims", $res1, $arr);
if (! isset($arr[2])) {
$this->error = TRUE;
$this->errmsg = 'Dropbox API failure';
} else if ($arr[1] >= 300) {
$json = json_decode($res);
$this->error = TRUE;
$this->errmsg = $arr[2];
} else {
$this->responses = $flagArg ? $res : json_decode($res);
}
return (! $this->error);
}
/**
* Dropbox API v2:更新された添削ファイル名の取得
* @param string $cursor: カーソル
* @return ファイル名/FALSE: 存在しない
* カーソルをfileに保存しておく
*/
function getUpdatedCorrection() {
$url = 'https://api.dropboxapi.com/2/files/list_folder/continue'; //リクエストURL
$post['cursor'] = $this->getCursor(); //カーソルの取得
$res = array();
if ($this->requestDropboxAPIv2($url, NULL, $post) == FALSE) {
$res = FALSE;
} else if (isset($this->responses->entries[0]->name)) { //ファイルが存在したら
$res = $this->responses->entries; //配列にする
//ファイルが存在するのでcursorの更新
$this->saveCursor ($this->responses->cursor);
}
return $res;
}
/**
* Dropboxのアクセストークンを取得する関数
* @return string Dropboxのアクセストークン
*/
function getAccessToken() {
$handle = fopen($this->access_token_path, "r") or //ファイルオープン
$this->fopen_error_func($this->access_token_path); //ファイルオープン失敗時に呼ばれる関数
$at = fgets($handle);
fclose($handle);
return ($at);
}
/**
* cursorの取得する関数
* @return string cursor
*/
function getCursor() {
$handle = fopen($this->cursor_file_path, "r") or //ファイルオープン
$this->fopen_error_func($this->cursor_file_path); //ファイルオープン失敗時に呼ばれる関数
$cursor = fgets($handle);
fclose($handle);
return ($cursor);
}
/**
* cursorを保存する関数
* @param string cursor
* @return なし
*/
function saveCursor($cursor) {
$handle = fopen($this->cursor_file_path, "w"); //ファイルオープン
fputs($handle, $cursor); //保存
fclose($handle);
}
/**
* ファイルオープンが失敗した時に呼ばれる関数
* @param string $fname ファイル名
*/
function fopen_error_func($fname) {
$error_file = fopen ("error.log", "a");
fputs($error_file, $fname . " was not opened.\n");
fclose($error_file);
die;
}
}
WebhookでPOSTパラメータを受け取るプログラムを作成します。
<?php
//verification request
if(isset($_GET['challenge'])){
echo htmlspecialchars($_GET['challenge']);
die;
}
//Dropbox APIの読み込み
require_once(__DIR__ . '/dropboxUpdateNotificationAPI.php');
$pdb = new dropboxUpdateNotificationAPI(); //更新を通知するクラス
//更新ファイルを取得できない場合
if (($update_files = $pdb->getUpdatedCorrection()) == FALSE) {
die;
}
$path = __DIR__ . "/dropbox.log"; //更新情報の記載
$file = fopen ($path, "w");
fputs($file, "---更新---\n");
foreach($update_files as $uf) {
$uf_arr = (array) $uf;
if($uf_arr[".tag"]=="file") {
//文字列が含まれていない場合
if (strpos($uf_arr["name"],'添削済み') === false) {
continue;
}
fputs($file, $uf_arr["name"] . " が添削されました\n");
}else if ($uf_arr[".tag"]=="deleted") {
fputs($file, $uf_arr["name"] . "が 削除されました\n");
}
}
fputs($file, "---更新ここまで---\n");
fclose ($file); //ファイルを閉じる
?>
冒頭の$_GET['challenge']
は、DropboxのWebhookに登録すると、確認のために送られるので、その対応のために記載されます。
その後、Dropbox APIのアプリページでWebhookのアドレスを登録します。
ここのWebページはhttpsである必要があります。
登録ができれば、該当のフォルダに何かファイルを入れてください。
うまくいけばdropbox.log
が追加され、変更内容が記載されます。
#SlackのAppの生成
Slack側でapiを作成するために、以下のURLをアクセスします。
https://slack.com/services/new/incoming-webhook
「Post to Channel」でチャンネルを選択します。
Workspaceが異なる場合は、右上のアイコンで選択し直します。
いきなりチャンネルに通知させるのは他人に迷惑がかかるかもしれませんので、
Privately to @xxx(you)
のように自分に送るといいかもしれません。
Incoming WebHooksのページのWebhookで「Webhook URL」をメモします。
https://hooks.slack.com/services/xxxxxxx <= slackのWebhookのURLを記述する
Slackにメッセージを通知するプログラムです。
<?php
//Slackにメッセージを通知するクラス
class slackMessageSendingAPI {
private $webhookURL_path; //SlackのWebhook URLのファイルパス
private $webhookURL;
/**
* コンストラクタ
* @pram なし
* @return なし
*/
function __construct() {
$this->webhookURL_path = __DIR__ . "/slackWebhookURL.txt";
$this->webhookURL = $this->getWebhookURL(); //Webhook URLの取得
}
/**
* webhook URLの取得
* @param なし
* @return string Webhook URL
*/
private function getWebhookURL() {
$handle = fopen($this->webhookURL_path, "r") or //ファイルオープン
$this->fopen_error_func($this->webhookURL_path); //ファイルオープン失敗時に呼ばれる関数
$webhookURL = fgets($handle);
fclose($handle);
return($webhookURL);
}
/**
* ファイルオープンが失敗した時に呼ばれる関数
* @param string $fname ファイル名
*/
private function fopen_error_func($fname) {
$error_file = fopen ("error.log", "a");
fputs($error_file, $fname . " was not opened.\n");
fclose($error_file);
die;
}
/**
* slackにメッセージ送信
* @param string $message メッセージ
* @return なし
*/
public function sendMessage($message) {
$message_data["text"] = $message;
//メッセージのjson化
$message_json = json_encode($message_data);
//payloadの値としてURLエンコード
$message_post = "payload=".urlencode($message_json);
$slack_file = fopen ("slack.log", "w");
fputs($slack_file, "message: " . $message . "\n");
fputs($slack_file, "url: " . $this->webhookURL . "\n");
fclose($slack_file);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL , $this->webhookURL);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $message_post);
curl_exec($curl);
curl_close($curl);
}
// End of Class ===========================================================
}
?>
webhook.phpにSlack API用の内容を記述します。
すでに記載したwebhook.phpを編集します。
(略)
//Slack APIの読み込み
require_once(__DIR__ . '/slackMessageSendingAPI.php');
$psk = new slackMessageSendingAPI(); //slackにメッセージを送るクラス
(略)
foreach($update_files as $uf) {
$uf_arr = (array) $uf;
if($uf_arr[".tag"]=="file") {
//文字列が含まれていない場合
if (strpos($uf_arr["name"],'澤野添削済み') === false) {
continue;
}
//slackメッセージ
$psk->sendMessage($uf_arr["name"] . " が添削されました");
fputs($file, $uf_arr["name"] . " が添削されました\n");
}else if ($uf_arr[".tag"]=="deleted") {
fputs($file, $uf_arr["name"] . "が 削除されました\n");
}
}
(略)
これで、Dropboxの監視フォルダが変更されたら、
Slackで通知されます。
#おわりに
はじめてのQiitaに投稿しました。
不備などあるかもしれませんが、
ご連絡いただけると助かります。
#参考記事