Googleマイマップの更新情報をSlackに通知する
やりたいこと
Googleマイマップにラーメン屋の感想を入力し、Slackに内容を通知して共有したい
調べた限り、マイマップには更新を検知するためのwebhook設定などはない。
今回はGoogle Apps Script, Fusion Tablesを利用して実現する。
※Fusion Tablesは2019年12月でサービス終了のアナウンスが流れているため注意してください
各種利用するサービスについて
マイマップ
自分専用のGoogle Mapを作り、メモとかピンができる
Fusion Tables
About Fusion Tables - Fusion Tables Help
スプレッドシートをRDBMSのようにSQLで編集することができる。
実装
既存マイマップをFusion Tablesに落とし込む
マイマップはkmlというxml形式でエクスポートすることができる。
ダウンロードしたkmlファイルを開いてURLをたどっていくと以下のような形式のkmlがダウンロードできるリンクにたどり着く
<?xml version="1.0" encoding="UTF-8"?> <kml xmlns="http://www.opengis.net/kml/2.2"> <Document> <name>hogehogeマップ</name> <description>hogehoge</description>
上記kmlをダウンロードし、新規作成したFusion Tablesにインポートする
正しくインポートされたら name
, description
カラムが追加される
Google Apps Scriptでkmlを監視する
以下に全文ソース記載。やっていることは
- 現在のFusion Tablesのデータを取得
- 現在のkmlのデータを取得
- kmlからpinデータのみを抽出
- Fusion Tablesのデータと比較
- Fusion Tablesにないものは新規レビューとしてFusion Tablesに追加、Slackに通知
function myFunction() { // table id を入力 var tableId = '*****************************************'; var selectSql = 'SELECT name, description FROM ' + tableId; var res = FusionTables.Query.sql(selectSql); // fusion tableのデータをハッシュに var fusionTable = {}; for(var i = 0; i < res.rows.length; i++) { var name = res.rows[i][0]; var description = res.rows[i][1]; fusionTable[name] = fusionTable[name] || []; fusionTable[name].push(description); } // kml urlを入力 var kmlUrl = 'https://www.google.com/maps/d/kml?forcekml=1&mid=******************************'; var kmlResponse = UrlFetchApp.fetch(kmlUrl); var xml = XmlService.parse(kmlResponse.getContentText()); // ハードコーディングしているが取得したほうが安全 var namespace = XmlService.getNamespace('http://www.opengis.net/kml/2.2') // kmlのFolder Elementの中にデータあり var folders = xml.getRootElement().getChildren()[0].getChildren('Folder', namespace); for(var i = 0; i < folders.length; i++) { var folder = folders[i]; // name, descriptionのみを取り出す var placemarks = folder.getChildren("Placemark", namespace); for(var j = 0; j < placemarks.length; j++) { var nameElement = placemarks[j].getChild("name", namespace); var descriptionElement = placemarks[j].getChild("description", namespace); var name = ''; var description = ''; if(nameElement) name = nameElement.getValue().trim(); if(descriptionElement) description = descriptionElement.getValue().trim(); // 現在のFusion Tablesとの比較 if(!fusionTable[name] || fusionTable[name].indexOf(description) == -1) { Logger.log(name + ": false"); Logger.log("name") Logger.log(name); Logger.log("description"); Logger.log(description); Logger.log("fusionTable"); Logger.log(fusionTable[name]); var insertSql = 'INSERT INTO ' + tableId + '(name, description) VALUES (\'' + name + '\', \'' + description + '\');'; FusionTables.Query.sql(insertSql); sendSlackMessage(name, description); } } } } function sendSlackMessage(name, description) { // Slack incoming webhook URLを入力 var slackWebhook = 'https://hooks.slack.com/services/****************************; var options = { "method": "post", "contentType": "application/json", "payload": JSON.stringify({ "text": "*" + name + "*\n" + description }) }; UrlFetchApp.fetch(slackWebhook, options); }
定期的に実行する
GASを定期実行するトリガーを設定する
Done
参考URL
RubyのClass#inheritedの実行タイミングはクラス定義文の実行直前
サブクラスを呼び出したかったがそうはならない。
class A def self.inherited(child) p child child.aaa B.aaa aaa end def self.aaa p "A" end end class B < A def self.aaa p "B" end end p "#" * 40 B.aaa
実行結果
B "A" "A" "A" "########################################" "B"
mount_smbfs: server rejected the connection: Authentication error
mount_smbfs: server rejected the connection: Authentication error
が出る場合、sambaのパスワードを設定しなせばいけた。
sudo su smbpasswd -a ユーザ名
参考リンク
yarn install 中の node-sass エラー
make: *** [Release/obj.target/binding/src/binding.o] Error 1 gyp ERR! build error gyp ERR! stack Error: `make` failed with exit code: 2 gyp ERR! stack at ChildProcess.onExit (/home/vagrant/rails/yuyuyui/node_modules/node-gyp/lib/build.js:258:23) gyp ERR! stack at ChildProcess.emit (events.js:182:13) gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:237:12) gyp ERR! System Linux 4.15.0-20-generic gyp ERR! command "/usr/local/bin/node" "/home/vagrant/rails/yuyuyui/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library=" gyp ERR! cwd /home/vagrant/rails/yuyuyui/node_modules/node-sass gyp ERR! node -v v10.6.0 gyp ERR! node-gyp -v v3.6.2 gyp ERR! not ok Build failed with error code: 1
nodeのバージョンの問題らしい
nodeのバージョンを 10.6.0 -> 9.0.0 にしたら治った。9.0.0は別の環境と揃えただけのバージョンなので良いバージョンを探すと良さそう。
rails/webpackerを使ってRailsで定義した定数をTypeScript(JavaScript)内で使う
環境
- Rails5.1.4
- rails/webpacker 3.2.1
公式のdocumentにあるerb-loaderを利用する。 https://github.com/rails/webpacker#erb
ubuntu@ubuntu-xenial:~/rails/yuyuyui$ bundle exec rails webpacker:install:erb rails aborted! Don't know how to build task 'webpacker:install:erb' (see --tasks)
taskがないと言われた(この時点ではrails/webpacker 3.0.1あたりだった)。webpackerをupgradeする
ubuntu@ubuntu-xenial:~/rails/yuyuyui$ bundle update webpacker ubuntu@ubuntu-xenial:~/rails/yuyuyui$ bundle exec rake -T | grep erb rake webpacker:install:erb # Install everything needed for Erb
タスクができたので再実行する
ubuntu@ubuntu-xenial:~/rails/yuyuyui$ bundle exec rails webpacker:install:erb Webpacker is installed � sing /home/ubuntu/rails/yuyuyui/config/webpacker.yml file for setting up webpack paths Copying erb loader to config/webpack/loaders create config/webpack/loaders/erb.js Adding erb loader to config/webpack/environment.js insert config/webpack/environment.js insert config/webpack/environment.js Updating webpack paths to include .erb file extension insert config/webpacker.yml Copying the example entry file to /home/ubuntu/rails/yuyuyui/app/javascript/packs create app/javascript/packs/hello_erb.js.erb Installing all Erb dependencies run yarn add rails-erb-loader from "."
いろいろファイルができた。とりあえずwebpack-dev-serverを起動してみる
ubuntu@ubuntu-xenial:~/rails/yuyuyui$ bundle exec bin/webpack-dev-server /home/ubuntu/rails/yuyuyui/config/webpack/environment.js:4 environment.loaders.append('erb', erb) ^ TypeError: environment.loaders.append is not a function
appendがないと言われる。理由はわからないがとりあえず config/webpack/environment.js
内のappendをsetにして回避する(情報求)
environment.loaders.set('erb', erb)
これで使えるようになる。今回はTypeScriptに書きたいので、config/webpack/loaders/erb.js
の test:
を以下のように書き換える。
module.exports = { test: /\.(erb|ts)$/, enforce: 'pre', exclude: /node_modules/, use: [{ loader: 'rails-erb-loader', options: { runner: 'bin/rails runner' } }] }
これで例えばconfig gemを利用して以下のようなTypeScriptが書ける。
getCards(): Observable<Card[]> { let url = "http://<%= Settings['api_host'] %>/api/cards.json"; return this.http.get<Card[]>(url); }
Rails5.1 + Angular4で templateUrl, styleUrlsの代替
以前紹介したとおりRails5.1+rails webpackでtemplateUrlを使うことはできない。
Rails5.1.2でAngularを使うとtemplateUrlでエラーが出る - こんがりぃ
ちなみにドキュメントがReadmeからdocディレクトリに移ったみたい。
webpacker/typescript.md at master · rails/webpacker · GitHub
ではstyleUrlsの代替は
@Component({ selector: 'hogehoge', styles: [require('./app.component.css').toString()] })
これでOK。
TS2304: Cannot find name 'require'.
というエラーが出た場合は
yarn add @types/node
でOK。
angular - Cannot find name 'require' after upgrading to Angular4 - Stack Overflow
BootstrapのModal内でSelect2をうまく表示する
BootstrapのModal内でSelect2を使うとレイアウトが崩れてしまう。
Modalの tabindex = -1
を取るといいというのもあるらしいが、それをしてもレイアウトが崩れて困っていたが以下のリンクの通りすればいけた。
$('.hoge').select2({ dropdownParent: $('.fuga') })